Merge branch 'master' into techbar
This commit is contained in:
@@ -5,7 +5,6 @@ require_once "database_version.php";
|
||||
|
||||
require_once "config.php";
|
||||
|
||||
|
||||
$folderPath = 'uploads';
|
||||
|
||||
function countFilesInDirectory($dir) {
|
||||
@@ -190,7 +189,8 @@ $phpVersion = phpversion();
|
||||
$mysqlVersion = $mysqli->server_version;
|
||||
$operatingSystem = php_uname();
|
||||
$webServer = $_SERVER['SERVER_SOFTWARE'];
|
||||
$errorLog = ini_get('error_log');
|
||||
$errorLog = ini_get('error_log') ?: "Debian/Ubuntu default is usually /var/log/apache2/error.log";
|
||||
$updates = fetchUpdates();
|
||||
|
||||
?>
|
||||
|
||||
@@ -207,7 +207,7 @@ $errorLog = ini_get('error_log');
|
||||
echo "MySQL Version: " . $mysqlVersion . "<br>";
|
||||
echo "Operating System: " . $operatingSystem . "<br>";
|
||||
echo "Web Server: " . $webServer . "<br>";
|
||||
echo "PHP Error Log: " . $errorLog
|
||||
echo "Apache/PHP Error Log: " . $errorLog
|
||||
?>
|
||||
|
||||
<hr>
|
||||
@@ -223,6 +223,14 @@ $errorLog = ini_get('error_log');
|
||||
echo "Total size of files in $folderPath and its subdirectories: " . $totalSizeMB . " MB";
|
||||
?>
|
||||
|
||||
<hr>
|
||||
<h3>ITFlow app</h3>
|
||||
<?php
|
||||
echo "App Version: " . $updates->current_version . "<br>";
|
||||
echo "Cron enabled: " . $config_enable_cron . "<br>";
|
||||
echo "App Timezone: " . $config_timezone;
|
||||
?>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>Database Structure Check</h3>
|
||||
|
||||
@@ -6,6 +6,35 @@ $order = "DESC";
|
||||
|
||||
require_once "inc_all_admin.php";
|
||||
|
||||
// User Filter
|
||||
if (isset($_GET['user']) & !empty($_GET['user'])) {
|
||||
$user_query = 'AND (log_user_id = ' . intval($_GET['user']) . ')';
|
||||
$user = intval($_GET['user']);
|
||||
} else {
|
||||
// Default - any
|
||||
$user_query = '';
|
||||
$user = '';
|
||||
}
|
||||
|
||||
// Log Type Filter
|
||||
if (isset($_GET['type']) & !empty($_GET['type'])) {
|
||||
$log_type_query = "AND (log_type = '" . sanitizeInput($_GET['type']) . "')";
|
||||
$type = nullable_htmlentities($_GET['type']);
|
||||
} else {
|
||||
// Default - any
|
||||
$log_type_query = '';
|
||||
$type = '';
|
||||
}
|
||||
|
||||
// Log Action Filter
|
||||
if (isset($_GET['action']) & !empty($_GET['action'])) {
|
||||
$log_action_query = "AND (log_action = '" . sanitizeInput($_GET['action']) . "')";
|
||||
$action = nullable_htmlentities($_GET['action']);
|
||||
} else {
|
||||
// Default - any
|
||||
$log_action_query = '';
|
||||
$action = '';
|
||||
}
|
||||
|
||||
//Rebuild URL
|
||||
$url_query_strings_sort = http_build_query($get_copy);
|
||||
@@ -17,6 +46,9 @@ $sql = mysqli_query(
|
||||
LEFT JOIN clients ON log_client_id = client_id
|
||||
WHERE (log_type LIKE '%$q%' OR log_action LIKE '%$q%' OR log_description LIKE '%$q%' OR log_ip LIKE '%$q%' OR log_user_agent LIKE '%$q%' OR user_name LIKE '%$q%' OR client_name LIKE '%$q%')
|
||||
AND DATE(log_created_at) BETWEEN '$dtf' AND '$dtt'
|
||||
$user_query
|
||||
$log_type_query
|
||||
$log_action_query
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
|
||||
@@ -40,6 +72,63 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<select class="form-control select2" name="user" onchange="this.form.submit()">
|
||||
<option value="" <?php if ($user == "") { echo "selected"; } ?>>- All Users -</option>
|
||||
|
||||
<?php
|
||||
$sql_users_filter = mysqli_query($mysqli, "SELECT * FROM users ORDER BY user_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_users_filter)) {
|
||||
$user_id = intval($row['user_id']);
|
||||
$user_name = nullable_htmlentities($row['user_name']);
|
||||
?>
|
||||
<option <?php if ($user == $user_id) { echo "selected"; } ?> value="<?php echo $user_id; ?>"><?php echo $user_name; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<select class="form-control select2" name="type" onchange="this.form.submit()">
|
||||
<option value="" <?php if ($type == "") { echo "selected"; } ?>>- All Types -</option>
|
||||
|
||||
<?php
|
||||
$sql_types_filter = mysqli_query($mysqli, "SELECT DISTINCT log_type FROM logs ORDER BY log_type ASC");
|
||||
while ($row = mysqli_fetch_array($sql_types_filter)) {
|
||||
$log_type = nullable_htmlentities($row['log_type']);
|
||||
?>
|
||||
<option <?php if ($type == $log_type) { echo "selected"; } ?>><?php echo $log_type; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<select class="form-control select2" name="action" onchange="this.form.submit()">
|
||||
<option value="" <?php if ($action == "") { echo "selected"; } ?>>- All Actions -</option>
|
||||
|
||||
<?php
|
||||
$sql_actions_filter = mysqli_query($mysqli, "SELECT DISTINCT log_action FROM logs ORDER BY log_action ASC");
|
||||
while ($row = mysqli_fetch_array($sql_actions_filter)) {
|
||||
$log_action = nullable_htmlentities($row['log_action']);
|
||||
?>
|
||||
<option <?php if ($action == $log_action) { echo "selected"; } ?>><?php echo $log_action; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse mt-3 <?php if (!empty($_GET['dtf']) || $_GET['canned_date'] !== "custom" ) { echo "show"; } ?>" id="advancedFilter">
|
||||
<div class="row">
|
||||
|
||||
@@ -87,7 +87,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="text-dark" href="#" data-toggle="modal" data-target="#editProjectTemplateModal<?php echo $project_id; ?>">
|
||||
<a class="text-dark" href="#" data-toggle="modal" data-target="#editProjectTemplateModal<?php echo $project_template_id; ?>">
|
||||
<div class="media">
|
||||
<i class="fa fa-fw fa-2x fa-project-diagram mr-3"></i>
|
||||
<div class="media-body">
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
<p>API Keys</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="admin_bulk_mail.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "admin_bulk_mail.php") {echo "active";} ?>">
|
||||
<i class="nav-icon fas fa-paper-plane"></i>
|
||||
<p>Bulk Mail</p>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!-- TAGS & CATEGORIES Section -->
|
||||
<li class="nav-item has-treeview mt-2 <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['admin_tags.php', 'admin_categories.php', 'admin_taxes.php', 'admin_account_types.php', 'admin_ticket_statuses.php']) ? 'menu-open' : ''); ?>">
|
||||
@@ -76,13 +82,13 @@
|
||||
</a>
|
||||
<ul class="nav nav-treeview">
|
||||
<li class="nav-item">
|
||||
<a href="admin_project_templates.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_project_templates.php' ? 'active' : ''); ?>">
|
||||
<a href="admin_project_templates.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['admin_project_templates.php', 'admin_project_template_details.php']) ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-project-diagram"></i>
|
||||
<p>Project Templates</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="admin_ticket_templates.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_ticket_templates.php' ? 'active' : ''); ?>">
|
||||
<a href="admin_ticket_templates.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['admin_ticket_templates.php', 'admin_ticket_template_details.php']) ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-life-ring"></i>
|
||||
<p>Ticket Templates</p>
|
||||
</a>
|
||||
@@ -100,7 +106,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="admin_document_templates.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_document_templates.php' ? 'active' : ''); ?>">
|
||||
<a href="admin_document_templates.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['admin_document_templates.php', 'admin_document_template_details.php']) ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-file"></i>
|
||||
<p>Document Templates</p>
|
||||
</a>
|
||||
@@ -118,7 +124,7 @@
|
||||
</a>
|
||||
<ul class="nav nav-treeview">
|
||||
<li class="nav-item">
|
||||
<a href="admin_mail_queue.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'admin_mail_queue.php' ? 'active' : ''); ?>">
|
||||
<a href="admin_mail_queue.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['admin_mail_queue.php', 'admin_mail_queue_message_view.php']) ? 'active' : ''); ?>">
|
||||
<i class="nav-icon fas fa-mail-bulk"></i>
|
||||
<p>Mail Queue</p>
|
||||
</a>
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
<option value="">- Type -</option>
|
||||
<option value="1">Client Tag</option>
|
||||
<option value="2">Location Tag</option>
|
||||
<option value="3">Contact Tag</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
<option value="">- Type -</option>
|
||||
<option value="1" <?php if ($tag_type == 1) { echo "selected"; } ?>>Client Tag</option>
|
||||
<option value="2" <?php if ($tag_type == 2) { echo "selected"; } ?>>Location Tag</option>
|
||||
<option value="3" <?php if ($tag_type == 3) { echo "selected"; } ?>>Contact Tag</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -66,6 +66,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
$tag_type_display = "Client Tag";
|
||||
} elseif ( $tag_type == 2) {
|
||||
$tag_type_display = "Location Tag";
|
||||
} elseif ( $tag_type == 3) {
|
||||
$tag_type_display = "Contact Tag";
|
||||
} else {
|
||||
$tag_type_display = "Unknown Tag";
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ $ticket_template_created_at = nullable_htmlentities($row['ticket_template_create
|
||||
$ticket_template_updated_at = nullable_htmlentities($row['ticket_template_updated_at']);
|
||||
|
||||
// Get Task Templates
|
||||
$sql_task_templates = mysqli_query($mysqli, "SELECT * FROM task_templates WHERE task_template_ticket_template_id = $ticket_template_id");
|
||||
$sql_task_templates = mysqli_query($mysqli, "SELECT * FROM task_templates WHERE task_template_ticket_template_id = $ticket_template_id ORDER BY task_template_order ASC, task_template_id ASC");
|
||||
|
||||
?>
|
||||
|
||||
@@ -95,20 +95,27 @@ $sql_task_templates = mysqli_query($mysqli, "SELECT * FROM task_templates WHERE
|
||||
<table class="table table-striped table-sm">
|
||||
<?php
|
||||
while($row = mysqli_fetch_array($sql_task_templates)){
|
||||
$task_template_id = intval($row['task_template_id']);
|
||||
$task_template_name = nullable_htmlentities($row['task_template_name']);
|
||||
$task_template_description = nullable_htmlentities($row['task_template_description']);
|
||||
$task_id = intval($row['task_template_id']);
|
||||
$task_name = nullable_htmlentities($row['task_template_name']);
|
||||
$task_order = intval($row['task_template_order']);
|
||||
$task_description = nullable_htmlentities($row['task_template_description']);
|
||||
?>
|
||||
<tr>
|
||||
<td><i class="far fa-fw fa-square text-secondary"></i></td>
|
||||
<td><?php echo $task_template_name; ?></td>
|
||||
<td><?php echo $task_name; ?></td>
|
||||
<td class="text-right">
|
||||
<a href="post.php?delete_task_template=<?php echo $task_template_id; ?>" class="btn btn-link btn-sm text-secondary">
|
||||
<button type="button" class="btn btn-link btn-sm text-secondary" data-toggle="modal" data-target="#editTaskModal<?php echo $task_id; ?>">
|
||||
<i class="fa fa-fw fa-pencil-alt"></i>
|
||||
</button>
|
||||
<a href="post.php?delete_task_template=<?php echo $task_id; ?>" class="btn btn-link btn-sm text-danger">
|
||||
<i class="fa fa-fw fa-trash-alt"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php
|
||||
require "task_edit_modal.php";
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -70,7 +70,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="text-dark" href="#" data-toggle="modal" data-target="#editTicketTemplateModal<?php echo $ticket_id; ?>">
|
||||
<a class="text-dark" href="#" data-toggle="modal" data-target="#editTicketTemplateModal<?php echo $ticket_template_id; ?>">
|
||||
<div class="media">
|
||||
<i class="fa fa-fw fa-2x fa-life-ring mr-3"></i>
|
||||
<div class="media-body">
|
||||
|
||||
@@ -34,6 +34,7 @@ $git_log = shell_exec("git log $repo_branch..origin/$repo_branch --pretty=format
|
||||
|
||||
<?php if (!empty($git_log)) { ?>
|
||||
<a class="btn btn-primary btn-lg my-4" href="post.php?update"><i class="fas fa-fw fa-4x fa-download mb-1"></i><h5>Update App</h5></a>
|
||||
<a class="btn btn-danger btn-lg" href="post.php?update&force_update=1"><i class="fas fa-fw fa-4x fa-hammer mb-1"></i><h5>FORCE Update App</h5></a>
|
||||
<hr>
|
||||
|
||||
<?php } else {
|
||||
|
||||
@@ -11,70 +11,117 @@
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<div class="modal-body bg-white">
|
||||
|
||||
<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>
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-user-details">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-user-access">Restrict Access</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-user-details">
|
||||
|
||||
<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="name" placeholder="Full Name" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Full Name" required autofocus>
|
||||
|
||||
<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="email" placeholder="Email Address" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Password <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-lock"></i></span>
|
||||
</div>
|
||||
<input type="password" class="form-control" data-toggle="password" name="password" id="password" placeholder="Enter a Password" autocomplete="new-password" required minlength="8">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
|
||||
</div>
|
||||
<div class="input-group-append">
|
||||
<span class="btn btn-default"><i class="fa fa-fw fa-question" onclick="generatePassword()"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Role <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-shield"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="role" required>
|
||||
<option value="">- Role -</option>
|
||||
<?php
|
||||
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE user_role_archived_at IS NULL");
|
||||
while ($row = mysqli_fetch_array($sql_user_roles)) {
|
||||
$user_role_id = intval($row['user_role_id']);
|
||||
$user_role_name = nullable_htmlentities($row['user_role_name']);
|
||||
|
||||
?>
|
||||
<option value="<?php echo $user_role_id; ?>"><?php echo $user_role_name; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Avatar</label>
|
||||
<input type="file" class="form-control-file" accept="image/*;capture=camera" name="file">
|
||||
</div>
|
||||
|
||||
<div class="form-group" <?php if(empty($config_smtp_host)) { echo "hidden"; } ?>>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" id="sendEmailCheckBox" name="send_email" value="" checked>
|
||||
<label for="sendEmailCheckBox" class="custom-control-label">
|
||||
Send user e-mail with login details?
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" id="forceMFACheckBox" name="force_mfa" value=1>
|
||||
<label for="forceMFACheckBox" class="custom-control-label">
|
||||
Force MFA
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</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="email" placeholder="Email Address" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="pills-user-access">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Password <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-lock"></i></span>
|
||||
<div class="alert alert-info">
|
||||
Check boxes to authorize user client access. No boxes grant full client access. Admin users are unaffected.
|
||||
</div>
|
||||
<input type="password" class="form-control" data-toggle="password" name="password" id="password" placeholder="Enter a Password" autocomplete="new-password" required minlength="8">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
|
||||
</div>
|
||||
<div class="input-group-append">
|
||||
<span class="btn btn-default"><i class="fa fa-fw fa-question" onclick="generatePassword()"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Role <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-shield"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="role" required>
|
||||
<option value="">- Role -</option>
|
||||
<?php
|
||||
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE user_role_archived_at IS NULL");
|
||||
while ($row = mysqli_fetch_array($sql_user_roles)) {
|
||||
$user_role_id = intval($row['user_role_id']);
|
||||
$user_role_name = nullable_htmlentities($row['user_role_name']);
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item bg-dark">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" onclick="this.closest('.tab-pane').querySelectorAll('.client-checkbox').forEach(checkbox => checkbox.checked = this.checked);">
|
||||
<label class="form-check-label ml-3"><strong>Restrict Access to Clients</strong></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
?>
|
||||
<option value="<?php echo $user_role_id; ?>"><?php echo $user_role_name; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Restrict Client Access</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-users"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="clients[]" data-placeholder="Restrict Client Access" multiple>
|
||||
<?php
|
||||
|
||||
$sql_client_select = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
|
||||
@@ -82,36 +129,20 @@
|
||||
$client_id = intval($row['client_id']);
|
||||
$client_name = nullable_htmlentities($row['client_name']);
|
||||
|
||||
?>
|
||||
<option value="<?php echo $client_id; ?>"><?php echo $client_name; ?></option>
|
||||
?>
|
||||
<li class="list-group-item">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input client-checkbox" name="clients[]" value="<?php echo $client_id; ?>">
|
||||
<label class="form-check-label ml-3"><?php echo $client_name; ?></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<small class="text-muted">Leave Blank for Full access to all clients, no affect on users with the admin role.</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Avatar</label>
|
||||
<input type="file" class="form-control-file" accept="image/*;capture=camera" name="file">
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
<div class="form-group" <?php if(empty($config_smtp_host)) { echo "hidden"; } ?>>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" id="sendEmailCheckBox" name="send_email" value="" checked>
|
||||
<label for="sendEmailCheckBox" class="custom-control-label">
|
||||
Send user e-mail with login details?
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" id="forceMFACheckBox" name="force_mfa" value=1>
|
||||
<label for="forceMFACheckBox" class="custom-control-label">
|
||||
Force MFA
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -13,81 +13,135 @@
|
||||
<input type="hidden" name="user_id" value="<?php echo $user_id; ?>">
|
||||
<div class="modal-body bg-white">
|
||||
|
||||
<center class="mb-3">
|
||||
<?php if (!empty($user_avatar)) { ?>
|
||||
<img class="img-fluid" src="<?php echo "uploads/users/$user_id/$user_avatar"; ?>">
|
||||
<?php } else { ?>
|
||||
<span class="fa-stack fa-4x">
|
||||
<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 } ?>
|
||||
</center>
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-user-details<?php echo $user_id; ?>">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-user-access<?php echo $user_id; ?>">Restrict Access</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<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="name" placeholder="Full Name"
|
||||
value="<?php echo $user_name; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
<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="email" placeholder="Email Address"
|
||||
value="<?php echo $user_email; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
|
||||
<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" data-toggle="password" name="new_password"
|
||||
placeholder="Leave Blank For No Password Change" autocomplete="new-password">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade show active" id="pills-user-details<?php echo $user_id; ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Role <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-shield"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="role" required>
|
||||
<?php
|
||||
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE user_role_archived_at IS NULL");
|
||||
while ($row = mysqli_fetch_array($sql_user_roles)) {
|
||||
$user_role_id = intval($row['user_role_id']);
|
||||
$user_role_name = nullable_htmlentities($row['user_role_name']);
|
||||
|
||||
?>
|
||||
<option <?php if ($user_role == $user_role_id) {echo "selected";} ?> value="<?php echo $user_role_id; ?>"><?php echo $user_role_name; ?></option>
|
||||
<center class="mb-3">
|
||||
<?php if (!empty($user_avatar)) { ?>
|
||||
<img class="img-fluid" src="<?php echo "uploads/users/$user_id/$user_avatar"; ?>">
|
||||
<?php } else { ?>
|
||||
<span class="fa-stack fa-4x">
|
||||
<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 } ?>
|
||||
</center>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Restrict Client Access</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-users"></i></span>
|
||||
<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="name" placeholder="Full Name"
|
||||
value="<?php echo $user_name; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<select class="form-control select2" name="clients[]" data-placeholder="Restrict Client Access" multiple>
|
||||
|
||||
<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="email" placeholder="Email Address"
|
||||
value="<?php echo $user_email; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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" data-toggle="password" name="new_password"
|
||||
placeholder="Leave Blank For No Password Change" autocomplete="new-password">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Role <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-shield"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="role" required>
|
||||
<?php
|
||||
$sql_user_roles = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE user_role_archived_at IS NULL");
|
||||
while ($row = mysqli_fetch_array($sql_user_roles)) {
|
||||
$user_role_id = intval($row['user_role_id']);
|
||||
$user_role_name = nullable_htmlentities($row['user_role_name']);
|
||||
|
||||
?>
|
||||
<option <?php if ($user_role == $user_role_id) {echo "selected";} ?> value="<?php echo $user_role_id; ?>"><?php echo $user_role_name; ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Avatar</label>
|
||||
<input type="file" class="form-control-file" accept="image/*;capture=camera" name="file">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" id="forceMFACheckBox<?php echo $user_id; ?>" name="force_mfa" value="1" <?php if($user_config_force_mfa == 1){ echo "checked"; } ?>>
|
||||
<label for="forceMFACheckBox<?php echo $user_id; ?>" class="custom-control-label">
|
||||
Force MFA
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($user_token)) { ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>2FA</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-id-card"></i></span>
|
||||
</div>
|
||||
<select class="form-control" name="2fa">
|
||||
<option value="">Keep enabled</option>
|
||||
<option value="disable">Disable</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-user-access<?php echo $user_id; ?>">
|
||||
|
||||
<div class="alert alert-info">
|
||||
Check boxes to authorize user client access. No boxes grant full client access. Admin users are unaffected.
|
||||
</div>
|
||||
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item bg-dark">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" onclick="this.closest('.tab-pane').querySelectorAll('.client-checkbox').forEach(checkbox => checkbox.checked = this.checked);">
|
||||
<label class="form-check-label ml-3"><strong>Restrict Access to Clients</strong></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<?php
|
||||
|
||||
$sql_client_select = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
|
||||
@@ -95,45 +149,22 @@
|
||||
$client_id_select = intval($row['client_id']);
|
||||
$client_name_select = nullable_htmlentities($row['client_name']);
|
||||
|
||||
?>
|
||||
<option <?php if (in_array($client_id_select, $client_access_array)) { echo "selected"; } ?> value="<?php echo $client_id_select; ?>"><?php echo $client_name_select; ?></option>
|
||||
?>
|
||||
|
||||
<li class="list-group-item">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input client-checkbox" name="clients[]" value="<?php echo $client_id_select; ?>" <?php if (in_array($client_id_select, $client_access_array)) { echo "checked"; } ?>>
|
||||
<label class="form-check-label ml-2"><?php echo $client_name_select; ?></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<small class="text-muted">Leave Blank for Full access to all clients, no affect on users with the admin role.</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Avatar</label>
|
||||
<input type="file" class="form-control-file" accept="image/*;capture=camera" name="file">
|
||||
</div>
|
||||
</ul>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input class="custom-control-input" type="checkbox" id="forceMFACheckBox<?php echo $user_id; ?>" name="force_mfa" value="1" <?php if($user_config_force_mfa == 1){ echo "checked"; } ?>>
|
||||
<label for="forceMFACheckBox<?php echo $user_id; ?>" class="custom-control-label">
|
||||
Force MFA
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($user_token)) { ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>2FA</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-id-card"></i></span>
|
||||
</div>
|
||||
<select class="form-control" name="2fa">
|
||||
<option value="">Keep enabled</option>
|
||||
<option value="disable">Disable</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer bg-white">
|
||||
|
||||
@@ -57,17 +57,17 @@ if (isset($_POST['asset_os'])) {
|
||||
}
|
||||
|
||||
if (isset($_POST['asset_ip'])) {
|
||||
$aip = sanitizeInput($_POST['asset_ip']);
|
||||
} elseif (isset($asset_row) && isset($asset_row['asset_ip'])) {
|
||||
$aip = $asset_row['asset_ip'];
|
||||
$ip = sanitizeInput($_POST['asset_ip']);
|
||||
} elseif (isset($asset_row) && isset($asset_row['interface_ip'])) {
|
||||
$ip = $asset_row['interface_ip'];
|
||||
} else {
|
||||
$aip = '';
|
||||
$ip = '';
|
||||
}
|
||||
|
||||
if (isset($_POST['asset_mac'])) {
|
||||
$mac = sanitizeInput($_POST['asset_mac']);
|
||||
} elseif (isset($asset_row) && isset($asset_row['asset_mac'])) {
|
||||
$mac = $asset_row['asset_mac'];
|
||||
} elseif (isset($asset_row) && isset($asset_row['interface_mac'])) {
|
||||
$mac = $asset_row['interface_mac'];
|
||||
} else {
|
||||
$mac = '';
|
||||
}
|
||||
@@ -146,8 +146,8 @@ if (isset($_POST['asset_contact_id'])) {
|
||||
|
||||
if (isset($_POST['asset_network_id'])) {
|
||||
$network = intval($_POST['asset_network_id']);
|
||||
} elseif (isset($asset_row) && isset($asset_row['asset_network_id'])) {
|
||||
$network = $asset_row['asset_network_id'];
|
||||
} elseif (isset($asset_row) && isset($asset_row['interface_network_id'])) {
|
||||
$network = $asset_row['interface_network_id'];
|
||||
} else {
|
||||
$network = '0';
|
||||
}
|
||||
|
||||
@@ -14,11 +14,14 @@ $insert_id = false;
|
||||
|
||||
if (!empty($name) && !empty($client_id)) {
|
||||
// Insert into Database
|
||||
$insert_sql = mysqli_query($mysqli, "INSERT INTO assets SET asset_name = '$name', asset_description = '$description', asset_type = '$type', asset_make = '$make', asset_model = '$model', asset_serial = '$serial', asset_os = '$os', asset_ip = '$aip', asset_mac = '$mac', asset_uri = '$uri', asset_status = '$status', asset_location_id = $location, asset_vendor_id = $vendor, asset_contact_id = $contact, asset_purchase_date = $purchase_date, asset_warranty_expire = $warranty_expire, asset_install_date = $install_date, asset_notes = '$notes', asset_network_id = $network, asset_client_id = $client_id");
|
||||
$insert_sql = mysqli_query($mysqli, "INSERT INTO assets SET asset_name = '$name', asset_description = '$description', asset_type = '$type', asset_make = '$make', asset_model = '$model', asset_serial = '$serial', asset_os = '$os', asset_uri = '$uri', asset_status = '$status', asset_location_id = $location, asset_vendor_id = $vendor, asset_contact_id = $contact, asset_purchase_date = $purchase_date, asset_warranty_expire = $warranty_expire, asset_install_date = $install_date, asset_notes = '$notes', asset_client_id = $client_id");
|
||||
|
||||
if ($insert_sql) {
|
||||
$insert_id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Add Primary Interface
|
||||
mysqli_query($mysqli,"INSERT INTO asset_interfaces SET interface_name = 'Primary', interface_mac = '$mac', interface_ip = '$ip', interface_port = 'eth0', interface_primary = 1, interface_network_id = $network, interface_asset_id = $insert_id");
|
||||
|
||||
//Logging
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Asset', log_action = 'Created', log_description = '$name via API ($api_key_name)', log_ip = '$ip', log_user_agent = '$user_agent', log_client_id = '$client_id'");
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'API', log_action = 'Success', log_description = 'Created asset $name via API ($api_key_name)', log_ip = '$ip', log_user_agent = '$user_agent', log_client_id = '$client_id'");
|
||||
|
||||
@@ -17,6 +17,9 @@ if (!empty($asset_id)) {
|
||||
|
||||
$delete_sql = mysqli_query($mysqli, "DELETE FROM assets WHERE asset_id = $asset_id AND asset_client_id = $client_id LIMIT 1");
|
||||
|
||||
// Delete Interfaces
|
||||
mysqli_query($mysqli,"DELETE FROM asset_interfaces WHERE interface_asset_id = $asset_id");
|
||||
|
||||
// Check delete & get affected rows
|
||||
if ($delete_sql && !empty($asset_name)) {
|
||||
$delete_count = mysqli_affected_rows($mysqli);
|
||||
|
||||
@@ -37,7 +37,7 @@ if (isset($_GET['asset_id'])) {
|
||||
// Asset query via mac
|
||||
|
||||
$mac = mysqli_real_escape_string($mysqli, $_GET['asset_mac']);
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM assets WHERE asset_mac = '$mac' AND asset_client_id LIKE '$client_id' ORDER BY asset_id LIMIT $limit OFFSET $offset");
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM assets LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 WHERE interface_mac = '$mac' AND asset_client_id LIKE '$client_id' ORDER BY asset_id LIMIT $limit OFFSET $offset");
|
||||
|
||||
} elseif (isset($_GET['asset_uri'])) {
|
||||
// Asset query via mac
|
||||
@@ -48,7 +48,7 @@ if (isset($_GET['asset_id'])) {
|
||||
}
|
||||
// All assets
|
||||
else {
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM assets WHERE asset_client_id LIKE '$client_id' ORDER BY asset_id LIMIT $limit OFFSET $offset");
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM assets LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 WHERE asset_client_id LIKE '$client_id' ORDER BY asset_id LIMIT $limit OFFSET $offset");
|
||||
}
|
||||
|
||||
// Output
|
||||
|
||||
@@ -19,12 +19,15 @@ if (!empty($asset_id)) {
|
||||
require_once 'asset_model.php';
|
||||
|
||||
|
||||
$update_sql = mysqli_query($mysqli, "UPDATE assets SET asset_name = '$name', asset_description = '$description', asset_type = '$type', asset_make = '$make', asset_model = '$model', asset_serial = '$serial', asset_os = '$os', asset_ip = '$aip', asset_mac = '$mac', asset_uri = '$uri', asset_status = '$status', asset_location_id = $location, asset_vendor_id = $vendor, asset_contact_id = $contact, asset_purchase_date = $purchase_date, asset_warranty_expire = $warranty_expire, asset_install_date = $install_date, asset_notes = '$notes', asset_network_id = $network WHERE asset_id = $asset_id AND asset_client_id = $client_id LIMIT 1");
|
||||
$update_sql = mysqli_query($mysqli, "UPDATE assets SET asset_name = '$name', asset_description = '$description', asset_type = '$type', asset_make = '$make', asset_model = '$model', asset_serial = '$serial', asset_os = '$os', asset_uri = '$uri', asset_status = '$status', asset_location_id = $location, asset_vendor_id = $vendor, asset_contact_id = $contact, asset_purchase_date = $purchase_date, asset_warranty_expire = $warranty_expire, asset_install_date = $install_date, asset_notes = '$notes' WHERE asset_id = $asset_id AND asset_client_id = $client_id LIMIT 1");
|
||||
|
||||
// Check insert & get insert ID
|
||||
if ($update_sql) {
|
||||
$update_count = mysqli_affected_rows($mysqli);
|
||||
|
||||
// Update Primary Interface
|
||||
mysqli_query($mysqli,"UPDATE asset_interfaces SET interface_mac = '$mac', interface_ip = '$ip', interface_network_id = $network WHERE interface_asset_id = $asset_id AND interface_primary = 1");
|
||||
|
||||
//Logging
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Asset', log_action = 'Updated', log_description = '$name via API ($api_key_name)', log_ip = '$ip', log_user_agent = '$user_agent', log_client_id = $client_id");
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'API', log_action = 'Success', log_description = 'Updated asset $name via API ($api_key_name)', log_ip = '$ip', log_user_agent = '$user_agent', log_client_id = $client_id");
|
||||
|
||||
234
budget.php
234
budget.php
@@ -1,154 +1,110 @@
|
||||
<?php
|
||||
|
||||
// Default Column Sortby/Order Filter
|
||||
$sort = "budget_year";
|
||||
$order = "DESC";
|
||||
|
||||
require_once "inc_all.php";
|
||||
|
||||
// Fetch categories
|
||||
$query = "SELECT category_id, category_name FROM categories WHERE category_type ='Expense' AND category_archived_at IS NULL";
|
||||
$result = mysqli_query($mysqli, $query);
|
||||
$categories = [];
|
||||
while($row = mysqli_fetch_assoc($result)) {
|
||||
$categories[] = $row;
|
||||
}
|
||||
|
||||
//Rebuild URL
|
||||
$url_query_strings_sort = http_build_query($get_copy);
|
||||
// Fetch years with budget
|
||||
$query = "SELECT DISTINCT budget_year FROM budget ORDER BY budget_year ASC";
|
||||
$result = mysqli_query($mysqli, $query);
|
||||
$years = [];
|
||||
while ($row = mysqli_fetch_assoc($result)) {
|
||||
$years[] = $row['budget_year'];
|
||||
}
|
||||
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM budget
|
||||
LEFT JOIN categories ON budget_category_id = category_id
|
||||
AND DATE(budget_created_at) BETWEEN '$dtf' AND '$dtt'
|
||||
AND (budget_description LIKE '%$q%' OR budget_amount LIKE '%$q%' OR budget_month LIKE '%$q%' OR budget_year LIKE '%$q%' OR category_name LIKE '%$q%')
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
// Fetch current year budgets
|
||||
$currentYear = date("Y");
|
||||
if (isset($_GET['year'])) {
|
||||
$currentYear = intval($_GET['year']);
|
||||
}
|
||||
|
||||
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
$query = "SELECT * FROM budget WHERE budget_year = $currentYear";
|
||||
$result = mysqli_query($mysqli, $query);
|
||||
$budgets = [];
|
||||
while ($row = mysqli_fetch_assoc($result)) {
|
||||
$budgets[] = $row;
|
||||
}
|
||||
|
||||
$months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
$columnTotals = array_fill(0, 12, 0);
|
||||
$grandTotal = 0;
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fas fa-fw fa-balance-scale mr-2"></i>Budget</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#createBudgetModal"><i class="fas fa-plus mr-2"></i>Create</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<form class="mb-4" autocomplete="off">
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group">
|
||||
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search...">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#advancedFilter"><i class="fas fa-filter"></i></button>
|
||||
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse mt-3 <?php if (!empty($_GET['dtf']) || $_GET['canned_date'] !== "custom" ) { echo "show"; } ?>" id="advancedFilter">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<label>Canned Date</label>
|
||||
<select onchange="this.form.submit()" class="form-control select2" name="canned_date">
|
||||
<option <?php if ($_GET['canned_date'] == "custom") { echo "selected"; } ?> value="">Custom</option>
|
||||
<option <?php if ($_GET['canned_date'] == "today") { echo "selected"; } ?> value="today">Today</option>
|
||||
<option <?php if ($_GET['canned_date'] == "yesterday") { echo "selected"; } ?> value="yesterday">Yesterday</option>
|
||||
<option <?php if ($_GET['canned_date'] == "thisweek") { echo "selected"; } ?> value="thisweek">This Week</option>
|
||||
<option <?php if ($_GET['canned_date'] == "lastweek") { echo "selected"; } ?> value="lastweek">Last Week</option>
|
||||
<option <?php if ($_GET['canned_date'] == "thismonth") { echo "selected"; } ?> value="thismonth">This Month</option>
|
||||
<option <?php if ($_GET['canned_date'] == "lastmonth") { echo "selected"; } ?> value="lastmonth">Last Month</option>
|
||||
<option <?php if ($_GET['canned_date'] == "thisyear") { echo "selected"; } ?> value="thisyear">This Year</option>
|
||||
<option <?php if ($_GET['canned_date'] == "lastyear") { echo "selected"; } ?> value="lastyear">Last Year</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<label>Date From</label>
|
||||
<input onchange="this.form.submit()" type="date" class="form-control" name="dtf" max="2999-12-31" value="<?php echo nullable_htmlentities($dtf); ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<label>Date To</label>
|
||||
<input onchange="this.form.submit()" type="date" class="form-control" name="dtt" max="2999-12-31" value="<?php echo nullable_htmlentities($dtt); ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=budget_year&order=<?php echo $disp; ?>">Year</a></th>
|
||||
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=budget_month&order=<?php echo $disp; ?>">Month</a></th>
|
||||
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=category_name&order=<?php echo $disp; ?>">Category</a></th>
|
||||
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=budget_description&order=<?php echo $disp; ?>">Description</a></th>
|
||||
<th class="text-right"><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=budget_amount&order=<?php echo $disp; ?>">Amount</a></th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$budget_id = intval($row['budget_id']);
|
||||
$budget_description = nullable_htmlentities($row['budget_description']);
|
||||
$budget_year = intval($row['budget_year']);
|
||||
$budget_month = intval($row['budget_month']);
|
||||
$budget_amount = floatval($row['budget_amount']);
|
||||
$budget_category_id = intval($row['budget_category_id']);
|
||||
$category_name = nullable_htmlentities($row['category_name']);
|
||||
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td><a class="text-dark" href="#" data-toggle="modal" data-target="#editBudgetModal<?php echo $budget_id; ?>"><?php echo $budget_year; ?></a></td>
|
||||
<td><?php echo $budget_month; ?></td>
|
||||
<td><?php echo $category_name; ?></td>
|
||||
<td><?php echo truncate($budget_description, 50); ?></td>
|
||||
<td class="text-bold text-right"><?php echo numfmt_format_currency($currency_format, $budget_amount, $session_company_currency); ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editBudgetModal<?php echo $budget_id; ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_budget=<?php echo $budget_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
require "budget_edit_modal.php";
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php require_once "pagination.php";
|
||||
?>
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fas fa-fw fa-balance-scale mr-2"></i>Budget for <span id="currentYear"><?php echo $currentYear; ?></span></h3>
|
||||
<div class="card-tools">
|
||||
<a href="budget_edit.php" class="btn btn-primary">
|
||||
<i class="fas fa-edit mr-2"></i>Edit Budget
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="yearForm" method="GET" action="budget.php">
|
||||
<div class="form-group">
|
||||
<select class="form-control" name="year" id="yearSelect" onchange="submit();">
|
||||
<?php foreach ($years as $year): ?>
|
||||
<option value="<?php echo $year; ?>" <?php if ($year == $currentYear) echo 'selected'; ?>><?php echo $year; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Expense</th>
|
||||
<?php foreach ($months as $month): ?>
|
||||
<th><?php echo $month; ?></th>
|
||||
<?php endforeach; ?>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($categories as $category): ?>
|
||||
<tr>
|
||||
<td><?php echo nullable_htmlentities($category['category_name']); ?></td>
|
||||
<?php
|
||||
$rowTotal = 0;
|
||||
foreach ($months as $index => $month):
|
||||
$amount = getBudgetAmount($budgets, $category['category_id'], $index + 1);
|
||||
$rowTotal += $amount;
|
||||
$columnTotals[$index] += $amount;
|
||||
?>
|
||||
<td><?php echo $amount; ?></td>
|
||||
<?php endforeach; ?>
|
||||
<td><?php echo $rowTotal; ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
$grandTotal += $rowTotal;
|
||||
endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th>Total</th>
|
||||
<?php foreach ($columnTotals as $total): ?>
|
||||
<th><?php echo $total; ?></th>
|
||||
<?php endforeach; ?>
|
||||
<th><?php echo $grandTotal; ?></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once "budget_create_modal.php";
|
||||
function getBudgetAmount($budgets, $categoryId, $month) {
|
||||
foreach ($budgets as $budget) {
|
||||
if ($budget['budget_category_id'] == $categoryId && $budget['budget_month'] == $month) {
|
||||
return intval($budget['budget_amount']);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
require_once "footer.php";
|
||||
|
||||
?>
|
||||
|
||||
110
budget_edit.php
Normal file
110
budget_edit.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
require_once "inc_all.php";
|
||||
|
||||
// Fetch categories
|
||||
$query = "SELECT category_id, category_name FROM categories WHERE category_type ='Expense' AND category_archived_at IS NULL";
|
||||
$result = mysqli_query($mysqli, $query);
|
||||
$categories = [];
|
||||
while($row = mysqli_fetch_assoc($result)) {
|
||||
$categories[] = $row;
|
||||
}
|
||||
|
||||
// Fetch current year budgets
|
||||
$currentYear = date("Y");
|
||||
if(isset($_GET['year'])) {
|
||||
$currentYear = intval($_GET['year']);
|
||||
}
|
||||
|
||||
$query = "SELECT * FROM budget WHERE budget_year = $currentYear";
|
||||
$result = mysqli_query($mysqli, $query);
|
||||
$budgets = [];
|
||||
while($row = mysqli_fetch_assoc($result)) {
|
||||
$budgets[] = $row;
|
||||
}
|
||||
|
||||
$months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||||
$columnTotals = array_fill(0, 12, 0);
|
||||
$grandTotal = 0;
|
||||
|
||||
?>
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fas fa-fw fa-balance-scale mr-2"></i>Editing Budget for <span id="currentYear"><?php echo $currentYear; ?></span></h3>
|
||||
<div class="card-tools">
|
||||
<a href="budget.php" class="btn btn-default text-dark">
|
||||
<i class="fas fa-eye mr-2"></i>View Budget
|
||||
</a>
|
||||
<button type="submit" name="save_budget" form="budgetForm" class="btn btn-primary"><i class="fas fa-fw fa-check mr-2"></i>Save Budget</button>
|
||||
<button type="submit" name="delete_budget" form="budgetForm" class="btn btn-danger"><i class="fas fa-fw fa-trash mr-2"></i>Delete Budget</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<form id="yearForm" method="GET" action="budget.php">
|
||||
<div class="form-group">
|
||||
<select class="form-control" name="year" id="yearSelect" onchange="submit();">
|
||||
<?php for ($i = $currentYear - 10; $i <= $currentYear + 5; $i++): ?>
|
||||
<option value="<?php echo $i; ?>" <?php if ($i == $currentYear) echo 'selected'; ?>><?php echo $i; ?></option>
|
||||
<?php endfor; ?>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
<form id="budgetForm" method="POST" action="post.php">
|
||||
<input type="hidden" name="year" value="<?php echo $currentYear; ?>">
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Expense</th>
|
||||
<?php foreach ($months as $month): ?>
|
||||
<th><?php echo $month; ?></th>
|
||||
<?php endforeach; ?>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($categories as $category): ?>
|
||||
<tr>
|
||||
<td><?php echo nullable_htmlentities($category['category_name']); ?></td>
|
||||
<?php
|
||||
$rowTotal = 0;
|
||||
foreach ($months as $index => $month):
|
||||
$amount = getBudgetAmount($budgets, $category['category_id'], $index + 1);
|
||||
$rowTotal += $amount;
|
||||
$columnTotals[$index] += $amount;
|
||||
?>
|
||||
<td><input type='text' inputmode='numeric' pattern='[0-9]*' class="form-control" name="budget[<?php echo intval($category['category_id']); ?>][<?php echo $index + 1; ?>]" value="<?php echo $amount; ?>"></td>
|
||||
<?php endforeach; ?>
|
||||
<td><?php echo $rowTotal; ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
$grandTotal += $rowTotal;
|
||||
endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th>Total</th>
|
||||
<?php foreach ($columnTotals as $total): ?>
|
||||
<th><?php echo $total; ?></th>
|
||||
<?php endforeach; ?>
|
||||
<th><?php echo $grandTotal; ?></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
function getBudgetAmount($budgets, $categoryId, $month) {
|
||||
foreach ($budgets as $budget) {
|
||||
if ($budget['budget_category_id'] == $categoryId && $budget['budget_month'] == $month) {
|
||||
return intval($budget['budget_amount']);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
require_once "footer.php";
|
||||
?>
|
||||
42
calendar_edit_modal.php
Normal file
42
calendar_edit_modal.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<div class="modal" id="editCalendarModal<?php echo $calendar_id; ?>" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content bg-dark">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-fw fa-circle mr-2" style="color:<?php echo $calendar_color; ?>"></i><?php echo $calendar_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="calendar_id" value="<?php echo $calendar_id; ?>">
|
||||
<div class="modal-body bg-white">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Name</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Name your calendar" value="<?php echo $calendar_name; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Color <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-paint-brush"></i></span>
|
||||
</div>
|
||||
<input type="color" class="form-control col-3" name="color" value="<?php echo $calendar_color; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer bg-white">
|
||||
<button type="submit" name="edit_calendar" class="btn btn-primary"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -16,9 +16,58 @@ if (isset($_GET['calendar_id'])) {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<div class="card">
|
||||
<div id='calendar'></div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="card">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-1">Calendars</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-dark btn-sm" data-toggle="modal" data-target="#addCalendarModal"><i class="fas fa-plus"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<form>
|
||||
<?php
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM calendars");
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$calendar_id = intval($row['calendar_id']);
|
||||
$calendar_name = nullable_htmlentities($row['calendar_name']);
|
||||
$calendar_color = nullable_htmlentities($row['calendar_color']);
|
||||
?>
|
||||
<div class="form-group">
|
||||
<i class="fas fa-fw fa-circle mr-2" style="color:<?php echo $calendar_color; ?>;"></i><?php echo $calendar_name; ?>
|
||||
<button type="button" class="btn btn-link btn-sm float-right" data-toggle="modal" data-target="#editCalendarModal<?php echo $calendar_id; ?>"><i class="fas fa-fw fa-pencil-alt text-secondary"></i></button>
|
||||
</div>
|
||||
<?php
|
||||
require "calendar_edit_modal.php";
|
||||
}
|
||||
?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-1">System Calendars</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-dark btn-sm"><i class="fas fa-eye"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-9">
|
||||
<div class="card">
|
||||
<div id='calendar'></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
require_once "calendar_event_add_modal.php";
|
||||
@@ -59,25 +108,18 @@ while ($row = mysqli_fetch_array($sql)) {
|
||||
themeSystem: 'bootstrap',
|
||||
defaultView: 'dayGridMonth',
|
||||
customButtons: {
|
||||
addEvent: {
|
||||
text: 'Add Event',
|
||||
bootstrapFontAwesome: 'fa fa-plus',
|
||||
newEvent: {
|
||||
text: 'New Event',
|
||||
bootstrapFontAwesome: 'fas fa-plus',
|
||||
click: function() {
|
||||
$("#addCalendarEventModal").modal();
|
||||
}
|
||||
},
|
||||
addCalendar: {
|
||||
text: 'Add Calendar',
|
||||
bootstrapFontAwesome: 'fa fa-calendar-plus',
|
||||
click: function() {
|
||||
$("#addCalendarModal").modal();
|
||||
}
|
||||
}
|
||||
},
|
||||
headerToolbar: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth addEvent addCalendar'
|
||||
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth newEvent'
|
||||
},
|
||||
<?php if (!$session_mobile) {
|
||||
?>aspectRatio: 2.5,
|
||||
|
||||
@@ -82,7 +82,7 @@ try {
|
||||
|
||||
// Role / Client Access Permission Check
|
||||
if ($session_user_role < 3 && !empty($client_access_string)) {
|
||||
$access_permission_query = "AND client_id IN ($client_access_string)";
|
||||
$access_permission_query = "AND clients.client_id IN ($client_access_string)";
|
||||
} else {
|
||||
$access_permission_query = "";
|
||||
}
|
||||
|
||||
@@ -291,6 +291,16 @@
|
||||
|
||||
<div class="tab-pane fade" id="pills-more">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Abbreviation</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-id-badge"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="abbreviation" placeholder="Abbreviated name for client" maxlength="6" oninput="this.value = this.value.toUpperCase()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Notes</label>
|
||||
<textarea class="form-control" rows="6" name="notes" placeholder="Enter some notes"></textarea>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
|
||||
@@ -174,6 +174,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>IPv6 Address</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="ipv6" placeholder="ex. 2001:0db8:0000:0000:0000:ff00:0042:8329">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>MAC Address</label>
|
||||
<div class="input-group">
|
||||
@@ -208,6 +218,16 @@
|
||||
|
||||
<div class="tab-pane fade" id="pills-assignment">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Physical Location</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="physical_location" placeholder="Physical location eg. Floor 2, Closet B">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Location</label>
|
||||
<div class="input-group">
|
||||
@@ -353,6 +373,11 @@
|
||||
|
||||
<div class="tab-pane fade" id="pills-notes">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Upload Photo</label>
|
||||
<input type="file" class="form-control-file" name="file">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"></textarea>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
|
||||
<div class="modal-body bg-white">
|
||||
@@ -171,6 +171,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>IPv6 Address</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="ipv6" value="<?php echo $asset_ipv6; ?>" placeholder="ex. 2001:0db8:0000:0000:0000:ff00:0042:8329">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>MAC Address</label>
|
||||
<div class="input-group">
|
||||
@@ -205,6 +215,16 @@
|
||||
|
||||
<div class="tab-pane fade" id="pillsAssignmentCopy<?php echo $asset_id; ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Physical Location</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="physical_location" placeholder="Physical location eg. Floor 2, Closet B">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Location</label>
|
||||
<div class="input-group">
|
||||
@@ -349,6 +369,11 @@
|
||||
|
||||
<div class="tab-pane fade" id="pillsNotesCopy<?php echo $asset_id; ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Upload Photo</label>
|
||||
<input type="file" class="form-control-file" name="file">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"><?php echo $asset_notes; ?></textarea>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,8 @@ if (isset($_GET['asset_id'])) {
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM assets
|
||||
LEFT JOIN contacts ON asset_contact_id = contact_id
|
||||
LEFT JOIN locations ON asset_location_id = location_id
|
||||
LEFT JOIN locations ON asset_location_id = location_id
|
||||
LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1
|
||||
WHERE asset_id = $asset_id
|
||||
AND asset_client_id = $client_id
|
||||
");
|
||||
@@ -22,21 +23,25 @@ if (isset($_GET['asset_id'])) {
|
||||
$asset_model = nullable_htmlentities($row['asset_model']);
|
||||
$asset_serial = nullable_htmlentities($row['asset_serial']);
|
||||
$asset_os = nullable_htmlentities($row['asset_os']);
|
||||
$asset_ip = nullable_htmlentities($row['asset_ip']);
|
||||
$asset_nat_ip = nullable_htmlentities($row['asset_nat_ip']);
|
||||
$asset_mac = nullable_htmlentities($row['asset_mac']);
|
||||
$asset_uri = nullable_htmlentities($row['asset_uri']);
|
||||
$asset_uri_2 = nullable_htmlentities($row['asset_uri_2']);
|
||||
$asset_status = nullable_htmlentities($row['asset_status']);
|
||||
$asset_purchase_date = nullable_htmlentities($row['asset_purchase_date']);
|
||||
$asset_warranty_expire = nullable_htmlentities($row['asset_warranty_expire']);
|
||||
$asset_install_date = nullable_htmlentities($row['asset_install_date']);
|
||||
$asset_photo = nullable_htmlentities($row['asset_photo']);
|
||||
$asset_physical_location = nullable_htmlentities($row['asset_physical_location']);
|
||||
$asset_notes = nullable_htmlentities($row['asset_notes']);
|
||||
$asset_created_at = nullable_htmlentities($row['asset_created_at']);
|
||||
$asset_vendor_id = intval($row['asset_vendor_id']);
|
||||
$asset_location_id = intval($row['asset_location_id']);
|
||||
$asset_contact_id = intval($row['asset_contact_id']);
|
||||
$asset_network_id = intval($row['asset_network_id']);
|
||||
|
||||
$asset_ip = nullable_htmlentities($row['interface_ip']);
|
||||
$asset_ipv6 = nullable_htmlentities($row['interface_ipv6']);
|
||||
$asset_nat_ip = nullable_htmlentities($row['interface_nat_ip']);
|
||||
$asset_mac = nullable_htmlentities($row['interface_mac']);
|
||||
$asset_network_id = intval($row['interface_network_id']);
|
||||
|
||||
$device_icon = getAssetIcon($asset_type);
|
||||
|
||||
@@ -79,6 +84,16 @@ if (isset($_GET['asset_id'])) {
|
||||
);
|
||||
$document_count = mysqli_num_rows($sql_related_documents);
|
||||
|
||||
// Network Interfaces
|
||||
$sql_related_interfaces = mysqli_query($mysqli, "SELECT * FROM asset_interfaces
|
||||
LEFT JOIN assets ON asset_id = interface_asset_id
|
||||
LEFT JOIN networks ON network_id = interface_network_id
|
||||
WHERE asset_id = $asset_id
|
||||
AND interface_archived_at IS NULL
|
||||
ORDER BY interface_name DESC"
|
||||
);
|
||||
$interface_count = mysqli_num_rows($sql_related_interfaces);
|
||||
|
||||
// Related Files
|
||||
$sql_related_files = mysqli_query($mysqli, "SELECT * FROM asset_files
|
||||
LEFT JOIN files ON asset_files.file_id = files.file_id
|
||||
@@ -86,7 +101,19 @@ if (isset($_GET['asset_id'])) {
|
||||
AND file_archived_at IS NULL
|
||||
ORDER BY file_name DESC"
|
||||
);
|
||||
$file_count = mysqli_num_rows($sql_related_files);
|
||||
$files_count = mysqli_num_rows($sql_related_files);
|
||||
// View Mode -- 0 List, 1 Thumbnail
|
||||
if (!empty($_GET['view'])) {
|
||||
$view = intval($_GET['view']);
|
||||
} else {
|
||||
$view = 0;
|
||||
}
|
||||
if ($view == 1) {
|
||||
$query_images = "AND (file_ext LIKE 'JPG' OR file_ext LIKE 'jpg' OR file_ext LIKE 'JPEG' OR file_ext LIKE 'jpeg' OR file_ext LIKE 'png' OR file_ext LIKE 'PNG' OR file_ext LIKE 'webp' OR file_ext LIKE 'WEBP')";
|
||||
} else {
|
||||
$query_images = '';
|
||||
}
|
||||
|
||||
|
||||
// Related Logins Query
|
||||
$sql_related_logins = mysqli_query($mysqli, "SELECT * FROM logins
|
||||
@@ -108,6 +135,8 @@ if (isset($_GET['asset_id'])) {
|
||||
|
||||
$software_count = mysqli_num_rows($sql_related_software);
|
||||
|
||||
|
||||
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
@@ -120,6 +149,9 @@ if (isset($_GET['asset_id'])) {
|
||||
<i class="fas fa-fw fa-edit"></i>
|
||||
</button>
|
||||
<h3 class="text-bold"><i class="fa fa-fw text-secondary fa-<?php echo $device_icon; ?> mr-3"></i><?php echo $asset_name; ?></h3>
|
||||
<?php if ($asset_photo) { ?>
|
||||
<img class="img-fluid img-circle p-3" alt="asset_photo" src="<?php echo "uploads/clients/$client_id/$asset_photo"; ?>">
|
||||
<?php } ?>
|
||||
<?php if ($asset_description) { ?>
|
||||
<div class="text-secondary"><?php echo $asset_description; ?></div>
|
||||
<?php } ?>
|
||||
@@ -167,7 +199,7 @@ if (isset($_GET['asset_id'])) {
|
||||
<div class="mt-2"><i class="fa fa-fw fa-link text-secondary mr-3"></i><a href="<?php echo $asset_uri; ?>" target="_blank">Link</a></div>
|
||||
<?php }
|
||||
if ($asset_uri_2) { ?>
|
||||
<div class="mt-2"><i class="fa fa-fw fa-link text-secondary mr-3"></i><a href="<?php echo $asset_uri; ?>" target="_blank">Link 2</a></div>
|
||||
<div class="mt-2"><i class="fa fa-fw fa-link text-secondary mr-3"></i><a href="<?php echo $asset_uri_2; ?>" target="_blank">Link 2</a></div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -223,6 +255,109 @@ if (isset($_GET['asset_id'])) {
|
||||
<li class="breadcrumb-item active"><?php echo $asset_name; ?></li>
|
||||
</ol>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fa fa-fw fa-ethernet mr-2"></i>Network Interfaces</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addAssetInterfaceModal"><i class="fas fa-plus mr-2"></i>New Interface</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="<?php if ($interface_count == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>MAC</th>
|
||||
<th>IP</th>
|
||||
<th>Port</th>
|
||||
<th>Connected To</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_related_interfaces)) {
|
||||
$interface_id = intval($row['interface_id']);
|
||||
$interface_name = nullable_htmlentities($row['interface_name']);
|
||||
$interface_mac = nullable_htmlentities($row['interface_mac']);
|
||||
if ($interface_mac) {
|
||||
$interface_mac_display = "$interface_mac";
|
||||
} else {
|
||||
$interface_mac_display = "-";
|
||||
}
|
||||
$interface_ip = nullable_htmlentities($row['interface_ip']);
|
||||
if ($interface_ip) {
|
||||
$interface_ip_display = "$interface_ip";
|
||||
} else {
|
||||
$interface_ip_display = "-";
|
||||
}
|
||||
$interface_ipv6 = nullable_htmlentities($row['interface_ipv6']);
|
||||
$interface_port = nullable_htmlentities($row['interface_port']);
|
||||
if ($interface_port) {
|
||||
$interface_port_display = "$interface_port";
|
||||
} else {
|
||||
$interface_port_display = "-";
|
||||
}
|
||||
$interface_primary = intval($row['interface_primary']);
|
||||
$network_id = intval($row['network_id']);
|
||||
$network_name = nullable_htmlentities($row['network_name']);
|
||||
if ($network_name) {
|
||||
$network_name_display = "<i class='fas fa-fw fa-network-wired mr-2'></i>$network_name";
|
||||
} else {
|
||||
$network_name_display = "-";
|
||||
}
|
||||
$interface_notes = nullable_htmlentities($row['interface_notes']);
|
||||
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<i class="fa fa-fw fa-ethernet text-secondary mr-2"></i>
|
||||
<a class="text-dark" href="#" data-toggle="modal" data-target="#editAssetInterfaceModal<?php echo $interface_id; ?>">
|
||||
<?php echo $interface_name; ?>
|
||||
</a>
|
||||
</td>
|
||||
<td><?php echo $interface_mac_display; ?></td>
|
||||
<td><?php echo $interface_ip_display; ?></td>
|
||||
<td><?php echo $interface_port_display; ?></td>
|
||||
<td><?php echo $network_name_display; ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editAssetInterfaceModal<?php echo $interface_id; ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<?php if ($session_user_role == 3 && $interface_primary == 0) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold" href="post.php?delete_asset_interface=<?php echo $interface_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
require "client_asset_interface_edit_modal.php";
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-dark <?php if ($login_count == 0) { echo "d-none"; } ?>">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fa fa-fw fa-key mr-2"></i>Passwords</h3>
|
||||
@@ -327,7 +462,7 @@ if (isset($_GET['asset_id'])) {
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card card-dark <?php if ($software_count == 0) { echo "d-none"; } ?>">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fa fa-fw fa-cube mr-2"></i>Licenses</h3>
|
||||
@@ -403,6 +538,87 @@ if (isset($_GET['asset_id'])) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-dark <?php if ($files_count == 0) { echo "d-none"; } ?>">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fa fa-fw fa-cube mr-2"></i>Files</h3>
|
||||
<div class="btn-group float-right">
|
||||
<?php
|
||||
if ($view == 0) {
|
||||
?>
|
||||
<a href="?client_id=<?=$client_id?>&asset_id=<?=$asset_id?>&view=0" class="btn btn-primary"><i class="fas fa-list-ul"></i></a>
|
||||
<a href="?client_id=<?=$client_id?>&asset_id=<?=$asset_id?>&view=1" class="btn btn-outline-secondary"><i class="fas fa-th-large"></i></a>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<a href="?client_id=<?=$client_id?>&asset_id=<?=$asset_id?>&view=0" class="btn btn-outline-secondary"><i class="fas fa-list-ul"></i></a>
|
||||
<a href="?client_id=<?=$client_id?>&asset_id=<?=$asset_id?>&view=1" class="btn btn-primary"><i class="fas fa-th-large"></i></a>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Uploaded</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_related_files)) {
|
||||
$file_id = intval($row['file_id']);
|
||||
$file_name = nullable_htmlentities($row['file_name']);
|
||||
$file_description = nullable_htmlentities($row['file_description']);
|
||||
$file_reference_name = nullable_htmlentities($row['file_reference_name']);
|
||||
$file_ext = nullable_htmlentities($row['file_ext']);
|
||||
if ($file_ext == 'pdf') {
|
||||
$file_icon = "file-pdf";
|
||||
} elseif ($file_ext == 'gz' || $file_ext == 'tar' || $file_ext == 'zip' || $file_ext == '7z' || $file_ext == 'rar') {
|
||||
$file_icon = "file-archive";
|
||||
} elseif ($file_ext == 'txt' || $file_ext == 'md') {
|
||||
$file_icon = "file-alt";
|
||||
} elseif ($file_ext == 'msg') {
|
||||
$file_icon = "envelope";
|
||||
} elseif ($file_ext == 'doc' || $file_ext == 'docx' || $file_ext == 'odt') {
|
||||
$file_icon = "file-word";
|
||||
} elseif ($file_ext == 'xls' || $file_ext == 'xlsx' || $file_ext == 'ods') {
|
||||
$file_icon = "file-excel";
|
||||
} elseif ($file_ext == 'pptx' || $file_ext == 'odp') {
|
||||
$file_icon = "file-powerpoint";
|
||||
} elseif ($file_ext == 'mp3' || $file_ext == 'wav' || $file_ext == 'ogg') {
|
||||
$file_icon = "file-audio";
|
||||
} elseif ($file_ext == 'mov' || $file_ext == 'mp4' || $file_ext == 'av1') {
|
||||
$file_icon = "file-video";
|
||||
} elseif ($file_ext == 'jpg' || $file_ext == 'jpeg' || $file_ext == 'png' || $file_ext == 'gif' || $file_ext == 'webp' || $file_ext == 'bmp' || $file_ext == 'tif') {
|
||||
$file_icon = "file-image";
|
||||
} else {
|
||||
$file_icon = "file";
|
||||
}
|
||||
$file_created_at = nullable_htmlentities($row['file_created_at']);
|
||||
?>
|
||||
<tr>
|
||||
<td><a class="text-dark" href="<?php echo "uploads/clients/$client_id/$file_reference_name"; ?>" target="_blank" ><?php echo "$file_name<br><span class='text-secondary'>$file_description</span>"; ?></a></td>
|
||||
<td><?php echo $file_created_at; ?></td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-dark <?php if ($ticket_count == 0) { echo "d-none"; } ?>">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fa fa-fw fa-life-ring mr-2"></i>Tickets</h3>
|
||||
@@ -536,5 +752,8 @@ if (isset($_GET['asset_id'])) {
|
||||
</script>
|
||||
|
||||
<?php
|
||||
|
||||
require_once "client_asset_interface_add_modal.php";
|
||||
|
||||
require_once "footer.php";
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
<input type="hidden" name="asset_id" value="<?php echo $asset_id; ?>">
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
|
||||
@@ -175,6 +175,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>IPv6 Address</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="ipv6" value="<?php echo $asset_ipv6; ?>" placeholder="ex. 2001:0db8:0000:0000:0000:ff00:0042:8329">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>MAC Address</label>
|
||||
<div class="input-group">
|
||||
@@ -209,6 +219,16 @@
|
||||
|
||||
<div class="tab-pane fade" id="pills-assignment<?php echo $asset_id; ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Physical Location</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="physical_location" placeholder="Physical location eg. Floor 2, Closet B" value="<?php echo $asset_physical_location; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Location</label>
|
||||
<div class="input-group">
|
||||
@@ -349,6 +369,17 @@
|
||||
|
||||
<div class="tab-pane fade" id="pills-notes<?php echo $asset_id; ?>">
|
||||
|
||||
<div class="mb-3 text-center">
|
||||
<?php if ($asset_photo) { ?>
|
||||
<img class="img-fluid" alt="asset_photo" src="<?php echo "uploads/clients/$client_id/$asset_photo"; ?>">
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Upload / Replace Photo</label>
|
||||
<input type="file" class="form-control-file" name="file">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"><?php echo $asset_notes; ?></textarea>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<div class="modal" id="addAssetInterfaceModal<?php echo $asset_id; ?>" tabindex="-1">
|
||||
<div class="modal" id="addAssetInterfaceModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content bg-dark">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-ethernet"></i> <i class="fa fa-fw fa-<?php echo $device_icon; ?>"></i> <?php echo $asset_name; ?></h5>
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-ethernet mr-2"></i>New Network Interface</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
@@ -10,107 +10,87 @@
|
||||
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
<input type="hidden" name="asset_id" value="<?php echo $asset_id; ?>">
|
||||
|
||||
<div class="modal-body bg-white">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-interfaces<?php echo $asset_id; ?>">Interfaces</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-create<?php echo $asset_id; ?>">Create</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade" id="pills-interfaces<?php echo $asset_id; ?>">
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label>Interface Name</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Interface Name" required>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-create<?php echo $asset_id; ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Interface Number</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="interface_number" placeholder="Port number">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description</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="interface_description" placeholder="Description">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Connected Asset</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="interface_connected_asset" placeholder="Connected Device">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Network</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-network-wired"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="interface_network">
|
||||
<option value="">- None -</option>
|
||||
<?php
|
||||
|
||||
$sql_network_select = mysqli_query($mysqli, "SELECT * FROM networks WHERE network_archived_at IS NULL AND network_client_id = $client_id ORDER BY network_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_network_select)) {
|
||||
$network_id = $row['network_id'];
|
||||
$network_name = nullable_htmlentities($row['network_name']);
|
||||
$network = nullable_htmlentities($row['network']);
|
||||
|
||||
?>
|
||||
<option value="<?php echo $network_id; ?>"><?php echo $network_name; ?> - <?php echo $network; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>IP</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="interface_ip" placeholder="IP Address" data-inputmask="'alias': 'ip'" data-mask>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>MAC Address</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="interface_mac" placeholder="MAC Address" data-inputmask="'alias': 'mac'" data-mask>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>MAC Address</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="mac" placeholder="MAC Address" data-inputmask="'alias': 'mac'" data-mask>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>IP</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="ip" placeholder="IP Address" data-inputmask="'alias': 'ip'" data-mask>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>IPv6</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="ipv6" placeholder="IPv6 Address">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Port</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="port" placeholder="Interface Port ex. eth0">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Connected to</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-network-wired"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="network">
|
||||
<option value="">- None -</option>
|
||||
<?php
|
||||
|
||||
$sql_network_select = mysqli_query($mysqli, "SELECT * FROM networks WHERE network_archived_at IS NULL AND network_client_id = $client_id ORDER BY network_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_network_select)) {
|
||||
$network_id = $row['network_id'];
|
||||
$network_name = nullable_htmlentities($row['network_name']);
|
||||
$network = nullable_htmlentities($row['network']);
|
||||
|
||||
?>
|
||||
<option value="<?php echo $network_id; ?>"><?php echo $network_name; ?> - <?php echo $network; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="5" placeholder="Enter some notes" name="notes"></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer bg-white">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
|
||||
102
client_asset_interface_edit_modal.php
Normal file
102
client_asset_interface_edit_modal.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<div class="modal" id="editAssetInterfaceModal<?php echo $interface_id; ?>" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content bg-dark">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-ethernet mr-2"></i>Editing: <?php echo $interface_name; ?></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
|
||||
<input type="hidden" name="interface_id" value="<?php echo $interface_id; ?>">
|
||||
|
||||
<div class="modal-body bg-white">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Interface Name</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Interface Name" value="<?php echo $interface_name; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>MAC Address</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="mac" placeholder="MAC Address" value="<?php echo $interface_mac; ?>" data-inputmask="'alias': 'mac'" data-mask>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>IP</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="ip" placeholder="IP Address" value="<?php echo $interface_ip; ?>" data-inputmask="'alias': 'ip'" data-mask>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>IPv6</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="ipv6" placeholder="IPv6 Address" value="<?php echo $interface_ipv6; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Port</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="port" placeholder="Interface Port ex. eth0" value="<?php echo $interface_port; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Connected to</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-network-wired"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="network">
|
||||
<option value="">- None -</option>
|
||||
<?php
|
||||
|
||||
$sql_network_select = mysqli_query($mysqli, "SELECT * FROM networks WHERE network_archived_at IS NULL AND network_client_id = $client_id ORDER BY network_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_network_select)) {
|
||||
$network_id_select = $row['network_id'];
|
||||
$network_name_select = nullable_htmlentities($row['network_name']);
|
||||
$network_select = nullable_htmlentities($row['network']);
|
||||
|
||||
?>
|
||||
<option <?php if ($network_id == $network_id_select) { echo "selected"; } ?> value="<?php echo $network_id_select; ?>"><?php echo $network_name_select; ?> - <?php echo $network_select; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="5" placeholder="Enter some notes" name="notes"><?php echo $interface_notes; ?></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer bg-white">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
<button type="submit" name="edit_asset_interface" class="btn btn-primary"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -29,6 +29,7 @@ if (isset($_GET['location']) & !empty($_GET['location'])) {
|
||||
} else {
|
||||
// Default - any
|
||||
$location_query = '';
|
||||
$location = '';
|
||||
}
|
||||
|
||||
//Get Asset Counts
|
||||
@@ -44,9 +45,10 @@ $row = mysqli_fetch_assoc(mysqli_query($mysqli, "
|
||||
SELECT assets.* FROM assets
|
||||
LEFT JOIN contacts ON asset_contact_id = contact_id
|
||||
LEFT JOIN locations ON asset_location_id = location_id
|
||||
LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1
|
||||
WHERE asset_client_id = $client_id
|
||||
AND asset_$archive_query
|
||||
AND (asset_name LIKE '%$q%' OR asset_description LIKE '%$q%' OR asset_type LIKE '%$q%' OR asset_ip LIKE '%$q%' OR asset_make LIKE '%$q%' OR asset_model LIKE '%$q%' OR asset_serial LIKE '%$q%' OR asset_os LIKE '%$q%' OR contact_name LIKE '%$q%' OR location_name LIKE '%$q%')
|
||||
AND (asset_name LIKE '%$q%' OR asset_description LIKE '%$q%' OR asset_type LIKE '%$q%' OR interface_ip LIKE '%$q%' OR interface_ipv6 LIKE '%$q%' OR asset_make LIKE '%$q%' OR asset_model LIKE '%$q%' OR asset_serial LIKE '%$q%' OR asset_os LIKE '%$q%' OR contact_name LIKE '%$q%' OR location_name LIKE '%$q%')
|
||||
$location_query
|
||||
) AS filtered_assets;
|
||||
"));
|
||||
@@ -77,9 +79,10 @@ $sql = mysqli_query(
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM assets
|
||||
LEFT JOIN contacts ON asset_contact_id = contact_id
|
||||
LEFT JOIN locations ON asset_location_id = location_id
|
||||
LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1
|
||||
WHERE asset_client_id = $client_id
|
||||
AND asset_$archive_query
|
||||
AND (asset_name LIKE '%$q%' OR asset_description LIKE '%$q%' OR asset_type LIKE '%$q%' OR asset_ip LIKE '%$q%' OR asset_make LIKE '%$q%' OR asset_model LIKE '%$q%' OR asset_serial LIKE '%$q%' OR asset_os LIKE '%$q%' OR contact_name LIKE '%$q%' OR location_name LIKE '%$q%')
|
||||
AND (asset_name LIKE '%$q%' OR asset_description LIKE '%$q%' OR asset_type LIKE '%$q%' OR interface_ip LIKE '%$q%' OR interface_ipv6 LIKE '%$q%' OR asset_make LIKE '%$q%' OR asset_model LIKE '%$q%' OR asset_serial LIKE '%$q%' OR asset_os LIKE '%$q%' OR contact_name LIKE '%$q%' OR location_name LIKE '%$q%')
|
||||
AND ($type_query)
|
||||
$location_query
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
@@ -173,11 +176,10 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
} ?>
|
||||
</div>
|
||||
<div class="btn-group mr-2">
|
||||
<?php if($archived == 1) { ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=0" class="btn btn-primary"><i class="fa fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
<?php } else { ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=1" class="btn btn-default"><i class="fa fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
<?php } ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=<?php if($archived == 1){ echo 0; } else { echo 1; } ?>"
|
||||
class="btn btn-<?php if($archived == 1){ echo "primary"; } else { echo "default"; } ?>">
|
||||
<i class="fa fa-fw fa-archive mr-2"></i>Archived
|
||||
</a>
|
||||
<div class="dropdown ml-2" id="bulkActionButton" hidden>
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
|
||||
@@ -194,6 +196,19 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#bulkEditStatusModal">
|
||||
<i class="fas fa-fw fa-info mr-2"></i>Set Status
|
||||
</a>
|
||||
<?php if ($archived) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-info"
|
||||
type="submit" form="bulkActions" name="bulk_unarchive_assets">
|
||||
<i class="fas fa-fw fa-redo mr-2"></i>Unarchive
|
||||
</button>
|
||||
<?php } else { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-danger confirm-link"
|
||||
type="submit" form="bulkActions" name="bulk_archive_assets">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</button>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -262,14 +277,15 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
} else {
|
||||
$asset_os_display = $asset_os;
|
||||
}
|
||||
$asset_ip = nullable_htmlentities($row['asset_ip']);
|
||||
$asset_ip = nullable_htmlentities($row['interface_ip']);
|
||||
if (empty($asset_ip)) {
|
||||
$asset_ip_display = "-";
|
||||
} else {
|
||||
$asset_ip_display = $asset_ip;
|
||||
}
|
||||
$asset_nat_ip = nullable_htmlentities($row['asset_nat_ip']);
|
||||
$asset_mac = nullable_htmlentities($row['asset_mac']);
|
||||
$asset_ipv6 = nullable_htmlentities($row['interface_ipv6']);
|
||||
$asset_nat_ip = nullable_htmlentities($row['interface_nat_ip']);
|
||||
$asset_mac = nullable_htmlentities($row['interface_mac']);
|
||||
$asset_uri = nullable_htmlentities($row['asset_uri']);
|
||||
$asset_uri_2 = nullable_htmlentities($row['asset_uri_2']);
|
||||
$asset_status = nullable_htmlentities($row['asset_status']);
|
||||
@@ -281,12 +297,15 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
} else {
|
||||
$asset_install_date_display = $asset_install_date;
|
||||
}
|
||||
$asset_photo = nullable_htmlentities($row['asset_photo']);
|
||||
$asset_physical_location = nullable_htmlentities($row['asset_physical_location']);
|
||||
$asset_notes = nullable_htmlentities($row['asset_notes']);
|
||||
$asset_created_at = nullable_htmlentities($row['asset_created_at']);
|
||||
$asset_archived_at = nullable_htmlentities($row['asset_archived_at']);
|
||||
$asset_vendor_id = intval($row['asset_vendor_id']);
|
||||
$asset_location_id = intval($row['asset_location_id']);
|
||||
$asset_contact_id = intval($row['asset_contact_id']);
|
||||
$asset_network_id = intval($row['asset_network_id']);
|
||||
$asset_network_id = intval($row['interface_network_id']);
|
||||
|
||||
$device_icon = getAssetIcon($asset_type);
|
||||
|
||||
@@ -412,11 +431,26 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<?php if (!empty($asset_uri)) { ?>
|
||||
<a class="btn btn-default btn-sm" href="<?php echo $asset_uri; ?>" target="_blank"><i class="fas fa-fw fa-external-link-alt"></i></a>
|
||||
<?php if ( !empty($asset_uri) || !empty($asset_uri_2) ) { ?>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-default btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fa fa-fw fa-external-link-alt"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<?php if ($asset_uri) { ?>
|
||||
<a href="<?php echo $asset_uri; ?>" alt="<?php echo $asset_uri; ?>" target="_blank" class="dropdown-item" >
|
||||
<i class="fa fa-fw fa-external-link-alt"></i> <?php echo truncate($asset_uri,40); ?>
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php if ($asset_uri_2) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="<?php echo $asset_uri_2; ?>" target="_blank" class="dropdown-item" >
|
||||
<i class="fa fa-fw fa-external-link-alt"></i> <?php echo truncate($asset_uri_2,40); ?>
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown"><i class="fas fa-ellipsis-h"></i></button>
|
||||
<div class="dropdown-menu">
|
||||
@@ -427,9 +461,15 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<i class="fas fa-fw fa-copy mr-2"></i>Copy
|
||||
</a>
|
||||
<?php if ($session_user_role > 2) { ?>
|
||||
<?php if ($asset_archived_at) { ?>
|
||||
<a class="dropdown-item text-info" href="post.php?unarchive_asset=<?php echo $asset_id; ?>">
|
||||
<i class="fas fa-fw fa-redo mr-2"></i>Unarchive
|
||||
</a>
|
||||
<?php } else { ?>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_asset=<?php echo $asset_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php if ($config_destructive_deletes_enable) { ?>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_asset=<?php echo $asset_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Delete
|
||||
|
||||
@@ -228,6 +228,27 @@
|
||||
<textarea class="form-control" rows="8" name="notes" placeholder="Enter some notes"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Tags</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="tags[]" data-placeholder="Add some tags" multiple>
|
||||
<?php
|
||||
|
||||
$sql_tags_select = mysqli_query($mysqli, "SELECT * FROM tags WHERE tag_type = 3 ORDER BY tag_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_tags_select)) {
|
||||
$tag_id_select = intval($row['tag_id']);
|
||||
$tag_name_select = nullable_htmlentities($row['tag_name']);
|
||||
?>
|
||||
<option value="<?php echo $tag_id_select; ?>"><?php echo $tag_name_select; ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
48
client_contact_bulk_assign_tags_modal.php
Normal file
48
client_contact_bulk_assign_tags_modal.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<div class="modal" id="bulkAssignTagsModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content bg-dark">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-tags mr-2"></i>Bulk Assign Tags</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body bg-white">
|
||||
<input type="hidden" name="bulk_remove_tags" value="0">
|
||||
|
||||
<div class="form-group form-check">
|
||||
<input type="checkbox" class="form-check-input" name="bulk_remove_tags" value="1">
|
||||
<label class="form-check-label text-danger">Remove Existing Tags</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Tags</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="bulk_tags[]" data-placeholder="Add some tags" multiple>
|
||||
<?php
|
||||
|
||||
$sql_tags_select = mysqli_query($mysqli, "SELECT * FROM tags WHERE tag_type = 3 ORDER BY tag_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_tags_select)) {
|
||||
$tag_id_select = intval($row['tag_id']);
|
||||
$tag_name_select = nullable_htmlentities($row['tag_name']);
|
||||
?>
|
||||
<option value="<?php echo $tag_id_select; ?>"><?php echo $tag_name_select; ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer bg-white">
|
||||
<button type="submit" name="bulk_assign_contact_tags" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Assign</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -31,9 +31,15 @@ if (isset($_GET['contact_id'])) {
|
||||
$contact_location_id = intval($row['contact_location_id']);
|
||||
$location_name = nullable_htmlentities($row['location_name']);
|
||||
$auth_method = nullable_htmlentities($row['contact_auth_method']);
|
||||
$contact_client_id = intval($row['contact_client_id']);
|
||||
|
||||
// Check to see if Contact belongs to client
|
||||
if($contact_client_id !== $client_id) {
|
||||
exit();
|
||||
}
|
||||
|
||||
// Related Assets Query
|
||||
$sql_related_assets = mysqli_query($mysqli, "SELECT * FROM assets WHERE asset_contact_id = $contact_id ORDER BY asset_name DESC");
|
||||
$sql_related_assets = mysqli_query($mysqli, "SELECT * FROM assets LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 WHERE asset_contact_id = $contact_id ORDER BY asset_name DESC");
|
||||
$asset_count = mysqli_num_rows($sql_related_assets);
|
||||
|
||||
// Related Logins Query
|
||||
@@ -59,6 +65,28 @@ if (isset($_GET['contact_id'])) {
|
||||
WHERE ticket_contact_id = $contact_id ORDER BY ticket_id DESC");
|
||||
$ticket_count = mysqli_num_rows($sql_related_tickets);
|
||||
|
||||
// Tags
|
||||
$contact_tag_name_display_array = array();
|
||||
$contact_tag_id_array = array();
|
||||
$sql_contact_tags = mysqli_query($mysqli, "SELECT * FROM contact_tags LEFT JOIN tags ON contact_tags.tag_id = tags.tag_id WHERE contact_id = $contact_id ORDER BY tag_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_contact_tags)) {
|
||||
|
||||
$contact_tag_id = intval($row['tag_id']);
|
||||
$contact_tag_name = nullable_htmlentities($row['tag_name']);
|
||||
$contact_tag_color = nullable_htmlentities($row['tag_color']);
|
||||
if (empty($contact_tag_color)) {
|
||||
$contact_tag_color = "dark";
|
||||
}
|
||||
$contact_tag_icon = nullable_htmlentities($row['tag_icon']);
|
||||
if (empty($contact_tag_icon)) {
|
||||
$contact_tag_icon = "tag";
|
||||
}
|
||||
|
||||
$contact_tag_id_array[] = $contact_tag_id;
|
||||
$contact_tag_name_display_array[] = "<a href='client_contacts.php?client_id=$client_id&q=$contact_tag_name'><span class='badge text-light p-1 mr-1' style='background-color: $contact_tag_color;'><i class='fa fa-fw fa-$contact_tag_icon mr-2'></i>$contact_tag_name</span></a>";
|
||||
}
|
||||
$contact_tags_display = implode('', $contact_tag_name_display_array);
|
||||
|
||||
?>
|
||||
|
||||
<div class="row">
|
||||
@@ -85,6 +113,12 @@ if (isset($_GET['contact_id'])) {
|
||||
</span>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php
|
||||
if (!empty($contact_tags_display)) { ?>
|
||||
<div class="mt-1">
|
||||
<?php echo $contact_tags_display; ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<hr>
|
||||
<?php if ($location_name) { ?>
|
||||
<div><i class="fa fa-fw fa-map-marker-alt text-secondary mr-2"></i><?php echo $location_name; ?></div>
|
||||
@@ -143,13 +177,26 @@ if (isset($_GET['contact_id'])) {
|
||||
<li class="breadcrumb-item active"><?php echo "$contact_name"; ?></li>
|
||||
</ol>
|
||||
|
||||
<div class="dropdown dropleft mb-3">
|
||||
<button type="button" class="btn btn-primary" data-toggle="dropdown"><i class="fas fa-fw fa-plus mr-2"></i>New</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#addTicketModal">
|
||||
<i class="fa fa-fw fa-plus mr-2"></i>New Ticket
|
||||
</a>
|
||||
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#addTicketFromTemplateModal">
|
||||
<i class="fa fa-fw fa-plus mr-2"></i>From Template
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-dark <?php if ($asset_count == 0) { echo "d-none"; } ?>">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fa fa-fw fa-desktop mr-2"></i>Related Assets</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<table class="table table-striped table-borderless table-hover dataTables" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name/Description</th>
|
||||
@@ -183,13 +230,15 @@ if (isset($_GET['contact_id'])) {
|
||||
} else {
|
||||
$asset_os_display = $asset_os;
|
||||
}
|
||||
$asset_ip = nullable_htmlentities($row['asset_ip']);
|
||||
$asset_ip = nullable_htmlentities($row['interface_ip']);
|
||||
if (empty($asset_ip)) {
|
||||
$asset_ip_display = "-";
|
||||
} else {
|
||||
$asset_ip_display = "$asset_ip<button class='btn btn-sm' data-clipboard-text='$asset_ip'><i class='far fa-copy text-secondary'></i></button>";
|
||||
}
|
||||
$asset_mac = nullable_htmlentities($row['asset_mac']);
|
||||
$asset_nat_ip = nullable_htmlentities($row['interface_nat_ip']);
|
||||
$asset_ipv6 = nullable_htmlentities($row['interface_ipv6']);
|
||||
$asset_mac = nullable_htmlentities($row['interface_mac']);
|
||||
$asset_status = nullable_htmlentities($row['asset_status']);
|
||||
$asset_purchase_date = nullable_htmlentities($row['asset_purchase_date']);
|
||||
$asset_warranty_expire = nullable_htmlentities($row['asset_warranty_expire']);
|
||||
@@ -199,11 +248,15 @@ if (isset($_GET['contact_id'])) {
|
||||
} else {
|
||||
$asset_install_date_display = $asset_install_date;
|
||||
}
|
||||
$asset_uri = nullable_htmlentities($row['asset_uri']);
|
||||
$asset_uri_2 = nullable_htmlentities($row['asset_uri_2']);
|
||||
$asset_photo = nullable_htmlentities($row['asset_photo']);
|
||||
$asset_physical_location = nullable_htmlentities($row['asset_physical_location']);
|
||||
$asset_notes = nullable_htmlentities($row['asset_notes']);
|
||||
$asset_created_at = nullable_htmlentities($row['asset_created_at']);
|
||||
$asset_vendor_id = intval($row['asset_vendor_id']);
|
||||
$asset_location_id = intval($row['asset_location_id']);
|
||||
$asset_network_id = intval($row['asset_network_id']);
|
||||
$asset_network_id = intval($row['interface_network_id']);
|
||||
$asset_contact_id = intval($row['asset_contact_id']);
|
||||
|
||||
$login_id = $row['login_id'];
|
||||
@@ -236,7 +289,6 @@ if (isset($_GET['contact_id'])) {
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown"><i class="fas fa-ellipsis-h"></i></button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#addAssetInterfaceModal<?php echo $asset_id; ?>">Interfaces</a>
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editAssetModal<?php echo $asset_id; ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
@@ -263,8 +315,6 @@ if (isset($_GET['contact_id'])) {
|
||||
|
||||
require "client_asset_copy_modal.php";
|
||||
|
||||
require "client_asset_interface_add_modal.php";
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -282,8 +332,8 @@ if (isset($_GET['contact_id'])) {
|
||||
<h3 class="card-title"><i class="fa fa-fw fa-key mr-2"></i>Related Logins</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive-sm-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover dataTables" style="width:100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
@@ -308,6 +358,7 @@ if (isset($_GET['contact_id'])) {
|
||||
} else {
|
||||
$login_uri_display = "$login_uri<button class='btn btn-sm clipboardjs' data-clipboard-text='$login_uri'><i class='far fa-copy text-secondary'></i></button><a href='https://$login_uri' target='_blank'><i class='fa fa-external-link-alt text-secondary'></i></a>";
|
||||
}
|
||||
$login_uri_2 = nullable_htmlentities($row['login_uri_2']);
|
||||
$login_username = nullable_htmlentities(decryptLoginEntry($row['login_username']));
|
||||
if (empty($login_username)) {
|
||||
$login_username_display = "-";
|
||||
@@ -388,7 +439,7 @@ if (isset($_GET['contact_id'])) {
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<table class="table table-striped table-borderless table-hover dataTables" style="width:100%">
|
||||
<thead class="text-dark">
|
||||
<tr>
|
||||
<th>Software</th>
|
||||
@@ -414,11 +465,6 @@ if (isset($_GET['contact_id'])) {
|
||||
|
||||
$seat_count = 0;
|
||||
|
||||
// Get Login
|
||||
$login_id = intval($row['login_id']);
|
||||
$login_username = nullable_htmlentities(decryptLoginEntry($row['login_username']));
|
||||
$login_password = nullable_htmlentities(decryptLoginEntry($row['login_password']));
|
||||
|
||||
// Asset Licenses
|
||||
$asset_licenses_sql = mysqli_query($mysqli, "SELECT asset_id FROM software_assets WHERE software_id = $software_id");
|
||||
$asset_licenses_array = array();
|
||||
@@ -463,7 +509,7 @@ if (isset($_GET['contact_id'])) {
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<table class="table table-striped table-borderless table-hover dataTables" style="width:100%">
|
||||
<thead class="text-dark">
|
||||
<tr>
|
||||
<th>Number</th>
|
||||
@@ -482,7 +528,7 @@ if (isset($_GET['contact_id'])) {
|
||||
$ticket_id = intval($row['ticket_id']);
|
||||
$ticket_prefix = nullable_htmlentities($row['ticket_prefix']);
|
||||
$ticket_number = intval($row['ticket_number']);
|
||||
$ticket_subject = intval($row['ticket_subject']);
|
||||
$ticket_subject = nullable_htmlentities($row['ticket_subject']);
|
||||
$ticket_priority = nullable_htmlentities($row['ticket_priority']);
|
||||
$ticket_status = nullable_htmlentities($row['ticket_status']);
|
||||
$ticket_status_name = nullable_htmlentities($row['ticket_status_name']);
|
||||
@@ -610,5 +656,9 @@ if (isset($_GET['contact_id'])) {
|
||||
</script>
|
||||
|
||||
<?php
|
||||
|
||||
require_once "ticket_add_modal.php";
|
||||
require_once "ticket_add_from_template_modal.php";
|
||||
|
||||
require_once "footer.php";
|
||||
|
||||
|
||||
@@ -255,6 +255,27 @@
|
||||
<textarea class="form-control" rows="8" name="notes" placeholder="Notes, eg Personal tidbits to spark convo, temperment, etc"><?php echo $contact_notes; ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Tags</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="tags[]" data-placeholder="Add some tags" multiple>
|
||||
<?php
|
||||
|
||||
$sql_tags_select = mysqli_query($mysqli, "SELECT * FROM tags WHERE tag_type = 3 ORDER BY tag_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_tags_select)) {
|
||||
$tag_id_select = intval($row['tag_id']);
|
||||
$tag_name_select = nullable_htmlentities($row['tag_name']);
|
||||
?>
|
||||
<option value="<?php echo $tag_id_select; ?>" <?php if (in_array($tag_id_select, $contact_tag_id_array)) { echo "selected"; } ?>><?php echo $tag_name_select; ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -6,15 +6,45 @@ $order = "ASC";
|
||||
|
||||
require_once "inc_all_client.php";
|
||||
|
||||
// Tags Filter
|
||||
if (isset($_GET['tags']) && is_array($_GET['tags']) && !empty($_GET['tags'])) {
|
||||
// Sanitize each element of the status array
|
||||
$sanitizedTags = array();
|
||||
foreach ($_GET['tags'] as $tag) {
|
||||
// Escape each status to prevent SQL injection
|
||||
$sanitizedTags[] = "'" . intval($tag) . "'";
|
||||
}
|
||||
|
||||
// Convert the sanitized tags into a comma-separated string
|
||||
$sanitizedTagsString = implode(",", $sanitizedTags);
|
||||
$tag_query = "AND tags.tag_id IN ($sanitizedTagsString)";
|
||||
} else {
|
||||
$tag_query = '';
|
||||
}
|
||||
|
||||
// Location Filter
|
||||
if (isset($_GET['location']) & !empty($_GET['location'])) {
|
||||
$location_query = 'AND (contact_location_id = ' . intval($_GET['location']) . ')';
|
||||
$location = intval($_GET['location']);
|
||||
} else {
|
||||
// Default - any
|
||||
$location_query = '';
|
||||
$location = '';
|
||||
}
|
||||
|
||||
//Rebuild URL
|
||||
$url_query_strings_sort = http_build_query($get_copy);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS * FROM contacts
|
||||
$sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS contacts.*, locations.*, GROUP_CONCAT(tags.tag_name) FROM contacts
|
||||
LEFT JOIN locations ON location_id = contact_location_id
|
||||
LEFT JOIN contact_tags ON contact_tags.contact_id = contacts.contact_id
|
||||
LEFT JOIN tags ON tags.tag_id = contact_tags.tag_id
|
||||
WHERE contact_$archive_query
|
||||
AND (contact_name LIKE '%$q%' OR contact_title LIKE '%$q%' OR location_name LIKE '%$q%' OR contact_email LIKE '%$q%' OR contact_department LIKE '%$q%' OR contact_phone LIKE '%$phone_query%' OR contact_extension LIKE '%$q%' OR contact_mobile LIKE '%$phone_query%')
|
||||
AND contact_client_id = $client_id
|
||||
$tag_query
|
||||
AND (contact_name LIKE '%$q%' OR contact_title LIKE '%$q%' OR location_name LIKE '%$q%' OR contact_email LIKE '%$q%' OR contact_department LIKE '%$q%' OR contact_phone LIKE '%$phone_query%' OR contact_extension LIKE '%$q%' OR contact_mobile LIKE '%$phone_query%' OR tag_name LIKE '%$q%')
|
||||
AND contact_client_id = $client_id
|
||||
$location_query
|
||||
GROUP BY contact_id
|
||||
ORDER BY contact_primary DESC, contact_important DESC, $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
|
||||
@@ -60,13 +90,48 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<select onchange="this.form.submit()" class="form-control select2" name="tags[]" data-placeholder="- Select Tags -" multiple>
|
||||
|
||||
<?php $sql_tags = mysqli_query($mysqli, "SELECT * FROM tags WHERE tag_type = 3");
|
||||
while ($row = mysqli_fetch_array($sql_tags)) {
|
||||
$tag_id = intval($row['tag_id']);
|
||||
$tag_name = nullable_htmlentities($row['tag_name']); ?>
|
||||
|
||||
<option value="<?php echo $tag_id ?>" <?php if (isset($_GET['tags']) && is_array($_GET['tags']) && in_array($tag_id, $_GET['tags'])) { echo 'selected'; } ?>> <?php echo $tag_name ?> </option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<select class="form-control select2" name="location" onchange="this.form.submit()">
|
||||
<option value="" <?php if ($location == "") { echo "selected"; } ?>>- All Locations -</option>
|
||||
|
||||
<?php
|
||||
$sql_locations_filter = mysqli_query($mysqli, "SELECT * FROM locations WHERE location_client_id = $client_id AND location_archived_at IS NULL ORDER BY location_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_locations_filter)) {
|
||||
$location_id = intval($row['location_id']);
|
||||
$location_name = nullable_htmlentities($row['location_name']);
|
||||
?>
|
||||
<option <?php if ($location == $location_id) { echo "selected"; } ?> value="<?php echo $location_id; ?>"><?php echo $location_name; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="btn-group float-right">
|
||||
<?php if($archived == 1){ ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=0" class="btn btn-primary"><i class="fa fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
<?php } else { ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=1" class="btn btn-default"><i class="fa fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
<?php } ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=<?php if($archived == 1){ echo 0; } else { echo 1; } ?>"
|
||||
class="btn btn-<?php if($archived == 1){ echo "primary"; } else { echo "default"; } ?>">
|
||||
<i class="fa fa-fw fa-archive mr-2"></i>Archived
|
||||
</a>
|
||||
<div class="dropdown ml-2" id="bulkActionButton" hidden>
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
|
||||
@@ -87,6 +152,28 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#bulkEditRoleModal">
|
||||
<i class="fas fa-fw fa-user-shield mr-2"></i>Set Roles
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#bulkAssignTagsModal">
|
||||
<i class="fas fa-fw fa-tags mr-2"></i>Assign Tags
|
||||
</a>
|
||||
<?php if ($archived) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-info"
|
||||
type="submit" form="bulkActions" name="bulk_unarchive_contacts">
|
||||
<i class="fas fa-fw fa-redo mr-2"></i>Unarchive
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-danger text-bold"
|
||||
type="submit" form="bulkActions" name="bulk_delete_contacts">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</button>
|
||||
<?php } else { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-danger confirm-link"
|
||||
type="submit" form="bulkActions" name="bulk_archive_contacts">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</button>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -169,6 +256,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
$contact_billing = intval($row['contact_billing']);
|
||||
$contact_technical = intval($row['contact_technical']);
|
||||
$contact_created_at = nullable_htmlentities($row['contact_created_at']);
|
||||
$contact_archived_at = nullable_htmlentities($row['contact_archived_at']);
|
||||
if ($contact_primary == 1) {
|
||||
$contact_primary_display = "<small class='text-success'>Primary Contact</small>";
|
||||
} else {
|
||||
@@ -203,6 +291,28 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
$sql_related_tickets = mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_contact_id = $contact_id ORDER BY ticket_id DESC");
|
||||
$ticket_count = mysqli_num_rows($sql_related_tickets);
|
||||
|
||||
// Tags
|
||||
$contact_tag_name_display_array = array();
|
||||
$contact_tag_id_array = array();
|
||||
$sql_contact_tags = mysqli_query($mysqli, "SELECT * FROM contact_tags LEFT JOIN tags ON contact_tags.tag_id = tags.tag_id WHERE contact_id = $contact_id ORDER BY tag_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_contact_tags)) {
|
||||
|
||||
$contact_tag_id = intval($row['tag_id']);
|
||||
$contact_tag_name = nullable_htmlentities($row['tag_name']);
|
||||
$contact_tag_color = nullable_htmlentities($row['tag_color']);
|
||||
if (empty($contact_tag_color)) {
|
||||
$contact_tag_color = "dark";
|
||||
}
|
||||
$contact_tag_icon = nullable_htmlentities($row['tag_icon']);
|
||||
if (empty($contact_tag_icon)) {
|
||||
$contact_tag_icon = "tag";
|
||||
}
|
||||
|
||||
$contact_tag_id_array[] = $contact_tag_id;
|
||||
$contact_tag_name_display_array[] = "<a href='client_contacts.php?client_id=$client_id&q=$contact_tag_name'><span class='badge text-light p-1 mr-1' style='background-color: $contact_tag_color;'><i class='fa fa-fw fa-$contact_tag_icon mr-2'></i>$contact_tag_name</span></a>";
|
||||
}
|
||||
$contact_tags_display = implode('', $contact_tag_name_display_array);
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td class="pr-0 bg-light">
|
||||
@@ -234,6 +344,12 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<?php
|
||||
if (!empty($contact_tags_display)) { ?>
|
||||
<div class="mt-1">
|
||||
<?php echo $contact_tags_display; ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<td><?php echo $contact_department_display; ?></td>
|
||||
<td><?php echo $contact_info_display; ?></td>
|
||||
@@ -251,14 +367,22 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<?php if ($session_user_role == 3 && $contact_primary == 0) { ?>
|
||||
<?php if ($contact_archived_at) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?anonymize_contact=<?php echo $contact_id; ?>">
|
||||
<i class="fas fa-fw fa-user-secret mr-2"></i>Anonymize & Archive
|
||||
<a class="dropdown-item text-info confirm-link" href="post.php?unarchive_contact=<?php echo $contact_id; ?>">
|
||||
<i class="fas fa-fw fa-redo mr-2"></i>Unarchive
|
||||
</a>
|
||||
<?php } else { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_contact=<?php echo $contact_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?anonymize_contact=<?php echo $contact_id; ?>">
|
||||
<i class="fas fa-fw fa-user-secret mr-2"></i>Anonymize & Archive
|
||||
</a>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($config_destructive_deletes_enable) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_contact=<?php echo $contact_id; ?>">
|
||||
@@ -286,6 +410,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<?php require_once "client_contact_bulk_edit_phone_modal.php"; ?>
|
||||
<?php require_once "client_contact_bulk_edit_department_modal.php"; ?>
|
||||
<?php require_once "client_contact_bulk_edit_role_modal.php"; ?>
|
||||
<?php require_once "client_contact_bulk_assign_tags_modal.php"; ?>
|
||||
</form>
|
||||
<?php require_once "pagination.php";
|
||||
?>
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
|
||||
<div class="modal-footer bg-white">
|
||||
|
||||
<button type="submit" name="add_document_from_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create & edit</button>
|
||||
<button type="submit" name="add_document_from_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -11,15 +11,15 @@ $purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'htt
|
||||
$purifier = new HTMLPurifier($purifier_config);
|
||||
|
||||
if (isset($_GET['document_id'])) {
|
||||
$document_id = intval($_GET['document_id']);
|
||||
$document_id = intval($_GET['document_id']);
|
||||
}
|
||||
|
||||
$folder_location = 0;
|
||||
|
||||
$sql_document = mysqli_query($mysqli, "SELECT * FROM documents
|
||||
LEFT JOIN folders ON document_folder_id = folder_id
|
||||
LEFT JOIN users ON document_created_by = user_id
|
||||
WHERE document_client_id = $client_id AND document_id = $document_id"
|
||||
LEFT JOIN folders ON document_folder_id = folder_id
|
||||
LEFT JOIN users ON document_created_by = user_id
|
||||
WHERE document_client_id = $client_id AND document_id = $document_id"
|
||||
);
|
||||
|
||||
$row = mysqli_fetch_array($sql_document);
|
||||
@@ -39,249 +39,293 @@ $document_parent = intval($row['document_parent']);
|
||||
?>
|
||||
|
||||
<ol class="breadcrumb d-print-none">
|
||||
<li class="breadcrumb-item">
|
||||
<a href="client_overview.php?client_id=<?php echo $client_id; ?>"><?php echo $client_name; ?></a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="client_documents.php?client_id=<?php echo $client_id; ?>">Documents</a>
|
||||
</li>
|
||||
<?php if ($document_folder_id > 0) { ?>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="client_documents.php?client_id=<?php echo $client_id; ?>&folder_id=<?php echo $document_folder_id; ?>"><i class="fas fa-fw fa-folder-open mr-2"></i><?php echo $folder_name; ?></a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<li class="breadcrumb-item active"><i class="fas fa-file"></i> <?php echo $document_name; ?> <?php if(!empty($document_archived_at)){ echo "<span class='text-danger ml-2'>(ARCHIVED on $document_archived_at)</span>"; } ?></li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="client_overview.php?client_id=<?php echo $client_id; ?>"><?php echo $client_name; ?></a>
|
||||
</li>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="client_documents.php?client_id=<?php echo $client_id; ?>">Documents</a>
|
||||
</li>
|
||||
<?php if ($document_folder_id > 0) { ?>
|
||||
<li class="breadcrumb-item">
|
||||
<a href="client_documents.php?client_id=<?php echo $client_id; ?>&folder_id=<?php echo $document_folder_id; ?>"><i class="fas fa-fw fa-folder-open mr-2"></i><?php echo $folder_name; ?></a>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<li class="breadcrumb-item active"><i class="fas fa-file"></i> <?php echo $document_name; ?> <?php if(!empty($document_archived_at)){ echo "<span class='text-danger ml-2'>(ARCHIVED on $document_archived_at)</span>"; } ?></li>
|
||||
</ol>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-9">
|
||||
<div class="card">
|
||||
<div class="card-header bg-dark">
|
||||
<div class="col-md-9">
|
||||
<div class="card">
|
||||
<div class="card-header bg-dark">
|
||||
|
||||
<h3><?php echo $document_name; ?> <?php if (!empty($document_description)) { ?><span class="h6 text-muted">(<?php echo $document_description; ?>)</span><?php } ?></h3>
|
||||
<h3><?php echo $document_name; ?> <?php if (!empty($document_description)) { ?><span class="h6 text-muted">(<?php echo $document_description; ?>)</span><?php } ?></h3>
|
||||
|
||||
<div class="row">
|
||||
<div class="col"><strong>Date:</strong> <?php echo date('Y-m-d', strtotime($document_created_at)); ?></div>
|
||||
<?php if(!empty($document_created_by_name)){ ?>
|
||||
<div class="col"><strong>Prepared By:</strong> <?php echo $document_created_by_name; ?></div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body prettyContent">
|
||||
<?php echo $document_content; ?>
|
||||
<hr>
|
||||
<h4>Documentation Revision History</h4>
|
||||
|
||||
<table class="table table-sm">
|
||||
<thead class="thead-light">
|
||||
<th>Revision</th>
|
||||
<th>Date</th>
|
||||
<th>Description</th>
|
||||
<th>Author</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$sql_document_revisions = mysqli_query($mysqli, "SELECT * FROM documents
|
||||
LEFT JOIN users ON document_created_by = user_id
|
||||
WHERE document_parent = $document_parent
|
||||
ORDER BY document_created_at ASC"
|
||||
);
|
||||
|
||||
$revision_count = 1; // Initialize the revision counter
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_document_revisions)) {
|
||||
$revision_document_id = intval($row['document_id']);
|
||||
$revision_document_name = nullable_htmlentities($row['document_name']);
|
||||
$revision_document_description = nullable_htmlentities($row['document_description']);
|
||||
if ($revision_document_description ) {
|
||||
$revision_document_description_display = $revision_document_description;
|
||||
} else {
|
||||
$revision_document_description_display = "-";
|
||||
}
|
||||
$revision_document_author = nullable_htmlentities($row['user_name']);
|
||||
$revision_document_created_date = date('Y-m-d', strtotime($row['document_created_at']));
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td><?php echo "$revision_count.0"; ?></td>
|
||||
<td><?php echo $revision_document_created_date; ?></td>
|
||||
<td><?php echo $revision_document_description_display; ?></td>
|
||||
<td><?php echo $revision_document_author; ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
$revision_count++; // Increment the counter
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 d-print-none">
|
||||
<div class="row">
|
||||
<div class="col"><strong>Date:</strong> <?php echo date('Y-m-d', strtotime($document_created_at)); ?></div>
|
||||
<?php if(!empty($document_created_by_name)){ ?>
|
||||
<div class="col"><strong>Prepared By:</strong> <?php echo $document_created_by_name; ?></div>
|
||||
<?php } ?>
|
||||
<div class="col-12 mb-3">
|
||||
<button type="button" class="btn btn-primary mr-2" data-toggle="modal" data-target="#editDocumentModal<?php echo $document_id; ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary mr-2" data-toggle="modal" data-target="#shareModal"
|
||||
onclick="populateShareModal(<?php echo "$client_id, 'Document', $document_id"; ?>)">
|
||||
<i class="fas fa-fw fa-share mr-2"></i>Share
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body prettyContent">
|
||||
<?php echo $document_content; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card card-body bg-light">
|
||||
<h5 class="mb-3"><i class="fas fa-tags mr-2"></i>Related Items</h5>
|
||||
<h6>
|
||||
<i class="fas fa-fw fa-paperclip text-secondary mr-2"></i>Files
|
||||
<button type="button" class="btn btn-link btn-sm" data-toggle="modal" data-target="#linkFileToDocumentModal">
|
||||
<i class="fas fa-fw fa-plus"></i>
|
||||
</button>
|
||||
</h6>
|
||||
<?php
|
||||
$sql_files = mysqli_query($mysqli, "SELECT * FROM files, document_files
|
||||
WHERE document_files.file_id = files.file_id
|
||||
AND document_files.document_id = $document_id
|
||||
ORDER BY file_name ASC"
|
||||
);
|
||||
|
||||
<div class="col-md-3 d-print-none">
|
||||
<div class="row">
|
||||
<div class="col-12 mb-3">
|
||||
<button type="button" class="btn btn-primary mr-2" data-toggle="modal" data-target="#editDocumentModal<?php echo $document_id; ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary mr-2" data-toggle="modal" data-target="#shareModal"
|
||||
onclick="populateShareModal(<?php echo "$client_id, 'Document', $document_id"; ?>)">
|
||||
<i class="fas fa-fw fa-share mr-2"></i>Share
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="window.print();"><i class="fas fa-fw fa-print mr-2"></i>Print</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card card-body bg-light">
|
||||
<h5 class="mb-3"><i class="fas fa-tags mr-2"></i>Related Items</h5>
|
||||
<h6>
|
||||
<i class="fas fa-fw fa-paperclip text-secondary mr-2"></i>Files
|
||||
<button type="button" class="btn btn-link btn-sm" data-toggle="modal" data-target="#linkFileToDocumentModal">
|
||||
<i class="fas fa-fw fa-plus"></i>
|
||||
</button>
|
||||
</h6>
|
||||
<?php
|
||||
$sql_files = mysqli_query($mysqli, "SELECT * FROM files, document_files
|
||||
WHERE document_files.file_id = files.file_id
|
||||
AND document_files.document_id = $document_id
|
||||
ORDER BY file_name ASC"
|
||||
);
|
||||
$linked_files = array();
|
||||
|
||||
$linked_files = array();
|
||||
while ($row = mysqli_fetch_array($sql_files)) {
|
||||
$file_id = intval($row['file_id']);
|
||||
$folder_id = intval($row['file_folder_id']);
|
||||
$file_name = nullable_htmlentities($row['file_name']);
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_files)) {
|
||||
$file_id = intval($row['file_id']);
|
||||
$folder_id = intval($row['file_folder_id']);
|
||||
$file_name = nullable_htmlentities($row['file_name']);
|
||||
$linked_files[] = $file_id;
|
||||
|
||||
$linked_files[] = $file_id;
|
||||
?>
|
||||
<div class="ml-2">
|
||||
<a href="client_files.php?client_id=<?php echo $client_id; ?>&folder_id=<?php echo $folder_id; ?>&q=<?php echo $file_name; ?>" target="_blank"><?php echo $file_name; ?></a>
|
||||
<a class="confirm-link" href="post.php?unlink_file_from_document&file_id=<?php echo $file_id; ?>&document_id=<?php echo $document_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary float-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<h6>
|
||||
<i class="fas fa-fw fa-users text-secondary mt-3 mr-2"></i>Contacts
|
||||
<button type="button" class="btn btn-link btn-sm" data-toggle="modal" data-target="#linkContactToDocumentModal">
|
||||
<i class="fas fa-fw fa-plus"></i>
|
||||
</button>
|
||||
</h6>
|
||||
<?php
|
||||
$sql_contacts = mysqli_query($mysqli, "SELECT * FROM contacts, contact_documents
|
||||
WHERE contacts.contact_id = contact_documents.contact_id
|
||||
AND contact_documents.document_id = $document_id
|
||||
ORDER BY contact_name ASC"
|
||||
);
|
||||
|
||||
?>
|
||||
<div class="ml-2">
|
||||
<a href="client_files.php?client_id=<?php echo $client_id; ?>&folder_id=<?php echo $folder_id; ?>&q=<?php echo $file_name; ?>" target="_blank"><?php echo $file_name; ?></a>
|
||||
<a class="confirm-link" href="post.php?unlink_file_from_document&file_id=<?php echo $file_id; ?>&document_id=<?php echo $document_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary float-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<h6>
|
||||
<i class="fas fa-fw fa-users text-secondary mt-3 mr-2"></i>Contacts
|
||||
<button type="button" class="btn btn-link btn-sm" data-toggle="modal" data-target="#linkContactToDocumentModal">
|
||||
<i class="fas fa-fw fa-plus"></i>
|
||||
</button>
|
||||
</h6>
|
||||
<?php
|
||||
$sql_contacts = mysqli_query($mysqli, "SELECT * FROM contacts, contact_documents
|
||||
WHERE contacts.contact_id = contact_documents.contact_id
|
||||
AND contact_documents.document_id = $document_id
|
||||
ORDER BY contact_name ASC"
|
||||
);
|
||||
$linked_contacts = array();
|
||||
|
||||
$linked_contacts = array();
|
||||
while ($row = mysqli_fetch_array($sql_contacts)) {
|
||||
$contact_id = intval($row['contact_id']);
|
||||
$contact_name = nullable_htmlentities($row['contact_name']);
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_contacts)) {
|
||||
$contact_id = intval($row['contact_id']);
|
||||
$contact_name = nullable_htmlentities($row['contact_name']);
|
||||
$linked_contacts[] = $contact_id;
|
||||
|
||||
$linked_contacts[] = $contact_id;
|
||||
?>
|
||||
<div class="ml-2">
|
||||
<a href="client_contact_details.php?client_id=<?php echo $client_id; ?>&contact_id=<?php echo $contact_id; ?>" target="_blank"><?php echo $contact_name; ?></a>
|
||||
<a class="confirm-link float-right" href="post.php?unlink_contact_from_document&contact_id=<?php echo $contact_id; ?>&document_id=<?php echo $document_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<h6>
|
||||
<i class="fas fa-fw fa-laptop text-secondary mr-2 mt-3"></i>Assets
|
||||
<button type="button" class="btn btn-link btn-sm" data-toggle="modal" data-target="#linkAssetToDocumentModal">
|
||||
<i class="fas fa-fw fa-plus"></i>
|
||||
</button>
|
||||
</h6>
|
||||
<?php
|
||||
$sql_assets = mysqli_query($mysqli, "SELECT * FROM assets, asset_documents
|
||||
WHERE assets.asset_id = asset_documents.asset_id
|
||||
AND asset_documents.document_id = $document_id
|
||||
ORDER BY asset_name ASC"
|
||||
);
|
||||
|
||||
?>
|
||||
<div class="ml-2">
|
||||
<a href="client_contact_details.php?client_id=<?php echo $client_id; ?>&contact_id=<?php echo $contact_id; ?>" target="_blank"><?php echo $contact_name; ?></a>
|
||||
<a class="confirm-link float-right" href="post.php?unlink_contact_from_document&contact_id=<?php echo $contact_id; ?>&document_id=<?php echo $document_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<h6>
|
||||
<i class="fas fa-fw fa-laptop text-secondary mr-2 mt-3"></i>Assets
|
||||
<button type="button" class="btn btn-link btn-sm" data-toggle="modal" data-target="#linkAssetToDocumentModal">
|
||||
<i class="fas fa-fw fa-plus"></i>
|
||||
</button>
|
||||
</h6>
|
||||
<?php
|
||||
$sql_assets = mysqli_query($mysqli, "SELECT * FROM assets, asset_documents
|
||||
WHERE assets.asset_id = asset_documents.asset_id
|
||||
AND asset_documents.document_id = $document_id
|
||||
ORDER BY asset_name ASC"
|
||||
);
|
||||
$linked_assets = array();
|
||||
|
||||
$linked_assets = array();
|
||||
while ($row = mysqli_fetch_array($sql_assets)) {
|
||||
$asset_id = intval($row['asset_id']);
|
||||
$asset_name = nullable_htmlentities($row['asset_name']);
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_assets)) {
|
||||
$asset_id = intval($row['asset_id']);
|
||||
$asset_name = nullable_htmlentities($row['asset_name']);
|
||||
$linked_assets[] = $asset_id;
|
||||
|
||||
$linked_assets[] = $asset_id;
|
||||
?>
|
||||
<div class="ml-2">
|
||||
<a href="client_asset_details.php?client_id=<?php echo $client_id; ?>&asset_id=<?php echo $asset_id; ?>" target="_blank"><?php echo $asset_name; ?></a>
|
||||
<a class="confirm-link float-right" href="post.php?unlink_asset_from_document&asset_id=<?php echo $asset_id; ?>&document_id=<?php echo $document_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<h6>
|
||||
<i class="fas fa-fw fa-cube text-secondary mr-2 mt-3"></i>Licenses
|
||||
<button type="button" class="btn btn-link btn-sm" data-toggle="modal" data-target="#linkSoftwareToDocumentModal">
|
||||
<i class="fas fa-fw fa-plus"></i>
|
||||
</button>
|
||||
</h6>
|
||||
<?php
|
||||
$sql_software = mysqli_query($mysqli, "SELECT * FROM software, software_documents
|
||||
WHERE software.software_id = software_documents.software_id
|
||||
AND software_documents.document_id = $document_id
|
||||
ORDER BY software_name ASC"
|
||||
);
|
||||
|
||||
?>
|
||||
<div class="ml-2">
|
||||
<a href="client_asset_details.php?client_id=<?php echo $client_id; ?>&asset_id=<?php echo $asset_id; ?>" target="_blank"><?php echo $asset_name; ?></a>
|
||||
<a class="confirm-link float-right" href="post.php?unlink_asset_from_document&asset_id=<?php echo $asset_id; ?>&document_id=<?php echo $document_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<h6>
|
||||
<i class="fas fa-fw fa-cube text-secondary mr-2 mt-3"></i>Licenses
|
||||
<button type="button" class="btn btn-link btn-sm" data-toggle="modal" data-target="#linkSoftwareToDocumentModal">
|
||||
<i class="fas fa-fw fa-plus"></i>
|
||||
</button>
|
||||
</h6>
|
||||
<?php
|
||||
$sql_software = mysqli_query($mysqli, "SELECT * FROM software, software_documents
|
||||
WHERE software.software_id = software_documents.software_id
|
||||
AND software_documents.document_id = $document_id
|
||||
ORDER BY software_name ASC"
|
||||
);
|
||||
$linked_software = array();
|
||||
|
||||
$linked_software = array();
|
||||
while ($row = mysqli_fetch_array($sql_software)) {
|
||||
$software_id = intval($row['software_id']);
|
||||
$software_name = nullable_htmlentities($row['software_name']);
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_software)) {
|
||||
$software_id = intval($row['software_id']);
|
||||
$software_name = nullable_htmlentities($row['software_name']);
|
||||
$linked_software[] = $software_id;
|
||||
|
||||
$linked_software[] = $software_id;
|
||||
?>
|
||||
<div class="ml-2">
|
||||
<a href="client_software.php?client_id=<?php echo $client_id; ?>&q=<?php echo $software_name; ?>" target="_blank"><?php echo $software_name; ?></a>
|
||||
<a class="confirm-link float-right" href="post.php?unlink_software_from_document&software_id=<?php echo $software_id; ?>&document_id=<?php echo $document_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<h6>
|
||||
<i class="fas fa-fw fa-building text-secondary mr-2 mt-3"></i>Vendors
|
||||
<button type="button" class="btn btn-link btn-sm" data-toggle="modal" data-target="#linkVendorToDocumentModal">
|
||||
<i class="fas fa-fw fa-plus"></i>
|
||||
</button>
|
||||
</h6>
|
||||
<?php
|
||||
$sql_vendors = mysqli_query($mysqli, "SELECT * FROM vendors, vendor_documents
|
||||
WHERE vendors.vendor_id = vendor_documents.vendor_id
|
||||
AND vendor_documents.document_id = $document_id
|
||||
ORDER BY vendor_name ASC"
|
||||
);
|
||||
|
||||
?>
|
||||
<div class="ml-2">
|
||||
<a href="client_software.php?client_id=<?php echo $client_id; ?>&q=<?php echo $software_name; ?>" target="_blank"><?php echo $software_name; ?></a>
|
||||
<a class="confirm-link float-right" href="post.php?unlink_software_from_document&software_id=<?php echo $software_id; ?>&document_id=<?php echo $document_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<h6>
|
||||
<i class="fas fa-fw fa-building text-secondary mr-2 mt-3"></i>Vendors
|
||||
<button type="button" class="btn btn-link btn-sm" data-toggle="modal" data-target="#linkVendorToDocumentModal">
|
||||
<i class="fas fa-fw fa-plus"></i>
|
||||
</button>
|
||||
</h6>
|
||||
<?php
|
||||
$sql_vendors = mysqli_query($mysqli, "SELECT * FROM vendors, vendor_documents
|
||||
WHERE vendors.vendor_id = vendor_documents.vendor_id
|
||||
AND vendor_documents.document_id = $document_id
|
||||
ORDER BY vendor_name ASC"
|
||||
);
|
||||
$associated_vendors = array();
|
||||
|
||||
$associated_vendors = array();
|
||||
while ($row = mysqli_fetch_array($sql_vendors)) {
|
||||
$vendor_id = intval($row['vendor_id']);
|
||||
$vendor_name = nullable_htmlentities($row['vendor_name']);
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_vendors)) {
|
||||
$vendor_id = intval($row['vendor_id']);
|
||||
$vendor_name = nullable_htmlentities($row['vendor_name']);
|
||||
$associated_vendors[] = $vendor_id;
|
||||
|
||||
$associated_vendors[] = $vendor_id;
|
||||
|
||||
?>
|
||||
<div class="ml-2">
|
||||
<a href="client_vendors.php?client_id=<?php echo $client_id; ?>&q=<?php echo $vendor_name; ?>" target="_blank"><?php echo $vendor_name; ?></a>
|
||||
<a class="confirm-link float-right" href="post.php?unlink_vendor_from_document&vendor_id=<?php echo $vendor_id; ?>&document_id=<?php echo $document_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
<div class="card card-body bg-light">
|
||||
<h6><i class="fas fa-history mr-2"></i>Revisions</h6>
|
||||
<?php
|
||||
|
||||
$sql_document_revisions = mysqli_query($mysqli, "SELECT * FROM documents
|
||||
LEFT JOIN users ON document_created_by = user_id
|
||||
WHERE document_parent = $document_parent
|
||||
ORDER BY document_created_at DESC"
|
||||
);
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_document_revisions)) {
|
||||
$revision_document_id = intval($row['document_id']);
|
||||
$revision_document_name = nullable_htmlentities($row['document_name']);
|
||||
$revision_document_description = nullable_htmlentities($row['document_description']);
|
||||
$revision_document_created_by_name = nullable_htmlentities($row['user_name']);
|
||||
$revision_document_created_date = nullable_htmlentities($row['document_created_at']);
|
||||
$revision_document_created_date = nullable_htmlentities($row['document_created_at']);
|
||||
|
||||
?>
|
||||
<div class="mt-1 <?php if($document_id == $revision_document_id){ echo "text-bold"; } ?>">
|
||||
<i class="fas fa-fw fa-history text-secondary mr-2"></i><a href="?client_id=<?php echo $client_id; ?>&document_id=<?php echo $revision_document_id; ?>"><?php echo " $revision_document_created_date"; ?></a><?php if($document_parent == $revision_document_id){ echo "<span class='float-right'>(Parent)</span>";
|
||||
} else { ?>
|
||||
<a class="confirm-link float-right" href="post.php?delete_document_version=<?php echo $revision_document_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary"></i>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<div class="ml-2">
|
||||
<a href="client_vendors.php?client_id=<?php echo $client_id; ?>&q=<?php echo $vendor_name; ?>" target="_blank"><?php echo $vendor_name; ?></a>
|
||||
<a class="confirm-link float-right" href="post.php?unlink_vendor_from_document&vendor_id=<?php echo $vendor_id; ?>&document_id=<?php echo $document_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="card card-body bg-light">
|
||||
<h6><i class="fas fa-history mr-2"></i>Revisions</h6>
|
||||
<?php
|
||||
|
||||
$sql_document_revisions = mysqli_query($mysqli, "SELECT * FROM documents
|
||||
LEFT JOIN users ON document_created_by = user_id
|
||||
WHERE document_parent = $document_parent
|
||||
ORDER BY document_created_at DESC"
|
||||
);
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_document_revisions)) {
|
||||
$revision_document_id = intval($row['document_id']);
|
||||
$revision_document_name = nullable_htmlentities($row['document_name']);
|
||||
$revision_document_description = nullable_htmlentities($row['document_description']);
|
||||
$revision_document_created_by_name = nullable_htmlentities($row['user_name']);
|
||||
$revision_document_created_date = nullable_htmlentities($row['document_created_at']);
|
||||
|
||||
?>
|
||||
<div class="mt-1 <?php if($document_id == $revision_document_id){ echo "text-bold"; } ?>">
|
||||
<i class="fas fa-fw fa-history text-secondary mr-2"></i><a href="?client_id=<?php echo $client_id; ?>&document_id=<?php echo $revision_document_id; ?>"><?php echo " $revision_document_created_date"; ?></a><?php if($document_parent == $revision_document_id){ echo "<span class='float-right'>(Parent)</span>";
|
||||
} else { ?>
|
||||
<a class="confirm-link float-right" href="post.php?delete_document_version=<?php echo $revision_document_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary"></i>
|
||||
</a>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -305,3 +349,4 @@ require_once "share_modal.php";
|
||||
|
||||
require_once "footer.php";
|
||||
|
||||
?>
|
||||
|
||||
@@ -200,10 +200,10 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<form id="bulkActions" action="post.php" method="post">
|
||||
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-sm table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<table class="table table-border">
|
||||
<thead class="thead-light <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<td class="bg-light">
|
||||
<td class="bg-light pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)">
|
||||
</div>
|
||||
@@ -238,7 +238,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td class="bg-light">
|
||||
<td class="bg-light pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input bulk-select" type="checkbox" name="document_ids[]" value="<?php echo $document_id ?>">
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,7 @@ $sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS domains.*,
|
||||
LEFT JOIN vendors AS mailhost ON domains.domain_mailhost = mailhost.vendor_id
|
||||
LEFT JOIN vendors AS webhost ON domains.domain_webhost = webhost.vendor_id
|
||||
WHERE domain_client_id = $client_id
|
||||
AND domain_archived_at IS NULL
|
||||
AND domain_$archive_query
|
||||
AND (domains.domain_name LIKE '%$q%' OR domains.domain_description LIKE '%$q%' OR registrar.vendor_name LIKE '%$q%' OR dnshost.vendor_name LIKE '%$q%' OR mailhost.vendor_name LIKE '%$q%' OR webhost.vendor_name LIKE '%$q%')
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to");
|
||||
|
||||
@@ -60,15 +60,31 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="btn-group float-right">
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=<?php if($archived == 1){ echo 0; } else { echo 1; } ?>"
|
||||
class="btn btn-<?php if($archived == 1){ echo "primary"; } else { echo "default"; } ?>">
|
||||
<i class="fa fa-fw fa-archive mr-2"></i>Archived
|
||||
</a>
|
||||
<div class="dropdown ml-2" id="bulkActionButton" hidden>
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<?php if ($archived) { ?>
|
||||
<button class="dropdown-item text-info"
|
||||
type="submit" form="bulkActions" name="bulk_unarchive_domains">
|
||||
<i class="fas fa-fw fa-redo mr-2"></i>Unarchive
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-danger text-bold"
|
||||
type="submit" form="bulkActions" name="bulk_delete_domains">
|
||||
type="submit" form="bulkActions" name="bulk_delete_domains">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</button>
|
||||
<?php } else { ?>
|
||||
<button class="dropdown-item text-danger confirm-link"
|
||||
type="submit" form="bulkActions" name="bulk_archive_domains">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</button>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -81,6 +97,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
<form id="bulkActions" action="post.php" method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
@@ -117,6 +134,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
$domain_dnshost_name = nullable_htmlentities($row['dnshost_name']);
|
||||
$domain_mailhost_name = nullable_htmlentities($row['mailhost_name']);
|
||||
$domain_created_at = nullable_htmlentities($row['domain_created_at']);
|
||||
$domain_archived_at = nullable_htmlentities($row['domain_archived_at']);
|
||||
// Add - if empty on the table
|
||||
$domain_registrar_name_display = $domain_registrar_name ? $domain_registrar_name : "-";
|
||||
$domain_webhost_name_display = $domain_webhost_name ? $domain_webhost_name : "-";
|
||||
@@ -128,10 +146,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<td class="pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input bulk-select" type="checkbox" name="domain_ids[]" value="<?php echo $domain_id ?>">
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<td class="">
|
||||
<a class="text-dark" href="#" data-toggle="modal" onclick="populateDomainEditModal(<?php echo $client_id, ",", $domain_id ?>)" data-target="#editDomainModal">
|
||||
<div class="media">
|
||||
<i class="fa fa-fw fa-2x fa-globe mr-3"></i>
|
||||
@@ -156,17 +173,24 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" onclick="populateDomainEditModal(<?php echo $client_id, ",", $domain_id ?>)" data-target="#editDomainModal">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<?php if ($session_user_role == 2) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_domain=<?php echo $domain_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php if ($session_user_role == 3) { ?>
|
||||
<?php if ($domain_archived_at) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-info confirm-link" href="post.php?unarchive_domain=<?php echo $domain_id; ?>">
|
||||
<i class="fas fa-fw fa-redo mr-2"></i>Unarchive
|
||||
</a>
|
||||
<?php if ($config_destructive_deletes_enable) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_domain=<?php echo $domain_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php } else { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_domain=<?php echo $domain_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -184,6 +184,16 @@
|
||||
|
||||
<div class="tab-pane fade" id="pills-client-more<?php echo $client_id; ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Abbreviation</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-id-badge"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="abbreviation" placeholder="Abbreviated name for client" value="<?php echo $client_abbreviation; ?>" maxlength="6" oninput="this.value = this.value.toUpperCase()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="8" placeholder="Enter some notes"
|
||||
name="notes"><?php echo $client_notes; ?></textarea>
|
||||
|
||||
77
client_file_link_asset_modal.php
Normal file
77
client_file_link_asset_modal.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<div class="modal" id="linkAssetToFileModal<?php echo $file_id; ?>" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content bg-dark">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-desktop mr-2"></i>Link Asset to <strong><?php echo $file_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
<input type="hidden" name="file_id" value="<?php echo $file_id; ?>">
|
||||
<div class="modal-body bg-white">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="asset_id">
|
||||
<option value="">- Select an Asset -</option>
|
||||
<?php
|
||||
|
||||
$sql_assets_select = mysqli_query($mysqli, "SELECT * FROM assets
|
||||
WHERE asset_client_id = $client_id
|
||||
AND asset_archived_at IS NULL
|
||||
$exclude_condition
|
||||
ORDER BY asset_name ASC"
|
||||
);
|
||||
while ($row = mysqli_fetch_array($sql_assets_select)) {
|
||||
$asset_id = intval($row['asset_id']);
|
||||
$asset_name = nullable_htmlentities($row['asset_name']);
|
||||
|
||||
?>
|
||||
<option value="<?php echo $asset_id ?>"><?php echo $asset_name; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
$sql_assets = mysqli_query($mysqli, "SELECT * FROM assets, asset_files
|
||||
WHERE assets.asset_id = asset_files.asset_id
|
||||
AND asset_files.file_id = $file_id
|
||||
ORDER BY asset_name ASC"
|
||||
);
|
||||
|
||||
$linked_assets = array();
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_assets)) {
|
||||
$asset_id = intval($row['asset_id']);
|
||||
$asset_name = nullable_htmlentities($row['asset_name']);
|
||||
|
||||
$linked_assets[] = $asset_id;
|
||||
|
||||
?>
|
||||
<div class="ml-2">
|
||||
<a href="client_asset_details.php?client_id=<?php echo $client_id; ?>&asset_id=<?php echo $asset_id; ?>" target="_blank"><?php echo $asset_name; ?></a>
|
||||
<a class="confirm-link float-right" href="post.php?unlink_asset_from_file&asset_id=<?php echo $asset_id; ?>&file_id=<?php echo $file_id; ?>">
|
||||
<i class="fas fa-fw fa-trash-alt text-secondary"></i>
|
||||
</a>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer bg-white">
|
||||
<button type="submit" name="link_asset_to_file" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Link</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -26,7 +26,7 @@
|
||||
$folder_id_select = intval($row['folder_id']);
|
||||
$folder_name_select = nullable_htmlentities($row['folder_name']);
|
||||
?>
|
||||
<option <?php if ($folder_id_select == $document_folder_id) echo "selected"; ?> value="<?php echo $folder_id_select ?>"><?php echo $folder_name_select; ?></option>
|
||||
<option <?php if ($folder_id_select == $get_folder_id) echo "selected"; ?> value="<?php echo $folder_id_select ?>"><?php echo $folder_name_select; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -20,6 +20,13 @@
|
||||
</div>
|
||||
<input type="text" class="form-control" name="file_name" placeholder="File Name" value="<?php echo $file_name; ?>" required>
|
||||
</div>
|
||||
<label>Description <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-folder"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="file_description" placeholder="Description" value="<?php echo $file_description; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -39,7 +39,7 @@ if ($view == 1) {
|
||||
// Set Folder Location Var used when creating folders
|
||||
$folder_location = 1;
|
||||
|
||||
if ($get_folder_id == 0 && $_GET["q"]) {
|
||||
if ($get_folder_id == 0 && isset($_GET["q"])) {
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM files
|
||||
@@ -246,7 +246,7 @@ $num_of_files = mysqli_num_rows($sql);
|
||||
|
||||
<thead class="thead-light <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<td class="bg-light">
|
||||
<td class="bg-light pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)">
|
||||
</div>
|
||||
@@ -293,7 +293,7 @@ $num_of_files = mysqli_num_rows($sql);
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td class="bg-light">
|
||||
<td class="bg-light pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input bulk-select" type="checkbox" name="file_ids[]" value="<?php echo $file_id ?>">
|
||||
</div>
|
||||
@@ -331,6 +331,9 @@ $num_of_files = mysqli_num_rows($sql);
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#moveFileModal<?php echo $file_id; ?>">
|
||||
<i class="fas fa-fw fa-exchange-alt mr-2"></i>Move
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#linkAssetToFileModal<?php echo $file_id; ?>">
|
||||
<i class="fas fa-fw fa-desktop mr-2"></i>Asset
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_file=<?php echo $file_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
@@ -348,6 +351,8 @@ $num_of_files = mysqli_num_rows($sql);
|
||||
|
||||
require "client_file_move_modal.php";
|
||||
|
||||
require "client_file_link_asset_modal.php";
|
||||
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
|
||||
48
client_location_bulk_assign_tags_modal.php
Normal file
48
client_location_bulk_assign_tags_modal.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<div class="modal" id="bulkAssignTagsModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content bg-dark">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-tags mr-2"></i>Bulk Assign Tags</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body bg-white">
|
||||
<input type="hidden" name="bulk_remove_tags" value="0">
|
||||
|
||||
<div class="form-group form-check">
|
||||
<input type="checkbox" class="form-check-input" name="bulk_remove_tags" value="1">
|
||||
<label class="form-check-label text-danger">Remove Existing Tags</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Tags</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="bulk_tags[]" data-placeholder="Add some tags" multiple>
|
||||
<?php
|
||||
|
||||
$sql_tags_select = mysqli_query($mysqli, "SELECT * FROM tags WHERE tag_type = 2 ORDER BY tag_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_tags_select)) {
|
||||
$tag_id_select = intval($row['tag_id']);
|
||||
$tag_name_select = nullable_htmlentities($row['tag_name']);
|
||||
?>
|
||||
<option value="<?php echo $tag_id_select; ?>"><?php echo $tag_name_select; ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer bg-white">
|
||||
<button type="submit" name="bulk_assign_location_tags" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Assign</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -6,6 +6,21 @@ $order = "ASC";
|
||||
|
||||
require_once "inc_all_client.php";
|
||||
|
||||
// Tags Filter
|
||||
if (isset($_GET['tags']) && is_array($_GET['tags']) && !empty($_GET['tags'])) {
|
||||
// Sanitize each element of the status array
|
||||
$sanitizedTags = array();
|
||||
foreach ($_GET['tags'] as $tag) {
|
||||
// Escape each status to prevent SQL injection
|
||||
$sanitizedTags[] = "'" . intval($tag) . "'";
|
||||
}
|
||||
|
||||
// Convert the sanitized tags into a comma-separated string
|
||||
$sanitizedTagsString = implode(",", $sanitizedTags);
|
||||
$tag_query = "AND tags.tag_id IN ($sanitizedTagsString)";
|
||||
} else {
|
||||
$tag_query = '';
|
||||
}
|
||||
|
||||
//Rebuild URL
|
||||
$url_query_strings_sort = http_build_query($get_copy);
|
||||
@@ -16,6 +31,7 @@ $sql = mysqli_query(
|
||||
LEFT JOIN location_tags ON location_tags.location_id = locations.location_id
|
||||
LEFT JOIN tags ON tags.tag_id = location_tags.tag_id
|
||||
WHERE location_client_id = $client_id
|
||||
$tag_query
|
||||
AND location_$archive_query
|
||||
AND (location_name LIKE '%$q%' OR location_description LIKE '%$q%' OR location_address LIKE '%$q%' OR location_phone LIKE '%$phone_query%' OR tag_name LIKE '%$q%')
|
||||
GROUP BY location_id
|
||||
@@ -62,149 +78,216 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="float-right">
|
||||
<?php if($archived == 1){ ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=0" class="btn btn-primary"><i class="fa fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
<?php } else { ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=1" class="btn btn-default"><i class="fa fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
<?php } ?>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<select onchange="this.form.submit()" class="form-control select2" name="tags[]" data-placeholder="- Select Tags -" multiple>
|
||||
<?php $sql_tags = mysqli_query($mysqli, "SELECT * FROM tags WHERE tag_type = 2");
|
||||
while ($row = mysqli_fetch_array($sql_tags)) {
|
||||
$tag_id = intval($row['tag_id']);
|
||||
$tag_name = nullable_htmlentities($row['tag_name']); ?>
|
||||
|
||||
<option value="<?php echo $tag_id ?>" <?php if (isset($_GET['tags']) && is_array($_GET['tags']) && in_array($tag_id, $_GET['tags'])) { echo 'selected'; } ?>> <?php echo $tag_name ?> </option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="btn-group float-right">
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=<?php if($archived == 1){ echo 0; } else { echo 1; } ?>"
|
||||
class="btn btn-<?php if($archived == 1){ echo "primary"; } else { echo "default"; } ?>">
|
||||
<i class="fa fa-fw fa-archive mr-2"></i>Archived
|
||||
</a>
|
||||
<div class="dropdown ml-2" id="bulkActionButton" hidden>
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#bulkAssignTagsModal">
|
||||
<i class="fas fa-fw fa-tags mr-2"></i>Assign Tags
|
||||
</a>
|
||||
<?php if ($archived) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-info"
|
||||
type="submit" form="bulkActions" name="bulk_unarchive_locations">
|
||||
<i class="fas fa-fw fa-redo mr-2"></i>Unarchive
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-danger text-bold"
|
||||
type="submit" form="bulkActions" name="bulk_delete_locations">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</button>
|
||||
<?php } else { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-danger confirm-link"
|
||||
type="submit" form="bulkActions" name="bulk_archive_locations">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</button>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="<?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=location_name&order=<?php echo $disp; ?>">Name</a></th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=location_address&order=<?php echo $disp; ?>">Address</a></th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=location_phone&order=<?php echo $disp; ?>">Phone</a></th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=location_hours&order=<?php echo $disp; ?>">Hours</a></th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$location_id = intval($row['location_id']);
|
||||
$location_name = nullable_htmlentities($row['location_name']);
|
||||
$location_description = nullable_htmlentities($row['location_description']);
|
||||
$location_country = nullable_htmlentities($row['location_country']);
|
||||
$location_address = nullable_htmlentities($row['location_address']);
|
||||
$location_city = nullable_htmlentities($row['location_city']);
|
||||
$location_state = nullable_htmlentities($row['location_state']);
|
||||
$location_zip = nullable_htmlentities($row['location_zip']);
|
||||
$location_phone = formatPhoneNumber($row['location_phone']);
|
||||
if (empty($location_phone)) {
|
||||
$location_phone_display = "-";
|
||||
} else {
|
||||
$location_phone_display = $location_phone;
|
||||
}
|
||||
$location_hours = nullable_htmlentities($row['location_hours']);
|
||||
if (empty($location_hours)) {
|
||||
$location_hours_display = "-";
|
||||
} else {
|
||||
$location_hours_display = $location_hours;
|
||||
}
|
||||
$location_photo = nullable_htmlentities($row['location_photo']);
|
||||
$location_notes = nullable_htmlentities($row['location_notes']);
|
||||
$location_created_at = nullable_htmlentities($row['location_created_at']);
|
||||
$location_contact_id = intval($row['location_contact_id']);
|
||||
$location_primary = intval($row['location_primary']);
|
||||
if ( $location_primary == 1 ) {
|
||||
$location_primary_display = "<small class='text-success'><i class='fa fa-fw fa-check'></i> Primary</small>";
|
||||
} else {
|
||||
$location_primary_display = "";
|
||||
}
|
||||
|
||||
// Tags
|
||||
|
||||
$location_tag_name_display_array = array();
|
||||
$location_tag_id_array = array();
|
||||
$sql_location_tags = mysqli_query($mysqli, "SELECT * FROM location_tags LEFT JOIN tags ON location_tags.tag_id = tags.tag_id WHERE location_tags.location_id = $location_id ORDER BY tag_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_location_tags)) {
|
||||
|
||||
$location_tag_id = intval($row['tag_id']);
|
||||
$location_tag_name = nullable_htmlentities($row['tag_name']);
|
||||
$location_tag_color = nullable_htmlentities($row['tag_color']);
|
||||
if (empty($location_tag_color)) {
|
||||
$location_tag_color = "dark";
|
||||
}
|
||||
$location_tag_icon = nullable_htmlentities($row['tag_icon']);
|
||||
if (empty($location_tag_icon)) {
|
||||
$location_tag_icon = "tag";
|
||||
}
|
||||
|
||||
$location_tag_id_array[] = $location_tag_id;
|
||||
$location_tag_name_display_array[] = "<a href='client_locations.php?client_id=$client_id&q=$location_tag_name'><span class='badge text-light p-1 mr-1' style='background-color: $location_tag_color;'><i class='fa fa-fw fa-$location_tag_icon mr-2'></i>$location_tag_name</span></a>";
|
||||
}
|
||||
$location_tags_display = implode('', $location_tag_name_display_array);
|
||||
|
||||
?>
|
||||
<form id="bulkActions" action="post.php" method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="<?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<td>
|
||||
<a class="text-dark" href="#" data-toggle="modal" data-target="#editLocationModal<?php echo $location_id; ?>">
|
||||
<div class="media">
|
||||
<i class="fa fa-fw fa-2x fa-map-marker-alt mr-3"></i>
|
||||
<div class="media-body">
|
||||
<div <?php if($location_primary) { echo "class='text-bold'"; } ?>><?php echo $location_name; ?></div>
|
||||
<div><small class="text-secondary"><?php echo $location_description; ?></small></div>
|
||||
<div><?php echo $location_primary_display; ?></div>
|
||||
<?php
|
||||
if (!empty($location_tags_display)) { ?>
|
||||
<div class="mt-1">
|
||||
<?php echo $location_tags_display; ?>
|
||||
</div>
|
||||
<td class="bg-light pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)">
|
||||
</div>
|
||||
</td>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=location_name&order=<?php echo $disp; ?>">Name</a></th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=location_address&order=<?php echo $disp; ?>">Address</a></th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=location_phone&order=<?php echo $disp; ?>">Phone</a></th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=location_hours&order=<?php echo $disp; ?>">Hours</a></th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$location_id = intval($row['location_id']);
|
||||
$location_name = nullable_htmlentities($row['location_name']);
|
||||
$location_description = nullable_htmlentities($row['location_description']);
|
||||
$location_country = nullable_htmlentities($row['location_country']);
|
||||
$location_address = nullable_htmlentities($row['location_address']);
|
||||
$location_city = nullable_htmlentities($row['location_city']);
|
||||
$location_state = nullable_htmlentities($row['location_state']);
|
||||
$location_zip = nullable_htmlentities($row['location_zip']);
|
||||
$location_phone = formatPhoneNumber($row['location_phone']);
|
||||
if (empty($location_phone)) {
|
||||
$location_phone_display = "-";
|
||||
} else {
|
||||
$location_phone_display = $location_phone;
|
||||
}
|
||||
$location_hours = nullable_htmlentities($row['location_hours']);
|
||||
if (empty($location_hours)) {
|
||||
$location_hours_display = "-";
|
||||
} else {
|
||||
$location_hours_display = $location_hours;
|
||||
}
|
||||
$location_photo = nullable_htmlentities($row['location_photo']);
|
||||
$location_notes = nullable_htmlentities($row['location_notes']);
|
||||
$location_created_at = nullable_htmlentities($row['location_created_at']);
|
||||
$location_archived_at = nullable_htmlentities($row['location_archived_at']);
|
||||
$location_contact_id = intval($row['location_contact_id']);
|
||||
$location_primary = intval($row['location_primary']);
|
||||
if ( $location_primary == 1 ) {
|
||||
$location_primary_display = "<small class='text-success'><i class='fa fa-fw fa-check'></i> Primary</small>";
|
||||
} else {
|
||||
$location_primary_display = "";
|
||||
}
|
||||
|
||||
// Tags
|
||||
|
||||
$location_tag_name_display_array = array();
|
||||
$location_tag_id_array = array();
|
||||
$sql_location_tags = mysqli_query($mysqli, "SELECT * FROM location_tags LEFT JOIN tags ON location_tags.tag_id = tags.tag_id WHERE location_tags.location_id = $location_id ORDER BY tag_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_location_tags)) {
|
||||
|
||||
$location_tag_id = intval($row['tag_id']);
|
||||
$location_tag_name = nullable_htmlentities($row['tag_name']);
|
||||
$location_tag_color = nullable_htmlentities($row['tag_color']);
|
||||
if (empty($location_tag_color)) {
|
||||
$location_tag_color = "dark";
|
||||
}
|
||||
$location_tag_icon = nullable_htmlentities($row['tag_icon']);
|
||||
if (empty($location_tag_icon)) {
|
||||
$location_tag_icon = "tag";
|
||||
}
|
||||
|
||||
$location_tag_id_array[] = $location_tag_id;
|
||||
$location_tag_name_display_array[] = "<a href='client_locations.php?client_id=$client_id&q=$location_tag_name'><span class='badge text-light p-1 mr-1' style='background-color: $location_tag_color;'><i class='fa fa-fw fa-$location_tag_icon mr-2'></i>$location_tag_name</span></a>";
|
||||
}
|
||||
$location_tags_display = implode('', $location_tag_name_display_array);
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td class="pr-0 bg-light">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input bulk-select" type="checkbox" name="location_ids[]" value="<?php echo $location_id ?>">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a class="text-dark" href="#" data-toggle="modal" data-target="#editLocationModal<?php echo $location_id; ?>">
|
||||
<div class="media">
|
||||
<i class="fa fa-fw fa-2x fa-map-marker-alt mr-3"></i>
|
||||
<div class="media-body">
|
||||
<div <?php if($location_primary) { echo "class='text-bold'"; } ?>><?php echo $location_name; ?></div>
|
||||
<div><small class="text-secondary"><?php echo $location_description; ?></small></div>
|
||||
<div><?php echo $location_primary_display; ?></div>
|
||||
<?php
|
||||
if (!empty($location_tags_display)) { ?>
|
||||
<div class="mt-1">
|
||||
<?php echo $location_tags_display; ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</td>
|
||||
<td><a href="//maps.<?php echo $session_map_source; ?>.com?q=<?php echo "$location_address $location_zip"; ?>" target="_blank"><?php echo $location_address; ?><br><?php echo "$location_city $location_state $location_zip"; ?></a></td>
|
||||
<td><?php echo $location_phone_display; ?></td>
|
||||
<td><?php echo $location_hours_display; ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editLocationModal<?php echo $location_id; ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<?php if ($session_user_role == 3 && $location_primary == 0) { ?>
|
||||
<?php if ($location_archived_at) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-info confirm-link" href="post.php?unarchive_location=<?php echo $location_id; ?>">
|
||||
<i class="fas fa-fw fa-redo mr-2"></i>Unarchive
|
||||
</a>
|
||||
<?php if ($config_destructive_deletes_enable) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_location=<?php echo $location_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php } else { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_location=<?php echo $location_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</a>
|
||||
<?php } ?>
|
||||
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</td>
|
||||
<td><a href="//maps.<?php echo $session_map_source; ?>.com?q=<?php echo "$location_address $location_zip"; ?>" target="_blank"><?php echo $location_address; ?><br><?php echo "$location_city $location_state $location_zip"; ?></a></td>
|
||||
<td><?php echo $location_phone_display; ?></td>
|
||||
<td><?php echo $location_hours_display; ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editLocationModal<?php echo $location_id; ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<?php if ($session_user_role == 3 && $location_primary == 0) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_location=<?php echo $location_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</a>
|
||||
<?php if ($config_destructive_deletes_enable) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_location=<?php echo $location_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php require "client_location_edit_modal.php";
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php require "client_location_edit_modal.php";
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php require_once "client_location_bulk_assign_tags_modal.php"; ?>
|
||||
</form>
|
||||
<?php require_once "pagination.php";
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/bulk_actions.js"></script>
|
||||
|
||||
<?php
|
||||
|
||||
require_once "client_location_add_modal.php";
|
||||
|
||||
@@ -55,12 +55,12 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Username</label>
|
||||
<label>Username / ID</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="username" placeholder="Username">
|
||||
<input type="text" class="form-control" name="username" placeholder="Username or ID">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -56,12 +56,12 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Username</label>
|
||||
<label>Username / ID</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="username" placeholder="Username" value="<?php echo $login_username; ?>">
|
||||
<input type="text" class="form-control" name="username" placeholder="Username or ID" value="<?php echo $login_username; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ $sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM logins
|
||||
WHERE login_client_id = $client_id
|
||||
AND login_$archive_query
|
||||
AND (login_name LIKE '%$q%' OR login_description LIKE '%$q%' OR login_uri LIKE '%$q%')
|
||||
ORDER BY login_important DESC, $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
@@ -58,118 +59,189 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="float-right">
|
||||
<div class="btn-group float-right">
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=<?php if($archived == 1){ echo 0; } else { echo 1; } ?>"
|
||||
class="btn btn-<?php if($archived == 1){ echo "primary"; } else { echo "default"; } ?>">
|
||||
<i class="fa fa-fw fa-archive mr-2"></i>Archived
|
||||
</a>
|
||||
<div class="dropdown ml-2" id="bulkActionButton" hidden>
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<?php if ($archived) { ?>
|
||||
<button class="dropdown-item text-info"
|
||||
type="submit" form="bulkActions" name="bulk_unarchive_logins">
|
||||
<i class="fas fa-fw fa-redo mr-2"></i>Unarchive
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-danger text-bold"
|
||||
type="submit" form="bulkActions" name="bulk_delete_logins">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</button>
|
||||
<?php } else { ?>
|
||||
<button class="dropdown-item text-danger confirm-link"
|
||||
type="submit" form="bulkActions" name="bulk_archive_logins">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</button>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) {
|
||||
echo "d-none";
|
||||
} ?>">
|
||||
<tr>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=login_name&order=<?php echo $disp; ?>">Name</a></th>
|
||||
<th>Username</th>
|
||||
<th>Password</th>
|
||||
<th>OTP</th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=login_uri&order=<?php echo $disp; ?>">URI</a></th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$login_id = intval($row['login_id']);
|
||||
$login_name = nullable_htmlentities($row['login_name']);
|
||||
$login_description = nullable_htmlentities($row['login_description']);
|
||||
$login_uri = nullable_htmlentities($row['login_uri']);
|
||||
if (empty($login_uri)) {
|
||||
$login_uri_display = "-";
|
||||
} else {
|
||||
$login_uri_display = truncate($login_uri,40) . "<button class='btn btn-sm clipboardjs' type='button' data-clipboard-text='$login_uri'><i class='far fa-copy text-secondary'></i></button>";
|
||||
}
|
||||
$login_uri_2 = nullable_htmlentities($row['login_uri_2']);
|
||||
$login_username = nullable_htmlentities(decryptLoginEntry($row['login_username']));
|
||||
if (empty($login_username)) {
|
||||
$login_username_display = "-";
|
||||
} else {
|
||||
$login_username_display = "$login_username<button class='btn btn-sm clipboardjs' type='button' data-clipboard-text='$login_username'><i class='far fa-copy text-secondary'></i></button>";
|
||||
}
|
||||
$login_password = nullable_htmlentities(decryptLoginEntry($row['login_password']));
|
||||
$login_otp_secret = nullable_htmlentities($row['login_otp_secret']);
|
||||
$login_id_with_secret = '"' . $row['login_id'] . '","' . $row['login_otp_secret'] . '"';
|
||||
if (empty($login_otp_secret)) {
|
||||
$otp_display = "-";
|
||||
} else {
|
||||
$otp_display = "<span onmouseenter='showOTPViaLoginID($login_id)'><i class='far fa-clock'></i> <span id='otp_$login_id'><i>Hover..</i></span></span>";
|
||||
}
|
||||
$login_note = nullable_htmlentities($row['login_note']);
|
||||
$login_important = intval($row['login_important']);
|
||||
$login_contact_id = intval($row['login_contact_id']);
|
||||
$login_vendor_id = intval($row['login_vendor_id']);
|
||||
$login_asset_id = intval($row['login_asset_id']);
|
||||
$login_software_id = intval($row['login_software_id']);
|
||||
|
||||
?>
|
||||
<tr class="<?php if (!empty($login_important)) { echo "text-bold"; } ?>">
|
||||
<td>
|
||||
<a class="text-dark" href="#" data-toggle="modal" data-target="#editLoginModal<?php echo $login_id; ?>">
|
||||
<div class="media">
|
||||
<i class="fa fa-fw fa-2x fa-key mr-3"></i>
|
||||
<div class="media-body">
|
||||
<div><?php echo $login_name; ?></div>
|
||||
<div><small class="text-secondary"><?php echo $login_description; ?></small></div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</td>
|
||||
<td><?php echo $login_username_display; ?></td>
|
||||
<td>
|
||||
<button class="btn p-0" type="button" data-toggle="popover" data-trigger="focus" data-placement="top" data-content="<?php echo $login_password; ?>"><i class="fas fa-2x fa-ellipsis-h text-secondary"></i><i class="fas fa-2x fa-ellipsis-h text-secondary"></i></button><button class="btn btn-sm clipboardjs" type="button" data-clipboard-text="<?php echo $login_password; ?>"><i class="far fa-copy text-secondary"></i></button>
|
||||
</td>
|
||||
<td><?php echo $otp_display; ?></td>
|
||||
<td><?php echo $login_uri_display; ?></td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group">
|
||||
<?php if ($login_uri) { ?>
|
||||
<a href="<?php echo $login_uri; ?>" target="_blank" class="btn btn-default btn-sm"><i class="fa fa-fw fa-external-link-alt"></i></a>
|
||||
<?php } ?>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editLoginModal<?php echo $login_id; ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#shareModal" onclick="populateShareModal(<?php echo "$client_id, 'Login', $login_id"; ?>)">
|
||||
<i class="fas fa-fw fa-share mr-2"></i>Share
|
||||
</a>
|
||||
<?php if ($session_user_role == 3) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold" href="post.php?delete_login=<?php echo $login_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<form id="bulkActions" action="post.php" method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<td class="pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)">
|
||||
</div>
|
||||
</td>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=login_name&order=<?php echo $disp; ?>">Name</a></th>
|
||||
<th>Username / ID</th>
|
||||
<th>Password / Key</th>
|
||||
<th>OTP</th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=login_uri&order=<?php echo $disp; ?>">URI</a></th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$login_id = intval($row['login_id']);
|
||||
$login_name = nullable_htmlentities($row['login_name']);
|
||||
$login_description = nullable_htmlentities($row['login_description']);
|
||||
$login_uri = nullable_htmlentities($row['login_uri']);
|
||||
if (empty($login_uri)) {
|
||||
$login_uri_display = "-";
|
||||
} else {
|
||||
$login_uri_display = truncate($login_uri,40) . "<button class='btn btn-sm clipboardjs' type='button' data-clipboard-text='$login_uri'><i class='far fa-copy text-secondary'></i></button>";
|
||||
}
|
||||
$login_uri_2 = nullable_htmlentities($row['login_uri_2']);
|
||||
$login_username = nullable_htmlentities(decryptLoginEntry($row['login_username']));
|
||||
if (empty($login_username)) {
|
||||
$login_username_display = "-";
|
||||
} else {
|
||||
$login_username_display = "$login_username<button class='btn btn-sm clipboardjs' type='button' data-clipboard-text='$login_username'><i class='far fa-copy text-secondary'></i></button>";
|
||||
}
|
||||
$login_password = nullable_htmlentities(decryptLoginEntry($row['login_password']));
|
||||
$login_otp_secret = nullable_htmlentities($row['login_otp_secret']);
|
||||
$login_id_with_secret = '"' . $row['login_id'] . '","' . $row['login_otp_secret'] . '"';
|
||||
if (empty($login_otp_secret)) {
|
||||
$otp_display = "-";
|
||||
} else {
|
||||
$otp_display = "<span onmouseenter='showOTPViaLoginID($login_id)'><i class='far fa-clock'></i> <span id='otp_$login_id'><i>Hover..</i></span></span>";
|
||||
}
|
||||
$login_note = nullable_htmlentities($row['login_note']);
|
||||
$login_created_at = nullable_htmlentities($row['login_created_at']);
|
||||
$login_archived_at = nullable_htmlentities($row['login_archived_at']);
|
||||
$login_important = intval($row['login_important']);
|
||||
$login_contact_id = intval($row['login_contact_id']);
|
||||
$login_vendor_id = intval($row['login_vendor_id']);
|
||||
$login_asset_id = intval($row['login_asset_id']);
|
||||
$login_software_id = intval($row['login_software_id']);
|
||||
|
||||
require "client_login_edit_modal.php";
|
||||
}
|
||||
?>
|
||||
<tr class="<?php if (!empty($login_important)) { echo "text-bold"; } ?>">
|
||||
<td class="pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input bulk-select" type="checkbox" name="login_ids[]" value="<?php echo $login_id ?>">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a class="text-dark" href="#" data-toggle="modal" data-target="#editLoginModal<?php echo $login_id; ?>">
|
||||
<div class="media">
|
||||
<i class="fa fa-fw fa-2x fa-key mr-3"></i>
|
||||
<div class="media-body">
|
||||
<div><?php echo $login_name; ?></div>
|
||||
<div><small class="text-secondary"><?php echo $login_description; ?></small></div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</td>
|
||||
<td><?php echo $login_username_display; ?></td>
|
||||
<td>
|
||||
<button class="btn p-0" type="button" data-toggle="popover" data-trigger="focus" data-placement="top" data-content="<?php echo $login_password; ?>"><i class="fas fa-2x fa-ellipsis-h text-secondary"></i><i class="fas fa-2x fa-ellipsis-h text-secondary"></i></button><button class="btn btn-sm clipboardjs" type="button" data-clipboard-text="<?php echo $login_password; ?>"><i class="far fa-copy text-secondary"></i></button>
|
||||
</td>
|
||||
<td><?php echo $otp_display; ?></td>
|
||||
<td><?php echo $login_uri_display; ?></td>
|
||||
<td class="text-center">
|
||||
<div class="btn-group">
|
||||
<?php if ( !empty($login_uri) || !empty($login_uri_2) ) { ?>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-default btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fa fa-fw fa-external-link-alt"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<?php if ($login_uri) { ?>
|
||||
<a href="<?php echo $login_uri; ?>" alt="<?php echo $login_uri; ?>" target="_blank" class="dropdown-item" >
|
||||
<i class="fa fa-fw fa-external-link-alt"></i> <?php echo truncate($login_uri,40); ?>
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php if ($login_uri_2) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="<?php echo $login_uri_2; ?>" target="_blank" class="dropdown-item" >
|
||||
<i class="fa fa-fw fa-external-link-alt"></i> <?php echo truncate($login_uri_2,40); ?>
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editLoginModal<?php echo $login_id; ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#shareModal" onclick="populateShareModal(<?php echo "$client_id, 'Login', $login_id"; ?>)">
|
||||
<i class="fas fa-fw fa-share mr-2"></i>Share
|
||||
</a>
|
||||
<?php if ($session_user_role == 3) { ?>
|
||||
<?php if ($login_archived_at) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-info confirm-link" href="post.php?unarchive_login=<?php echo $login_id; ?>">
|
||||
<i class="fas fa-fw fa-redo mr-2"></i>Unarchive
|
||||
</a>
|
||||
<?php if ($config_destructive_deletes_enable) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_login=<?php echo $login_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php } else { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_login=<?php echo $login_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
?>
|
||||
<?php
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
require "client_login_edit_modal.php";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
<?php require_once "pagination.php";
|
||||
?>
|
||||
</div>
|
||||
@@ -181,6 +253,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<!-- Include script to generate readable passwords for login entries -->
|
||||
<script src="js/logins_generate_password.js"></script>
|
||||
|
||||
<script src="js/bulk_actions.js"></script>
|
||||
|
||||
<?php
|
||||
|
||||
require_once "client_login_add_modal.php";
|
||||
|
||||
153
client_rack_add_modal.php
Normal file
153
client_rack_add_modal.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<div class="modal" id="addRackModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content bg-dark">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-server mr-2"></i>New Rack</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
|
||||
<div class="modal-body bg-white">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-details">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-notes">Notes</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-details">
|
||||
|
||||
<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-server"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Rack name" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="description" placeholder="Description of the rack">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Type <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-server"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="type" required>
|
||||
<option value="">- Type -</option>
|
||||
<?php foreach($rack_type_select_array as $rack_type) { ?>
|
||||
<option><?php echo $rack_type; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Model</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="make" placeholder="ex StarTech 12U Open Frame">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Depth</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ruler"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="depth" placeholder="Rack Depth eg 800 mm or 31.5 Inches">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Number of Units <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-sort-numeric-up-alt"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="units" placeholder="Number of Units" min="1" max="44" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Physical Location</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="physical_location" placeholder="Physical location eg. Floor 2, Closet B">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Location</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="location">
|
||||
<option value="">- Location -</option>
|
||||
<?php
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM locations WHERE location_archived_at IS NULL AND location_client_id = $client_id ORDER BY location_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$location_id = intval($row['location_id']);
|
||||
$location_name = nullable_htmlentities($row['location_name']);
|
||||
?>
|
||||
<option value="<?php echo $location_id; ?>"><?php echo $location_name; ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-notes">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Upload Photo</label>
|
||||
<input type="file" class="form-control-file" name="file">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer bg-white">
|
||||
<button type="submit" name="add_rack" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
158
client_rack_edit_modal.php
Normal file
158
client_rack_edit_modal.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<div class="modal" id="editRackModal<?php echo $rack_id; ?>" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content bg-dark">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-server mr-2"></i>Editing Rack: <strong><?php echo $rack_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
|
||||
<input type="hidden" name="rack_id" value="<?php echo $rack_id; ?>">
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
|
||||
<div class="modal-body bg-white">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="pill" href="#pills-rack-details<?php echo $rack_id; ?>">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-rack-notes<?php echo $rack_id; ?>">Notes</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-rack-details<?php echo $rack_id; ?>">
|
||||
|
||||
<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-server"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Rack name" value="<?php echo $rack_name; ?>" required autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-angle-right"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="description" placeholder="Description of the rack" value="<?php echo $rack_description; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Type <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-server"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="type" required>
|
||||
<option value="">- Type -</option>
|
||||
<?php foreach($rack_type_select_array as $rack_type_select) { ?>
|
||||
<option <?php if ($rack_type == $rack_type_select) { echo "selected"; } ?>><?php echo $rack_type_select; ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Model</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="make" placeholder="ex StarTech 12U Open Frame" value="<?php echo $rack_model; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Depth</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ruler"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="depth" placeholder="Rack Depth eg 800 mm or 31.5 Inches" value="<?php echo $rack_depth; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Number of Units <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-sort-numeric-up-alt"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="units" placeholder="Number of Units" min="1" max="44" value="<?php echo $rack_units; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Physical Location</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="physical_location" placeholder="Physical location eg. Floor 2, Closet B" value="<?php echo $rack_physical_location; ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Location</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="location">
|
||||
<option value="">- Location -</option>
|
||||
<?php
|
||||
|
||||
$sql_location_select = mysqli_query($mysqli, "SELECT * FROM locations WHERE location_archived_at IS NULL AND location_client_id = $client_id ORDER BY location_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_location_select)) {
|
||||
$location_id_select = intval($row['location_id']);
|
||||
$location_name_select = nullable_htmlentities($row['location_name']);
|
||||
?>
|
||||
<option <?php if ($rack_location_id == $location_id_select) { echo "selected"; } ?> value="<?php echo $location_id_select; ?>"><?php echo $location_name_select; ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-rack-notes<?php echo $rack_id; ?>">
|
||||
|
||||
<?php if ($rack_photo) { ?>
|
||||
<img class="img-fluid p-3" alt="rack_photo" src="<?php echo "uploads/clients/$client_id/$rack_photo"; ?>">
|
||||
<?php } ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Upload Photo</label>
|
||||
<input type="file" class="form-control-file" name="file">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"><?php echo $rack_notes; ?></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer bg-white">
|
||||
<button type="submit" name="edit_rack" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
78
client_rack_unit_add_modal.php
Normal file
78
client_rack_unit_add_modal.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<div class="modal" id="addRackUnitModal<?php echo $rack_id; ?>" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content bg-dark">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-server mr-2"></i>Adding Device to Rack <strong><?php echo $rack_name; ?></strong></h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
<input type="hidden" name="rack_id" value="<?php echo $rack_id; ?>">
|
||||
|
||||
<div class="modal-body bg-white">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Custom Device</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-server"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="name" placeholder="Device Name">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Or Select a Device</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="asset">
|
||||
<option value="">- Asset -</option>
|
||||
<?php
|
||||
// Fetch IDs of all assets already assigned to any rack
|
||||
$assigned_assets = [];
|
||||
$assigned_sql = mysqli_query($mysqli, "SELECT unit_asset_id FROM rack_units");
|
||||
while ($assigned_row = mysqli_fetch_assoc($assigned_sql)) {
|
||||
$assigned_assets[] = intval($assigned_row['unit_asset_id']);
|
||||
}
|
||||
$assigned_assets_list = implode(',', $assigned_assets);
|
||||
$assigned_assets_list = empty($assigned_assets_list) ? '0' : $assigned_assets_list;
|
||||
|
||||
// Fetch assets not assigned to any rack
|
||||
$sql_assets = mysqli_query($mysqli, "SELECT * FROM assets WHERE asset_archived_at IS NULL AND asset_client_id = $client_id AND asset_id NOT IN ($assigned_assets_list) ORDER BY asset_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_assets)) {
|
||||
$asset_id = intval($row['asset_id']);
|
||||
$asset_name = nullable_htmlentities($row['asset_name']);
|
||||
?>
|
||||
<option value="<?php echo $asset_id; ?>"><?php echo $asset_name; ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Unit Number Start - End <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-sort-numeric-up-alt"></i></span>
|
||||
</div>
|
||||
<input type="number" class="form-control" name="unit_start" placeholder="Unit Start" min="1" max="<?php echo $rack_units; ?>" required>
|
||||
<input type="number" class="form-control" name="unit_end" placeholder="Unit End" min="1" max="<?php echo $rack_units; ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer bg-white">
|
||||
<button type="submit" name="add_rack_unit" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Add to Rack</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
243
client_racks.php
Normal file
243
client_racks.php
Normal file
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "rack_name";
|
||||
$order = "ASC";
|
||||
|
||||
require_once "inc_all_client.php";
|
||||
|
||||
// Rebuild URL
|
||||
$url_query_strings_sort = http_build_query($get_copy);
|
||||
|
||||
$sql = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT SQL_CALC_FOUND_ROWS * FROM racks
|
||||
LEFT JOIN locations ON location_id = rack_location_id
|
||||
WHERE rack_client_id = $client_id
|
||||
AND rack_$archive_query
|
||||
AND (rack_name LIKE '%$q%' OR rack_type LIKE '%$q%' OR rack_units LIKE '%$q%')
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
|
||||
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fas fa-fw fa-server mr-2"></i>Network Racks</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addRackModal">
|
||||
<i class="fas fa-plus mr-2"></i>New Rack
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form autocomplete="off">
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
<input type="hidden" name="archived" value="<?php echo $archived; ?>">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="input-group mb-3 mb-md-0">
|
||||
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search Racks">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-dark"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="float-right">
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=<?php if($archived == 1){ echo 0; } else { echo 1; } ?>"
|
||||
class="btn btn-<?php if($archived == 1){ echo "primary"; } else { echo "default"; } ?>">
|
||||
<i class="fa fa-fw fa-archive mr-2"></i>Archived
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$rack_id = intval($row['rack_id']);
|
||||
$rack_name = nullable_htmlentities($row['rack_name']);
|
||||
$rack_description = nullable_htmlentities($row['rack_description']);
|
||||
$rack_model = nullable_htmlentities($row['rack_model']);
|
||||
$rack_depth = nullable_htmlentities($row['rack_depth']);
|
||||
$rack_type = nullable_htmlentities($row['rack_type']);
|
||||
$rack_units = intval($row['rack_units']);
|
||||
$rack_photo = nullable_htmlentities($row['rack_photo']);
|
||||
$rack_physical_location = nullable_htmlentities($row['rack_physical_location']);
|
||||
$rack_notes = nullable_htmlentities($row['rack_notes']);
|
||||
$rack_location_id = nullable_htmlentities($row['rack_location_id']);
|
||||
$rack_location_name = nullable_htmlentities($row['location_name']);
|
||||
$rack_created_at = nullable_htmlentities($row['rack_created_at']);
|
||||
|
||||
// Fetch rack units
|
||||
$unit_sql = mysqli_query($mysqli, "SELECT * FROM rack_units LEFT JOIN assets ON unit_asset_id = asset_id WHERE unit_rack_id = $rack_id ORDER BY unit_start_number ASC");
|
||||
$rack_units_data = [];
|
||||
while ($unit_row = mysqli_fetch_assoc($unit_sql)) {
|
||||
$rack_units_data[] = $unit_row;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="col-md-6">
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-fw fa-server mr-2"></i><?php echo "$rack_name - $rack_units"; ?>U</h3>
|
||||
|
||||
<div class="card-tools">
|
||||
<div class="dropdown dropleft">
|
||||
<button class="btn btn-tool" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-fw fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#addRackUnitModal<?php echo $rack_id; ?>">
|
||||
<i class="fas fa-fw fa-plus text-secondary mr-2"></i>Add Device
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#editRackModal<?php echo $rack_id; ?>">
|
||||
<i class="fas fa-fw fa-edit text-secondary mr-2"></i>Edit
|
||||
</a>
|
||||
<?php if ($session_user_role == 3) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_rack=<?php echo $rack_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_rack=<?php echo $rack_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<?php if ($rack_photo) { ?>
|
||||
<img class="img-fluid mb-3" alt="rack_photo" src="<?php echo "uploads/clients/$client_id/$rack_photo"; ?>">
|
||||
<?php } ?>
|
||||
<table class="table table-sm table-borderless">
|
||||
<tbody>
|
||||
<?php if ($rack_description) { ?>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<td><?php echo $rack_description; ?></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php if ($rack_type) { ?>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<td><?php echo $rack_type; ?></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php if ($rack_model) { ?>
|
||||
<tr>
|
||||
<th>Model</th>
|
||||
<td><?php echo $rack_model; ?></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php if ($rack_depth) { ?>
|
||||
<tr>
|
||||
<th>Depth</th>
|
||||
<td><?php echo $rack_depth; ?></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php if ($rack_location_name) { ?>
|
||||
<tr>
|
||||
<th>Location</th>
|
||||
<td><?php echo $rack_location_name; ?></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php if ($rack_physical_location) { ?>
|
||||
<tr>
|
||||
<th>Physical Location</th>
|
||||
<td><?php echo $rack_physical_location; ?></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php if ($rack_notes) { ?>
|
||||
<tr>
|
||||
<th>Notes</th>
|
||||
<td><?php echo $rack_notes; ?></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<table class="table table-bordered">
|
||||
<?php
|
||||
for ($i = $rack_units; $i >= 1; $i--) {
|
||||
$unit_devices = [];
|
||||
foreach ($rack_units_data as $unit_data) {
|
||||
if ($i >= $unit_data['unit_start_number'] && $i <= $unit_data['unit_end_number']) {
|
||||
$unit_devices[] = [
|
||||
'unit_id' => intval($unit_data['unit_id']),
|
||||
'device' => nullable_htmlentities($unit_data['unit_device']),
|
||||
'asset_id' => intval($unit_data['asset_id']),
|
||||
'asset_name' => nullable_htmlentities($unit_data['asset_name']),
|
||||
'asset_type' => nullable_htmlentities($unit_data['asset_type']),
|
||||
'icon' => getAssetIcon($unit_data['asset_type'])
|
||||
];
|
||||
}
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td class="px-0 text-center bg-light"><?php echo sprintf('%02d', $i); ?></td>
|
||||
<td class="text-center">
|
||||
<?php foreach ($unit_devices as $unit_device) { ?>
|
||||
<?php echo $unit_device['device']; ?>
|
||||
<?php if ($unit_device['asset_name']) { ?>
|
||||
<i class="fa fa-fw fa-<?php echo $unit_device['icon']; ?> mr-1"></i>
|
||||
<a href="client_asset_details.php?client_id=<?php echo $client_id; ?>&asset_id=<?php echo $unit_device['asset_id']; ?>" target="_blank"><?php echo $unit_device['asset_name']; ?><i class="fas fa-fw fa-external-link-alt ml-1"></i></a>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<?php if(!empty($unit_devices)) { ?>
|
||||
<td class="px-0 text-right">
|
||||
<div class="dropdown dropleft">
|
||||
<button class="btn btn-tool" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-fw fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<?php foreach ($unit_devices as $unit_device) { ?>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?remove_rack_unit=<?php echo $unit_device['unit_id']; ?>">
|
||||
<i class="fas fa-fw fa-minus mr-2"></i>Remove
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<?php } ?>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php require "client_rack_edit_modal.php"; ?>
|
||||
<?php require "client_rack_unit_add_modal.php"; ?>
|
||||
<?php } ?>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
require_once "client_rack_add_modal.php";
|
||||
require_once "footer.php";
|
||||
?>
|
||||
@@ -49,13 +49,29 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="btn-group float-right">
|
||||
<div class="dropdown ml-2" id="bulkActionButton" hidden>
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<button class="dropdown-item text-danger text-bold"
|
||||
type="submit" form="bulkActions" name="bulk_delete_recurring_tickets">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
|
||||
<div class="table-responsive-sm">
|
||||
|
||||
<form id="bulk_actions" action="post.php" method="post">
|
||||
<form id="bulkActions" action="post.php" method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
@@ -63,7 +79,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
echo "d-none";
|
||||
} ?>">
|
||||
<tr>
|
||||
<th><a class="text-dark">Select</a></th>
|
||||
<td class="pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)">
|
||||
</div>
|
||||
</td>
|
||||
<th><a class="text-dark">Subject</a></th>
|
||||
<th><a class="text-dark">Priority</a></th>
|
||||
<th><a class="text-dark">Frequency</a></th>
|
||||
@@ -83,9 +103,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
?>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<td class="pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="scheduled_ticket_ids[]" onchange="showBulkDeleteButton()" value="<?php echo $scheduled_ticket_id ?>">
|
||||
<input class="form-check-input bulk-select" type="checkbox" name="scheduled_ticket_ids[]" value="<?php echo $scheduled_ticket_id ?>">
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -128,10 +148,6 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input type="submit" id="button_bulk_delete" form="bulk_actions" name="bulk_delete_recurring_tickets" value="Bulk Delete" hidden>
|
||||
</div>
|
||||
|
||||
<?php require_once 'pagination.php';
|
||||
?>
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
mysqli_data_seek($sql_assets, 0);
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_assets)) {
|
||||
if (!empty($row['asset_ip'])) {
|
||||
$ip = '('.nullable_htmlentities($row["asset_ip"]).')';
|
||||
if (!empty($row['interface_ip'])) {
|
||||
$ip = '('.nullable_htmlentities($row["interface_ip"]).')';
|
||||
} else {
|
||||
$ip = '';
|
||||
}
|
||||
|
||||
@@ -137,8 +137,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
$mysqli,
|
||||
"SELECT * FROM service_assets
|
||||
LEFT JOIN assets ON service_assets.asset_id = assets.asset_id
|
||||
LEFT JOIN asset_interfaces ON interface_asset_id = assets.asset_id AND interface_primary = 1
|
||||
LEFT JOIN logins ON service_assets.asset_id = logins.login_asset_id
|
||||
LEFT JOIN networks ON assets.asset_network_id = networks.network_id
|
||||
LEFT JOIN networks ON interface_network_id = networks.network_id
|
||||
LEFT JOIN locations ON assets.asset_location_id = locations.location_id
|
||||
WHERE service_id = $service_id"
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<aside class="main-sidebar sidebar-dark-<?php echo nullable_htmlentities($config_theme); ?> d-print-none">
|
||||
|
||||
<a class="brand-link pb-1 mt-1" href="clients.php">
|
||||
<p class="h5"><i class="nav-icon fas fa-arrow-left ml-3 mr-2"></i> Back | <strong><?php echo shortenClient($client_name); ?></strong></p>
|
||||
<p class="h5"><i class="nav-icon fas fa-arrow-left ml-3 mr-2"></i> Back | <strong><?php if($client_abbreviation) { echo $client_abbreviation; } else { echo shortenClient($client_name); } ?></strong></p>
|
||||
</a>
|
||||
|
||||
<!-- Sidebar -->
|
||||
@@ -162,6 +162,19 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="client_racks.php?client_id=<?php echo $client_id; ?>" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "client_racks.php") { echo "active"; } ?>">
|
||||
<i class="nav-icon fas fa-server"></i>
|
||||
<p>
|
||||
Racks
|
||||
<?php
|
||||
if ($num_racks > 0) { ?>
|
||||
<span class="right badge text-light"><?php echo $num_racks; ?></span>
|
||||
<?php } ?>
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="client_certificates.php?client_id=<?php echo $client_id; ?>" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "client_certificates.php") { echo "active"; } ?>">
|
||||
<i class="nav-icon fas fa-lock"></i>
|
||||
|
||||
@@ -61,11 +61,10 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="float-right">
|
||||
<?php if($archived == 1){ ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=0" class="btn btn-primary"><i class="fa fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
<?php } else { ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=1" class="btn btn-default"><i class="fa fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
<?php } ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=<?php if($archived == 1){ echo 0; } else { echo 1; } ?>"
|
||||
class="btn btn-<?php if($archived == 1){ echo "primary"; } else { echo "default"; } ?>">
|
||||
<i class="fa fa-fw fa-archive mr-2"></i>Archived
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-licensing">Licensing</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-device-licenses">Devices</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-user-licenses">Users</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="pill" href="#pills-notes">Notes</a>
|
||||
</li>
|
||||
@@ -133,53 +139,79 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Devices</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-device-licenses">
|
||||
|
||||
<ul class="list-group">
|
||||
|
||||
<li class="list-group-item bg-dark">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" onclick="this.closest('.tab-pane').querySelectorAll('.asset-checkbox').forEach(checkbox => checkbox.checked = this.checked);">
|
||||
<label class="form-check-label ml-3"><strong>Licensed Devices</strong></label>
|
||||
</div>
|
||||
<select class="form-control select2" name="assets[]" data-placeholder="Select licensed Assets" multiple>
|
||||
<?php
|
||||
</li>
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM assets LEFT JOIN contacts ON asset_contact_id = contact_id WHERE asset_archived_at IS NULL AND asset_client_id = $client_id ORDER BY asset_name ASC");
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$asset_id = intval($row['asset_id']);
|
||||
$asset_name = nullable_htmlentities($row['asset_name']);
|
||||
$asset_type = nullable_htmlentities($row['asset_type']);
|
||||
$contact_name = nullable_htmlentities($row['contact_name']);
|
||||
?>
|
||||
<option value="<?php echo $asset_id; ?>"><?php echo "$asset_name - $contact_name"; ?></option>
|
||||
<?php } ?>
|
||||
<?php
|
||||
$sql_assets_select = mysqli_query($mysqli, "SELECT * FROM assets LEFT JOIN contacts ON asset_contact_id = contact_id WHERE (asset_archived_at > '$software_created_at' OR asset_archived_at IS NULL) AND asset_client_id = $client_id ORDER BY asset_archived_at ASC, asset_name ASC");
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
while ($row = mysqli_fetch_array($sql_assets_select)) {
|
||||
$asset_id_select = intval($row['asset_id']);
|
||||
$asset_name_select = nullable_htmlentities($row['asset_name']);
|
||||
$asset_type_select = nullable_htmlentities($row['asset_type']);
|
||||
$asset_archived_at = nullable_htmlentities($row['asset_archived_at']);
|
||||
if (empty($asset_archived_at)) {
|
||||
$asset_archived_display = "";
|
||||
} else {
|
||||
$asset_archived_display = "Archived - ";
|
||||
}
|
||||
$contact_name_select = nullable_htmlentities($row['contact_name']);
|
||||
|
||||
<div class="form-group">
|
||||
<label>Users</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-users"></i></span>
|
||||
?>
|
||||
<li class="list-group-item">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input asset-checkbox" name="assets[]" value="<?php echo $asset_id_select; ?>">
|
||||
<label class="form-check-label ml-2"><?php echo "$asset_archived_display$asset_name_select - $contact_name_select"; ?></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-user-licenses">
|
||||
|
||||
<ul class="list-group">
|
||||
|
||||
<li class="list-group-item bg-dark">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" onclick="this.closest('.tab-pane').querySelectorAll('.user-checkbox').forEach(checkbox => checkbox.checked = this.checked);">
|
||||
<label class="form-check-label ml-3"><strong>Licensed Users</strong></label>
|
||||
</div>
|
||||
<select class="form-control select2" name="contacts[]" data-placeholder="Select licensed Users" multiple>
|
||||
<?php
|
||||
</li>
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM contacts WHERE contact_archived_at IS NULL AND contact_client_id = $client_id ORDER BY contact_name ASC");
|
||||
<?php
|
||||
$sql_contacts_select = mysqli_query($mysqli, "SELECT * FROM contacts WHERE contact_archived_at IS NULL AND contact_client_id = $client_id ORDER BY contact_name ASC");
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$contact_id = intval($row['contact_id']);
|
||||
$contact_name = nullable_htmlentities($row['contact_name']);
|
||||
$contact_email = nullable_htmlentities($row['contact_email']);
|
||||
|
||||
?>
|
||||
<option value="<?php echo $contact_id; ?>"><?php echo "$contact_name - $contact_email"; ?></option>
|
||||
<?php } ?>
|
||||
while ($row = mysqli_fetch_array($sql_contacts_select)) {
|
||||
$contact_id_select = intval($row['contact_id']);
|
||||
$contact_name_select = nullable_htmlentities($row['contact_name']);
|
||||
$contact_email_select = nullable_htmlentities($row['contact_email']);
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
?>
|
||||
<li class="list-group-item">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input user-checkbox" name="contacts[]" value="<?php echo $contact_id_select; ?>">
|
||||
<label class="form-check-label ml-2"><?php echo "$contact_name_select - $contact_email_select"; ?></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -143,12 +143,16 @@
|
||||
|
||||
<div class="tab-pane fade" id="pills-device-licenses<?php echo $software_id; ?>">
|
||||
|
||||
<div class="alert alert-info">
|
||||
Select Assets that are licensed for this software
|
||||
</div>
|
||||
|
||||
<ul class="list-group">
|
||||
|
||||
<li class="list-group-item bg-dark">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" onclick="this.closest('.tab-pane').querySelectorAll('.asset-checkbox').forEach(checkbox => checkbox.checked = this.checked);">
|
||||
<label class="form-check-label ml-3"><strong>Licensed Devices</strong></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
|
||||
<?php
|
||||
$sql_assets_select = mysqli_query($mysqli, "SELECT * FROM assets LEFT JOIN contacts ON asset_contact_id = contact_id WHERE (asset_archived_at > '$software_created_at' OR asset_archived_at IS NULL) AND asset_client_id = $client_id ORDER BY asset_archived_at ASC, asset_name ASC");
|
||||
|
||||
@@ -167,7 +171,7 @@
|
||||
?>
|
||||
<li class="list-group-item">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" name="assets[]" value="<?php echo $asset_id_select; ?>" <?php if (in_array($asset_id_select, $asset_licenses_array)) { echo "checked"; } ?>>
|
||||
<input type="checkbox" class="form-check-input asset-checkbox" name="assets[]" value="<?php echo $asset_id_select; ?>" <?php if (in_array($asset_id_select, $asset_licenses_array)) { echo "checked"; } ?>>
|
||||
<label class="form-check-label ml-2"><?php echo "$asset_archived_display$asset_name_select - $contact_name_select"; ?></label>
|
||||
</div>
|
||||
</li>
|
||||
@@ -180,12 +184,15 @@
|
||||
|
||||
<div class="tab-pane fade" id="pills-user-licenses<?php echo $software_id; ?>">
|
||||
|
||||
<div class="alert alert-info">
|
||||
Select Users that are licensed for this software
|
||||
</div>
|
||||
|
||||
<ul class="list-group">
|
||||
|
||||
<li class="list-group-item bg-dark">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" onclick="this.closest('.tab-pane').querySelectorAll('.user-checkbox').forEach(checkbox => checkbox.checked = this.checked);">
|
||||
<label class="form-check-label ml-3"><strong>Licensed Users</strong></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<?php
|
||||
$sql_contacts_select = mysqli_query($mysqli, "SELECT * FROM contacts WHERE (contact_archived_at > '$software_created_at' OR contact_archived_at IS NULL) AND contact_client_id = $client_id ORDER BY contact_archived_at ASC, contact_name ASC");
|
||||
|
||||
@@ -203,7 +210,7 @@
|
||||
?>
|
||||
<li class="list-group-item">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" name="contacts[]" value="<?php echo $contact_id_select; ?>" <?php if (in_array("$contact_id_select", $contact_licenses_array)) { echo "checked"; } ?>>
|
||||
<input type="checkbox" class="form-check-input user-checkbox" name="contacts[]" value="<?php echo $contact_id_select; ?>" <?php if (in_array("$contact_id_select", $contact_licenses_array)) { echo "checked"; } ?>>
|
||||
<label class="form-check-label ml-2"><?php echo "$contact_archived_display$contact_name_select - $contact_email_select"; ?></label>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -154,11 +154,11 @@ $total_tickets_closed = intval($row['total_tickets_closed']);
|
||||
$ticket_closed_at = nullable_htmlentities($row['ticket_closed_at']);
|
||||
|
||||
if ($ticket_priority == "High") {
|
||||
$ticket_priority_display = "<span class='p-2 badge badge-danger'>$ticket_priority</span>";
|
||||
$ticket_priority_display = "<span class='p-2 badge badge-pill badge-danger'>$ticket_priority</span>";
|
||||
} elseif ($ticket_priority == "Medium") {
|
||||
$ticket_priority_display = "<span class='p-2 badge badge-warning'>$ticket_priority</span>";
|
||||
$ticket_priority_display = "<span class='p-2 badge badge-pill badge-warning'>$ticket_priority</span>";
|
||||
} elseif ($ticket_priority == "Low") {
|
||||
$ticket_priority_display = "<span class='p-2 badge badge-info'>$ticket_priority</span>";
|
||||
$ticket_priority_display = "<span class='p-2 badge badge-pill badge-info'>$ticket_priority</span>";
|
||||
} else{
|
||||
$ticket_priority_display = "-";
|
||||
}
|
||||
@@ -238,9 +238,9 @@ $total_tickets_closed = intval($row['total_tickets_closed']);
|
||||
<a href="#" data-toggle="modal" data-target="#editTicketBillableModal<?php echo $ticket_id; ?>">
|
||||
<?php
|
||||
if ($ticket_billable == 1) {
|
||||
echo "<span class='badge badge-pill badge-success'>$</span>";
|
||||
echo "<span class='badge badge-pill badge-success p-2'>Yes</span>";
|
||||
} else {
|
||||
echo "<span class='badge badge-pill badge-secondary'>X</span>";
|
||||
echo "<span class='badge badge-pill badge-secondary p-2'>No</span>";
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
|
||||
@@ -61,132 +61,178 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="float-right">
|
||||
<?php if($archived == 1){ ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=0" class="btn btn-primary"><i class="fa fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
<?php } else { ?>
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=1" class="btn btn-default"><i class="fa fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
<?php } ?>
|
||||
<div class="btn-group float-right">
|
||||
<a href="?client_id=<?php echo $client_id; ?>&archived=<?php if($archived == 1){ echo 0; } else { echo 1; } ?>"
|
||||
class="btn btn-<?php if($archived == 1){ echo "primary"; } else { echo "default"; } ?>">
|
||||
<i class="fa fa-fw fa-archive mr-2"></i>Archived
|
||||
</a>
|
||||
<div class="dropdown ml-2" id="bulkActionButton" hidden>
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<?php if ($archived) { ?>
|
||||
<button class="dropdown-item text-info"
|
||||
type="submit" form="bulkActions" name="bulk_unarchive_vendors">
|
||||
<i class="fas fa-fw fa-redo mr-2"></i>Unarchive
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item text-danger text-bold"
|
||||
type="submit" form="bulkActions" name="bulk_delete_vendors">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</button>
|
||||
<?php } else { ?>
|
||||
<button class="dropdown-item text-danger confirm-link"
|
||||
type="submit" form="bulkActions" name="bulk_archive_vendors">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</button>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=vendor_name&order=<?php echo $disp; ?>">Vendor</a></th>
|
||||
<th>Contact</th>
|
||||
<th>Website</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$vendor_id = intval($row['vendor_id']);
|
||||
$vendor_name = nullable_htmlentities($row['vendor_name']);
|
||||
$vendor_description = nullable_htmlentities($row['vendor_description']);
|
||||
$vendor_account_number = nullable_htmlentities($row['vendor_account_number']);
|
||||
$vendor_contact_name = nullable_htmlentities($row['vendor_contact_name']);
|
||||
if (empty($vendor_contact_name)) {
|
||||
$vendor_contact_name_display = "-";
|
||||
} else {
|
||||
$vendor_contact_name_display = $vendor_contact_name;
|
||||
}
|
||||
$vendor_phone = formatPhoneNumber($row['vendor_phone']);
|
||||
$vendor_extension = nullable_htmlentities($row['vendor_extension']);
|
||||
$vendor_email = nullable_htmlentities($row['vendor_email']);
|
||||
$vendor_website = nullable_htmlentities($row['vendor_website']);
|
||||
$vendor_hours = nullable_htmlentities($row['vendor_hours']);
|
||||
$vendor_sla = nullable_htmlentities($row['vendor_sla']);
|
||||
$vendor_code = nullable_htmlentities($row['vendor_code']);
|
||||
$vendor_notes = nullable_htmlentities($row['vendor_notes']);
|
||||
$vendor_template_id = intval($row['vendor_template_id']);
|
||||
|
||||
if (empty($vendor_website)) {
|
||||
$vendor_website_display = "-";
|
||||
} else {
|
||||
$vendor_website_display = "<button class='btn btn-sm clipboardjs' data-clipboard-text='$vendor_website'><i class='far fa-copy text-secondary'></i></button><a href='https://$vendor_website' target='_blank'><i class='fa fa-external-link-alt text-secondary'></i></a>";
|
||||
}
|
||||
|
||||
?>
|
||||
<form id="bulkActions" action="post.php" method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
<div class="table-responsive-sm">
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<td>
|
||||
<a class="text-dark" href="#" data-toggle="modal" data-target="#editVendorModal<?php echo $vendor_id; ?>">
|
||||
<div class="media">
|
||||
<i class="fa fa-fw fa-2x fa-building mr-3"></i>
|
||||
<div class="media-body">
|
||||
<div><?php echo $vendor_name; ?></div>
|
||||
<div><small class="text-secondary"><?php echo $vendor_description; ?></small></div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
if (!empty($vendor_contact_name)) { ?>
|
||||
<i class="fa fa-fw fa-user text-secondary mr-2 mb-2"></i><?php echo $vendor_contact_name_display; ?>
|
||||
<br>
|
||||
<?php } else {
|
||||
echo $vendor_contact_name_display;
|
||||
}
|
||||
|
||||
if (!empty($vendor_phone)) { ?>
|
||||
<i class="fa fa-fw fa-phone text-secondary mr-2 mb-2"></i><?php echo $vendor_phone; ?>
|
||||
<br>
|
||||
<?php }
|
||||
|
||||
if (!empty($vendor_email)) { ?>
|
||||
<i class="fa fa-fw fa-envelope text-secondary mr-2 mb-2"></i><?php echo $vendor_email; ?>
|
||||
<br>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<td><?php echo $vendor_website_display; ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editVendorModal<?php echo $vendor_id; ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<?php if ($session_user_role == 3) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_vendor=<?php echo $vendor_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</a>
|
||||
<?php if ($config_destructive_deletes_enable) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_vendor=<?php echo $vendor_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<td class="pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)">
|
||||
</div>
|
||||
</td>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=vendor_name&order=<?php echo $disp; ?>">Vendor</a></th>
|
||||
<th>Contact</th>
|
||||
<th>Website</th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
require "vendor_edit_modal.php";
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$vendor_id = intval($row['vendor_id']);
|
||||
$vendor_name = nullable_htmlentities($row['vendor_name']);
|
||||
$vendor_description = nullable_htmlentities($row['vendor_description']);
|
||||
$vendor_account_number = nullable_htmlentities($row['vendor_account_number']);
|
||||
$vendor_contact_name = nullable_htmlentities($row['vendor_contact_name']);
|
||||
if (empty($vendor_contact_name)) {
|
||||
$vendor_contact_name_display = "-";
|
||||
} else {
|
||||
$vendor_contact_name_display = $vendor_contact_name;
|
||||
}
|
||||
$vendor_phone = formatPhoneNumber($row['vendor_phone']);
|
||||
$vendor_extension = nullable_htmlentities($row['vendor_extension']);
|
||||
$vendor_email = nullable_htmlentities($row['vendor_email']);
|
||||
$vendor_website = nullable_htmlentities($row['vendor_website']);
|
||||
$vendor_hours = nullable_htmlentities($row['vendor_hours']);
|
||||
$vendor_sla = nullable_htmlentities($row['vendor_sla']);
|
||||
$vendor_code = nullable_htmlentities($row['vendor_code']);
|
||||
$vendor_notes = nullable_htmlentities($row['vendor_notes']);
|
||||
$vendor_created_at = nullable_htmlentities($row['vendor_created_at']);
|
||||
$vendor_archived_at = nullable_htmlentities($row['vendor_archived_at']);
|
||||
$vendor_template_id = intval($row['vendor_template_id']);
|
||||
|
||||
if (empty($vendor_website)) {
|
||||
$vendor_website_display = "-";
|
||||
} else {
|
||||
$vendor_website_display = "<button class='btn btn-sm clipboardjs' data-clipboard-text='$vendor_website'><i class='far fa-copy text-secondary'></i></button><a href='https://$vendor_website' target='_blank'><i class='fa fa-external-link-alt text-secondary'></i></a>";
|
||||
}
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td class="pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input bulk-select" type="checkbox" name="vendor_ids[]" value="<?php echo $vendor_id ?>">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a class="text-dark" href="#" data-toggle="modal" data-target="#editVendorModal<?php echo $vendor_id; ?>">
|
||||
<div class="media">
|
||||
<i class="fa fa-fw fa-2x fa-building mr-3"></i>
|
||||
<div class="media-body">
|
||||
<div><?php echo $vendor_name; ?></div>
|
||||
<div><small class="text-secondary"><?php echo $vendor_description; ?></small></div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
if (!empty($vendor_contact_name)) { ?>
|
||||
<i class="fa fa-fw fa-user text-secondary mr-2 mb-2"></i><?php echo $vendor_contact_name_display; ?>
|
||||
<br>
|
||||
<?php } else {
|
||||
echo $vendor_contact_name_display;
|
||||
}
|
||||
|
||||
} ?>
|
||||
if (!empty($vendor_phone)) { ?>
|
||||
<i class="fa fa-fw fa-phone text-secondary mr-2 mb-2"></i><?php echo $vendor_phone; ?>
|
||||
<br>
|
||||
<?php }
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
if (!empty($vendor_email)) { ?>
|
||||
<i class="fa fa-fw fa-envelope text-secondary mr-2 mb-2"></i><?php echo $vendor_email; ?>
|
||||
<br>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<td><?php echo $vendor_website_display; ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#editVendorModal<?php echo $vendor_id; ?>">
|
||||
<i class="fas fa-fw fa-edit mr-2"></i>Edit
|
||||
</a>
|
||||
<?php if ($session_user_role == 3) { ?>
|
||||
<?php if ($vendor_archived_at) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-info confirm-link" href="post.php?unarchive_vendor=<?php echo $vendor_id; ?>">
|
||||
<i class="fas fa-fw fa-redo mr-2"></i>Unarchive
|
||||
</a>
|
||||
<?php if ($config_destructive_deletes_enable) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_vendor=<?php echo $vendor_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php } else { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_vendor=<?php echo $vendor_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
require "vendor_edit_modal.php";
|
||||
|
||||
} ?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
<?php require_once "pagination.php";
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/bulk_actions.js"></script>
|
||||
|
||||
<?php
|
||||
require_once "vendor_add_modal.php";
|
||||
|
||||
|
||||
66
clients.php
66
clients.php
@@ -20,6 +20,22 @@ if($leads == 1){
|
||||
$leads_query = 0;
|
||||
}
|
||||
|
||||
// Tags Filter
|
||||
if (isset($_GET['tags']) && is_array($_GET['tags']) && !empty($_GET['tags'])) {
|
||||
// Sanitize each element of the status array
|
||||
$sanitizedTags = array();
|
||||
foreach ($_GET['tags'] as $tag) {
|
||||
// Escape each status to prevent SQL injection
|
||||
$sanitizedTags[] = "'" . intval($tag) . "'";
|
||||
}
|
||||
|
||||
// Convert the sanitized tags into a comma-separated string
|
||||
$sanitizedTagsString = implode(",", $sanitizedTags);
|
||||
$tag_query = "AND tags.tag_id IN ($sanitizedTagsString)";
|
||||
} else{
|
||||
$tag_query = '';
|
||||
}
|
||||
|
||||
// Industry Filter
|
||||
if (isset($_GET['industry']) & !empty($_GET['industry'])) {
|
||||
$industry_query = "AND (clients.client_type = '" . sanitizeInput($_GET['industry']) . "')";
|
||||
@@ -27,6 +43,7 @@ if (isset($_GET['industry']) & !empty($_GET['industry'])) {
|
||||
} else {
|
||||
// Default - any
|
||||
$industry_query = '';
|
||||
$industry = '';
|
||||
}
|
||||
|
||||
// Referral Filter
|
||||
@@ -36,6 +53,7 @@ if (isset($_GET['referral']) & !empty($_GET['referral'])) {
|
||||
} else {
|
||||
// Default - any
|
||||
$referral_query = '';
|
||||
$referral = '';
|
||||
}
|
||||
|
||||
//Rebuild URL
|
||||
@@ -59,6 +77,7 @@ $sql = mysqli_query(
|
||||
AND DATE(client_created_at) BETWEEN '$dtf' AND '$dtt'
|
||||
AND client_lead = $leads
|
||||
$access_permission_query
|
||||
$tag_query
|
||||
$industry_query
|
||||
$referral_query
|
||||
GROUP BY client_id
|
||||
@@ -113,16 +132,30 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
</div>
|
||||
|
||||
<div class="btn-group mr-2">
|
||||
<?php if($archived == 1) { ?>
|
||||
<a href="?<?php echo $url_query_strings_sort ?>&archived=0" class="btn btn-primary"><i class="fa fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
<?php } else { ?>
|
||||
<a href="?<?php echo $url_query_strings_sort ?>&archived=1" class="btn btn-default"><i class="fa fa-fw fa-archive mr-2"></i>Archived</a>
|
||||
<?php } ?>
|
||||
<a href="?<?php echo $url_query_strings_sort ?>&archived=<?php if($archived == 1){ echo 0; } else { echo 1; } ?>"
|
||||
class="btn btn-<?php if($archived == 1){ echo "primary"; } else { echo "default"; } ?>">
|
||||
<i class="fa fa-fw fa-archive mr-2"></i>Archived
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse mt-3 <?php if ($_GET['dtf'] || $_GET['industry'] || $_GET['referral'] || $_GET['canned_date'] !== "custom" ) { echo "show"; } ?>" id="advancedFilter">
|
||||
<div
|
||||
class="collapse mt-3
|
||||
<?php
|
||||
if (
|
||||
isset($_GET['dtf'])
|
||||
|| isset($_GET['industry'])
|
||||
|| isset($_GET['referral'])
|
||||
|| (isset($_GET['tags']) && is_array($_GET['tags']))
|
||||
|| $_GET['canned_date'] !== "custom" )
|
||||
{
|
||||
echo "show";
|
||||
}
|
||||
?>
|
||||
"
|
||||
id="advancedFilter"
|
||||
>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
@@ -152,6 +185,22 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<input onchange="this.form.submit()" type="date" class="form-control" name="dtt" max="2999-12-31" value="<?php echo nullable_htmlentities($dtt); ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<label>Tag</label>
|
||||
<select onchange="this.form.submit()" class="form-control select2" name="tags[]" data-placeholder="- Select Tags -" multiple>
|
||||
<?php
|
||||
$sql_tags = mysqli_query($mysqli, "SELECT * FROM tags WHERE tag_type = 1");
|
||||
while ($row = mysqli_fetch_array($sql_tags)) {
|
||||
$tag_id = intval($row['tag_id']);
|
||||
$tag_name = nullable_htmlentities($row['tag_name']); ?>
|
||||
|
||||
<option value="<?php echo $tag_id ?>" <?php if (isset($_GET['tags']) && is_array($_GET['tags']) && in_array($tag_id, $_GET['tags'])) { echo 'selected'; } ?>> <?php echo $tag_name ?> </option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<label>Industry</label>
|
||||
@@ -198,7 +247,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<table class="table table-striped table-hover table-borderless">
|
||||
<thead class="<?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=client_name&order=<?php echo $disp; ?>">Name</a></th>
|
||||
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=client_name&order=<?php echo $disp; ?>">Client Name</a></th>
|
||||
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=location_city&order=<?php echo $disp; ?>">Primary Location </a></th>
|
||||
<th><a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=contact_name&order=<?php echo $disp; ?>">Primary Contact</a></th>
|
||||
<?php if (($session_user_role == 3 || $session_user_role == 1) && $config_module_enable_accounting == 1) { ?> <th class="text-right">Billing</th> <?php } ?>
|
||||
@@ -236,6 +285,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
$client_net_terms = intval($row['client_net_terms']);
|
||||
$client_tax_id_number = nullable_htmlentities($row['client_tax_id_number']);
|
||||
$client_referral = nullable_htmlentities($row['client_referral']);
|
||||
$client_abbreviation = nullable_htmlentities($row['client_abbreviation']);
|
||||
$client_notes = nullable_htmlentities($row['client_notes']);
|
||||
$client_created_at = date('Y-m-d', strtotime($row['client_created_at']));
|
||||
$client_updated_at = nullable_htmlentities($row['client_updated_at']);
|
||||
@@ -301,7 +351,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="font-weight-bold" href="client_overview.php?client_id=<?php echo $client_id; ?>"><?php echo $client_name; ?> <i class="fas fa-fw fa-arrow-circle-right"></i></a>
|
||||
<a class="font-weight-bold" href="client_overview.php?client_id=<?php echo $client_id; ?>"><?php echo $client_name; ?></a>
|
||||
|
||||
<?php
|
||||
if (!empty($client_type)) {
|
||||
|
||||
10
cron.php
10
cron.php
@@ -67,9 +67,12 @@ $config_telemetry = intval($row['config_telemetry']);
|
||||
$config_enable_alert_domain_expire = intval($row['config_enable_alert_domain_expire']);
|
||||
$config_send_invoice_reminders = intval($row['config_send_invoice_reminders']);
|
||||
|
||||
// Remmeber Token Expire
|
||||
// Remember-me Token Expiry
|
||||
$config_login_remember_me_expire = intval($row['config_login_remember_me_expire']);
|
||||
|
||||
// Log retention
|
||||
$config_log_retention = intval($row['config_log_retention']);
|
||||
|
||||
// Set Currency Format
|
||||
$currency_format = numfmt_create($company_locale, NumberFormatter::CURRENCY);
|
||||
|
||||
@@ -120,9 +123,12 @@ mysqli_query($mysqli, "DELETE FROM notifications WHERE notification_dismissed_at
|
||||
// Clean-up mail queue
|
||||
mysqli_query($mysqli, "DELETE FROM email_queue WHERE email_queued_at < CURDATE() - INTERVAL 90 DAY");
|
||||
|
||||
// Clean-up old remember me tokens (2 or more days old)
|
||||
// Clean-up old remember me tokens
|
||||
mysqli_query($mysqli, "DELETE FROM remember_tokens WHERE remember_token_created_at < CURDATE() - INTERVAL $config_login_remember_me_expire DAY");
|
||||
|
||||
// Cleanup old audit logs
|
||||
mysqli_query($mysqli, "DELETE FROM logs WHERE log_created_at < CURDATE() - INTERVAL $config_log_retention DAY");
|
||||
|
||||
//Logging
|
||||
//mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron', log_action = 'Task', log_description = 'Cron cleaned up old data'");
|
||||
|
||||
|
||||
@@ -4,23 +4,17 @@
|
||||
* Process emails and create/update tickets
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- Process unregistered contacts/clients into an inbox to allow a ticket to be created/ignored
|
||||
- Support for authenticating with OAuth
|
||||
- Separate Mailbox Account for tickets 2022-12-14 - JQ
|
||||
|
||||
*/
|
||||
|
||||
// Set working directory to the directory this cron script lives at.
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Autoload Composer dependencies
|
||||
require_once __DIR__ . '/plugins/php-imap/vendor/autoload.php';
|
||||
|
||||
// Get ITFlow config & helper functions
|
||||
require_once "config.php";
|
||||
|
||||
// Set Timezone
|
||||
require_once "inc_set_timezone.php";
|
||||
|
||||
require_once "functions.php";
|
||||
|
||||
// Get settings for the "default" company
|
||||
@@ -47,16 +41,6 @@ if ($argv[1] !== $config_cron_key && $_GET['key'] !== $config_cron_key) {
|
||||
exit("Cron Key invalid -- Quitting..");
|
||||
}
|
||||
|
||||
// Check IMAP extension works/installed
|
||||
if (!function_exists('imap_open')) {
|
||||
exit("Email Parser: PHP IMAP extension is not installed. See https://docs.itflow.org/ticket_email_parse -- Quitting..");
|
||||
}
|
||||
|
||||
// Check mailparse extension works/installed
|
||||
if (!function_exists('mailparse_msg_parse_file')) {
|
||||
exit("Email Parser: PHP mailparse extension is not installed. See https://docs.itflow.org/ticket_email_parse -- Quitting..");
|
||||
}
|
||||
|
||||
// Get system temp directory
|
||||
$temp_dir = sys_get_temp_dir();
|
||||
|
||||
@@ -80,109 +64,89 @@ if (file_exists($lock_file_path)) {
|
||||
// Create a lock file
|
||||
file_put_contents($lock_file_path, "Locked");
|
||||
|
||||
// PHP Mail Parser
|
||||
use PhpMimeMailParser\Parser;
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Contracts/CharsetManager.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Contracts/Middleware.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Attachment.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Charset.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Exception.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Middleware.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/MiddlewareStack.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/MimePart.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Parser.php";
|
||||
|
||||
// Webklex PHP-IMAP
|
||||
use Webklex\PHPIMAP\ClientManager;
|
||||
use Webklex\PHPIMAP\Message\Attachment;
|
||||
|
||||
// Allowed attachment extensions
|
||||
$allowed_extensions = array('jpg', 'jpeg', 'gif', 'png', 'webp', 'pdf', 'txt', 'md', 'doc', 'docx', 'csv', 'xls', 'xlsx', 'xlsm', 'zip', 'tar', 'gz');
|
||||
|
||||
// Function to raise a new ticket for a given contact and email them confirmation (if configured)
|
||||
function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message, $attachments) {
|
||||
function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message, $attachments, $original_message_file) {
|
||||
global $mysqli, $config_app_name, $company_name, $company_phone, $config_ticket_prefix, $config_ticket_client_general_notifications, $config_ticket_new_ticket_notification_email, $config_base_url, $config_ticket_from_name, $config_ticket_from_email, $config_smtp_host, $config_smtp_port, $config_smtp_encryption, $config_smtp_username, $config_smtp_password, $allowed_extensions;
|
||||
|
||||
// Access global variables
|
||||
global $mysqli,$config_app_name, $company_name, $company_phone, $config_ticket_prefix, $config_ticket_client_general_notifications, $config_ticket_new_ticket_notification_email, $config_base_url, $config_ticket_from_name, $config_ticket_from_email, $config_smtp_host, $config_smtp_port, $config_smtp_encryption, $config_smtp_username, $config_smtp_password, $allowed_extensions;
|
||||
|
||||
// Get the next Ticket Number and add 1 for the new ticket number
|
||||
$ticket_number_sql = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_ticket_next_number FROM settings WHERE company_id = 1"));
|
||||
$ticket_number = intval($ticket_number_sql['config_ticket_next_number']);
|
||||
$new_config_ticket_next_number = $ticket_number + 1;
|
||||
mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1");
|
||||
|
||||
// Prep ticket details
|
||||
$message = nl2br($message);
|
||||
$message = mysqli_escape_string($mysqli, "<i>Email from: $contact_email at $date:-</i> <br><br>$message");
|
||||
// Clean up the message
|
||||
$message = trim($message); // Remove leading/trailing whitespace
|
||||
$message = preg_replace('/\s+/', ' ', $message); // Replace multiple spaces with a single space
|
||||
$message = nl2br($message); // Convert newlines to <br>
|
||||
|
||||
// Wrap the message in a div with controlled line height
|
||||
$message = "<i>Email from: $contact_email at $date:-</i> <br><br><div style='line-height:1.5;'>$message</div>";
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$message', ticket_priority = 'Low', ticket_status = 1, ticket_created_by = 0, ticket_contact_id = $contact_id, ticket_client_id = $client_id");
|
||||
$ticket_prefix_esc = mysqli_real_escape_string($mysqli, $config_ticket_prefix);
|
||||
$subject_esc = mysqli_real_escape_string($mysqli, $subject);
|
||||
$message_esc = mysqli_real_escape_string($mysqli, $message);
|
||||
$contact_email_esc = mysqli_real_escape_string($mysqli, $contact_email);
|
||||
$client_id_esc = intval($client_id);
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$ticket_prefix_esc', ticket_number = $ticket_number, ticket_subject = '$subject_esc', ticket_details = '$message_esc', ticket_priority = 'Low', ticket_status = 1, ticket_created_by = 0, ticket_contact_id = $contact_id, ticket_client_id = $client_id_esc");
|
||||
$id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Logging
|
||||
echo "Created new ticket.<br>";
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Create', log_description = 'Email parser: Client contact $contact_email created ticket $config_ticket_prefix$ticket_number ($subject) ($id)', log_client_id = $client_id");
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Create', log_description = 'Email parser: Client contact $contact_email_esc created ticket $ticket_prefix_esc$ticket_number ($subject_esc) ($id)', log_client_id = $client_id_esc");
|
||||
|
||||
// Process attachments (after ticket is logged as created)
|
||||
mkdirMissing('uploads/tickets/');
|
||||
foreach ($attachments as $attachment) {
|
||||
$att_dir = "uploads/tickets/" . $id . "/";
|
||||
mkdirMissing($att_dir);
|
||||
|
||||
// Get name and extension
|
||||
$att_name = $attachment->getFileName();
|
||||
rename("uploads/tmp/{$original_message_file}", "{$att_dir}/{$original_message_file}");
|
||||
$original_message_file_esc = mysqli_real_escape_string($mysqli, $original_message_file);
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = 'Original-parsed-email.eml', ticket_attachment_reference_name = '$original_message_file_esc', ticket_attachment_ticket_id = $id");
|
||||
|
||||
foreach ($attachments as $attachment) {
|
||||
$att_name = $attachment->getName();
|
||||
$att_extarr = explode('.', $att_name);
|
||||
$att_extension = strtolower(end($att_extarr));
|
||||
|
||||
// Check the extension is allowed
|
||||
if (in_array($att_extension, $allowed_extensions)) {
|
||||
|
||||
// Setup directory for this ticket ID
|
||||
$att_dir = "uploads/tickets/" . $id . "/";
|
||||
mkdirMissing($att_dir);
|
||||
|
||||
// Save attachment with a random name
|
||||
$att_saved_path = $attachment->save($att_dir, Parser::ATTACHMENT_RANDOM_FILENAME);
|
||||
|
||||
// Access the random name to add into the database (this won't work on Windows)
|
||||
$att_tmparr = explode($att_dir, $att_saved_path);
|
||||
$att_saved_filename = md5(uniqid(rand(), true)) . '.' . $att_extension;
|
||||
$att_saved_path = $att_dir . $att_saved_filename;
|
||||
$attachment->save($att_dir); // Save the attachment to the directory
|
||||
rename($att_dir . $attachment->getName(), $att_saved_path); // Rename the saved file to the hashed name
|
||||
|
||||
$ticket_attachment_name = sanitizeInput($att_name);
|
||||
$ticket_attachment_reference_name = sanitizeInput(end($att_tmparr));
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = '$ticket_attachment_name', ticket_attachment_reference_name = '$ticket_attachment_reference_name', ticket_attachment_ticket_id = $id");
|
||||
$ticket_attachment_reference_name = sanitizeInput($att_saved_filename);
|
||||
|
||||
$ticket_attachment_name_esc = mysqli_real_escape_string($mysqli, $ticket_attachment_name);
|
||||
$ticket_attachment_reference_name_esc = mysqli_real_escape_string($mysqli, $ticket_attachment_reference_name);
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = '$ticket_attachment_name_esc', ticket_attachment_reference_name = '$ticket_attachment_reference_name_esc', ticket_attachment_ticket_id = $id");
|
||||
} else {
|
||||
$ticket_attachment_name = sanitizeInput($att_name);
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Blocked attachment $ticket_attachment_name from Client contact $contact_email for ticket $config_ticket_prefix$ticket_number', log_client_id = $client_id");
|
||||
$ticket_attachment_name_esc = mysqli_real_escape_string($mysqli, $att_name);
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Blocked attachment $ticket_attachment_name_esc from Client contact $contact_email_esc for ticket $ticket_prefix_esc$ticket_number', log_client_id = $client_id_esc");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$data = [];
|
||||
// E-mail client notification that ticket has been created
|
||||
if ($config_ticket_client_general_notifications == 1) {
|
||||
|
||||
$subject_email = "Ticket created - [$config_ticket_prefix$ticket_number] - $subject";
|
||||
$body = "<i style=\'color: #808080\'>##- Please type your reply above this line -##</i><br><br>Hello $contact_name,<br><br>Thank you for your email. A ticket regarding \"$subject\" has been automatically created for you.<br><br>Ticket: $config_ticket_prefix$ticket_number<br>Subject: $subject<br>Status: New<br>https://$config_base_url/portal/ticket.php?id=$id<br><br>--<br>$company_name - Support<br>$config_ticket_from_email<br>$company_phone";
|
||||
$body = "<i style='color: #808080'>##- Please type your reply above this line -##</i><br><br>Hello $contact_name,<br><br>Thank you for your email. A ticket regarding \"$subject\" has been automatically created for you.<br><br>Ticket: $config_ticket_prefix$ticket_number<br>Subject: $subject<br>Status: New<br>https://$config_base_url/portal/ticket.php?id=$id<br><br>--<br>$company_name - Support<br>$config_ticket_from_email<br>$company_phone";
|
||||
|
||||
$data[] = [
|
||||
'from' => $config_ticket_from_email,
|
||||
'from_name' => $config_ticket_from_name,
|
||||
'recipient' => $contact_email,
|
||||
'recipient_name' => $contact_name,
|
||||
'subject' => $subject_email,
|
||||
'body' => $body
|
||||
'subject' => mysqli_real_escape_string($mysqli, $subject_email),
|
||||
'body' => mysqli_real_escape_string($mysqli, $body)
|
||||
];
|
||||
}
|
||||
|
||||
// Notify agent DL of the new ticket, if populated with a valid email
|
||||
if ($config_ticket_new_ticket_notification_email) {
|
||||
|
||||
// Get client info
|
||||
$client_sql = mysqli_query($mysqli, "SELECT client_name FROM clients WHERE client_id = $client_id");
|
||||
$client_row = mysqli_fetch_array($client_sql);
|
||||
$client_name = sanitizeInput($client_row['client_name']);
|
||||
@@ -195,44 +159,42 @@ function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date
|
||||
'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
|
||||
'subject' => mysqli_real_escape_string($mysqli, $email_subject),
|
||||
'body' => mysqli_real_escape_string($mysqli, $email_body)
|
||||
];
|
||||
}
|
||||
|
||||
addToMailQueue($mysqli, $data);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
// End Add Ticket Function
|
||||
|
||||
// Add Reply Function
|
||||
function addReply($from_email, $date, $subject, $ticket_number, $message, $attachments) {
|
||||
// Add email as a comment/reply to an existing ticket
|
||||
|
||||
// Access global variables
|
||||
global $mysqli, $config_app_name, $company_name, $company_phone, $config_ticket_prefix, $config_base_url, $config_ticket_from_name, $config_ticket_from_email, $config_smtp_host, $config_smtp_port, $config_smtp_encryption, $config_smtp_username, $config_smtp_password, $allowed_extensions;
|
||||
|
||||
// Set default reply type
|
||||
$ticket_reply_type = 'Client';
|
||||
// Clean up the message
|
||||
$message_parts = explode("##- Please type your reply above this line -##", $message);
|
||||
$message_body = $message_parts[0];
|
||||
$message_body = trim($message_body); // Remove leading/trailing whitespace
|
||||
$message_body = preg_replace('/\r\n|\r|\n/', ' ', $message_body); // Replace newlines with a space
|
||||
$message_body = nl2br($message_body); // Convert remaining newlines to <br>
|
||||
|
||||
// Capture just the latest/most recent email reply content
|
||||
// based off the "##- Please type your reply above this line -##" line that we prepend the outgoing emails with
|
||||
$message = explode("##- Please type your reply above this line -##", $message);
|
||||
$message = nl2br($message[0]);
|
||||
$message = mysqli_escape_string($mysqli, "<i>Email from: $from_email at $date:-</i> <br><br>$message");
|
||||
// Wrap the message in a div with controlled line height
|
||||
$message = "<i>Email from: $from_email at $date:-</i> <br><br><div style='line-height:1.5;'>$message_body</div>";
|
||||
|
||||
$ticket_number_esc = intval($ticket_number);
|
||||
$message_esc = mysqli_real_escape_string($mysqli, $message);
|
||||
$from_email_esc = mysqli_real_escape_string($mysqli, $from_email);
|
||||
|
||||
// Lookup the ticket ID
|
||||
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT ticket_id, ticket_subject, ticket_status, ticket_contact_id, ticket_client_id, contact_email, client_name
|
||||
FROM tickets
|
||||
LEFT JOIN contacts on tickets.ticket_contact_id = contacts.contact_id
|
||||
LEFT JOIN clients on tickets.ticket_client_id = clients.client_id
|
||||
WHERE ticket_number = $ticket_number LIMIT 1"));
|
||||
WHERE ticket_number = $ticket_number_esc LIMIT 1"));
|
||||
|
||||
if ($row) {
|
||||
|
||||
// Get ticket details
|
||||
$ticket_id = intval($row['ticket_id']);
|
||||
$ticket_subject = sanitizeInput($row['ticket_subject']);
|
||||
$ticket_status = sanitizeInput($row['ticket_status']);
|
||||
@@ -241,12 +203,16 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac
|
||||
$client_id = intval($row['ticket_client_id']);
|
||||
$client_name = sanitizeInput($row['client_name']);
|
||||
|
||||
// Check ticket isn't closed - tickets can't be re-opened
|
||||
if ($ticket_status == 5) {
|
||||
mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Ticket', notification = 'Email parser: $from_email attempted to re-open ticket $config_ticket_prefix$ticket_number (ID $ticket_id) - check inbox manually to see email', notification_action = 'ticket.php?ticket_id=$ticket_id', notification_client_id = $client_id");
|
||||
$config_ticket_prefix_esc = mysqli_real_escape_string($mysqli, $config_ticket_prefix);
|
||||
$ticket_number_esc = mysqli_real_escape_string($mysqli, $ticket_number);
|
||||
$ticket_id_esc = intval($ticket_id);
|
||||
$client_id_esc = intval($client_id);
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Ticket', notification = 'Email parser: $from_email attempted to re-open ticket $config_ticket_prefix_esc$ticket_number_esc (ID $ticket_id_esc) - check inbox manually to see email', notification_action = 'ticket.php?ticket_id=$ticket_id_esc', notification_client_id = $client_id_esc");
|
||||
|
||||
$email_subject = "Action required: This ticket is already closed";
|
||||
$email_body = "Hi there, <br><br>You\'ve tried to reply to a ticket that is closed - we won\'t see your response. <br><br>Please raise a new ticket by sending a fresh e-mail to our support address below. <br><br>--<br>$company_name - Support<br>$config_ticket_from_email<br>$company_phone";
|
||||
$email_body = "Hi there, <br><br>You've tried to reply to a ticket that is closed - we won't see your response. <br><br>Please raise a new ticket by sending a fresh e-mail to our support address below. <br><br>--<br>$company_name - Support<br>$config_ticket_from_email<br>$company_phone";
|
||||
|
||||
$data = [
|
||||
[
|
||||
@@ -254,8 +220,8 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac
|
||||
'from_name' => $config_ticket_from_name,
|
||||
'recipient' => $from_email,
|
||||
'recipient_name' => $from_email,
|
||||
'subject' => $email_subject,
|
||||
'body' => $email_body
|
||||
'subject' => mysqli_real_escape_string($mysqli, $email_subject),
|
||||
'body' => mysqli_real_escape_string($mysqli, $email_body)
|
||||
]
|
||||
];
|
||||
|
||||
@@ -264,75 +230,53 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check WHO replied (was it the owner of the ticket or someone else on CC?)
|
||||
if (empty($ticket_contact_email) || $ticket_contact_email !== $from_email) {
|
||||
|
||||
// It wasn't the contact currently assigned to the ticket, check if it's another registered contact for that client
|
||||
|
||||
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT contact_id FROM contacts WHERE contact_email = '$from_email' AND contact_client_id = $client_id LIMIT 1"));
|
||||
$from_email_esc = mysqli_real_escape_string($mysqli, $from_email);
|
||||
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT contact_id FROM contacts WHERE contact_email = '$from_email_esc' AND contact_client_id = $client_id LIMIT 1"));
|
||||
if ($row) {
|
||||
|
||||
// Contact is known - we can keep the reply type as client
|
||||
$ticket_reply_contact = intval($row['contact_id']);
|
||||
|
||||
} else {
|
||||
// Mark the reply as internal as we don't recognise the contact (so the actual contact doesn't see it, and the tech can edit/delete if needed)
|
||||
$ticket_reply_type = 'Internal';
|
||||
$ticket_reply_contact = '0';
|
||||
$message = "<b>WARNING: Contact email mismatch</b><br>$message"; // Add a warning at the start of the message - for the techs benefit (think phishing/scams)
|
||||
$message = "<b>WARNING: Contact email mismatch</b><br>$message";
|
||||
$message_esc = mysqli_real_escape_string($mysqli, $message);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the comment
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = '$message', ticket_reply_type = '$ticket_reply_type', ticket_reply_time_worked = '00:00:00', ticket_reply_by = $ticket_reply_contact, ticket_reply_ticket_id = $ticket_id");
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = '$message_esc', ticket_reply_type = '$ticket_reply_type', ticket_reply_time_worked = '00:00:00', ticket_reply_by = $ticket_reply_contact, ticket_reply_ticket_id = $ticket_id");
|
||||
$reply_id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Process attachments
|
||||
mkdirMissing('uploads/tickets/');
|
||||
foreach ($attachments as $attachment) {
|
||||
|
||||
// Get name and extension
|
||||
$att_name = $attachment->getFileName();
|
||||
$att_name = $attachment->getName();
|
||||
$att_extarr = explode('.', $att_name);
|
||||
$att_extension = strtolower(end($att_extarr));
|
||||
|
||||
// Check the extension is allowed
|
||||
if (in_array($att_extension, $allowed_extensions)) {
|
||||
|
||||
// Setup directory for this ticket ID
|
||||
$att_dir = "uploads/tickets/" . $ticket_id . "/";
|
||||
mkdirMissing($att_dir);
|
||||
|
||||
// Save attachment with a random name
|
||||
$att_saved_path = $attachment->save($att_dir, Parser::ATTACHMENT_RANDOM_FILENAME);
|
||||
|
||||
// Access the random name to add into the database (this won't work on Windows)
|
||||
$att_tmparr = explode($att_dir, $att_saved_path);
|
||||
$att_saved_filename = md5(uniqid(rand(), true)) . '.' . $att_extension;
|
||||
$att_saved_path = "uploads/tickets/" . $ticket_id . "/" . $att_saved_filename;
|
||||
$attachment->save("uploads/tickets/" . $ticket_id); // Save the attachment to the directory
|
||||
rename("uploads/tickets/" . $ticket_id . "/" . $attachment->getName(), $att_saved_path); // Rename the saved file to the hashed name
|
||||
|
||||
$ticket_attachment_name = sanitizeInput($att_name);
|
||||
$ticket_attachment_reference_name = sanitizeInput(end($att_tmparr));
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = '$ticket_attachment_name', ticket_attachment_reference_name = '$ticket_attachment_reference_name', ticket_attachment_reply_id = $reply_id, ticket_attachment_ticket_id = $ticket_id");
|
||||
$ticket_attachment_reference_name = sanitizeInput($att_saved_filename);
|
||||
|
||||
$ticket_attachment_name_esc = mysqli_real_escape_string($mysqli, $ticket_attachment_name);
|
||||
$ticket_attachment_reference_name_esc = mysqli_real_escape_string($mysqli, $ticket_attachment_reference_name);
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = '$ticket_attachment_name_esc', ticket_attachment_reference_name = '$ticket_attachment_reference_name_esc', ticket_attachment_reply_id = $reply_id, ticket_attachment_ticket_id = $ticket_id");
|
||||
} else {
|
||||
$ticket_attachment_name = sanitizeInput($att_name);
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Blocked attachment $ticket_attachment_name from Client contact $from_email for ticket $config_ticket_prefix$ticket_number', log_client_id = $client_id");
|
||||
$ticket_attachment_name_esc = mysqli_real_escape_string($mysqli, $att_name);
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Blocked attachment $ticket_attachment_name_esc from Client contact $from_email_esc for ticket $config_ticket_prefix$ticket_number_esc', log_client_id = $client_id");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// E-mail techs assigned to the ticket to notify them of the reply
|
||||
$ticket_assigned_to = mysqli_query($mysqli, "SELECT ticket_assigned_to FROM tickets WHERE ticket_id = $ticket_id LIMIT 1");
|
||||
|
||||
if ($ticket_assigned_to) {
|
||||
|
||||
$row = mysqli_fetch_array($ticket_assigned_to);
|
||||
$ticket_assigned_to = intval($row['ticket_assigned_to']);
|
||||
|
||||
if ($ticket_assigned_to) {
|
||||
|
||||
// Get tech details
|
||||
$tech_sql = mysqli_query($mysqli, "SELECT user_email, user_name FROM users WHERE user_id = $ticket_assigned_to LIMIT 1");
|
||||
$tech_row = mysqli_fetch_array($tech_sql);
|
||||
$tech_email = sanitizeInput($tech_row['user_email']);
|
||||
@@ -347,193 +291,142 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac
|
||||
'from_name' => $config_ticket_from_name,
|
||||
'recipient' => $tech_email,
|
||||
'recipient_name' => $tech_name,
|
||||
'subject' => $email_subject,
|
||||
'body' => $email_body
|
||||
'subject' => mysqli_real_escape_string($mysqli, $email_subject),
|
||||
'body' => mysqli_real_escape_string($mysqli, $email_body)
|
||||
]
|
||||
];
|
||||
|
||||
addToMailQueue($mysqli, $data);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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 = $client_id LIMIT 1");
|
||||
|
||||
echo "Updated existing ticket.<br>";
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Client contact $from_email updated ticket $config_ticket_prefix$ticket_number ($subject)', log_client_id = $client_id");
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Client contact $from_email_esc updated ticket $config_ticket_prefix$ticket_number_esc ($subject)', log_client_id = $client_id");
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// Invalid ticket number
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// END ADD REPLY FUNCTION -------------------------------------------------
|
||||
|
||||
// Prepare connection string with encryption (TLS/SSL/<blank>)
|
||||
$imap_mailbox = "$config_imap_host:$config_imap_port/imap/novalidate-cert/$config_imap_encryption";
|
||||
$imap_mailbox = "$config_imap_host:$config_imap_port/imap/$config_imap_encryption";
|
||||
|
||||
// Connect to host via IMAP
|
||||
$imap = imap_open("{{$imap_mailbox}}INBOX", $config_imap_username, $config_imap_password);
|
||||
// Connect to the IMAP server
|
||||
$client->connect();
|
||||
|
||||
// Check connection
|
||||
if (!$imap) {
|
||||
// Logging
|
||||
//$extended_log_description = var_export(imap_errors(), true);
|
||||
// Remove the lock file
|
||||
unlink($lock_file_path);
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Mail', log_action = 'Error', log_description = 'Email parser: Failed to connect to IMAP. Details'");
|
||||
exit("Could not connect to IMAP");
|
||||
// Possible names for the inbox folder
|
||||
$inboxNames = ['Inbox', 'INBOX', 'inbox'];
|
||||
|
||||
// Function to get the correct inbox folder
|
||||
function getInboxFolder($client, $inboxNames) {
|
||||
foreach ($inboxNames as $name) {
|
||||
try {
|
||||
$folder = $client->getFolder($name);
|
||||
if ($folder) {
|
||||
return $folder;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Continue to the next name if the current one fails
|
||||
continue;
|
||||
}
|
||||
}
|
||||
throw new Exception("No inbox folder found.");
|
||||
}
|
||||
|
||||
// Check for the ITFlow mailbox that we move messages to once processed
|
||||
$imap_folder = 'ITFlow';
|
||||
$list = imap_list($imap, "{{$imap_mailbox}}", "*");
|
||||
if (array_search("{{$imap_mailbox}}$imap_folder", $list) === false) {
|
||||
imap_createmailbox($imap, imap_utf7_encode("{{$imap_mailbox}}$imap_folder"));
|
||||
imap_subscribe($imap, imap_utf7_encode("{{$imap_mailbox}}$imap_folder"));
|
||||
try {
|
||||
$inbox = getInboxFolder($client, $inboxNames);
|
||||
$messages = $inbox->query()->unseen()->get();
|
||||
} catch (Exception $e) {
|
||||
echo "Error: " . $e->getMessage();
|
||||
}
|
||||
|
||||
// Search for unread ("UNSEEN") emails
|
||||
$emails = imap_search($imap, 'UNSEEN');
|
||||
|
||||
if ($emails) {
|
||||
|
||||
// Sort
|
||||
rsort($emails);
|
||||
|
||||
// Loop through each email
|
||||
foreach ($emails as $email) {
|
||||
|
||||
// Default false
|
||||
if ($messages->count() > 0) {
|
||||
foreach ($messages as $message) {
|
||||
$email_processed = false;
|
||||
|
||||
// Get details from message and invoke PHP Mime Mail Parser
|
||||
$msg_to_parse = imap_fetchheader($imap, $email, FT_PREFETCHTEXT) . imap_body($imap, $email, FT_PEEK);
|
||||
$parser = new PhpMimeMailParser\Parser();
|
||||
$parser->setText($msg_to_parse);
|
||||
mkdirMissing('uploads/tmp/');
|
||||
$original_message_file = "processed-eml-" . randomString(200) . ".eml";
|
||||
file_put_contents("uploads/tmp/{$original_message_file}", $message->getRawMessage());
|
||||
|
||||
// Process message attributes
|
||||
$from_address = $message->getFrom();
|
||||
$from_name = sanitizeInput($from_address[0]->personal ?? 'Unknown');
|
||||
$from_email = sanitizeInput($from_address[0]->mail ?? 'itflow-guest@example.com');
|
||||
|
||||
$from_array = $parser->getAddresses('from')[0];
|
||||
$from_name = sanitizeInput($from_array['display']);
|
||||
|
||||
// Handle blank 'From' emails
|
||||
$from_email = "itflow-guest@example.com";
|
||||
if (filter_var($from_array['address'], FILTER_VALIDATE_EMAIL)) {
|
||||
$from_email = sanitizeInput($from_array['address']);
|
||||
}
|
||||
|
||||
$from_domain = explode("@", $from_array['address']);
|
||||
$from_domain = explode("@", $from_email);
|
||||
$from_domain = sanitizeInput(end($from_domain));
|
||||
|
||||
$subject = sanitizeInput($parser->getHeader('subject'));
|
||||
$date = sanitizeInput($parser->getHeader('date'));
|
||||
$attachments = $parser->getAttachments();
|
||||
$subject = sanitizeInput($message->getSubject() ?? 'No Subject');
|
||||
$date = sanitizeInput($message->getDate() ?? date('Y-m-d H:i:s'));
|
||||
$message_body = $message->getHtmlBody() ?? '';
|
||||
|
||||
// Get the message content
|
||||
// (first try HTML parsing, but switch to plain text if the email is empty/plain-text only)
|
||||
// $message = $parser->getMessageBody('htmlEmbedded');
|
||||
// if (empty($message)) {
|
||||
// echo "DEBUG: Switching to plain text parsing for this message ($subject)";
|
||||
// $message = $parser->getMessageBody('text');
|
||||
// }
|
||||
if (empty($message_body)) {
|
||||
$text_body = $message->getTextBody() ?? '';
|
||||
$message_body = nl2br(htmlspecialchars($text_body));
|
||||
}
|
||||
|
||||
// TODO: Default to getting HTML and fallback to plaintext, but HTML emails seem to break the forward/agent notifications
|
||||
|
||||
$message = $parser->getMessageBody('text');
|
||||
|
||||
// Check if we can identify a ticket number (in square brackets)
|
||||
if (preg_match("/\[$config_ticket_prefix\d+\]/", $subject, $ticket_number)) {
|
||||
|
||||
// Looks like there's a ticket number in the subject line (e.g. [TCK-091]
|
||||
// Process as a ticket reply
|
||||
|
||||
// Get the actual ticket number (without the brackets)
|
||||
preg_match('/\d+/', $ticket_number[0], $ticket_number);
|
||||
$ticket_number = intval($ticket_number[0]);
|
||||
|
||||
if (addReply($from_email, $date, $subject, $ticket_number, $message, $attachments)) {
|
||||
if (addReply($from_email, $date, $subject, $ticket_number, $message_body, $message->getAttachments())) {
|
||||
$email_processed = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Couldn't match this email to an existing ticket
|
||||
|
||||
// Check if we can match the sender to a pre-existing contact
|
||||
$any_contact_sql = mysqli_query($mysqli, "SELECT * FROM contacts WHERE contact_email = '$from_email' LIMIT 1");
|
||||
$from_email_esc = mysqli_real_escape_string($mysqli, $from_email);
|
||||
$any_contact_sql = mysqli_query($mysqli, "SELECT * FROM contacts WHERE contact_email = '$from_email_esc' LIMIT 1");
|
||||
$row = mysqli_fetch_array($any_contact_sql);
|
||||
|
||||
if ($row) {
|
||||
// Sender exists as a contact
|
||||
$contact_name = sanitizeInput($row['contact_name']);
|
||||
$contact_id = intval($row['contact_id']);
|
||||
$contact_email = sanitizeInput($row['contact_email']);
|
||||
$client_id = intval($row['contact_client_id']);
|
||||
|
||||
if (addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message, $attachments)) {
|
||||
if (addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message_body, $message->getAttachments(), $original_message_file)) {
|
||||
$email_processed = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Couldn't match this email to an existing ticket or an existing client contact
|
||||
// Checking to see if the sender domain matches a client website
|
||||
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM domains WHERE domain_name = '$from_domain' LIMIT 1"));
|
||||
$from_domain_esc = mysqli_real_escape_string($mysqli, $from_domain);
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM domains WHERE domain_name = '$from_domain_esc' LIMIT 1"));
|
||||
|
||||
if ($row && $from_domain == $row['domain_name']) {
|
||||
|
||||
// We found a match - create a contact under this client and raise a ticket for them
|
||||
|
||||
// Client details
|
||||
$client_id = intval($row['domain_client_id']);
|
||||
|
||||
// Contact details
|
||||
$password = password_hash(randomString(), PASSWORD_DEFAULT);
|
||||
$contact_name = $from_name; // This was already Sanitized above
|
||||
$contact_email = $from_email; // This was already Sanitized above
|
||||
mysqli_query($mysqli, "INSERT INTO contacts SET contact_name = '$contact_name', contact_email = '$contact_email', contact_notes = 'Added automatically via email parsing.', contact_password_hash = '$password', contact_client_id = $client_id");
|
||||
$contact_name = $from_name;
|
||||
$contact_email = $from_email;
|
||||
mysqli_query($mysqli, "INSERT INTO contacts SET contact_name = '".mysqli_real_escape_string($mysqli, $contact_name)."', contact_email = '".mysqli_real_escape_string($mysqli, $contact_email)."', contact_notes = 'Added automatically via email parsing.', contact_password_hash = '$password', contact_client_id = $client_id");
|
||||
$contact_id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Logging for contact creation
|
||||
echo "Created new contact.<br>";
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Contact', log_action = 'Create', log_description = 'Email parser: created contact $contact_name', log_client_id = $client_id");
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Contact', log_action = 'Create', log_description = 'Email parser: created contact ".mysqli_real_escape_string($mysqli, $contact_name)."', log_client_id = $client_id");
|
||||
|
||||
if (addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message, $attachments)) {
|
||||
if (addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message_body, $message->getAttachments(), $original_message_file)) {
|
||||
$email_processed = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Couldn't match this email to an existing ticket, existing contact or an existing client via the "from" domain
|
||||
// In the future we might make a page where these can be nicely viewed / managed, but for now we'll just flag them in the Inbox as needing attention
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Deal with the message (move it if processed, flag it if not)
|
||||
if ($email_processed) {
|
||||
imap_setflag_full($imap, $email, "\\Seen");
|
||||
imap_mail_move($imap, $email, $imap_folder);
|
||||
$message->setFlag(['Seen']);
|
||||
$message->move('ITFlow');
|
||||
} else {
|
||||
// Basically just flags all emails keep them unread and it doesnt move closed tickets
|
||||
echo "Failed to process email - flagging for manual review.";
|
||||
imap_setflag_full($imap, $email, "\\Flagged");
|
||||
$message->setFlag(['Flagged']);
|
||||
}
|
||||
|
||||
if (file_exists("uploads/tmp/{$original_message_file}")) {
|
||||
unlink("uploads/tmp/{$original_message_file}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
imap_expunge($imap);
|
||||
imap_close($imap);
|
||||
$client->expunge();
|
||||
$client->disconnect();
|
||||
|
||||
// Remove the lock file
|
||||
unlink($lock_file_path);
|
||||
|
||||
376
dashboard.php
376
dashboard.php
@@ -1,12 +1,10 @@
|
||||
<?php
|
||||
require_once "inc_all.php";
|
||||
|
||||
if (isset($_GET['year'])) {
|
||||
$year = intval($_GET['year']);
|
||||
} else {
|
||||
$year = date('Y');
|
||||
}
|
||||
// Get current year or the selected year
|
||||
$year = isset($_GET['year']) ? intval($_GET['year']) : date('Y');
|
||||
|
||||
// Update user settings based on GET parameters
|
||||
if (isset($_GET['enable_financial'])) {
|
||||
$enable_financial = intval($_GET['enable_financial']);
|
||||
mysqli_query($mysqli, "UPDATE user_settings SET user_config_dashboard_financial_enable = $enable_financial WHERE user_id = $session_user_id");
|
||||
@@ -23,10 +21,9 @@ $row = mysqli_fetch_array($sql_user_dashboard_settings);
|
||||
$user_config_dashboard_financial_enable = intval($row['user_config_dashboard_financial_enable']);
|
||||
$user_config_dashboard_technical_enable = intval($row['user_config_dashboard_technical_enable']);
|
||||
|
||||
//GET unique years from expenses, payments invoices and revenues
|
||||
$sql_years_select = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT YEAR(expense_date) AS all_years FROM expenses
|
||||
// Get unique years from expenses, payments, invoices, revenues, tickets, clients, and users
|
||||
$sql_years_select = mysqli_query($mysqli, "
|
||||
SELECT YEAR(expense_date) AS all_years FROM expenses
|
||||
UNION DISTINCT SELECT YEAR(payment_date) FROM payments
|
||||
UNION DISTINCT SELECT YEAR(revenue_date) FROM revenues
|
||||
UNION DISTINCT SELECT YEAR(invoice_date) FROM invoices
|
||||
@@ -34,84 +31,75 @@ $sql_years_select = mysqli_query(
|
||||
UNION DISTINCT SELECT YEAR(client_created_at) FROM clients
|
||||
UNION DISTINCT SELECT YEAR(user_created_at) FROM users
|
||||
ORDER BY all_years DESC
|
||||
"
|
||||
);
|
||||
");
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-body">
|
||||
<form class="form-inline">
|
||||
<input type="hidden" name="enable_financial" value="0">
|
||||
<input type="hidden" name="enable_technical" value="0">
|
||||
|
||||
<select onchange="this.form.submit()" class="form-control mr-sm-3 col-sm-2" name="year">
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_years_select)) {
|
||||
<label for="year" class="mr-sm-2">Select Year:</label>
|
||||
<select id="year" onchange="this.form.submit()" class="form-control mr-sm-3 col-sm-2" name="year">
|
||||
<?php while ($row = mysqli_fetch_array($sql_years_select)) {
|
||||
$year_select = $row['all_years'];
|
||||
if (empty($year_select)) {
|
||||
$year_select = date('Y');
|
||||
}
|
||||
?>
|
||||
<option <?php if ($year == $year_select) { echo "selected"; } ?>> <?php echo $year_select; ?></option>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<option value="<?php echo $year_select; ?>" <?php if ($year == $year_select) { echo "selected"; } ?>>
|
||||
<?php echo $year_select; ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
|
||||
<?php if ($session_user_role == 1 || $session_user_role == 3 && $config_module_enable_accounting == 1) { ?>
|
||||
<?php if ($session_user_role == 1 || ($session_user_role == 3 && $config_module_enable_accounting == 1)) { ?>
|
||||
<div class="custom-control custom-switch mr-sm-3">
|
||||
<input type="checkbox" onchange="this.form.submit()" class="custom-control-input" id="customSwitch1" name="enable_financial" value="1" <?php if ($user_config_dashboard_financial_enable == 1) { echo "checked"; } ?>>
|
||||
<label class="custom-control-label" for="customSwitch1">Toggle Financial</label>
|
||||
<label class="custom-control-label" for="customSwitch1">Financial</label>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($session_user_role >= 2 && $config_module_enable_ticketing == 1) { ?>
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" onchange="this.form.submit()" class="custom-control-input" id="customSwitch2" name="enable_technical" value="1" <?php if ($user_config_dashboard_technical_enable == 1) { echo "checked"; } ?>>
|
||||
<label class="custom-control-label" for="customSwitch2">Toggle Technical</label>
|
||||
<label class="custom-control-label" for="customSwitch2">Technical</label>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
|
||||
if ($user_config_dashboard_financial_enable == 1) {
|
||||
|
||||
// Enforce accountant / admin role for the financial dashboard
|
||||
// Ensure the user has the appropriate role to view the financial dashboard
|
||||
if ($_SESSION['user_role'] != 3 && $_SESSION['user_role'] != 1) {
|
||||
exit('<script type="text/javascript">window.location.href = \'dashboard_technical.php\';</script>');
|
||||
}
|
||||
|
||||
|
||||
//Define var so it doesnt throw errors in logs
|
||||
// Fetch financial data for the dashboard
|
||||
// Define variables to avoid errors in logs
|
||||
$largest_income_month = 0;
|
||||
|
||||
|
||||
//Get Total income
|
||||
$sql_total_payments_to_invoices = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS total_payments_to_invoices FROM payments WHERE YEAR(payment_date) = $year");
|
||||
$row = mysqli_fetch_array($sql_total_payments_to_invoices);
|
||||
$total_payments_to_invoices = floatval($row['total_payments_to_invoices']);
|
||||
//Do not grab transfer payment as these have a category_id of 0
|
||||
|
||||
$sql_total_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS total_revenues FROM revenues WHERE YEAR(revenue_date) = $year AND revenue_category_id > 0");
|
||||
$row = mysqli_fetch_array($sql_total_revenues);
|
||||
$total_revenues = floatval($row['total_revenues']);
|
||||
|
||||
$total_income = $total_payments_to_invoices + $total_revenues;
|
||||
|
||||
//Get Total expenses and do not grab transfer expenses as these have a vendor of 0
|
||||
$sql_total_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS total_expenses FROM expenses WHERE expense_vendor_id > 0 AND YEAR(expense_date) = $year");
|
||||
$row = mysqli_fetch_array($sql_total_expenses);
|
||||
$total_expenses = floatval($row['total_expenses']);
|
||||
|
||||
//Total up all the Invoices that are not draft or cancelled
|
||||
$sql_invoice_totals = mysqli_query($mysqli, "SELECT SUM(invoice_amount) AS invoice_totals FROM invoices WHERE invoice_status NOT LIKE 'Draft' AND invoice_status NOT LIKE 'Cancelled' AND YEAR(invoice_date) = $year");
|
||||
$row = mysqli_fetch_array($sql_invoice_totals);
|
||||
$invoice_totals = floatval($row['invoice_totals']);
|
||||
|
||||
//Quaeries from Receivables
|
||||
$sql_total_payments_to_invoices_all_years = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS total_payments_to_invoices_all_years FROM payments");
|
||||
$row = mysqli_fetch_array($sql_total_payments_to_invoices_all_years);
|
||||
$total_payments_to_invoices_all_years = floatval($row['total_payments_to_invoices_all_years']);
|
||||
@@ -126,68 +114,55 @@ if ($user_config_dashboard_financial_enable == 1) {
|
||||
|
||||
$sql_accounts = mysqli_query($mysqli, "SELECT * FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
|
||||
|
||||
$sql_latest_invoice_payments = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT * FROM payments, invoices, clients
|
||||
WHERE payment_invoice_id = invoice_id
|
||||
AND invoice_client_id = client_id
|
||||
ORDER BY payment_id DESC LIMIT 5"
|
||||
);
|
||||
$sql_latest_invoice_payments = mysqli_query($mysqli, "
|
||||
SELECT * FROM payments
|
||||
JOIN invoices ON payment_invoice_id = invoice_id
|
||||
JOIN clients ON invoice_client_id = client_id
|
||||
ORDER BY payment_id DESC LIMIT 5
|
||||
");
|
||||
|
||||
$sql_latest_expenses = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT * FROM expenses, vendors, categories
|
||||
WHERE expense_vendor_id = vendor_id
|
||||
AND expense_category_id = category_id
|
||||
ORDER BY expense_id DESC LIMIT 5"
|
||||
);
|
||||
$sql_latest_expenses = mysqli_query($mysqli, "
|
||||
SELECT * FROM expenses
|
||||
JOIN vendors ON expense_vendor_id = vendor_id
|
||||
JOIN categories ON expense_category_id = category_id
|
||||
ORDER BY expense_id DESC LIMIT 5
|
||||
");
|
||||
|
||||
//Get Yearly Recurring Income Total
|
||||
// Get recurring totals
|
||||
$sql_recurring_yearly_total = mysqli_query($mysqli, "SELECT SUM(recurring_amount) AS recurring_yearly_total FROM recurring WHERE recurring_status = 1 AND recurring_frequency = 'year' AND YEAR(recurring_created_at) <= $year");
|
||||
$row = mysqli_fetch_array($sql_recurring_yearly_total);
|
||||
$recurring_yearly_total = floatval($row['recurring_yearly_total']);
|
||||
|
||||
//Get Monthly Recurring Income Total
|
||||
$sql_recurring_monthly_total = mysqli_query($mysqli, "SELECT SUM(recurring_amount) AS recurring_monthly_total FROM recurring WHERE recurring_status = 1 AND recurring_frequency = 'month' AND YEAR(recurring_created_at) <= $year");
|
||||
$row = mysqli_fetch_array($sql_recurring_monthly_total);
|
||||
$recurring_monthly_total = floatval($row['recurring_monthly_total']) + ($recurring_yearly_total / 12);
|
||||
|
||||
//Get Yearly Recurring Expenses Total
|
||||
$sql_recurring_expense_yearly_total = mysqli_query($mysqli, "SELECT SUM(recurring_expense_amount) AS recurring_expense_yearly_total FROM recurring_expenses WHERE recurring_expense_status = 1 AND recurring_expense_frequency = 2 AND YEAR(recurring_expense_created_at) <= $year");
|
||||
$sql_recurring_expense_yearly_total = mysqli_query($mysqli, "SELECT SUM(recurring_expense_amount) AS recurring_expense_yearly_total FROM recurring_expenses WHERE recurring_expense_status = 1 AND recurring_expense_frequency = 'year' AND YEAR(recurring_expense_created_at) <= $year");
|
||||
$row = mysqli_fetch_array($sql_recurring_expense_yearly_total);
|
||||
$recurring_expense_yearly_total = floatval($row['recurring_expense_yearly_total']);
|
||||
|
||||
//Get Monthly Recurring Expenses Total
|
||||
$sql_recurring_expense_monthly_total = mysqli_query($mysqli, "SELECT SUM(recurring_expense_amount) AS recurring_expense_monthly_total FROM recurring_expenses WHERE recurring_expense_status = 1 AND recurring_expense_frequency = 1 AND YEAR(recurring_expense_created_at) <= $year");
|
||||
$sql_recurring_expense_monthly_total = mysqli_query($mysqli, "SELECT SUM(recurring_expense_amount) AS recurring_expense_monthly_total FROM recurring_expenses WHERE recurring_expense_status = 1 AND recurring_expense_frequency = 'month' AND YEAR(recurring_expense_created_at) <= $year");
|
||||
$row = mysqli_fetch_array($sql_recurring_expense_monthly_total);
|
||||
$recurring_expense_monthly_total = floatval($row['recurring_expense_monthly_total']) + ($recurring_expense_yearly_total / 12);
|
||||
|
||||
//Get Total Miles Driven
|
||||
$sql_miles_driven = mysqli_query($mysqli, "SELECT SUM(trip_miles) AS total_miles FROM trips WHERE YEAR(trip_date) = $year");
|
||||
$row = mysqli_fetch_array($sql_miles_driven);
|
||||
$total_miles = floatval($row['total_miles']);
|
||||
|
||||
if ($config_module_enable_ticketing && $config_module_enable_accounting) {
|
||||
//Get Unbilled, closed tickets
|
||||
$sql_unbilled_tickets = mysqli_query($mysqli, "SELECT COUNT('ticket_id') AS unbilled_tickets FROM tickets WHERE ticket_closed_at IS NOT NULL AND ticket_billable = 1 AND ticket_invoice_id = 0 AND YEAR(ticket_created_at) = $year");
|
||||
$sql_unbilled_tickets = mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS unbilled_tickets FROM tickets WHERE ticket_closed_at IS NOT NULL AND ticket_billable = 1 AND ticket_invoice_id = 0 AND YEAR(ticket_created_at) = $year");
|
||||
$row = mysqli_fetch_array($sql_unbilled_tickets);
|
||||
$unbilled_tickets = intval($row['unbilled_tickets']);
|
||||
} else {
|
||||
//Get Total Recurring Invoices added
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('recurring_id') AS recurring_invoices_added FROM recurring WHERE YEAR(recurring_created_at) = $year"));
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(recurring_id) AS recurring_invoices_added FROM recurring WHERE YEAR(recurring_created_at) = $year"));
|
||||
$recurring_invoices_added = intval($row['recurring_invoices_added']);
|
||||
}
|
||||
|
||||
|
||||
//Get Total Clients added
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('client_id') AS clients_added FROM clients WHERE YEAR(client_created_at) = $year AND client_archived_at IS NULL"));
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(client_id) AS clients_added FROM clients WHERE YEAR(client_created_at) = $year AND client_archived_at IS NULL"));
|
||||
$clients_added = intval($row['clients_added']);
|
||||
|
||||
|
||||
//Get Total Vendors added
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('vendor_id') AS vendors_added FROM vendors WHERE YEAR(vendor_created_at) = $year AND vendor_client_id = 0 AND vendor_template = 0 AND vendor_archived_at IS NULL"));
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(vendor_id) AS vendors_added FROM vendors WHERE YEAR(vendor_created_at) = $year AND vendor_client_id = 0 AND vendor_template = 0 AND vendor_archived_at IS NULL"));
|
||||
$vendors_added = intval($row['vendors_added']);
|
||||
|
||||
?>
|
||||
<div class="card card-body">
|
||||
<!-- Icon Cards-->
|
||||
@@ -199,7 +174,7 @@ if ($user_config_dashboard_financial_enable == 1) {
|
||||
<h3><?php echo numfmt_format_currency($currency_format, $total_income, "$session_company_currency"); ?></h3>
|
||||
<p>Income</p>
|
||||
<hr>
|
||||
<small>Receivables: <?php echo numfmt_format_currency($currency_format, $receivables, "$session_company_currency"); ?></h3></small>
|
||||
<small>Receivables: <?php echo numfmt_format_currency($currency_format, $receivables, "$session_company_currency"); ?></small>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fa fa-hand-holding-usd"></i>
|
||||
@@ -265,24 +240,19 @@ if ($user_config_dashboard_financial_enable == 1) {
|
||||
<!-- ./col -->
|
||||
|
||||
<?php if ($config_module_enable_ticketing && $config_module_enable_accounting) { ?>
|
||||
|
||||
<div class="col-lg-4 col-md-6 col-sm-12">
|
||||
<!-- small box -->
|
||||
<a class="small-box bg-secondary" href="report_tickets_unbilled.php">
|
||||
<div class="inner">
|
||||
<h3><?php echo $unbilled_tickets; ?></h3>
|
||||
<p>Unbilled Ticket<?php if ($unbilled_tickets > 1 || $unbilled_tickets = 0) {
|
||||
echo "s";
|
||||
} ?></p>
|
||||
<p>Unbilled Ticket<?php if ($unbilled_tickets > 1 || $unbilled_tickets == 0) { echo "s"; } ?></p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fa fa-ticket-alt"></i>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php } else { ?>
|
||||
|
||||
<div class="col-lg-4 col-md-6 col-sm-12">
|
||||
<!-- small box -->
|
||||
<a class="small-box bg-secondary" href="recurring_invoices.php?dtf=<?php echo $year; ?>-01-01&dtt=<?php echo $year; ?>-12-31">
|
||||
@@ -295,7 +265,6 @@ if ($user_config_dashboard_financial_enable == 1) {
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<!-- ./col -->
|
||||
<?php } ?>
|
||||
|
||||
<div class="col-lg-4 col-6">
|
||||
@@ -420,12 +389,10 @@ if ($user_config_dashboard_financial_enable == 1) {
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($sql_accounts)) {
|
||||
<?php while ($row = mysqli_fetch_array($sql_accounts)) {
|
||||
$account_id = intval($row['account_id']);
|
||||
$account_name = nullable_htmlentities($row['account_name']);
|
||||
$opening_balance = floatval($row['opening_balance']);
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td><?php echo $account_name; ?></td>
|
||||
@@ -450,15 +417,13 @@ if ($user_config_dashboard_financial_enable == 1) {
|
||||
?>
|
||||
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $balance, "$session_company_currency"); ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- .col -->
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card card-dark mb-3">
|
||||
<div class="card-header">
|
||||
@@ -480,8 +445,7 @@ if ($user_config_dashboard_financial_enable == 1) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($sql_latest_invoice_payments)) {
|
||||
<?php while ($row = mysqli_fetch_array($sql_latest_invoice_payments)) {
|
||||
$payment_date = nullable_htmlentities($row['payment_date']);
|
||||
$payment_amount = floatval($row['payment_amount']);
|
||||
$invoice_prefix = nullable_htmlentities($row['invoice_prefix']);
|
||||
@@ -494,14 +458,13 @@ if ($user_config_dashboard_financial_enable == 1) {
|
||||
<td><?php echo "$invoice_prefix$invoice_number"; ?></td>
|
||||
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $payment_amount, "$session_company_currency"); ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- .col -->
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card card-dark mb-3">
|
||||
<div class="card-header">
|
||||
@@ -523,13 +486,11 @@ if ($user_config_dashboard_financial_enable == 1) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
while ($row = mysqli_fetch_array($sql_latest_expenses)) {
|
||||
<?php while ($row = mysqli_fetch_array($sql_latest_expenses)) {
|
||||
$expense_date = nullable_htmlentities($row['expense_date']);
|
||||
$expense_amount = floatval($row['expense_amount']);
|
||||
$vendor_name = nullable_htmlentities($row['vendor_name']);
|
||||
$category_name = nullable_htmlentities($row['category_name']);
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td><?php echo $expense_date; ?></td>
|
||||
@@ -537,14 +498,13 @@ if ($user_config_dashboard_financial_enable == 1) {
|
||||
<td><?php echo $category_name; ?></td>
|
||||
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $expense_amount, "$session_company_currency"); ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- .col -->
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="card card-dark mb-3">
|
||||
<div class="card-header">
|
||||
@@ -564,100 +524,51 @@ if ($user_config_dashboard_financial_enable == 1) {
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- row -->
|
||||
</div> <!--card -->
|
||||
</div> <!-- card -->
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<!-- Technical Dashboard -->
|
||||
|
||||
<?php
|
||||
|
||||
if ($user_config_dashboard_technical_enable == 1) {
|
||||
|
||||
// Get Total Clients added
|
||||
$sql_clients = mysqli_fetch_assoc(mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT COUNT('client_id') AS clients_added FROM clients
|
||||
WHERE YEAR(client_created_at) = $year"
|
||||
));
|
||||
// Fetch technical data for the dashboard
|
||||
$sql_clients = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(client_id) AS clients_added FROM clients WHERE YEAR(client_created_at) = $year"));
|
||||
$clients_added = $sql_clients['clients_added'];
|
||||
|
||||
// Get Total contacts added
|
||||
$sql_contacts = mysqli_fetch_assoc(mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT COUNT('contact_id') AS contacts_added FROM contacts
|
||||
WHERE YEAR(contact_created_at) = $year"
|
||||
));
|
||||
$sql_contacts = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(contact_id) AS contacts_added FROM contacts WHERE YEAR(contact_created_at) = $year"));
|
||||
$contacts_added = $sql_contacts['contacts_added'];
|
||||
|
||||
// Get Total assets added
|
||||
$sql_assets = mysqli_fetch_assoc(mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT COUNT('asset_id') AS assets_added FROM assets
|
||||
WHERE YEAR(asset_created_at) = $year"
|
||||
));
|
||||
$sql_assets = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(asset_id) AS assets_added FROM assets WHERE YEAR(asset_created_at) = $year"));
|
||||
$assets_added = $sql_assets['assets_added'];
|
||||
|
||||
// Ticket count
|
||||
$sql_tickets = mysqli_fetch_assoc(mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT COUNT('ticket_id') AS active_tickets
|
||||
FROM tickets
|
||||
WHERE ticket_closed_at IS NULL"
|
||||
));
|
||||
$sql_tickets = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS active_tickets FROM tickets WHERE ticket_closed_at IS NULL"));
|
||||
$active_tickets = $sql_tickets['active_tickets'];
|
||||
|
||||
// Your Ticket count
|
||||
$sql_your_tickets = mysqli_fetch_assoc(mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT COUNT('ticket_id') AS your_tickets
|
||||
FROM tickets
|
||||
WHERE ticket_closed_at IS NULL
|
||||
AND ticket_assigned_to = $session_user_id"
|
||||
));
|
||||
$sql_your_tickets = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(ticket_id) AS your_tickets FROM tickets WHERE ticket_closed_at IS NULL AND ticket_assigned_to = $session_user_id"));
|
||||
$your_tickets = $sql_your_tickets['your_tickets'];
|
||||
|
||||
// Expiring domains (but not ones that have already expired)
|
||||
$sql_domains_expiring = mysqli_fetch_assoc(mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT COUNT('domain_id') as expiring_domains
|
||||
FROM domains
|
||||
WHERE domain_expire IS NOT NULL
|
||||
AND domain_expire > CURRENT_DATE
|
||||
AND domain_expire < CURRENT_DATE + INTERVAL 30 DAY
|
||||
AND domain_archived_at IS NULL"
|
||||
));
|
||||
$sql_domains_expiring = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(domain_id) AS expiring_domains FROM domains WHERE domain_expire IS NOT NULL AND domain_expire > CURRENT_DATE AND domain_expire < CURRENT_DATE + INTERVAL 30 DAY AND domain_archived_at IS NULL"));
|
||||
$expiring_domains = $sql_domains_expiring['expiring_domains'];
|
||||
|
||||
// Expiring Certificates (but not ones that have already expired)
|
||||
$sql_certs_expiring = mysqli_fetch_assoc(mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT COUNT('certificate_id') as expiring_certs
|
||||
FROM certificates
|
||||
WHERE certificate_expire IS NOT NULL
|
||||
AND certificate_expire > CURRENT_DATE
|
||||
AND certificate_expire < CURRENT_DATE + INTERVAL 30 DAY
|
||||
AND certificate_archived_at IS NULL"
|
||||
));
|
||||
$sql_certs_expiring = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(certificate_id) AS expiring_certs FROM certificates WHERE certificate_expire IS NOT NULL AND certificate_expire > CURRENT_DATE AND certificate_expire < CURRENT_DATE + INTERVAL 30 DAY AND certificate_archived_at IS NULL"));
|
||||
$expiring_certificates = $sql_certs_expiring['expiring_certs'];
|
||||
|
||||
$sql_your_tickets = mysqli_query(
|
||||
$mysqli,
|
||||
"SELECT * FROM tickets
|
||||
LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id
|
||||
LEFT JOIN clients ON ticket_client_id = client_id
|
||||
LEFT JOIN contacts ON ticket_contact_id = contact_id
|
||||
WHERE ticket_assigned_to = $session_user_id
|
||||
AND ticket_closed_at IS NULL
|
||||
ORDER BY ticket_number DESC"
|
||||
);
|
||||
|
||||
$sql_your_tickets = mysqli_query($mysqli, "
|
||||
SELECT * FROM tickets
|
||||
LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id
|
||||
LEFT JOIN clients ON ticket_client_id = client_id
|
||||
LEFT JOIN contacts ON ticket_contact_id = contact_id
|
||||
WHERE ticket_assigned_to = $session_user_id
|
||||
AND ticket_closed_at IS NULL
|
||||
ORDER BY ticket_number DESC
|
||||
");
|
||||
?>
|
||||
|
||||
<div class="card card-body">
|
||||
<!-- Icon Cards-->
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-4 col-6">
|
||||
<!-- small box -->
|
||||
<a class="small-box bg-secondary" href="clients.php?date_from=<?php echo $year; ?>-01-01&date_to=<?php echo $year; ?>-12-31">
|
||||
@@ -736,8 +647,7 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
</a>
|
||||
</div>
|
||||
<!-- ./col -->
|
||||
|
||||
</div> <!-- rows -->
|
||||
</div> <!-- row -->
|
||||
|
||||
<?php if ($your_tickets) { ?>
|
||||
<div class="row">
|
||||
@@ -765,9 +675,7 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql_your_tickets)) {
|
||||
<?php while ($row = mysqli_fetch_array($sql_your_tickets)) {
|
||||
$ticket_id = intval($row['ticket_id']);
|
||||
$ticket_prefix = nullable_htmlentities($row['ticket_prefix']);
|
||||
$ticket_number = intval($row['ticket_number']);
|
||||
@@ -780,83 +688,39 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
$ticket_created_at_time_ago = timeAgo($row['ticket_created_at']);
|
||||
$ticket_updated_at = nullable_htmlentities($row['ticket_updated_at']);
|
||||
$ticket_updated_at_time_ago = timeAgo($row['ticket_updated_at']);
|
||||
if (empty($ticket_updated_at)) {
|
||||
if (!empty($ticket_closed_at)) {
|
||||
$ticket_updated_at_display = "<p>Never</p>";
|
||||
} else {
|
||||
$ticket_updated_at_display = "<p class='text-danger'>Never</p>";
|
||||
}
|
||||
} else {
|
||||
$ticket_updated_at_display = "$ticket_updated_at_time_ago";
|
||||
}
|
||||
|
||||
$ticket_updated_at_display = empty($ticket_updated_at) ? (empty($ticket_closed_at) ? "<p class='text-danger'>Never</p>" : "<p>Never</p>") : $ticket_updated_at_time_ago;
|
||||
|
||||
$client_id = intval($row['ticket_client_id']);
|
||||
$client_name = nullable_htmlentities($row['client_name']);
|
||||
$contact_id = intval($row['ticket_contact_id']);
|
||||
$contact_name = nullable_htmlentities($row['contact_name']);
|
||||
|
||||
|
||||
|
||||
if ($ticket_priority == "High") {
|
||||
$ticket_priority_color = "danger";
|
||||
} elseif ($ticket_priority == "Medium") {
|
||||
$ticket_priority_color = "warning";
|
||||
} else {
|
||||
$ticket_priority_color = "info";
|
||||
}
|
||||
|
||||
if (empty($contact_name)) {
|
||||
$contact_display = "-";
|
||||
} else {
|
||||
$contact_display = "<a href='client_contact_details.php?client_id=$client_id&contact_id=$contact_id'>$contact_name</a>";
|
||||
}
|
||||
|
||||
$ticket_priority_color = $ticket_priority == "High" ? "danger" : ($ticket_priority == "Medium" ? "warning" : "info");
|
||||
$contact_display = empty($contact_name) ? "-" : "<a href='client_contact_details.php?client_id=$client_id&contact_id=$contact_id'>$contact_name</a>";
|
||||
?>
|
||||
|
||||
<tr class="<?php if (empty($ticket_updated_at)) {
|
||||
echo "text-bold";
|
||||
} ?>">
|
||||
<tr class="<?php echo empty($ticket_updated_at) ? 'text-bold' : ''; ?>">
|
||||
<td><a class="text-dark" href="ticket.php?ticket_id=<?php echo $ticket_id; ?>"><?php echo "$ticket_prefix$ticket_number"; ?></a></td>
|
||||
<td>
|
||||
<a href="ticket.php?ticket_id=<?php echo $ticket_id; ?>"><?php echo $ticket_subject; ?></a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="client_tickets.php?client_id=<?php echo $client_id; ?>"><strong><?php echo $client_name; ?></strong></a>
|
||||
</td>
|
||||
<td><a href="ticket.php?ticket_id=<?php echo $ticket_id; ?>"><?php echo $ticket_subject; ?></a></td>
|
||||
<td><a href="client_tickets.php?client_id=<?php echo $client_id; ?>"><strong><?php echo $client_name; ?></strong></a></td>
|
||||
<td><?php echo $contact_display; ?></td>
|
||||
<td><span class='p-2 badge badge-pill badge-<?php echo $ticket_priority_color; ?>'><?php echo $ticket_priority; ?></span></td>
|
||||
<td>
|
||||
<span class='badge badge-pill text-light p-2' style="background-color: <?php echo $ticket_status_color; ?>"><?php echo $ticket_status_name; ?></span>
|
||||
</td>
|
||||
<td><span class='badge badge-pill text-light p-2' style="background-color: <?php echo $ticket_status_color; ?>"><?php echo $ticket_status_name; ?></span></td>
|
||||
<td><?php echo $ticket_updated_at_display; ?></td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div> <!-- Card -->
|
||||
|
||||
<?php } ?>
|
||||
|
||||
|
||||
</div> <!-- card -->
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<!-- End Tech Dashboard -->
|
||||
|
||||
<?php require_once "footer.php";
|
||||
?>
|
||||
<?php require_once "footer.php"; ?>
|
||||
|
||||
<script>
|
||||
// Set new default font family and font color to mimic Bootstrap's default styling
|
||||
@@ -895,17 +759,9 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
if ($income_for_month > 0 && $income_for_month > $largest_income_month) {
|
||||
$largest_income_month = $income_for_month;
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
<?php echo "$income_for_month,"; ?>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<?php } ?>
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -934,17 +790,9 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
if ($income_for_month > 0 && $income_for_month > $largest_income_month) {
|
||||
$largest_income_month = $income_for_month;
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
<?php echo "$income_for_month,"; ?>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<?php } ?>
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -959,9 +807,7 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
pointBorderWidth: 2,
|
||||
data: [
|
||||
<?php
|
||||
|
||||
$largest_invoice_month = 0;
|
||||
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
$sql_projected = mysqli_query($mysqli, "SELECT SUM(invoice_amount) AS invoice_amount_for_month FROM invoices WHERE YEAR(invoice_due) = $year AND MONTH(invoice_due) = $month AND invoice_status NOT LIKE 'Cancelled' AND invoice_status NOT LIKE 'Draft'");
|
||||
$row = mysqli_fetch_array($sql_projected);
|
||||
@@ -970,16 +816,9 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
if ($invoice_for_month > 0 && $invoice_for_month > $largest_invoice_month) {
|
||||
$largest_invoice_month = $invoice_for_month;
|
||||
}
|
||||
|
||||
?>
|
||||
<?php echo "$invoice_for_month,"; ?>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<?php } ?>
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -995,9 +834,7 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
pointBorderWidth: 2,
|
||||
data: [
|
||||
<?php
|
||||
|
||||
$largest_expense_month = 0;
|
||||
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
$sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS expense_amount_for_month FROM expenses WHERE YEAR(expense_date) = $year AND MONTH(expense_date) = $month AND expense_vendor_id > 0");
|
||||
$row = mysqli_fetch_array($sql_expenses);
|
||||
@@ -1006,17 +843,9 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
if ($expenses_for_month > 0 && $expenses_for_month > $largest_expense_month) {
|
||||
$largest_expense_month = $expenses_for_month;
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
<?php echo "$expenses_for_month,"; ?>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<?php } ?>
|
||||
],
|
||||
}
|
||||
],
|
||||
@@ -1076,26 +905,18 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
pointBorderWidth: 2,
|
||||
data: [
|
||||
<?php
|
||||
$largest_trip_miles_month = 0;
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
$sql_trips = mysqli_query($mysqli, "SELECT SUM(trip_miles) AS trip_miles_for_month FROM trips WHERE YEAR(trip_date) = $year AND MONTH(trip_date) = $month");
|
||||
$row = mysqli_fetch_array($sql_trips);
|
||||
$trip_miles_for_month = floatval($row['trip_miles_for_month']);
|
||||
$largest_trip_miles_month = 0;
|
||||
|
||||
if ($trip_miles_for_month > 0 && $trip_miles_for_month > $largest_trip_miles_month) {
|
||||
$largest_trip_miles_month = $trip_miles_for_month;
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
<?php echo "$trip_miles_for_month,"; ?>
|
||||
|
||||
<?php
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<?php } ?>
|
||||
],
|
||||
}],
|
||||
},
|
||||
@@ -1130,10 +951,6 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
}
|
||||
});
|
||||
|
||||
// Set new default font family and font color to mimic Bootstrap's default styling
|
||||
Chart.defaults.global.defaultFontFamily = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
|
||||
Chart.defaults.global.defaultFontColor = '#292b2c';
|
||||
|
||||
// Pie Chart Example
|
||||
var ctx = document.getElementById("incomeByCategoryPieChart");
|
||||
var myPieChart = new Chart(ctx, {
|
||||
@@ -1155,7 +972,6 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
echo "'Others',";
|
||||
}
|
||||
?>
|
||||
|
||||
],
|
||||
datasets: [{
|
||||
data: [
|
||||
@@ -1169,7 +985,6 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
echo "$other_income,";
|
||||
}
|
||||
?>
|
||||
|
||||
],
|
||||
backgroundColor: [
|
||||
<?php
|
||||
@@ -1182,7 +997,6 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
echo "'#999999',"; // color for 'Others' category
|
||||
}
|
||||
?>
|
||||
|
||||
],
|
||||
}],
|
||||
},
|
||||
@@ -1194,10 +1008,6 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
}
|
||||
});
|
||||
|
||||
// Set new default font family and font color to mimic Bootstrap's default styling
|
||||
Chart.defaults.global.defaultFontFamily = '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
|
||||
Chart.defaults.global.defaultFontColor = '#292b2c';
|
||||
|
||||
// Pie Chart Example
|
||||
var ctx = document.getElementById("expenseByCategoryPieChart");
|
||||
var myPieChart = new Chart(ctx, {
|
||||
@@ -1219,7 +1029,6 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
echo "'Others',";
|
||||
}
|
||||
?>
|
||||
|
||||
],
|
||||
datasets: [{
|
||||
data: [
|
||||
@@ -1233,7 +1042,6 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
echo "$other_expense,";
|
||||
}
|
||||
?>
|
||||
|
||||
],
|
||||
backgroundColor: [
|
||||
<?php
|
||||
@@ -1246,7 +1054,6 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
echo "'#999999',"; // color for 'Others' category
|
||||
}
|
||||
?>
|
||||
|
||||
],
|
||||
}],
|
||||
},
|
||||
@@ -1279,7 +1086,6 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
echo "'Others',";
|
||||
}
|
||||
?>
|
||||
|
||||
],
|
||||
datasets: [{
|
||||
data: [
|
||||
@@ -1293,7 +1099,6 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
echo "$other_expense,";
|
||||
}
|
||||
?>
|
||||
|
||||
],
|
||||
backgroundColor: [
|
||||
<?php
|
||||
@@ -1306,7 +1111,6 @@ if ($user_config_dashboard_technical_enable == 1) {
|
||||
echo "'#999999',"; // color for 'Others' vendor
|
||||
}
|
||||
?>
|
||||
|
||||
],
|
||||
}],
|
||||
},
|
||||
|
||||
@@ -1937,15 +1937,159 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) {
|
||||
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.5'");
|
||||
}
|
||||
|
||||
// if (CURRENT_DATABASE_VERSION == '1.3.5') {
|
||||
// // Insert queries here required to update to DB version 1.3.6
|
||||
if (CURRENT_DATABASE_VERSION == '1.3.5') {
|
||||
mysqli_query($mysqli, "CREATE TABLE `contact_tags` (`contact_id` int(11) NOT NULL,`tag_id` int(11) NOT NULL, PRIMARY KEY (`contact_id`,`tag_id`))");
|
||||
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.6'");
|
||||
}
|
||||
|
||||
if (CURRENT_DATABASE_VERSION == '1.3.6') {
|
||||
mysqli_query($mysqli, "ALTER TABLE `clients` ADD `client_abbreviation` VARCHAR(10) DEFAULT NULL AFTER `client_tax_id_number`");
|
||||
|
||||
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.7'");
|
||||
}
|
||||
|
||||
if (CURRENT_DATABASE_VERSION == '1.3.7') {
|
||||
mysqli_query($mysqli, "ALTER TABLE `assets` ADD `asset_ipv6` VARCHAR(200) DEFAULT NULL AFTER `asset_ip`");
|
||||
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.8'");
|
||||
}
|
||||
|
||||
if (CURRENT_DATABASE_VERSION == '1.3.8') {
|
||||
mysqli_query($mysqli, "DROP TABLE `interfaces`");
|
||||
|
||||
mysqli_query($mysqli, "CREATE TABLE `asset_interfaces` (
|
||||
`interface_id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`interface_name` VARCHAR(200) NOT NULL,
|
||||
`interface_mac` VARCHAR(200) DEFAULT NULL,
|
||||
`interface_ip` VARCHAR(200) DEFAULT NULL,
|
||||
`interface_nat_ip` VARCHAR(200) DEFAULT NULL,
|
||||
`interface_ipv6` VARCHAR(200) DEFAULT NULL,
|
||||
`interface_port` VARCHAR(200) DEFAULT NULL,
|
||||
`interface_notes` TEXT DEFAULT NULL,
|
||||
`interface_primary` TINYINT(1) DEFAULT 0,
|
||||
`interface_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`interface_updated_at` DATETIME ON UPDATE CURRENT_TIMESTAMP NULL,
|
||||
`interface_archived_at` DATETIME NULL,
|
||||
`interface_network_id` INT(11) DEFAULT NULL,
|
||||
`interface_asset_id` INT(11) NOT NULL,
|
||||
PRIMARY KEY (`interface_id`)
|
||||
)");
|
||||
|
||||
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.9'");
|
||||
|
||||
}
|
||||
|
||||
if (CURRENT_DATABASE_VERSION == '1.3.9') {
|
||||
// Migrate all Network Info from Assets to Interface Table and make it primary interface
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM assets");
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$asset_id = intval($row['asset_id']);
|
||||
$mac = sanitizeInput($row['asset_mac']);
|
||||
$ip = sanitizeInput($row['asset_ip']);
|
||||
$nat_ip = sanitizeInput($row['asset_nat_ip']);
|
||||
$ipv6 = sanitizeInput($row['asset_ipv6']);
|
||||
$network = intval($row['asset_network_id']);
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO `asset_interfaces` SET interface_name = 'Primary', interface_mac = '$mac', interface_ip = '$ip', interface_nat_ip = '$nat_ip', interface_ipv6 = '$ipv6', interface_port = 'eth0', interface_primary = 1, interface_network_id = $network, interface_asset_id = $asset_id");
|
||||
}
|
||||
|
||||
// Drop Fields from assets as they moved to asset_interfaces
|
||||
mysqli_query($mysqli, "ALTER TABLE `assets` DROP `asset_ip`");
|
||||
mysqli_query($mysqli, "ALTER TABLE `assets` DROP `asset_ipv6`");
|
||||
mysqli_query($mysqli, "ALTER TABLE `assets` DROP `asset_nat_ip`");
|
||||
mysqli_query($mysqli, "ALTER TABLE `assets` DROP `asset_mac`");
|
||||
mysqli_query($mysqli, "ALTER TABLE `assets` DROP `asset_network_id`");
|
||||
|
||||
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.0'");
|
||||
|
||||
}
|
||||
|
||||
if (CURRENT_DATABASE_VERSION == '1.4.0') {
|
||||
|
||||
mysqli_query($mysqli, "CREATE TABLE `racks` (
|
||||
`rack_id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`rack_name` VARCHAR(200) NOT NULL,
|
||||
`rack_description` TEXT DEFAULT NULL,
|
||||
`rack_model` VARCHAR(200) DEFAULT NULL,
|
||||
`rack_depth` VARCHAR(50) DEFAULT NULL,
|
||||
`rack_type` VARCHAR(50) DEFAULT NULL,
|
||||
`rack_units` INT(11) NOT NULL,
|
||||
`rack_photo` VARCHAR(200) DEFAULT NULL,
|
||||
`rack_physical_location` VARCHAR(200) DEFAULT NULL,
|
||||
`rack_notes` TEXT DEFAULT NULL,
|
||||
`rack_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`rack_updated_at` DATETIME ON UPDATE CURRENT_TIMESTAMP NULL,
|
||||
`rack_archived_at` DATETIME NULL,
|
||||
`rack_location_id` INT(11) DEFAULT NULL,
|
||||
`rack_client_id` INT(11) NOT NULL,
|
||||
PRIMARY KEY (`rack_id`)
|
||||
)");
|
||||
|
||||
mysqli_query($mysqli, "CREATE TABLE `rack_units` (
|
||||
`unit_id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`unit_start_number` INT(11) NOT NULL,
|
||||
`unit_end_number` INT(11) NOT NULL,
|
||||
`unit_device` VARCHAR(200) DEFAULT NULL,
|
||||
`unit_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`unit_updated_at` DATETIME ON UPDATE CURRENT_TIMESTAMP NULL,
|
||||
`unit_archived_at` DATETIME NULL,
|
||||
`unit_asset_id` INT(11) DEFAULT NULL,
|
||||
`unit_rack_id` INT(11) NOT NULL,
|
||||
PRIMARY KEY (`unit_id`),
|
||||
FOREIGN KEY (`unit_rack_id`) REFERENCES `racks`(`rack_id`) ON DELETE CASCADE
|
||||
)");
|
||||
|
||||
mysqli_query($mysqli, "CREATE TABLE `patch_panels` (
|
||||
`patch_panel_id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`patch_panel_name` VARCHAR(200) NOT NULL,
|
||||
`patch_panel_description` TEXT DEFAULT NULL,
|
||||
`patch_panel_type` VARCHAR(200) DEFAULT NULL,
|
||||
`patch_panel_ports` INT(11) NOT NULL,
|
||||
`patch_panel_physical_location` VARCHAR(200) DEFAULT NULL,
|
||||
`patch_panel_notes` TEXT DEFAULT NULL,
|
||||
`patch_panel_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`patch_panel_updated_at` DATETIME ON UPDATE CURRENT_TIMESTAMP NULL,
|
||||
`patch_panel_archived_at` DATETIME NULL,
|
||||
`patch_panel_location_id` INT(11) DEFAULT NULL,
|
||||
`patch_panel_rack_id` INT(11) DEFAULT NULL,
|
||||
`patch_panel_client_id` INT(11) NOT NULL,
|
||||
PRIMARY KEY (`patch_panel_id`)
|
||||
)");
|
||||
|
||||
mysqli_query($mysqli, "CREATE TABLE `patch_panel_ports` (
|
||||
`port_id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`port_number` INT(11) NOT NULL,
|
||||
`port_name` VARCHAR(200) DEFAULT NULL,
|
||||
`port_description` TEXT DEFAULT NULL,
|
||||
`port_type` VARCHAR(200) DEFAULT NULL,
|
||||
`port_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`port_updated_at` DATETIME ON UPDATE CURRENT_TIMESTAMP NULL,
|
||||
`port_archived_at` DATETIME NULL,
|
||||
`port_asset_id` INT(11) DEFAULT NULL,
|
||||
`port_patch_panel_id` INT(11) NOT NULL,
|
||||
PRIMARY KEY (`port_id`),
|
||||
FOREIGN KEY (`port_patch_panel_id`) REFERENCES `patch_panels`(`patch_panel_id`) ON DELETE CASCADE
|
||||
)");
|
||||
|
||||
mysqli_query($mysqli, "ALTER TABLE `assets` ADD `asset_photo` VARCHAR(200) DEFAULT NULL AFTER `asset_install_date`");
|
||||
|
||||
mysqli_query($mysqli, "ALTER TABLE `assets` ADD `asset_physical_location` VARCHAR(200) DEFAULT NULL AFTER `asset_photo`");
|
||||
|
||||
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.1'");
|
||||
}
|
||||
|
||||
if (CURRENT_DATABASE_VERSION == '1.4.1') {
|
||||
mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_log_retention` INT(11) NOT NULL DEFAULT '90' AFTER `config_login_remember_me_expire`;");
|
||||
mysqli_query($mysqli, "UPDATE `settings` SET `config_log_retention` = '2555' WHERE company_id = 1;"); // Set to 7 years for existing installs
|
||||
|
||||
mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.2'");
|
||||
}
|
||||
|
||||
// if (CURRENT_DATABASE_VERSION == '1.4.2') {
|
||||
// // Insert queries here required to update to DB version 1.4.3
|
||||
// // Then, update the database to the next sequential version
|
||||
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.3.6'");
|
||||
// mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.4.3'");
|
||||
// }
|
||||
|
||||
} else {
|
||||
// Up-to-date
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
* It is used in conjunction with database_updates.php
|
||||
*/
|
||||
|
||||
DEFINE("LATEST_DATABASE_VERSION", "1.3.5");
|
||||
DEFINE("LATEST_DATABASE_VERSION", "1.4.2");
|
||||
|
||||
172
db.sql
172
db.sql
@@ -117,6 +117,32 @@ CREATE TABLE `asset_files` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `asset_interfaces`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `asset_interfaces`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `asset_interfaces` (
|
||||
`interface_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`interface_name` varchar(200) NOT NULL,
|
||||
`interface_mac` varchar(200) DEFAULT NULL,
|
||||
`interface_ip` varchar(200) DEFAULT NULL,
|
||||
`interface_nat_ip` varchar(200) DEFAULT NULL,
|
||||
`interface_ipv6` varchar(200) DEFAULT NULL,
|
||||
`interface_port` varchar(200) DEFAULT NULL,
|
||||
`interface_notes` text DEFAULT NULL,
|
||||
`interface_primary` tinyint(1) DEFAULT 0,
|
||||
`interface_created_at` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`interface_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(),
|
||||
`interface_archived_at` datetime DEFAULT NULL,
|
||||
`interface_network_id` int(11) DEFAULT NULL,
|
||||
`interface_asset_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`interface_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `assets`
|
||||
--
|
||||
@@ -133,15 +159,14 @@ CREATE TABLE `assets` (
|
||||
`asset_model` varchar(200) DEFAULT NULL,
|
||||
`asset_serial` varchar(200) DEFAULT NULL,
|
||||
`asset_os` varchar(200) DEFAULT NULL,
|
||||
`asset_ip` varchar(20) DEFAULT NULL,
|
||||
`asset_nat_ip` varchar(200) DEFAULT NULL,
|
||||
`asset_mac` varchar(17) DEFAULT NULL,
|
||||
`asset_uri` varchar(500) DEFAULT NULL,
|
||||
`asset_uri_2` varchar(500) DEFAULT NULL,
|
||||
`asset_status` varchar(200) DEFAULT NULL,
|
||||
`asset_purchase_date` date DEFAULT NULL,
|
||||
`asset_warranty_expire` date DEFAULT NULL,
|
||||
`asset_install_date` date DEFAULT NULL,
|
||||
`asset_photo` varchar(200) DEFAULT NULL,
|
||||
`asset_physical_location` varchar(200) DEFAULT NULL,
|
||||
`asset_notes` text DEFAULT NULL,
|
||||
`asset_important` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`asset_created_at` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
@@ -151,7 +176,6 @@ CREATE TABLE `assets` (
|
||||
`asset_vendor_id` int(11) NOT NULL DEFAULT 0,
|
||||
`asset_location_id` int(11) NOT NULL DEFAULT 0,
|
||||
`asset_contact_id` int(11) NOT NULL DEFAULT 0,
|
||||
`asset_network_id` int(11) NOT NULL DEFAULT 0,
|
||||
`asset_client_id` int(11) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`asset_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
@@ -274,6 +298,7 @@ CREATE TABLE `clients` (
|
||||
`client_currency_code` varchar(200) NOT NULL,
|
||||
`client_net_terms` int(10) NOT NULL,
|
||||
`client_tax_id_number` varchar(255) DEFAULT NULL,
|
||||
`client_abbreviation` varchar(10) DEFAULT NULL,
|
||||
`client_notes` text DEFAULT NULL,
|
||||
`client_created_at` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`client_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(),
|
||||
@@ -366,6 +391,20 @@ CREATE TABLE `contact_logins` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `contact_tags`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `contact_tags`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `contact_tags` (
|
||||
`contact_id` int(11) NOT NULL,
|
||||
`tag_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`contact_id`,`tag_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `contacts`
|
||||
--
|
||||
@@ -671,29 +710,6 @@ CREATE TABLE `history` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `interfaces`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `interfaces`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `interfaces` (
|
||||
`interface_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`interface_number` int(11) DEFAULT NULL,
|
||||
`interface_description` varchar(200) DEFAULT NULL,
|
||||
`interface_connected_asset` varchar(200) DEFAULT NULL,
|
||||
`interface_ip` varchar(200) DEFAULT NULL,
|
||||
`interface_created_at` datetime DEFAULT current_timestamp(),
|
||||
`interface_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(),
|
||||
`interface_archived_at` datetime DEFAULT NULL,
|
||||
`interface_connected_asset_id` int(11) NOT NULL DEFAULT 0,
|
||||
`interface_network_id` int(11) NOT NULL DEFAULT 0,
|
||||
`interface_asset_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`interface_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `invoice_items`
|
||||
--
|
||||
@@ -902,6 +918,55 @@ CREATE TABLE `notifications` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `patch_panel_ports`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `patch_panel_ports`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `patch_panel_ports` (
|
||||
`port_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`port_number` int(11) NOT NULL,
|
||||
`port_name` varchar(200) DEFAULT NULL,
|
||||
`port_description` text DEFAULT NULL,
|
||||
`port_type` varchar(200) DEFAULT NULL,
|
||||
`port_created_at` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`port_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(),
|
||||
`port_archived_at` datetime DEFAULT NULL,
|
||||
`port_asset_id` int(11) DEFAULT NULL,
|
||||
`port_patch_panel_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`port_id`),
|
||||
KEY `port_patch_panel_id` (`port_patch_panel_id`),
|
||||
CONSTRAINT `patch_panel_ports_ibfk_1` FOREIGN KEY (`port_patch_panel_id`) REFERENCES `patch_panels` (`patch_panel_id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `patch_panels`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `patch_panels`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `patch_panels` (
|
||||
`patch_panel_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`patch_panel_name` varchar(200) NOT NULL,
|
||||
`patch_panel_description` text DEFAULT NULL,
|
||||
`patch_panel_type` varchar(200) DEFAULT NULL,
|
||||
`patch_panel_ports` int(11) NOT NULL,
|
||||
`patch_panel_physical_location` varchar(200) DEFAULT NULL,
|
||||
`patch_panel_notes` text DEFAULT NULL,
|
||||
`patch_panel_created_at` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`patch_panel_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(),
|
||||
`patch_panel_archived_at` datetime DEFAULT NULL,
|
||||
`patch_panel_location_id` int(11) DEFAULT NULL,
|
||||
`patch_panel_rack_id` int(11) DEFAULT NULL,
|
||||
`patch_panel_client_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`patch_panel_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `payments`
|
||||
--
|
||||
@@ -1033,6 +1098,56 @@ CREATE TABLE `quotes` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `rack_units`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `rack_units`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `rack_units` (
|
||||
`unit_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`unit_start_number` int(11) NOT NULL,
|
||||
`unit_end_number` int(11) NOT NULL,
|
||||
`unit_device` varchar(200) DEFAULT NULL,
|
||||
`unit_created_at` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`unit_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(),
|
||||
`unit_archived_at` datetime DEFAULT NULL,
|
||||
`unit_asset_id` int(11) DEFAULT NULL,
|
||||
`unit_rack_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`unit_id`),
|
||||
KEY `unit_rack_id` (`unit_rack_id`),
|
||||
CONSTRAINT `rack_units_ibfk_1` FOREIGN KEY (`unit_rack_id`) REFERENCES `racks` (`rack_id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `racks`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `racks`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `racks` (
|
||||
`rack_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`rack_name` varchar(200) NOT NULL,
|
||||
`rack_description` text DEFAULT NULL,
|
||||
`rack_model` varchar(200) DEFAULT NULL,
|
||||
`rack_depth` varchar(50) DEFAULT NULL,
|
||||
`rack_type` varchar(50) DEFAULT NULL,
|
||||
`rack_units` int(11) NOT NULL,
|
||||
`rack_photo` varchar(200) DEFAULT NULL,
|
||||
`rack_physical_location` varchar(200) DEFAULT NULL,
|
||||
`rack_notes` text DEFAULT NULL,
|
||||
`rack_created_at` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`rack_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(),
|
||||
`rack_archived_at` datetime DEFAULT NULL,
|
||||
`rack_location_id` int(11) DEFAULT NULL,
|
||||
`rack_client_id` int(11) NOT NULL,
|
||||
PRIMARY KEY (`rack_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `records`
|
||||
--
|
||||
@@ -1386,6 +1501,7 @@ CREATE TABLE `settings` (
|
||||
`config_login_key_required` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`config_login_key_secret` varchar(255) DEFAULT NULL,
|
||||
`config_login_remember_me_expire` int(11) NOT NULL DEFAULT 3,
|
||||
`config_log_retention` int(11) NOT NULL DEFAULT 90,
|
||||
`config_module_enable_ticketing` tinyint(1) NOT NULL DEFAULT 1,
|
||||
`config_theme` varchar(200) DEFAULT 'blue',
|
||||
`config_telemetry` tinyint(1) DEFAULT 0,
|
||||
@@ -1952,4 +2068,4 @@ CREATE TABLE `vendors` (
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2024-05-31 16:45:46
|
||||
-- Dump completed on 2024-06-13 12:39:55
|
||||
|
||||
187
domains.php
Normal file
187
domains.php
Normal file
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
// Default Column Sortby Filter
|
||||
$sort = "domain_name";
|
||||
$order = "ASC";
|
||||
|
||||
require_once "inc_all_reports.php";
|
||||
|
||||
//Rebuild URL
|
||||
$url_query_strings_sort = http_build_query($get_copy);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS domains.*,
|
||||
clients.*,
|
||||
registrar.vendor_name AS registrar_name,
|
||||
dnshost.vendor_name AS dnshost_name,
|
||||
mailhost.vendor_name AS mailhost_name,
|
||||
webhost.vendor_name AS webhost_name
|
||||
FROM domains
|
||||
LEFT JOIN clients ON client_id = domain_client_id
|
||||
LEFT JOIN vendors AS registrar ON domains.domain_registrar = registrar.vendor_id
|
||||
LEFT JOIN vendors AS dnshost ON domains.domain_dnshost = dnshost.vendor_id
|
||||
LEFT JOIN vendors AS mailhost ON domains.domain_mailhost = mailhost.vendor_id
|
||||
LEFT JOIN vendors AS webhost ON domains.domain_webhost = webhost.vendor_id
|
||||
WHERE domain_archived_at IS NULL
|
||||
AND (domain_name LIKE '%$q%' OR domain_description LIKE '%$q%' OR registrar.vendor_name LIKE '%$q%' OR dnshost.vendor_name LIKE '%$q%' OR mailhost.vendor_name LIKE '%$q%' OR webhost.vendor_name LIKE '%$q%' OR client_name LIKE '%$q%')
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to");
|
||||
|
||||
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
?>
|
||||
|
||||
<div class="card card-dark">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title mt-1"><i class="fa fa-fw fa-globe mr-2"></i>Domain Management</h3>
|
||||
<div class="card-tools">
|
||||
<div class="btn-group">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<form autocomplete="off">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="input-group mb-3 mb-md-0">
|
||||
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search Domains">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-dark"><i class="fa fa-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="btn-group float-right">
|
||||
<div class="dropdown ml-2" id="bulkActionButton" hidden>
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-fw fa-layer-group mr-2"></i>Bulk Action (<span id="selectedCount">0</span>)
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<button class="dropdown-item text-danger text-bold"
|
||||
type="submit" form="bulkActions" name="bulk_delete_domains">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
<hr>
|
||||
<div class="table-responsive-sm">
|
||||
|
||||
<form id="bulkActions" action="post.php" method="post">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
|
||||
|
||||
<table class="table table-striped table-borderless table-hover">
|
||||
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
|
||||
<tr>
|
||||
<td class="pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)">
|
||||
</div>
|
||||
</td>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=domain_name&order=<?php echo $disp; ?>">Domain</a></th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=registrar_name&order=<?php echo $disp; ?>">Registrar</a></th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=webhost_name&order=<?php echo $disp; ?>">Web Host</a></th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=dnshost_name&order=<?php echo $disp; ?>">DNS Host</a></th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=mailhost_name&order=<?php echo $disp; ?>">Mail Host</a></th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=domain_expire&order=<?php echo $disp; ?>">Expires</a></th>
|
||||
<th><a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=client_name&order=<?php echo $disp; ?>">Client</a></th>
|
||||
<th class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$domain_id = intval($row['domain_id']);
|
||||
$domain_name = nullable_htmlentities($row['domain_name']);
|
||||
$domain_description = nullable_htmlentities($row['domain_description']);
|
||||
$domain_expire = nullable_htmlentities($row['domain_expire']);
|
||||
$domain_registrar_name = nullable_htmlentities($row['registrar_name']);
|
||||
if($domain_registrar_name) {
|
||||
$domain_registrar_name_display = $domain_registrar_name;
|
||||
} else {
|
||||
$domain_registrar_name_display = "-";
|
||||
}
|
||||
$domain_webhost_name = nullable_htmlentities($row['webhost_name']);
|
||||
$domain_dnshost_name = nullable_htmlentities($row['dnshost_name']);
|
||||
$domain_mailhost_name = nullable_htmlentities($row['mailhost_name']);
|
||||
$domain_created_at = nullable_htmlentities($row['domain_created_at']);
|
||||
// Add - if empty on the table
|
||||
$domain_registrar_name_display = $domain_registrar_name ? $domain_registrar_name : "-";
|
||||
$domain_webhost_name_display = $domain_webhost_name ? $domain_webhost_name : "-";
|
||||
$domain_dnshost_name_display = $domain_dnshost_name ? $domain_dnshost_name : "-";
|
||||
$domain_mailhost_name_display = $domain_mailhost_name ? $domain_mailhost_name : "-";
|
||||
$client_id = intval($row['client_id']);
|
||||
$client_name = nullable_htmlentities($row['client_name']);
|
||||
|
||||
?>
|
||||
<tr>
|
||||
<td class="pr-0">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input bulk-select" type="checkbox" name="domain_ids[]" value="<?php echo $domain_id ?>">
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a class="text-dark" href="#" data-toggle="modal" onclick="populateDomainEditModal(<?php echo $client_id, ",", $domain_id ?>)" data-target="#editDomainModal">
|
||||
<div class="media">
|
||||
<i class="fa fa-fw fa-2x fa-globe mr-3"></i>
|
||||
<div class="media-body">
|
||||
<div><?php echo $domain_name; ?></div>
|
||||
<div><small class="text-secondary"><?php echo $domain_description; ?></small></div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</td>
|
||||
<td><?php echo $domain_registrar_name_display; ?></td>
|
||||
<td><?php echo $domain_webhost_name_display; ?></td>
|
||||
<td><?php echo $domain_dnshost_name_display; ?></td>
|
||||
<td><?php echo $domain_mailhost_name_display; ?></td>
|
||||
<td><?php echo $domain_expire; ?></td>
|
||||
<td><?php echo $client_name; ?></td>
|
||||
<td>
|
||||
<div class="dropdown dropleft text-center">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-h"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<?php if ($session_user_role > 1) { ?>
|
||||
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_domain=<?php echo $domain_id; ?>">
|
||||
<i class="fas fa-fw fa-archive mr-2"></i>Archive
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php if ($session_user_role == 3) { ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_domain=<?php echo $domain_id; ?>">
|
||||
<i class="fas fa-fw fa-trash mr-2"></i>Delete
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<?php require_once "pagination.php";
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/domain_edit_modal.js"></script>
|
||||
<script src="js/bulk_actions.js"></script>
|
||||
|
||||
<?php require_once "footer.php";
|
||||
|
||||
83
expenses.php
83
expenses.php
@@ -13,6 +13,27 @@ if (isset($_GET['account']) & !empty($_GET['account'])) {
|
||||
} else {
|
||||
// Default - any
|
||||
$account_query = '';
|
||||
$account = '';
|
||||
}
|
||||
|
||||
// Vendor Filter
|
||||
if (isset($_GET['vendor']) & !empty($_GET['vendor'])) {
|
||||
$vendor_query = 'AND (vendor_id = ' . intval($_GET['vendor']) . ')';
|
||||
$vendor = intval($_GET['vendor']);
|
||||
} else {
|
||||
// Default - any
|
||||
$vendor_query = '';
|
||||
$vendor = '';
|
||||
}
|
||||
|
||||
// Category Filter
|
||||
if (isset($_GET['category']) & !empty($_GET['category'])) {
|
||||
$category_query = 'AND (category_id = ' . intval($_GET['category']) . ')';
|
||||
$category = intval($_GET['category']);
|
||||
} else {
|
||||
// Default - any
|
||||
$category_query = '';
|
||||
$category = '';
|
||||
}
|
||||
|
||||
//Rebuild URL
|
||||
@@ -27,6 +48,8 @@ $sql = mysqli_query(
|
||||
LEFT JOIN clients ON expense_client_id = client_id
|
||||
WHERE expense_vendor_id > 0
|
||||
AND DATE(expense_date) BETWEEN '$dtf' AND '$dtt'
|
||||
$vendor_query
|
||||
$category_query
|
||||
AND (vendor_name LIKE '%$q%' OR client_name LIKE '%$q%' OR category_name LIKE '%$q%' OR account_name LIKE '%$q%' OR expense_description LIKE '%$q%' OR expense_amount LIKE '%$q%')
|
||||
$account_query
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
@@ -40,7 +63,15 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fas fa-fw fa-shopping-cart mr-2"></i>Expenses</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addExpenseModal"><i class="fas fa-plus mr-2"></i>New Expense</button>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addExpenseModal"><i class="fas fa-plus mr-2"></i>New Expense</button>
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#exportExpensesModal">
|
||||
<i class="fa fa-fw fa-download mr-2"></i>Export
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -79,7 +110,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse mt-3 <?php if ($_GET['dtf'] || $_GET['canned_date'] !== "custom" || $_GET['account']) { echo "show"; } ?>" id="advancedFilter">
|
||||
<div class="collapse mt-3 <?php if (isset($_GET['dtf']) || $_GET['canned_date'] !== "custom" || isset($_GET['account']) || isset($_GET['vendor']) || isset($_GET['category'])) { echo "show"; } ?>" id="advancedFilter">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
@@ -109,6 +140,46 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<input onchange="this.form.submit()" type="date" class="form-control" name="dtt" max="2999-12-31" value="<?php echo nullable_htmlentities($dtt); ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<label>Vendor</label>
|
||||
<select class="form-control select2" name="vendor" onchange="this.form.submit()">
|
||||
<option value="" <?php if ($vendor == "") { echo "selected"; } ?>>- All Vendors -</option>
|
||||
|
||||
<?php
|
||||
$sql_vendors_filter = mysqli_query($mysqli, "SELECT * FROM vendors WHERE vendor_client_id = 0 AND vendor_template = 0 ORDER BY vendor_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_vendors_filter)) {
|
||||
$vendor_id = intval($row['vendor_id']);
|
||||
$vendor_name = nullable_htmlentities($row['vendor_name']);
|
||||
?>
|
||||
<option <?php if ($vendor == $vendor_id) { echo "selected"; } ?> value="<?php echo $vendor_id; ?>"><?php echo $vendor_name; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<label>Category</label>
|
||||
<select class="form-control select2" name="category" onchange="this.form.submit()">
|
||||
<option value="" <?php if ($category == "") { echo "selected"; } ?>>- All Categories -</option>
|
||||
|
||||
<?php
|
||||
$sql_categories_filter = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Expense' ORDER BY category_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_categories_filter)) {
|
||||
$category_id = intval($row['category_id']);
|
||||
$category_name = nullable_htmlentities($row['category_name']);
|
||||
?>
|
||||
<option <?php if ($category == $category_id) { echo "selected"; } ?> value="<?php echo $category_id; ?>"><?php echo $category_name; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<label>Account</label>
|
||||
@@ -129,11 +200,6 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="float-right">
|
||||
<button type="button" class="btn btn-default mt-4" data-toggle="modal" data-target="#exportExpensesModal"><i class="fa fa-fw fa-download mr-2"></i>Export</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -245,8 +311,6 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
require "expense_refund_modal.php";
|
||||
|
||||
require "expense_export_modal.php";
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -268,5 +332,6 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
<?php
|
||||
require_once "expense_add_modal.php";
|
||||
require_once "expense_export_modal.php";
|
||||
|
||||
require_once "footer.php";
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<script src="plugins/Show-Hide-Passwords-Bootstrap-4/bootstrap-show-password.min.js"></script>
|
||||
<script src="plugins/clipboardjs/clipboard.min.js"></script>
|
||||
<script src="js/keepalive.js"></script>
|
||||
<script src="plugins/DataTables/datatables.min.js"></script>
|
||||
|
||||
|
||||
<!-- AdminLTE App -->
|
||||
|
||||
172
functions.php
172
functions.php
@@ -380,36 +380,6 @@ function encryptLoginEntry($login_password_cleartext)
|
||||
return $iv . $ciphertext;
|
||||
}
|
||||
|
||||
// Get domain expiration date
|
||||
function getDomainExpirationDate($name)
|
||||
{
|
||||
|
||||
// Only run if we think the domain is valid
|
||||
if (!filter_var($name, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) {
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "http://lookup.itflow.org:8080/$name");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
$response = json_decode(curl_exec($ch), 1);
|
||||
|
||||
if ($response) {
|
||||
if (is_array($response['expiration_date'])) {
|
||||
$expiry = new DateTime($response['expiration_date'][1]);
|
||||
} elseif (isset($response['expiration_date'])) {
|
||||
$expiry = new DateTime($response['expiration_date']);
|
||||
} else {
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
return $expiry->format('Y-m-d');
|
||||
}
|
||||
|
||||
// Default return
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
// Get domain general info (whois + NS/A/MX records)
|
||||
function getDomainRecords($name)
|
||||
{
|
||||
@@ -1221,4 +1191,144 @@ function fetchUpdates() {
|
||||
|
||||
return $updates;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Get domain expiration date -- Remove in the future Replace with PHP function
|
||||
function getDomainExpirationDateOLD($name)
|
||||
{
|
||||
|
||||
// Only run if we think the domain is valid
|
||||
if (!filter_var($name, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) {
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, "http://lookup.itflow.org:8080/$name");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
$response = json_decode(curl_exec($ch), 1);
|
||||
|
||||
if ($response) {
|
||||
if (is_array($response['expiration_date'])) {
|
||||
$expiry = new DateTime($response['expiration_date'][1]);
|
||||
} elseif (isset($response['expiration_date'])) {
|
||||
$expiry = new DateTime($response['expiration_date']);
|
||||
} else {
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
return $expiry->format('Y-m-d');
|
||||
}
|
||||
|
||||
// Default return
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
function getDomainExpirationDate($domain) {
|
||||
// Execute the whois command
|
||||
$result = shell_exec("whois " . escapeshellarg($domain));
|
||||
if (!$result) {
|
||||
return null; // Return null if WHOIS query fails
|
||||
}
|
||||
|
||||
$expireDate = '';
|
||||
|
||||
// Regular expressions to match different date formats
|
||||
$patterns = [
|
||||
'/Expiration Date: (.+)/',
|
||||
'/Registry Expiry Date: (.+)/',
|
||||
'/expires: (.+)/',
|
||||
'/Expiry Date: (.+)/',
|
||||
'/renewal date: (.+)/',
|
||||
'/Expires On: (.+)/',
|
||||
'/paid-till: (.+)/',
|
||||
'/Expiration Time: (.+)/',
|
||||
'/\[Expires on\]\s+(.+)/',
|
||||
'/expire: (.+)/',
|
||||
'/validity: (.+)/',
|
||||
'/Expires on.*: (.+)/i',
|
||||
'/Expiry on.*: (.+)/i',
|
||||
'/renewal: (.+)/i',
|
||||
'/Expir\w+ Date: (.+)/i',
|
||||
'/Valid Until: (.+)/i',
|
||||
'/Valid until: (.+)/i',
|
||||
'/expire-date: (.+)/i',
|
||||
'/Expiration Date: (.+)/i',
|
||||
'/Registry Expiry Date: (.+)/i',
|
||||
'/Expire Date: (.+)/i',
|
||||
'/expiry: (.+)/i',
|
||||
'/expires: (.+)/i',
|
||||
'/Registry Expiry Date: (.+)/i',
|
||||
'/Expiration Time: (.+)/i',
|
||||
'/validity: (.+)/i',
|
||||
'/expires: (.+)/i',
|
||||
'/paid-till: (.+)/i',
|
||||
'/Expire Date: (.+)/i',
|
||||
'/Expiration Date: (.+)/i',
|
||||
'/expire: (.+)/i',
|
||||
'/expiry: (.+)/i',
|
||||
'/renewal date: (.+)/i',
|
||||
'/Expiration Date: (.+)/i',
|
||||
'/Expiration Time: (.+)/i',
|
||||
'/Expires: (.+)/i',
|
||||
];
|
||||
|
||||
// Known date formats
|
||||
$knownFormats = [
|
||||
"d-M-Y",
|
||||
"d-F-Y",
|
||||
"d-m-Y",
|
||||
"Y-m-d",
|
||||
"d.m.Y",
|
||||
"Y.m.d",
|
||||
"Y/m/d",
|
||||
"Y/m/d H:i:s",
|
||||
"Ymd",
|
||||
"Ymd H:i:s",
|
||||
"d/m/Y",
|
||||
"Y. m. d.",
|
||||
"Y.m.d H:i:s",
|
||||
"d-M-Y H:i:s",
|
||||
"D M d H:i:s T Y",
|
||||
"D M d Y",
|
||||
"Y-m-d\TH:i:s",
|
||||
"Y-m-d\TH:i:s\Z",
|
||||
"Y-m-d H:i:s\Z",
|
||||
"Y-m-d H:i:s",
|
||||
"d M Y H:i:s",
|
||||
"d/m/Y H:i:s",
|
||||
"d/m/Y H:i:s T",
|
||||
"B d Y",
|
||||
"d.m.Y H:i:s",
|
||||
"before M-Y",
|
||||
"before Y-m-d",
|
||||
"before Ymd",
|
||||
"Y-m-d H:i:s (\T\Z\Z)",
|
||||
"Y-M-d.",
|
||||
];
|
||||
|
||||
// Check each pattern to find a match
|
||||
foreach ($patterns as $pattern) {
|
||||
if (preg_match($pattern, $result, $matches)) {
|
||||
$expireDate = trim($matches[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($expireDate) {
|
||||
// Try parsing with known formats
|
||||
foreach ($knownFormats as $format) {
|
||||
$parsedDate = DateTime::createFromFormat($format, $expireDate);
|
||||
if ($parsedDate && $parsedDate->format($format) === $expireDate) {
|
||||
return $parsedDate->format('Y-m-d');
|
||||
}
|
||||
}
|
||||
|
||||
// If none of the formats matched, try to parse it directly
|
||||
$parsedDate = date_create($expireDate);
|
||||
if ($parsedDate) {
|
||||
return $parsedDate->format('Y-m-d');
|
||||
}
|
||||
}
|
||||
|
||||
return null; // Return null if expiration date is not found
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ $config_login_message = $row['config_login_message'];
|
||||
$config_login_key_required = $row['config_login_key_required'];
|
||||
$config_login_key_secret = $row['config_login_key_secret'];
|
||||
$config_login_remember_me_expire = intval($row['config_login_remember_me_expire']);
|
||||
$config_log_retention = intval($row['config_log_retention']);
|
||||
|
||||
// Locale
|
||||
$config_currency_format = "US_en";
|
||||
@@ -283,3 +284,11 @@ $start_page_select_array = array (
|
||||
'invoices.php' => 'Invoices'
|
||||
);
|
||||
|
||||
$rack_type_select_array = array(
|
||||
"Open Wall-Mount",
|
||||
"Enclosed Wall-Mount",
|
||||
"Open Floor-Standing",
|
||||
"Enclosed Floor-Standing",
|
||||
"Other"
|
||||
);
|
||||
|
||||
|
||||
@@ -121,8 +121,9 @@ if (isset($_GET['query'])) {
|
||||
LEFT JOIN contacts ON asset_contact_id = contact_id
|
||||
LEFT JOIN locations ON asset_location_id = location_id
|
||||
LEFT JOIN clients ON asset_client_id = client_id
|
||||
LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1
|
||||
WHERE asset_archived_at IS NULL
|
||||
AND (asset_name LIKE '%$query%' OR asset_description LIKE '%$query%' OR asset_type LIKE '%$query%' OR asset_make LIKE '%$query%' OR asset_model LIKE '%$query%' OR asset_serial LIKE '%$query%' OR asset_os LIKE '%$query%' OR asset_ip LIKE '%$query%' OR asset_nat_ip LIKE '%$query%' OR asset_mac LIKE '%$query%' OR asset_status LIKE '%$query%')
|
||||
AND (asset_name LIKE '%$query%' OR asset_description LIKE '%$query%' OR asset_type LIKE '%$query%' OR asset_make LIKE '%$query%' OR asset_model LIKE '%$query%' OR asset_serial LIKE '%$query%' OR asset_os LIKE '%$query%' OR interface_ip LIKE '%$query%' OR interface_nat_ip LIKE '%$query%' OR interface_mac LIKE '%$query%' OR asset_status LIKE '%$query%')
|
||||
$access_permission_query
|
||||
ORDER BY asset_name DESC LIMIT 5"
|
||||
);
|
||||
|
||||
@@ -38,6 +38,7 @@ header("X-Frame-Options: DENY");
|
||||
<link href="plugins/select2-bootstrap4-theme/select2-bootstrap4.min.css" rel="stylesheet" type="text/css">
|
||||
<link href='plugins/daterangepicker/daterangepicker.css' rel='stylesheet' />
|
||||
<link href="plugins/toastr/toastr.min.css" rel="stylesheet">
|
||||
<link href="plugins/DataTables/datatables.min.css" rel="stylesheet">
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="plugins/jquery/jquery.min.js"></script>
|
||||
|
||||
@@ -51,6 +51,7 @@ if (isset($_GET['client_id'])) {
|
||||
$client_net_terms = $config_default_net_terms;
|
||||
}
|
||||
$client_tax_id_number = nullable_htmlentities($row['client_tax_id_number']);
|
||||
$client_abbreviation = nullable_htmlentities($row['client_abbreviation']);
|
||||
$client_rate = floatval($row['client_rate']);
|
||||
$client_notes = nullable_htmlentities($row['client_notes']);
|
||||
$client_created_at = nullable_htmlentities($row['client_created_at']);
|
||||
@@ -155,6 +156,9 @@ if (isset($_GET['client_id'])) {
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('network_id') AS num FROM networks WHERE network_archived_at IS NULL AND network_client_id = $client_id"));
|
||||
$num_networks = $row['num'];
|
||||
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('rack_id') AS num FROM racks WHERE rack_archived_at IS NULL AND rack_client_id = $client_id"));
|
||||
$num_racks = $row['num'];
|
||||
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('domain_id') AS num FROM domains WHERE domain_archived_at IS NULL AND domain_client_id = $client_id"));
|
||||
$num_domains = $row['num'];
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ require_once "inc_wrapper.php";
|
||||
|
||||
require_once "inc_alert_feedback.php";
|
||||
|
||||
require_once "pagination_head.php";
|
||||
|
||||
// Set variable default values
|
||||
$largest_income_month = 0;
|
||||
|
||||
44
invoice_export_modal.php
Normal file
44
invoice_export_modal.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<div class="modal" id="exportInvoicesModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content bg-dark">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-download mr-2"></i>Export Invoices to CSV</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<div class="modal-body bg-white">
|
||||
|
||||
<?php require_once "inc_export_warning.php";
|
||||
?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Date From</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
|
||||
</div>
|
||||
<input type="date" class="form-control" name="date_from" max="2999-12-31" value="<?php echo nullable_htmlentities($dtf); ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Date To</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
|
||||
</div>
|
||||
<input type="date" class="form-control" name="date_to" max="2999-12-31" value="<?php echo nullable_htmlentities($dtt); ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer bg-white">
|
||||
<button type="submit" name="export_invoices_csv" class="btn btn-primary text-bold"><i class="fas fa-fw fa-download mr-2"></i>Download CSV</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
12
invoices.php
12
invoices.php
@@ -57,6 +57,7 @@ $real_overdue_amount = $total_overdue_amount - $total_overdue_partial_amount;
|
||||
$total_unpaid_amount = $total_sent_amount + $total_viewed_amount + $total_partial_amount;
|
||||
$unpaid_count = $sent_count + $viewed_count + $partial_count;
|
||||
|
||||
$overdue_query = '';
|
||||
//Invoice status from GET
|
||||
if (isset($_GET['status']) && ($_GET['status']) == 'Draft') {
|
||||
$status_query = "invoice_status = 'Draft'";
|
||||
@@ -137,7 +138,15 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
<div class="card-header py-2">
|
||||
<h3 class="card-title mt-2"><i class="fa fa-fw fa-file-invoice mr-2"></i>Invoices</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addInvoiceModal"><i class="fas fa-plus mr-2"></i>New Invoice</button>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addInvoiceModal"><i class="fas fa-plus mr-2"></i>New Invoice</button>
|
||||
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item text-dark" href="#" data-toggle="modal" data-target="#exportInvoicesModal">
|
||||
<i class="fa fa-fw fa-download mr-2"></i>Export
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -312,6 +321,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
|
||||
<?php
|
||||
require_once "invoice_add_modal.php";
|
||||
require_once "invoice_export_modal.php";
|
||||
|
||||
require_once "footer.php";
|
||||
|
||||
|
||||
@@ -166,3 +166,6 @@ clipboard.on('error', function(e) {
|
||||
$(function () {
|
||||
$('[data-toggle="popover"]').popover()
|
||||
});
|
||||
|
||||
// Data Tables
|
||||
new DataTable('.dataTables');
|
||||
556
legacy_cron_ticket_email_parser.php
Normal file
556
legacy_cron_ticket_email_parser.php
Normal file
@@ -0,0 +1,556 @@
|
||||
<?php
|
||||
/*
|
||||
* CRON - Email Parser
|
||||
* Process emails and create/update tickets
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- Process unregistered contacts/clients into an inbox to allow a ticket to be created/ignored
|
||||
- Support for authenticating with OAuth
|
||||
- Separate Mailbox Account for tickets 2022-12-14 - JQ
|
||||
|
||||
*/
|
||||
|
||||
// Set working directory to the directory this cron script lives at.
|
||||
chdir(dirname(__FILE__));
|
||||
|
||||
// Get ITFlow config & helper functions
|
||||
require_once "config.php";
|
||||
|
||||
// Set Timezone
|
||||
require_once "inc_set_timezone.php";
|
||||
|
||||
require_once "functions.php";
|
||||
|
||||
// Get settings for the "default" company
|
||||
require_once "get_settings.php";
|
||||
|
||||
$config_ticket_prefix = sanitizeInput($config_ticket_prefix);
|
||||
$config_ticket_from_name = sanitizeInput($config_ticket_from_name);
|
||||
|
||||
// Get company name & phone & timezone
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1");
|
||||
$row = mysqli_fetch_array($sql);
|
||||
$company_name = sanitizeInput($row['company_name']);
|
||||
$company_phone = sanitizeInput(formatPhoneNumber($row['company_phone']));
|
||||
|
||||
// Check setting enabled
|
||||
if ($config_ticket_email_parse == 0) {
|
||||
exit("Email Parser: Feature is not enabled - check Settings > Ticketing > Email-to-ticket parsing. See https://docs.itflow.org/ticket_email_parse -- Quitting..");
|
||||
}
|
||||
|
||||
$argv = $_SERVER['argv'];
|
||||
|
||||
// Check Cron Key
|
||||
if ( $argv[1] !== $config_cron_key ) {
|
||||
exit("Cron Key invalid -- Quitting..");
|
||||
}
|
||||
|
||||
// Check IMAP extension works/installed
|
||||
if (!function_exists('imap_open')) {
|
||||
exit("Email Parser: PHP IMAP extension is not installed. See https://docs.itflow.org/ticket_email_parse -- Quitting..");
|
||||
}
|
||||
|
||||
// Check mailparse extension works/installed
|
||||
if (!function_exists('mailparse_msg_parse_file')) {
|
||||
exit("Email Parser: PHP mailparse extension is not installed. See https://docs.itflow.org/ticket_email_parse -- Quitting..");
|
||||
}
|
||||
|
||||
// Get system temp directory
|
||||
$temp_dir = sys_get_temp_dir();
|
||||
|
||||
// Create the path for the lock file using the temp directory
|
||||
$lock_file_path = "{$temp_dir}/itflow_legacy_email_parser_{$installation_id}.lock";
|
||||
|
||||
// Check for lock file to prevent concurrent script runs
|
||||
if (file_exists($lock_file_path)) {
|
||||
$file_age = time() - filemtime($lock_file_path);
|
||||
|
||||
// If file is older than 3 minutes (180 seconds), delete and continue
|
||||
if ($file_age > 300) {
|
||||
unlink($lock_file_path);
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron-Email-Parser', log_action = 'Delete', log_description = 'Cron Email Parser detected a lock file was present but was over 10 minutes old so it removed it'");
|
||||
} else {
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Cron-Email-Parser', log_action = 'Locked', log_description = 'Cron Email Parser attempted to execute but was already executing, so instead it terminated.'");
|
||||
exit("Script is already running. Exiting.");
|
||||
}
|
||||
}
|
||||
|
||||
// Create a lock file
|
||||
file_put_contents($lock_file_path, "Locked");
|
||||
|
||||
// PHP Mail Parser
|
||||
use PhpMimeMailParser\Parser;
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Contracts/CharsetManager.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Contracts/Middleware.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Attachment.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Charset.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Exception.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Middleware.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/MiddlewareStack.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/MimePart.php";
|
||||
|
||||
require_once "plugins/php-mime-mail-parser/src/Parser.php";
|
||||
|
||||
|
||||
// Allowed attachment extensions
|
||||
$allowed_extensions = array('jpg', 'jpeg', 'gif', 'png', 'webp', 'pdf', 'txt', 'md', 'doc', 'docx', 'csv', 'xls', 'xlsx', 'xlsm', 'zip', 'tar', 'gz');
|
||||
|
||||
// Function to raise a new ticket for a given contact and email them confirmation (if configured)
|
||||
function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message, $attachments, $original_message_file) {
|
||||
|
||||
// Access global variables
|
||||
global $mysqli,$config_app_name, $company_name, $company_phone, $config_ticket_prefix, $config_ticket_client_general_notifications, $config_ticket_new_ticket_notification_email, $config_base_url, $config_ticket_from_name, $config_ticket_from_email, $config_smtp_host, $config_smtp_port, $config_smtp_encryption, $config_smtp_username, $config_smtp_password, $allowed_extensions;
|
||||
|
||||
// Get the next Ticket Number and add 1 for the new ticket number
|
||||
$ticket_number_sql = mysqli_fetch_array(mysqli_query($mysqli, "SELECT config_ticket_next_number FROM settings WHERE company_id = 1"));
|
||||
$ticket_number = intval($ticket_number_sql['config_ticket_next_number']);
|
||||
$new_config_ticket_next_number = $ticket_number + 1;
|
||||
mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1");
|
||||
|
||||
// Prep ticket details
|
||||
$message = nl2br($message);
|
||||
$message = mysqli_escape_string($mysqli, "<i>Email from: $contact_email at $date:-</i> <br><br>$message");
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$message', ticket_priority = 'Low', ticket_status = 1, ticket_created_by = 0, ticket_contact_id = $contact_id, ticket_client_id = $client_id");
|
||||
$id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Logging
|
||||
echo "Created new ticket.<br>";
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Create', log_description = 'Email parser: Client contact $contact_email created ticket $config_ticket_prefix$ticket_number ($subject) ($id)', log_client_id = $client_id");
|
||||
|
||||
// -- Process attachments (after ticket is logged as created because we save to the folder named after the ticket ID) --
|
||||
|
||||
mkdirMissing('uploads/tickets/'); // Create tickets dir
|
||||
|
||||
// Setup directory for this ticket ID
|
||||
$att_dir = "uploads/tickets/" . $id . "/";
|
||||
mkdirMissing($att_dir);
|
||||
|
||||
// Save original email message as ticket attachment
|
||||
rename("uploads/tmp/{$original_message_file}", "{$att_dir}/{$original_message_file}");
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = 'Original-parsed-email.eml', ticket_attachment_reference_name = '$original_message_file', ticket_attachment_ticket_id = $id");
|
||||
|
||||
// Process each attachment
|
||||
foreach ($attachments as $attachment) {
|
||||
|
||||
// Get name and extension
|
||||
$att_name = $attachment->getFileName();
|
||||
$att_extarr = explode('.', $att_name);
|
||||
$att_extension = strtolower(end($att_extarr));
|
||||
|
||||
// Check the extension is allowed
|
||||
if (in_array($att_extension, $allowed_extensions)) {
|
||||
|
||||
// Save attachment with a random name
|
||||
$att_saved_path = $attachment->save($att_dir, Parser::ATTACHMENT_RANDOM_FILENAME);
|
||||
|
||||
// Access the random name to add into the database (this won't work on Windows)
|
||||
$att_tmparr = explode($att_dir, $att_saved_path);
|
||||
|
||||
$ticket_attachment_name = sanitizeInput($att_name);
|
||||
$ticket_attachment_reference_name = sanitizeInput(end($att_tmparr));
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = '$ticket_attachment_name', ticket_attachment_reference_name = '$ticket_attachment_reference_name', ticket_attachment_ticket_id = $id");
|
||||
|
||||
} else {
|
||||
$ticket_attachment_name = sanitizeInput($att_name);
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Blocked attachment $ticket_attachment_name from Client contact $contact_email for ticket $config_ticket_prefix$ticket_number', log_client_id = $client_id");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$data = [];
|
||||
// E-mail client notification that ticket has been created
|
||||
if ($config_ticket_client_general_notifications == 1) {
|
||||
|
||||
$subject_email = "Ticket created - [$config_ticket_prefix$ticket_number] - $subject";
|
||||
$body = "<i style=\'color: #808080\'>##- Please type your reply above this line -##</i><br><br>Hello $contact_name,<br><br>Thank you for your email. A ticket regarding \"$subject\" has been automatically created for you.<br><br>Ticket: $config_ticket_prefix$ticket_number<br>Subject: $subject<br>Status: New<br>https://$config_base_url/portal/ticket.php?id=$id<br><br>--<br>$company_name - Support<br>$config_ticket_from_email<br>$company_phone";
|
||||
|
||||
$data[] = [
|
||||
'from' => $config_ticket_from_email,
|
||||
'from_name' => $config_ticket_from_name,
|
||||
'recipient' => $contact_email,
|
||||
'recipient_name' => $contact_name,
|
||||
'subject' => $subject_email,
|
||||
'body' => $body
|
||||
];
|
||||
}
|
||||
|
||||
// Notify agent DL of the new ticket, if populated with a valid email
|
||||
if ($config_ticket_new_ticket_notification_email) {
|
||||
|
||||
// Get client info
|
||||
$client_sql = mysqli_query($mysqli, "SELECT client_name FROM clients WHERE client_id = $client_id");
|
||||
$client_row = mysqli_fetch_array($client_sql);
|
||||
$client_name = sanitizeInput($client_row['client_name']);
|
||||
|
||||
$email_subject = "$config_app_name - New Ticket - $client_name: $subject";
|
||||
$email_body = "Hello, <br><br>This is a notification that a new ticket has been raised in ITFlow. <br>Client: $client_name<br>Priority: Low (email parsed)<br>Link: https://$config_base_url/ticket.php?ticket_id=$id <br><br>--------------------------------<br><br><b>$subject</b><br>$details";
|
||||
|
||||
$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($mysqli, $data);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
// End Add Ticket Function
|
||||
|
||||
// Add Reply Function
|
||||
function addReply($from_email, $date, $subject, $ticket_number, $message, $attachments) {
|
||||
// Add email as a comment/reply to an existing ticket
|
||||
|
||||
// Access global variables
|
||||
global $mysqli, $config_app_name, $company_name, $company_phone, $config_ticket_prefix, $config_base_url, $config_ticket_from_name, $config_ticket_from_email, $config_smtp_host, $config_smtp_port, $config_smtp_encryption, $config_smtp_username, $config_smtp_password, $allowed_extensions;
|
||||
|
||||
// Set default reply type
|
||||
$ticket_reply_type = 'Client';
|
||||
|
||||
// Capture just the latest/most recent email reply content
|
||||
// based off the "##- Please type your reply above this line -##" line that we prepend the outgoing emails with
|
||||
$message = explode("##- Please type your reply above this line -##", $message);
|
||||
$message = nl2br($message[0]);
|
||||
$message = mysqli_escape_string($mysqli, "<i>Email from: $from_email at $date:-</i> <br><br>$message");
|
||||
|
||||
// Lookup the ticket ID
|
||||
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT ticket_id, ticket_subject, ticket_status, ticket_contact_id, ticket_client_id, contact_email, client_name
|
||||
FROM tickets
|
||||
LEFT JOIN contacts on tickets.ticket_contact_id = contacts.contact_id
|
||||
LEFT JOIN clients on tickets.ticket_client_id = clients.client_id
|
||||
WHERE ticket_number = $ticket_number LIMIT 1"));
|
||||
|
||||
if ($row) {
|
||||
|
||||
// Get ticket details
|
||||
$ticket_id = intval($row['ticket_id']);
|
||||
$ticket_subject = sanitizeInput($row['ticket_subject']);
|
||||
$ticket_status = sanitizeInput($row['ticket_status']);
|
||||
$ticket_reply_contact = intval($row['ticket_contact_id']);
|
||||
$ticket_contact_email = sanitizeInput($row['contact_email']);
|
||||
$client_id = intval($row['ticket_client_id']);
|
||||
$client_name = sanitizeInput($row['client_name']);
|
||||
|
||||
// Check ticket isn't closed - tickets can't be re-opened
|
||||
if ($ticket_status == 5) {
|
||||
mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Ticket', notification = 'Email parser: $from_email attempted to re-open ticket $config_ticket_prefix$ticket_number (ID $ticket_id) - check inbox manually to see email', notification_action = 'ticket.php?ticket_id=$ticket_id', notification_client_id = $client_id");
|
||||
|
||||
$email_subject = "Action required: This ticket is already closed";
|
||||
$email_body = "Hi there, <br><br>You\'ve tried to reply to a ticket that is closed - we won\'t see your response. <br><br>Please raise a new ticket by sending a fresh e-mail to our support address below. <br><br>--<br>$company_name - Support<br>$config_ticket_from_email<br>$company_phone";
|
||||
|
||||
$data = [
|
||||
[
|
||||
'from' => $config_ticket_from_email,
|
||||
'from_name' => $config_ticket_from_name,
|
||||
'recipient' => $from_email,
|
||||
'recipient_name' => $from_email,
|
||||
'subject' => $email_subject,
|
||||
'body' => $email_body
|
||||
]
|
||||
];
|
||||
|
||||
addToMailQueue($mysqli, $data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check WHO replied (was it the owner of the ticket or someone else on CC?)
|
||||
if (empty($ticket_contact_email) || $ticket_contact_email !== $from_email) {
|
||||
|
||||
// It wasn't the contact currently assigned to the ticket, check if it's another registered contact for that client
|
||||
|
||||
$row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT contact_id FROM contacts WHERE contact_email = '$from_email' AND contact_client_id = $client_id LIMIT 1"));
|
||||
if ($row) {
|
||||
|
||||
// Contact is known - we can keep the reply type as client
|
||||
$ticket_reply_contact = intval($row['contact_id']);
|
||||
|
||||
} else {
|
||||
// Mark the reply as internal as we don't recognise the contact (so the actual contact doesn't see it, and the tech can edit/delete if needed)
|
||||
$ticket_reply_type = 'Internal';
|
||||
$ticket_reply_contact = '0';
|
||||
$message = "<b>WARNING: Contact email mismatch</b><br>$message"; // Add a warning at the start of the message - for the techs benefit (think phishing/scams)
|
||||
}
|
||||
}
|
||||
|
||||
// Add the comment
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = '$message', ticket_reply_type = '$ticket_reply_type', ticket_reply_time_worked = '00:00:00', ticket_reply_by = $ticket_reply_contact, ticket_reply_ticket_id = $ticket_id");
|
||||
|
||||
$reply_id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Process attachments
|
||||
mkdirMissing('uploads/tickets/');
|
||||
foreach ($attachments as $attachment) {
|
||||
|
||||
// Get name and extension
|
||||
$att_name = $attachment->getFileName();
|
||||
$att_extarr = explode('.', $att_name);
|
||||
$att_extension = strtolower(end($att_extarr));
|
||||
|
||||
// Check the extension is allowed
|
||||
if (in_array($att_extension, $allowed_extensions)) {
|
||||
|
||||
// Setup directory for this ticket ID
|
||||
$att_dir = "uploads/tickets/" . $ticket_id . "/";
|
||||
mkdirMissing($att_dir);
|
||||
|
||||
// Save attachment with a random name
|
||||
$att_saved_path = $attachment->save($att_dir, Parser::ATTACHMENT_RANDOM_FILENAME);
|
||||
|
||||
// Access the random name to add into the database (this won't work on Windows)
|
||||
$att_tmparr = explode($att_dir, $att_saved_path);
|
||||
|
||||
$ticket_attachment_name = sanitizeInput($att_name);
|
||||
$ticket_attachment_reference_name = sanitizeInput(end($att_tmparr));
|
||||
|
||||
mysqli_query($mysqli, "INSERT INTO ticket_attachments SET ticket_attachment_name = '$ticket_attachment_name', ticket_attachment_reference_name = '$ticket_attachment_reference_name', ticket_attachment_reply_id = $reply_id, ticket_attachment_ticket_id = $ticket_id");
|
||||
|
||||
} else {
|
||||
$ticket_attachment_name = sanitizeInput($att_name);
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Blocked attachment $ticket_attachment_name from Client contact $from_email for ticket $config_ticket_prefix$ticket_number', log_client_id = $client_id");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// E-mail techs assigned to the ticket to notify them of the reply
|
||||
$ticket_assigned_to = mysqli_query($mysqli, "SELECT ticket_assigned_to FROM tickets WHERE ticket_id = $ticket_id LIMIT 1");
|
||||
|
||||
if ($ticket_assigned_to) {
|
||||
|
||||
$row = mysqli_fetch_array($ticket_assigned_to);
|
||||
$ticket_assigned_to = intval($row['ticket_assigned_to']);
|
||||
|
||||
if ($ticket_assigned_to) {
|
||||
|
||||
// Get tech details
|
||||
$tech_sql = mysqli_query($mysqli, "SELECT user_email, user_name FROM users WHERE user_id = $ticket_assigned_to LIMIT 1");
|
||||
$tech_row = mysqli_fetch_array($tech_sql);
|
||||
$tech_email = sanitizeInput($tech_row['user_email']);
|
||||
$tech_name = sanitizeInput($tech_row['user_name']);
|
||||
|
||||
$email_subject = "$config_app_name - Ticket updated - [$config_ticket_prefix$ticket_number] $ticket_subject";
|
||||
$email_body = "Hello $tech_name,<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' => $email_subject,
|
||||
'body' => $email_body
|
||||
]
|
||||
];
|
||||
|
||||
addToMailQueue($mysqli, $data);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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 = $client_id LIMIT 1");
|
||||
|
||||
echo "Updated existing ticket.<br>";
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Client contact $from_email updated ticket $config_ticket_prefix$ticket_number ($subject)', log_client_id = $client_id");
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// Invalid ticket number
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// END ADD REPLY FUNCTION -------------------------------------------------
|
||||
|
||||
// Prepare connection string with encryption (TLS/SSL/<blank>)
|
||||
$imap_mailbox = "$config_imap_host:$config_imap_port/imap/$config_imap_encryption";
|
||||
|
||||
// Connect to host via IMAP
|
||||
$imap = imap_open("{{$imap_mailbox}}INBOX", $config_imap_username, $config_imap_password);
|
||||
|
||||
// Check connection
|
||||
if (!$imap) {
|
||||
// Logging
|
||||
//$extended_log_description = var_export(imap_errors(), true);
|
||||
// Remove the lock file
|
||||
unlink($lock_file_path);
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Mail', log_action = 'Error', log_description = 'Email parser: Failed to connect to IMAP. Details'");
|
||||
exit("Could not connect to IMAP");
|
||||
}
|
||||
|
||||
// Check for the ITFlow mailbox that we move messages to once processed
|
||||
$imap_folder = 'ITFlow';
|
||||
$list = imap_list($imap, "{{$imap_mailbox}}", "*");
|
||||
if (array_search("{{$imap_mailbox}}$imap_folder", $list) === false) {
|
||||
imap_createmailbox($imap, imap_utf7_encode("{{$imap_mailbox}}$imap_folder"));
|
||||
imap_subscribe($imap, imap_utf7_encode("{{$imap_mailbox}}$imap_folder"));
|
||||
}
|
||||
|
||||
// Search for unread ("UNSEEN") emails
|
||||
$emails = imap_search($imap, 'UNSEEN');
|
||||
|
||||
if ($emails) {
|
||||
|
||||
// Sort
|
||||
rsort($emails);
|
||||
|
||||
// Loop through each email
|
||||
foreach ($emails as $email) {
|
||||
|
||||
// Default false
|
||||
$email_processed = false;
|
||||
|
||||
// Save the original email (to be moved later)
|
||||
mkdirMissing('uploads/tmp/'); // Create tmp dir
|
||||
$original_message_file = "processed-eml-" . randomString(200) . ".eml";
|
||||
imap_savebody($imap, "uploads/tmp/{$original_message_file}", $email);
|
||||
|
||||
// Get details from message and invoke PHP Mime Mail Parser
|
||||
$msg_to_parse = imap_fetchheader($imap, $email, FT_PREFETCHTEXT) . imap_body($imap, $email, FT_PEEK);
|
||||
$parser = new PhpMimeMailParser\Parser();
|
||||
$parser->setText($msg_to_parse);
|
||||
|
||||
// Process message attributes
|
||||
|
||||
$from_array = $parser->getAddresses('from')[0];
|
||||
$from_name = sanitizeInput($from_array['display']);
|
||||
|
||||
// Handle blank 'From' emails
|
||||
$from_email = "itflow-guest@example.com";
|
||||
if (filter_var($from_array['address'], FILTER_VALIDATE_EMAIL)) {
|
||||
$from_email = sanitizeInput($from_array['address']);
|
||||
}
|
||||
|
||||
$from_domain = explode("@", $from_array['address']);
|
||||
$from_domain = sanitizeInput(end($from_domain));
|
||||
|
||||
$subject = sanitizeInput($parser->getHeader('subject'));
|
||||
$date = sanitizeInput($parser->getHeader('date'));
|
||||
$attachments = $parser->getAttachments();
|
||||
|
||||
// Get the message content
|
||||
// (first try HTML parsing, but switch to plain text if the email is empty/plain-text only)
|
||||
// $message = $parser->getMessageBody('htmlEmbedded');
|
||||
// if (empty($message)) {
|
||||
// echo "DEBUG: Switching to plain text parsing for this message ($subject)";
|
||||
// $message = $parser->getMessageBody('text');
|
||||
// }
|
||||
|
||||
// TODO: Default to getting HTML and fallback to plaintext, but HTML emails seem to break the forward/agent notifications
|
||||
|
||||
$message = $parser->getMessageBody('text');
|
||||
|
||||
// Check if we can identify a ticket number (in square brackets)
|
||||
if (preg_match("/\[$config_ticket_prefix\d+\]/", $subject, $ticket_number)) {
|
||||
|
||||
// Looks like there's a ticket number in the subject line (e.g. [TCK-091]
|
||||
// Process as a ticket reply
|
||||
|
||||
// Get the actual ticket number (without the brackets)
|
||||
preg_match('/\d+/', $ticket_number[0], $ticket_number);
|
||||
$ticket_number = intval($ticket_number[0]);
|
||||
|
||||
if (addReply($from_email, $date, $subject, $ticket_number, $message, $attachments)) {
|
||||
$email_processed = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Couldn't match this email to an existing ticket
|
||||
|
||||
// Check if we can match the sender to a pre-existing contact
|
||||
$any_contact_sql = mysqli_query($mysqli, "SELECT * FROM contacts WHERE contact_email = '$from_email' LIMIT 1");
|
||||
$row = mysqli_fetch_array($any_contact_sql);
|
||||
|
||||
if ($row) {
|
||||
// Sender exists as a contact
|
||||
$contact_name = sanitizeInput($row['contact_name']);
|
||||
$contact_id = intval($row['contact_id']);
|
||||
$contact_email = sanitizeInput($row['contact_email']);
|
||||
$client_id = intval($row['contact_client_id']);
|
||||
|
||||
if (addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message, $attachments, $original_message_file)) {
|
||||
$email_processed = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Couldn't match this email to an existing ticket or an existing client contact
|
||||
// Checking to see if the sender domain matches a client website
|
||||
|
||||
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM domains WHERE domain_name = '$from_domain' LIMIT 1"));
|
||||
|
||||
if ($row && $from_domain == $row['domain_name']) {
|
||||
|
||||
// We found a match - create a contact under this client and raise a ticket for them
|
||||
|
||||
// Client details
|
||||
$client_id = intval($row['domain_client_id']);
|
||||
|
||||
// Contact details
|
||||
$password = password_hash(randomString(), PASSWORD_DEFAULT);
|
||||
$contact_name = $from_name; // This was already Sanitized above
|
||||
$contact_email = $from_email; // This was already Sanitized above
|
||||
mysqli_query($mysqli, "INSERT INTO contacts SET contact_name = '$contact_name', contact_email = '$contact_email', contact_notes = 'Added automatically via email parsing.', contact_password_hash = '$password', contact_client_id = $client_id");
|
||||
$contact_id = mysqli_insert_id($mysqli);
|
||||
|
||||
// Logging for contact creation
|
||||
echo "Created new contact.<br>";
|
||||
mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Contact', log_action = 'Create', log_description = 'Email parser: created contact $contact_name', log_client_id = $client_id");
|
||||
|
||||
if (addTicket($contact_id, $contact_name, $contact_email, $client_id, $date, $subject, $message, $attachments, $original_message_file)) {
|
||||
$email_processed = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Couldn't match this email to an existing ticket, existing contact or an existing client via the "from" domain
|
||||
// In the future we might make a page where these can be nicely viewed / managed, but for now we'll just flag them in the Inbox as needing attention
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Deal with the message (move it if processed, flag it if not)
|
||||
if ($email_processed) {
|
||||
imap_setflag_full($imap, $email, "\\Seen");
|
||||
imap_mail_move($imap, $email, $imap_folder);
|
||||
} else {
|
||||
// Basically just flags all emails to be manually checked
|
||||
echo "Failed to process email - flagging for manual review.";
|
||||
imap_setflag_full($imap, $email, "\\Flagged");
|
||||
}
|
||||
|
||||
// Remove temp original message if still there
|
||||
if (file_exists("uploads/tmp/{$original_message_file}")) {
|
||||
unlink("uploads/tmp/{$original_message_file}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
imap_expunge($imap);
|
||||
imap_close($imap);
|
||||
|
||||
// Remove the lock file
|
||||
unlink($lock_file_path);
|
||||
@@ -230,7 +230,7 @@ if (isset($_POST['login'])) {
|
||||
// HTML code for the token input field
|
||||
$token_field = "
|
||||
<div class='input-group mb-3'>
|
||||
<input type='text' inputmode='numeric' pattern='[0-9]*' class='form-control' placeholder='Enter your 2FA code' name='current_code' required autofocus>
|
||||
<input type='text' inputmode='numeric' pattern='[0-9]*' maxlength='6' class='form-control' placeholder='Enter your 2FA code' name='current_code' required autofocus>
|
||||
<div class='input-group-append'>
|
||||
<div class='input-group-text'>
|
||||
<span class='fas fa-key'></span>
|
||||
|
||||
60
payments.php
60
payments.php
@@ -7,6 +7,26 @@ $order = "DESC";
|
||||
require_once "inc_all.php";
|
||||
|
||||
|
||||
// Payment Method Filter
|
||||
if (isset($_GET['method']) & !empty($_GET['method'])) {
|
||||
$payment_method_query = "AND (payment_method = '" . sanitizeInput($_GET['method']) . "')";
|
||||
$method = nullable_htmlentities($_GET['method']);
|
||||
} else {
|
||||
// Default - any
|
||||
$payment_method_query = '';
|
||||
$method = '';
|
||||
}
|
||||
|
||||
// Account Filter
|
||||
if (isset($_GET['account']) & !empty($_GET['account'])) {
|
||||
$account_query = 'AND (payment_account_id = ' . intval($_GET['account']) . ')';
|
||||
$account = intval($_GET['account']);
|
||||
} else {
|
||||
// Default - any
|
||||
$account_query = '';
|
||||
$account = '';
|
||||
}
|
||||
|
||||
//Rebuild URL
|
||||
$url_query_strings_sort = http_build_query($get_copy);
|
||||
|
||||
@@ -18,6 +38,8 @@ $sql = mysqli_query(
|
||||
LEFT JOIN accounts ON payment_account_id = account_id
|
||||
WHERE DATE(payment_date) BETWEEN '$dtf' AND '$dtt'
|
||||
AND (CONCAT(invoice_prefix,invoice_number) LIKE '%$q%' OR client_name LIKE '%$q%' OR account_name LIKE '%$q%' OR payment_method LIKE '%$q%' OR payment_reference LIKE '%$q%')
|
||||
$account_query
|
||||
$payment_method_query
|
||||
ORDER BY $sort $order LIMIT $record_from, $record_to"
|
||||
);
|
||||
|
||||
@@ -42,6 +64,44 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group">
|
||||
<select class="form-control select2" name="account" onchange="this.form.submit()">
|
||||
<option value="" <?php if ($account == "") { echo "selected"; } ?>>- All Accounts -</option>
|
||||
|
||||
<?php
|
||||
$sql_accounts_filter = mysqli_query($mysqli, "SELECT * FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql_accounts_filter)) {
|
||||
$account_id = intval($row['account_id']);
|
||||
$account_name = nullable_htmlentities($row['account_name']);
|
||||
?>
|
||||
<option <?php if ($account == $account_id) { echo "selected"; } ?> value="<?php echo $account_id; ?>"><?php echo $account_name; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<div class="form-group">
|
||||
<select class="form-control select2" name="method" onchange="this.form.submit()">
|
||||
<option value="" <?php if ($method == "") { echo "selected"; } ?>>- All Payment Methods -</option>
|
||||
|
||||
<?php
|
||||
$sql_payment_methods_filter = mysqli_query($mysqli, "SELECT DISTINCT payment_method FROM payments ORDER BY payment_method ASC");
|
||||
while ($row = mysqli_fetch_array($sql_payment_methods_filter)) {
|
||||
$payment_method = nullable_htmlentities($row['payment_method']);
|
||||
?>
|
||||
<option <?php if ($method == $payment_method) { echo "selected"; } ?>><?php echo $payment_method; ?></option>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse mt-3 <?php if (!empty($_GET['dtf']) || $_GET['canned_date'] !== "custom" ) { echo "show"; } ?>" id="advancedFilter">
|
||||
<div class="row">
|
||||
|
||||
501
plugins/DataTables/datatables.css
Normal file
501
plugins/DataTables/datatables.css
Normal file
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
* This combined file was created by the DataTables downloader builder:
|
||||
* https://datatables.net/download
|
||||
*
|
||||
* To rebuild or modify this file with the latest versions of the included
|
||||
* software please visit:
|
||||
* https://datatables.net/download/#bs4/dt-2.0.8
|
||||
*
|
||||
* Included libraries:
|
||||
* DataTables 2.0.8
|
||||
*/
|
||||
|
||||
@charset "UTF-8";
|
||||
:root {
|
||||
--dt-row-selected: 2, 117, 216;
|
||||
--dt-row-selected-text: 255, 255, 255;
|
||||
--dt-row-selected-link: 9, 10, 11;
|
||||
--dt-row-stripe: 0, 0, 0;
|
||||
--dt-row-hover: 0, 0, 0;
|
||||
--dt-column-ordering: 0, 0, 0;
|
||||
--dt-html-background: white;
|
||||
}
|
||||
:root.dark {
|
||||
--dt-html-background: rgb(33, 37, 41);
|
||||
}
|
||||
|
||||
table.dataTable td.dt-control {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
table.dataTable td.dt-control:before {
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
content: "";
|
||||
border-top: 5px solid transparent;
|
||||
border-left: 10px solid rgba(0, 0, 0, 0.5);
|
||||
border-bottom: 5px solid transparent;
|
||||
border-right: 0px solid transparent;
|
||||
}
|
||||
table.dataTable tr.dt-hasChild td.dt-control:before {
|
||||
border-top: 10px solid rgba(0, 0, 0, 0.5);
|
||||
border-left: 5px solid transparent;
|
||||
border-bottom: 0px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
}
|
||||
|
||||
html.dark table.dataTable td.dt-control:before,
|
||||
:root[data-bs-theme=dark] table.dataTable td.dt-control:before {
|
||||
border-left-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
html.dark table.dataTable tr.dt-hasChild td.dt-control:before,
|
||||
:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before {
|
||||
border-top-color: rgba(255, 255, 255, 0.5);
|
||||
border-left-color: transparent;
|
||||
}
|
||||
|
||||
div.dt-scroll-body thead tr,
|
||||
div.dt-scroll-body tfoot tr {
|
||||
height: 0;
|
||||
}
|
||||
div.dt-scroll-body thead tr th, div.dt-scroll-body thead tr td,
|
||||
div.dt-scroll-body tfoot tr th,
|
||||
div.dt-scroll-body tfoot tr td {
|
||||
height: 0 !important;
|
||||
padding-top: 0px !important;
|
||||
padding-bottom: 0px !important;
|
||||
border-top-width: 0px !important;
|
||||
border-bottom-width: 0px !important;
|
||||
}
|
||||
div.dt-scroll-body thead tr th div.dt-scroll-sizing, div.dt-scroll-body thead tr td div.dt-scroll-sizing,
|
||||
div.dt-scroll-body tfoot tr th div.dt-scroll-sizing,
|
||||
div.dt-scroll-body tfoot tr td div.dt-scroll-sizing {
|
||||
height: 0 !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
table.dataTable thead > tr > th:active,
|
||||
table.dataTable thead > tr > td:active {
|
||||
outline: none;
|
||||
}
|
||||
table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before,
|
||||
table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:before,
|
||||
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before {
|
||||
position: absolute;
|
||||
display: block;
|
||||
bottom: 50%;
|
||||
content: "▲";
|
||||
content: "▲"/"";
|
||||
}
|
||||
table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after,
|
||||
table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:after,
|
||||
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 50%;
|
||||
content: "▼";
|
||||
content: "▼"/"";
|
||||
}
|
||||
table.dataTable thead > tr > th.dt-orderable-asc, table.dataTable thead > tr > th.dt-orderable-desc, table.dataTable thead > tr > th.dt-ordering-asc, table.dataTable thead > tr > th.dt-ordering-desc,
|
||||
table.dataTable thead > tr > td.dt-orderable-asc,
|
||||
table.dataTable thead > tr > td.dt-orderable-desc,
|
||||
table.dataTable thead > tr > td.dt-ordering-asc,
|
||||
table.dataTable thead > tr > td.dt-ordering-desc {
|
||||
position: relative;
|
||||
padding-right: 30px;
|
||||
}
|
||||
table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order,
|
||||
table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order,
|
||||
table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order,
|
||||
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order,
|
||||
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 12px;
|
||||
}
|
||||
table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-asc span.dt-column-order:after, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:before, table.dataTable thead > tr > th.dt-orderable-desc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:after, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after,
|
||||
table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:before,
|
||||
table.dataTable thead > tr > td.dt-orderable-asc span.dt-column-order:after,
|
||||
table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:before,
|
||||
table.dataTable thead > tr > td.dt-orderable-desc span.dt-column-order:after,
|
||||
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before,
|
||||
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:after,
|
||||
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:before,
|
||||
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after {
|
||||
left: 0;
|
||||
opacity: 0.125;
|
||||
line-height: 9px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
table.dataTable thead > tr > th.dt-orderable-asc, table.dataTable thead > tr > th.dt-orderable-desc,
|
||||
table.dataTable thead > tr > td.dt-orderable-asc,
|
||||
table.dataTable thead > tr > td.dt-orderable-desc {
|
||||
cursor: pointer;
|
||||
}
|
||||
table.dataTable thead > tr > th.dt-orderable-asc:hover, table.dataTable thead > tr > th.dt-orderable-desc:hover,
|
||||
table.dataTable thead > tr > td.dt-orderable-asc:hover,
|
||||
table.dataTable thead > tr > td.dt-orderable-desc:hover {
|
||||
outline: 2px solid rgba(0, 0, 0, 0.05);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
table.dataTable thead > tr > th.dt-ordering-asc span.dt-column-order:before, table.dataTable thead > tr > th.dt-ordering-desc span.dt-column-order:after,
|
||||
table.dataTable thead > tr > td.dt-ordering-asc span.dt-column-order:before,
|
||||
table.dataTable thead > tr > td.dt-ordering-desc span.dt-column-order:after {
|
||||
opacity: 0.6;
|
||||
}
|
||||
table.dataTable thead > tr > th.sorting_desc_disabled span.dt-column-order:after, table.dataTable thead > tr > th.sorting_asc_disabled span.dt-column-order:before,
|
||||
table.dataTable thead > tr > td.sorting_desc_disabled span.dt-column-order:after,
|
||||
table.dataTable thead > tr > td.sorting_asc_disabled span.dt-column-order:before {
|
||||
display: none;
|
||||
}
|
||||
table.dataTable thead > tr > th:active,
|
||||
table.dataTable thead > tr > td:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
div.dt-scroll-body > table.dataTable > thead > tr > th,
|
||||
div.dt-scroll-body > table.dataTable > thead > tr > td {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:root.dark table.dataTable thead > tr > th.dt-orderable-asc:hover, :root.dark table.dataTable thead > tr > th.dt-orderable-desc:hover,
|
||||
:root.dark table.dataTable thead > tr > td.dt-orderable-asc:hover,
|
||||
:root.dark table.dataTable thead > tr > td.dt-orderable-desc:hover,
|
||||
:root[data-bs-theme=dark] table.dataTable thead > tr > th.dt-orderable-asc:hover,
|
||||
:root[data-bs-theme=dark] table.dataTable thead > tr > th.dt-orderable-desc:hover,
|
||||
:root[data-bs-theme=dark] table.dataTable thead > tr > td.dt-orderable-asc:hover,
|
||||
:root[data-bs-theme=dark] table.dataTable thead > tr > td.dt-orderable-desc:hover {
|
||||
outline: 2px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
div.dt-processing {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 200px;
|
||||
margin-left: -100px;
|
||||
margin-top: -22px;
|
||||
text-align: center;
|
||||
padding: 2px;
|
||||
z-index: 10;
|
||||
}
|
||||
div.dt-processing > div:last-child {
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 15px;
|
||||
margin: 1em auto;
|
||||
}
|
||||
div.dt-processing > div:last-child > div {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border-radius: 50%;
|
||||
background: rgb(2, 117, 216);
|
||||
background: rgb(var(--dt-row-selected));
|
||||
animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
||||
}
|
||||
div.dt-processing > div:last-child > div:nth-child(1) {
|
||||
left: 8px;
|
||||
animation: datatables-loader-1 0.6s infinite;
|
||||
}
|
||||
div.dt-processing > div:last-child > div:nth-child(2) {
|
||||
left: 8px;
|
||||
animation: datatables-loader-2 0.6s infinite;
|
||||
}
|
||||
div.dt-processing > div:last-child > div:nth-child(3) {
|
||||
left: 32px;
|
||||
animation: datatables-loader-2 0.6s infinite;
|
||||
}
|
||||
div.dt-processing > div:last-child > div:nth-child(4) {
|
||||
left: 56px;
|
||||
animation: datatables-loader-3 0.6s infinite;
|
||||
}
|
||||
|
||||
@keyframes datatables-loader-1 {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes datatables-loader-3 {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
@keyframes datatables-loader-2 {
|
||||
0% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translate(24px, 0);
|
||||
}
|
||||
}
|
||||
table.dataTable.nowrap th, table.dataTable.nowrap td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
table.dataTable th,
|
||||
table.dataTable td {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
table.dataTable th.dt-left,
|
||||
table.dataTable td.dt-left {
|
||||
text-align: left;
|
||||
}
|
||||
table.dataTable th.dt-center,
|
||||
table.dataTable td.dt-center {
|
||||
text-align: center;
|
||||
}
|
||||
table.dataTable th.dt-right,
|
||||
table.dataTable td.dt-right {
|
||||
text-align: right;
|
||||
}
|
||||
table.dataTable th.dt-justify,
|
||||
table.dataTable td.dt-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
table.dataTable th.dt-nowrap,
|
||||
table.dataTable td.dt-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
table.dataTable th.dt-empty,
|
||||
table.dataTable td.dt-empty {
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.dataTable th.dt-type-numeric, table.dataTable th.dt-type-date,
|
||||
table.dataTable td.dt-type-numeric,
|
||||
table.dataTable td.dt-type-date {
|
||||
text-align: right;
|
||||
}
|
||||
table.dataTable thead th,
|
||||
table.dataTable thead td,
|
||||
table.dataTable tfoot th,
|
||||
table.dataTable tfoot td {
|
||||
text-align: left;
|
||||
}
|
||||
table.dataTable thead th.dt-head-left,
|
||||
table.dataTable thead td.dt-head-left,
|
||||
table.dataTable tfoot th.dt-head-left,
|
||||
table.dataTable tfoot td.dt-head-left {
|
||||
text-align: left;
|
||||
}
|
||||
table.dataTable thead th.dt-head-center,
|
||||
table.dataTable thead td.dt-head-center,
|
||||
table.dataTable tfoot th.dt-head-center,
|
||||
table.dataTable tfoot td.dt-head-center {
|
||||
text-align: center;
|
||||
}
|
||||
table.dataTable thead th.dt-head-right,
|
||||
table.dataTable thead td.dt-head-right,
|
||||
table.dataTable tfoot th.dt-head-right,
|
||||
table.dataTable tfoot td.dt-head-right {
|
||||
text-align: right;
|
||||
}
|
||||
table.dataTable thead th.dt-head-justify,
|
||||
table.dataTable thead td.dt-head-justify,
|
||||
table.dataTable tfoot th.dt-head-justify,
|
||||
table.dataTable tfoot td.dt-head-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
table.dataTable thead th.dt-head-nowrap,
|
||||
table.dataTable thead td.dt-head-nowrap,
|
||||
table.dataTable tfoot th.dt-head-nowrap,
|
||||
table.dataTable tfoot td.dt-head-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
table.dataTable tbody th.dt-body-left,
|
||||
table.dataTable tbody td.dt-body-left {
|
||||
text-align: left;
|
||||
}
|
||||
table.dataTable tbody th.dt-body-center,
|
||||
table.dataTable tbody td.dt-body-center {
|
||||
text-align: center;
|
||||
}
|
||||
table.dataTable tbody th.dt-body-right,
|
||||
table.dataTable tbody td.dt-body-right {
|
||||
text-align: right;
|
||||
}
|
||||
table.dataTable tbody th.dt-body-justify,
|
||||
table.dataTable tbody td.dt-body-justify {
|
||||
text-align: justify;
|
||||
}
|
||||
table.dataTable tbody th.dt-body-nowrap,
|
||||
table.dataTable tbody td.dt-body-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.dataTable.table {
|
||||
clear: both;
|
||||
max-width: none;
|
||||
border-spacing: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
table.dataTable.table.table-striped > tbody > tr:nth-of-type(2n+1) {
|
||||
background-color: transparent;
|
||||
}
|
||||
table.dataTable.table > tbody > tr {
|
||||
background-color: transparent;
|
||||
}
|
||||
table.dataTable.table > tbody > tr.selected > * {
|
||||
box-shadow: inset 0 0 0 9999px rgb(2, 117, 216);
|
||||
box-shadow: inset 0 0 0 9999px rgb(var(--dt-row-selected));
|
||||
color: rgb(255, 255, 255);
|
||||
color: rgb(var(--dt-row-selected-text));
|
||||
}
|
||||
table.dataTable.table > tbody > tr.selected a {
|
||||
color: rgb(9, 10, 11);
|
||||
color: rgb(var(--dt-row-selected-link));
|
||||
}
|
||||
table.dataTable.table.table-striped > tbody > tr:nth-of-type(2n+1) > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05);
|
||||
}
|
||||
table.dataTable.table.table-striped > tbody > tr:nth-of-type(2n+1).selected > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(2, 117, 216, 0.95);
|
||||
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95);
|
||||
}
|
||||
table.dataTable.table.table-hover > tbody > tr:hover > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075);
|
||||
}
|
||||
table.dataTable.table.table-hover > tbody > tr.selected:hover > * {
|
||||
box-shadow: inset 0 0 0 9999px rgba(2, 117, 216, 0.975);
|
||||
box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975);
|
||||
}
|
||||
|
||||
div.dt-container div.row {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
div.dt-container div.row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.dt-container div.dt-length label {
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.dt-container div.dt-length select {
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
div.dt-container div.dt-search label {
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.dt-container div.dt-search input {
|
||||
margin-left: 0.5em;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
}
|
||||
div.dt-container div.dt-info {
|
||||
padding-top: 0.85em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
div.dt-container div.dt-paging {
|
||||
margin: 0;
|
||||
}
|
||||
div.dt-container div.dt-paging ul.pagination {
|
||||
margin: 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
div.dt-container div.dt-processing {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 200px;
|
||||
margin-left: -100px;
|
||||
margin-top: -26px;
|
||||
text-align: center;
|
||||
padding: 1em 0;
|
||||
}
|
||||
div.dt-container div.dt-scroll-body {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
div.dt-container div.dt-scroll-body table,
|
||||
div.dt-container div.dt-scroll-body tbody > tr:last-child > * {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
div.dt-scroll-head table.dataTable {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
div.dt-scroll-body > table {
|
||||
border-top: none;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
div.dt-scroll-body > table thead .dt-orderable-asc:before,
|
||||
div.dt-scroll-body > table thead .dt-orderable-desc:after {
|
||||
display: none;
|
||||
}
|
||||
div.dt-scroll-body > table > tbody tr:first-child th,
|
||||
div.dt-scroll-body > table > tbody tr:first-child td {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
div.dt-scroll-foot > .dt-scroll-footInner {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
div.dt-scroll-foot > .dt-scroll-footInner > table {
|
||||
margin-top: 0 !important;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
div.dt-container div.dt-length,
|
||||
div.dt-container div.dt-search,
|
||||
div.dt-container div.dt-info,
|
||||
div.dt-container div.dt-paging {
|
||||
text-align: center;
|
||||
}
|
||||
div.dt-container div.row {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.dt-container div.row > * {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
div.dt-container div.dt-paging ul.pagination {
|
||||
justify-content: center !important;
|
||||
}
|
||||
}
|
||||
table.dataTable.table-sm > thead > tr th.dt-orderable-asc, table.dataTable.table-sm > thead > tr th.dt-orderable-desc, table.dataTable.table-sm > thead > tr th.dt-ordering-asc, table.dataTable.table-sm > thead > tr th.dt-ordering-desc,
|
||||
table.dataTable.table-sm > thead > tr td.dt-orderable-asc,
|
||||
table.dataTable.table-sm > thead > tr td.dt-orderable-desc,
|
||||
table.dataTable.table-sm > thead > tr td.dt-ordering-asc,
|
||||
table.dataTable.table-sm > thead > tr td.dt-ordering-desc {
|
||||
padding-right: 20px;
|
||||
}
|
||||
table.dataTable.table-sm > thead > tr th.dt-orderable-asc span.dt-column-order, table.dataTable.table-sm > thead > tr th.dt-orderable-desc span.dt-column-order, table.dataTable.table-sm > thead > tr th.dt-ordering-asc span.dt-column-order, table.dataTable.table-sm > thead > tr th.dt-ordering-desc span.dt-column-order,
|
||||
table.dataTable.table-sm > thead > tr td.dt-orderable-asc span.dt-column-order,
|
||||
table.dataTable.table-sm > thead > tr td.dt-orderable-desc span.dt-column-order,
|
||||
table.dataTable.table-sm > thead > tr td.dt-ordering-asc span.dt-column-order,
|
||||
table.dataTable.table-sm > thead > tr td.dt-ordering-desc span.dt-column-order {
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
div.dt-scroll-head table.table-bordered {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
div.table-responsive > div.dt-container > div.row {
|
||||
margin: 0;
|
||||
}
|
||||
div.table-responsive > div.dt-container > div.row > div[class^=col-]:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
div.table-responsive > div.dt-container > div.row > div[class^=col-]:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
|
||||
13334
plugins/DataTables/datatables.js
Normal file
13334
plugins/DataTables/datatables.js
Normal file
File diff suppressed because it is too large
Load Diff
15
plugins/DataTables/datatables.min.css
vendored
Normal file
15
plugins/DataTables/datatables.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
22
plugins/DataTables/datatables.min.js
vendored
Normal file
22
plugins/DataTables/datatables.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
plugins/php-imap/.github/FUNDING.yml
vendored
Normal file
2
plugins/php-imap/.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
ko_fi: webklex
|
||||
custom: ['https://www.buymeacoffee.com/webklex']
|
||||
32
plugins/php-imap/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
32
plugins/php-imap/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Used config**
|
||||
Please provide the used config, if you are not using the package default config.
|
||||
|
||||
**Code to Reproduce**
|
||||
The troubling code section which produces the reported bug.
|
||||
```php
|
||||
echo "Bug";
|
||||
```
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop / Server (please complete the following information):**
|
||||
- OS: [e.g. Debian 10]
|
||||
- PHP: [e.g. 5.5.9]
|
||||
- Version [e.g. v2.3.1]
|
||||
- Provider [e.g. Gmail, Outlook, Dovecot]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
17
plugins/php-imap/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
plugins/php-imap/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
12
plugins/php-imap/.github/ISSUE_TEMPLATE/general-help-request.md
vendored
Normal file
12
plugins/php-imap/.github/ISSUE_TEMPLATE/general-help-request.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
name: General help request
|
||||
about: Feel free to ask about any project related stuff
|
||||
|
||||
---
|
||||
|
||||
Please be aware that these issues will be closed if inactive for more then 14 days.
|
||||
|
||||
Also make sure to use https://github.com/Webklex/php-imap/issues/new?template=bug_report.md if you want to report a bug
|
||||
or https://github.com/Webklex/php-imap/issues/new?template=feature_request.md if you want to suggest a feature.
|
||||
|
||||
Still here? Well clean this out and go ahead :)
|
||||
23
plugins/php-imap/.github/docker/Dockerfile
vendored
Normal file
23
plugins/php-imap/.github/docker/Dockerfile
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
FROM ubuntu:latest
|
||||
LABEL maintainer="Webklex <github@webklex.com>"
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get upgrade -y
|
||||
RUN apt-get install -y sudo dovecot-imapd
|
||||
|
||||
ENV LIVE_MAILBOX=true
|
||||
ENV LIVE_MAILBOX_HOST=mail.example.local
|
||||
ENV LIVE_MAILBOX_PORT=993
|
||||
ENV LIVE_MAILBOX_ENCRYPTION=ssl
|
||||
ENV LIVE_MAILBOX_VALIDATE_CERT=true
|
||||
ENV LIVE_MAILBOX_USERNAME=root@example.local
|
||||
ENV LIVE_MAILBOX_PASSWORD=foobar
|
||||
ENV LIVE_MAILBOX_QUOTA_SUPPORT=true
|
||||
|
||||
EXPOSE 993
|
||||
|
||||
ADD dovecot_setup.sh /root/dovecot_setup.sh
|
||||
RUN chmod +x /root/dovecot_setup.sh
|
||||
|
||||
CMD ["/bin/bash", "-c", "/root/dovecot_setup.sh && tail -f /dev/null"]
|
||||
|
||||
63
plugins/php-imap/.github/docker/dovecot_setup.sh
vendored
Normal file
63
plugins/php-imap/.github/docker/dovecot_setup.sh
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
sudo apt-get -q update
|
||||
sudo apt-get -q -y install dovecot-imapd
|
||||
|
||||
{
|
||||
echo "127.0.0.1 $LIVE_MAILBOX_HOST"
|
||||
} | sudo tee -a /etc/hosts
|
||||
|
||||
SSL_CERT="/etc/ssl/certs/dovecot.crt"
|
||||
SSL_KEY="/etc/ssl/private/dovecot.key"
|
||||
|
||||
sudo openssl req -new -x509 -days 3 -nodes \
|
||||
-out "$SSL_CERT" \
|
||||
-keyout "$SSL_KEY" \
|
||||
-subj "/C=EU/ST=Europe/L=Home/O=Webklex/OU=Webklex DEV/CN=""$LIVE_MAILBOX_HOST"
|
||||
|
||||
sudo chown root:dovecot "$SSL_CERT" "$SSL_KEY"
|
||||
sudo chmod 0440 "$SSL_CERT"
|
||||
sudo chmod 0400 "$SSL_KEY"
|
||||
|
||||
DOVECOT_CONF="/etc/dovecot/local.conf"
|
||||
MAIL_CONF="/etc/dovecot/conf.d/10-mail.conf"
|
||||
IMAP_CONF="/etc/dovecot/conf.d/20-imap.conf"
|
||||
QUOTA_CONF="/etc/dovecot/conf.d/90-quota.conf"
|
||||
sudo touch "$DOVECOT_CONF" "$MAIL_CONF" "$IMAP_CONF" "$QUOTA_CONF"
|
||||
sudo chown root:dovecot "$DOVECOT_CONF" "$MAIL_CONF" "$IMAP_CONF" "$QUOTA_CONF"
|
||||
sudo chmod 0640 "$DOVECOT_CONF" "$MAIL_CONF" "$IMAP_CONF" "$QUOTA_CONF"
|
||||
|
||||
{
|
||||
echo "ssl = required"
|
||||
echo "disable_plaintext_auth = yes"
|
||||
echo "ssl_cert = <""$SSL_CERT"
|
||||
echo "ssl_key = <""$SSL_KEY"
|
||||
echo "ssl_protocols = !SSLv2 !SSLv3"
|
||||
echo "ssl_cipher_list = AES128+EECDH:AES128+EDH"
|
||||
} | sudo tee -a "$DOVECOT_CONF"
|
||||
|
||||
{
|
||||
echo "mail_plugins = \$mail_plugins quota"
|
||||
} | sudo tee -a "$MAIL_CONF"
|
||||
|
||||
{
|
||||
echo "protocol imap {"
|
||||
echo " mail_plugins = \$mail_plugins imap_quota"
|
||||
echo "}"
|
||||
} | sudo tee -a "$IMAP_CONF"
|
||||
|
||||
{
|
||||
echo "plugin {"
|
||||
echo " quota = maildir:User quota"
|
||||
echo " quota_rule = *:storage=1G"
|
||||
echo "}"
|
||||
} | sudo tee -a "$QUOTA_CONF"
|
||||
|
||||
sudo useradd --create-home --shell /bin/false "$LIVE_MAILBOX_USERNAME"
|
||||
echo "$LIVE_MAILBOX_USERNAME"":""$LIVE_MAILBOX_PASSWORD" | sudo chpasswd
|
||||
|
||||
sudo service dovecot restart
|
||||
|
||||
sudo doveadm auth test -x service=imap "$LIVE_MAILBOX_USERNAME" "$LIVE_MAILBOX_PASSWORD"
|
||||
50
plugins/php-imap/.github/workflows/tests.yaml
vendored
Normal file
50
plugins/php-imap/.github/workflows/tests.yaml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
phpunit:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
php: ['8.0', 8.1, 8.2]
|
||||
|
||||
name: PHP ${{ matrix.php }}
|
||||
|
||||
env:
|
||||
LIVE_MAILBOX: true
|
||||
LIVE_MAILBOX_DEBUG: true
|
||||
LIVE_MAILBOX_HOST: mail.example.local
|
||||
LIVE_MAILBOX_PORT: 993
|
||||
LIVE_MAILBOX_USERNAME: root@example.local
|
||||
LIVE_MAILBOX_ENCRYPTION: ssl
|
||||
LIVE_MAILBOX_PASSWORD: foobar
|
||||
LIVE_MAILBOX_QUOTA_SUPPORT: true
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: openssl, json, mbstring, iconv, fileinfo, libxml, zip
|
||||
coverage: none
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --prefer-dist --no-interaction --no-progress
|
||||
|
||||
- run: "sh .github/docker/dovecot_setup.sh"
|
||||
|
||||
- name: Execute tests
|
||||
run: vendor/bin/phpunit
|
||||
6
plugins/php-imap/.gitignore
vendored
Normal file
6
plugins/php-imap/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
composer.lock
|
||||
.idea
|
||||
/build/
|
||||
test.php
|
||||
.phpunit.result.cache
|
||||
phpunit.xml
|
||||
911
plugins/php-imap/CHANGELOG.md
Executable file
911
plugins/php-imap/CHANGELOG.md
Executable file
@@ -0,0 +1,911 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to `webklex/php-imap` will be documented in this file.
|
||||
|
||||
Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
|
||||
|
||||
## [UNRELEASED]
|
||||
### Fixed
|
||||
- Fixed date issue if timezone is UT and a 2 digit year #429 (thanks @ferrisbuellers)
|
||||
- Make the space optional after a comma separator #437 (thanks @marc0adam)
|
||||
- Fix bug when multipart message getHTMLBody() method returns null #455 (thanks @michalkortas)
|
||||
- Fix: Improve return type hints and return docblocks for query classes #470 (thanks @olliescase)
|
||||
- Fix - Query - Chunked - Resolved infinite loop when start chunk > 1 #477 (thanks @NeekTheNook)
|
||||
|
||||
### Added
|
||||
- IMAP STATUS command support added `Folder::status()` #424 (thanks @InterLinked1)
|
||||
- Add attributes and special flags #428 (thanks @sazanof)
|
||||
- Better connection check for IMAP #449 (thanks @thin-k-design)
|
||||
- Config handling moved into a new class `Config::class` to allow class serialization (sponsored by elb-BIT GmbH)
|
||||
- Support for Carbon 3 added #483
|
||||
|
||||
### Breaking changes
|
||||
- `Folder::getStatus()` no longer returns the results of `EXAMINE` but `STATUS` instead. If you want to use `EXAMINE` you can use the `Folder::examine()` method instead.
|
||||
- `ClientManager::class` has now longer access to all configs. Config handling has been moved to its own class `Config::class`. If you want to access the config you can use the retriever method `::getConfig()` instead. Example: `$client->getConfig()` or `$message->getConfig()`, etc.
|
||||
- `ClientManager::get` isn't available anymore. Use the regular config accessor instead. Example: `$cm->getConfig()`
|
||||
- `M̀essage::getConfig()` now returns the client configuration instead of the fetching options configuration. Please use `$message->getOptions()` instead.
|
||||
- `Attachment::getConfig()` now returns the client configuration instead of the fetching options configuration. Please use `$attachment->getOptions()` instead.
|
||||
- `Header::getConfig()` now returns the client configuration instead of the fetching options configuration. Please use `$header->getOptions()` instead.
|
||||
- `M̀essage::setConfig` now expects the client configuration instead of the fetching options configuration. Please use `$message->setOptions` instead.
|
||||
- `Attachment::setConfig` now expects the client configuration instead of the fetching options configuration. Please use `$attachment->setOptions` instead.
|
||||
- `Header::setConfig` now expects the client configuration instead of the fetching options configuration. Please use `$header->setOptions` instead.
|
||||
- All protocol constructors now require a `Config::class` instance
|
||||
- The `Client::class` constructors now require a `Config::class` instance
|
||||
- The `Part::class` constructors now require a `Config::class` instance
|
||||
- The `Header::class` constructors now require a `Config::class` instance
|
||||
- The `Message::fromFile` method now requires a `Config::class` instance
|
||||
- The `Message::fromString` method now requires a `Config::class` instance
|
||||
- The `Message::boot` method now requires a `Config::class` instance
|
||||
|
||||
## [5.5.0] - 2023-06-28
|
||||
### Fixed
|
||||
- Error token length mismatch in `ImapProtocol::readResponse` #400
|
||||
- Attachment name parsing fixed #410 #421 (thanks @nuernbergerA)
|
||||
- Additional Attachment name fallback added to prevent missing attachments
|
||||
- Attachment id is now static (based on the raw part content) instead of random
|
||||
- Always parse the attachment description if it is available
|
||||
|
||||
### Added
|
||||
- Attachment content hash added
|
||||
|
||||
|
||||
## [5.4.0] - 2023-06-24
|
||||
### Fixed
|
||||
- Legacy protocol support fixed (object to array conversion) #411
|
||||
- Header value decoding improved #410
|
||||
- Protocol exception handling improved (bad response message added) #408
|
||||
- Prevent fetching singular rfc partials from running indefinitely #407
|
||||
- Subject with colon ";" is truncated #401
|
||||
- Catching and handling iconv decoding exception #397
|
||||
|
||||
### Added
|
||||
- Additional timestamp formats added #198 #392 (thanks @esk-ap)
|
||||
|
||||
|
||||
## [5.3.0] - Security patch - 2023-06-20
|
||||
### Fixed
|
||||
- Potential RCE through path traversal fixed #414 (special thanks @angelej)
|
||||
|
||||
### Security Impact and Mitigation
|
||||
Impacted are all versions below v5.3.0.
|
||||
If possible, update to >= v5.3.0 as soon as possible. Impacted was the `Attachment::save`
|
||||
method which could be used to write files to the local filesystem. The path was not
|
||||
properly sanitized and could be used to write files to arbitrary locations.
|
||||
|
||||
However, the `Attachment::save` method is not used by default and has to be called
|
||||
manually. If you are using this method without providing a sanitized path, you are
|
||||
affected by this vulnerability.
|
||||
If you are not using this method or are providing a sanitized path, you are not affected
|
||||
by this vulnerability and no immediate action is required.
|
||||
|
||||
If you have any questions, please feel welcome to join this issue: https://github.com/Webklex/php-imap/issues/416
|
||||
#### Timeline
|
||||
- 17.06.23 21:30: Vulnerability reported
|
||||
- 18.06.23 19:14: Vulnerability confirmed
|
||||
- 19.06.23 18:41: Vulnerability fixed via PR #414
|
||||
- 20.06.23 13:45: Security patch released
|
||||
- 21.06.23 20:48: CVE-2023-35169 got assigned
|
||||
- 21.06.23 20:58: Advisory released https://github.com/Webklex/php-imap/security/advisories/GHSA-47p7-xfcc-4pv9
|
||||
|
||||
|
||||
## [5.2.0] - 2023-04-11
|
||||
### Fixed
|
||||
- Use all available methods to detect the attachment extension instead of just one
|
||||
- Allow the `LIST` command response to be empty #393
|
||||
- Initialize folder children attributes on class initialization
|
||||
|
||||
### Added
|
||||
- Soft fail option added to all folder fetching methods. If soft fail is enabled, the method will return an empty collection instead of throwing an exception if the folder doesn't exist
|
||||
|
||||
|
||||
## [5.1.0] - 2023-03-16
|
||||
### Fixed
|
||||
- IMAP Quota root command fixed
|
||||
- Prevent line-breaks in folder path caused by special chars
|
||||
- Partial fix for #362 (allow overview response to be empty)
|
||||
- `Message::setConfig()` config parameter type set to array
|
||||
- Reset the protocol uid cache if the session gets expunged
|
||||
- Set the "seen" flag only if the flag isn't set and the fetch option isn't `IMAP::FT_PEEK`
|
||||
- `Message::is()` date comparison fixed
|
||||
- `Message::$client` could not be set to null
|
||||
- `in_reply_to` and `references` parsing fixed
|
||||
- Prevent message body parser from injecting empty lines
|
||||
- Don't parse regular inline message parts without name or filename as attachment
|
||||
- `Message::hasTextBody()` and `Message::hasHtmlBody()` should return `false` if the body is empty
|
||||
- Imap-Protocol "empty response" detection extended to catch an empty response caused by a broken resource stream
|
||||
- `iconv_mime_decode()` is now used with `ICONV_MIME_DECODE_CONTINUE_ON_ERROR` to prevent the decoding from failing
|
||||
- Date decoding rules extended to support more date formats
|
||||
- Unset the currently active folder if it gets deleted (prevent infinite loop)
|
||||
- Attachment name and filename parsing fixed and improved to support more formats
|
||||
- Check if the next uid is available (after copying or moving a message) before fetching it #381
|
||||
- Default pagination `$total` attribute value set to 0 #385 (thanks @hhniao)
|
||||
- Use attachment ID as fallback filename for saving an attachment
|
||||
- Address decoding error detection added #388
|
||||
|
||||
### Added
|
||||
- Extended UTF-7 support added (RFC2060) #383
|
||||
- `Protocol::sizes()` support added (fetch the message byte size via RFC822.SIZE). Accessible through `Message::getSize()` #379 (thanks @didi1357)
|
||||
- `Message::hasFlag()` method added to check if a message has a specific flag
|
||||
- `Message::getConfig()` method added to get the current message configuration
|
||||
- `Folder::select()` method added to select a folder
|
||||
- `Message::getAvailableFlags()` method added to get all available flags
|
||||
- Live mailbox and fixture tests added
|
||||
- `Attribute::map()` method added to map all attribute values
|
||||
- `Header::has()` method added to check if a header attribute / value exist
|
||||
- All part attributes are now accessible via linked attribute
|
||||
- Restore a message from string `Message::fromString()`
|
||||
|
||||
|
||||
## [5.0.1] - 2023-03-01
|
||||
### Fixed
|
||||
- More unique ID generation to prevent multiple attachments with same ID #363 (thanks @Guite)
|
||||
- Not all attachments are pushed to the collection #372 (thanks @AdrianKuriata)
|
||||
- Partial fix for #362 (allow search response to be empty)
|
||||
- Unsafe usage of switch case. #354 #366 (thanks @shuergab)
|
||||
- Fix use of ST_MSGN as sequence method #356 (thanks @gioid)
|
||||
- Prevent infinite loop in ImapProtocol #316 (thanks @thin-k-design)
|
||||
|
||||
|
||||
## [5.0.0] - 2023-01-18
|
||||
### Fixed
|
||||
- The message uid and message number will only be fetched if accessed and wasn't previously set #326 #285 (thanks @szymekjanaczek)
|
||||
- Fix undefined attachment name when headers use "filename*=" format #301 (thanks @JulienChavee)
|
||||
- Fixed `ImapProtocol::logout` always throws 'not connected' Exception after upgraded to 4.1.2 #351
|
||||
- Protocol interface and methods unified
|
||||
- Strict attribute and return types introduced where ever possible
|
||||
- Parallel messages during idle #338
|
||||
- Idle timeout / stale resource stream issue fixed
|
||||
- Syntax updated to support php 8 features
|
||||
- Get the attachment file extension from the filename if no mimetype detection library is available
|
||||
- Prevent the structure parsing from parsing an empty part
|
||||
- Convert all header keys to their lower case representation
|
||||
- Restructure the decode function #355 (thanks @istid)
|
||||
|
||||
### Added
|
||||
- Unit tests added #347 #242 (thanks @sergiy-petrov, @boekkooi-lengoo)
|
||||
- `Client::clone()` method added to clone a client instance
|
||||
- Save an entire message (including its headers) `Message::save()`
|
||||
- Restore a message from a local or remote file `Message::fromFile()`
|
||||
- Protocol resource stream accessor added `Protocol::getStream()`
|
||||
- Protocol resource stream meta data accessor added `Protocol::meta()`
|
||||
- ImapProtocol resource stream reset method added `ImapProtocol::reset()`
|
||||
- Protocol `Response::class` introduced to handle and unify all protocol requests
|
||||
- Static mask config accessor added `ClientManager::getMask()` added
|
||||
- An `Attribute::class` instance can be treated as array
|
||||
- Get the current client account configuration via `Client::getConfig()`
|
||||
- Delete a folder via `Client::deleteFolder()`
|
||||
|
||||
### Breaking changes
|
||||
- PHP ^8.0.2 required
|
||||
- `nesbot/carbon` version bumped to ^2.62.1
|
||||
- `phpunit/phpunit` version bumped to ^9.5.10
|
||||
- `Header::get()` always returns an `Attribute::class` instance
|
||||
- `Attribute::class` accessor methods renamed to shorten their names and improve the readability
|
||||
- All protocol methods that used to return `array|bool` will now always return a `Response::class` instance.
|
||||
- `ResponseException::class` gets thrown if a response is empty or contains errors
|
||||
- Message client is optional and can be null (e.g. if used in combination with `Message::fromFile()`)
|
||||
- The message text or html body is now "" if its empty and not `null`
|
||||
|
||||
|
||||
## [4.1.2] - 2022-12-14
|
||||
### Fixed
|
||||
- Attachment ID can return an empty value #318
|
||||
- Additional message date format added #345 (thanks @amorebietakoUdala)
|
||||
|
||||
|
||||
## [4.1.1] - 2022-11-16
|
||||
### Fixed
|
||||
- Fix for extension recognition #325 (thanks @pwoszczyk)
|
||||
- Missing null check added #327 (thanks @spanjeta)
|
||||
- Leading white-space in response causes an infinite loop #321 (thanks @thin-k-design)
|
||||
- Fix error when creating folders with special chars #319 (thanks @thin-k-design)
|
||||
- `Client::getFoldersWithStatus()` recursive loading fixed #312 (thanks @szymekjanaczek)
|
||||
- Fix Folder name encoding error in `Folder::appendMessage()` #306 #307 (thanks @rskrzypczak)
|
||||
|
||||
|
||||
## [4.1.0] - 2022-10-18
|
||||
### Fixed
|
||||
- Fix assumedNextTaggedLine bug #288 (thanks @Blear)
|
||||
- Fix empty response error for blank lines #274 (thanks @bierpub)
|
||||
- Fix empty body #233 (thanks @latypoff)
|
||||
- Fix imap_reopen folder argument #234 (thanks @latypoff)
|
||||
|
||||
### Added
|
||||
- Added possibility of loading a Folder status #298 (thanks @szymekjanaczek)
|
||||
|
||||
|
||||
## [4.0.2] - 2022-08-26
|
||||
### Fixed
|
||||
- RFC 822 3.1.1. long header fields regular expression fixed #268 #269 (thanks @hbraehne)
|
||||
|
||||
|
||||
## [4.0.1] - 2022-08-25
|
||||
### Fixed
|
||||
- Type casting added to several ImapProtocol return values #261
|
||||
- Remove IMAP::OP_READONLY flag from imap_reopen if POP3 or NNTP protocol is selected #135 (thanks @xianzhe18)
|
||||
- Several statements optimized and redundant checks removed
|
||||
- Check if the Protocol supports the fetch method if extensions are present
|
||||
- Detect `NONEXISTENT` errors while selecting or examining a folder #266
|
||||
- Missing type cast added to `PaginatedCollection::paginate` #267 (thanks @rogerb87)
|
||||
- Fix multiline header unfolding #250 (thanks @sulgie-eitea)
|
||||
- Fix problem with illegal offset error #226 (thanks @szymekjanaczek)
|
||||
- Typos fixed
|
||||
|
||||
### Affected Classes
|
||||
- [Query::class](src/Query/Query.php)
|
||||
- [ImapProtocol::class](src/Connection/Protocols/ImapProtocol.php)
|
||||
- [LegacyProtocol::class](src/Connection/Protocols/LegacyProtocol.php)
|
||||
- [PaginatedCollection::class](src/Support/PaginatedCollection.php)
|
||||
|
||||
|
||||
## [4.0.0] - 2022-08-19
|
||||
### Fixed
|
||||
- PHP dependency updated to support php v8.0 #212 #214 (thanks @freescout-helpdesk)
|
||||
- Method return and argument types added
|
||||
- Imap `DONE` method refactored
|
||||
- UID cache loop fixed
|
||||
- `HasEvent::getEvent` return value set to mixed to allow multiple event types
|
||||
- Protocol line reader changed to `fread` (stream_context timeout issue fixed)
|
||||
- Issue setting the client timeout fixed
|
||||
- IMAP Connection debugging improved
|
||||
- `Folder::idle()` method reworked and several issues fixed #170 #229 #237 #249 #258
|
||||
- Datetime conversion rules extended #189 #173
|
||||
|
||||
### Affected Classes
|
||||
- [Client::class](src/Client.php)
|
||||
- [Folder::class](src/Folder.php)
|
||||
- [ImapProtocol::class](src/Connection/Protocols/ImapProtocol.php)
|
||||
- [HasEvents::class](src/Traits/HasEvents.php)
|
||||
|
||||
### Breaking changes
|
||||
- No longer supports php >=5.5.9 but instead requires at least php v7.0.0.
|
||||
- `HasEvent::getEvent` returns a mixed result. Either an `Event` or a class string representing the event class.
|
||||
- The error message, if the connection fails to read the next line, is now `empty response` instead of `failed to read - connection closed?`.
|
||||
- The `$auto_reconnect` used with `Folder::indle()` is deprecated and doesn't serve any purpose anymore.
|
||||
|
||||
|
||||
## [3.2.0] - 2022-03-07
|
||||
### Fixed
|
||||
- Fix attribute serialization #179 (thanks @netpok)
|
||||
- Use real tls instead of starttls #180 (thanks @netpok)
|
||||
- Allow to fully overwrite default config arrays #194 (thanks @laurent-rizer)
|
||||
- Query::chunked does not loop over the last chunk #196 (thanks @laurent-rizer)
|
||||
- Fix isAttachment that did not properly take in consideration dispositions options #195 (thanks @laurent-rizer)
|
||||
- Extend date parsing error message #173
|
||||
- Fixed 'Where' method replaces the content with uppercase #148
|
||||
- Don't surround numeric search values with quotes
|
||||
- Context added to `InvalidWhereQueryCriteriaException`
|
||||
- Redundant `stream_set_timeout()` removed
|
||||
|
||||
### Added
|
||||
- UID Cache added #204 (thanks @HelloSebastian)
|
||||
- Query::class extended with `getByUidLower`, `getByUidLowerOrEqual` , `getByUidGreaterOrEqual` , `getByUidGreater` to fetch certain ranges of uids #201 (thanks @HelloSebastian)
|
||||
- Check if IDLE is supported if `Folder::idle()` is called #199 (thanks @HelloSebastian)
|
||||
- Fallback date support added. The config option `options.fallback_date` is used as fallback date is it is set. Otherwise, an exception will be thrown #198
|
||||
- UID filter support added
|
||||
- Make boundary regex configurable #169 #150 #126 #121 #111 #152 #108 (thanks @EthraZa)
|
||||
- IMAP ID support added #174
|
||||
- Enable debug mode via config
|
||||
- Custom UID alternative support added
|
||||
- Fetch additional extensions using `Folder::query(["FEATURE_NAME"])`
|
||||
- Optionally move a message during "deletion" instead of just "flagging" it #106 (thanks @EthraZa)
|
||||
- `WhereQuery::where()` accepts now a wide range of criteria / values. #104
|
||||
|
||||
### Affected Classes
|
||||
- [Part::class](src/Part.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
- [Client::class](src/Client.php)
|
||||
- [Header::class](src/Header.php)
|
||||
- [Protocol::class](src/Connection/Protocols/Protocol.php)
|
||||
- [ClientManager::class](src/ClientManager.php)
|
||||
|
||||
### Breaking changes
|
||||
- If you are using the legacy protocol to search, the results no longer return false if the search criteria could not be interpreted but instead return an empty array. This will ensure it is compatible to the rest of this library and no longer result in a potential type confusion.
|
||||
- `Folder::idle` will throw an `Webklex\PHPIMAP\Exceptions\NotSupportedCapabilityException` exception if IMAP isn't supported by the mail server
|
||||
- All protocol methods which had a `boolean` `$uid` option no longer support a boolean value. Use `IMAP::ST_UID` or `IMAP::NIL` instead. If you want to use an alternative to `UID` just use the string instead.
|
||||
- Default config option `options.sequence` changed from `IMAP::ST_MSGN` to `IMAP::ST_UID`.
|
||||
- `Folder::query()` no longer accepts a charset string. It has been replaced by an extension array, which provides the ability to automatically fetch additional features.
|
||||
|
||||
|
||||
## [3.1.0-alpha] - 2022-02-03
|
||||
### Fixed
|
||||
- Fix attribute serialization #179 (thanks @netpok)
|
||||
- Use real tls instead of starttls #180 (thanks @netpok)
|
||||
- Allow to fully overwrite default config arrays #194 (thanks @laurent-rizer)
|
||||
- Query::chunked does not loop over the last chunk #196 (thanks @laurent-rizer)
|
||||
- Fix isAttachment that did not properly take in consideration dispositions options #195 (thanks @laurent-rizer)
|
||||
|
||||
### Affected Classes
|
||||
- [Header::class](src/Header.php)
|
||||
- [Protocol::class](src/Connection/Protocols/Protocol.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
- [Part::class](src/Part.php)
|
||||
- [ClientManager::class](src/ClientManager.php)
|
||||
|
||||
## [3.0.0-alpha] - 2021-11-04
|
||||
### Fixed
|
||||
- Extend date parsing error message #173
|
||||
- Fixed 'Where' method replaces the content with uppercase #148
|
||||
- Don't surround numeric search values with quotes
|
||||
- Context added to `InvalidWhereQueryCriteriaException`
|
||||
- Redundant `stream_set_timeout()` removed
|
||||
|
||||
### Added
|
||||
- Make boundary regex configurable #169 #150 #126 #121 #111 #152 #108 (thanks @EthraZa)
|
||||
- IMAP ID support added #174
|
||||
- Enable debug mode via config
|
||||
- Custom UID alternative support added
|
||||
- Fetch additional extensions using `Folder::query(["FEATURE_NAME"])`
|
||||
- Optionally move a message during "deletion" instead of just "flagging" it #106 (thanks @EthraZa)
|
||||
- `WhereQuery::where()` accepts now a wide range of criteria / values. #104
|
||||
|
||||
### Affected Classes
|
||||
- [Header::class](src/Header.php)
|
||||
- [Protocol::class](src/Connection/Protocols/Protocol.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
- [WhereQuery::class](src/Query/WhereQuery.php)
|
||||
- [Message::class](src/Message.php)
|
||||
|
||||
### Breaking changes
|
||||
- All protocol methods which had a `boolean` `$uid` option no longer support a boolean. Use `IMAP::ST_UID` or `IMAP::NIL` instead. If you want to use an alternative to `UID` just use the string instead.
|
||||
- Default config option `options.sequence` changed from `IMAP::ST_MSGN` to `IMAP::ST_UID`.
|
||||
- `Folder::query()` no longer accepts a charset string. It has been replaced by an extension array, which provides the ability to automatically fetch additional features.
|
||||
|
||||
## [2.7.2] - 2021-09-27
|
||||
### Fixed
|
||||
- Fixed problem with skipping last line of the response. #166 (thanks @szymekjanaczek)
|
||||
|
||||
## [2.7.1] - 2021-09-08
|
||||
### Added
|
||||
- Added `UID` as available search criteria #161 (thanks @szymekjanaczek)
|
||||
|
||||
## [2.7.0] - 2021-09-04
|
||||
### Fixed
|
||||
- Fixes handling of long header lines which are seperated by `\r\n\t` (thanks @Oliver-Holz)
|
||||
- Fixes to line parsing with multiple addresses (thanks @Oliver-Holz)
|
||||
|
||||
### Added
|
||||
- Expose message folder path #154 (thanks @Magiczne)
|
||||
- Adds mailparse_rfc822_parse_addresses integration (thanks @Oliver-Holz)
|
||||
- Added moveManyMessages method (thanks @Magiczne)
|
||||
- Added copyManyMessages method (thanks @Magiczne)
|
||||
|
||||
### Affected Classes
|
||||
- [Header::class](src/Header.php)
|
||||
- [Message::class](src/Message.php)
|
||||
|
||||
## [2.6.0] - 2021-08-20
|
||||
### Fixed
|
||||
- POP3 fixes #151 (thanks @Korko)
|
||||
|
||||
### Added
|
||||
- Added imap 4 handling. #146 (thanks @szymekjanaczek)
|
||||
- Added laravel's conditionable methods. #147 (thanks @szymekjanaczek)
|
||||
|
||||
### Affected Classes
|
||||
- [Query::class](src/Query/Query.php)
|
||||
- [Client::class](src/Client.php)
|
||||
|
||||
## [2.5.1] - 2021-06-19
|
||||
### Fixed
|
||||
- Fix setting default mask from config #133 (thanks @shacky)
|
||||
- Chunked fetch fails in case of less available mails than page size #114
|
||||
- Protocol::createStream() exception information fixed #137
|
||||
- Legacy methods (headers, content, flags) fixed #125
|
||||
- Legacy connection cycle fixed #124 (thanks @zssarkany)
|
||||
|
||||
### Added
|
||||
- Disable rfc822 header parsing via config option #115
|
||||
|
||||
## [2.5.0] - 2021-02-01
|
||||
### Fixed
|
||||
- Attachment saving filename fixed
|
||||
- Unnecessary parameter removed from `Client::getTimeout()`
|
||||
- Missing encryption variable added - could have caused problems with unencrypted communications
|
||||
- Prefer attachment filename attribute over name attribute #82
|
||||
- Missing connection settings added to `Folder:idle()` auto mode #89
|
||||
- Message move / copy expect a folder path #79
|
||||
- `Client::getFolder()` updated to circumvent special edge cases #79
|
||||
- Missing connection status checks added to various methods
|
||||
- Unused default attribute `message_no` removed from `Message::class`
|
||||
|
||||
### Added
|
||||
- Dynamic Attribute access support added (e.g `$message->from[0]`)
|
||||
- Message not found exception added #93
|
||||
- Chunked fetching support added `Query::chunked()`. Just in case you can't fetch all messages at once
|
||||
- "Soft fail" support added
|
||||
- Count method added to `Attribute:class`
|
||||
- Convert an Attribute instance into a Carbon date object #95
|
||||
|
||||
### Affected Classes
|
||||
- [Attachment::class](src/Attachment.php)
|
||||
- [Attribute::class](src/Attribute.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
- [Message::class](src/Message.php)
|
||||
- [Client::class](src/Client.php)
|
||||
- [Folder::class](src/Folder.php)
|
||||
|
||||
### Breaking changes
|
||||
- A new exception can occur if a message can't be fetched (`\Webklex\PHPIMAP\Exceptions\MessageNotFoundException::class`)
|
||||
- `Message::move()` and `Message::copy()` no longer accept folder names as folder path
|
||||
- A `Message::class` instance might no longer have a `message_no` attribute
|
||||
|
||||
## [2.4.4] - 2021-01-22
|
||||
### Fixed
|
||||
- Boundary detection simplified #90
|
||||
- Prevent potential body overwriting #90
|
||||
- CSV files are no longer regarded as plain body
|
||||
- Boundary detection overhauled to support "related" and "alternative" multipart messages #90 #91
|
||||
|
||||
### Affected Classes
|
||||
- [Structure::class](src/Structure.php)
|
||||
- [Message::class](src/Message.php)
|
||||
- [Header::class](src/Header.php)
|
||||
- [Part::class](src/Part.php)
|
||||
|
||||
## [2.4.3] - 2021-01-21
|
||||
### Fixed
|
||||
- Attachment detection updated #82 #90
|
||||
- Timeout handling improved
|
||||
- Additional utf-8 checks added to prevent decoding of unencoded values #76
|
||||
|
||||
### Added
|
||||
- Auto reconnect option added to `Folder::idle()` #89
|
||||
|
||||
### Affected Classes
|
||||
- [Folder::class](src/Folder.php)
|
||||
- [Part::class](src/Part.php)
|
||||
- [Client::class](src/Client.php)
|
||||
- [Header::class](src/Header.php)
|
||||
|
||||
## [2.4.2] - 2021-01-09
|
||||
### Fixed
|
||||
- Attachment::save() return error 'A facade root has not been set' #87
|
||||
- Unused dependencies removed
|
||||
- Fix PHP 8 error that changes null back in to an empty string. #88 (thanks @mennovanhout)
|
||||
- Fix regex to be case insensitive #88 (thanks @mennovanhout)
|
||||
|
||||
### Affected Classes
|
||||
- [Attachment::class](src/Attachment.php)
|
||||
- [Address::class](src/Address.php)
|
||||
- [Attribute::class](src/Attribute.php)
|
||||
- [Structure::class](src/Structure.php)
|
||||
|
||||
## [2.4.1] - 2021-01-06
|
||||
### Fixed
|
||||
- Debug line position fixed
|
||||
- Handle incomplete address to string conversion #83
|
||||
- Configured message key gets overwritten by the first fetched message #84
|
||||
|
||||
### Affected Classes
|
||||
- [Address::class](src/Address.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
|
||||
## [2.4.0] - 2021-01-03
|
||||
### Fixed
|
||||
- Get partial overview when `IMAP::ST_UID` is set #74
|
||||
- Unnecessary "'" removed from address names
|
||||
- Folder referral typo fixed
|
||||
- Legacy protocol fixed
|
||||
- Treat message collection keys always as strings
|
||||
|
||||
### Added
|
||||
- Configurable supported default flags added
|
||||
- Message attribute class added to unify value handling
|
||||
- Address class added and integrated
|
||||
- Alias `Message::attachments()` for `Message::getAttachments()` added
|
||||
- Alias `Message::addFlag()` for `Message::setFlag()` added
|
||||
- Alias `Message::removeFlag()` for `Message::unsetFlag()` added
|
||||
- Alias `Message::flags()` for `Message::getFlags()` added
|
||||
- New Exception `MessageFlagException::class` added
|
||||
- New method `Message::setSequenceId($id)` added
|
||||
- Optional Header attributizion option added
|
||||
|
||||
### Affected Classes
|
||||
- [Folder::class](src/Folder.php)
|
||||
- [Header::class](src/Header.php)
|
||||
- [Message::class](src/Message.php)
|
||||
- [Address::class](src/Address.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
- [Attribute::class](src/Attribute.php)
|
||||
|
||||
### Breaking changes
|
||||
- Stringified message headers are now separated by ", " instead of " ".
|
||||
- All message header values such as subject, message_id, from, to, etc now consists of an `Àttribute::class` instance (should behave the same way as before, but might cause some problem in certain edge cases)
|
||||
- The formal address object "from", "to", etc now consists of an `Address::class` instance (should behave the same way as before, but might cause some problem in certain edge cases)
|
||||
- When fetching or manipulating message flags a `MessageFlagException::class` exception can be thrown if a runtime error occurs
|
||||
- Learn more about the new `Attribute` class here: [www.php-imap.com/api/attribute](https://www.php-imap.com/api/attribute)
|
||||
- Learn more about the new `Address` class here: [www.php-imap.com/api/address](https://www.php-imap.com/api/address)
|
||||
- Folder attribute "referal" is now called "referral"
|
||||
|
||||
## [2.3.1] - 2020-12-30
|
||||
### Fixed
|
||||
- Missing RFC attributes added
|
||||
- Set the message sequence when idling
|
||||
- Missing UID commands added #64
|
||||
|
||||
### Added
|
||||
- Get a message by its message number
|
||||
- Get a message by its uid #72 #66 #63
|
||||
|
||||
### Affected Classes
|
||||
- [Message::class](src/Message.php)
|
||||
- [Folder::class](src/Folder.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
|
||||
## [2.3.0] - 2020-12-21
|
||||
### Fixed
|
||||
- Cert validation issue fixed
|
||||
- Allow boundaries ending with a space or semicolon (thanks [@smartilabs](https://github.com/smartilabs))
|
||||
- Ignore IMAP DONE command response #57
|
||||
- Default `options.fetch` set to `IMAP::FT_PEEK`
|
||||
- Address parsing fixed #60
|
||||
- Alternative rfc822 header parsing fixed #60
|
||||
- Parse more than one Received: header #61
|
||||
- Fetch folder overview fixed
|
||||
- `Message::getTextBody()` fallback value fixed
|
||||
|
||||
### Added
|
||||
- Proxy support added
|
||||
- Flexible disposition support added #58
|
||||
- New `options.message_key` option `uid` added
|
||||
- Protocol UID support added
|
||||
- Flexible sequence type support added
|
||||
|
||||
### Affected Classes
|
||||
- [Structure::class](src/Structure.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
- [Client::class](src/Client.php)
|
||||
- [Header::class](src/Header.php)
|
||||
- [Folder::class](src/Folder.php)
|
||||
- [Part::class](src/Part.php)
|
||||
|
||||
### Breaking changes
|
||||
- Depending on your configuration, your certificates actually get checked. Which can cause an aborted connection if the certificate can not be validated.
|
||||
- Messages don't get flagged as read unless you are using your own custom config.
|
||||
- All `Header::class` attribute keys are now in a snake_format and no longer minus-separated.
|
||||
- `Message::getTextBody()` no longer returns false if no text body is present. `null` is returned instead.
|
||||
|
||||
## [2.2.5] - 2020-12-11
|
||||
### Fixed
|
||||
- Missing array decoder method added #51 (thanks [@lutchin](https://github.com/lutchin))
|
||||
- Additional checks added to prevent message from getting marked as seen #33
|
||||
- Boundary parsing improved #39 #36 (thanks [@AntonioDiPassio-AppSys](https://github.com/AntonioDiPassio-AppSys))
|
||||
- Idle operation updated #44
|
||||
|
||||
### Added
|
||||
- Force a folder to be opened
|
||||
|
||||
### Affected Classes
|
||||
- [Header::class](src/Header.php)
|
||||
- [Folder::class](src/Folder.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
- [Message::class](src/Message.php)
|
||||
- [Structure::class](src/Structure.php)
|
||||
|
||||
## [2.2.4] - 2020-12-08
|
||||
### Fixed
|
||||
- Search performance increased by fetching all headers, bodies and flags at once #42
|
||||
- Legacy protocol support updated
|
||||
- Fix Query pagination. (#52 [@mikemiller891](https://github.com/mikemiller891))
|
||||
|
||||
### Added
|
||||
- Missing message setter methods added
|
||||
- `Folder::overview()` method added to fetch all headers of all messages in the current folder
|
||||
|
||||
### Affected Classes
|
||||
- [Message::class](src/Message.php)
|
||||
- [Folder::class](src/Folder.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
- [PaginatedCollection::class](src/Support/PaginatedCollection.php)
|
||||
|
||||
## [2.2.3] - 2020-11-02
|
||||
### Fixed
|
||||
- Text/Html body fetched as attachment if subtype is null #34
|
||||
- Potential header overwriting through header extensions #35
|
||||
- Prevent empty attachments #37
|
||||
|
||||
### Added
|
||||
- Set fetch order during query #41 [@Max13](https://github.com/Max13)
|
||||
|
||||
### Affected Classes
|
||||
- [Message::class](src/Message.php)
|
||||
- [Part::class](src/Part.php)
|
||||
- [Header::class](src/Header.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
|
||||
|
||||
## [2.2.2] - 2020-10-20
|
||||
### Fixed
|
||||
- IMAP::FT_PEEK removing "Seen" flag issue fixed #33
|
||||
|
||||
### Affected Classes
|
||||
- [Message::class](src/Message.php)
|
||||
|
||||
## [2.2.1] - 2020-10-19
|
||||
### Fixed
|
||||
- Header decoding problem fixed #31
|
||||
|
||||
### Added
|
||||
- Search for messages by message-Id
|
||||
- Search for messages by In-Reply-To
|
||||
- Message threading added `Message::thread()`
|
||||
- Default folder locations added
|
||||
|
||||
### Affected Classes
|
||||
- [Query::class](src/Query/Query.php)
|
||||
- [Message::class](src/Message.php)
|
||||
- [Header::class](src/Header.php)
|
||||
|
||||
|
||||
## [2.2.0] - 2020-10-16
|
||||
### Fixed
|
||||
- Prevent text bodies from being fetched as attachment #27
|
||||
- Missing variable check added to prevent exception while parsing an address [webklex/laravel-imap #356](https://github.com/Webklex/laravel-imap/issues/356)
|
||||
- Missing variable check added to prevent exception while parsing a part subtype #27
|
||||
- Missing variable check added to prevent exception while parsing a part content-type [webklex/laravel-imap #356](https://github.com/Webklex/laravel-imap/issues/356)
|
||||
- Mixed message header attribute `in_reply_to` "unified" to be always an array #26
|
||||
- Potential message moving / copying problem fixed #29
|
||||
- Move messages by using `Protocol::moveMessage()` instead of `Protocol::copyMessage()` and `Message::delete()` #29
|
||||
|
||||
### Added
|
||||
- `Protocol::moveMessage()` method added #29
|
||||
|
||||
### Affected Classes
|
||||
- [Message::class](src/Message.php)
|
||||
- [Header::class](src/Header.php)
|
||||
- [Part::class](src/Part.php)
|
||||
|
||||
### Breaking changes
|
||||
- Text bodies might no longer get fetched as attachment
|
||||
- `Message::$in_reply_to` type changed from mixed to array
|
||||
|
||||
## [2.1.13] - 2020-10-13
|
||||
### Fixed
|
||||
- Boundary detection problem fixed (#28 [@DasTobbel](https://github.com/DasTobbel))
|
||||
- Content-Type detection problem fixed (#28 [@DasTobbel](https://github.com/DasTobbel))
|
||||
|
||||
### Affected Classes
|
||||
- [Structure::class](src/Structure.php)
|
||||
|
||||
## [2.1.12] - 2020-10-13
|
||||
### Fixed
|
||||
- If content disposition is multiline, implode the array to a simple string (#25 [@DasTobbel](https://github.com/DasTobbel))
|
||||
|
||||
### Affected Classes
|
||||
- [Part::class](src/Part.php)
|
||||
|
||||
## [2.1.11] - 2020-10-13
|
||||
### Fixed
|
||||
- Potential problematic prefixed white-spaces removed from header attributes
|
||||
|
||||
### Added
|
||||
- Expended `Client::getFolder($name, $deleimiter = null)` to accept either a folder name or path ([@DasTobbel](https://github.com/DasTobbel))
|
||||
- Special MS-Exchange header decoding support added
|
||||
|
||||
### Affected Classes
|
||||
- [Client::class](src/Client.php)
|
||||
- [Header::class](src/Header.php)
|
||||
|
||||
## [2.1.10] - 2020-10-09
|
||||
### Added
|
||||
- `ClientManager::make()` method added to support undefined accounts
|
||||
|
||||
### Affected Classes
|
||||
- [ClientManager::class](src/ClientManager.php)
|
||||
|
||||
## [2.1.9] - 2020-10-08
|
||||
### Fixed
|
||||
- Fix inline attachments and embedded images (#22 [@dwalczyk](https://github.com/dwalczyk))
|
||||
|
||||
### Added
|
||||
- Alternative attachment names support added (#20 [@oneFoldSoftware](https://github.com/oneFoldSoftware))
|
||||
- Fetch message content without leaving a "Seen" flag behind
|
||||
|
||||
### Affected Classes
|
||||
- [Attachment::class](src/Attachment.php)
|
||||
- [Message::class](src/Message.php)
|
||||
- [Part::class](src/Part.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
|
||||
## [2.1.8] - 2020-10-08
|
||||
### Fixed
|
||||
- Possible error during address decoding fixed (#16 [@Slauta](https://github.com/Slauta))
|
||||
- Flag event dispatching fixed #15
|
||||
|
||||
### Added
|
||||
- Support multiple boundaries (#17, #19 [@dwalczyk](https://github.com/dwalczyk))
|
||||
|
||||
### Affected Classes
|
||||
- [Structure::class](src/Structure.php)
|
||||
|
||||
## [2.1.7] - 2020-10-03
|
||||
### Fixed
|
||||
- Fixed `Query::paginate()` (#13 #14 by [@Max13](https://github.com/Max13))
|
||||
|
||||
### Affected Classes
|
||||
- [Query::class](src/Query/Query.php)
|
||||
|
||||
## [2.1.6] - 2020-10-02
|
||||
### Fixed
|
||||
- `Message::getAttributes()` hasn't returned all parameters
|
||||
|
||||
### Affected Classes
|
||||
- [Message::class](src/Message.php)
|
||||
|
||||
### Added
|
||||
- Part number added to attachment
|
||||
- `Client::getFolderByPath()` added (#12 by [@Max13](https://github.com/Max13))
|
||||
- `Client::getFolderByName()` added (#12 by [@Max13](https://github.com/Max13))
|
||||
- Throws exceptions if the authentication fails (#11 by [@Max13](https://github.com/Max13))
|
||||
|
||||
### Affected Classes
|
||||
- [Client::class](src/Client.php)
|
||||
|
||||
## [2.1.5] - 2020-09-30
|
||||
### Fixed
|
||||
- Wrong message content property reference fixed (#10)
|
||||
|
||||
## [2.1.4] - 2020-09-30
|
||||
### Fixed
|
||||
- Fix header extension values
|
||||
- Part header detection method changed (#10)
|
||||
|
||||
### Affected Classes
|
||||
- [Header::class](src/Header.php)
|
||||
- [Part::class](src/Part.php)
|
||||
|
||||
## [2.1.3] - 2020-09-29
|
||||
### Fixed
|
||||
- Possible decoding problem fixed
|
||||
- `Str::class` dependency removed from `Header::class`
|
||||
|
||||
### Affected Classes
|
||||
- [Header::class](src/Header.php)
|
||||
|
||||
## [2.1.2] - 2020-09-28
|
||||
### Fixed
|
||||
- Dependency problem in `Attachement::getExtension()` fixed (#9)
|
||||
|
||||
### Affected Classes
|
||||
- [Attachment::class](src/Attachment.php)
|
||||
|
||||
## [2.1.1] - 2020-09-23
|
||||
### Fixed
|
||||
- Missing default config parameter added
|
||||
|
||||
### Added
|
||||
- Default account config fallback added
|
||||
|
||||
### Affected Classes
|
||||
- [Client::class](src/Client.php)
|
||||
|
||||
## [2.1.0] - 2020-09-22
|
||||
### Fixed
|
||||
- Quota handling fixed
|
||||
|
||||
### Added
|
||||
- Event system and callbacks added
|
||||
|
||||
### Affected Classes
|
||||
- [Client::class](src/Client.php)
|
||||
- [Folder::class](src/Folder.php)
|
||||
- [Message::class](src/Message.php)
|
||||
|
||||
## [2.0.1] - 2020-09-20
|
||||
### Fixed
|
||||
- Carbon dependency fixed
|
||||
|
||||
## [2.0.0] - 2020-09-20
|
||||
### Fixed
|
||||
- Missing pagination item records fixed
|
||||
|
||||
### Added
|
||||
- php-imap module replaced by direct socket communication
|
||||
- Legacy support added
|
||||
- IDLE support added
|
||||
- oAuth support added
|
||||
- Charset detection method updated
|
||||
- Decoding fallback charsets added
|
||||
|
||||
### Affected Classes
|
||||
- All
|
||||
|
||||
## [1.4.5] - 2019-01-23
|
||||
### Fixed
|
||||
- .csv attachement is not processed
|
||||
- mail part structure property comparison changed to lowercase
|
||||
- Replace helper functions for Laravel 6.0 #4 (@koenhoeijmakers)
|
||||
- Date handling in Folder::appendMessage() fixed
|
||||
- Carbon Exception Parse Data
|
||||
- Convert sender name from non-utf8 to uf8 (@hwilok)
|
||||
- Convert encoding of personal data struct
|
||||
|
||||
### Added
|
||||
- Path prefix option added to Client::getFolder() method
|
||||
- Attachment size handling added
|
||||
- Find messages by custom search criteria
|
||||
|
||||
### Affected Classes
|
||||
- [Query::class](src/Query/WhereQuery.php)
|
||||
- [Mask::class](src/Support/Masks/Mask.php)
|
||||
- [Attachment::class](src/Attachment.php)
|
||||
- [Client::class](src/Client.php)
|
||||
- [Folder::class](src/Folder.php)
|
||||
- [Message::class](src/Message.php)
|
||||
|
||||
## [1.4.2.1] - 2019-07-03
|
||||
### Fixed
|
||||
- Error in Attachment::__construct #3
|
||||
- Examples added
|
||||
|
||||
## [1.4.2] - 2019-07-02
|
||||
### Fixed
|
||||
- Pagination count total bug #213
|
||||
- Changed internal message move and copy methods #210
|
||||
- Query::since() query returning empty response #215
|
||||
- Carbon Exception Parse Data #45
|
||||
- Reading a blank body (text / html) but only from this sender #203
|
||||
- Problem with Message::moveToFolder() and multiple moves #31
|
||||
- Problem with encoding conversion #203
|
||||
- Message null value attribute problem fixed
|
||||
- Client connection path handling changed to be handled inside the calling method #31
|
||||
- iconv(): error suppressor for //IGNORE added #184
|
||||
- Typo Folder attribute fullName changed to full_name
|
||||
- Query scope error fixed #153
|
||||
- Replace embedded image with URL #151
|
||||
- Fix sender name in non-latin emails sent from Gmail (#155)
|
||||
- Fix broken non-latin characters in body in ASCII (us-ascii) charset #156
|
||||
- Message::getMessageId() returns wrong value #197
|
||||
- Message date validation extended #45 #192
|
||||
- Removed "-i" from "iso-8859-8-i" in Message::parseBody #146
|
||||
|
||||
### Added
|
||||
- Message::getFolder() method
|
||||
- Create a fast count method for queries #216
|
||||
- STARTTLS encryption alias added
|
||||
- Mailbox fetching exception added #201
|
||||
- Message::moveToFolder() fetches new Message::class afterwards #31
|
||||
- Message structure accessor added #182
|
||||
- Shadow Imap const class added #188
|
||||
- Connectable "NOT" queries added
|
||||
- Additional where methods added
|
||||
- Message attribute handling changed
|
||||
- Attachment attribute handling changed
|
||||
- Message flag handling updated
|
||||
- Message::getHTMLBody($callback) extended
|
||||
- Masks added (take look at the examples for more information on masks)
|
||||
- More examples added
|
||||
- Query::paginate() method added
|
||||
- Imap client timeout can be modified and read #186
|
||||
- Decoder config options added #175
|
||||
- Message search criteria "NOT" added #181
|
||||
- Invalid message date exception added
|
||||
- Blade examples
|
||||
|
||||
### Breaking changes
|
||||
- Message::moveToFolder() returns either a Message::class instance or null and not a boolean
|
||||
- Folder::fullName is now Folder::full_name
|
||||
- Attachment::image_src might no longer work as expected - use Attachment::getImageSrc() instead
|
||||
|
||||
### Affected Classes
|
||||
- [Folder::class](src/Folder.php)
|
||||
- [Client::class](src/Client.php)
|
||||
- [Message::class](src/Message.php)
|
||||
- [Attachment::class](src/Attachment.php)
|
||||
- [Query::class](src/Query/Query.php)
|
||||
- [WhereQuery::class](src/Query/WhereQuery.php)
|
||||
|
||||
## 0.0.3 - 2018-12-02
|
||||
### Fixed
|
||||
- Folder delimiter check added #137
|
||||
- Config setting not getting loaded
|
||||
- Date parsing updated
|
||||
|
||||
### Affected Classes
|
||||
- [Folder::class](src/IMAP/Client.php)
|
||||
- [Folder::class](src/IMAP/Message.php)
|
||||
|
||||
## 0.0.1 - 2018-08-13
|
||||
### Added
|
||||
- new php-imap package (fork from [webklex/laravel-imap](https://github.com/Webklex/laravel-imap))
|
||||
46
plugins/php-imap/CODE_OF_CONDUCT.md
Normal file
46
plugins/php-imap/CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at github@webklex.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
@@ -1,293 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* File: ClientManager.php
|
||||
* Category: -
|
||||
* Author: M. Goldenbaum
|
||||
* Created: 19.01.17 22:21
|
||||
* Updated: -
|
||||
*
|
||||
* Description:
|
||||
* -
|
||||
*/
|
||||
|
||||
namespace Webklex\PHPIMAP;
|
||||
|
||||
/**
|
||||
* Class ClientManager
|
||||
*
|
||||
* @package Webklex\IMAP
|
||||
*
|
||||
* @mixin Client
|
||||
*/
|
||||
class ClientManager {
|
||||
|
||||
/**
|
||||
* All library config
|
||||
*
|
||||
* @var array $config
|
||||
*/
|
||||
public static array $config = [];
|
||||
|
||||
/**
|
||||
* @var array $accounts
|
||||
*/
|
||||
protected array $accounts = [];
|
||||
|
||||
/**
|
||||
* ClientManager constructor.
|
||||
* @param array|string $config
|
||||
*/
|
||||
public function __construct(array|string $config = []) {
|
||||
$this->setConfig($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically pass calls to the default account.
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
*
|
||||
* @return mixed
|
||||
* @throws Exceptions\MaskNotFoundException
|
||||
*/
|
||||
public function __call(string $method, array $parameters) {
|
||||
$callable = [$this->account(), $method];
|
||||
|
||||
return call_user_func_array($callable, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely create a new client instance which is not listed in accounts
|
||||
* @param array $config
|
||||
*
|
||||
* @return Client
|
||||
* @throws Exceptions\MaskNotFoundException
|
||||
*/
|
||||
public function make(array $config): Client {
|
||||
return new Client($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a dotted config parameter
|
||||
* @param string $key
|
||||
* @param null $default
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function get(string $key, $default = null): mixed {
|
||||
$parts = explode('.', $key);
|
||||
$value = null;
|
||||
foreach ($parts as $part) {
|
||||
if ($value === null) {
|
||||
if (isset(self::$config[$part])) {
|
||||
$value = self::$config[$part];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (isset($value[$part])) {
|
||||
$value = $value[$part];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $value === null ? $default : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mask for a given section
|
||||
* @param string $section section name such as "message" or "attachment"
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getMask(string $section): ?string {
|
||||
$default_masks = ClientManager::get("masks");
|
||||
if (isset($default_masks[$section])) {
|
||||
if (class_exists($default_masks[$section])) {
|
||||
return $default_masks[$section];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a account instance.
|
||||
* @param string|null $name
|
||||
*
|
||||
* @return Client
|
||||
* @throws Exceptions\MaskNotFoundException
|
||||
*/
|
||||
public function account(string $name = null): Client {
|
||||
$name = $name ?: $this->getDefaultAccount();
|
||||
|
||||
// If the connection has not been resolved we will resolve it now as all
|
||||
// the connections are resolved when they are actually needed, so we do
|
||||
// not make any unnecessary connection to the various queue end-points.
|
||||
if (!isset($this->accounts[$name])) {
|
||||
$this->accounts[$name] = $this->resolve($name);
|
||||
}
|
||||
|
||||
return $this->accounts[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an account.
|
||||
* @param string $name
|
||||
*
|
||||
* @return Client
|
||||
* @throws Exceptions\MaskNotFoundException
|
||||
*/
|
||||
protected function resolve(string $name): Client {
|
||||
$config = $this->getClientConfig($name);
|
||||
|
||||
return new Client($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the account configuration.
|
||||
* @param string|null $name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getClientConfig(?string $name): array {
|
||||
if ($name === null || $name === 'null' || $name === "") {
|
||||
return ['driver' => 'null'];
|
||||
}
|
||||
$account = self::$config["accounts"][$name] ?? [];
|
||||
|
||||
return is_array($account) ? $account : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the default account.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultAccount(): string {
|
||||
return self::$config['default'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the default account.
|
||||
* @param string $name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDefaultAccount(string $name): void {
|
||||
self::$config['default'] = $name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merge the vendor settings with the local config
|
||||
*
|
||||
* The default account identifier will be used as default for any missing account parameters.
|
||||
* If however the default account is missing a parameter the package default account parameter will be used.
|
||||
* This can be disabled by setting imap.default in your config file to 'false'
|
||||
*
|
||||
* @param array|string $config
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setConfig(array|string $config): ClientManager {
|
||||
|
||||
if (is_array($config) === false) {
|
||||
$config = require $config;
|
||||
}
|
||||
|
||||
$config_key = 'imap';
|
||||
$path = __DIR__ . '/config/' . $config_key . '.php';
|
||||
|
||||
$vendor_config = require $path;
|
||||
$config = $this->array_merge_recursive_distinct($vendor_config, $config);
|
||||
|
||||
if (is_array($config)) {
|
||||
if (isset($config['default'])) {
|
||||
if (isset($config['accounts']) && $config['default']) {
|
||||
|
||||
$default_config = $vendor_config['accounts']['default'];
|
||||
if (isset($config['accounts'][$config['default']])) {
|
||||
$default_config = array_merge($default_config, $config['accounts'][$config['default']]);
|
||||
}
|
||||
|
||||
if (is_array($config['accounts'])) {
|
||||
foreach ($config['accounts'] as $account_key => $account) {
|
||||
$config['accounts'][$account_key] = array_merge($default_config, $account);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::$config = $config;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marge arrays recursively and distinct
|
||||
*
|
||||
* Merges any number of arrays / parameters recursively, replacing
|
||||
* entries with string keys with values from latter arrays.
|
||||
* If the entry or the next value to be assigned is an array, then it
|
||||
* automatically treats both arguments as an array.
|
||||
* Numeric entries are appended, not replaced, but only if they are
|
||||
* unique
|
||||
*
|
||||
* @return array|mixed
|
||||
*
|
||||
* @link http://www.php.net/manual/en/function.array-merge-recursive.php#96201
|
||||
* @author Mark Roduner <mark.roduner@gmail.com>
|
||||
*/
|
||||
private function array_merge_recursive_distinct(): mixed {
|
||||
|
||||
$arrays = func_get_args();
|
||||
$base = array_shift($arrays);
|
||||
|
||||
// From https://stackoverflow.com/a/173479
|
||||
$isAssoc = function(array $arr) {
|
||||
if (array() === $arr) return false;
|
||||
return array_keys($arr) !== range(0, count($arr) - 1);
|
||||
};
|
||||
|
||||
if (!is_array($base)) $base = empty($base) ? array() : array($base);
|
||||
|
||||
foreach ($arrays as $append) {
|
||||
|
||||
if (!is_array($append)) $append = array($append);
|
||||
|
||||
foreach ($append as $key => $value) {
|
||||
|
||||
if (!array_key_exists($key, $base) and !is_numeric($key)) {
|
||||
$base[$key] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
(
|
||||
is_array($value)
|
||||
&& $isAssoc($value)
|
||||
)
|
||||
|| (
|
||||
is_array($base[$key])
|
||||
&& $isAssoc($base[$key])
|
||||
)
|
||||
) {
|
||||
// If the arrays are not associates we don't want to array_merge_recursive_distinct
|
||||
// else merging $baseConfig['dispositions'] = ['attachment', 'inline'] with $customConfig['dispositions'] = ['attachment']
|
||||
// results in $resultConfig['dispositions'] = ['attachment', 'inline']
|
||||
$base[$key] = $this->array_merge_recursive_distinct($base[$key], $value);
|
||||
} else if (is_numeric($key)) {
|
||||
if (!in_array($value, $base)) $base[] = $value;
|
||||
} else {
|
||||
$base[$key] = $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $base;
|
||||
}
|
||||
}
|
||||
21
plugins/php-imap/LICENSE
Normal file
21
plugins/php-imap/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Webklex
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user