Merge branch 'master' into stage
Some checks failed
SQL Syntax Check for db.sql / Check db.sql SQL Syntax (pull_request) Failing after 18s
PHPLint / build (pull_request) Successful in 26s
Welcome New Contributor / run (pull_request_target) Successful in 22s

# Conflicts:
#	guest/guest_post.php
This commit is contained in:
2025-10-21 13:07:25 -04:00
2998 changed files with 196488 additions and 27619 deletions

108
admin/ai_model.php Normal file
View File

@@ -0,0 +1,108 @@
<?php
// Default Column Sortby Filter
$sort = "ai_model_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli, "SELECT * FROM ai_models LEFT JOIN ai_providers ON ai_model_ai_provider_id = ai_provider_id ORDER BY $sort $order");
$num_rows = mysqli_num_rows($sql);
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-robot mr-2"></i>AI Models</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addAIModelModal"><i class="fas fa-plus mr-2"></i>Add Model</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows == 0) { echo "d-none"; } ?>">
<tr>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_model_name&order=<?php echo $disp; ?>">
Model <?php if ($sort == 'ai_model_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_provider_name&order=<?php echo $disp; ?>">
Provider <?php if ($sort == 'ai_provider_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_model_use_case&order=<?php echo $disp; ?>">
Use Case<?php if ($sort == 'ai_model_use_case') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark">Prompt</a>
</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$provider_id = intval($row['ai_provider_id']);
$provider_name = nullable_htmlentities($row['ai_provider_name']);
$model_id = intval($row['ai_model_id']);
$model_name = nullable_htmlentities($row['ai_model_name']);
$use_case = nullable_htmlentities($row['ai_model_use_case']);
$prompt = nl2br(nullable_htmlentities($row['ai_model_prompt']));
?>
<tr>
<td>
<a class="text-dark text-bold ajax-modal" href="#"
data-modal-url="modals/ai/ai_model_edit.php?id=<?= $model_id ?>">
<?php echo $model_name; ?>
</a>
</td>
<td><?php echo $provider_name; ?></td>
<td><?php echo $use_case; ?></td>
<td><?php echo $prompt; ?></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 ajax-modal" href="#"
data-modal-url="modals/ai/ai_model_edit.php?id=<?= $model_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_ai_model=<?php echo $model_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>
</td>
</tr>
<?php
}
if ($num_rows == 0) {
echo "<h3 class='text-secondary mt-3' style='text-align: center'>No Records Here</h3>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "modals/ai/ai_model_add.php";
require_once "../includes/footer.php";

109
admin/ai_provider.php Normal file
View File

@@ -0,0 +1,109 @@
<?php
// Default Column Sortby Filter
$sort = "ai_provider_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli, "SELECT * FROM ai_providers ORDER BY $sort $order");
$num_rows = mysqli_num_rows($sql);
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-robot mr-2"></i>AI Providers</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addAIProviderModal"><i class="fas fa-plus mr-2"></i>Add Provider</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows == 0) { echo "d-none"; } ?>">
<tr>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_provider_name&order=<?php echo $disp; ?>">
Provider <?php if ($sort == 'ai_provider_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_provider_api_url&order=<?php echo $disp; ?>">
URL <?php if ($sort == 'ai_provider_api_url') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=ai_provider_api_key&order=<?php echo $disp; ?>">
Key <?php if ($sort == 'ai_provider_api_key') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark">Models</a>
</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$provider_id = intval($row['ai_provider_id']);
$provider_name = nullable_htmlentities($row['ai_provider_name']);
$url = nullable_htmlentities($row['ai_provider_api_url']);
$key = nullable_htmlentities($row['ai_provider_api_key']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('ai_model_id') AS ai_model_count FROM ai_models WHERE ai_model_ai_provider_id = $provider_id"));
$ai_model_count = intval($row['ai_model_count']);
?>
<tr>
<td>
<a class="text-dark text-bold ajax-modal" href="#"
data-modal-url="modals/ai/ai_provider_edit.php?id=<?= $provider_id ?>">
<?php echo $provider_name; ?>
</a>
</td>
<td><?php echo $url; ?></td>
<td><?php echo $key; ?></td>
<td><?php echo $ai_model_count; ?></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 ajax-modal" href="#"
data-modal-url="modals/ai/ai_provider_edit.php?id=<?= $provider_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_ai_provider=<?php echo $provider_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>
</td>
</tr>
<?php
}
if ($num_rows == 0) {
echo "<h3 class='text-secondary mt-3' style='text-align: center'>No Records Here</h3>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "modals/ai/ai_provider_add.php";
require_once "../includes/footer.php";

176
admin/api_keys.php Normal file
View File

@@ -0,0 +1,176 @@
<?php
// Default Column Sortby Filter
$sort = "api_key_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM api_keys
LEFT JOIN clients on api_keys.api_key_client_id = clients.client_id
WHERE (api_key_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 py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-key mr-2"></i>API Keys</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addApiKeyModal"><i class="fas fa-plus mr-2"></i>Create</button>
</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 keys">
<div class="input-group-append">
<button class="btn btn-primary"><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_api_keys">
<i class="fas fa-fw fa-trash mr-2"></i>Revoke
</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" type="checkbox" onclick="checkAll(this)">
</div>
</td>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=api_key_name&order=<?php echo $disp; ?>">
Name <?php if ($sort == 'api_key_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=api_key_client_id&order=<?php echo $disp; ?>">
Client <?php if ($sort == 'api_key_client_id') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=api_key_secret&order=<?php echo $disp; ?>">
Secret <?php if ($sort == 'api_key_secret') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=api_key_created_at&order=<?php echo $disp; ?>">
Created <?php if ($sort == 'api_key_created_at') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=api_key_expire&order=<?php echo $disp; ?>">
Expires <?php if ($sort == 'api_key_expire') { echo $order_icon; } ?>
</a>
</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$api_key_id = intval($row['api_key_id']);
$api_key_name = nullable_htmlentities($row['api_key_name']);
$api_key_secret = nullable_htmlentities("************" . substr($row['api_key_secret'], -4));
$api_key_created_at = nullable_htmlentities($row['api_key_created_at']);
$api_key_expire = nullable_htmlentities($row['api_key_expire']);
if ($api_key_expire < date("Y-m-d H:i:s")) {
$api_key_expire = $api_key_expire . " (Expired)";
}
if ($row['api_key_client_id'] == 0) {
$api_key_client = "<i>All Clients</i>";
} else {
$api_key_client = nullable_htmlentities($row['client_name']);
}
?>
<tr>
<td class="pr-0">
<div class="form-check">
<input class="form-check-input bulk-select" type="checkbox" name="api_key_ids[]" value="<?php echo $api_key_id ?>">
</div>
</td>
<td class="text-bold"><?php echo $api_key_name; ?></td>
<td><?php echo $api_key_client; ?></td>
<td><?php echo $api_key_secret; ?></td>
<td><?php echo $api_key_created_at; ?></td>
<td><?php echo $api_key_expire; ?></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 text-danger text-bold confirm-link" href="post.php?delete_api_key=<?php echo $api_key_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-times mr-2"></i>Revoke
</a>
</div>
</div>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</form>
</div>
<?php require_once "../includes/filter_footer.php";
?>
</div>
</div>
<script src="../js/bulk_actions.js"></script>
<?php
require_once "modals/api/api_key_add.php";
require_once "../includes/footer.php";

192
admin/app_log.php Normal file
View File

@@ -0,0 +1,192 @@
<?php
// Default Column Sortby Filter
$sort = "app_log_id";
$order = "DESC";
require_once "includes/inc_all_admin.php";
// Log Type Filter
if (isset($_GET['type']) & !empty($_GET['type'])) {
$log_type_query = "AND (app_log_type = '" . sanitizeInput($_GET['type']) . "')";
$type_filter = nullable_htmlentities($_GET['type']);
} else {
// Default - any
$log_type_query = '';
$type_filter = '';
}
// Log Category Filter
if (isset($_GET['category']) & !empty($_GET['catergory'])) {
$log_category_query = "AND (app_log_category = '" . sanitizeInput($_GET['category']) . "')";
$category_filter = nullable_htmlentities($_GET['category']);
} else {
// Default - any
$log_category_query = '';
$category_filter = '';
}
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM app_logs
WHERE (app_log_type LIKE '%$q%' OR app_log_category LIKE '%$q%' OR app_log_details LIKE '%$q%')
AND DATE(app_log_created_at) BETWEEN '$dtf' AND '$dtt'
$log_type_query
$log_category_query
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-3">
<h3 class="card-title"><i class="fas fa-fw fa-history mr-2"></i>App Logs</h3>
</div>
<div class="card-body">
<form autocomplete="off">
<div class="row">
<div class="col-sm-4">
<div class="form-group">
<div class="input-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search app logs">
<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>
<div class="col-sm-2">
<div class="form-group">
<select class="form-control select2" name="type" onchange="this.form.submit()">
<option value="">- All Types -</option>
<?php
$sql_types_filter = mysqli_query($mysqli, "SELECT DISTINCT app_log_type FROM app_logs ORDER BY app_log_type ASC");
while ($row = mysqli_fetch_array($sql_types_filter)) {
$log_type = nullable_htmlentities($row['app_log_type']);
?>
<option <?php if ($type_filter == $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="category" onchange="this.form.submit()">
<option value="">- All Categories -</option>
<?php
$sql_categories_filter = mysqli_query($mysqli, "SELECT DISTINCT app_log_category FROM app_logs ORDER BY app_log_category ASC");
while ($row = mysqli_fetch_array($sql_categories_filter)) {
$log_category = nullable_htmlentities($row['app_log_category']);
?>
<option <?php if ($category_filter == $log_category) { echo "selected"; } ?>><?php echo $log_category; ?></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">
<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-sm 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=app_log_created_at&order=<?php echo $disp; ?>">
Timestamp <?php if ($sort == 'app_log_created_at') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=app_log_type&order=<?php echo $disp; ?>">
Type <?php if ($sort == 'app_log_type') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=app_log_category&order=<?php echo $disp; ?>">
Category <?php if ($sort == 'app_log_category') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=app_log_details&order=<?php echo $disp; ?>">
Details <?php if ($sort == 'app_log_details') { echo $order_icon; } ?>
</a>
</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$log_id = intval($row['app_log_id']);
$log_type = nullable_htmlentities($row['app_log_type']);
$log_category = nullable_htmlentities($row['app_log_category']);
$log_details = nullable_htmlentities($row['app_log_details']);
$log_created_at = nullable_htmlentities($row['app_log_created_at']);
?>
<tr>
<td><?php echo $log_created_at; ?></td>
<td><?php echo $log_type; ?></td>
<td><?php echo $log_category; ?></td>
<td><?php echo $log_details; ?></td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
<?php require_once "../includes/filter_footer.php";
?>
</div>
</div>
<?php
require_once "../includes/footer.php";

302
admin/audit_log.php Normal file
View File

@@ -0,0 +1,302 @@
<?php
// Default Column Sortby Filter
$sort = "log_id";
$order = "DESC";
require_once "includes/inc_all_admin.php";
// User Filter
if (isset($_GET['user']) & !empty($_GET['user'])) {
$user_query = 'AND (log_user_id = ' . intval($_GET['user']) . ')';
$user_filter = intval($_GET['user']);
} else {
// Default - any
$user_query = '';
$user_filter = '';
}
// Client Filter
if (isset($_GET['client']) & !empty($_GET['client'])) {
$client_query = 'AND (log_client_id = ' . intval($_GET['client']) . ')';
$client_filter = intval($_GET['client']);
} else {
// Default - any
$client_query = '';
$client_filter = '';
}
// Log Type Filter
if (isset($_GET['type']) & !empty($_GET['type'])) {
$log_type_query = "AND (log_type = '" . sanitizeInput($_GET['type']) . "')";
$type_filter = nullable_htmlentities($_GET['type']);
} else {
// Default - any
$log_type_query = '';
$type_filter = '';
}
// Log Action Filter
if (isset($_GET['action']) & !empty($_GET['action'])) {
$log_action_query = "AND (log_action = '" . sanitizeInput($_GET['action']) . "')";
$action_filter = nullable_htmlentities($_GET['action']);
} else {
// Default - any
$log_action_query = '';
$action_filter = '';
}
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM logs
LEFT JOIN users ON log_user_id = user_id
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
$client_query
$log_type_query
$log_action_query
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-3">
<h3 class="card-title"><i class="fas fa-fw fa-history mr-2"></i>Audit Logs</h3>
</div>
<div class="card-body">
<form class="mb-4" autocomplete="off">
<div class="row">
<div class="col-sm-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 audit logs">
<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-2">
<div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="client" onchange="this.form.submit()">
<option value="">- All Clients -</option>
<?php
$sql_clients_filter = mysqli_query($mysqli, "SELECT * FROM clients ORDER BY client_name ASC");
while ($row = mysqli_fetch_array($sql_clients_filter)) {
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
?>
<option <?php if ($client_filter == $client_id) { echo "selected"; } ?> value="<?php echo $client_id; ?>"><?php echo $client_name; ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="col-sm-2">
<div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="user" onchange="this.form.submit()">
<option value="">- 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_filter == $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="input-group mb-3 mb-md-0">
<select class="form-control select2" name="type" onchange="this.form.submit()">
<option value="">- 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_filter == $log_type) { echo "selected"; } ?>><?php echo $log_type; ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="col-sm-2">
<div class="input-group mb-3 mb-md-0">
<select class="form-control select2" name="action" onchange="this.form.submit()">
<option value="">- 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_filter == $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">
<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-sm table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?> text-nowrap">
<tr>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_created_at&order=<?php echo $disp; ?>">
Timestamp <?php if ($sort == 'log_created_at') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=user_name&order=<?php echo $disp; ?>">
User <?php if ($sort == 'user_name') { echo $order_icon; } ?>
</a>
</th>
<?php if (empty($client)) { ?>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=client_name&order=<?php echo $disp; ?>">
Client <?php if ($sort == 'client_name') { echo $order_icon; } ?>
</a>
</th>
<?php } ?>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_type&order=<?php echo $disp; ?>">
Type <?php if ($sort == 'log_type') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_action&order=<?php echo $disp; ?>">
Action <?php if ($sort == 'log_action') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_description&order=<?php echo $disp; ?>">
Description <?php if ($sort == 'log_description') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_ip&order=<?php echo $disp; ?>">
IP Address <?php if ($sort == 'log_ip') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=log_user_agent&order=<?php echo $disp; ?>">
User Agent <?php if ($sort == 'log_user_agent') { echo $order_icon; } ?>
</a>
</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$log_id = intval($row['log_id']);
$log_type = nullable_htmlentities($row['log_type']);
$log_action = nullable_htmlentities($row['log_action']);
$log_description = nullable_htmlentities($row['log_description']);
$log_ip = nullable_htmlentities($row['log_ip']);
$log_user_agent = nullable_htmlentities($row['log_user_agent']);
$log_user_os = getOS($log_user_agent);
$log_user_browser = getWebBrowser($log_user_agent);
$log_created_at = nullable_htmlentities($row['log_created_at']);
$user_id = intval($row['user_id']);
$user_name = nullable_htmlentities($row['user_name']);
if (empty($user_name)) {
$user_name_display = "-";
} else {
$user_name_display = $user_name;
}
$client_name = nullable_htmlentities($row['client_name']);
$client_id = intval($row['client_id']);
if (empty($client_name)) {
$client_name_display = "-";
} else {
$client_name_display = "<a href='../agent/client_overview.php?client_id=$client_id'>$client_name</a>";
}
$log_entity_id = intval($row['log_entity_id']);
?>
<tr>
<td><?php echo $log_created_at; ?></td>
<td><?php echo $user_name_display; ?></td>
<?php if(empty($client)) { ?>
<td><?php echo $client_name_display; ?></td>
<?php } ?>
<td><?php echo $log_type; ?></td>
<td><?php echo $log_action; ?></td>
<td><?php echo $log_description; ?></td>
<td><?php echo $log_ip; ?></td>
<td><?php echo "$log_user_os<div class='text-secondary'>$log_user_browser</div>"; ?></td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
<?php require_once "../includes/filter_footer.php";
?>
</div>
</div>
<?php
require_once "../includes/footer.php";

38
admin/backup.php Normal file
View File

@@ -0,0 +1,38 @@
<?php
require_once "includes/inc_all_admin.php";
?>
<div class="card card-dark mb-3">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-database mr-2"></i>Download Database</h3>
</div>
<div class="card-body" style="text-align: center;">
<div class="alert alert-secondary">If you are unable to back up the entire VM, you'll need to back up the files & database individually. There is no built-in restore. See the <a href="https://docs.itflow.org/backups" target="_blank">docs here</a>.</div>
<a class="btn btn-primary btn-lg p-3" href="post.php?download_backup&csrf_token=<?php echo $_SESSION['csrf_token'] ?>"><i class="fas fa-fw fa-4x fa-download"></i><br><br>Download Backup</a>
</div>
</div>
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-key mr-2"></i>Backup Master Encryption Key</h3>
</div>
<div class="card-body">
<div class="card-body">
<form action="post.php" method="POST">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="d-flex justify-content-center">
<div class="input-group col-sm-4">
<input type="password" class="form-control" placeholder="Enter your account password" name="password" autocomplete="new-password" required>
<div class="input-group-append">
<button class="btn btn-primary" type="submit" name="backup_master_key"><i class="fas fa-key"></i></button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<?php
require_once "../includes/footer.php";

179
admin/category.php Normal file
View File

@@ -0,0 +1,179 @@
<?php
// Default Column Sortby Filter
$sort = "category_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
if (isset($_GET['category'])) {
$category = sanitizeInput($_GET['category']);
} else {
$category = "Expense";
}
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM categories
WHERE category_name LIKE '%$q%'
AND category_type = '$category'
AND category_$archive_query
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="fa fa-fw fa-list-ul mr-2"></i>
<?php echo nullable_htmlentities($category); ?> Categories
</h3>
<?php
if (!isset($_GET['archived'])) {
?>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/category/category_add.php?category=<?= nullable_htmlentities($category) ?>"><i
class="fas fa-plus mr-2"></i>New <?php echo nullable_htmlentities($category); ?> Category</button>
</div>
<?php
}
?>
</div>
<div class="card-body">
<form autocomplete="off">
<input type="hidden" name="category" value="<?php echo nullable_htmlentities($category); ?>">
<div class="row">
<div class="col-sm-4 mb-2">
<div class="input-group">
<input type="search" class="form-control" name="q"
value="<?php if (isset($q)) {
echo stripslashes(nullable_htmlentities($q));
} ?>"
placeholder="Search <?php echo nullable_htmlentities($category); ?> Categories ">
<div class="input-group-append">
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</div>
<div class="col-sm-8">
<div class="btn-group float-right">
<a href="?category=Expense"
class="btn <?php if ($category == 'Expense') {
echo 'btn-primary';
} else {
echo 'btn-default';
} ?>">Expense</a>
<a href="?category=Income"
class="btn <?php if ($category == 'Income') {
echo 'btn-primary';
} else {
echo 'btn-default';
} ?>">Income</a>
<a href="?category=Referral"
class="btn <?php if ($category == 'Referral') {
echo 'btn-primary';
} else {
echo 'btn-default';
} ?>">Referral</a>
<a href="?category=Ticket"
class="btn <?php if ($category == 'Ticket') {
echo 'btn-primary';
} else {
echo 'btn-default';
} ?>">Ticket</a>
<a href="?<?php echo $url_query_strings_sort ?>&archived=1"
class="btn <?php if (isset($_GET['archived'])) {
echo 'btn-primary';
} else {
echo 'btn-default';
} ?>"><i
class="fas fa-fw fa-archive mr-2"></i>Archived</a>
</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=category_name&order=<?php echo $disp; ?>">
Name <?php if ($sort == 'category_name') { echo $order_icon; } ?>
</a>
</th>
<th>Color</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$category_id = intval($row['category_id']);
$category_name = nullable_htmlentities($row['category_name']);
$category_color = nullable_htmlentities($row['category_color']);
?>
<tr>
<td>
<a class="text-dark ajax-modal" href="#"
data-modal-url="modals/category/category_edit.php?id=<?= $category_id ?>">
<?php echo $category_name; ?>
</a>
</td>
<td><i class="fa fa-3x fa-circle" style="color:<?php echo $category_color; ?>;"></i></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 ($archived) {
?>
<a class="dropdown-item text-success confirm-link"
href="post.php?unarchive_category=<?php echo $category_id; ?>">
<i class="fas fa-fw fa-archive mr-2"></i>Unarchive
</a>
<a class="dropdown-item text-danger confirm-link"
href="post.php?delete_category=<?php echo $category_id; ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
<?php
} else {
?>
<a class="dropdown-item ajax-modal" href="#"
data-modal-url="modals/category/category_edit.php?id=<?= $category_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<a class="dropdown-item text-danger confirm-link"
href="post.php?archive_category=<?php echo $category_id; ?>">
<i class="fas fa-fw fa-archive mr-2"></i>Archive
</a>
<?php
}
?>
</div>
</div>
</td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
<?php require_once "../includes/filter_footer.php";
?>
</div>
</div>
<?php
require_once "../includes/footer.php";

8
admin/custom/readme.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
/*
- Custom Pages -
If you wish to add custom pages to ITFlow, add them to this directory"
Link to Documentation for File Directory Structure and examples
*/

149
admin/custom_link.php Normal file
View File

@@ -0,0 +1,149 @@
<?php
// Default Column Sortby Filter
$sort = "custom_link_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM custom_links
WHERE custom_link_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 py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-external-link-alt mr-2"></i>Custom Links</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addLinkModal"><i class="fas fa-plus mr-2"></i>New Link</button>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-sm-4 mb-2">
<form autocomplete="off">
<div class="input-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search Links">
<div class="input-group-append">
<button class="btn btn-primary"><i class="fa fa-search"></i></button>
</div>
</div>
</form>
</div>
<div class="col-sm-8">
</div>
</div>
<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=custom_link_name&order=<?php echo $disp; ?>">
Name <?php if ($sort == 'custom_link_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=custom_link_order&order=<?php echo $disp; ?>">
Order <?php if ($sort == 'custom_link_order') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=custom_link_uri&order=<?php echo $disp; ?>">
URI / <span class="text-secondary">New Tab</span> <?php if ($sort == 'custom_link_uri') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=custom_link_location&order=<?php echo $disp; ?>">
Location <?php if ($sort == 'custom_link_location') { echo $order_icon; } ?>
</a>
</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$custom_link_id = intval($row['custom_link_id']);
$custom_link_name = nullable_htmlentities($row['custom_link_name']);
$custom_link_uri = nullable_htmlentities($row['custom_link_uri']);
$custom_link_icon = nullable_htmlentities($row['custom_link_icon']);
$custom_link_new_tab = intval($row['custom_link_new_tab']);
if ($custom_link_new_tab == 1 ) {
$custom_link_new_tab_display = "<i class='fas fa-fw fa-checkmark'></i>";
} else {
$custom_link_new_tab_display = "";
}
$custom_link_order = intval($row['custom_link_order']);
if ($custom_link_order == 0 ) {
$custom_link_order_display = "-";
} else {
$custom_link_order_display = $custom_link_order;
}
$custom_link_location = intval($row['custom_link_location']);
if ($custom_link_location == 1) {
$custom_link_location_display = "Main Side Nav";
} elseif ($custom_link_location == 2) {
$custom_link_location_display = "Top Nav";
} elseif ($custom_link_location == 3) {
$custom_link_location_display = "Client Portal Nav";
} elseif ($custom_link_location == 4) {
$custom_link_location_display = "Admin Nav";
} elseif ($custom_link_location == 5) {
$custom_link_location_display = "Reports Nav";
}
?>
<tr>
<td>
<a class="ajax-modal" href="#"
data-modal-url="modals/custom_link/custom_link_edit.php?id=<?= $custom_link_id ?>">
<i class="fa fa-fw fa-<?php echo $custom_link_icon; ?> mr-2"></i><?php echo $custom_link_name;?>
</a>
</td>
<td><?php echo $custom_link_order_display; ?></td>
<td><?php echo "$custom_link_uri $custom_link_new_tab_display"; ?></td>
<td><?php echo $custom_link_location_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 ajax-modal" href="#" data-modal-url="modals/custom_link/custom_link_edit.php?id=<?= $custom_link_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_custom_link=<?php echo $custom_link_id; ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>
</td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
<?php require_once "../includes/filter_footer.php";
?>
</div>
</div>
<?php
require_once "modals/custom_link/custom_link_add.php";
require_once "../includes/footer.php";

4044
admin/database_updates.php Normal file

File diff suppressed because it is too large Load Diff

770
admin/debug.php Normal file
View File

@@ -0,0 +1,770 @@
<?php
require_once "includes/inc_all_admin.php";
require_once "../includes/database_version.php";
require_once "../config.php";
$checks = [];
// Execute the git command to get the latest commit hash
$commitHash = shell_exec('git log -1 --format=%H');
// Get branch info
$gitBranch = shell_exec('git rev-parse --abbrev-ref HEAD');
// Section: System Information
$systemInfo = [];
// Operating System and Version
$os = php_uname();
$systemInfo[] = [
'name' => 'Operating System',
'value' => $os,
];
// Web Server and Version
$webServer = $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown';
$systemInfo[] = [
'name' => 'Web Server',
'value' => $webServer,
];
// Kernel and Version
$kernelVersion = php_uname('r');
$systemInfo[] = [
'name' => 'Kernel Version',
'value' => $kernelVersion,
];
// Database and Version
$dbVersion = $mysqli->server_info;
$systemInfo[] = [
'name' => 'Database Version',
'value' => $dbVersion,
];
// Section: PHP Extensions
$phpExtensions = [];
$extensions = [
'php-mailparse' => 'mailparse',
'php-imap' => 'imap',
'php-mysqli' => 'mysqli',
'php-intl' => 'intl',
'php-curl' => 'curl',
'php-mbstring' => 'mbstring',
'php-gd' => 'gd',
'php-zip' => 'zip',
];
foreach ($extensions as $name => $ext) {
$loaded = extension_loaded($ext);
$phpExtensions[] = [
'name' => "$name installed",
'passed' => $loaded,
'value' => $loaded ? 'Installed' : 'Not Installed',
];
}
// Section: PHP Configuration
$phpConfig = [];
// Check if shell_exec is enabled
$disabled_functions = explode(',', ini_get('disable_functions'));
$disabled_functions = array_map('trim', $disabled_functions);
$shell_exec_enabled = !in_array('shell_exec', $disabled_functions);
$phpConfig[] = [
'name' => 'shell_exec is enabled',
'passed' => $shell_exec_enabled,
'value' => $shell_exec_enabled ? 'Enabled' : 'Disabled',
];
// Check upload_max_filesize and post_max_size >= 500M
function return_bytes($val) {
$val = trim($val);
$unit = strtolower(substr($val, -1));
$num = (float)$val;
switch ($unit) {
case 'g':
$num *= 1024;
case 'm':
$num *= 1024;
case 'k':
$num *= 1024;
}
return $num;
}
$required_bytes = 500 * 1024 * 1024; // 500M in bytes
$upload_max_filesize = ini_get('upload_max_filesize');
$post_max_size = ini_get('post_max_size');
$upload_passed = return_bytes($upload_max_filesize) >= $required_bytes;
$post_passed = return_bytes($post_max_size) >= $required_bytes;
$phpConfig[] = [
'name' => 'upload_max_filesize >= 500M',
'passed' => $upload_passed,
'value' => $upload_max_filesize,
];
$phpConfig[] = [
'name' => 'post_max_size >= 500M',
'passed' => $post_passed,
'value' => $post_max_size,
];
// PHP Memory Limit >= 128M
$memoryLimit = ini_get('memory_limit');
$memoryLimitBytes = return_bytes($memoryLimit);
$memoryLimitPassed = $memoryLimitBytes >= (128 * 1024 * 1024);
$phpConfig[] = [
'name' => 'PHP Memory Limit >= 128M',
'passed' => $memoryLimitPassed,
'value' => $memoryLimit,
];
// Max Execution Time >= 300 seconds
$maxExecutionTime = ini_get('max_execution_time');
$maxExecutionTimePassed = $maxExecutionTime >= 300;
$phpConfig[] = [
'name' => 'Max Execution Time >= 300 seconds',
'passed' => $maxExecutionTimePassed,
'value' => $maxExecutionTime . ' seconds',
];
// Check PHP version >= 8.2.0
$php_version = PHP_VERSION;
$php_passed = version_compare($php_version, '8.2.0', '>=');
$phpConfig[] = [
'name' => 'PHP version >= 8.2.0',
'passed' => $php_passed,
'value' => $php_version,
];
// Section: Shell Commands
$shellCommands = [];
if ($shell_exec_enabled) {
$commands = ['whois', 'dig', 'git'];
foreach ($commands as $command) {
$which = trim(shell_exec("which $command 2>/dev/null"));
$exists = !empty($which);
$shellCommands[] = [
'name' => "Command '$command' available",
'passed' => $exists,
'value' => $exists ? $which : 'Not Found',
];
}
} else {
// If shell_exec is disabled, mark commands as unavailable
foreach (['whois', 'dig', 'git'] as $command) {
$shellCommands[] = [
'name' => "Command '$command' available",
'passed' => false,
'value' => 'shell_exec Disabled',
];
}
}
// Section: SSL Checks
$sslChecks = [];
// Check if accessing via HTTPS
$https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443;
$sslChecks[] = [
'name' => 'Accessing via HTTPS',
'passed' => $https,
'value' => $https ? 'Yes' : 'No',
];
// SSL Certificate Validity Check
if ($https) {
$streamContext = stream_context_create(["ssl" => ["capture_peer_cert" => true]]);
$socket = @stream_socket_client("ssl://{$_SERVER['HTTP_HOST']}:443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $streamContext);
if ($socket) {
$params = stream_context_get_params($socket);
$cert = $params['options']['ssl']['peer_certificate'];
$certInfo = openssl_x509_parse($cert);
$validFrom = $certInfo['validFrom_time_t'];
$validTo = $certInfo['validTo_time_t'];
$currentTime = time();
$certValid = ($currentTime >= $validFrom && $currentTime <= $validTo);
$sslChecks[] = [
'name' => 'SSL Certificate is valid',
'passed' => $certValid,
'value' => $certValid ? 'Valid' : 'Invalid or Expired',
];
} else {
$sslChecks[] = [
'name' => 'SSL Certificate is valid',
'passed' => false,
'value' => 'Unable to retrieve certificate',
];
}
} else {
$sslChecks[] = [
'name' => 'SSL Certificate is valid',
'passed' => false,
'value' => 'Not using HTTPS',
];
}
// Section: Domain Checks
$domainChecks = [];
// Check if the site has a valid FQDN
$fqdn = $_SERVER['HTTP_HOST'];
$isValidFqdn = (bool) filter_var('http://' . $fqdn, FILTER_VALIDATE_URL) && preg_match('/^[a-z0-9.-]+\.[a-z]{2,}$/i', $fqdn);
$domainChecks[] = [
'name' => 'Site has a valid FQDN',
'passed' => $isValidFqdn,
'value' => $fqdn,
];
// Section: File Permissions
$filePermissions = [];
// Check if web user has write access to webroot directory
$webroot = $_SERVER['DOCUMENT_ROOT'];
$writable = is_writable($webroot);
$filePermissions[] = [
'name' => 'Web user has write access to webroot directory',
'passed' => $writable,
'value' => $webroot,
];
// Section: Uploads Directory Stats
$uploadsStats = [];
// Define the uploads directory path
$uploadsDir = __DIR__ . '/../uploads'; // Adjust the path if needed
if (is_dir($uploadsDir)) {
// Function to recursively count files and calculate total size
function getDirStats($dir) {
$files = 0;
$size = 0;
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
foreach ($iterator as $file) {
if ($file->isFile()) {
$files++;
$size += $file->getSize();
}
}
return ['files' => $files, 'size' => $size];
}
$stats = getDirStats($uploadsDir);
$sizeInMB = round($stats['size'] / (1024 * 1024), 2);
$uploadsStats[] = [
'name' => 'Number of files in uploads directory',
'value' => $stats['files'],
];
$uploadsStats[] = [
'name' => 'Total size of uploads directory (MB)',
'value' => $sizeInMB . ' MB',
];
} else {
$uploadsStats[] = [
'name' => 'Uploads directory exists',
'value' => 'Directory not found',
];
}
// Section: Database Stats
$databaseStats = [];
// Get list of tables
$tablesResult = $mysqli->query("SHOW TABLE STATUS");
if ($tablesResult) {
$totalTables = 0;
$totalFields = 0;
$totalRows = 0;
$totalSize = 0;
$tableDetails = [];
while ($table = $tablesResult->fetch_assoc()) {
$tableName = $table['Name'];
// Accurate row count
$countResult = $mysqli->query("SELECT COUNT(*) AS cnt FROM `$tableName`");
$countRow = $countResult->fetch_assoc();
$tableRows = $countRow['cnt'];
$countResult->free();
$dataLength = $table['Data_length'];
$indexLength = $table['Index_length'];
$tableSize = ($dataLength + $indexLength) / (1024 * 1024); // Size in MB
// Get number of fields
$fieldsResult = $mysqli->query("SHOW COLUMNS FROM `$tableName`");
$numFields = $fieldsResult->num_rows;
$fieldsResult->free();
$totalTables++;
$totalFields += $numFields;
$totalRows += $tableRows;
$totalSize += $tableSize;
$tableDetails[] = [
'name' => $tableName,
'fields' => $numFields,
'rows' => $tableRows,
'size' => round($tableSize, 2),
];
}
$tablesResult->free();
$databaseStats[] = [
'name' => 'Total number of tables',
'value' => $totalTables,
];
$databaseStats[] = [
'name' => 'Total number of fields',
'value' => $totalFields,
];
$databaseStats[] = [
'name' => 'Total number of rows',
'value' => $totalRows,
];
$databaseStats[] = [
'name' => 'Total database size (MB)',
'value' => round($totalSize, 2) . ' MB',
];
}
// Section: Database Structure Comparison
$dbComparison = [];
// Path to the db.sql file
$dbSqlFile = __DIR__ . '/../db.sql';
if (file_exists($dbSqlFile)) {
// Read the db.sql file
$sqlContent = file_get_contents($dbSqlFile);
// Remove comments and empty lines
$lines = explode("\n", $sqlContent);
$sqlStatements = [];
$statement = '';
foreach ($lines as $line) {
// Remove single-line comments
$line = preg_replace('/--.*$/', '', $line);
$line = preg_replace('/\/\*.*?\*\//', '', $line);
// Skip empty lines
if (trim($line) == '') {
continue;
}
// Append line to the current statement
$statement .= $line . "\n";
// Check if the statement ends with a semicolon
if (preg_match('/;\s*$/', $line)) {
$sqlStatements[] = $statement;
$statement = '';
}
}
// Parse the CREATE TABLE statements
$sqlTables = [];
foreach ($sqlStatements as $sql) {
if (preg_match('/CREATE TABLE\s+`?([^` ]+)`?\s*\((.*)\)(.*?);/msi', $sql, $match)) {
$tableName = $match[1];
$columnsDefinition = $match[2];
// Extract column names and data types
$columns = [];
$columnLines = explode("\n", $columnsDefinition);
foreach ($columnLines as $line) {
$line = trim($line);
// Skip empty lines and lines that do not define columns
if ($line == '' || strpos($line, 'PRIMARY KEY') !== false || strpos($line, 'UNIQUE KEY') !== false || strpos($line, 'KEY') === 0 || strpos($line, 'CONSTRAINT') === 0 || strpos($line, ')') === 0) {
continue;
}
// Remove trailing comma if present
$line = rtrim($line, ',');
// Match column definition
if (preg_match('/^`([^`]+)`\s+(.+)/', $line, $colMatch)) {
$colName = $colMatch[1];
$colDefinition = $colMatch[2];
// Extract the data type from the column definition
$tokens = preg_split('/\s+/', $colDefinition);
$colType = $tokens[0];
// Handle data types with parentheses (e.g., varchar(255), decimal(15,2))
if (preg_match('/^([a-zA-Z]+)\(([^)]+)\)/', $colType, $typeMatch)) {
$colType = $typeMatch[1] . '(' . $typeMatch[2] . ')';
}
$columns[$colName] = $colType;
}
}
$sqlTables[$tableName] = $columns;
}
}
// Get current database table structures
$dbTables = [];
$tablesResult = $mysqli->query("SHOW TABLES");
while ($row = $tablesResult->fetch_row()) {
$tableName = $row[0];
$columnsResult = $mysqli->query("SHOW COLUMNS FROM `$tableName`");
$columns = [];
while ($col = $columnsResult->fetch_assoc()) {
$columns[$col['Field']] = $col['Type'];
}
$columnsResult->free();
$dbTables[$tableName] = $columns;
}
$tablesResult->free();
// Compare the structures
foreach ($sqlTables as $tableName => $sqlColumns) {
if (!isset($dbTables[$tableName])) {
$dbComparison[] = [
'name' => "Table `$tableName` missing in database",
'status' => 'Missing Table',
];
continue;
}
// Compare columns
$dbColumns = $dbTables[$tableName];
foreach ($sqlColumns as $colName => $colType) {
if (!isset($dbColumns[$colName])) {
$dbComparison[] = [
'name' => "Column `$colName` missing in table `$tableName`",
'status' => 'Missing Column',
];
} else {
// Normalize data types for comparison
$sqlColType = strtolower($colType);
$dbColType = strtolower($dbColumns[$colName]);
// Remove attributes and constraints
$sqlColType = preg_replace('/\s+.*$/', '', $sqlColType);
$dbColType = preg_replace('/\s+.*$/', '', $dbColType);
// Remove additional attributes like unsigned, zerofill, etc.
$sqlColType = preg_replace('/\s+unsigned|\s+zerofill|\s+binary/', '', $sqlColType);
$dbColType = preg_replace('/\s+unsigned|\s+zerofill|\s+binary/', '', $dbColType);
if ($sqlColType != $dbColType) {
$dbComparison[] = [
'name' => "Data type mismatch for `$colName` in table `$tableName`",
'status' => "Expected: $colType, Found: {$dbColumns[$colName]}",
];
}
}
}
// Check for extra columns in the database that are not in the SQL file
foreach ($dbColumns as $colName => $colType) {
if (!isset($sqlColumns[$colName])) {
$dbComparison[] = [
'name' => "Extra column `$colName` in table `$tableName` not present in db.sql",
'status' => 'Extra Column',
];
}
}
}
// Check for tables in the database not present in the db.sql file
foreach ($dbTables as $tableName => $dbColumns) {
if (!isset($sqlTables[$tableName])) {
$dbComparison[] = [
'name' => "Extra table `$tableName` in database not present in db.sql",
'status' => 'Extra Table',
];
}
}
} else {
$dbComparison[] = [
'name' => 'db.sql file not found',
'status' => 'File Missing',
];
}
$mysqli->close();
?>
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-bug mr-2"></i>Debug</h3>
</div>
<div class="card-body">
<h2>Debugging</h2>
<ul>
<li>If you are experiencing a problem with ITFlow, this page should help you identify any configuration issues.</li>
<li>Note: You might also need to gather <a href="https://docs.itflow.org/gathering_logs#error_logs">error logs</a></li>
</ul>
<hr>
<div class="table-responsive">
<table class="table table-bordered mb-3">
<tr>
<th>ITFlow release version</th>
<th><?php echo APP_VERSION; ?></th>
</tr>
<tr>
<td>Current DB Version</td>
<td><?php echo CURRENT_DATABASE_VERSION; ?></td>
</tr>
<tr>
<td>Current Code Commit</td>
<td><?php echo $commitHash; ?></td>
</tr>
<tr>
<td>Current Branch</td>
<td><?php echo $gitBranch; ?></td>
</tr>
</table>
</div>
<!-- System Information Table -->
<h3>System Information</h3>
<table class="table table-sm table-bordered">
<tbody>
<?php foreach ($systemInfo as $info): ?>
<tr>
<td><?= htmlspecialchars($info['name']); ?></td>
<td><?= htmlspecialchars($info['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- PHP Extensions and Configuration Table -->
<h3 class="mt-3">PHP Extensions and Configuration</h3>
<div class="table-responsive">
<table class="table table-sm table-bordered">
<!-- PHP Extensions Section -->
<thead>
<tr class="table-secondary">
<th colspan="3">PHP Extensions</th>
</tr>
</thead>
<tbody>
<?php foreach ($phpExtensions as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<!-- PHP Configuration Section -->
<thead>
<tr class="table-secondary">
<th colspan="3">PHP Configuration</th>
</tr>
</thead>
<tbody>
<?php foreach ($phpConfig as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<thead>
<tr class="table-secondary">
<th colspan="3">Shell Commands</th>
</tr>
</thead>
<tbody>
<?php foreach ($shellCommands as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<thead>
<tr class="table-secondary">
<th colspan="3">SSL Checks</th>
</tr>
</thead>
<tbody>
<?php foreach ($sslChecks as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<thead>
<tr class="table-secondary">
<th colspan="3">Domain Checks</th>
</tr>
</thead>
<tbody>
<?php foreach ($domainChecks as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<!-- File Permissions Table -->
<thead>
<tr class="table-secondary">
<th colspan="3">File Permissions</th>
</tr>
</thead>
<tbody>
<?php foreach ($filePermissions as $check): ?>
<tr>
<td><?= htmlspecialchars($check['name']); ?></td>
<td class="text-center">
<?php if ($check['passed']): ?>
<i class="fas fa-check" style="color:green"></i>
<?php else: ?>
<i class="fas fa-times" style="color:red"></i>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($check['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Database Structure Comparison Table -->
<h3 class="mt-3">Database Structure Comparison</h3>
<div class="table-responsive">
<table class="table table-sm table-bordered">
<tbody>
<?php if (!empty($dbComparison)): ?>
<?php foreach ($dbComparison as $issue): ?>
<tr>
<td><?= htmlspecialchars($issue['name']); ?></td>
<td colspan="2"><?= htmlspecialchars($issue['status']); ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="3">No discrepancies found between the database and db.sql file.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- Uploads Directory Stats Table -->
<h3 class="mt-3">Uploads Directory Stats</h3>
<div class="table-responsive">
<table class="table table-sm table-bordered">
<tbody>
<?php foreach ($uploadsStats as $stat): ?>
<tr>
<td><?= htmlspecialchars($stat['name']); ?></td>
<td colspan="2"><?= htmlspecialchars($stat['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Database Stats Table -->
<h3 class="mt-3">Database Stats</h3>
<div class="table-responsive">
<table class="table table-sm table-bordered">
<tbody>
<?php foreach ($databaseStats as $stat): ?>
<tr>
<td><?= htmlspecialchars($stat['name']); ?></td>
<td colspan="2"><?= htmlspecialchars($stat['value']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Table Stats Table -->
<h3 class="mt-3">Table Stats</h3>
<div class="table-responsive">
<table class="table table-sm table-bordered">
<thead>
<tr>
<th>Table Name</th>
<th>Fields / Rows</th>
<th>Size (MB)</th>
</tr>
</thead>
<tbody>
<?php foreach ($tableDetails as $table): ?>
<tr>
<td><?= htmlspecialchars($table['name']); ?></td>
<td><?= htmlspecialchars("Fields: {$table['fields']}, Rows: {$table['rows']}"); ?></td>
<td><?= htmlspecialchars($table['size'] . ' MB'); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "../includes/footer.php";

158
admin/document_template.php Normal file
View File

@@ -0,0 +1,158 @@
<?php
// Default Column Sort by Filter
$sort = "document_template_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM document_templates
LEFT JOIN users ON document_template_created_by = user_id
WHERE user_name LIKE '%$q%' OR document_template_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 py-2">
<h3 class="card-title mt-2"><i class="fa fa-fw fa-file mr-2"></i>Document Templates</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addDocumentTemplateModal">
<i class="fas fa-plus mr-2"></i>New Template
</button>
</div>
</div>
<div class="card-body">
<form autocomplete="off">
<div class="input-group">
<input type="search" class="form-control " name="q" value="<?php if (isset($q)) { echo stripslashes(nullable_htmlentities($q)); } ?>" placeholder="Search templates">
<div class="input-group-append">
<button class="btn btn-secondary"><i class="fa fa-search"></i></button>
</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=document_template_name&order=<?php echo $disp; ?>">
Template Name <?php if ($sort == 'document_template_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=document_template_created_at&order=<?php echo $disp; ?>">
Created <?php if ($sort == 'document_template_created_at') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-secondary" href="?<?php echo $url_query_strings_sort; ?>&sort=document_template_updated_at&order=<?php echo $disp; ?>">
Updated <?php if ($sort == 'document_template_updated_at') { echo $order_icon; } ?>
</a>
</th>
<th class="text-center">
Action
</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$document_template_id = intval($row['document_template_id']);
$document_template_name = nullable_htmlentities($row['document_template_name']);
$document_template_description = nullable_htmlentities($row['document_template_description']);
$document_template_content = nullable_htmlentities($row['document_template_content']);
$document_template_created_by_name = nullable_htmlentities($row['user_name']);
$document_template_created_at = nullable_htmlentities($row['document_template_created_at']);
$document_template_updated_at = nullable_htmlentities($row['document_template_updated_at']);
?>
<tr>
<td>
<a class="text-bold" href="document_template_details.php?document_template_id=<?php echo $document_template_id; ?>"><i class="fas fa-fw fa-file-alt text-dark"></i> <?php echo $document_template_name; ?></a>
<div class="mt-1 text-secondary"><?php echo $document_template_description; ?></div>
</td>
<td>
<?php echo $document_template_created_at; ?>
<div class="text-secondary"><?php echo $document_template_created_by_name; ?></div>
</td>
<td><?php echo $document_template_updated_at; ?></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 ajax-modal" href="#"
data-modal-size="xl"
data-modal-url="modals/document_template/document_template_edit.php?id=<?= $document_template_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" href="post.php?delete_document_template=<?php echo $document_template_id; ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>
</td>
</tr>
<?php
}
?>
</tbody>
</table>
<br>
</div>
<?php require_once "../includes/filter_footer.php"; ?>
</div>
</div>
<?php require_once "modals/document_template/document_template_add.php"; ?>
<?php require_once "../includes/footer.php"; ?>
<script>
$(document).ready(function(){
$('#generateAIContent').on('click', function(){
var prompt = $('#aiPrompt').val().trim();
if(prompt === '') {
alert('Please enter a prompt.');
return;
}
$('#generateAIContent').prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> Generating...');
$.ajax({
url: 'post.php?ai_create_document_template', // The PHP script that calls the OpenAI API
method: 'POST',
data: { prompt: prompt },
dataType: 'html',
success: function(response) {
// Assuming you have exactly one TinyMCE instance on the page
// and it's targeting the .tinymce textarea:
tinymce.activeEditor.setContent(response);
},
error: function() {
alert('Error generating content. Please try again.');
},
complete: function() {
$('#generateAIContent').prop('disabled', false).html('<i class="fa fa-fw fa-magic mr-1"></i>Generate with AI');
}
});
});
});
</script>

View File

@@ -0,0 +1,64 @@
<?php
require_once "includes/inc_all_admin.php";
//Initialize the HTML Purifier to prevent XSS
require "../plugins/htmlpurifier/HTMLPurifier.standalone.php";
$purifier_config = HTMLPurifier_Config::createDefault();
$purifier_config->set('Cache.DefinitionImpl', null); // Disable cache by setting a non-existent directory or an invalid one
$purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]);
$purifier = new HTMLPurifier($purifier_config);
if (isset($_GET['document_template_id'])) {
$document_template_id = intval($_GET['document_template_id']);
}
$sql_document = mysqli_query($mysqli, "SELECT * FROM document_templates WHERE document_template_id = $document_template_id");
$row = mysqli_fetch_array($sql_document);
$document_template_name = nullable_htmlentities($row['document_template_name']);
$document_template_description = nullable_htmlentities($row['document_template_description']);
$document_template_content = $purifier->purify($row['document_template_content']);
$document_template_created_at = nullable_htmlentities($row['document_template_created_at']);
$document_template_updated_at = nullable_htmlentities($row['document_template_updated_at']);
?>
<ol class="breadcrumb d-print-none">
<li class="breadcrumb-item">
<a href="../">Home</a>
</li>
<li class="breadcrumb-item">
<a href="users.php">Admin</a>
</li>
<li class="breadcrumb-item">
<a href="document_template.php">Document Templates</a>
</li>
<li class="breadcrumb-item active"><i class="fas fa-file mr-2"></i><?php echo $document_template_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-file mr-2"></i><?php echo $document_template_name; ?></h3>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal"
data-modal-size="xl"
data-modal-url="modals/document_template/document_template_edit.php?id=<?= $document_template_id ?>">
<i class="fas fa-edit mr-2"></i>Edit
</button>
</div>
</div>
<div class="card-body prettyContent">
<?php echo $document_template_content; ?>
</div>
</div>
<script src="../js/pretty_content.js"></script>
<?php
require_once "../includes/footer.php";

View File

@@ -0,0 +1,58 @@
<?php
require_once "includes/inc_all_admin.php";
?>
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-fingerprint mr-2"></i>Identity Providers</h3>
</div>
<div class="card-body">
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<h4>Client Portal SSO via Microsoft Entra</h4>
<div class="form-group">
<label>Identity Provider <small class='text-secondary'>(Currently only works with Microsoft Entra ID/AAD)</small></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-fingerprint"></i></span>
</div>
<select class="form-control select2" readonly>
<option <?php if (empty($config_azure_client_id)) { echo "selected"; } ?>>Disabled</option>
<option <?php if ($config_azure_client_id) { echo "selected"; } ?>>Microsoft Entra</option>
<option>Google (WIP)</option>
<option>Custom SSO (WIP)</option>
</select>
</div>
</div>
<div class="form-group">
<label>MS Entra OAuth App (Client) 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="azure_client_id" placeholder="e721e3b6-01d6-50e8-7f22-c84d951a52e7" value="<?php echo nullable_htmlentities($config_azure_client_id); ?>">
</div>
</div>
<div class="form-group">
<label>MS Entra OAuth Secret</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="password" class="form-control" name="azure_client_secret" placeholder="Auto-generated from App Registration" value="<?php echo nullable_htmlentities($config_azure_client_secret); ?>" autocomplete="new-password">
</div>
</div>
<hr>
<button type="submit" name="edit_identity_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Save</button>
</form>
</div>
</div>
<?php require_once "../includes/footer.php";

View File

@@ -0,0 +1,16 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/config.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/functions.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/check_login.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/page_title.php';
if (!isset($session_is_admin) || !$session_is_admin) {
exit(WORDING_ROLECHECK_FAILED . "<br>Tell your admin: Your role does not have admin access.");
}
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/top_nav.php';
require_once 'includes/side_nav.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/inc_wrapper.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/inc_alert_feedback.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/filter_header.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/app_version.php';

316
admin/includes/side_nav.php Normal file
View File

@@ -0,0 +1,316 @@
<!-- Main Sidebar Container -->
<aside class="main-sidebar sidebar-dark-<?php echo nullable_htmlentities($config_theme); ?> d-print-none">
<a class="brand-link pb-1 mt-1" href="/agent/<?php echo $config_start_page ?>">
<p class="h6">
<i class="nav-icon fas fa-arrow-left ml-3 mr-2"></i>
<span class="brand-text">
Back | <strong>Administration</strong>
</span>
</p>
</a>
<!-- Sidebar -->
<div class="sidebar">
<!-- Sidebar Menu -->
<nav>
<ul class="nav nav-pills nav-sidebar flex-column mt-2" data-widget="treeview" data-accordion="false">
<!-- ACCESS Section -->
<li class="nav-item">
<a href="users.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "users.php") {echo "active";} ?>">
<i class="nav-icon fas fa-users"></i>
<p>Users</p>
</a>
</li>
<li class="nav-item">
<a href="roles.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "roles.php") {echo "active";} ?>">
<i class="nav-icon fas fa-user-shield"></i>
<p>Roles</p>
</a>
</li>
<li class="nav-item">
<a href="api_keys.php" class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == "api_keys.php") {echo "active";} ?>">
<i class="nav-icon fas fa-key"></i>
<p>API Keys</p>
</a>
</li>
<li class="nav-header">TAGS & CATEGORIES</li>
<li class="nav-item">
<a href="tag.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'tag.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-tags"></i>
<p>Tags</p>
</a>
</li>
<li class="nav-item">
<a href="category.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'category.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-list-ul"></i>
<p>Categories</p>
</a>
</li>
<?php if ($config_module_enable_accounting) { ?>
<li class="nav-item">
<a href="tax.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'tax.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-balance-scale"></i>
<p>Taxes</p>
</a>
</li>
<li class="nav-item">
<a href="payment_method.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'payment_method.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-hand-holding-usd"></i>
<p>Payment Methods</p>
</a>
</li>
<li class="nav-item">
<a href="payment_provider.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'payment_provider.php' ? 'active' : ''); ?>">
<i class="nav-icon far fa-credit-card"></i>
<p>Payment Providers</p>
</a>
</li>
<li class="nav-item">
<a href="saved_payment_method.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'saved_payment_method.php' ? 'active' : ''); ?>">
<i class="nav-icon far fa-credit-card"></i>
<p>Saved Payments</p>
</a>
</li>
<?php } ?>
<li class="nav-item">
<a href="ai_provider.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'ai_provider.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-robot"></i>
<p>AI Providers</p>
</a>
</li>
<li class="nav-item">
<a href="ai_model.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'ai_model.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-robot"></i>
<p>AI Models</p>
</a>
</li>
<?php if ($config_module_enable_ticketing) { ?>
<li class="nav-item">
<a href="ticket_status.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'ticket_status.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-info-circle"></i>
<p>Ticket Statuses</p>
</a>
</li>
<?php } ?>
<li class="nav-item">
<a href="custom_link.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'custom_link.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-external-link-alt"></i>
<p>Custom Links</p>
</a>
</li>
<?php if ($config_module_enable_itdoc) { ?>
<li class="nav-header">TEMPLATES</li>
<li class="nav-item">
<a href="project_template.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['project_template.php', '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="ticket_template.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['ticket_template.php', 'ticket_template_details.php']) ? 'active' : ''); ?>">
<i class="nav-icon fas fa-life-ring"></i>
<p>Ticket Templates</p>
</a>
</li>
<li class="nav-item">
<a href="vendor_template.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'vendor_template.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-building"></i>
<p>Vendor Templates</p>
</a>
</li>
<li class="nav-item">
<a href="software_template.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'software_template.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-rocket"></i>
<p>License Templates</p>
</a>
</li>
<li class="nav-item">
<a href="document_template.php" class="nav-link <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['document_template.php', 'document_template_details.php']) ? 'active' : ''); ?>">
<i class="nav-icon fas fa-file"></i>
<p>Document Templates</p>
</a>
</li>
<?php } ?>
<li class="nav-header">MAINTENANCE</li>
<li class="nav-item">
<a href="mail_queue.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'mail_queue.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-mail-bulk"></i>
<p>Mail Queue</p>
</a>
</li>
<li class="nav-item">
<a href="audit_log.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'audit_log.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-history"></i>
<p>Audit Logs</p>
</a>
</li>
<li class="nav-item">
<a href="app_log.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'app_log.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-history"></i>
<p>App Logs</p>
</a>
</li>
<li class="nav-item">
<a href="backup.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'backup.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-cloud-upload-alt"></i>
<p>Backup</p>
</a>
</li>
<li class="nav-item">
<a href="debug.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'debug.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-bug"></i>
<p>Debug</p>
</a>
</li>
<li class="nav-item">
<a href="update.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'update.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-download"></i>
<p>Update</p>
</a>
</li>
<!-- SETTINGS Section -->
<li class="nav-item has-treeview mt-2 <?php echo (in_array(basename($_SERVER['PHP_SELF']), ['settings_company.php', 'settings_localization.php', 'settings_theme.php', 'settings_security.php', 'settings_mail.php', 'settings_notification.php', 'settings_default.php', 'settings_invoice.php', 'settings_quote.php', 'settings_online_payment.php', 'settings_online_payment_clients.php', 'settings_project.php', 'settings_ticket.php', 'settings_ai.php', 'identity_provider.php', 'settings_telemetry.php', 'settings_module.php']) ? 'menu-open' : ''); ?>">
<a href="#" class="nav-link">
<p>
SETTINGS
<i class="right fas fa-angle-left"></i>
</p>
</a>
<ul class="nav nav-treeview">
<li class="nav-item">
<a href="settings_company.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_company.php' ? 'active' : ''); ?>">
<i class="nav-icon fa fa-briefcase"></i>
<p>Company Details</p>
</a>
</li>
<li class="nav-item">
<a href="settings_localization.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_localization.php' ? 'active' : ''); ?>">
<i class="nav-icon fa fa-globe"></i>
<p>Localization</p>
</a>
</li>
<li class="nav-item">
<a href="settings_theme.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_theme.php' ? 'active' : ''); ?>">
<i class="nav-icon fa fa-paint-brush"></i>
<p>Theme</p>
</a>
</li>
<li class="nav-item">
<a href="settings_security.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_security.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-shield-alt"></i>
<p>Security</p>
</a>
</li>
<li class="nav-item">
<a href="settings_mail.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_mail.php' ? 'active' : ''); ?>">
<i class="nav-icon far fa-envelope"></i>
<p>Mail</p>
</a>
</li>
<li class="nav-item">
<a href="settings_notification.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_notification.php' ? 'active' : ''); ?>">
<i class="nav-icon far fa-bell"></i>
<p>Notifications</p>
</a>
</li>
<li class="nav-item">
<a href="settings_default.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_default.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-cogs"></i>
<p>Defaults</p>
</a>
</li>
<?php if ($config_module_enable_accounting) { ?>
<li class="nav-item">
<a href="settings_invoice.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_invoice.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-file-invoice"></i>
<p>Invoice</p>
</a>
</li>
<li class="nav-item">
<a href="settings_quote.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_quote.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-comment-dollar"></i>
<p>Quote</p>
</a>
</li>
<?php } ?>
<?php if ($config_module_enable_ticketing) { ?>
<li class="nav-item">
<a href="settings_project.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_project.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-project-diagram"></i>
<p>Project</p>
</a>
</li>
<li class="nav-item">
<a href="settings_ticket.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_ticket.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-life-ring"></i>
<p>Ticket</p>
</a>
</li>
<?php } ?>
<!-- Currently the only integration is the client portal SSO -->
<?php if ($config_client_portal_enable) { ?>
<li class="nav-item">
<a href="identity_provider.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'identity_provider.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-fingerprint"></i>
<p>Identity Provider</p>
</a>
</li>
<?php } ?>
<li class="nav-item">
<a href="settings_telemetry.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_telemetry.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-satellite-dish"></i>
<p>Telemetry</p>
</a>
</li>
<li class="nav-item">
<a href="settings_module.php" class="nav-link <?php echo (basename($_SERVER['PHP_SELF']) == 'settings_module.php' ? 'active' : ''); ?>">
<i class="nav-icon fas fa-cube"></i>
<p>Modules</p>
</a>
</li>
</ul>
</li>
<?php
$sql_custom_links = mysqli_query($mysqli, "SELECT * FROM custom_links
WHERE custom_link_location = 4 AND custom_link_archived_at IS NULL
ORDER BY custom_link_order ASC, custom_link_name ASC"
);
while ($row = mysqli_fetch_array($sql_custom_links)) {
$custom_link_name = nullable_htmlentities($row['custom_link_name']);
$custom_link_uri = sanitize_url($row['custom_link_uri']);
$custom_link_icon = nullable_htmlentities($row['custom_link_icon']);
$custom_link_new_tab = intval($row['custom_link_new_tab']);
if ($custom_link_new_tab == 1) {
$target = "target='_blank' rel='noopener noreferrer'";
} else {
$target = "";
}
?>
<li class="nav-item">
<a href="<?php echo $custom_link_uri; ?>" <?php echo $target; ?> class="nav-link <?php if (basename($_SERVER["PHP_SELF"]) == basename($custom_link_uri)) { echo "active"; } ?>">
<i class="fas fa-<?php echo $custom_link_icon; ?> nav-icon"></i>
<p><?php echo $custom_link_name; ?></p>
<i class="fas fa-angle-right nav-icon float-right"></i>
</a>
</li>
<?php } ?>
</ul>
</nav>
<!-- /.sidebar-menu -->
<div class="mb-3"></div>
</div>
<!-- /.sidebar -->
</aside>

4
admin/index.php Normal file
View File

@@ -0,0 +1,4 @@
<?php
header('Location: users.php');

212
admin/mail_queue.php Normal file
View File

@@ -0,0 +1,212 @@
<?php
// Default Column Sortby Filter
$sort = "email_id";
$order = "DESC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query(
$mysqli,
"SELECT SQL_CALC_FOUND_ROWS * FROM email_queue
WHERE (email_id LIKE '%$q%' OR email_from LIKE '%$q%' OR email_from_name LIKE '%$q%' OR email_recipient LIKE '%$q%' OR email_recipient_name LIKE '%$q%' OR email_subject LIKE '%$q%')
AND DATE(email_queued_at) BETWEEN '$dtf' AND '$dtt'
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-3">
<h3 class="card-title"><i class="fas fa-fw fa-mail-bulk mr-2"></i>Email Queue</h3>
</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 mail queue">
<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 class="dropdown float-right" 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"
type="submit" form="bulkActions" name="bulk_cancel_emails">
<i class="fas fa-fw fa-ban mr-2"></i>Cancel
</button>
<div class="dropdown-divider"></div>
<button class="dropdown-item text-danger text-bold"
type="submit" form="bulkActions" name="bulk_delete_emails">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</button>
</div>
</div>
</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>
<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-sm table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows[0] == 0) { echo "d-none"; } ?>">
<tr>
<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-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=email_queued_at&order=<?php echo $disp; ?>">
Queued <?php if ($sort == 'email_queued_at') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=email_from&order=<?php echo $disp; ?>">
From <?php if ($sort == 'email_from') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=email_recipient&order=<?php echo $disp; ?>">
To <?php if ($sort == 'email_recipient') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=email_subject&order=<?php echo $disp; ?>">
Subject <?php if ($sort == 'email_subject') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=email_status&order=<?php echo $disp; ?>">
Status <?php if ($sort == 'email_status') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=email_attempts&order=<?php echo $disp; ?>">
Attempts <?php if ($sort == 'email_attempts') { echo $order_icon; } ?>
</a>
</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$email_id = intval($row['email_id']);
$email_from = nullable_htmlentities($row['email_from']);
$email_from_name = nullable_htmlentities($row['email_from_name']);
$email_recipient = nullable_htmlentities($row['email_recipient']);
$email_recipient_name = nullable_htmlentities($row['email_recipient_name']);
$email_subject = nullable_htmlentities($row['email_subject']);
$email_attempts = intval($row['email_attempts']);
$email_queued_at = nullable_htmlentities($row['email_queued_at']);
$email_failed_at = nullable_htmlentities($row['email_failed_at']);
$email_sent_at = nullable_htmlentities($row['email_sent_at']);
$email_status = intval($row['email_status']);
if ($email_status == 0) {
$email_status_display = "<div class='text-primary'>Queued</div>";
} elseif($email_status == 1) {
$email_status_display = "<div class='text-warning'>Sending</div>";
} elseif($email_status == 2) {
$email_status_display = "<div class='text-danger'>Failed</div><small class='text-secondary'>$email_failed_at</small>";
} else {
$email_status_display = "<div class='text-success'>Sent</div><small class='text-secondary'>$email_sent_at</small>";
}
?>
<tr>
<td class="pr-0 bg-light">
<?php if ($email_status !== 3) { ?>
<div class="form-check">
<input class="form-check-input bulk-select" type="checkbox" name="email_ids[]" value="<?php echo $email_id ?>">
</div>
<?php } ?>
</td>
<td><?php echo $email_queued_at; ?></td>
<td><?php echo "$email_from<br><small class='text-secondary'>$email_from_name</small>"?></td>
<td><?php echo "$email_recipient<br><small class='text-secondary'>$email_recipient_name</small>"?></td>
<td><?php echo $email_subject; ?></td>
<td><?php echo $email_status_display; ?></td>
<td><?php echo $email_attempts; ?></td>
<td class="text-center">
<a class="btn btn-sm btn-secondary ajax-modal" href="#"
data-modal-size="lg"
data-modal-url="modals/mail_queue/mail_queue_message_view.php?id=<?= $email_id ?>">
<i class="fas fa-fw fa-eye"></i>
</a>
<!-- Show force resend if all retries have failed -->
<?php if ($email_status == 2 && $email_attempts > 3) { ?>
<a class="btn btn-sm btn-success" href="post.php?send_failed_mail=<?php echo $email_id; ?>"><i class="fas fa-fw fa-paper-plane"></i></a>
<?php } ?>
<!-- Allow cancelling a message if it hasn't yet been picked up (e.g. stuck/bugged) -->
<?php if ($email_status !== 3) { ?>
<a class="btn btn-sm btn-danger confirm-link" href="post.php?cancel_mail=<?php echo $email_id; ?>"><i class="fas fa-fw fa-trash"></i></a>
<?php } ?>
</td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
</form>
<?php require_once "../includes/filter_footer.php"; ?>
</div>
</div>
<script src="../js/bulk_actions.js"></script>
<?php
require_once "../includes/footer.php";

View File

@@ -0,0 +1,73 @@
<div class="form-group">
<div class="modal" id="addAIModelModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>Add AI Model</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="form-group">
<label>Provider <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-robot"></i></span>
</div>
<select class="form-control select2" name="provider" required>
<option value="">- Select an AI Provider -</option>
<?php
$sql_ai_providers = mysqli_query($mysqli, "SELECT * FROM ai_providers");
while ($row = mysqli_fetch_array($sql_ai_providers)) {
$ai_provider_id = intval($row['ai_provider_id']);
$ai_provider_name = nullable_htmlentities($row['ai_provider_name']);
?>
<option value="<?php echo $ai_provider_id; ?>"><?php echo $ai_provider_name; ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Model 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-robot"></i></span>
</div>
<input type="text" class="form-control" name="model" placeholder="ex gpt-4">
</div>
</div>
<div class="form-group">
<label>Use Case <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-th-list"></i></span>
</div>
<select class="form-control select2" name="use_case">
<option>General</option>
<option>Tickets</option>
<option>Documentation</option>
</select>
</div>
</div>
<div class="form-group">
<textarea class="form-control" rows="8" name="prompt" placeholder="Enter a model prompt:"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_ai_model" 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>

View File

@@ -0,0 +1,90 @@
<?php
require_once '../../../includes/modal_header.php';
$model_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM ai_models WHERE ai_model_id = $model_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$ai_model_ai_provider_id = intval($row['ai_model_ai_provider_id']);
$model_id = intval($row['ai_model_id']);
$model_name = nullable_htmlentities($row['ai_model_name']);
$use_case = nullable_htmlentities($row['ai_model_use_case']);
$prompt = nullable_htmlentities($row['ai_model_prompt']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>Editing: <strong><?php echo $model_name; ?></strong></h5>
<button type="button" class="close text-light" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="model_id" value="<?php echo $model_id; ?>">
<div class="modal-body">
<div class="form-group">
<label>Provider <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-robot"></i></span>
</div>
<select class="form-control select2" name="provider" required>
<option value="">- Select an AI Provider -</option>
<?php
$sql_ai_providers = mysqli_query($mysqli, "SELECT * FROM ai_providers");
while ($row = mysqli_fetch_array($sql_ai_providers)) {
$ai_provider_id = intval($row['ai_provider_id']);
$ai_provider_name = nullable_htmlentities($row['ai_provider_name']);
?>
<option <?php if ($ai_provider_id = $ai_model_ai_provider_id) { echo "selected"; } ?> value="<?php echo $ai_provider_id; ?>"><?php echo $ai_provider_name; ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Model 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-robot"></i></span>
</div>
<input type="text" class="form-control" name="model" value="<?php echo $model_name; ?>" placeholder="ex gpt-4">
</div>
</div>
<div class="form-group">
<label>Use Case <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-th-list"></i></span>
</div>
<select class="form-control select2" name="use_case">
<option <?php if ($use_case == 'General') { echo "selected"; } ?>>General</option>
<option <?php if ($use_case == 'Tickets') { echo "selected"; } ?>>Tickets</option>
<option <?php if ($use_case == 'Documentation') { echo "selected"; } ?>>Documentation</option>
</select>
</div>
</div>
<div class="form-group">
<textarea class="form-control" rows="8" name="prompt" placeholder="Enter a model prompt:"><?php echo $prompt; ?></textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ai_model" 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>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,54 @@
<div class="form-group">
<div class="modal" id="addAIProviderModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>New AI Provider</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="form-group">
<label>Provider 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-robot"></i></span>
</div>
<input type="text" class="form-control" name="provider" placeholder="ex OpenAI">
</div>
</div>
<div class="form-group">
<label>URL <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-globe"></i></span>
</div>
<input type="url" class="form-control" name="url" placeholder="ex https://ai.company.ext/api">
</div>
</div>
<div class="form-group">
<label>API Key</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="api_key" placeholder="Enter API key here">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_ai_provider" 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>

View File

@@ -0,0 +1,69 @@
<?php
require_once '../../../includes/modal_header.php';
$provider_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM ai_providers WHERE ai_provider_id = $provider_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$provider_name = nullable_htmlentities($row['ai_provider_name']);
$url = nullable_htmlentities($row['ai_provider_api_url']);
$key = nullable_htmlentities($row['ai_provider_api_key']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-robot mr-2"></i>Editing: <strong><?php echo $provider_name; ?></strong></h5>
<button type="button" class="close text-light" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="provider_id" value="<?php echo $provider_id; ?>">
<div class="modal-body">
<div class="form-group">
<label>Provider 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-robot"></i></span>
</div>
<input type="text" class="form-control" name="provider" value="<?php echo $provider_name; ?>" placeholder="ex OpenAI">
</div>
</div>
<div class="form-group">
<label>URL <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-globe"></i></span>
</div>
<input type="url" class="form-control" name="url" value="<?php echo $url; ?>" placeholder="ex https://ai.company.ext/api">
</div>
</div>
<div class="form-group">
<label>API Key</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="api_key" value="<?php echo $key; ?>" placeholder="Enter API key here">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ai_provider" 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>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,121 @@
<?php
$key = randomString(156);
$decryptPW = randomString(160);
?>
<div class="modal" id="addApiKeyModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-key mr-2"></i>New Key</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body">
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-toggle="pill" href="#pills-api-details">Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="pill" href="#pills-api-keys">Keys</a>
</li>
</ul>
<hr>
<div class="tab-content">
<div class="tab-pane fade show active" id="pills-api-details">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="key" value="<?php echo $key ?>">
<input type="hidden" name="password" value="<?php echo $decryptPW ?>">
<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-sticky-note"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Key Name" maxlength="255" required autofocus>
</div>
</div>
<div class="form-group">
<label>Expiration Date <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-calendar"></i></span>
</div>
<input type="date" class="form-control" name="expire" min="<?php echo date('Y-m-d')?>" max="2999-12-31" required>
</div>
</div>
<div class="form-group">
<label>Client Access <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>
<select class="form-control select2" name="client" required>
<option value="0"> ALL CLIENTS </option>
<?php
$sql = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC");
while ($row = mysqli_fetch_array($sql)) {
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']); ?>
<option value="<?php echo $client_id; ?>"><?php echo "$client_name (Client ID: $client_id)"; ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
<div class="tab-pane fade" id="pills-api-keys">
<div class="form-group">
<label>API Key <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-key"></i></span>
</div>
<input type="text" class="form-control" value="<?php echo $key ?>" required disabled>
<div class="input-group-append">
<button class="btn btn-default clipboardjs" type="button" data-clipboard-text="<?php echo $key; ?>"><i class="fa fa-fw fa-copy"></i></button>
</div>
</div>
</div>
<div class="form-group">
<label>Login credential decryption 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-unlock-alt"></i></span>
</div>
<input type="text" class="form-control" value="<?php echo $decryptPW ?>" required disabled>
<div class="input-group-append">
<button class="btn btn-default clipboardjs" type="button" data-clipboard-text="<?php echo $decryptPW; ?>"><i class="fa fa-fw fa-copy"></i></button>
</div>
</div>
</div>
<br>
<div class="form-group">
<label>I have made a copy of the key(s)<strong class="text-danger">*</strong></label>
<div class="input-group">
<div class="input-group-prepend">
<input type="checkbox" name="ack" value="1" required>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_api_key" 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="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,48 @@
<?php
require_once '../../../includes/modal_header.php';
$category = nullable_htmlentities($_GET['category']);
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-list-ul mr-2"></i>New <strong><?= nullable_htmlentities($category) ?></strong> Category</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="type" value="<?php echo ($category); ?>">
<div class="modal-body">
<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-list-ul"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Category name" maxlength="200" required autofocus>
</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" required>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_category" 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>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,56 @@
<?php
require_once '../../../includes/modal_header.php';
$category_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_id = $category_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$category_name = nullable_htmlentities($row['category_name']);
$category_color = nullable_htmlentities($row['category_color']);
$category_type = nullable_htmlentities($row['category_type']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-list-ul mr-2"></i>Editing category: <strong><?php echo $category_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="category_id" value="<?php echo $category_id; ?>">
<input type="hidden" name="type" value="<?php echo $category_type; ?>">
<div class="modal-body">
<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-list-ul"></i></span>
</div>
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $category_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 $category_color; ?>" required>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_category" 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>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,41 @@
<div class="modal" id="createCustomFieldModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-th-list mr-2"></i>Create <?php echo nullable_htmlentities($table); ?> field</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="table" value="<?php echo nullable_htmlentities($table); ?>">
<div class="modal-body">
<div class="form-group">
<label>Label <strong class="text-danger">*</strong></label>
<input type="text" class="form-control" name="label" placeholder="Enter a custom field label" maxlength="255" required autofocus>
</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-th"></i></span>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Select a field type -</option>
<option>Text</option>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="create_custom_field" 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>

View File

@@ -0,0 +1,40 @@
<div class="modal" id="editCustomFieldModal<?php echo $custom_field_id; ?>" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-th-list mr-2"></i>Editing custom field: <strong><?php echo $custom_field_label; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="custom_field_id" value="<?php echo $custom_field_id; ?>">
<div class="modal-body">
<div class="form-group">
<label>Label <strong class="text-danger">*</strong></label>
<input type="text" class="form-control" name="label" maxlength="255" value="<?php echo $custom_field_label; ?>" required>
</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-th"></i></span>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Select a field type -</option>
<option <?php if ($custom_field_type == 'text') { echo "selected"; } ?> value="text">Text</option>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_custom_field" 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>

View File

@@ -0,0 +1,83 @@
<div class="modal" id="addLinkModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-external-link-alt mr-2"></i>New Custom Link</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body">
<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-list-ul"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Link name" maxlength="200" required autofocus>
</div>
</div>
<div class="form-group">
<label>Order</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
</div>
<input type="number" class="form-control" name="order" placeholder="Leave blank for no order">
</div>
</div>
<div class="form-group">
<label>URI <strong class="text-danger">*</strong></label> / <span class="text-secondary">Open New Tab</span>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-external-link-alt"></i></span>
</div>
<input type="text" class="form-control" name="uri" placeholder="Enter Link" maxlength="500" required>
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="new_tab" value="1">
</div>
</div>
</div>
</div>
<div class="form-group">
<label>Icon</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
</div>
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake" maxlength="200">
</div>
</div>
<div class="form-group">
<label>Location <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-home"></i></span>
</div>
<select class="form-control select2" name="location" required>
<option value="1">Main Side Nav</option>
<option value="2">Top Nav (Icon Required)</option>
<option value="3">Client Portal Nav</option>
<option value="4">Admin Nav</option>
<option value="5">Reports Nav</option>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_custom_link" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,101 @@
<?php
require_once '../../../includes/modal_header.php';
$custom_link_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM custom_links WHERE custom_link_id = $custom_link_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$custom_link_name = nullable_htmlentities($row['custom_link_name']);
$custom_link_uri = nullable_htmlentities($row['custom_link_uri']);
$custom_link_icon = nullable_htmlentities($row['custom_link_icon']);
$custom_link_new_tab = intval($row['custom_link_new_tab']);
$custom_link_order = intval($row['custom_link_order']);
$custom_link_location = intval($row['custom_link_location']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-external-link-alt mr-2"></i>Editing link: <strong><?php echo $custom_link_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="custom_link_id" value="<?php echo $custom_link_id; ?>">
<div class="modal-body">
<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-list-ul"></i></span>
</div>
<input type="text" class="form-control" name="name" value="<?php echo $custom_link_name; ?>" maxlength="200" required>
</div>
</div>
<div class="form-group">
<label>Order</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
</div>
<input type="number" class="form-control" name="order" placeholder="Leave blank for no order" value="<?php echo $custom_link_order; ?>">
</div>
</div>
<div class="form-group">
<label>URI <strong class="text-danger">*</strong></label> / <span class="text-secondary">Open New Tab</span>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-external-link-alt"></i></span>
</div>
<input type="text" class="form-control" name="uri" placeholder="Enter Link" maxlength="500" value="<?php echo $custom_link_uri; ?>" required>
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="new_tab" value="1" <?php if ($custom_link_new_tab == 1) { echo "checked"; } ?>>
</div>
</div>
</div>
</div>
<div class="form-group">
<label>Icon</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
</div>
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake" maxlength="200" value="<?php echo $custom_link_icon; ?>">
</div>
</div>
<div class="form-group">
<label>Location <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-home"></i></span>
</div>
<select class="form-control select2" name="location" required>
<option value="1" <?php if ($custom_link_location === 1) { echo "selected"; } ?> >Main Side Nav</option>
<option value="2" <?php if ($custom_link_location === 2) { echo "selected"; } ?> >Top Nav (Icon Required)</option>
<option value="3" <?php if ($custom_link_location === 3) { echo "selected"; } ?> >Client Portal Nav</option>
<option value="4" <?php if ($custom_link_location === 4) { echo "selected"; } ?> >Admin Nav</option>
<option value="5" <?php if ($custom_link_location === 5) { echo "selected"; } ?> >Reports Nav</option>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_custom_link" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,49 @@
<div class="modal" id="addDocumentTemplateModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-file-alt mr-2"></i>Creating Document Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body">
<div class="form-group">
<input type="text" class="form-control" name="name" placeholder="Template name" maxlength="200">
</div>
<div class="form-group">
<label>Enter a prompt for the type of IT documentation you want to generate:</label>
<div class="input-group mb-3">
<input type="text" class="form-control" id="aiPrompt" placeholder="e.g. 'A network troubleshooting guide for junior technicians'">
<div class="input-group-append">
<button class="btn btn-info" type="button" id="generateAIContent">
<i class="fa fa-fw fa-magic mr-1"></i>Generate with AI
</button>
</div>
</div>
</div>
<!-- TinyMCE Content -->
<div class="form-group">
<textarea class="form-control tinymce" name="content"></textarea>
</div>
<div class="form-group">
<input type="text" class="form-control" name="description" placeholder="Enter a short summary">
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_document_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>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,47 @@
<?php
require_once '../../../includes/modal_header.php';
$document_template_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM document_templates WHERE document_template_id = $document_template_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$document_template_name = nullable_htmlentities($row['document_template_name']);
$document_template_description = nullable_htmlentities($row['document_template_description']);
$document_template_content = nullable_htmlentities($row['document_template_content']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-file-alt mr-2"></i>Editing template: <strong><?php echo $document_template_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="document_template_id" value="<?php echo $document_template_id; ?>">
<div class="modal-body">
<div class="form-group">
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $document_template_name; ?>" placeholder="Name" required>
</div>
<div class="form-group">
<textarea class="form-control tinymce" name="content"><?php echo $document_template_content; ?></textarea>
</div>
<div class="form-group">
<input type="text" class="form-control" name="description" value="<?php echo $document_template_description; ?>" placeholder="Short summary">
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_document_template" 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>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,79 @@
<?php
require_once '../../../includes/modal_header.php';
if (!isset($session_is_admin) || !$session_is_admin) {
exit(WORDING_ROLECHECK_FAILED . "<br>Tell your admin: Your role does not have admin access.");
}
$email_id = intval($_GET['id']);
//Initialize the HTML Purifier to prevent XSS
require "../../../plugins/htmlpurifier/HTMLPurifier.standalone.php";
$purifier_config = HTMLPurifier_Config::createDefault();
$purifier_config->set('Cache.DefinitionImpl', null); // Disable cache by setting a non-existent directory or an invalid one
$purifier_config->set('URI.AllowedSchemes', ['data' => true, 'src' => true, 'http' => true, 'https' => true]);
$purifier = new HTMLPurifier($purifier_config);
$sql = mysqli_query($mysqli, "SELECT * FROM email_queue WHERE email_id = $email_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$email_from = nullable_htmlentities($row['email_from']);
$email_from_name = nullable_htmlentities($row['email_from_name']);
$email_recipient = nullable_htmlentities($row['email_recipient']);
$email_recipient_name = nullable_htmlentities($row['email_recipient_name']);
$email_subject = nullable_htmlentities($row['email_subject']);
$email_content = $purifier->purify($row['email_content']);
$email_attempts = intval($row['email_attempts']);
$email_queued_at = nullable_htmlentities($row['email_queued_at']);
$email_failed_at = nullable_htmlentities($row['email_failed_at']);
$email_sent_at = nullable_htmlentities($row['email_sent_at']);
$email_status = intval($row['email_status']);
if ($email_status == 0) {
$email_status_display = "<div class='text-primary'>Queued</div>";
} elseif($email_status == 1) {
$email_status_display = "<div class='text-warning'>Sending</div>";
} elseif($email_status == 2) {
$email_status_display = "<div class='text-danger'>Failed</div><small class='text-secondary'>$email_failed_at</small>";
} else {
$email_status_display = "<div class='text-success'>Sent</div><small class='text-secondary'>$email_sent_at</small>";
}
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class='fas fa-fw fa-envelope-open mr-2'></i><strong><?php echo $email_subject; ?></strong></h5>
<button type="button" class="close text-light" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-1">
<span class="text-secondary">From:</span>
</div>
<div class="col-md-10">
<?php echo "<strong>$email_from_name</strong> ($email_from)"; ?>
</div>
</div>
<hr class="my-2">
<div class="row">
<div class="col-md-1">
<span class="text-secondary">To:</span>
</div>
<div class="col-md-10">
<?php echo "<strong>$email_recipient_name</strong> ($email_recipient)"; ?>
</div>
</div>
<hr class="my-2">
<div class="prettyContent">
<?php echo $email_content; ?>
</div>
</div>
<script src="../../js/pretty_content.js"></script>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,37 @@
<div class="modal" id="addPaymentMethodModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Creating: <strong>Payment Method</strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<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-credit-card"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Payment method name" maxlength="200" required autofocus>
</div>
</div>
<div class="form-group">
<textarea class="form-control" rows="3" name="description" placeholder="Enter a description..."></textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_payment_method" 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>

View File

@@ -0,0 +1,51 @@
<?php
require_once '../../../includes/modal_header.php';
$payment_method_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM payment_methods WHERE payment_method_id = $payment_method_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$payment_method_id = intval($row['payment_method_id']);
$payment_method_name = nullable_htmlentities($row['payment_method_name']);
$payment_method_description = nullable_htmlentities($row['payment_method_description']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Editing: <strong><?php echo $payment_method_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<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-credit-card"></i></span>
</div>
<input type="text" class="form-control" name="name" value="<?php echo $payment_method_name; ?>" placeholder="Payment method name" maxlength="200" required autofocus>
</div>
</div>
<div class="form-group">
<textarea class="form-control" rows="3" name="description" placeholder="Enter a description..."><?php echo $payment_method_description; ?></textarea>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_payment_method" 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>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,103 @@
<div class="form-group">
<div class="modal" id="addPaymentProviderModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Add Payment Provider</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="alert alert-info">
An income account named after the provider will always be created and used for income of paid invoices.<br>
If "Enable Expense" option is enabled, a matching vendor will also be automatically created for expense tracking. Additionally, an expense category named "Payment Processing" will be created.
</div>
<div class="form-group">
<label>Provider <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-credit-card"></i></span>
</div>
<select class="form-control select2" name="provider">
<option>Stripe</option>
</select>
</div>
</div>
<div class="form-group">
<label>Publishable key <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-eye"></i></span>
</div>
<input type="text" class="form-control" name="public_key" placeholder="Publishable API Key (pk_...)">
</div>
</div>
<div class="form-group">
<label>Secret key <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-key"></i></span>
</div>
<input type="text" class="form-control" name="private_key" placeholder="Secret API Key (sk_...)">
</div>
</div>
<div class="form-group">
<label>Threshold</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
</div>
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,2}" name="threshold" placeholder="1000.00">
</div>
<small class="form-text text-muted">Will not show as an option at Checkout if invoice amount is above this number, 0 disables the threshold check.</small>
</div>
<hr>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="enable_expense" checked value="1" id="enableExpenseSwitch">
<label class="custom-control-label" for="enableExpenseSwitch">Enable Expense</label>
</div>
</div>
<div class="form-group">
<label>Percentage Fee to expense</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-percent"></i></span>
</div>
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,2}" name="percentage_fee" placeholder="Enter Percentage">
</div>
<small class="form-text text-muted">See <a href="https://stripe.com/pricing" target="_blank">here <i class="fas fa-fw fa-external-link-alt"></i></a> for the latest Stripe Fees.</small>
</div>
<div class="form-group">
<label>Flat Fee to expense</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
</div>
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,3}" name="flat_fee" placeholder="0.030">
</div>
<small class="form-text text-muted">See <a href="https://stripe.com/pricing" target="_blank">here <i class="fas fa-fw fa-external-link-alt"></i></a> for the latest Stripe Fees.</small>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_payment_provider" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Add</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>

View File

@@ -0,0 +1,107 @@
<?php
require_once '../../../includes/modal_header.php';
$provider_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM payment_providers WHERE payment_provider_id = $provider_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$provider_name = nullable_htmlentities($row['payment_provider_name']);
$public_key = nullable_htmlentities($row['payment_provider_public_key']);
$private_key = nullable_htmlentities($row['payment_provider_private_key']);
$account_id = nullable_htmlentities($row['payment_provider_account']);
$threshold = floatval($row['payment_provider_threshold']);
$vendor_id = nullable_htmlentities($row['payment_provider_expense_vendor']);
$category_id = nullable_htmlentities($row['payment_provider_expense_category']);
$percent_fee = floatval($row['payment_provider_expense_percentage_fee']) * 100;
$flat_fee = floatval($row['payment_provider_expense_flat_fee']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Editing: <strong><?php echo $provider_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="provider_id" value="<?php echo $provider_id; ?>">
<div class="modal-body">
<div class="form-group">
<label>Publishable key <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-eye"></i></span>
</div>
<input type="text" class="form-control" name="public_key" placeholder="Publishable API Key (pk_...)" value="<?php echo $public_key; ?>">
</div>
</div>
<div class="form-group">
<label>Secret key <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-key"></i></span>
</div>
<input type="text" class="form-control" name="private_key" placeholder="Secret API Key (sk_...)" value="<?php echo $private_key; ?>">
</div>
</div>
<div class="form-group">
<label>Threshold</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
</div>
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,2}" name="threshold" placeholder="1000.00" value="<?php echo $threshold; ?>">
</div>
<small class="form-text text-muted">Will not show as an option at Checkout if above this number</small>
</div>
<hr>
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="enable_expense" <?php if ($vendor_id) { echo "checked"; } ?> value="1" id="enableEditExpenseSwitch">
<label class="custom-control-label" for="enableEditExpenseSwitch">Enable Expense</label>
</div>
<small>(Category: Payment Processing -- Vendor: <?php echo $provider_name; ?></small>
</div>
<div class="form-group">
<label>Percentage Fee to expense</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-percent"></i></span>
</div>
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,2}" name="percentage_fee" value="<?php echo $percent_fee; ?>" placeholder="Enter Percentage">
</div>
<small class="form-text text-muted">See <a href="https://stripe.com/pricing" target="_blank">here <i class="fas fa-fw fa-external-link-alt"></i></a> for the latest Stripe Fees.</small>
</div>
<div class="form-group">
<label>Flat Fee to expense</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
</div>
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,3}" name="flat_fee" value="<?php echo $flat_fee; ?>" placeholder="0.030">
</div>
<small class="form-text text-muted">See <a href="https://stripe.com/pricing" target="_blank">here <i class="fas fa-fw fa-external-link-alt"></i></a> for the latest Stripe Fees.</small>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_payment_provider" 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>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,41 @@
<div class="modal" id="addProjectTemplateModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-project-diagram mr-2"></i>Creating Project Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body">
<div class="form-group">
<label>Project Template 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-project-diagram"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Project Template Name" maxlength="255" 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">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_project_template" class="btn btn-primary text-bold"><i class="fas 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>

View File

@@ -0,0 +1,44 @@
<div class="modal" id="editProjectTemplateModal<?php echo $project_template_id; ?>" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-project-diagram mr-2"></i>Editing Project Template: <strong><?php echo $project_template_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="project_template_id" value="<?php echo $project_template_id; ?>">
<div class="modal-body">
<div class="form-group">
<label>Project Template 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-project-diagram"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Project Template Name" maxlength="255" value="<?php echo $project_template_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" value="<?php echo $project_template_description; ?>">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_project_template" class="btn btn-primary text-bold"><i class="fas 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>

View File

@@ -0,0 +1,64 @@
<div class="modal" id="addProjectTemplateTicketTemplateModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-life-ring mr-2"></i>Adding Ticket Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="project_template_id" value="<?php echo $project_template_id; ?>">
<div class="modal-body">
<div class="form-group">
<label>Ticket Template <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-life-ring"></i></span>
</div>
<select class="form-control select2" name="ticket_template_id" required>
<option value="">- Select a Ticket Template -</option>
<?php
$sql_ticket_templates_select = mysqli_query($mysqli, "SELECT ticket_template_id, ticket_template_name FROM ticket_templates
WHERE ticket_template_id NOT IN (
SELECT ticket_template_id FROM project_template_ticket_templates
WHERE project_template_id = $project_template_id
)
AND ticket_template_archived_at IS NULL
ORDER BY ticket_template_name ASC"
);
while ($row = mysqli_fetch_array($sql_ticket_templates_select)) {
$ticket_template_id_select = intval($row['ticket_template_id']);
$ticket_template_name_select = nullable_htmlentities($row['ticket_template_name']);
?>
<option value="<?php echo $ticket_template_id_select; ?>"><?php echo $ticket_template_name_select; ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="form-group">
<label>Order</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
</div>
<input type="text" class="form-control" name="order" value="1">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_ticket_template_to_project_template" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Add</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,58 @@
<div class="modal" id="addRoleModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-shield mr-2"></i>Add new role</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="tab-content">
<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-shield"></i></span>
</div>
<input type="text" class="form-control" name="role_name" placeholder="Role Name" maxlength="200" required>
</div>
</div>
<div class="form-group">
<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-chevron-right"></i></span>
</div>
<input type="text" class="form-control" name="role_description" placeholder="Role Description" maxlength="200" required>
</div>
</div>
<div class="form-group">
<label>Admin Access <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-tools"></i></span>
</div>
<select class="form-control select2" name="role_is_admin" required>
<option value="0">No - edit after creation to set permissions</option>
<option value="1">Yes - this role should have full admin access</option>
</select>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_role" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,153 @@
<?php
require_once '../../../includes/modal_header.php';
$role_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM user_roles WHERE role_id = $role_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$role_name = nullable_htmlentities($row['role_name']);
$role_description = nullable_htmlentities($row['role_description']);
$role_admin = intval($row['role_is_admin']);
// Count number of users that have each role
$sql_role_user_count = mysqli_query($mysqli, "SELECT COUNT(user_id) FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
$role_user_count = mysqli_fetch_row($sql_role_user_count)[0];
$sql_users = mysqli_query($mysqli, "SELECT * FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
// Initialize an empty array to hold user names
$user_names = [];
// Fetch each row and store the user_name in the array
while($row = mysqli_fetch_assoc($sql_users)) {
$user_names[] = nullable_htmlentities($row['user_name']);
}
// Convert the array of user names to a comma-separated string
$user_names_string = implode(",", $user_names) ;
if (empty($user_names_string)) {
$user_names_string = "-";
}
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-shield mr-2"></i>Editing role:
<strong><?php echo $role_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="role_id" value="<?php echo $role_id; ?>">
<div class="modal-body">
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-toggle="pill" href="#pills-role-details<?php echo $role_id; ?>">Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="pill" href="#pills-role-access<?php echo $role_id; ?>">Access</a>
</li>
</ul>
<hr>
<div class="tab-content">
<div class="tab-pane fade show active" id="pills-role-details<?php echo $role_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-user-shield"></i></span>
</div>
<input type="text" class="form-control" name="role_name" placeholder="Role Name" maxlength="200" value="<?php echo $role_name; ?>" required>
</div>
</div>
<div class="form-group">
<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-chevron-right"></i></span>
</div>
<input type="text" class="form-control" name="role_description" placeholder="Role Description" maxlength="200" value="<?php echo $role_description; ?>" required>
</div>
</div>
<div class="form-group">
<label>Admin Access <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-tools"></i></span>
</div>
<select class="form-control select2" name="role_is_admin" required>
<option value="1" <?php if ($role_admin) { echo 'selected'; } ?> >Yes - this role should have full admin access</option>
<option value="0" <?php if (!$role_admin) { echo 'selected'; } ?>>No - use permissions on the next tab</option>
</select>
</div>
</div>
</div>
<div class="tab-pane fade" id="pills-role-access<?php echo $role_id; ?>">
<?php if ($role_admin) { ?>
<div class="alert alert-warning"><strong>Module permissions do not apply to Admins.</strong></div>
<?php } ?>
<?php
// Enumerate modules
$sql_modules = mysqli_query($mysqli, "SELECT * FROM modules");
while ($row_modules = mysqli_fetch_array($sql_modules)) {
$module_id = intval($row_modules['module_id']);
$module_name = nullable_htmlentities($row_modules['module_name']);
$module_name_display = ucfirst(str_replace("module_","",$module_name));
$module_description = nullable_htmlentities($row_modules['module_description']);
// Get permission level for module
$module_permission_row = mysqli_fetch_array(mysqli_query($mysqli, "SELECT user_role_permission_level FROM user_role_permissions WHERE module_id = $module_id AND user_role_id = $role_id LIMIT 1"));
$module_permission = 0;
if ($module_permission_row) {
$module_permission = $module_permission_row['user_role_permission_level'];
}
?>
<div class="form-group">
<label> <?php echo $module_name_display ?> <strong class="text-danger">*</strong></label>
<div class="input-group">
<select class="form-control select2" name="<?php echo "$module_id##$module_name" ?>" required>
<option value="0" <?php if ($module_permission == 0) { echo 'selected'; } ?> >None</option>
<option value="1" <?php if ($module_permission == 1) { echo 'selected'; } ?> >Read</option>
<option value="2" <?php if ($module_permission == 2) { echo 'selected'; } ?>>Modify (Read, Edit, Archive)</option>
<option value="3" <?php if ($module_permission == 3) { echo 'selected'; } ?>>Full (Read, Edit, Archive, Delete)</option>
</select>
</div>
<small class="form-text text-muted"><?php echo $module_description ?></small>
</div>
<?php } // End while ?>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_role" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,83 @@
<div class="modal" id="addSoftwareTemplateModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-cube mr-2"></i>New License Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body">
<div class="form-group">
<label>Template 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-cube"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Software name" maxlength="200" required autofocus>
</div>
</div>
<div class="form-group">
<label>Version</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
</div>
<input type="text" class="form-control" name="version" placeholder="Software version" maxlength="200">
</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="Short 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-tag"></i></span>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Type -</option>
<?php foreach($software_types_array as $software_type) { ?>
<option><?php echo $software_type; ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>License Type</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
</div>
<select class="form-control select2" name="license_type">
<option value="">- Select a License Type -</option>
<?php foreach($license_types_array as $license_type) { ?>
<option><?php echo $license_type; ?></option>
<?php } ?>
</select>
</div>
</div>
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"></textarea>
</div>
<div class="modal-footer">
<button type="submit" name="add_software_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>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,99 @@
<?php
require_once '../../../includes/modal_header.php';
$software_template_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM software_templates WHERE software_template_id = $software_template_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$software_name = nullable_htmlentities($row['software_template_name']);
$software_version = nullable_htmlentities($row['software_template_version']);
$software_description = nullable_htmlentities($row['software_template_description']);
$software_type = nullable_htmlentities($row['software_template_type']);
$software_license_type = nullable_htmlentities($row['software_template_license_type']);
$software_notes = nullable_htmlentities($row['software_template_notes']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-cube mr-2"></i>Editing template: <strong><?php echo $software_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="software_template_id" value="<?php echo $software_template_id; ?>">
<div class="modal-body">
<div class="form-group">
<label>Template 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-cube"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Software name" maxlength="200" value="<?php echo $software_name; ?>" required>
</div>
</div>
<div class="form-group">
<label>Version</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
</div>
<input type="text" class="form-control" name="version" placeholder="Software version" maxlength="200" value="<?php echo $software_version; ?>">
</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="Short description" value="<?php echo $software_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-tag"></i></span>
</div>
<select class="form-control select2" name="type" required>
<?php foreach($software_types_array as $software_type_select) { ?>
<option <?php if($software_type == $software_type_select) { echo "selected"; } ?>><?php echo $software_type_select; ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>License Type</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
</div>
<select class="form-control select2" name="license_type">
<option value="">- Select a License Type -</option>
<?php foreach($license_types_array as $license_type_select) { ?>
<option <?php if($license_type_select == $software_license_type){ echo "selected"; } ?>><?php echo $license_type_select; ?></option>
<?php } ?>
</select>
</div>
</div>
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"><?php echo $software_notes; ?></textarea>
</div>
<div class="modal-footer">
<button type="submit" name="edit_software_template" 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>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,94 @@
<?php
require_once '../../../includes/modal_header.php';
$type_display = '';
if (isset($_GET['type'])) {
$type = intval($_GET['type']);
if ($type === 1) {
$type_display = "Client";
} elseif($type === 2) {
$type_display = "Location";
} elseif ($type === 3) {
$type_display = "Contact";
} elseif ($type === 4) {
$type_display = "Credential";
}
}
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-tag mr-2"></i>New <strong><?= $type_display ?></strong> Tag</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="type" value="<?php echo $type; ?>">
<div class="modal-body">
<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-tag"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Tag name" maxlength="200" required autofocus>
</div>
</div>
<?php if (isset($_GET['type'])) { ?>
<input type="hidden" name="type" value="<?= $type ?>">
<?php } else { ?>
<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-th"></i></span>
</div>
<select class="form-control select2" name="type" required>
<option value="">- Type -</option>
<option value="1">Client Tag</option>
<option value="2">Location Tag</option>
<option value="3">Contact Tag</option>
<option value="4">Credential Tag</option>
</select>
</div>
</div>
<?php } ?>
<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" required>
</div>
</div>
<div class="form-group">
<label>Icon</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
</div>
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_tag" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,82 @@
<?php
require_once '../../../includes/modal_header.php';
$tag_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM tags WHERE tag_id = $tag_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$tag_name = nullable_htmlentities($row['tag_name']);
$tag_type = intval($row['tag_type']);
$tag_color = nullable_htmlentities($row['tag_color']);
$tag_icon = nullable_htmlentities($row['tag_icon']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-tag mr-2"></i>Editing tag: <strong><?php echo $tag_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="tag_id" value="<?php echo $tag_id; ?>">
<div class="modal-body">
<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-tag"></i></span>
</div>
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $tag_name; ?>" required>
</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-th"></i></span>
</div>
<select class="form-control select2" name="type" required>
<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>
<option value="4" <?php if ($tag_type == 4) { echo "selected"; } ?>>Credential Tag</option>
</select>
</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 $tag_color; ?>" required>
</div>
</div>
<div class="form-group">
<label>Icon</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-image"></i></span>
</div>
<input type="text" class="form-control" name="icon" placeholder="Icon ex handshake" value="<?php echo $tag_icon; ?>">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_tag" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,30 @@
<div class="modal" id="addTaxModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-balance-scale mr-2"></i>New Tax</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<input type="text" class="form-control" name="name" placeholder="Tax name" maxlength="200" required autofocus>
</div>
<div class="form-group">
<label>Percent <strong class="text-danger">*</strong></label>
<input type="number" min="0" step="any" class="form-control col-md-4" name="percent">
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_tax" 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="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,46 @@
<?php
require_once '../../../includes/modal_header.php';
$tax_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM taxes WHERE tax_id = $tax_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$tax_name = nullable_htmlentities($row['tax_name']);
$tax_percent = floatval($row['tax_percent']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-balance-scale mr-2"></i>Editing tax: <strong><?php echo $tax_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="tax_id" value="<?php echo $tax_id; ?>">
<div class="modal-body">
<div class="form-group">
<label>Name <strong class="text-danger">*</strong></label>
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $tax_name; ?>" required>
</div>
<div class="form-group">
<label>Percent <strong class="text-danger">*</strong></label>
<input type="number" min="0" step="any" class="form-control col-md-4" name="percent" value="<?php echo $tax_percent; ?>">
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_tax" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,42 @@
<div class="modal" id="addTicketStatusModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-info-circle mr-2"></i>New Ticket Status</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body">
<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-info-circle"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Ticket Status name" maxlength="200" required autofocus>
</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" required>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_ticket_status" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,79 @@
<?php
require_once '../../../includes/modal_header.php';
$ticket_status_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM ticket_statuses WHERE ticket_status_id = $ticket_status_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$ticket_status_name = nullable_htmlentities($row['ticket_status_name']);
$ticket_status_color = nullable_htmlentities($row['ticket_status_color']);
$ticket_status_order = intval($row['ticket_status_order']);
$ticket_status_active = intval($row['ticket_status_active']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-info-circle mr-2"></i>Editing Ticket Status: <strong><?php echo $ticket_status_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="ticket_status_id" value="<?php echo $ticket_status_id; ?>">
<div class="modal-body">
<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-tag"></i></span>
</div>
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $ticket_status_name; ?>" required <?php if ($ticket_status_id <= 5) { echo "readonly"; } ?>>
</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 $ticket_status_color; ?>" required>
</div>
</div>
<div class="form-group">
<label>Order</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-sort-numeric-down"></i></span>
</div>
<input type="number" class="form-control" name="order" placeholder="Leave blank for no order" value="<?php echo $ticket_status_order; ?>">
</div>
</div>
<div class="form-group">
<label>Status <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-info-circle"></i></span>
</div>
<select class="form-control select2" name="status" required>
<option <?php if ($ticket_status_active == 1) { echo "selected"; } ?> value="1">Active</option>
<option <?php if ($ticket_status_active == 0) { echo "selected"; } ?> value="0" <?php if ($ticket_status_id <= 5) { echo "disabled"; } ?>>Inactive</option>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ticket_status" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,75 @@
<div class="modal" id="addTicketTemplateModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-life-ring mr-2"></i>Creating Ticket Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body">
<div class="form-group">
<label>Template 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-life-ring"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Template name" maxlength="200" required autofocus>
</div>
</div>
<div class="form-group">
<label>Subject</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="subject" placeholder="Subject" maxlength="500">
</div>
</div>
<div class="form-group">
<textarea class="form-control tinymceTicket" name="details"></textarea>
</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="Short description">
</div>
</div>
<div class="form-group">
<label>Add it to a Project Template?</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-project-diagram"></i></span>
</div>
<select class="form-control select2" name="project_template">
<option value="0">- No -</option>
<?php
$sql_project_templates = mysqli_query($mysqli, "SELECT * FROM project_templates WHERE project_template_archived_at IS NULL ORDER BY project_template_name ASC");
while ($row = mysqli_fetch_array($sql_project_templates)) {
$project_template_id_select = intval($row['project_template_id']);
$project_template_name_select = nullable_htmlentities($row['project_template_name']); ?>
<option value="<?php echo $project_template_id_select; ?>"><?php echo $project_template_name_select; ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_ticket_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>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,57 @@
<div class="modal" id="editTicketTemplateModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-life-ring mr-2"></i>Editing Ticket Template: <?php echo $ticket_template_name; ?></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="ticket_template_id" value="<?php echo $ticket_template_id; ?>">
<div class="modal-body">
<div class="form-group">
<label>Template 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-life-ring"></i></span>
</div>
<input type="text" class="form-control" name="name" maxlength="200" value="<?php echo $ticket_template_name; ?>" placeholder="Template name" required autofocus>
</div>
</div>
<div class="form-group">
<label>Subject</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="subject" maxlength="500" value="<?php echo $ticket_template_subject; ?>" placeholder="Subject">
</div>
</div>
<div class="form-group">
<textarea class="form-control tinymceTicket" name="details"><?php echo $ticket_template_details; ?></textarea>
</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" value="<?php echo $ticket_template_description; ?>" placeholder="Short description">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ticket_template" 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>

View File

@@ -0,0 +1,62 @@
<?php
require_once '../../../includes/modal_header.php';
$task_template_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM task_templates WHERE task_template_id = $task_template_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$task_template_name = nullable_htmlentities($row['task_template_name']);
$task_template_order = intval($row['task_template_order']);
$task_template_completion_estimate = intval($row['task_template_completion_estimate']);
//$task_template_description = nullable_htmlentities($row['task_template_description']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-tasks mr-2"></i>Editing task</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="task_template_id" value="<?php echo $task_template_id; ?>">
<div class="modal-body">
<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-tag"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Name the task" maxlength="255" value="<?php echo $task_template_name; ?>" required autofocus>
</div>
</div>
<div class="form-group">
<label>Estimated Completion Time <span class="text-secondary">(Minutes)</span></label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
</div>
<input type="number" class="form-control" name="completion_estimate" placeholder="Estimated time to complete task in mins" value="<?php echo $task_template_completion_estimate; ?>">
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_ticket_template_task" 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>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -0,0 +1,156 @@
<div class="modal" id="addUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-plus mr-2"></i>New User</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<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" maxlength="200" required autofocus>
</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" maxlength="200" 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" minlength="8" required>
<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 role_archived_at IS NULL");
while ($row = mysqli_fetch_array($sql_user_roles)) {
$role_id = intval($row['role_id']);
$role_name = nullable_htmlentities($row['role_name']);
?>
<option value="<?php echo $role_id; ?>"><?php echo $role_name; ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Avatar</label>
<input type="file" class="form-control-file" accept="image/*" 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 class="tab-pane fade" id="pills-user-access">
<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");
while ($row = mysqli_fetch_array($sql_client_select)) {
$client_id = intval($row['client_id']);
$client_name = nullable_htmlentities($row['client_name']);
?>
<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 } ?>
</ul>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="add_user" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Create</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,31 @@
<div class="modal" id="resetAllUserPassModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-body">
<div class="mb-4" style="text-align: center;">
<i class="far fas fa-10x fa-skull-crossbones text-danger mb-3 mt-3"></i>
<h2>Incident Response: Agent Password Reset</h2>
<br>
<div class="alert alert-danger" role="alert">
<b>This is a potentially destructive function.<br>It is intended to be used as part of a potential security incident.</b>
</div>
<h6 class="mb-4 text-secondary"><b>All ITFlow agent passwords will be reset and shown to you </b><i>(except yours - change yours first!)</i>.<br/><br/>You should communicate temporary passwords to agents out of band (e.g. via a phone call) and require they are changed ASAP.</h6>
<form action="post.php" method="POST">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="row col-7 offset-4">
<div class="input-group">
<div class="input-group-prepend">
<input type="password" class="form-control" placeholder="Enter your account password to continue" name="admin_password" required>
</div>
</div>
</div>
<br>
<button class="btn btn-danger" type="submit" name="ir_reset_user_password"><i class="fas fa-fw fa-key mr-2"></i>Reset passwords</button>
</form>
</div>
<button type="button" class="btn btn-outline-secondary btn-lg px-5 mr-4" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,83 @@
<?php
require_once '../../../includes/modal_header.php';
$user_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM users WHERE users.user_id = $user_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$user_name = nullable_htmlentities($row['user_name']);
$user_email = nullable_htmlentities($row['user_email']);
$user_avatar = nullable_htmlentities($row['user_avatar']);
$user_initials = nullable_htmlentities(initials($user_name));
$sql_related_tickets = mysqli_query($mysqli, "SELECT * FROM tickets
WHERE ticket_assigned_to = $user_id AND ticket_resolved_at IS NULL AND ticket_closed_at IS NULL");
$ticket_count = mysqli_num_rows($sql_related_tickets);
// Related Recurring Tickets Query
$sql_related_recurring_tickets = mysqli_query($mysqli, "SELECT * FROM recurring_tickets WHERE recurring_ticket_assigned_to = $user_id");
$recurring_ticket_count = mysqli_num_rows($sql_related_recurring_tickets);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-slash mr-2"></i>Archiving user:
<strong><?php echo $user_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="user_id" value="<?php echo $user_id; ?>">
<div class="modal-body">
<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>
<div class="form-group">
<label>Reassign <?= $ticket_count ?> Open Tickets and <?= $recurring_ticket_count ?> Recurring Tickets To:</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>
<select class="form-control select2" name="ticket_assign" required>
<option value="0">No one</option>
<?php
$sql_users = mysqli_query($mysqli, "SELECT * FROM users WHERE user_type = 1 AND user_archived_at IS NULL");
while ($row = mysqli_fetch_array($sql_users)) {
$user_id_select = intval($row['user_id']);
$user_name_select = nullable_htmlentities($row['user_name']);
?>
<option value="<?= $user_id_select ?>"><?= $user_name_select ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="archive_user" class="btn btn-danger text-bold"><i class="fas fa-archive mr-2"></i>Archive</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../../../includes/modal_footer.php";

View File

@@ -0,0 +1,204 @@
<?php
require_once '../../../includes/modal_header.php';
$user_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM users
LEFT JOIN user_settings ON users.user_id = user_settings.user_id
WHERE users.user_id = $user_id LIMIT 1"
);
$row = mysqli_fetch_array($sql);
$user_name = nullable_htmlentities($row['user_name']);
$user_email = nullable_htmlentities($row['user_email']);
$user_avatar = nullable_htmlentities($row['user_avatar']);
$user_token = nullable_htmlentities($row['user_token']);
$user_config_force_mfa = intval($row['user_config_force_mfa']);
$user_role_id = intval($row['user_role_id']);
$user_initials = nullable_htmlentities(initials($user_name));
// Get User Client Access Permissions
$user_client_access_sql = mysqli_query($mysqli,"SELECT client_id FROM user_client_permissions WHERE user_id = $user_id");
$client_access_array = [];
while ($row = mysqli_fetch_assoc($user_client_access_sql)) {
$client_access_array[] = intval($row['client_id']);
}
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-edit mr-2"></i>Editing user:
<strong><?php echo $user_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="user_id" value="<?php echo $user_id; ?>">
<div class="modal-body">
<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>
<hr>
<div class="tab-content">
<div class="tab-pane fade show active" id="pills-user-details<?php echo $user_id; ?>">
<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>
<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" maxlength="200"
value="<?php echo $user_name; ?>" required>
</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" maxlength="200"
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 role_archived_at IS NULL");
while ($row = mysqli_fetch_array($sql_user_roles)) {
$role_id = intval($row['role_id']);
$role_name = nullable_htmlentities($row['role_name']);
?>
<option <?php if ($role_id == $user_role_id) {echo "selected";} ?> value="<?php echo $role_id; ?>"><?php echo $role_name; ?></option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Avatar</label>
<input type="file" class="form-control-file" accept="image/*" 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");
while ($row = mysqli_fetch_array($sql_client_select)) {
$client_id_select = intval($row['client_id']);
$client_name_select = nullable_htmlentities($row['client_name']);
?>
<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 } ?>
</ul>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="edit_user" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Save</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../../../includes/modal_footer.php";

View File

@@ -0,0 +1,21 @@
<div class="modal" id="exportUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Users to CSV</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="submit" name="export_users_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="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
</div>
</div>
</div>

View File

@@ -0,0 +1,45 @@
<div class="modal" id="userInviteModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-user-plus"></i>Invite User</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<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" maxlength="200" required>
</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>
<!-- //TODO: Pull from roles -->
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="invite_user" class="btn btn-primary text-bold"><i class="fas fa-paper-plane mr-2"></i>Send Invite</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>

View File

@@ -0,0 +1,87 @@
<?php
require_once '../../../includes/modal_header.php';
$user_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM users WHERE user_id = $user_id AND user_archived_at IS NOT NULL LIMIT 1");
$row = mysqli_fetch_array($sql);
$user_name = str_replace(" (archived)", "", $row['user_name']); //Removed (archived) from user_name
$user_name = nullable_htmlentities($user_name);
$user_email = nullable_htmlentities($row['user_email']);
$user_avatar = nullable_htmlentities($row['user_avatar']);
$user_initials = initials($user_name);
$user_role_id = intval($row['user_role_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-redo-alt mr-2"></i>Restoring user:
<strong><?php echo $user_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<input type="hidden" name="user_id" value="<?php echo $user_id; ?>">
<div class="modal-body">
<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>
<div class="form-group">
<label>Set a 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="Enter a new password" autocomplete="new-password" required>
<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 role_archived_at IS NULL");
while ($row = mysqli_fetch_array($sql_user_roles)) {
$role_id = intval($row['role_id']);
$role_name = nullable_htmlentities($row['role_name']);
?>
<option <?php if ($role_id == $user_role_id) {echo "selected";} ?> value="<?php echo $role_id; ?>"><?php echo $role_name; ?></option>
<?php } ?>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="restore_user" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Restore</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once "../../../includes/modal_footer.php";

View File

@@ -0,0 +1,168 @@
<div class="modal" id="addVendorTemplateModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-building mr-2"></i>New Vendor Template</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="client_id" value="<?php if (isset($_GET['client_id'])) { echo $client_id; } else { echo 0; } ?>">
<div class="modal-body">
<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-support">Support</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>Vendor 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-building"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Vendor Name" maxlength="200" 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" maxlength="200">
</div>
</div>
<div class="form-group">
<label>Account Number</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-fingerprint"></i></span>
</div>
<input type="text" class="form-control" name="account_number" placeholder="Account number" maxlength="200">
</div>
</div>
<div class="form-group">
<label>Account Manager</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
</div>
<input type="text" class="form-control" name="contact_name" placeholder="Account manager's name" maxlength="200">
</div>
</div>
</div>
<div class="tab-pane fade" id="pills-support">
<label>Support Phone / <span class="text-secondary">Extension</span></label>
<div class="form-row">
<div class="col-9">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div>
<input type="tel" class="form-control col-2" name="phone_country_code" placeholder="+" maxlength="4">
<input type="tel" class="form-control" name="phone" placeholder="Phone Number" maxlength="200">
</div>
</div>
</div>
<div class="col-3">
<div class="form-group">
<input type="text" class="form-control" name="extension" placeholder="ext." maxlength="200">
</div>
</div>
</div>
<div class="form-group">
<label>Support Hours</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="hours" placeholder="Support Hours" maxlength="200">
</div>
</div>
<div class="form-group">
<label>Support Email</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="Support Email" maxlength="200">
</div>
</div>
<div class="form-group">
<label>Support Website URL</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
</div>
<input type="text" class="form-control" name="website" placeholder="Do not include http(s)://" maxlength="200">
</div>
</div>
<div class="form-group">
<label>Pin/Code</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="code" placeholder="Access Code or Pin" maxlength="200">
</div>
</div>
<div class="form-group">
<label>SLA</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-handshake"></i></span>
</div>
<input type="text" class="form-control" name="sla" placeholder="SLA Response Time" maxlength="200">
</div>
</div>
</div>
<div class="tab-pane fade" id="pills-notes">
<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">
<button type="submit" name="add_vendor_template" class="btn btn-primary text-bold"><i class="fa fa-check mr-2"></i>Create Template</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>

View File

@@ -0,0 +1,254 @@
<?php
require_once '../../../includes/modal_header.php';
$vendor_template_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT * FROM vendor_templates WHERE vendor_template_id = $vendor_template_id LIMIT 1");
$row = mysqli_fetch_array($sql);
$vendor_name = nullable_htmlentities($row['vendor_template_name']);
$vendor_description = nullable_htmlentities($row['vendor_template_description']);
$vendor_account_number = nullable_htmlentities($row['vendor_template_account_number']);
$vendor_contact_name = nullable_htmlentities($row['vendor_template_contact_name']);
$vendor_phone_country_code = intval($row['vendor_template_phone_country_code']);
$vendor_phone = formatPhoneNumber($row['vendor_template_phone'], $vendor_phone_country_code);
$vendor_extension = nullable_htmlentities($row['vendor_template_extension']);
$vendor_email = nullable_htmlentities($row['vendor_template_email']);
$vendor_website = nullable_htmlentities($row['vendor_template_website']);
$vendor_hours = nullable_htmlentities($row['vendor_template_hours']);
$vendor_sla = nullable_htmlentities($row['vendor_template_sla']);
$vendor_code = nullable_htmlentities($row['vendor_template_code']);
$vendor_notes = nullable_htmlentities($row['vendor_template_notes']);
// Generate the HTML form content using output buffering.
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-building mr-2"></i>Editing vendor template: <strong><?php echo $vendor_name; ?></strong></h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="vendor_template_id" value="<?php echo $vendor_template_id; ?>">
<div class="modal-body">
<ul class="nav nav-pills nav-justified mb-3">
<li class="nav-item">
<a class="nav-link active" data-toggle="pill" href="#pills-details<?php echo $vendor_template_id; ?>">Details</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="pill" href="#pills-support<?php echo $vendor_template_id; ?>">Support</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="pill" href="#pills-notes<?php echo $vendor_template_id; ?>">Notes</a>
</li>
</ul>
<hr>
<div class="alert alert-info">Check the fields you would like to update globally</div>
<div class="tab-content">
<div class="tab-pane fade show active" id="pills-details<?php echo $vendor_template_id; ?>">
<div class="form-group">
<label>Vendor 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-building"></i></span>
</div>
<input type="text" class="form-control" name="name" placeholder="Vendor Name" maxlength="200" value="<?php echo "$vendor_name"; ?>" required>
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="global_update_vendor_name" value="1">
</div>
</div>
</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" maxlength="200" value="<?php echo $vendor_description; ?>">
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="global_update_vendor_description" value="1">
</div>
</div>
</div>
</div>
<div class="form-group">
<label>Account Number</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-fingerprint"></i></span>
</div>
<input type="text" class="form-control" name="account_number" placeholder="Account number" maxlength="200" value="<?php echo $vendor_account_number; ?>">
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="global_update_vendor_account_number" value="1">
</div>
</div>
</div>
</div>
<div class="form-group">
<label>Account Manager</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
</div>
<input type="text" class="form-control" name="contact_name" maxlength="200" value="<?php echo $vendor_contact_name; ?>" placeholder="Vendor contact name">
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="global_update_vendor_contact_name" value="1">
</div>
</div>
</div>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="updateVendorsCheckbox<?php echo $vendor_template_id; ?>" name="update_base_vendors" value="1" >
<label class="custom-control-label" for="updateVendorsCheckbox<?php echo $vendor_template_id; ?>">Update All Base Vendors</label>
</div>
</div>
</div>
<div class="tab-pane fade" id="pills-support<?php echo $vendor_template_id; ?>">
<label>Support Phone</label>
<div class="form-row">
<div class="col-8">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-phone"></i></span>
</div>
<input type="tel" class="form-control col-2" name="phone_country_code" placeholder="+" maxlength="4" value="<?php echo $vendor_phone_country_code; ?>">
<input type="tel" class="form-control" name="phone" value="<?php echo $vendor_phone; ?>">
</div>
</div>
</div>
<div class="col-4">
<div class="input-group">
<input type="text" class="form-control" name="extension" placeholder="Prompts" maxlength="200" value="<?php echo $vendor_extension; ?>">
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="global_update_vendor_phone" value="1">
</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<label>Support Hours</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="hours" placeholder="Support Hours" maxlength="200" value="<?php echo $vendor_hours; ?>">
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="global_update_vendor_hours" value="1">
</div>
</div>
</div>
</div>
<div class="form-group">
<label>Support Email</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="Support Email" maxlength="200" value="<?php echo $vendor_email; ?>">
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="global_update_vendor_email" value="1">
</div>
</div>
</div>
</div>
<div class="form-group">
<label>Support Website URL</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
</div>
<input type="text" class="form-control" name="website" placeholder="Do not include http(s)://" maxlength="200" value="<?php echo $vendor_website; ?>">
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="global_update_vendor_website" value="1">
</div>
</div>
</div>
</div>
<div class="form-group">
<label>SLA</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-handshake"></i></span>
</div>
<input type="text" class="form-control" name="sla" placeholder="SLA Response Time" maxlength="200" value="<?php echo $vendor_sla; ?>">
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="global_update_vendor_sla" value="1">
</div>
</div>
</div>
</div>
<div class="form-group">
<label>Pin/Code</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-key"></i></span>
</div>
<input type="text" class="form-control" name="code" placeholder="Access Code or Pin" maxlength="200" value="<?php echo $vendor_code; ?>">
<div class="input-group-append">
<div class="input-group-text">
<input type="checkbox" name="global_update_vendor_code" value="1">
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="pills-notes<?php echo $vendor_template_id; ?>">
<div class="form-group">
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"><?php echo $vendor_notes; ?></textarea>
</div>
<div class="form-group">
<label>Update Notes Globally?</label>
<input type="checkbox" name="global_update_vendor_notes" value="1">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary text-bold" name="edit_vendor_template"><i class="fa fa-check mr-2"></i>Update Template</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

102
admin/payment_method.php Normal file
View File

@@ -0,0 +1,102 @@
<?php
// Default Column Sortby Filter
$sort = "payment_method_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli, "SELECT * FROM payment_methods ORDER BY $sort $order");
$num_rows = mysqli_num_rows($sql);
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-credit-card mr-2"></i>Payment Methods</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addPaymentMethodModal"><i class="fas fa-plus mr-2"></i>Add Payment Method</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows == 0) { echo "d-none"; } ?>">
<tr>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_method_name&order=<?php echo $disp; ?>">
Method <?php if ($sort == 'payment_method_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_method_description&order=<?php echo $disp; ?>">
Description <?php if ($sort == 'payment_method_description') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_method_created_at&order=<?php echo $disp; ?>">
Created at <?php if ($sort == 'payment_method_created_at') { echo $order_icon; } ?>
</a>
</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$payment_method_id = intval($row['payment_method_id']);
$payment_method_name = nullable_htmlentities($row['payment_method_name']);
$payment_method_description = nullable_htmlentities($row['payment_method_description']);
$payment_method_created_at = nullable_htmlentities($row['payment_method_created_at']);
?>
<tr>
<td>
<a class="text-dark text-bold ajax-modal" href="#"
data-modal-url="modals/payment_method/payment_method_edit.php?id=<?= $payment_method_id ?>">
<?php echo $payment_method_name; ?>
</a>
</td>
<td><?php echo $payment_method_description; ?></td>
<td><?php echo $payment_method_created_at; ?></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 ajax-modal" href="#"
data-modal-url="modals/payment_method/payment_method_edit.php?id=<?= $payment_method_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_payment_method=<?php echo $payment_method_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>
</td>
</tr>
<?php
}
if ($num_rows == 0) {
echo "<h3 class='text-secondary mt-3' style='text-align: center'>No Records Here</h3>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "modals/payment_method/payment_method_add.php";
require_once "../includes/footer.php";

139
admin/payment_provider.php Normal file
View File

@@ -0,0 +1,139 @@
<?php
// Default Column Sortby Filter
$sort = "payment_provider_name";
$order = "ASC";
require_once "includes/inc_all_admin.php";
$sql = mysqli_query($mysqli, "SELECT * FROM payment_providers
LEFT JOIN accounts ON payment_provider_account = account_id
LEFT JOIN vendors ON payment_provider_expense_vendor = vendor_id
LEFT JOIN categories ON payment_provider_expense_category = category_id
ORDER BY $sort $order"
);
$num_rows = mysqli_num_rows($sql);
?>
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fas fa-fw fa-credit-card mr-2"></i>Payment Providers</h3>
<div class="card-tools">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addPaymentProviderModal"><i class="fas fa-plus mr-2"></i>Add Provider</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive-sm">
<table class="table table-striped table-borderless table-hover">
<thead class="text-dark <?php if ($num_rows == 0) { echo "d-none"; } ?>">
<tr>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_provider_name&order=<?php echo $disp; ?>">
Provider <?php if ($sort == 'payment_provider_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=account_name&order=<?php echo $disp; ?>">
Expense / Income Account <?php if ($sort == 'account_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=payment_provider_threshold&order=<?php echo $disp; ?>">
Threshold <?php if ($sort == 'payment_provider_threshold') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=vendor_name&order=<?php echo $disp; ?>">
Expense Vendor <?php if ($sort == 'vendor_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark" href="?<?php echo $url_query_strings_sort; ?>&sort=category_name&order=<?php echo $disp; ?>">
Expense Category <?php if ($sort == 'category_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-dark">Expensed Fee</a>
</th>
<th>
<a class="text-dark">Saved Payment Methods</a>
</th>
<th class="text-center">Action</th>
</tr>
</thead>
<tbody>
<?php
while ($row = mysqli_fetch_array($sql)) {
$provider_id = intval($row['payment_provider_id']);
$provider_name = nullable_htmlentities($row['payment_provider_name']);
$provider_description = nullable_htmlentities($row['payment_provider_description']);
$account_name = nullable_htmlentities($row['account_name']);
$threshold = floatval($row['payment_provider_threshold']);
$vendor_name = nullable_htmlentities($row['vendor_name']);
$category = nullable_htmlentities($row['category_name']);
$percent_fee = floatval($row['payment_provider_expense_percentage_fee']) * 100;
$flat_fee = floatval($row['payment_provider_expense_flat_fee']);
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('saved_payment_id') AS saved_payment_count FROM client_saved_payment_methods WHERE saved_payment_provider_id = $provider_id"));
$saved_payment_count = intval($row['saved_payment_count']);
?>
<tr>
<td>
<a class="text-dark text-bold ajax-modal" href="#"
data-modal-url="modals/payment_provider/payment_provider_edit.php?id=<?= $provider_id ?>">
<?php echo $provider_name; ?>
</a>
<span class="text-secondary"><?php echo $provider_description; ?></span>
</td>
<td><?php echo $account_name; ?></td>
<td><?php echo numfmt_format_currency($currency_format, $threshold, $session_company_currency); ?></td>
<td><?php echo $vendor_name; ?></td>
<td><?php echo $category; ?></td>
<td><?php echo $percent_fee; ?>% + <?php echo numfmt_format_currency($currency_format, $flat_fee, $session_company_currency); ?></td>
<td><?php echo $saved_payment_count; ?></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 ajax-modal" href="#"
data-modal-url="modals/payment_provider/payment_provider_edit.php?id=<?= $provider_id ?>">
<i class="fas fa-fw fa-edit mr-2"></i>Edit
</a>
<div class="dropdown-divider"></div>
<!-- <a class="dropdown-item text-danger confirm-link" href="post.php?disable_payment_provider=--><?php //echo $provider_id; ?><!--&csrf_token=--><?php //echo $_SESSION['csrf_token'] ?><!--">-->
<!-- <i class="fas fa-fw fa-thumbs-down mr-2"></i>Disable-->
<!-- </a>-->
<!-- <a class="dropdown-item text-danger confirm-link" href="post.php?delete_payment_provider=--><?php //echo $provider_id; ?><!--&csrf_token=--><?php //echo $_SESSION['csrf_token'] ?><!--">-->
<!-- <i class="fas fa-fw fa-trash mr-2"></i>Delete-->
<!-- </a>-->
</div>
</div>
</td>
</tr>
<?php
}
if ($num_rows == 0) {
echo "<h3 class='text-secondary mt-3' style='text-align: center'>No Records Here</h3>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php
require_once "modals/payment_provider/payment_provider_add.php";
require_once "../includes/footer.php";

42
admin/post.php Normal file
View File

@@ -0,0 +1,42 @@
<?php
/*
* ITFlow - Admin GET/POST request handler
*/
require_once "../config.php";
require_once "../functions.php";
require_once "../includes/check_login.php";
// Define a variable that we can use to only allow running post files via inclusion (prevents people/bots poking them)
define('FROM_POST_HANDLER', true);
// Determine which files we should load
// Parse URL & get the path
$path = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_PATH);
// Get the base name (the page name)
$module = explode(".", basename($path))[0];
// Strip off any _details bits
$module = str_ireplace('_details', '', $module);
// Dynamically load admin-related module POST logic
if (isset($session_is_admin) && $session_is_admin) {
// As (almost) every admin setting is only changed from 1 page, we can dynamically load the relevant logic inside this single admin check IF statement
// To add a new admin POST request handler, add a file named after the admin page
// e.g. changes made on the page http://itflow/admin_ticket_statues.php will load the page admin/post/admin_ticket_statues.php to handle the changes
include_once "post/$module.php";
}
// Logout is the same for user and admin
require_once "../post/logout.php";
// TODO: Find a home for these
require_once "../post/ai.php";
require_once "../post/misc.php";

65
admin/post/ai_model.php Normal file
View File

@@ -0,0 +1,65 @@
<?php
/*
* ITFlow - GET/POST request handler for AI Models ('ai_model')
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_ai_model'])) {
validateCSRFToken($_POST['csrf_token']);
$provider_id = intval($_POST['provider']);
$model = sanitizeInput($_POST['model']);
$prompt = sanitizeInput($_POST['prompt']);
$use_case = sanitizeInput($_POST['use_case']);
mysqli_query($mysqli,"INSERT INTO ai_models SET ai_model_name = '$model', ai_model_prompt = '$prompt', ai_model_use_case = '$use_case', ai_model_ai_provider_id = $provider_id");
$ai_model_id = mysqli_insert_id($mysqli);
logAction("AI Model", "Create", "$session_name created AI Model $model");
flash_alert("AI Model <strong>$model</strong> created");
redirect();
}
if (isset($_POST['edit_ai_model'])) {
validateCSRFToken($_POST['csrf_token']);
$model_id = intval($_POST['model_id']);
$model = sanitizeInput($_POST['model']);
$prompt = sanitizeInput($_POST['prompt']);
$use_case = sanitizeInput($_POST['use_case']);
mysqli_query($mysqli,"UPDATE ai_models SET ai_model_name = '$model', ai_model_prompt = '$prompt', ai_model_use_case = '$use_case' WHERE ai_model_id = $model_id");
logAction("AI Model", "Edit", "$session_name edited AI Model $model");
flash_alert("AI Model <strong>$model</strong> edited");
redirect();
}
if (isset($_GET['delete_ai_model'])) {
validateCSRFToken($_GET['csrf_token']);
$model_id = intval($_GET['delete_ai_model']);
$model_name = sanitizeInput(getFieldById('ai_models', $model_id, 'ai_model_name'));
mysqli_query($mysqli,"DELETE FROM ai_models WHERE ai_model_id = $model_id");
logAction("AI Model", "Delete", "$session_name deleted AI Model $model_name");
flash_alert("AI Model <strong>$model_name</strong> deleted", 'error');
redirect();
}

View File

@@ -0,0 +1,65 @@
<?php
/*
* ITFlow - GET/POST request handler for AI Providers ('ai_provider')
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_ai_provider'])) {
validateCSRFToken($_POST['csrf_token']);
$provider = sanitizeInput($_POST['provider']);
$url = sanitizeInput($_POST['url']);
$model = sanitizeInput($_POST['model']);
$api_key = sanitizeInput($_POST['api_key']);
mysqli_query($mysqli,"INSERT INTO ai_providers SET ai_provider_name = '$provider', ai_provider_api_url = '$url', ai_provider_api_key = '$api_key'");
$ai_provider_id = mysqli_insert_id($mysqli);
logAction("AI Provider", "Create", "$session_name created AI Provider $provider");
flash_alert("AI Model <strong>$provider</strong> created");
redirect();
}
if (isset($_POST['edit_ai_provider'])) {
validateCSRFToken($_POST['csrf_token']);
$provider_id = intval($_POST['provider_id']);
$provider = sanitizeInput($_POST['provider']);
$url = sanitizeInput($_POST['url']);
$api_key = sanitizeInput($_POST['api_key']);
mysqli_query($mysqli,"UPDATE ai_providers SET ai_provider_name = '$provider', ai_provider_api_url = '$url', ai_provider_api_key = '$api_key' WHERE ai_provider_id = $provider_id");
logAction("AI Provider", "Edit", "$session_name edited AI Provider $provider");
flash_alert("AI Model <strong>$provider</strong> edited");
redirect();
}
if (isset($_GET['delete_ai_provider'])) {
validateCSRFToken($_GET['csrf_token']);
$provider_id = intval($_GET['delete_ai_provider']);
$provider_name = sanitizeInput(getFieldById('ai_providers', $provider_id, 'ai_provider_name'));
mysqli_query($mysqli,"DELETE FROM ai_providers WHERE ai_provider_id = $provider_id");
logAction("AI Provider", "Delete", "$session_name deleted AI Provider $provider_name", 'error');
flash_alert("AI Provider <strong>$provider_name</strong> deleted", 'error');
redirect();
}

87
admin/post/api_keys.php Normal file
View File

@@ -0,0 +1,87 @@
<?php
/*
* ITFlow - GET/POST request handler for API settings
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_api_key'])) {
validateCSRFToken($_POST['csrf_token']);
$name = sanitizeInput($_POST['name']);
$expire = sanitizeInput($_POST['expire']);
$client_id = intval($_POST['client']);
$secret = sanitizeInput($_POST['key']); // API Key
// Credential decryption password
$password = password_hash(trim($_POST['password']), PASSWORD_DEFAULT);
$apikey_specific_encryption_ciphertext = encryptUserSpecificKey(trim($_POST['password']));
mysqli_query($mysqli,"INSERT INTO api_keys SET api_key_name = '$name', api_key_secret = '$secret', api_key_decrypt_hash = '$apikey_specific_encryption_ciphertext', api_key_expire = '$expire', api_key_client_id = $client_id");
$api_key_id = mysqli_insert_id($mysqli);
logAction("API Key", "Create", "$session_name created API key $name set to expire on $expire", $client_id, $api_key_id);
flash_alert("API Key <strong>$name</strong> created");
redirect();
}
if (isset($_GET['delete_api_key'])) {
validateCSRFToken($_GET['csrf_token']);
$api_key_id = intval($_GET['delete_api_key']);
// Get API Key Name
$row = mysqli_fetch_array(mysqli_query($mysqli,"SELECT api_key_name, api_key_client_id FROM api_keys WHERE api_key_id = $api_key_id"));
$api_key_name = sanitizeInput($row['api_key_name']);
$client_id = intval($row['api_key_client_id']);
mysqli_query($mysqli,"DELETE FROM api_keys WHERE api_key_id = $api_key_id");
logAction("API Key", "Delete", "$session_name deleted API key $name", $client_id);
flash_alert("API Key <strong>$name</strong> deleted", 'error');
redirect();
}
if (isset($_POST['bulk_delete_api_keys'])) {
validateCSRFToken($_POST['csrf_token']);
if (isset($_POST['api_key_ids'])) {
$count = count($_POST['api_key_ids']);
// Cycle through array and delete each record
foreach ($_POST['api_key_ids'] as $api_key_id) {
$api_key_id = intval($api_key_id);
// Get API Key Name
$row = mysqli_fetch_array(mysqli_query($mysqli,"SELECT api_key_name, api_key_client_id FROM api_keys WHERE api_key_id = $api_key_id"));
$api_key_name = sanitizeInput($row['api_key_name']);
$client_id = intval($row['api_key_client_id']);
mysqli_query($mysqli, "DELETE FROM api_keys WHERE api_key_id = $api_key_id");
logAction("API Key", "Delete", "$session_name deleted API key $name", $client_id);
}
logAction("API Key", "Bulk Delete", "$session_name deleted $count API key(s)");
flash_alert("Deleted <strong>$count</strong> API keys(s)", 'error');
}
redirect();
}

213
admin/post/backup.php Normal file
View File

@@ -0,0 +1,213 @@
<?php
/*
* ITFlow - GET/POST request handler for DB / master key backup
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
require_once "../includes/app_version.php";
if (isset($_GET['download_backup'])) {
validateCSRFToken($_GET['csrf_token']);
$timestamp = date('YmdHis');
$baseName = "itflow_$timestamp";
// === 0. Scoped cleanup ===
$cleanupFiles = [];
$registerTempFileForCleanup = function ($file) use (&$cleanupFiles) {
$cleanupFiles[] = $file;
};
register_shutdown_function(function () use (&$cleanupFiles) {
foreach ($cleanupFiles as $file) {
if (is_file($file)) {
@unlink($file);
}
}
});
// === 1. Local helper function: zipFolder
$zipFolder = function ($folderPath, $zipFilePath) {
$zip = new ZipArchive();
if ($zip->open($zipFilePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
error_log("Failed to open zip file: $zipFilePath");
http_response_code(500);
exit("Internal Server Error: Cannot open zip archive.");
}
$folderPath = realpath($folderPath);
if (!$folderPath) {
error_log("Invalid folder path: $folderPath");
http_response_code(500);
exit("Internal Server Error: Invalid folder path.");
}
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($folderPath),
RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $file) {
if (!$file->isDir()) {
$filePath = $file->getRealPath();
$relativePath = substr($filePath, strlen($folderPath) + 1);
$zip->addFile($filePath, $relativePath);
}
}
$zip->close();
};
// === 2. Create all temp files
$sqlFile = tempnam(sys_get_temp_dir(), $baseName . "_sql_");
$uploadsZip = tempnam(sys_get_temp_dir(), $baseName . "_uploads_");
$versionFile = tempnam(sys_get_temp_dir(), $baseName . "_version_");
$finalZip = tempnam(sys_get_temp_dir(), $baseName . "_backup_");
foreach ([$sqlFile, $uploadsZip, $versionFile, $finalZip] as $f) {
$registerTempFileForCleanup($f);
chmod($f, 0600);
}
// === 3. Generate SQL Dump
$sqlContent = "-- UTF-8 + Foreign Key Safe Dump\n";
$sqlContent .= "SET NAMES 'utf8mb4';\n";
$sqlContent .= "SET foreign_key_checks = 0;\n\n";
$tables = [];
$res = $mysqli->query("SHOW TABLES");
if (!$res) {
error_log("MySQL Error: " . $mysqli->error);
exit("Error retrieving tables.");
}
while ($row = $res->fetch_row()) {
$tables[] = $row[0];
}
foreach ($tables as $table) {
$createRes = $mysqli->query("SHOW CREATE TABLE `$table`");
if (!$createRes) {
error_log("MySQL Error: " . $mysqli->error);
continue;
}
$createRow = $createRes->fetch_assoc();
$createSQL = array_values($createRow)[1];
$sqlContent .= "\n-- ----------------------------\n";
$sqlContent .= "-- Table structure for `$table`\n";
$sqlContent .= "-- ----------------------------\n";
$sqlContent .= "DROP TABLE IF EXISTS `$table`;\n";
$sqlContent .= $createSQL . ";\n\n";
$dataRes = $mysqli->query("SELECT * FROM `$table`");
if ($dataRes && $dataRes->num_rows > 0) {
$sqlContent .= "-- Dumping data for table `$table`\n";
while ($row = $dataRes->fetch_assoc()) {
$columns = array_map(fn($col) => '`' . $mysqli->real_escape_string($col) . '`', array_keys($row));
$values = array_map(function ($val) use ($mysqli) {
return is_null($val) ? "NULL" : "'" . $mysqli->real_escape_string($val) . "'";
}, array_values($row));
$sqlContent .= "INSERT INTO `$table` (" . implode(", ", $columns) . ") VALUES (" . implode(", ", $values) . ");\n";
}
$sqlContent .= "\n";
}
}
$sqlContent .= "SET foreign_key_checks = 1;\n";
file_put_contents($sqlFile, $sqlContent);
// === 4. Zip the uploads folder
$zipFolder("../uploads", $uploadsZip);
// === 5. Create version.txt
$commitHash = trim(shell_exec('git log -1 --format=%H')) ?: 'N/A';
$gitBranch = trim(shell_exec('git rev-parse --abbrev-ref HEAD')) ?: 'N/A';
$versionContent = "ITFlow Backup Metadata\n";
$versionContent .= "-----------------------------\n";
$versionContent .= "Generated: " . date('Y-m-d H:i:s') . "\n";
$versionContent .= "Backup File: " . basename($finalZip) . "\n";
$versionContent .= "Generated By: $session_name\n";
$versionContent .= "Host: " . gethostname() . "\n";
$versionContent .= "Git Branch: $gitBranch\n";
$versionContent .= "Git Commit: $commitHash\n";
$versionContent .= "ITFlow Version: " . (defined('APP_VERSION') ? APP_VERSION : 'Unknown') . "\n";
$versionContent .= "Database Version: " . (defined('CURRENT_DATABASE_VERSION') ? CURRENT_DATABASE_VERSION : 'Unknown') . "\n";
$versionContent .= "Checksum (SHA256): \n";
file_put_contents($versionFile, $versionContent);
// === 6. Build final ZIP
$final = new ZipArchive();
if ($final->open($finalZip, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== TRUE) {
error_log("Failed to create final zip: $finalZip");
http_response_code(500);
exit("Internal Server Error: Unable to create backup archive.");
}
$final->addFile($sqlFile, "db.sql");
$final->addFile($uploadsZip, "uploads.zip");
$final->addFile($versionFile, "version.txt");
$final->close();
chmod($finalZip, 0600);
$checksum = hash_file('sha256', $finalZip);
file_put_contents($versionFile, $versionContent . "$checksum\n");
// === 7. Serve final ZIP
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="' . basename($finalZip) . '"');
header('Content-Length: ' . filesize($finalZip));
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Content-Transfer-Encoding: binary');
flush();
$fp = fopen($finalZip, 'rb');
fpassthru($fp);
fclose($fp);
logAction("System", "Backup Download", "$session_name downloaded full backup.");
flash_alert("Full backup downloaded.");
exit;
}
if (isset($_POST['backup_master_key'])) {
validateCSRFToken($_POST['csrf_token']);
$password = $_POST['password'];
$sql = mysqli_query($mysqli, "SELECT * FROM users WHERE user_id = $session_user_id");
$row = mysqli_fetch_array($sql);
if (password_verify($password, $row['user_password'])) {
$site_encryption_master_key = decryptUserSpecificKey($row['user_specific_encryption_ciphertext'], $password);
logAction("Master Key", "Download", "$session_name retrieved the master encryption key");
appNotify("Master Key", "$session_name retrieved the master encryption key");
echo "==============================";
echo "<br>Master encryption key:<br>";
echo "<b>$site_encryption_master_key</b>";
echo "<br>==============================";
} else {
logAction("Master Key", "Download", "$session_name attempted to retrieve the master encryption key but failed");
flash_alert("Incorrect password.", 'error');
redirect();
}
}

99
admin/post/category.php Normal file
View File

@@ -0,0 +1,99 @@
<?php
/*
* ITFlow - GET/POST request handler for categories ('category')
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_category'])) {
require_once 'category_model.php';
mysqli_query($mysqli,"INSERT INTO categories SET category_name = '$name', category_type = '$type', category_color = '$color'");
$category_id = mysqli_insert_id($mysqli);
logAction("Category", "Create", "$session_name created category $type $name", 0, $category_id);
flash_alert("Category $type <strong>$name</strong> created");
redirect();
}
if (isset($_POST['edit_category'])) {
require_once 'category_model.php';
$category_id = intval($_POST['category_id']);
mysqli_query($mysqli,"UPDATE categories SET category_name = '$name', category_type = '$type', category_color = '$color' WHERE category_id = $category_id");
logAction("Category", "Edit", "$session_name edited category $type $name", 0, $category_id);
flash_alert("Category $type <strong>$name</strong> edited");
redirect();
}
if (isset($_GET['archive_category'])) {
$category_id = intval($_GET['archive_category']);
// Get Category Name and Type for logging
$sql = mysqli_query($mysqli,"SELECT category_name, category_type FROM categories WHERE category_id = $category_id");
$row = mysqli_fetch_array($sql);
$category_name = sanitizeInput($row['category_name']);
$category_type = sanitizeInput($row['category_type']);
mysqli_query($mysqli,"UPDATE categories SET category_archived_at = NOW() WHERE category_id = $category_id");
logAction("Category", "Archive", "$session_name archived category $category_type $category_name", 0, $category_id);
flash_alert("Category $category_type <strong>$category_name</strong> archived", 'error');
redirect();
}
if (isset($_GET['unarchive_category'])) {
$category_id = intval($_GET['unarchive_category']);
// Get Category Name and Type for logging
$sql = mysqli_query($mysqli,"SELECT category_name, category_type FROM categories WHERE category_id = $category_id");
$row = mysqli_fetch_array($sql);
$category_name = sanitizeInput($row['category_name']);
$category_type = sanitizeInput($row['category_type']);
mysqli_query($mysqli,"UPDATE categories SET category_archived_at = NULL WHERE category_id = $category_id");
logAction("Category", "Unarchive", "$session_name unarchived category $category_type $category_name", 0, $category_id);
flash_alert("Category $category_type <strong>$category_name</strong> unarchived");
redirect();
}
if (isset($_GET['delete_category'])) {
$category_id = intval($_GET['delete_category']);
// Get Category Name and Type for logging
$sql = mysqli_query($mysqli,"SELECT category_name, category_type FROM categories WHERE category_id = $category_id");
$row = mysqli_fetch_array($sql);
$category_name = sanitizeInput($row['category_name']);
$category_type = sanitizeInput($row['category_type']);
mysqli_query($mysqli,"DELETE FROM categories WHERE category_id = $category_id");
logAction("Category", "Delete", "$session_name deleted category $category_type $category_name");
flash_alert("Category $category_type <strong>$category_name</strong> deleted", 'error');
redirect();
}

View File

@@ -0,0 +1,6 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
$name = sanitizeInput($_POST['name']);
$type = sanitizeInput($_POST['type']);
$color = sanitizeInput($_POST['color']);

View File

@@ -0,0 +1,57 @@
<?php
/*
* ITFlow - GET/POST request handler for custom fields
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if(isset($_POST['create_custom_field'])){
require_once 'custom_field_model.php';
$table = sanitizeInput($_POST['table']);
mysqli_query($mysqli,"INSERT INTO custom_fields SET custom_field_table = '$table', custom_field_label = '$label', custom_field_type = '$type'");
$custom_field_id = mysqli_insert_id($mysqli);
logAction("Custom Field", "Create", "$session_name created custom field $label", 0, $custom_field_id);
flash_alert("Custom field <strong>$label</strong> created");
redirect();
}
if(isset($_POST['edit_custom_field'])){
require_once 'custom_field_model.php';
$custom_field_id = intval($_POST['custom_field_id']);
mysqli_query($mysqli,"UPDATE custom_fields SET custom_field_label = '$label', custom_field_type = '$type' WHERE custom_field_id = $custom_field_id");
logAction("Custom Field", "Edit", "$session_name edited custom field $label", 0, $custom_field_id);
flash_alert("Custom field <strong>$label</strong> edited");
redirect();
}
if(isset($_GET['delete_custom_field'])){
$custom_field_id = intval($_GET['delete_custom_field']);
$label = sanitizeInput(getFieldById('custom_fields', $custom_field_id, 'custom_field_label'));
mysqli_query($mysqli,"DELETE FROM custom_fields WHERE custom_field_id = $custom_field_id");
logAction("Custom Field", "Delete", "$session_name deleted custom field $label");
flash_alert("Custom field <strong>$label</strong> deleted", 'error');
redirect();
}

View File

@@ -0,0 +1,5 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
$label = sanitizeInput($_POST['label']);
$type = sanitizeInput($_POST['type']);

View File

@@ -0,0 +1,68 @@
<?php
/*
* ITFlow - GET/POST request handler for showing custom links on navbars
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_custom_link'])) {
$name = sanitizeInput($_POST['name']);
$uri = sanitizeInput($_POST['uri']);
$new_tab = intval($_POST['new_tab'] ?? 0);
$icon = preg_replace("/[^0-9a-zA-Z-]/", "", sanitizeInput($_POST['icon']));
$order = intval($_POST['order'] ?? 0);
$location = intval($_POST['location']);
mysqli_query($mysqli,"INSERT INTO custom_links SET custom_link_name = '$name', custom_link_uri = '$uri', custom_link_new_tab = $new_tab, custom_link_icon = '$icon', custom_link_order = $order, custom_link_location = $location");
$custom_link_id = mysqli_insert_id($mysqli);
logAction("Custom Link", "Create", "$session_name created custom link $name -> $uri", 0, $custom_link_id);
flash_alert("Custom link <strong>$name</strong> created");
redirect();
}
if (isset($_POST['edit_custom_link'])) {
$custom_link_id = intval($_POST['custom_link_id']);
$name = sanitizeInput($_POST['name']);
$uri = sanitizeInput($_POST['uri']);
$new_tab = intval($_POST['new_tab'] ?? 0);
$icon = preg_replace("/[^0-9a-zA-Z-]/", "", sanitizeInput($_POST['icon']));
$order = intval($_POST['order'] ?? 0);
$location = intval($_POST['location']);
mysqli_query($mysqli,"UPDATE custom_links SET custom_link_name = '$name', custom_link_uri = '$uri', custom_link_new_tab = $new_tab, custom_link_icon = '$icon', custom_link_order = $order, custom_link_location = $location WHERE custom_link_id = $custom_link_id");
logAction("Custom Link", "Edit", "$session_name edited custom link $name -> $uri", 0, $custom_link_id);
flash_alert("Custom Link <strong>$name</strong> edited");
redirect();
}
if (isset($_GET['delete_custom_link'])) {
$custom_link_id = intval($_GET['delete_custom_link']);
// Get Custom Link name and uri for logging
$sql = mysqli_query($mysqli,"SELECT custom_link_name, custom_link_uri FROM custom_links WHERE custom_link_id = $custom_link_id");
$row = mysqli_fetch_array($sql);
$custom_link_name = sanitizeInput($row['custom_link_name']);
$custom_link_uri = sanitizeInput($row['custom_link_uri']);
mysqli_query($mysqli,"DELETE FROM custom_links WHERE custom_link_id = $custom_link_id");
logAction("Custom Link", "Delete", "$session_name deleted custom link $custom_link_name -> $custom_link_uri");
flash_alert("Custom Link <strong>$name</strong> deleted", 'error');
redirect();
}

View File

@@ -0,0 +1,57 @@
<?php
// Doc Templates
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_document_template'])) {
$name = sanitizeInput($_POST['name']);
$description = sanitizeInput($_POST['description']);
$content = mysqli_real_escape_string($mysqli,$_POST['content']);
mysqli_query($mysqli,"INSERT INTO document_templates SET document_template_name = '$name', document_template_description = '$description', document_template_content = '$content', document_template_created_by = $session_user_id");
$document_template_id = mysqli_insert_id($mysqli);
logAction("Document Template", "Create", "$session_name created document template $name", 0, $document_template_id);
flash_alert("Document template <strong>$name</strong> created");
redirect();
}
if (isset($_POST['edit_document_template'])) {
$document_template_id = intval($_POST['document_template_id']);
$name = sanitizeInput($_POST['name']);
$description = sanitizeInput($_POST['description']);
$content = mysqli_real_escape_string($mysqli,$_POST['content']);
// Document edit query
mysqli_query($mysqli,"UPDATE document_templates SET document_template_name = '$name', document_template_description = '$description', document_template_content = '$content', document_template_updated_by = $session_user_id WHERE document_template_id = $document_template_id");
logAction("Document Template", "Edit", "$session_name edited document template $name", 0, $document_template_id);
flash_alert("Document Template <strong>$name</strong> edited");
redirect();
}
if (isset($_GET['delete_document_template'])) {
$document_template_id = intval($_GET['delete_document_template']);
$document_template_name = sanitizeInput(getFieldById('document_templates', $document_template_id, 'document_template_name'));
mysqli_query($mysqli,"DELETE FROM document_templates WHERE document_template_id = $document_template_id");
logAction("Document Template", "Delete", "$session_name deleted document template $document_template_name");
flash_alert("Document Template <strong>$document_template_name</strong> deleted", 'error');
redirect();
}

View File

@@ -0,0 +1,20 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_identity_provider'])) {
validateCSRFToken($_POST['csrf_token']);
$azure_client_id = sanitizeInput($_POST['azure_client_id']);
$azure_client_secret = sanitizeInput($_POST['azure_client_secret']);
mysqli_query($mysqli,"UPDATE settings SET config_azure_client_id = '$azure_client_id', config_azure_client_secret = '$azure_client_secret' WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited identity provider settings");
flash_alert("Identity Provider Settings updated");
redirect();
}

87
admin/post/mail_queue.php Normal file
View File

@@ -0,0 +1,87 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_GET['send_failed_mail'])) {
$email_id = intval($_GET['send_failed_mail']);
mysqli_query($mysqli,"UPDATE email_queue SET email_status = 0, email_attempts = 3 WHERE email_id = $email_id");
logAction("Email", "Send", "$session_name attempted to force send email id: $email_id in the mail queue", 0, $email_id);
flash_alert("Email Force Sent, give it a minute to resend");
redirect();
}
if (isset($_GET['cancel_mail'])) {
$email_id = intval($_GET['cancel_mail']);
mysqli_query($mysqli,"UPDATE email_queue SET email_status = 2, email_attempts = 99, email_failed_at = NOW() WHERE email_id = $email_id");
logAction("Email", "Send", "$session_name canceled send email id: $email_id in the mail queue", 0, $email_id);
flash_alert("Email cancelled and marked as failed.", 'error');
redirect();
}
if (isset($_POST['bulk_cancel_emails'])) {
validateCSRFToken($_POST['csrf_token']);
if (isset($_POST['email_ids'])) {
$count = count($_POST['email_ids']);
// Cycle through array and mark each email as failed
foreach ($_POST['email_ids'] as $email_id) {
$email_id = intval($email_id);
mysqli_query($mysqli,"UPDATE email_queue SET email_status = 2, email_attempts = 99, email_failed_at = NOW() WHERE email_id = $email_id");
logAction("Email", "Cancel", "$session_name cancelled email id: $email_id in the mail queue", 0, $email_id);
}
logAction("Email", "Bulk Cancel", "$session_name cancelled $count email(s) in the mail queue");
flash_alert("Cancelled <strong>$count</strong> email(s)", 'error');
}
redirect();
}
if (isset($_POST['bulk_delete_emails'])) {
validateCSRFToken($_POST['csrf_token']);
if (isset($_POST['email_ids'])) {
$count = count($_POST['email_ids']);
// Cycle through array and delete each email
foreach ($_POST['email_ids'] as $email_id) {
$email_id = intval($email_id);
mysqli_query($mysqli,"DELETE FROM email_queue WHERE email_id = $email_id");
logAction("Email", "Delete", "$session_name deleted email id: $email_id from the mail queue");
}
logAction("Email", "Bulk Delete", "$session_name deleted $count email(s) from the mail queue");
flash_alert("Deleted <strong>$count</strong> email(s)", 'error');
}
redirect();
}

View File

@@ -0,0 +1,58 @@
<?php
/*
* ITFlow - GET/POST request handler for AI Providers ('ai_providers')
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_payment_method'])) {
validateCSRFToken($_POST['csrf_token']);
$name = sanitizeInput($_POST['name']);
$description = sanitizeInput($_POST['description']);
mysqli_query($mysqli,"INSERT INTO payment_methods SET payment_method_name = '$name', payment_method_description = '$description'");
logAction("Payment Method", "Create", "$session_name created Payment Method $name");
flash_alert("Payment Method <strong>$name</strong> created");
redirect();
}
if (isset($_POST['edit_payment_method'])) {
validateCSRFToken($_POST['csrf_token']);
$payment_method_id = intval($_POST['payment_method_id']);
$name = sanitizeInput($_POST['name']);
$description = sanitizeInput($_POST['description']);
mysqli_query($mysqli,"UPDATE payment_methods SET payment_method_name = '$name', payment_method_description = '$description' WHERE payment_method_id = $payment_method_id");
logAction("Payment Method", "Edit", "$session_name edited Payment Method $name");
flash_alert("Payment Method <strong>$name</strong> edited");
redirect();
}
if (isset($_GET['delete_payment_method'])) {
$payment_method_id = intval($_GET['delete_payment_method']);
$payment_method_name = sanitizeInput(getFieldById('payment_methods', $payment_method_is, 'payment_method_name'));
mysqli_query($mysqli,"DELETE FROM payment_methods WHERE payment_method_id = $payment_method_id");
logAction("Payment Method", "Delete", "$session_name deleted Payment Method $payment_method_name");
flash_alert("Payment Method <strong>$payment_method_name</strong> deleted", 'error');
redirect();
}

View File

@@ -0,0 +1,115 @@
<?php
/*
* ITFlow - GET/POST request handler for AI Providers ('ai_providers')
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_payment_provider'])) {
validateCSRFToken($_POST['csrf_token']);
$provider = sanitizeInput($_POST['provider']);
$public_key = sanitizeInput($_POST['public_key']);
$private_key = sanitizeInput($_POST['private_key']);
$threshold = floatval($_POST['threshold']);
$enable_expense = intval($_POST['enable_expense'] ?? 0);
$percentage_fee = floatval($_POST['percentage_fee']) / 100 ?? 0;
$flat_fee = floatval($_POST['flat_fee']) ?? 0;
// Check to ensure provider isn't added twice
$sql = "SELECT 1 FROM payment_providers WHERE payment_provider_name = '$provider' LIMIT 1";
$result = mysqli_query($mysqli, $sql);
if (mysqli_num_rows($result) > 0) {
flash_alert("Payment Provider <strong>$provider</strong> already exists", 'error');
redirect();
}
// Check for Stripe Account, if not create it
$sql_account = mysqli_query($mysqli,"SELECT account_id FROM accounts WHERE account_name = '$provider' AND account_archived_at IS NULL LIMIT 1");
if (mysqli_num_rows($sql_account) == 0) {
$account_id = mysqli_insert_id($mysqli);
} else {
$row = mysqli_fetch_array($sql_account);
$account_id = intval($row['account_id']);
}
// Expense defaults
$category_id = 0;
$vendor_id = 0;
if ($enable_expense) {
// Category
$sql_category = mysqli_query($mysqli,"SELECT category_id FROM categories WHERE category_name = 'Payment Processing' AND category_type = 'Expense' AND category_archived_at IS NULL LIMIT 1");
if (mysqli_num_rows($sql_category) == 0) {
mysqli_query($mysqli,"INSERT INTO categories SET category_name = 'Processing Fee', category_type = 'Payment Processing', category_color = 'gray'");
$category_id = mysqli_insert_id($mysqli);
} else {
$row = mysqli_fetch_array($sql_category);
$category_id = intval($row['category_id']);
}
// Vendor
$sql_vendor = mysqli_query($mysqli,"SELECT vendor_id FROM vendors WHERE vendor_name = '$provider' AND vendor_client_id = 0 AND vendor_archived_at IS NULL LIMIT 1");
if (mysqli_num_rows($sql_vendor) == 0) {
mysqli_query($mysqli,"INSERT INTO vendors SET vendor_name = '$provider', vendor_description = 'Payment Processor Provider', vendor_client_id = 0");
$vendor_id = mysqli_insert_id($mysqli);
} else {
$row = mysqli_fetch_array($sql_vendor);
$vendor_id = intval($row['vendor_id']);
}
}
mysqli_query($mysqli,"INSERT INTO payment_providers SET payment_provider_name = '$provider', payment_provider_public_key = '$public_key', payment_provider_private_key = '$private_key', payment_provider_threshold = $threshold, payment_provider_account = $account_id, payment_provider_expense_vendor = $vendor_id, payment_provider_expense_category = $category_id, payment_provider_expense_percentage_fee = $percentage_fee, payment_provider_expense_flat_fee = $flat_fee");
$provider_id = mysqli_insert_id($mysqli);
logAction("Payment Provider", "Create", "$session_name created AI Provider $provider");
flash_alert("Payment provider <strong>$provider</strong> created");
redirect();
}
if (isset($_POST['edit_payment_provider'])) {
validateCSRFToken($_POST['csrf_token']);
$provider_id = intval($_POST['provider_id']);
$description = sanitizeInput($_POST['description']);
$public_key = sanitizeInput($_POST['public_key']);
$private_key = sanitizeInput($_POST['private_key']);
$threshold = floatval($_POST['threshold']);
$enable_expense = intval($_POST['enable_expense'] ?? 0);
$percentage_fee = floatval($_POST['percentage_fee']) / 100;
$flat_fee = floatval($_POST['flat_fee']);
mysqli_query($mysqli,"UPDATE payment_providers SET payment_provider_public_key = '$public_key', payment_provider_private_key = '$private_key', payment_provider_threshold = $threshold, payment_provider_expense_percentage_fee = $percentage_fee, payment_provider_expense_flat_fee = $flat_fee WHERE payment_provider_id = $provider_id");
logAction("Payment Provider", "Edit", "$session_name edited Payment Provider $provider");
flash_alert("Payment Provider <strong>$provider</strong> edited");
redirect();
}
if (isset($_GET['delete_payment_provider'])) {
validateCSRFToken($_GET['csrf_token']);
$provider_id = intval($_GET['delete_payment_provider']);
$provider_name = sanitizeInput(getFieldById('payment_providers', $provider_id, 'provider_name'));
// Delete provider
mysqli_query($mysqli,"DELETE FROM payment_providers WHERE payment_provider_id = $provider_id");
logAction("Payment Provider", "Delete", "$session_name deleted Payment Provider $provider_name");
flash_alert("Payment Provider <strong>$provider_name</strong> deleted", 'error');
redirect();
}

View File

@@ -0,0 +1,99 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_project_template'])) {
$name = sanitizeInput($_POST['name']);
$description = sanitizeInput($_POST['description']);
mysqli_query($mysqli, "INSERT INTO project_templates SET project_template_name = '$name', project_template_description = '$description'");
$project_template_id = mysqli_insert_id($mysqli);
logAction("Project Template", "Create", "$session_name created project template $name", 0, $project_template_id);
flash_alert("Project Template <strong>$name</strong> created");
redirect();
}
if (isset($_POST['edit_project_template'])) {
$project_template_id = intval($_POST['project_template_id']);
$name = sanitizeInput($_POST['name']);
$description = sanitizeInput($_POST['description']);
mysqli_query($mysqli, "UPDATE project_templates SET project_template_name = '$name', project_template_description = '$description' WHERE project_template_id = $project_template_id");
logAction("Project Template", "Edit", "$session_name edited project template $name", 0, $project_template_id);
flash_alert("Project Template <strong>$name</strong> edited");
redirect();
}
if (isset($_POST['edit_ticket_template_order'])) {
$ticket_template_id = intval($_POST['ticket_template_id']);
$project_template_id = intval($_POST['project_template_id']);
$order = intval($_POST['order']);
mysqli_query($mysqli, "UPDATE project_template_ticket_templates SET ticket_template_order = $order WHERE ticket_template_id = $ticket_template_id AND project_template_id = $project_template_id");
redirect();
}
if (isset($_POST['add_ticket_template_to_project_template'])) {
$project_template_id = intval($_POST['project_template_id']);
$ticket_template_id = intval($_POST['ticket_template_id']);
$order = intval($_POST['order']);
mysqli_query($mysqli, "INSERT INTO project_template_ticket_templates SET project_template_id = $project_template_id, ticket_template_id = $ticket_template_id, ticket_template_order = $order");
logAction("Project Template", "Edit", "$session_name added ticket template to project_template", 0, $project_template_id);
flash_alert("Ticket template added");
redirect();
}
if (isset($_POST['remove_ticket_template_from_project_template'])) {
validateTechRole();
$ticket_template_id = intval($_POST['ticket_template_id']);
$project_template_id = intval($_POST['project_template_id']);
mysqli_query($mysqli, "DELETE FROM project_template_ticket_templates WHERE project_template_id = $project_template_id AND ticket_template_id = $ticket_template_id");
logAction("Project Template", "Edit", "$session_name removed ticket template from project template", 0, $project_template_id);
flash_alert("Ticket template removed", 'error');
redirect();
}
if (isset($_GET['delete_project_template'])) {
$project_template_id = intval($_GET['delete_project_template']);
$project_template_name = sanitizeInput(getFieldById('project_templates', $project_template_id, 'project_template_name'));
mysqli_query($mysqli, "DELETE FROM project_templates WHERE project_template_id = $project_template_id");
// Remove Associated Ticket Templates
mysqli_query($mysqli, "DELETE FROM project_template_ticket_templates WHERE project_template_id = $project_template_id");
logAction("Project Template", "Delete", "$session_name deleted project template $project_template_name and its associated ticket templates and tasks");
flash_alert("Project Template <strong>$project_template_name</strong> and its associated ticket templates and tasks deleted", 'error');
redirect();
}

87
admin/post/roles.php Normal file
View File

@@ -0,0 +1,87 @@
<?php
/*
* ITFlow - GET/POST request handler for roles
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_role'])) {
validateCSRFToken($_POST['csrf_token']);
$name = sanitizeInput($_POST['role_name']);
$description = sanitizeInput($_POST['role_description']);
$admin = intval($_POST['role_is_admin']);
mysqli_query($mysqli, "INSERT INTO user_roles SET role_name = '$name', role_description = '$description', role_is_admin = $admin");
$role_id = mysqli_insert_id($mysqli);
logAction("User Role", "Create", "$session_name created user role $name", 0, $role_id);
flash_alert("User Role <strong$name</strong> created");
redirect();
}
if (isset($_POST['edit_role'])) {
validateCSRFToken($_POST['csrf_token']);
$role_id = intval($_POST['role_id']);
$name = sanitizeInput($_POST['role_name']);
$description = sanitizeInput($_POST['role_description']);
$admin = intval($_POST['role_is_admin']);
mysqli_query($mysqli, "UPDATE user_roles SET role_name = '$name', role_description = '$description', role_is_admin = $admin WHERE role_id = $role_id");
// Update role access levels
mysqli_query($mysqli, "DELETE FROM user_role_permissions WHERE user_role_id = $role_id");
foreach ($_POST as $key => $value) {
if (str_contains($key, '##module_')){
$module_id = intval(explode('##', $key)[0]);
$access_level = intval($value);
if ($access_level > 0) {
mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = $role_id, module_id = $module_id, user_role_permission_level = $access_level");
}
}
}
logAction("User Role", "Edit", "$session_name edited user role $name", 0, $role_id);
flash_alert("User Role <strong>$name</strong> edited");
redirect();
}
if (isset($_GET['archive_role'])) {
validateCSRFToken($_GET['csrf_token']);
$role_id = intval($_GET['archive_role']);
// Check role isn't in use
$sql_role_user_count = mysqli_query($mysqli, "SELECT COUNT(user_id) FROM users WHERE user_role_id = $role_id AND user_archived_at IS NULL");
$role_user_count = mysqli_fetch_row($sql_role_user_count)[0];
if ($role_user_count != 0) {
flash_alert("Role must not in use to archive it", 'error');
redirect();
}
mysqli_query($mysqli, "UPDATE user_roles SET role_archived_at = NOW() WHERE role_id = $role_id");
$role_name = sanitizeInput(getFieldById('roles', $role_id, 'role_name'));
logAction("User Role", "Archive", "$session_name archived user role $role_name", 0, $role_id);
flash_alert("User Role <strong>$role_name</strong> archived", 'error');
redirect();
}

View File

@@ -0,0 +1,70 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_GET['delete_saved_payment'])) {
validateCSRFToken($_GET['csrf_token']);
$saved_payment_id = intval($_GET['delete_saved_payment']);
$sql = mysqli_query($mysqli, "
SELECT
client_saved_payment_methods.saved_payment_id,
client_saved_payment_methods.saved_payment_client_id,
client_saved_payment_methods.saved_payment_provider_id,
client_saved_payment_methods.saved_payment_provider_method,
client_saved_payment_methods.saved_payment_description,
client_payment_provider.payment_provider_client,
payment_providers.payment_provider_name,
payment_providers.payment_provider_private_key
FROM client_saved_payment_methods
LEFT JOIN client_payment_provider
ON client_payment_provider.client_id = client_saved_payment_methods.saved_payment_client_id
AND client_payment_provider.payment_provider_id = client_saved_payment_methods.saved_payment_provider_id
LEFT JOIN payment_providers
ON payment_providers.payment_provider_id = client_saved_payment_methods.saved_payment_provider_id
WHERE client_saved_payment_methods.saved_payment_id = $saved_payment_id"
);
$row = mysqli_fetch_array($sql);
$client_id = intval($row['saved_payment_client_id']);
$provider_id = intval($row['saved_payment_provider_id']);
$payment_provider_name = nullable_htmlentities($row['payment_provider_name']);
$saved_payment_description = nullable_htmlentities($row['saved_payment_description']);
$provider_client = nullable_htmlentities($row['payment_provider_client']);
$payment_method = $row['saved_payment_provider_method'];
$private_key = $row['payment_provider_private_key'];
// Separate logic for each Payment Provider
if ($payment_provider_name == 'Stripe') {
try {
// Initialize stripe
require_once 'plugins/stripe-php/init.php';
$stripe = new \Stripe\StripeClient($private_key);
// Detach PM
$stripe->paymentMethods->detach($payment_method, []);
} catch (Exception $e) {
$error = $e->getMessage();
error_log("Stripe payment error - encountered exception when removing payment method info for $payment_method: $error");
logApp("Stripe", "error", "Exception removing payment method for $payment_method: $error");
}
}
// Remove payment method from ITFlow
mysqli_query($mysqli, "DELETE FROM client_saved_payment_methods WHERE saved_payment_id = $saved_payment_id");
// SQL Cascade delete will Remove All Associated Auto Payment Methods on recurring invoices in the recurring payments table.
logAction("Payment Provider", "Update", "$session_name deleted saved payment method $saved_payment_description (PM: $payment_method)", $client_id);
flash_alert("Payment method <strong>$saved_payment_description</strong> removed", 'error');
redirect();
}

View File

@@ -0,0 +1,27 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_ai_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$provider = sanitizeInput($_POST['provider']);
if($provider){
$ai_enable = 1;
} else {
$ai_enable = 0;
}
$model = sanitizeInput($_POST['model']);
$url = sanitizeInput($_POST['url']);
$api_key = sanitizeInput($_POST['api_key']);
mysqli_query($mysqli,"UPDATE settings SET config_ai_enable = $ai_enable, config_ai_provider = '$provider', config_ai_model = '$model', config_ai_url = '$url', config_ai_api_key = '$api_key' WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited AI settings");
flash_alert("AI Settings updated");
redirect();
}

View File

@@ -0,0 +1,71 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_company'])) {
validateCSRFToken($_POST['csrf_token']);
$name = sanitizeInput($_POST['name']);
$address = sanitizeInput($_POST['address']);
$city = sanitizeInput($_POST['city']);
$state = sanitizeInput($_POST['state']);
$zip = sanitizeInput($_POST['zip']);
$country = sanitizeInput($_POST['country']);
$phone_country_code = preg_replace("/[^0-9]/", '',$_POST['phone_country_code']);
$phone = preg_replace("/[^0-9]/", '',$_POST['phone']);
$email = sanitizeInput($_POST['email']);
$website = sanitizeInput($_POST['website']);
$tax_id = sanitizeInput($_POST['tax_id']);
$sql = mysqli_query($mysqli,"SELECT company_logo FROM companies WHERE company_id = 1");
$row = mysqli_fetch_array($sql);
$existing_file_name = sanitizeInput($row['company_logo']);
// Company logo
if (isset($_FILES['file']['tmp_name'])) {
if ($new_file_name = checkFileUpload($_FILES['file'], array('jpg', 'jpeg', 'png'))) {
$file_tmp_path = $_FILES['file']['tmp_name'];
// directory in which the uploaded file will be moved
$upload_file_dir = "../uploads/settings/";
$dest_path = $upload_file_dir . $new_file_name;
move_uploaded_file($file_tmp_path, $dest_path);
// Delete old file
unlink("../uploads/settings/$existing_file_name");
// Set Logo
mysqli_query($mysqli,"UPDATE companies SET company_logo = '$new_file_name' WHERE company_id = 1");
}
}
mysqli_query($mysqli,"UPDATE companies SET company_name = '$name', company_address = '$address', company_city = '$city', company_state = '$state', company_zip = '$zip', company_country = '$country', company_phone_country_code = '$phone_country_code', company_phone = '$phone', company_email = '$email', company_website = '$website', company_tax_id = '$tax_id' WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited company details");
flash_alert("Company <strong>$name</strong> edited");
redirect();
}
if (isset($_GET['remove_company_logo'])) {
$sql = mysqli_query($mysqli,"SELECT company_logo FROM companies");
$row = mysqli_fetch_array($sql);
$company_logo = $row['company_logo']; // FileSystem Operation Logo is already sanitized
unlink("../uploads/settings/$company_logo");
mysqli_query($mysqli,"UPDATE companies SET company_logo = NULL WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name deleted company logo");
flash_alert("Removed company logo", 'error');
redirect();
}

View File

@@ -0,0 +1,28 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_default_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$start_page = sanitizeInput($_POST['start_page']);
$expense_account = intval($_POST['expense_account']);
$payment_account = intval($_POST['payment_account']);
$payment_method = sanitizeInput($_POST['payment_method']);
$expense_payment_method = sanitizeInput($_POST['expense_payment_method']);
$transfer_from_account = intval($_POST['transfer_from_account']);
$transfer_to_account = intval($_POST['transfer_to_account']);
$calendar = intval($_POST['calendar']);
$net_terms = intval($_POST['net_terms']);
$hourly_rate = floatval($_POST['hourly_rate']);
mysqli_query($mysqli,"UPDATE settings SET config_start_page = '$start_page', config_default_expense_account = $expense_account, config_default_payment_account = $payment_account, config_default_payment_method = '$payment_method', config_default_expense_payment_method = '$expense_payment_method', config_default_transfer_from_account = $transfer_from_account, config_default_transfer_to_account = $transfer_to_account, config_default_calendar = $calendar, config_default_net_terms = $net_terms, config_default_hourly_rate = $hourly_rate WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited default settings");
flash_alert("Default settings edited");
redirect();
}

View File

@@ -0,0 +1,30 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_invoice_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_invoice_prefix = sanitizeInput($_POST['config_invoice_prefix']);
$config_invoice_next_number = intval($_POST['config_invoice_next_number']);
$config_invoice_footer = sanitizeInput($_POST['config_invoice_footer']);
$config_invoice_show_tax_id = intval($_POST['config_invoice_show_tax_id'] ?? 0);
$config_invoice_late_fee_enable = intval($_POST['config_invoice_late_fee_enable'] ?? 0);
$config_invoice_late_fee_percent = floatval($_POST['config_invoice_late_fee_percent']);
$config_recurring_invoice_prefix = sanitizeInput($_POST['config_recurring_invoice_prefix']);
$config_recurring_invoice_next_number = intval($_POST['config_recurring_invoice_next_number']);
$config_invoice_paid_notification_email = '';
if (filter_var($_POST['config_invoice_paid_notification_email'], FILTER_VALIDATE_EMAIL)) {
$config_invoice_paid_notification_email = sanitizeInput($_POST['config_invoice_paid_notification_email']);
}
mysqli_query($mysqli,"UPDATE settings SET config_invoice_prefix = '$config_invoice_prefix', config_invoice_next_number = $config_invoice_next_number, config_invoice_footer = '$config_invoice_footer', config_invoice_show_tax_id = $config_invoice_show_tax_id, config_invoice_late_fee_enable = $config_invoice_late_fee_enable, config_invoice_late_fee_percent = $config_invoice_late_fee_percent, config_invoice_paid_notification_email = '$config_invoice_paid_notification_email', config_recurring_invoice_prefix = '$config_recurring_invoice_prefix', config_recurring_invoice_next_number = $config_recurring_invoice_next_number WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited invoice settings");
flash_alert("Invoice Settings edited");
redirect();
}

View File

@@ -0,0 +1,23 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_localization'])) {
validateCSRFToken($_POST['csrf_token']);
$locale = sanitizeInput($_POST['locale']);
$currency_code = sanitizeInput($_POST['currency_code']);
$timezone = sanitizeInput($_POST['timezone']);
mysqli_query($mysqli,"UPDATE companies SET company_locale = '$locale', company_currency = '$currency_code' WHERE company_id = 1");
mysqli_query($mysqli,"UPDATE settings SET config_timezone = '$timezone' WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited localization settings");
flash_alert("Company localization updated");
redirect();
}

View File

@@ -0,0 +1,186 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_mail_smtp_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_smtp_provider = sanitizeInput($_POST['config_smtp_provider']);
$config_smtp_host = sanitizeInput($_POST['config_smtp_host']);
$config_smtp_port = intval($_POST['config_smtp_port'] ?? 0);
$config_smtp_encryption = sanitizeInput($_POST['config_smtp_encryption']);
$config_smtp_username = sanitizeInput($_POST['config_smtp_username']);
$config_smtp_password = sanitizeInput($_POST['config_smtp_password']);
// Shared OAuth fields
$config_mail_oauth_client_id = sanitizeInput($_POST['config_mail_oauth_client_id']);
$config_mail_oauth_client_secret = sanitizeInput($_POST['config_mail_oauth_client_secret']);
$config_mail_oauth_tenant_id = sanitizeInput($_POST['config_mail_oauth_tenant_id']);
$config_mail_oauth_refresh_token = sanitizeInput($_POST['config_mail_oauth_refresh_token']);
$config_mail_oauth_access_token = sanitizeInput($_POST['config_mail_oauth_access_token']);
mysqli_query($mysqli, "
UPDATE settings SET
config_smtp_provider = '$config_smtp_provider',
config_smtp_host = '$config_smtp_host',
config_smtp_port = $config_smtp_port,
config_smtp_encryption = '$config_smtp_encryption',
config_smtp_username = '$config_smtp_username',
config_smtp_password = '$config_smtp_password',
config_mail_oauth_client_id = '$config_mail_oauth_client_id',
config_mail_oauth_client_secret = '$config_mail_oauth_client_secret',
config_mail_oauth_tenant_id = '$config_mail_oauth_tenant_id',
config_mail_oauth_refresh_token = '$config_mail_oauth_refresh_token',
config_mail_oauth_access_token = '$config_mail_oauth_access_token'
WHERE company_id = 1
");
logAction("Settings", "Edit", "$session_name edited SMTP settings");
flash_alert("SMTP Mail Settings updated");
redirect();
}
if (isset($_POST['edit_mail_imap_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_imap_provider = sanitizeInput($_POST['config_imap_provider']);
$config_imap_host = sanitizeInput($_POST['config_imap_host']);
$config_imap_port = intval($_POST['config_imap_port'] ?? 0);
$config_imap_encryption = sanitizeInput($_POST['config_imap_encryption']);
$config_imap_username = sanitizeInput($_POST['config_imap_username']);
$config_imap_password = sanitizeInput($_POST['config_imap_password']);
// Shared OAuth fields
$config_mail_oauth_client_id = sanitizeInput($_POST['config_mail_oauth_client_id']);
$config_mail_oauth_client_secret = sanitizeInput($_POST['config_mail_oauth_client_secret']);
$config_mail_oauth_tenant_id = sanitizeInput($_POST['config_mail_oauth_tenant_id']);
$config_mail_oauth_refresh_token = sanitizeInput($_POST['config_mail_oauth_refresh_token']);
$config_mail_oauth_access_token = sanitizeInput($_POST['config_mail_oauth_access_token']);
mysqli_query($mysqli, "
UPDATE settings SET
config_imap_provider = '$config_imap_provider',
config_imap_host = '$config_imap_host',
config_imap_port = $config_imap_port,
config_imap_encryption = '$config_imap_encryption',
config_imap_username = '$config_imap_username',
config_imap_password = '$config_imap_password',
config_mail_oauth_client_id = '$config_mail_oauth_client_id',
config_mail_oauth_client_secret = '$config_mail_oauth_client_secret',
config_mail_oauth_tenant_id = '$config_mail_oauth_tenant_id',
config_mail_oauth_refresh_token = '$config_mail_oauth_refresh_token',
config_mail_oauth_access_token = '$config_mail_oauth_access_token'
WHERE company_id = 1
");
logAction("Settings", "Edit", "$session_name edited IMAP settings");
flash_alert("IMAP Mail Settings updated");
redirect();
}
if (isset($_POST['edit_mail_from_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_mail_from_email = sanitizeInput(filter_var($_POST['config_mail_from_email'], FILTER_VALIDATE_EMAIL));
$config_mail_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_mail_from_name']));
$config_invoice_from_email = sanitizeInput(filter_var($_POST['config_invoice_from_email'], FILTER_VALIDATE_EMAIL));
$config_invoice_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_invoice_from_name']));
$config_quote_from_email = sanitizeInput(filter_var($_POST['config_quote_from_email'], FILTER_VALIDATE_EMAIL));
$config_quote_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_quote_from_name']));
$config_ticket_from_email = sanitizeInput(filter_var($_POST['config_ticket_from_email'], FILTER_VALIDATE_EMAIL));
$config_ticket_from_name = sanitizeInput(preg_replace('/[^a-zA-Z0-9\s]/', '', $_POST['config_ticket_from_name']));
mysqli_query($mysqli,"UPDATE settings SET config_mail_from_email = '$config_mail_from_email', config_mail_from_name = '$config_mail_from_name', config_invoice_from_email = '$config_invoice_from_email', config_invoice_from_name = '$config_invoice_from_name', config_quote_from_email = '$config_quote_from_email', config_quote_from_name = '$config_quote_from_name', config_ticket_from_email = '$config_ticket_from_email', config_ticket_from_name = '$config_ticket_from_name' WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited mail from settings");
flash_alert("Mail From Settings updated");
redirect();
}
if (isset($_POST['test_email_smtp'])) {
validateCSRFToken($_POST['csrf_token']);
$test_email = intval($_POST['test_email']);
if($test_email == 1) {
$email_from = sanitizeInput($config_mail_from_email);
$email_from_name = sanitizeInput($config_mail_from_name);
} elseif ($test_email == 2) {
$email_from = sanitizeInput($config_invoice_from_email);
$email_from_name = sanitizeInput($config_invoice_from_name);
} elseif ($test_email == 3) {
$email_from = sanitizeInput($config_quote_from_email);
$email_from_name = sanitizeInput($config_quote_from_name);
} else {
$email_from = sanitizeInput($config_ticket_from_email);
$email_from_name = sanitizeInput($config_ticket_from_name);
}
$email_to = sanitizeInput($_POST['email_to']);
$subject = "Test email from ITFlow";
$body = "This is a test email from ITFlow. If you are reading this, it worked!";
$data = [
[
'from' => $email_from,
'from_name' => $email_from_name,
'recipient' => $email_to,
'recipient_name' => 'Chap',
'subject' => $subject,
'body' => $body
]
];
$mail = addToMailQueue($data);
if ($mail === true) {
flash_alert("Test email queued! <a class='text-bold text-light' href='admin_mail_queue.php'>Check Admin > Mail queue</a>");
} else {
flash_alert("Failed to add test mail to queue", 'error');
}
redirect();
}
if (isset($_POST['test_email_imap'])) {
validateCSRFToken($_POST['csrf_token']);
// Setup your IMAP connection parameters
$hostname = "{" . $config_imap_host . ":" . $config_imap_port . "/" . $config_imap_encryption . "/novalidate-cert}INBOX";
$username = $config_imap_username;
$password = $config_imap_password;
try {
$inbox = @imap_open($hostname, $username, $password);
if ($inbox) {
imap_close($inbox);
flash_alert("Connected successfully");
} else {
throw new Exception(imap_last_error());
}
} catch (Exception $e) {
flash_alert("<strong>IMAP connection failed:</strong> " . $e->getMessage(), 'error');
}
redirect();
}

View File

@@ -0,0 +1,28 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_module_settings'])) {
$config_module_enable_itdoc = intval($_POST['config_module_enable_itdoc'] ?? 0);
$config_module_enable_ticketing = intval($_POST['config_module_enable_ticketing'] ?? 0);
$config_module_enable_accounting = intval($_POST['config_module_enable_accounting'] ?? 0);
$config_client_portal_enable = intval($_POST['config_client_portal_enable'] ?? 0);
$config_whitelabel_key = sanitizeInput($_POST['config_whitelabel_key']);
mysqli_query($mysqli,"UPDATE settings SET config_module_enable_itdoc = $config_module_enable_itdoc, config_module_enable_ticketing = $config_module_enable_ticketing, config_module_enable_accounting = $config_module_enable_accounting, config_client_portal_enable = $config_client_portal_enable WHERE company_id = 1");
// Validate white label key
if (!empty($config_whitelabel_key && validateWhitelabelKey($config_whitelabel_key))) {
mysqli_query($mysqli, "UPDATE settings SET config_whitelabel_enabled = 1, config_whitelabel_key = '$config_whitelabel_key' WHERE company_id = 1");
} else {
mysqli_query($mysqli, "UPDATE settings SET config_whitelabel_enabled = 0, config_whitelabel_key = '' WHERE company_id = 1");
}
logAction("Settings", "Edit", "$session_name edited module settings");
flash_alert("Module Settings updated");
redirect();
}

View File

@@ -0,0 +1,23 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_notification_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_enable_cron = intval($_POST['config_enable_cron'] ?? 0);
$config_enable_alert_domain_expire = intval($_POST['config_enable_alert_domain_expire'] ?? 0);
$config_send_invoice_reminders = intval($_POST['config_send_invoice_reminders'] ?? 0);
$config_recurring_auto_send_invoice = intval($_POST['config_recurring_auto_send_invoice'] ?? 0);
$config_ticket_client_general_notifications = intval($_POST['config_ticket_client_general_notifications'] ?? 0);
mysqli_query($mysqli,"UPDATE settings SET config_send_invoice_reminders = $config_send_invoice_reminders, config_recurring_auto_send_invoice = $config_recurring_auto_send_invoice, config_enable_cron = $config_enable_cron, config_enable_alert_domain_expire = $config_enable_alert_domain_expire, config_ticket_client_general_notifications = $config_ticket_client_general_notifications WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited notification settings");
flash_alert("Notification Settings updated");
redirect();
}

View File

@@ -0,0 +1,30 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_online_payment_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_stripe_enable = intval($_POST['config_stripe_enable'] ?? 0);
$config_stripe_publishable = sanitizeInput($_POST['config_stripe_publishable']);
$config_stripe_secret = sanitizeInput($_POST['config_stripe_secret']);
$config_stripe_account = intval($_POST['config_stripe_account']);
$config_stripe_expense_vendor = intval($_POST['config_stripe_expense_vendor']);
$config_stripe_expense_category = intval($_POST['config_stripe_expense_category']);
$config_stripe_percentage_fee = floatval($_POST['config_stripe_percentage_fee']) / 100;
$config_stripe_flat_fee = floatval($_POST['config_stripe_flat_fee']);
mysqli_query($mysqli,"UPDATE settings SET config_stripe_enable = $config_stripe_enable, config_stripe_publishable = '$config_stripe_publishable', config_stripe_secret = '$config_stripe_secret', config_stripe_account = $config_stripe_account, config_stripe_expense_vendor = $config_stripe_expense_vendor, config_stripe_expense_category = $config_stripe_expense_category, config_stripe_percentage_fee = $config_stripe_percentage_fee, config_stripe_flat_fee = $config_stripe_flat_fee WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited online payment settings");
if ($config_stripe_enable && $config_stripe_account == 0) {
flash_alert("Stripe payment account must be specified!", 'error');
} else {
flash_alert("Online Payment Settings updated");
}
redirect();
}

View File

@@ -0,0 +1,73 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_GET['stripe_remove_pm'])) {
validateCSRFToken($_GET['csrf_token']);
if (!$config_stripe_enable) {
flash_alert("Stripe not enabled", 'error');
redirect();
}
$client_id = intval($_GET['client_id']);
$payment_method = sanitizeInput($_GET['pm']);
try {
// Initialize stripe
require_once '../plugins/stripe-php/init.php';
$stripe = new \Stripe\StripeClient($config_stripe_secret);
// Detach PM
$stripe->paymentMethods->detach($payment_method, []);
} catch (Exception $e) {
$error = $e->getMessage();
error_log("Stripe payment error - encountered exception when removing payment method info for $payment_method: $error");
logApp("Stripe", "error", "Exception removing payment method for $payment_method: $error");
}
// Remove payment method from ITFlow
mysqli_query($mysqli, "UPDATE client_stripe SET stripe_pm = NULL WHERE client_id = $client_id LIMIT 1");
// Remove Auto Pay on recurring invoices that are stripe
$sql_recurring_invoices = mysqli_query($mysqli, "SELECT recurring_invoice_id FROM recurring_invoices WHERE recurring_invoice_client_id = $client_id");
while ($row = mysqli_fetch_array($sql_recurring_invoices)) {
$recurring_invoice_id = intval($row['recurring_invoice_id']);
mysqli_query($mysqli, "DELETE FROM recurring_payments WHERE recurring_payment_method = 'Stripe' AND recurring_payment_recurring_invoice_id = $recurring_invoice_id");
}
logAction("Stripe", "Update", "$session_name deleted saved Stripe payment method (PM: $payment_method)", $client_id);
flash_alert("Payment method removed", 'error');
redirect();
}
if (isset($_GET['stripe_reset_customer'])) {
validateCSRFToken($_GET['csrf_token']);
$client_id = intval($_GET['client_id']);
// Delete the customer id and payment method id stored in ITFlow, allowing the client to set these up again
mysqli_query($mysqli, "DELETE FROM client_stripe WHERE client_id = $client_id");
// Remove Auto Pay on recurring invoices that are stripe
$sql_recurring_invoices = mysqli_query($mysqli, "SELECT recurring_invoice_id FROM recurring_invoices WHERE recurring_invoice_client_id = $client_id");
while ($row = mysqli_fetch_array($sql_recurring_invoices)) {
$recurring_invoice_id = intval($row['recurring_invoice_id']);
mysqli_query($mysqli, "DELETE FROM recurring_payments WHERE recurring_payment_method = 'Stripe' AND recurring_payment_recurring_invoice_id = $recurring_invoice_id");
}
logAction("Stripe", "Delete", "$session_name reset Stripe settings for client", $client_id);
flash_alert("Reset client Stripe settings", 'error');
redirect();
}

View File

@@ -0,0 +1,20 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_project_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_project_prefix = sanitizeInput($_POST['config_project_prefix']);
$config_project_next_number = intval($_POST['config_project_next_number']);
mysqli_query($mysqli,"UPDATE settings SET config_project_prefix = '$config_project_prefix', config_project_next_number = $config_project_next_number WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited project settings");
flash_alert("Project Settings updated");
redirect();
}

View File

@@ -0,0 +1,25 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_quote_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_quote_prefix = sanitizeInput($_POST['config_quote_prefix']);
$config_quote_next_number = intval($_POST['config_quote_next_number']);
$config_quote_footer = sanitizeInput($_POST['config_quote_footer']);
$config_quote_notification_email = '';
if (filter_var($_POST['config_quote_notification_email'], FILTER_VALIDATE_EMAIL)) {
$config_quote_notification_email = sanitizeInput($_POST['config_quote_notification_email']);
}
mysqli_query($mysqli,"UPDATE settings SET config_quote_prefix = '$config_quote_prefix', config_quote_next_number = $config_quote_next_number, config_quote_footer = '$config_quote_footer', config_quote_notification_email = '$config_quote_notification_email' WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited Quote settings");
flash_alert("Quote Settings updated");
redirect();
}

View File

@@ -0,0 +1,28 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_security_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_login_message = sanitizeInput($_POST['config_login_message']);
$config_login_key_required = intval($_POST['config_login_key_required'] ?? 0);
$config_login_key_secret = sanitizeInput($_POST['config_login_key_secret']);
$config_login_remember_me_expire = intval($_POST['config_login_remember_me_expire']);
$config_log_retention = intval($_POST['config_log_retention']);
// Disallow turning on login key without a secret
if (empty($config_login_key_secret)) {
$config_login_key_required = 0;
}
mysqli_query($mysqli,"UPDATE settings SET config_login_message = '$config_login_message', config_login_key_required = '$config_login_key_required', config_login_key_secret = '$config_login_key_secret', config_login_remember_me_expire = $config_login_remember_me_expire, config_log_retention = $config_log_retention WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited security settings");
flash_alert("Security settings updated");
redirect();
}

View File

@@ -0,0 +1,19 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_telemetry_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$config_telemetry = intval($_POST['config_telemetry']);
mysqli_query($mysqli,"UPDATE settings SET config_telemetry = $config_telemetry WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited telemetry settings");
flash_alert("Telemetry Settings updated");
redirect();
}

View File

@@ -0,0 +1,51 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_theme_settings'])) {
validateCSRFToken($_POST['csrf_token']);
$theme = preg_replace("/[^0-9a-zA-Z-]/", "", sanitizeInput($_POST['edit_theme_settings']));
mysqli_query($mysqli,"UPDATE settings SET config_theme = '$theme' WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited theme settings $dark_mode");
flash_alert("Changed theme to <strong>$theme</strong>");
redirect();
}
if (isset($_POST['edit_favicon_settings'])) {
validateCSRFToken($_POST['csrf_token']);
// Check to see if a file is attached
if (isset($_FILES['file']['tmp_name'])) {
if ($new_file_name = checkFileUpload($_FILES['file'], array('ico'))) {
$file_tmp_path = $_FILES['file']['tmp_name'];
// Delete old file
if(file_exists("../uploads/favicon.ico")) {
unlink("../uploads/favicon.ico");
}
// directory in which the uploaded file will be moved
$upload_file_dir = "../uploads/";
//Force File Name
$new_file_name = "favicon.ico";
$dest_path = $upload_file_dir . $new_file_name;
move_uploaded_file($file_tmp_path, $dest_path);
}
}
logAction("Settings", "Edit", "$session_name changed the favicon");
flash_alert("Favicon Updated");
redirect();
}

View File

@@ -0,0 +1,30 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['edit_ticket_settings'])) {
$config_ticket_prefix = sanitizeInput($_POST['config_ticket_prefix']);
$config_ticket_next_number = intval($_POST['config_ticket_next_number']);
$config_ticket_email_parse = intval($_POST['config_ticket_email_parse'] ?? 0);
$config_ticket_email_parse_unknown_senders = intval($_POST['config_ticket_email_parse_unknown_senders'] ?? 0);
$config_ticket_default_billable = intval($_POST['config_ticket_default_billable'] ?? 0);
$config_ticket_autoclose_hours = intval($_POST['config_ticket_autoclose_hours']);
$config_ticket_new_ticket_notification_email = '';
if (filter_var($_POST['config_ticket_new_ticket_notification_email'], FILTER_VALIDATE_EMAIL)) {
$config_ticket_new_ticket_notification_email = sanitizeInput($_POST['config_ticket_new_ticket_notification_email']);
}
$config_ticket_default_view = intval($_POST['config_ticket_default_view']);
$config_ticket_moving_columns = intval($_POST['config_ticket_moving_columns']);
$config_ticket_ordering = intval($_POST['config_ticket_ordering']);
$config_ticket_timer_autostart = intval($_POST['config_ticket_timer_autostart']);
mysqli_query($mysqli,"UPDATE settings SET config_ticket_prefix = '$config_ticket_prefix', config_ticket_next_number = $config_ticket_next_number, config_ticket_email_parse = $config_ticket_email_parse, config_ticket_email_parse_unknown_senders = $config_ticket_email_parse_unknown_senders, config_ticket_autoclose_hours = $config_ticket_autoclose_hours, config_ticket_new_ticket_notification_email = '$config_ticket_new_ticket_notification_email', config_ticket_default_billable = $config_ticket_default_billable, config_ticket_default_view = $config_ticket_default_view, config_ticket_moving_columns = $config_ticket_moving_columns, config_ticket_ordering = $config_ticket_ordering, config_ticket_timer_autostart = $config_ticket_timer_autostart WHERE company_id = 1");
logAction("Settings", "Edit", "$session_name edited ticket settings");
flash_alert("Ticket Settings updated");
redirect();
}

View File

@@ -0,0 +1,65 @@
<?php
// Software/License Templates
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_software_template'])) {
$name = sanitizeInput($_POST['name']);
$version = sanitizeInput($_POST['version']);
$description = sanitizeInput($_POST['description']);
$type = sanitizeInput($_POST['type']);
$license_type = sanitizeInput($_POST['license_type']);
$notes = sanitizeInput($_POST['notes']);
mysqli_query($mysqli,"INSERT INTO software_templates SET software_template_name = '$name', software_template_version = '$version', software_template_description = '$description', software_template_type = '$type', software_template_license_type = '$license_type', software_template_notes = '$notes'");
$software_template_id = mysqli_insert_id($mysqli);
logAction("Software Template", "Create", "$session_name created software template $name", 0, $software_template_id);
flash_alert("Software template <strong>$name</strong> created");
redirect();
}
if (isset($_POST['edit_software_template'])) {
$software_template_id = intval($_POST['software_template_id']);
$name = sanitizeInput($_POST['name']);
$version = sanitizeInput($_POST['version']);
$description = sanitizeInput($_POST['description']);
$type = sanitizeInput($_POST['type']);
$license_type = sanitizeInput($_POST['license_type']);
$notes = sanitizeInput($_POST['notes']);
mysqli_query($mysqli,"UPDATE software_templates SET software_template_name = '$name', software_template_version = '$version', software_template_description = '$description', software_template_type = '$type', software_template_license_type = '$license_type', software_template_notes = '$notes' WHERE software_template_id = $software_template_id");
logAction("Software Template", "Edit", "$session_name edited software template $name", 0, $software_template_id);
flash_alert("Software template <strong>$name</strong> edited");
redirect();
}
if (isset($_GET['delete_software_template'])) {
$software_template_id = intval($_GET['delete_software_template']);
// Get Software Template Name for logging and alert message
$sql = mysqli_query($mysqli,"SELECT software_template_name FROM software_templates WHERE software_template_id = $software_template_id");
$row = mysqli_fetch_array($sql);
$software_template_name = sanitizeInput($row['software_template_name']);
mysqli_query($mysqli,"DELETE FROM software_templates WHERE software_template_id = $software_template_id");
logAction("Software Template", "Delete", "$session_name deleted software template $software_template_name");
flash_alert("Software Template <strong>$software_template_name</strong> deleted", 'error');
redirect();
}

55
admin/post/tag.php Normal file
View File

@@ -0,0 +1,55 @@
<?php
/*
* ITFlow - GET/POST request handler for tagging
*/
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
if (isset($_POST['add_tag'])) {
require_once 'tag_model.php';
mysqli_query($mysqli,"INSERT INTO tags SET tag_name = '$name', tag_type = $type, tag_color = '$color', tag_icon = '$icon'");
$tag_id = mysqli_insert_id($mysqli);
logAction("Tag", "Create", "$session_name created tag $name", 0, $tag_id);
flash_alert("Tag <strong>$name</strong> created");
redirect();
}
if (isset($_POST['edit_tag'])) {
require_once 'post/tag_model.php';
$tag_id = intval($_POST['tag_id']);
mysqli_query($mysqli,"UPDATE tags SET tag_name = '$name', tag_type = $type, tag_color = '$color', tag_icon = '$icon' WHERE tag_id = $tag_id");
logAction("Tag", "Edit", "$session_name edited tag $name", 0, $tag_id);
flash_alert("Tag <strong>$name</strong> edited");
redirect();
}
if (isset($_GET['delete_tag'])) {
$tag_id = intval($_GET['delete_tag']);
$tag_name = sanitizeInput(getFieldById('tags', $tag_id, 'tag_name'));
mysqli_query($mysqli,"DELETE FROM tags WHERE tag_id = $tag_id");
logAction("Tag", "Delete", "$session_name deleted tag $tag_name");
flash_alert("Tag <strong>$tag_name</strong> deleted", 'error');
redirect();
}

7
admin/post/tag_model.php Normal file
View File

@@ -0,0 +1,7 @@
<?php
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
$name = sanitizeInput($_POST['name']);
$type = intval($_POST['type']);
$color = sanitizeInput($_POST['color']);
$icon = preg_replace("/[^0-9a-zA-Z-]/", "", sanitizeInput($_POST['icon']));

Some files were not shown because too many files have changed in this diff Show More