Feature: On all export modals added Filter Tab and Selectable Columns tab with default selected, also you can now export to a PDF

This commit is contained in:
johnnyq
2026-07-30 00:46:15 -04:00
parent 729d22d19b
commit 3c8f812a16
61 changed files with 3997 additions and 829 deletions

View File

@@ -1,25 +1,70 @@
<?php
require_once '../../includes/modal_header.php';
require_once '../../../includes/modal_header.php';
enforceAdminPermission();
// Pre-filled from the users page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Archived Filter
$archived_filter = (isset($_GET['archived']) && $_GET['archived'] == 1) ? 1 : 0;
ob_start();
?>
<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>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Users</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Name or email">
</div>
</div>
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>
</div>
</div>
<?php exportTabsColumns('users'); ?>
</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>
<?php renderExportButtons('export_users'); ?>
</div>
</form>

View File

@@ -301,57 +301,68 @@ if (isset($_POST['restore_user'])) {
}
if (isset($_POST['export_users_csv'])) {
if (isset($_POST['export_users'])) {
validateCSRFToken();
//get records from database
$sql = mysqli_query($mysqli, "SELECT * FROM users LEFT JOIN user_roles ON user_role_id = role_id ORDER BY user_name ASC");
enforceAdminPermission();
$count = mysqli_num_rows($sql);
$format = resolveExportFormat($_POST['export_users']);
if ($count > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = "Users-" . date('Y-m-d') . ".csv";
// Filters inherited from the users page - mirrors admin/users.php
$filter_summary = [];
//create a file pointer
$f = fopen('php://memory', 'w');
// Archived Filter
if (isset($_POST['archived']) && $_POST['archived'] == 1) {
$archive_query = "user_archived_at IS NOT NULL";
$filter_summary['Archived'] = 'Archived only';
} else {
$archive_query = "user_archived_at IS NULL";
}
//set column headers
$fields = array('Name', 'Email', 'Role', 'Status', 'Creation Date');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
//output each row of the data, format line as csv and write to file pointer
while($row = $sql->fetch_assoc()) {
$sql = mysqli_query(
$mysqli,
"SELECT * FROM users
LEFT JOIN user_roles ON user_role_id = role_id
WHERE (user_name LIKE '%$q%' OR user_email LIKE '%$q%')
AND user_type = 1
AND $archive_query
ORDER BY user_name ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
guardExportPdfRowCount($format, $num_rows);
$export = beginExport('users', $format, "$session_company_name-Users", 'Users', summarizeExportFilters($filter_summary));
while ($row = mysqli_fetch_assoc($sql)) {
$user_status = intval($row['user_status']);
if ($user_status == 2) {
$user_status_display = "Invited";
$row['user_status_display'] = "Invited";
} elseif ($user_status == 1) {
$user_status_display = "Active";
} else{
$user_status_display = "Disabled";
$row['user_status_display'] = "Active";
} else {
$row['user_status_display'] = "Disabled";
}
$lineData = array($row['user_name'], $row['user_email'], $row['role_name'], $user_status_display, $row['user_created_at']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
// Logging
logAudit("User", "Export", "$session_name exported $count user(s) to a CSV file");
finishExport($export);
}
logAudit("User", "Export", "$session_name exported $num_rows user(s) to a " . strtoupper($format) . " file");
exit;
}

View File

@@ -34,7 +34,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<!--<a class="dropdown-item text-dark ajax-modal" href="#" data-modal-url="modals/user/user_invite.php"><i class="fas fa-paper-plane mr-2"></i>Invite User</a>-->
<?php if ($num_rows[0] > 1) { ?>
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/user/user_export.php">
data-modal-url="<?= buildExportModalUrl('modals/user/user_export.php', ['archived', 'q']) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
<div class="dropdown-divider"></div>

View File

@@ -225,7 +225,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<?php if ($num_rows[0] > 0) { ?>
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/asset/asset_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/asset/asset_export.php', ['client_id', 'type', 'client', 'location', 'tags', 'expire_days', 'archived', 'q']) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
<?php } ?>

View File

@@ -85,7 +85,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button>
<div class="dropdown-menu">
<a class="dropdown-item text-dark ajax-modal" href="#"\
data-modal-url="modals/certificate/certificate_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/certificate/certificate_export.php', ['client_id', 'client', 'expire_days', 'archived', 'q']) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
</div>

View File

@@ -98,7 +98,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/client/client_export.php">
data-modal-url="<?= buildExportModalUrl('modals/client/client_export.php', ['leads', 'tags', 'industry', 'referral', 'q', 'archived'], ['dtf' => $dtf, 'dtt' => $dtt]) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
</div>

View File

@@ -105,7 +105,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="dropdown-divider"></div>
<?php } ?>
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/contact/contact_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/contact/contact_export.php', ['client_id', 'client', 'location', 'tags', 'archived', 'q']) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
</div>

View File

@@ -120,7 +120,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<?php } ?>
<?php if ($num_rows[0] > 0) { ?>
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/credential/credential_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/credential/credential_export.php', ['client_id', 'client', 'tags', 'archived', 'q']) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
<?php } ?>

View File

@@ -97,7 +97,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button>
<div class="dropdown-menu">
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/domain/domain_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/domain/domain_export.php', ['client_id', 'client', 'expire_days', 'archived', 'q']) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
</div>

View File

@@ -69,7 +69,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button>
<div class="dropdown-menu">
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/expense/expense_export.php">
data-modal-url="<?= buildExportModalUrl('modals/expense/expense_export.php', ['account', 'vendor', 'category', 'q'], ['dtf' => $dtf, 'dtt' => $dtt]) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
</div>

View File

@@ -171,7 +171,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button>
<div class="dropdown-menu">
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/invoice/invoice_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/invoice/invoice_export.php', ['client_id', 'status', 'category', 'q'], ['dtf' => $dtf, 'dtt' => $dtt]) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
</div>

View File

@@ -91,7 +91,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<?php if ($num_rows[0] > 0) { ?>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/location/location_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/location/location_export.php', ['client_id', 'client', 'tags', 'archived', 'q']) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
<?php } ?>

View File

@@ -2,28 +2,187 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_support');
// Pre-filled from the assets page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Type Filter
$type_filter = $_GET['type'] ?? '';
// Client Filter
$client_filter = intval($_GET['client'] ?? 0);
// Location Filter
$location_filter = intval($_GET['location'] ?? 0);
// Tags Filter
$tag_filter = (isset($_GET['tags']) && is_array($_GET['tags'])) ? array_map('intval', $_GET['tags']) : [];
// Expiring In Filter
$expire_filter = $_GET['expire_days'] ?? '';
// Archived Filter
$archived_filter = (isset($_GET['archived']) && $_GET['archived'] == 1) ? 1 : 0;
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Assets to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Assets</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Name, serial, IP, OS">
</div>
</div>
<div class="form-group">
<label>Type</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-desktop"></i></span>
</div>
<select class="form-control select2" name="type">
<option value="">- All Types -</option>
<option <?php if ($type_filter === 'workstation') { echo "selected"; } ?> value="workstation">Workstations</option>
<option <?php if ($type_filter === 'server') { echo "selected"; } ?> value="server">Servers</option>
<option <?php if ($type_filter === 'virtual') { echo "selected"; } ?> value="virtual">Virtual Machines</option>
<option <?php if ($type_filter === 'network') { echo "selected"; } ?> value="network">Network Devices</option>
<option <?php if ($type_filter === 'other') { echo "selected"; } ?> value="other">Other</option>
</select>
</div>
</div>
<?php if (!$client_id) { ?>
<div class="form-group">
<label>Client</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">
<option value="">- All Clients -</option>
<?php
$sql_clients_filter = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE EXISTS (SELECT 1 FROM assets WHERE asset_client_id = client_id) ORDER BY client_name ASC");
while ($row = mysqli_fetch_assoc($sql_clients_filter)) {
$filter_client_id = intval($row['client_id']);
$filter_client_name = escapeHtml($row['client_name']);
?>
<option <?php if ($client_filter == $filter_client_id) { echo "selected"; } ?> value="<?= $filter_client_id ?>"><?= $filter_client_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<?php } ?>
<?php if ($client_id) { ?>
<div class="form-group">
<label>Location</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
</div>
<select class="form-control select2" name="location">
<option value="">- All Locations -</option>
<?php
$sql_locations_filter = mysqli_query($mysqli, "SELECT location_id, location_name FROM locations WHERE location_client_id = $client_id AND location_archived_at IS NULL ORDER BY location_name ASC");
while ($row = mysqli_fetch_assoc($sql_locations_filter)) {
$filter_location_id = intval($row['location_id']);
$filter_location_name = escapeHtml($row['location_name']);
?>
<option <?php if ($location_filter == $filter_location_id) { echo "selected"; } ?> value="<?= $filter_location_id ?>"><?= $filter_location_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<?php } ?>
<div class="form-group">
<label>Tags</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
</div>
<select class="form-control select2" name="tags[]" data-placeholder="- All Tags -" multiple>
<?php
$sql_tags_filter = mysqli_query($mysqli, "SELECT tag_id, tag_name FROM tags WHERE tag_type = 5 ORDER BY tag_name ASC");
while ($row = mysqli_fetch_assoc($sql_tags_filter)) {
$filter_tag_id = intval($row['tag_id']);
$filter_tag_name = escapeHtml($row['tag_name']);
?>
<option <?php if (in_array($filter_tag_id, $tag_filter, true)) { echo "selected"; } ?> value="<?= $filter_tag_id ?>"><?= $filter_tag_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="form-group">
<label>Warranty Expiring In</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-hourglass-half"></i></span>
</div>
<select class="form-control select2" name="expire_days">
<option value="">- Any -</option>
<option <?php if ($expire_filter === 'expired') { echo "selected"; } ?> value="expired">Expired</option>
<?php foreach ([7, 30, 45, 60, 90] as $expire_option) { ?>
<option <?php if ($expire_filter !== '' && $expire_filter == $expire_option) { echo "selected"; } ?> value="<?= $expire_option ?>"><?= $expire_option ?> Days</option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>
</div>
</div>
<?php exportTabsColumns('assets'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_assets_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>
<?php renderExportButtons('export_assets'); ?>
</div>
</form>

View File

@@ -2,7 +2,7 @@
<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 Interfaces to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Interfaces</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
@@ -13,10 +13,11 @@
<div class="modal-body">
<?php renderExportColumnPicker('asset_interfaces'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_client_asset_interfaces_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>
<?php renderExportButtons('export_asset_interfaces'); ?>
</div>
</form>
</div>

View File

@@ -2,27 +2,116 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_support');
// Pre-filled from the certificates page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Client Filter
$client_filter = intval($_GET['client'] ?? 0);
// Expiring In Filter
$expire_filter = $_GET['expire_days'] ?? '';
// Archived Filter
$archived_filter = (isset($_GET['archived']) && $_GET['archived'] == 1) ? 1 : 0;
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Certificates to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Certificates</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Name, domain, issuer">
</div>
</div>
<?php if (!$client_id) { ?>
<div class="form-group">
<label>Client</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">
<option value="">- All Clients -</option>
<?php
$sql_clients_filter = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE EXISTS (SELECT 1 FROM certificates WHERE certificate_client_id = client_id) ORDER BY client_name ASC");
while ($row = mysqli_fetch_assoc($sql_clients_filter)) {
$filter_client_id = intval($row['client_id']);
$filter_client_name = escapeHtml($row['client_name']);
?>
<option <?php if ($client_filter == $filter_client_id) { echo "selected"; } ?> value="<?= $filter_client_id ?>"><?= $filter_client_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<?php } ?>
<div class="form-group">
<label>Expiring In</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-hourglass-half"></i></span>
</div>
<select class="form-control select2" name="expire_days">
<option value="">- Any -</option>
<option <?php if ($expire_filter === 'expired') { echo "selected"; } ?> value="expired">Expired</option>
<?php foreach ([7, 30, 45, 60, 90] as $expire_option) { ?>
<option <?php if ($expire_filter !== '' && $expire_filter == $expire_option) { echo "selected"; } ?> value="<?= $expire_option ?>"><?= $expire_option ?> Days</option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>
</div>
</div>
<?php exportTabsColumns('certificates'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_certificates_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>
<?php renderExportButtons('export_certificates'); ?>
</div>
</form>

View File

@@ -2,23 +2,181 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_client');
// Pre-filled from the clients page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Showing Filter
$leads_filter = $_GET['leads'] ?? '';
// Industry Filter
$industry_filter = $_GET['industry'] ?? '';
// Referral Filter
$referral_filter = $_GET['referral'] ?? '';
// Tags Filter
$tag_filter = (isset($_GET['tags']) && is_array($_GET['tags'])) ? array_map('intval', $_GET['tags']) : [];
// Date Filter - the all-time sentinels from filter_header.php leave the fields blank
$date_from_filter = (!empty($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') ? escapeHtml($_GET['dtf']) : '';
$date_to_filter = (!empty($_GET['dtt']) && $_GET['dtt'] !== '2099-12-31') ? escapeHtml($_GET['dtt']) : '';
// Archived Filter
$archived_filter = (isset($_GET['archived']) && $_GET['archived'] == 1) ? 1 : 0;
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Clients to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Clients</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Name, contact, address, tag">
</div>
</div>
<div class="form-group">
<label>Showing</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user-friends"></i></span>
</div>
<select class="form-control select2" name="leads">
<option value="">- Clients -</option>
<option <?php if ($leads_filter === '1') { echo "selected"; } ?> value="1">Leads</option>
</select>
</div>
</div>
<div class="form-group">
<label>Industry</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-industry"></i></span>
</div>
<select class="form-control select2" name="industry">
<option value="">- All Industries -</option>
<?php
$sql_industry_filter = mysqli_query($mysqli, "SELECT DISTINCT client_type FROM clients WHERE client_type != '' ORDER BY client_type ASC");
while ($row = mysqli_fetch_assoc($sql_industry_filter)) {
$filter_industry = escapeHtml($row['client_type']);
?>
<option <?php if ($industry_filter === $row['client_type']) { echo "selected"; } ?> value="<?= $filter_industry ?>"><?= $filter_industry ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="form-group">
<label>Referral</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-share-alt"></i></span>
</div>
<select class="form-control select2" name="referral">
<option value="">- All Referrals -</option>
<?php
$sql_referral_filter = mysqli_query($mysqli, "SELECT DISTINCT client_referral FROM clients WHERE client_referral != '' ORDER BY client_referral ASC");
while ($row = mysqli_fetch_assoc($sql_referral_filter)) {
$filter_referral = escapeHtml($row['client_referral']);
?>
<option <?php if ($referral_filter === $row['client_referral']) { echo "selected"; } ?> value="<?= $filter_referral ?>"><?= $filter_referral ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="form-group">
<label>Tags</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
</div>
<select class="form-control select2" name="tags[]" data-placeholder="- All Tags -" multiple>
<?php
$sql_tags_filter = mysqli_query($mysqli, "SELECT tag_id, tag_name FROM tags WHERE tag_type = 1 ORDER BY tag_name ASC");
while ($row = mysqli_fetch_assoc($sql_tags_filter)) {
$filter_tag_id = intval($row['tag_id']);
$filter_tag_name = escapeHtml($row['tag_name']);
?>
<option <?php if (in_array($filter_tag_id, $tag_filter, true)) { echo "selected"; } ?> value="<?= $filter_tag_id ?>"><?= $filter_tag_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="form-group">
<label>Created From</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="dtf" value="<?= $date_from_filter ?>" max="2999-12-31">
</div>
</div>
<div class="form-group">
<label>Created To</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="dtt" value="<?= $date_to_filter ?>" max="2999-12-31">
</div>
</div>
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>
</div>
</div>
<?php exportTabsColumns('clients'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_clients_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>
<?php renderExportButtons('export_clients'); ?>
</div>
</form>

View File

@@ -2,28 +2,148 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_client');
// Pre-filled from the contacts page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Client Filter
$client_filter = intval($_GET['client'] ?? 0);
// Location Filter
$location_filter = intval($_GET['location'] ?? 0);
// Tags Filter
$tag_filter = (isset($_GET['tags']) && is_array($_GET['tags'])) ? array_map('intval', $_GET['tags']) : [];
// Archived Filter
$archived_filter = (isset($_GET['archived']) && $_GET['archived'] == 1) ? 1 : 0;
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Contacts to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Contacts</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Name, title, email, phone">
</div>
</div>
<?php if (!$client_id) { ?>
<div class="form-group">
<label>Client</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">
<option value="">- All Clients -</option>
<?php
$sql_clients_filter = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE EXISTS (SELECT 1 FROM contacts WHERE contact_client_id = client_id) ORDER BY client_name ASC");
while ($row = mysqli_fetch_assoc($sql_clients_filter)) {
$filter_client_id = intval($row['client_id']);
$filter_client_name = escapeHtml($row['client_name']);
?>
<option <?php if ($client_filter == $filter_client_id) { echo "selected"; } ?> value="<?= $filter_client_id ?>"><?= $filter_client_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<?php } ?>
<?php if ($client_id) { ?>
<div class="form-group">
<label>Location</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
</div>
<select class="form-control select2" name="location">
<option value="">- All Locations -</option>
<?php
$sql_locations_filter = mysqli_query($mysqli, "SELECT location_id, location_name FROM locations WHERE location_client_id = $client_id AND location_archived_at IS NULL ORDER BY location_name ASC");
while ($row = mysqli_fetch_assoc($sql_locations_filter)) {
$filter_location_id = intval($row['location_id']);
$filter_location_name = escapeHtml($row['location_name']);
?>
<option <?php if ($location_filter == $filter_location_id) { echo "selected"; } ?> value="<?= $filter_location_id ?>"><?= $filter_location_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<?php } ?>
<div class="form-group">
<label>Tags</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
</div>
<select class="form-control select2" name="tags[]" data-placeholder="- All Tags -" multiple>
<?php
$sql_tags_filter = mysqli_query($mysqli, "SELECT tag_id, tag_name FROM tags WHERE tag_type = 3 ORDER BY tag_name ASC");
while ($row = mysqli_fetch_assoc($sql_tags_filter)) {
$filter_tag_id = intval($row['tag_id']);
$filter_tag_name = escapeHtml($row['tag_name']);
?>
<option <?php if (in_array($filter_tag_id, $tag_filter, true)) { echo "selected"; } ?> value="<?= $filter_tag_id ?>"><?= $filter_tag_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>
</div>
</div>
<?php exportTabsColumns('contacts'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_contacts_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>
<?php renderExportButtons('export_contacts'); ?>
</div>
</form>

View File

@@ -2,28 +2,121 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_credential');
// Pre-filled from the credentials page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Client Filter
$client_filter = intval($_GET['client'] ?? 0);
// Tags Filter
$tag_filter = (isset($_GET['tags']) && is_array($_GET['tags'])) ? array_map('intval', $_GET['tags']) : [];
// Archived Filter
$archived_filter = (isset($_GET['archived']) && $_GET['archived'] == 1) ? 1 : 0;
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Credentials to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Credentials</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Name, description, URI, tag">
</div>
</div>
<?php if (!$client_id) { ?>
<div class="form-group">
<label>Client</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">
<option value="">- All Clients -</option>
<?php
$sql_clients_filter = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE EXISTS (SELECT 1 FROM credentials WHERE credential_client_id = client_id) ORDER BY client_name ASC");
while ($row = mysqli_fetch_assoc($sql_clients_filter)) {
$filter_client_id = intval($row['client_id']);
$filter_client_name = escapeHtml($row['client_name']);
?>
<option <?php if ($client_filter == $filter_client_id) { echo "selected"; } ?> value="<?= $filter_client_id ?>"><?= $filter_client_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<?php } ?>
<div class="form-group">
<label>Tags</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
</div>
<select class="form-control select2" name="tags[]" data-placeholder="- All Tags -" multiple>
<?php
$sql_tags_filter = mysqli_query($mysqli, "SELECT tag_id, tag_name FROM tags WHERE tag_type = 4 ORDER BY tag_name ASC");
while ($row = mysqli_fetch_assoc($sql_tags_filter)) {
$filter_tag_id = intval($row['tag_id']);
$filter_tag_name = escapeHtml($row['tag_name']);
?>
<option <?php if (in_array($filter_tag_id, $tag_filter, true)) { echo "selected"; } ?> value="<?= $filter_tag_id ?>"><?= $filter_tag_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>
</div>
</div>
<?php exportTabsColumns('credentials'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_credentials_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>
<?php renderExportButtons('export_credentials'); ?>
</div>
</form>

View File

@@ -2,27 +2,116 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_support');
// Pre-filled from the domains page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Client Filter
$client_filter = intval($_GET['client'] ?? 0);
// Expiring In Filter
$expire_filter = $_GET['expire_days'] ?? '';
// Archived Filter
$archived_filter = (isset($_GET['archived']) && $_GET['archived'] == 1) ? 1 : 0;
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Domains to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Domains</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Domain, registrar, web host">
</div>
</div>
<?php if (!$client_id) { ?>
<div class="form-group">
<label>Client</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">
<option value="">- All Clients -</option>
<?php
$sql_clients_filter = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE EXISTS (SELECT 1 FROM domains WHERE domain_client_id = client_id) ORDER BY client_name ASC");
while ($row = mysqli_fetch_assoc($sql_clients_filter)) {
$filter_client_id = intval($row['client_id']);
$filter_client_name = escapeHtml($row['client_name']);
?>
<option <?php if ($client_filter == $filter_client_id) { echo "selected"; } ?> value="<?= $filter_client_id ?>"><?= $filter_client_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<?php } ?>
<div class="form-group">
<label>Expiring In</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-hourglass-half"></i></span>
</div>
<select class="form-control select2" name="expire_days">
<option value="">- Any -</option>
<option <?php if ($expire_filter === 'expired') { echo "selected"; } ?> value="expired">Expired</option>
<?php foreach ([7, 30, 45, 60, 90] as $expire_option) { ?>
<option <?php if ($expire_filter !== '' && $expire_filter == $expire_option) { echo "selected"; } ?> value="<?= $expire_option ?>"><?= $expire_option ?> Days</option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>
</div>
</div>
<?php exportTabsColumns('domains'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_domains_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>
<?php renderExportButtons('export_domains'); ?>
</div>
</form>

View File

@@ -2,21 +2,61 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_financial');
// Pre-filled from the expenses page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Account Filter
$account_filter = intval($_GET['account'] ?? 0);
// Vendor Filter
$vendor_filter = intval($_GET['vendor'] ?? 0);
// Category Filter
$category_filter = intval($_GET['category'] ?? 0);
// Date Filter - the all-time sentinels from filter_header.php leave the fields blank
$date_from_filter = (!empty($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') ? escapeHtml($_GET['dtf']) : '';
$date_to_filter = (!empty($_GET['dtt']) && $_GET['dtt'] !== '2099-12-31') ? escapeHtml($_GET['dtt']) : '';
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-download mr-2"></i>Exporting Expenses to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Expenses</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Vendor, client, category, description">
</div>
</div>
<div class="form-group">
<label>Account</label>
<div class="input-group">
@@ -25,18 +65,16 @@ ob_start();
</div>
<select class="form-control select2" name="account">
<option value="">- All Accounts -</option>
<?php
$sql_accounts_filter = mysqli_query($mysqli, "SELECT * FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
while ($row = mysqli_fetch_assoc($sql_accounts_filter)) {
$account_id = intval($row['account_id']);
$account_name = escapeHtml($row['account_name']);
$sql_account_filter = mysqli_query($mysqli, "SELECT account_id, account_name FROM accounts WHERE account_archived_at IS NULL ORDER BY account_name ASC");
while ($row = mysqli_fetch_assoc($sql_account_filter)) {
$filter_option_id = intval($row['account_id']);
$filter_option_name = escapeHtml($row['account_name']);
?>
<option <?php if ($account_filter == $account_id) { echo "selected"; } ?> value="<?= $account_id ?>"><?= $account_name ?></option>
<option <?php if ($account_filter == $filter_option_id) { echo "selected"; } ?> value="<?= $filter_option_id ?>"><?= $filter_option_name ?></option>
<?php
}
?>
</select>
</div>
</div>
@@ -49,18 +87,16 @@ ob_start();
</div>
<select class="form-control select2" name="vendor">
<option value="">- All Vendors -</option>
<?php
$sql_vendors_filter = mysqli_query($mysqli, "SELECT * FROM vendors WHERE vendor_client_id = 0 ORDER BY vendor_name ASC");
while ($row = mysqli_fetch_assoc($sql_vendors_filter)) {
$vendor_id = intval($row['vendor_id']);
$vendor_name = escapeHtml($row['vendor_name']);
$sql_vendor_filter = mysqli_query($mysqli, "SELECT vendor_id, vendor_name FROM vendors WHERE EXISTS (SELECT 1 FROM expenses WHERE expense_vendor_id = vendor_id) ORDER BY vendor_name ASC");
while ($row = mysqli_fetch_assoc($sql_vendor_filter)) {
$filter_option_id = intval($row['vendor_id']);
$filter_option_name = escapeHtml($row['vendor_name']);
?>
<option <?php if ($vendor_filter == $vendor_id) { echo "selected"; } ?> value="<?= $vendor_id ?>"><?= $vendor_name ?></option>
<option <?php if ($vendor_filter == $filter_option_id) { echo "selected"; } ?> value="<?= $filter_option_id ?>"><?= $filter_option_name ?></option>
<?php
}
?>
</select>
</div>
</div>
@@ -73,46 +109,45 @@ ob_start();
</div>
<select class="form-control select2" name="category">
<option value="">- All Categories -</option>
<?php
$sql_categories_filter = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Expense' ORDER BY category_name ASC");
while ($row = mysqli_fetch_assoc($sql_categories_filter)) {
$category_id = intval($row['category_id']);
$category_name = escapeHtml($row['category_name']);
$sql_category_filter = mysqli_query($mysqli, "SELECT category_id, category_name FROM categories WHERE category_type = 'Expense' ORDER BY category_name ASC");
while ($row = mysqli_fetch_assoc($sql_category_filter)) {
$filter_option_id = intval($row['category_id']);
$filter_option_name = escapeHtml($row['category_name']);
?>
<option <?php if ($category_filter == $category_id) { echo "selected"; } ?> value="<?= $category_id ?>"><?= $category_name ?></option>
<option <?php if ($category_filter == $filter_option_id) { echo "selected"; } ?> value="<?= $filter_option_id ?>"><?= $filter_option_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="form-group">
<label>Date From</label>
<label>Dated From</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="date_from" max="2999-12-31">
<input type="date" class="form-control" name="dtf" value="<?= $date_from_filter ?>" max="2999-12-31">
</div>
</div>
<div class="form-group">
<label>Date To</label>
<label>Dated To</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="date_to" max="2999-12-31">
<input type="date" class="form-control" name="dtt" value="<?= $date_to_filter ?>" max="2999-12-31">
</div>
</div>
<?php exportTabsColumns('expenses'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_expenses_csv" class="btn btn-primary text-bold"><i class="fas fa-fw fa-download mr-2"></i>Download CSV</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
<?php renderExportButtons('export_expenses'); ?>
</div>
</form>

View File

@@ -53,17 +53,21 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-download mr-2"></i>Exporting Income to CSV</h5>
<h5 class="modal-title"><i class="fa fa-fw fa-download mr-2"></i>Export Income</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
@@ -163,10 +167,12 @@ ob_start();
</div>
</div>
<?php exportTabsColumns('income'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_income_csv" class="btn btn-primary text-bold"><i class="fas fa-fw fa-download mr-2"></i>Download CSV</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
<?php renderExportButtons('export_income'); ?>
</div>
</form>

View File

@@ -2,47 +2,121 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_sales');
// Pre-filled from the invoices page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Status Filter
$status_filter = $_GET['status'] ?? '';
// Category Filter
$category_filter = intval($_GET['category'] ?? 0);
// Date Filter - the all-time sentinels from filter_header.php leave the fields blank
$date_from_filter = (!empty($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') ? escapeHtml($_GET['dtf']) : '';
$date_to_filter = (!empty($_GET['dtt']) && $_GET['dtt'] !== '2099-12-31') ? escapeHtml($_GET['dtt']) : '';
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-download mr-2"></i>Export Invoices to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Invoices</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Date From</label>
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="date" class="form-control" name="date_from" max="2999-12-31">
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Number, scope, client, amount">
</div>
</div>
<div class="form-group">
<label>Date To</label>
<label>Status</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-file-invoice-dollar"></i></span>
</div>
<select class="form-control select2" name="status">
<option value="">- All Statuses -</option>
<option <?php if ($status_filter === 'Draft') { echo "selected"; } ?> value="Draft">Draft</option>
<option <?php if ($status_filter === 'Unpaid') { echo "selected"; } ?> value="Unpaid">Unpaid</option>
<option <?php if ($status_filter === 'Overdue') { echo "selected"; } ?> value="Overdue">Overdue</option>
</select>
</div>
</div>
<div class="form-group">
<label>Category</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-list"></i></span>
</div>
<select class="form-control select2" name="category">
<option value="">- All Categories -</option>
<?php
$sql_category_filter = mysqli_query($mysqli, "SELECT category_id, category_name FROM categories WHERE category_type = 'Income' ORDER BY category_name ASC");
while ($row = mysqli_fetch_assoc($sql_category_filter)) {
$filter_option_id = intval($row['category_id']);
$filter_option_name = escapeHtml($row['category_name']);
?>
<option <?php if ($category_filter == $filter_option_id) { echo "selected"; } ?> value="<?= $filter_option_id ?>"><?= $filter_option_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="form-group">
<label>Issued From</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="date_to" max="2999-12-31">
<input type="date" class="form-control" name="dtf" value="<?= $date_from_filter ?>" max="2999-12-31">
</div>
</div>
<div class="form-group">
<label>Issued To</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="dtt" value="<?= $date_to_filter ?>" max="2999-12-31">
</div>
</div>
<?php exportTabsColumns('invoices'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_invoices_csv" class="btn btn-primary text-bold"><i class="fas fa-fw fa-download mr-2"></i>Download CSV</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
<?php renderExportButtons('export_invoices'); ?>
</div>
</form>

View File

@@ -2,27 +2,121 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_client');
// Pre-filled from the locations page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Client Filter
$client_filter = intval($_GET['client'] ?? 0);
// Tags Filter
$tag_filter = (isset($_GET['tags']) && is_array($_GET['tags'])) ? array_map('intval', $_GET['tags']) : [];
// Archived Filter
$archived_filter = (isset($_GET['archived']) && $_GET['archived'] == 1) ? 1 : 0;
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Locations to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Locations</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Name, address, city, phone">
</div>
</div>
<?php if (!$client_id) { ?>
<div class="form-group">
<label>Client</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">
<option value="">- All Clients -</option>
<?php
$sql_clients_filter = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE EXISTS (SELECT 1 FROM locations WHERE location_client_id = client_id) ORDER BY client_name ASC");
while ($row = mysqli_fetch_assoc($sql_clients_filter)) {
$filter_client_id = intval($row['client_id']);
$filter_client_name = escapeHtml($row['client_name']);
?>
<option <?php if ($client_filter == $filter_client_id) { echo "selected"; } ?> value="<?= $filter_client_id ?>"><?= $filter_client_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<?php } ?>
<div class="form-group">
<label>Tags</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
</div>
<select class="form-control select2" name="tags[]" data-placeholder="- All Tags -" multiple>
<?php
$sql_tags_filter = mysqli_query($mysqli, "SELECT tag_id, tag_name FROM tags WHERE tag_type = 2 ORDER BY tag_name ASC");
while ($row = mysqli_fetch_assoc($sql_tags_filter)) {
$filter_tag_id = intval($row['tag_id']);
$filter_tag_name = escapeHtml($row['tag_name']);
?>
<option <?php if (in_array($filter_tag_id, $tag_filter, true)) { echo "selected"; } ?> value="<?= $filter_tag_id ?>"><?= $filter_tag_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>
</div>
</div>
<?php exportTabsColumns('locations'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_locations_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>
<?php renderExportButtons('export_locations'); ?>
</div>
</form>

View File

@@ -2,27 +2,124 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_support');
// Pre-filled from the networks page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Client Filter
$client_filter = intval($_GET['client'] ?? 0);
// Location Filter
$location_filter = intval($_GET['location'] ?? 0);
// Archived Filter
$archived_filter = (isset($_GET['archived']) && $_GET['archived'] == 1) ? 1 : 0;
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Networks to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Networks</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Name, CIDR, gateway, DNS">
</div>
</div>
<?php if (!$client_id) { ?>
<div class="form-group">
<label>Client</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">
<option value="">- All Clients -</option>
<?php
$sql_clients_filter = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE EXISTS (SELECT 1 FROM networks WHERE network_client_id = client_id) ORDER BY client_name ASC");
while ($row = mysqli_fetch_assoc($sql_clients_filter)) {
$filter_client_id = intval($row['client_id']);
$filter_client_name = escapeHtml($row['client_name']);
?>
<option <?php if ($client_filter == $filter_client_id) { echo "selected"; } ?> value="<?= $filter_client_id ?>"><?= $filter_client_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<?php } ?>
<?php if ($client_id) { ?>
<div class="form-group">
<label>Location</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
</div>
<select class="form-control select2" name="location">
<option value="">- All Locations -</option>
<?php
$sql_locations_filter = mysqli_query($mysqli, "SELECT location_id, location_name FROM locations WHERE location_client_id = $client_id AND location_archived_at IS NULL ORDER BY location_name ASC");
while ($row = mysqli_fetch_assoc($sql_locations_filter)) {
$filter_location_id = intval($row['location_id']);
$filter_location_name = escapeHtml($row['location_name']);
?>
<option <?php if ($location_filter == $filter_location_id) { echo "selected"; } ?> value="<?= $filter_location_id ?>"><?= $filter_location_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<?php } ?>
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>
</div>
</div>
<?php exportTabsColumns('networks'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_networks_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>
<?php renderExportButtons('export_networks'); ?>
</div>
</form>

View File

@@ -2,24 +2,111 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_sales');
// Pre-filled from the products page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Type Filter
$type_filter = $_GET['type'] ?? '';
// Category Filter
$category_filter = intval($_GET['category'] ?? 0);
// Archived Filter
$archived_filter = (isset($_GET['archived']) && $_GET['archived'] == 1) ? 1 : 0;
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-download mr-2"></i>Export Products to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Products</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Name, code, description">
</div>
</div>
<div class="form-group">
<label>Type</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-box"></i></span>
</div>
<select class="form-control select2" name="type">
<option value="">- All Types -</option>
<option <?php if ($type_filter === 'product') { echo "selected"; } ?> value="product">Products</option>
<option <?php if ($type_filter === 'service') { echo "selected"; } ?> value="service">Services</option>
</select>
</div>
</div>
<div class="form-group">
<label>Category</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-list"></i></span>
</div>
<select class="form-control select2" name="category">
<option value="">- All Categories -</option>
<?php
$sql_category_filter = mysqli_query($mysqli, "SELECT category_id, category_name FROM categories WHERE category_type = 'Income' ORDER BY category_name ASC");
while ($row = mysqli_fetch_assoc($sql_category_filter)) {
$filter_option_id = intval($row['category_id']);
$filter_option_name = escapeHtml($row['category_name']);
?>
<option <?php if ($category_filter == $filter_option_id) { echo "selected"; } ?> value="<?= $filter_option_id ?>"><?= $filter_option_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>
</div>
</div>
<?php exportTabsColumns('products'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_products_csv" class="btn btn-primary text-bold"><i class="fas fa-fw fa-download mr-2"></i>Download CSV</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
<?php renderExportButtons('export_products'); ?>
</div>
</form>

View File

@@ -2,28 +2,78 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_sales');
// Pre-filled from the quotes page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Date Filter - the all-time sentinels from filter_header.php leave the fields blank
$date_from_filter = (!empty($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') ? escapeHtml($_GET['dtf']) : '';
$date_to_filter = (!empty($_GET['dtt']) && $_GET['dtt'] !== '2099-12-31') ? escapeHtml($_GET['dtt']) : '';
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Quotes to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Quotes</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Number, scope, client, amount">
</div>
</div>
<div class="form-group">
<label>Dated From</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="dtf" value="<?= $date_from_filter ?>" max="2999-12-31">
</div>
</div>
<div class="form-group">
<label>Dated To</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="dtt" value="<?= $date_to_filter ?>" max="2999-12-31">
</div>
</div>
<?php exportTabsColumns('quotes'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_quotes_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>
<?php renderExportButtons('export_quotes'); ?>
</div>
</form>

View File

@@ -1,23 +1,98 @@
<div class="modal" id="exportRecurringModal" 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 Recurring Invoices 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">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php
</div>
<div class="modal-footer">
<button type="submit" name="export_client_recurring_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>
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_sales');
// Pre-filled from the recurring invoices page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Status Filter
$status_filter = $_GET['status'] ?? '';
// Date Filter - the all-time sentinels from filter_header.php leave the fields blank
$date_from_filter = (!empty($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') ? escapeHtml($_GET['dtf']) : '';
$date_to_filter = (!empty($_GET['dtt']) && $_GET['dtt'] !== '2099-12-31') ? escapeHtml($_GET['dtt']) : '';
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Recurring Invoices</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Number, scope, frequency, client">
</div>
</div>
<div class="form-group">
<label>Status</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-toggle-on"></i></span>
</div>
<select class="form-control select2" name="status">
<option value="">- Any Status -</option>
<option <?php if ($status_filter === 'active') { echo "selected"; } ?> value="active">Active</option>
<option <?php if ($status_filter === 'inactive') { echo "selected"; } ?> value="inactive">Inactive</option>
</select>
</div>
</div>
<div class="form-group">
<label>Created From</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="dtf" value="<?= $date_from_filter ?>" max="2999-12-31">
</div>
</div>
<div class="form-group">
<label>Created To</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="dtt" value="<?= $date_to_filter ?>" max="2999-12-31">
</div>
</div>
<?php exportTabsColumns('recurring_invoices'); ?>
</div>
<div class="modal-footer">
<?php renderExportButtons('export_recurring_invoices'); ?>
</div>
</form>
<?php
require_once '../../../includes/modal_footer.php';

View File

@@ -2,28 +2,116 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_support');
// Pre-filled from the software page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Client Filter
$client_filter = intval($_GET['client'] ?? 0);
// Expiring In Filter
$expire_filter = $_GET['expire_days'] ?? '';
// Archived Filter
$archived_filter = (isset($_GET['archived']) && $_GET['archived'] == 1) ? 1 : 0;
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Licenses to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Software</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Name, type, key">
</div>
</div>
<?php if (!$client_id) { ?>
<div class="form-group">
<label>Client</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">
<option value="">- All Clients -</option>
<?php
$sql_clients_filter = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE EXISTS (SELECT 1 FROM software WHERE software_client_id = client_id) ORDER BY client_name ASC");
while ($row = mysqli_fetch_assoc($sql_clients_filter)) {
$filter_client_id = intval($row['client_id']);
$filter_client_name = escapeHtml($row['client_name']);
?>
<option <?php if ($client_filter == $filter_client_id) { echo "selected"; } ?> value="<?= $filter_client_id ?>"><?= $filter_client_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<?php } ?>
<div class="form-group">
<label>Expiring In</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-hourglass-half"></i></span>
</div>
<select class="form-control select2" name="expire_days">
<option value="">- Any -</option>
<option <?php if ($expire_filter === 'expired') { echo "selected"; } ?> value="expired">Expired</option>
<?php foreach ([7, 30, 45, 60, 90] as $expire_option) { ?>
<option <?php if ($expire_filter !== '' && $expire_filter == $expire_option) { echo "selected"; } ?> value="<?= $expire_option ?>"><?= $expire_option ?> Days</option>
<?php } ?>
</select>
</div>
</div>
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>
</div>
</div>
<?php exportTabsColumns('software'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_software_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>
<?php renderExportButtons('export_software'); ?>
</div>
</form>

View File

@@ -2,27 +2,214 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_support');
// Pre-filled from the tickets page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Status Filter - the page uses an ID set, or the Open / Closed shorthand
$status_filter = (isset($_GET['status']) && is_array($_GET['status'])) ? array_map('intval', $_GET['status']) : [];
$status_shorthand = (isset($_GET['status']) && !is_array($_GET['status'])) ? $_GET['status'] : '';
// Client Filter
$client_filter = intval($_GET['client'] ?? 0);
// Category Filter
$category_filter = intval($_GET['category'] ?? 0);
// Assigned To Filter
$assigned_filter = intval($_GET['assigned'] ?? 0);
// SLA Filter
$sla_filter = $_GET['sla'] ?? '';
// Date Filter - the all-time sentinels from filter_header.php leave the fields blank
$date_from_filter = (!empty($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') ? escapeHtml($_GET['dtf']) : '';
$date_to_filter = (!empty($_GET['dtt']) && $_GET['dtt'] !== '2099-12-31') ? escapeHtml($_GET['dtt']) : '';
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Tickets to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Tickets</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Number, subject, client, contact">
</div>
</div>
<div class="form-group">
<label>Status</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tasks"></i></span>
</div>
<select class="form-control select2" name="status[]" data-placeholder="- Open tickets -" multiple>
<?php
$sql_statuses_filter = mysqli_query($mysqli, "SELECT ticket_status_id, ticket_status_name FROM ticket_statuses ORDER BY ticket_status_name ASC");
while ($row = mysqli_fetch_assoc($sql_statuses_filter)) {
$filter_status_id = intval($row['ticket_status_id']);
$filter_status_name = escapeHtml($row['ticket_status_name']);
?>
<option <?php if (in_array($filter_status_id, $status_filter, true)) { echo "selected"; } ?> value="<?= $filter_status_id ?>"><?= $filter_status_name ?></option>
<?php
}
?>
</select>
</div>
<small class="form-text text-muted">Leave empty for open tickets only.</small>
</div>
<div class="form-group">
<label>Resolution</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-check"></i></span>
</div>
<select class="form-control select2" name="resolution">
<option <?php if ($status_shorthand !== 'Closed') { echo "selected"; } ?> value="">Open</option>
<option <?php if ($status_shorthand === 'Closed') { echo "selected"; } ?> value="Closed">Closed</option>
</select>
</div>
</div>
<?php if (!$client_id) { ?>
<div class="form-group">
<label>Client</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">
<option value="">- All Clients -</option>
<?php
$sql_clients_filter = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE EXISTS (SELECT 1 FROM tickets WHERE ticket_client_id = client_id) ORDER BY client_name ASC");
while ($row = mysqli_fetch_assoc($sql_clients_filter)) {
$filter_client_id = intval($row['client_id']);
$filter_client_name = escapeHtml($row['client_name']);
?>
<option <?php if ($client_filter == $filter_client_id) { echo "selected"; } ?> value="<?= $filter_client_id ?>"><?= $filter_client_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<?php } ?>
<div class="form-group">
<label>Category</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-list"></i></span>
</div>
<select class="form-control select2" name="category">
<option value="">- All Categories -</option>
<?php
$sql_category_filter = mysqli_query($mysqli, "SELECT category_id, category_name FROM categories WHERE category_type = 'Ticket' ORDER BY category_name ASC");
while ($row = mysqli_fetch_assoc($sql_category_filter)) {
$filter_option_id = intval($row['category_id']);
$filter_option_name = escapeHtml($row['category_name']);
?>
<option <?php if ($category_filter == $filter_option_id) { echo "selected"; } ?> value="<?= $filter_option_id ?>"><?= $filter_option_name ?></option>
<?php
}
?>
</select>
</div>
</div>
<div class="form-group">
<label>Assigned To</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-user-tie"></i></span>
</div>
<select class="form-control select2" name="assigned">
<option value="">- Anyone -</option>
<?php
$sql_assigned_filter = mysqli_query($mysqli, "SELECT user_id, user_name FROM users WHERE user_type = 1 AND user_archived_at IS NULL ORDER BY user_name ASC");
while ($row = mysqli_fetch_assoc($sql_assigned_filter)) {
$filter_option_id = intval($row['user_id']);
$filter_option_name = escapeHtml($row['user_name']);
?>
<option <?php if ($assigned_filter == $filter_option_id) { echo "selected"; } ?> value="<?= $filter_option_id ?>"><?= $filter_option_name ?></option>
<?php
}
?>
</select>
</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-stopwatch"></i></span>
</div>
<select class="form-control select2" name="sla">
<option value="">- Any SLA state -</option>
<option <?php if ($sla_filter === 'breached') { echo "selected"; } ?> value="breached">Breached</option>
<option <?php if ($sla_filter === 'at_risk') { echo "selected"; } ?> value="at_risk">At risk</option>
<option <?php if ($sla_filter === 'paused') { echo "selected"; } ?> value="paused">Paused</option>
<option <?php if ($sla_filter === 'met') { echo "selected"; } ?> value="met">Met</option>
<option <?php if ($sla_filter === 'none') { echo "selected"; } ?> value="none">No SLA</option>
</select>
</div>
</div>
<div class="form-group">
<label>Opened From</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="dtf" value="<?= $date_from_filter ?>" max="2999-12-31">
</div>
</div>
<div class="form-group">
<label>Opened To</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="dtt" value="<?= $date_to_filter ?>" max="2999-12-31">
</div>
</div>
<?php exportTabsColumns('tickets'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_tickets_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>
<?php renderExportButtons('export_tickets'); ?>
</div>
</form>

View File

@@ -76,16 +76,20 @@ ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fa fa-fw fa-download mr-2"></i>Exporting Transactions to CSV</h5>
<h5 class="modal-title"><i class="fa fa-fw fa-download mr-2"></i>Export Transactions</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Account</label>
<div class="input-group">
@@ -239,10 +243,11 @@ ob_start();
</div>
</div>
<?php exportTabsColumns('transactions'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_transactions_csv" class="btn btn-primary text-bold"><i class="fas fa-fw fa-download mr-2"></i>Download CSV</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fa fa-times mr-2"></i>Cancel</button>
<?php renderExportButtons('export_transactions'); ?>
</div>
</form>

View File

@@ -2,47 +2,78 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_financial');
// Pre-filled from the trips page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Date Filter - the all-time sentinels from filter_header.php leave the fields blank
$date_from_filter = (!empty($_GET['dtf']) && $_GET['dtf'] !== '1970-01-01') ? escapeHtml($_GET['dtf']) : '';
$date_to_filter = (!empty($_GET['dtt']) && $_GET['dtt'] !== '2099-12-31') ? escapeHtml($_GET['dtt']) : '';
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Trips to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Trips</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Date From</label>
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="date" class="form-control" name="date_from" max="2999-12-31">
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Purpose, source, destination">
</div>
</div>
<div class="form-group">
<label>Date To</label>
<label>Dated From</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="date_to" max="2999-12-31">
<input type="date" class="form-control" name="dtf" value="<?= $date_from_filter ?>" max="2999-12-31">
</div>
</div>
<div class="form-group">
<label>Dated To</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
</div>
<input type="date" class="form-control" name="dtt" value="<?= $date_to_filter ?>" max="2999-12-31">
</div>
</div>
<?php exportTabsColumns('trips'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_trips_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>
<?php renderExportButtons('export_trips'); ?>
</div>
</form>

View File

@@ -2,28 +2,70 @@
require_once '../../../includes/modal_header.php';
enforceUserPermission('module_client');
// Pre-filled from the vendors page's current filters. Everything here is editable -
// the export runs whatever this form posts, not whatever the page happened to show.
$client_id = intval($_GET['client_id'] ?? 0);
if ($client_id) {
enforceClientAccess();
}
// Search Filter
$q_filter = $_GET['q'] ?? '';
// Archived Filter
$archived_filter = (isset($_GET['archived']) && $_GET['archived'] == 1) ? 1 : 0;
ob_start();
?>
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Vendors to CSV</h5>
<h5 class="modal-title"><i class="fas fa-fw fa-download mr-2"></i>Export Vendors</h5>
<button type="button" class="close text-white" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<?php exportTabsNav(); ?>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<div class="modal-body">
<?php exportTabsFiltersOpen(); ?>
<div class="form-group">
<label>Search</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-search"></i></span>
</div>
<input type="text" class="form-control" name="q" value="<?= stripslashes(escapeHtml($q_filter)) ?>" placeholder="Name, contact, account number">
</div>
</div>
<div class="form-group">
<label>Archived</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-archive"></i></span>
</div>
<select class="form-control select2" name="archived">
<option <?php if (!$archived_filter) { echo "selected"; } ?> value="0">Active only</option>
<option <?php if ($archived_filter) { echo "selected"; } ?> value="1">Archived only</option>
</select>
</div>
</div>
<?php exportTabsColumns('vendors'); ?>
</div>
<div class="modal-footer">
<button type="submit" name="export_vendors_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>
<?php renderExportButtons('export_vendors'); ?>
</div>
</form>

View File

@@ -83,7 +83,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<div class="dropdown-menu">
<?php if ($num_rows[0] > 0) { ?>
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/network/network_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/network/network_export.php', ['client_id', 'client', 'location', 'archived', 'q']) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
<?php } ?>

View File

@@ -1303,62 +1303,155 @@ if (isset($_GET['download_assets_csv_template'])) {
}
if (isset($_POST['export_assets_csv'])) {
if (isset($_POST['export_assets'])) {
validateCSRFToken();
enforceUserPermission('module_support');
if ($_POST['client_id']) {
$format = resolveExportFormat($_POST['export_assets']);
// Filters inherited from the assets page - mirrors agent/assets.php
$filter_summary = [];
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "AND asset_client_id = $client_id";
$client_row = mysqli_fetch_assoc(mysqli_query($mysqli,"SELECT client_name FROM clients WHERE client_id = $client_id"));
$client_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT client_name FROM clients WHERE client_id = $client_id"));
$client_name = $client_row['client_name'];
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
} else {
$client_query = '';
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
// Client Filter
if (!empty($_POST['client'])) {
$filter_client_id = intval($_POST['client']);
$client_query = "AND (asset_client_id = $filter_client_id)";
$client_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT client_name FROM clients WHERE client_id = $filter_client_id"));
$filter_summary['Client'] = $client_row['client_name'] ?? '';
}
}
// Get records from database
$sql = mysqli_query($mysqli,"SELECT * FROM assets LEFT JOIN contacts ON asset_contact_id = contact_id LEFT JOIN locations ON asset_location_id = location_id LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1 LEFT JOIN clients ON asset_client_id = client_id WHERE asset_archived_at IS NULL $client_query $access_permission_query ORDER BY asset_name ASC");
// Archived Filter
if (isset($_POST['archived']) && $_POST['archived'] == 1) {
$archive_query = $client_id ? "asset_archived_at IS NOT NULL" : "(client_archived_at IS NOT NULL OR asset_archived_at IS NOT NULL)";
$filter_summary['Archived'] = 'Archived only';
} else {
$archive_query = $client_id ? "asset_archived_at IS NULL" : "(client_archived_at IS NULL AND asset_archived_at IS NULL)";
}
// Type Filter
$type = $_POST['type'] ?? '';
if ($type == 'workstation') {
$type_query = "asset_type = 'desktop' OR asset_type = 'laptop'";
} elseif ($type == 'server') {
$type_query = "asset_type = 'server'";
} elseif ($type == 'virtual') {
$type_query = "asset_type = 'Virtual Machine'";
} elseif ($type == 'network') {
$type_query = "asset_type = 'Firewall/Router' OR asset_type = 'Switch' OR asset_type = 'Access Point'";
} elseif ($type == 'other') {
$type_query = "asset_type NOT LIKE 'laptop' AND asset_type NOT LIKE 'desktop' AND asset_type NOT LIKE 'server' AND asset_type NOT LIKE 'virtual machine' AND asset_type NOT LIKE 'firewall/router' AND asset_type NOT LIKE 'switch' AND asset_type NOT LIKE 'access point'";
} else {
// Default - any
$type = '';
$type_query = "asset_type LIKE '%'";
}
if ($type) {
$filter_summary['Type'] = ucwords($type);
}
// Location Filter
if (!empty($_POST['location'])) {
$filter_location_id = intval($_POST['location']);
$location_query = "AND (asset_location_id = $filter_location_id)";
$location_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT location_name FROM locations WHERE location_id = $filter_location_id"));
$filter_summary['Location'] = $location_row['location_name'] ?? '';
} else {
// Default - any
$location_query = '';
}
// Tags Filter
if (isset($_POST['tags']) && is_array($_POST['tags']) && !empty($_POST['tags'])) {
$tag_filter = implode(",", array_map('intval', $_POST['tags']));
$tag_query = "AND tag_id IN ($tag_filter)";
$tag_names = [];
$sql_tags = mysqli_query($mysqli, "SELECT tag_name FROM tags WHERE tag_id IN ($tag_filter) ORDER BY tag_name ASC");
while ($tag_row = mysqli_fetch_assoc($sql_tags)) {
$tag_names[] = $tag_row['tag_name'];
}
$filter_summary['Tags'] = implode(', ', $tag_names);
} else {
// Default - any
$tag_query = '';
}
// Expiring In Filter
if (!empty($_POST['expire_days'])) {
if ($_POST['expire_days'] == "expired") {
$expire_query = "AND (asset_warranty_expire IS NOT NULL AND asset_warranty_expire != '0000-00-00' AND asset_warranty_expire < CURDATE())";
$filter_summary['Warranty'] = 'Expired';
} else {
$expire_days = intval($_POST['expire_days']);
$expire_query = "AND (asset_warranty_expire IS NOT NULL AND asset_warranty_expire != '0000-00-00' AND asset_warranty_expire BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL $expire_days DAY))";
$filter_summary['Warranty'] = "Expiring within $expire_days days";
}
} else {
// Default - any
$expire_query = '';
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
// Get records from database - same shape as the assets page list query
$sql = mysqli_query(
$mysqli,
"SELECT * FROM assets
LEFT JOIN clients ON asset_client_id = client_id
LEFT JOIN contacts ON asset_contact_id = contact_id
LEFT JOIN locations ON asset_location_id = location_id
LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1
LEFT JOIN asset_tags ON asset_tag_asset_id = asset_id
LEFT JOIN tags ON tag_id = asset_tag_tag_id
WHERE $archive_query
$tag_query
AND (asset_name LIKE '%$q%' OR asset_description LIKE '%$q%' OR asset_type LIKE '%$q%' OR interface_ip LIKE '%$q%' OR interface_ipv6 LIKE '%$q%' OR interface_mac LIKE '%$q%' OR asset_make LIKE '%$q%' OR asset_model LIKE '%$q%' OR asset_serial LIKE '%$q%' OR asset_os LIKE '%$q%' OR contact_name LIKE '%$q%' OR location_name LIKE '%$q%' OR client_name LIKE '%$q%' OR tag_name LIKE '%$q%')
AND ($type_query)
$access_permission_query
$location_query
$expire_query
$client_query
GROUP BY asset_id
ORDER BY asset_name ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Assets-" . date('Y-m-d_H-i-s') . ".csv");
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Name', 'Description', 'Type', 'Make', 'Model', 'Serial Number', 'Operating System', 'Purchase Date', 'Warranty Expire', 'Install Date', 'Assigned To', 'Location', 'Physical Location', 'Notes');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('assets', $format, $file_name_prepend . 'Assets', 'Assets', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while ($row = mysqli_fetch_assoc($sql)) {
$lineData = array($row['asset_name'], $row['asset_description'], $row['asset_type'], $row['asset_make'], $row['asset_model'], $row['asset_serial'], $row['asset_os'], $row['asset_purchase_date'], $row['asset_warranty_expire'], $row['asset_install_date'], $row['contact_name'], $row['location_name'], $row['asset_physical_location'], $row['asset_notes']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Asset", "Export", "$session_name exported $num_rows asset(s) to a CSV file", $client_id);
logAudit("Asset", "Export", "$session_name exported $num_rows asset(s) to a " . strtoupper($format) . " file", $client_id);
exit;
@@ -1950,57 +2043,50 @@ if (isset($_GET['download_client_asset_interfaces_csv_template'])) {
}
if (isset($_POST['export_client_asset_interfaces_csv'])) {
if (isset($_POST['export_asset_interfaces'])) {
validateCSRFToken();
enforceUserPermission('module_support');
$format = resolveExportFormat($_POST['export_asset_interfaces']);
$asset_id = intval($_POST['asset_id']);
$client_id = intval(getFieldById('assets', $asset_id, 'asset_client_id'));
enforceClientAccess();
//get records from database
$sql = mysqli_query($mysqli,"SELECT * FROM asset_interfaces LEFT JOIN assets ON asset_id = interface_asset_id LEFT JOIN networks ON interface_network_id = network_id LEFT JOIN clients ON asset_client_id = client_id WHERE asset_id = $asset_id AND interface_archived_at IS NULL ORDER BY interface_name ASC");
$row = mysqli_fetch_assoc($sql);
$asset_name = getFieldById('assets', $asset_id, 'asset_name');
// Get records from database - scoped to the one asset, so no page filters apply
$sql = mysqli_query(
$mysqli,
"SELECT * FROM asset_interfaces
LEFT JOIN assets ON asset_id = interface_asset_id
LEFT JOIN networks ON interface_network_id = network_id
LEFT JOIN clients ON asset_client_id = client_id
WHERE asset_id = $asset_id
AND interface_archived_at IS NULL
ORDER BY interface_name ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
mysqli_data_seek($sql, 0); // <— rewind to the start
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = toAlphanumeric($asset_name) . "-Interfaces-" . date('Y-m-d') . ".csv";
guardExportPdfRowCount($format, $num_rows);
//create a file pointer
$f = fopen('php://memory', 'w');
$export = beginExport('asset_interfaces', $format, toAlphanumeric($asset_name) . '-Interfaces', "$asset_name - Interfaces", '');
//set column headers
$fields = array('Name', 'Description', 'Type', 'MAC', 'IP', 'NAT IP', 'IPv6', 'Network');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
//output each row of the data, format line as csv and write to file pointer
while($row = mysqli_fetch_assoc($sql)) {
$lineData = array($row['interface_name'], $row['interface_description'], $row['interface_type'], $row['interface_mac'], $row['interface_ip'], $row['interface_nat_ip'], $row['interface_ipv6'], $row['network_name']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
while ($row = mysqli_fetch_assoc($sql)) {
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Asset Interface", "Export", "$session_name exported $num_rows interfaces(s) to a CSV file", $client_id);
logAudit("Asset Interface", "Export", "$session_name exported $num_rows interface(s) to a " . strtoupper($format) . " file", $client_id);
exit;

View File

@@ -338,59 +338,98 @@ if (isset($_POST['bulk_delete_certificates'])) {
}
if (isset($_POST['export_certificates_csv'])) {
if (isset($_POST['export_certificates'])) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_support');
if ($_POST['client_id']) {
$format = resolveExportFormat($_POST['export_certificates']);
// Filters inherited from the certificates page - mirrors agent/certificates.php
$filter_summary = [];
// Archived Filter
$archived = (isset($_POST['archived']) && $_POST['archived'] == 1);
if ($archived) {
$filter_summary['Archived'] = 'Archived only';
}
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "AND certificate_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
$archive_query = $archived ? "certificate_archived_at IS NOT NULL" : "certificate_archived_at IS NULL";
} else {
$client_query = '';
$client_id = 0;
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
// Client Filter
if (!empty($_POST['client'])) {
$filter_client_id = intval($_POST['client']);
$client_query = "AND (certificate_client_id = $filter_client_id)";
$filter_summary['Client'] = getFieldById('clients', $filter_client_id, 'client_name');
}
$archive_query = $archived ? "(client_archived_at IS NOT NULL OR certificate_archived_at IS NOT NULL)" : "(client_archived_at IS NULL AND certificate_archived_at IS NULL)";
}
$sql = mysqli_query($mysqli,"SELECT * FROM certificates LEFT JOIN clients ON client_id = certificate_client_id WHERE certificate_archived_at IS NULL $client_query $access_permission_query ORDER BY certificate_name ASC");
// Expiring In Filter
if (!empty($_POST['expire_days'])) {
if ($_POST['expire_days'] == "expired") {
$expire_query = "AND (certificate_expire IS NOT NULL AND certificate_expire != '0000-00-00' AND certificate_expire < CURDATE())";
$filter_summary['Expiry'] = 'Expired';
} else {
$expire_days = intval($_POST['expire_days']);
$expire_query = "AND (certificate_expire IS NOT NULL AND certificate_expire != '0000-00-00' AND certificate_expire BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL $expire_days DAY))";
$filter_summary['Expiry'] = "Expiring within $expire_days days";
}
} else {
// Default - any
$expire_query = '';
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
$sql = mysqli_query(
$mysqli,
"SELECT * FROM certificates
LEFT JOIN clients ON client_id = certificate_client_id
WHERE $archive_query
AND (certificate_name LIKE '%$q%' OR certificate_domain LIKE '%$q%' OR certificate_description LIKE '%$q%' OR certificate_issued_by LIKE '%$q%' OR client_name LIKE '%$q%')
$access_permission_query
$client_query
$expire_query
ORDER BY certificate_name ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Certificates-" . date('Y-m-d_H-i-s') . ".csv");
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Name', 'Description', 'Domain', 'Issuer', 'Expiration Date');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('certificates', $format, $file_name_prepend . 'Certificates', 'Certificates', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while($row = $sql->fetch_assoc()) {
$lineData = array($row['certificate_name'], $row['certificate_description'], $row['certificate_domain'], $row['certificate_issued_by'], $row['certificate_expire']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
while ($row = mysqli_fetch_assoc($sql)) {
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Certificate", "Export", "$session_name exported $num_rows certificate(s) to a CSV file", $client_id);
logAudit("Certificate", "Export", "$session_name exported $num_rows certificate(s) to a " . strtoupper($format) . " file", $client_id);
exit;

View File

@@ -528,51 +528,125 @@ if (isset($_GET['delete_client'])) {
}
if (isset($_POST['export_clients_csv'])) {
if (isset($_POST['export_clients'])) {
validateCSRFToken();
enforceUserPermission('module_client', 1);
enforceUserPermission('module_client');
//get records from database
$sql = mysqli_query($mysqli, "SELECT * FROM clients
$format = resolveExportFormat($_POST['export_clients']);
// Filters inherited from the clients page - mirrors agent/clients.php
$filter_summary = [];
// Leads Filter
if (isset($_POST['leads']) && $_POST['leads'] == 1) {
$leads_query = "AND client_lead = 1";
$filter_summary['Showing'] = 'Leads';
$export_label = 'Leads';
} else {
$leads_query = "AND client_lead = 0";
$export_label = 'Clients';
}
// Tags Filter
if (isset($_POST['tags']) && is_array($_POST['tags']) && !empty($_POST['tags'])) {
$tag_filter = implode(",", array_map('intval', $_POST['tags']));
$tag_query = "AND tags.tag_id IN ($tag_filter)";
$tag_names = [];
$sql_tags = mysqli_query($mysqli, "SELECT tag_name FROM tags WHERE tag_id IN ($tag_filter) ORDER BY tag_name ASC");
while ($tag_row = mysqli_fetch_assoc($sql_tags)) {
$tag_names[] = $tag_row['tag_name'];
}
$filter_summary['Tags'] = implode(', ', $tag_names);
} else {
// Default - any
$tag_query = '';
}
// Industry Filter
if (!empty($_POST['industry'])) {
$industry_query = "AND (clients.client_type = '" . escapeSql($_POST['industry']) . "')";
$filter_summary['Industry'] = $_POST['industry'];
} else {
// Default - any
$industry_query = '';
}
// Referral Filter
if (!empty($_POST['referral'])) {
$referral_query = "AND (clients.client_referral = '" . escapeSql($_POST['referral']) . "')";
$filter_summary['Referral'] = $_POST['referral'];
} else {
// Default - any
$referral_query = '';
}
// Archived Filter
if (isset($_POST['archived']) && $_POST['archived'] == 1) {
$archive_query = "client_archived_at IS NOT NULL";
$filter_summary['Archived'] = 'Archived only';
} else {
$archive_query = "client_archived_at IS NULL";
}
// Date Filter - all-time sentinels from filter_header.php are left in place,
// they match everything anyway
$dtf = escapeSql(!empty($_POST['dtf']) ? $_POST['dtf'] : '1970-01-01');
$dtt = escapeSql(!empty($_POST['dtt']) ? $_POST['dtt'] : '2099-12-31');
$date_range = formatExportDateRange($dtf, $dtt);
if ($date_range) {
$filter_summary['Created'] = $date_range;
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
$phone_query = preg_replace('/\D/', '', $q);
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
// Get records from database - same shape as the clients page list query
$sql = mysqli_query(
$mysqli,
"SELECT clients.*, contacts.*, locations.*
FROM clients
LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1
LEFT JOIN locations ON clients.client_id = locations.location_client_id AND location_primary = 1
ORDER BY client_name ASC
");
LEFT JOIN client_tags ON client_tags.client_id = clients.client_id
LEFT JOIN tags ON tags.tag_id = client_tags.tag_id
WHERE (client_name LIKE '%$q%' OR client_abbreviation LIKE '%$q%' OR client_type LIKE '%$q%' OR client_referral LIKE '%$q%'
OR contact_email LIKE '%$q%' OR contact_name LIKE '%$q%' OR contact_phone LIKE '%$phone_query%'
OR contact_mobile LIKE '%$phone_query%' OR location_address LIKE '%$q%'
OR location_city LIKE '%$q%' OR location_state LIKE '%$q%' OR location_zip LIKE '%$q%' OR location_country LIKE '%$q%'
OR tag_name LIKE '%$q%' OR client_tax_id_number LIKE '%$q%')
AND $archive_query
AND DATE(client_created_at) BETWEEN '$dtf' AND '$dtt'
$leads_query
$access_permission_query
$tag_query
$industry_query
$referral_query
GROUP BY client_id
ORDER BY client_name ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($session_company_name . "-Clients-" . date('Y-m-d_H-i-s') . ".csv");
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Client Name', 'Industry', 'Referral', 'Website', 'Primary Location Name', 'Location Phone', 'Location Address', 'City', 'State', 'Postal Code', 'Country', 'Primary Contact Name', 'Title', 'Contact Phone', 'Extension', 'Contact Mobile', 'Contact Email', 'Hourly Rate', 'Currency', 'Payment Terms', 'Tax ID', 'Abbreviation');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('clients', $format, "$session_company_name-$export_label", $export_label, summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while($row = $sql->fetch_assoc()) {
$lineData = array($row['client_name'], $row['client_type'], $row['client_referral'], $row['client_website'], $row['location_name'], formatPhoneNumber($row['location_phone']), $row['location_address'], $row['location_city'], $row['location_state'], $row['location_zip'], $row['location_country'], $row['contact_name'], $row['contact_title'], formatPhoneNumber($row['contact_phone']), $row['contact_extension'], formatPhoneNumber($row['contact_mobile']), $row['contact_email'], $row['client_rate'], $row['client_currency_code'], $row['client_net_terms'], $row['client_tax_id_number'], $row['client_abbreviation']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
while ($row = mysqli_fetch_assoc($sql)) {
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
finishExport($export);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
logAudit("Client", "Export", "$session_name exported $num_rows client(s) to a CSV file");
logAudit("Client", "Export", "$session_name exported $num_rows client(s) to a " . strtoupper($format) . " file");
}

View File

@@ -1262,59 +1262,116 @@ if (isset($_GET['unlink_contact_from_file'])) {
}
if (isset($_POST['export_contacts_csv'])) {
if (isset($_POST['export_contacts'])) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_client');
if ($_POST['client_id']) {
$format = resolveExportFormat($_POST['export_contacts']);
// Filters inherited from the contacts page - mirrors agent/contacts.php
$filter_summary = [];
// Archived Filter
$archived = (isset($_POST['archived']) && $_POST['archived'] == 1);
if ($archived) {
$filter_summary['Archived'] = 'Archived only';
}
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "AND contact_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
$archive_query = $archived ? "contact_archived_at IS NOT NULL" : "contact_archived_at IS NULL";
} else {
$client_query = '';
$client_id = 0; //Logging;
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
// Client Filter
if (!empty($_POST['client'])) {
$filter_client_id = intval($_POST['client']);
$client_query = "AND (contact_client_id = $filter_client_id)";
$filter_summary['Client'] = getFieldById('clients', $filter_client_id, 'client_name');
}
$archive_query = $archived ? "(client_archived_at IS NOT NULL OR contact_archived_at IS NOT NULL)" : "(client_archived_at IS NULL AND contact_archived_at IS NULL)";
}
//Contacts
$sql = mysqli_query($mysqli,"SELECT * FROM contacts LEFT JOIN locations ON location_id = contact_location_id LEFT JOIN clients ON client_id = contact_client_id WHERE contact_archived_at IS NULL AND client_archived_at IS NULL $client_query $access_permission_query ORDER BY contact_name ASC");
// Tags Filter
if (isset($_POST['tags']) && is_array($_POST['tags']) && !empty($_POST['tags'])) {
$tag_filter = implode(",", array_map('intval', $_POST['tags']));
$tag_query = "AND tags.tag_id IN ($tag_filter)";
$tag_names = [];
$sql_tags = mysqli_query($mysqli, "SELECT tag_name FROM tags WHERE tag_id IN ($tag_filter) ORDER BY tag_name ASC");
while ($tag_row = mysqli_fetch_assoc($sql_tags)) {
$tag_names[] = $tag_row['tag_name'];
}
$filter_summary['Tags'] = implode(', ', $tag_names);
} else {
// Default - any
$tag_query = '';
}
// Location Filter
if (!empty($_POST['location'])) {
$filter_location_id = intval($_POST['location']);
$location_query = "AND (contact_location_id = $filter_location_id)";
$filter_summary['Location'] = getFieldById('locations', $filter_location_id, 'location_name');
} else {
// Default - any
$location_query = '';
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
$sql = mysqli_query(
$mysqli,
"SELECT contacts.*, clients.*, locations.*, users.*
FROM contacts
LEFT JOIN clients ON client_id = contact_client_id
LEFT JOIN locations ON location_id = contact_location_id
LEFT JOIN users ON user_id = contact_user_id
LEFT JOIN contact_tags ON contact_tags.contact_id = contacts.contact_id
LEFT JOIN tags ON tags.tag_id = contact_tags.tag_id
WHERE $archive_query
$tag_query
AND (contact_name LIKE '%$q%' OR contact_title LIKE '%$q%' OR location_name LIKE '%$q%' OR contact_email LIKE '%$q%' OR contact_department LIKE '%$q%' OR contact_phone LIKE '%$q%' OR contact_mobile LIKE '%$q%' OR client_name LIKE '%$q%' OR tag_name LIKE '%$q%')
$access_permission_query
$client_query
$location_query
GROUP BY contact_id
ORDER BY contact_name ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Contacts-" . date('Y-m-d_H-i-s') . ".csv");
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Name', 'Title', 'Department', 'Email', 'Phone', 'Ext', 'Mobile', 'Location');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('contacts', $format, $file_name_prepend . 'Contacts', 'Contacts', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while($row = $sql->fetch_assoc()) {
$lineData = array($row['contact_name'], $row['contact_title'], $row['contact_department'], $row['contact_email'], formatPhoneNumber($row['contact_phone']), $row['contact_extension'], formatPhoneNumber($row['contact_mobile']), $row['location_name']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
while ($row = mysqli_fetch_assoc($sql)) {
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Contact", "Export", "$session_name exported $num_rows contact(s) to a CSV file", $client_id);
logAudit("Contact", "Export", "$session_name exported $num_rows contact(s) to a " . strtoupper($format) . " file", $client_id);
exit;

View File

@@ -410,61 +410,107 @@ if (isset($_POST['bulk_delete_credentials'])) {
}
if (isset($_POST['export_credentials_csv'])) {
if (isset($_POST['export_credentials'])) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_credential');
if ($_POST['client_id']) {
$format = resolveExportFormat($_POST['export_credentials']);
// Filters inherited from the credentials page - mirrors agent/credentials.php
$filter_summary = [];
// Archived Filter
$archived = (isset($_POST['archived']) && $_POST['archived'] == 1);
if ($archived) {
$filter_summary['Archived'] = 'Archived only';
}
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "AND credential_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
$archive_query = $archived ? "credential_archived_at IS NOT NULL" : "credential_archived_at IS NULL";
} else {
$client_query = '';
$client_id = 0;
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
// Client Filter
if (!empty($_POST['client'])) {
$filter_client_id = intval($_POST['client']);
$client_query = "AND (credential_client_id = $filter_client_id)";
$filter_summary['Client'] = getFieldById('clients', $filter_client_id, 'client_name');
}
$archive_query = $archived ? "(client_archived_at IS NOT NULL OR credential_archived_at IS NOT NULL)" : "(client_archived_at IS NULL AND credential_archived_at IS NULL)";
}
//get records from database
$sql = mysqli_query($mysqli,"SELECT * FROM credentials LEFT JOIN clients ON client_id = credential_client_id WHERE credential_archived_at IS NULL $client_query $access_permission_query ORDER BY credential_name ASC");
// Tags Filter
if (isset($_POST['tags']) && is_array($_POST['tags']) && !empty($_POST['tags'])) {
$tag_filter = implode(",", array_map('intval', $_POST['tags']));
$tag_query = "AND tags.tag_id IN ($tag_filter)";
$tag_names = [];
$sql_tags = mysqli_query($mysqli, "SELECT tag_name FROM tags WHERE tag_id IN ($tag_filter) ORDER BY tag_name ASC");
while ($tag_row = mysqli_fetch_assoc($sql_tags)) {
$tag_names[] = $tag_row['tag_name'];
}
$filter_summary['Tags'] = implode(', ', $tag_names);
} else {
// Default - any
$tag_query = '';
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
$sql = mysqli_query(
$mysqli,
"SELECT c.*, clients.*, contacts.*, assets.*
FROM credentials c
LEFT JOIN credential_tags ON credential_tags.credential_id = c.credential_id
LEFT JOIN tags ON tags.tag_id = credential_tags.tag_id
LEFT JOIN clients ON client_id = credential_client_id
LEFT JOIN contacts ON contact_id = credential_contact_id
LEFT JOIN assets ON asset_id = credential_asset_id
WHERE $archive_query
$tag_query
AND (c.credential_name LIKE '%$q%' OR c.credential_description LIKE '%$q%' OR c.credential_uri LIKE '%$q%' OR tag_name LIKE '%$q%' OR client_name LIKE '%$q%')
$access_permission_query
$client_query
GROUP BY c.credential_id
ORDER BY c.credential_name ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Credentials-" . date('Y-m-d_H-i-s') . ".csv");
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Name', 'Description', 'Username', 'Password', 'TOTP', 'URI');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('credentials', $format, $file_name_prepend . 'Credentials', 'Credentials', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while($row = mysqli_fetch_assoc($sql)){
$credential_username = decryptCredentialEntry($row['credential_username']);
$credential_password = decryptCredentialEntry($row['credential_password']);
$lineData = array($row['credential_name'], $row['credential_description'], $credential_username, $credential_password, $row['credential_otp_secret'], $row['credential_uri']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
while ($row = mysqli_fetch_assoc($sql)) {
$row['credential_username'] = decryptCredentialEntry($row['credential_username']);
$row['credential_password'] = decryptCredentialEntry($row['credential_password']);
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Credential", "Export", "$session_name exported $num_rows credential(s) to a CSV file", $client_id);
logAudit("Credential", "Export", "$session_name exported $num_rows credential(s) to a " . strtoupper($format) . " file", $client_id);
exit;

View File

@@ -455,59 +455,103 @@ if (isset($_POST['bulk_refresh_domains'])) {
}
if (isset($_POST['export_domains_csv'])) {
if (isset($_POST['export_domains'])) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_support');
if ($_POST['client_id']) {
$format = resolveExportFormat($_POST['export_domains']);
// Filters inherited from the domains page - mirrors agent/domains.php
$filter_summary = [];
// Archived Filter
$archived = (isset($_POST['archived']) && $_POST['archived'] == 1);
if ($archived) {
$filter_summary['Archived'] = 'Archived only';
}
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "AND domain_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
$archive_query = $archived ? "domain_archived_at IS NOT NULL" : "domain_archived_at IS NULL";
} else {
$client_query = '';
$client_id = 0;
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
// Client Filter
if (!empty($_POST['client'])) {
$filter_client_id = intval($_POST['client']);
$client_query = "AND (domain_client_id = $filter_client_id)";
$filter_summary['Client'] = getFieldById('clients', $filter_client_id, 'client_name');
}
$archive_query = $archived ? "(client_archived_at IS NOT NULL OR domain_archived_at IS NOT NULL)" : "(client_archived_at IS NULL AND domain_archived_at IS NULL)";
}
$sql = mysqli_query($mysqli,"SELECT * FROM domains LEFT JOIN clients ON client_id = domain_client_id WHERE domain_archived_at IS NULL $client_query $access_permission_query ORDER BY domain_name ASC");
// Expiring In Filter
if (!empty($_POST['expire_days'])) {
if ($_POST['expire_days'] == "expired") {
$expire_query = "AND (domain_expire IS NOT NULL AND domain_expire != '0000-00-00' AND domain_expire < CURDATE())";
$filter_summary['Expiry'] = 'Expired';
} else {
$expire_days = intval($_POST['expire_days']);
$expire_query = "AND (domain_expire IS NOT NULL AND domain_expire != '0000-00-00' AND domain_expire BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL $expire_days DAY))";
$filter_summary['Expiry'] = "Expiring within $expire_days days";
}
} else {
// Default - any
$expire_query = '';
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
$sql = mysqli_query(
$mysqli,
"SELECT domains.*, clients.*,
registrar.vendor_name AS domain_registrar_name,
webhost.vendor_name AS domain_webhost_name
FROM domains
LEFT JOIN clients ON client_id = domain_client_id
LEFT JOIN vendors AS registrar ON domains.domain_registrar = registrar.vendor_id
LEFT JOIN vendors AS webhost ON domains.domain_webhost = webhost.vendor_id
WHERE (domains.domain_name LIKE '%$q%' OR domains.domain_description LIKE '%$q%' OR registrar.vendor_name LIKE '%$q%' OR webhost.vendor_name LIKE '%$q%' OR client_name LIKE '%$q%')
AND $archive_query
$access_permission_query
$client_query
$expire_query
ORDER BY domains.domain_name ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Domains-" . date('Y-m-d_H-i-s') . ".csv");
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Domain', 'Description', 'Registrar', 'Web Host', 'Expiration Date');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('domains', $format, $file_name_prepend . 'Domains', 'Domains', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while($row = $sql->fetch_assoc()) {
$lineData = array($row['domain_name'], $row['domain_description'], $row['domain_registrar'], $row['domain_webhost'], $row['domain_expire']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
while ($row = mysqli_fetch_assoc($sql)) {
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Domain", "Export", "$session_name exported $num_rows domain(s)", $client_id);
logAudit("Domain", "Export", "$session_name exported $num_rows domain(s) to a " . strtoupper($format) . " file", $client_id);
exit;

View File

@@ -298,96 +298,98 @@ if (isset($_POST['bulk_delete_expenses'])) {
}
if (isset($_POST['export_expenses_csv'])) {
if (isset($_POST['export_expenses'])) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_financial');
$date_from = escapeSql($_POST['date_from']);
$date_to = escapeSql($_POST['date_to']);
$account = intval($_POST['account']);
$vendor = intval($_POST['vendor']);
$category = intval($_POST['category']);
$format = resolveExportFormat($_POST['export_expenses']);
if (!empty($date_from) && !empty($date_to)) {
$date_query = "AND DATE(expense_date) BETWEEN '$date_from' AND '$date_to'";
$file_name_date = "$date_from-to-$date_to";
}else{
$date_query = "";
$file_name_date = date('Y-m-d');
}
// Filters inherited from the expenses page - mirrors agent/expenses.php
$filter_summary = [];
// Vendor Filter
if ($account) {
$account_query = "AND expense_account_id = $account";
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
// Account Filter
if (!empty($_POST['account'])) {
$filter_account_id = intval($_POST['account']);
$account_query = "AND (expense_account_id = $filter_account_id)";
$filter_summary['Account'] = getFieldById('accounts', $filter_account_id, 'account_name');
} else {
// Default - any
$account_query = '';
}
// Vendor Filter
if ($vendor) {
$vendor_query = "AND expense_vendor_id = $vendor";
if (!empty($_POST['vendor'])) {
$filter_vendor_id = intval($_POST['vendor']);
$vendor_query = "AND (vendor_id = $filter_vendor_id)";
$filter_summary['Vendor'] = getFieldById('vendors', $filter_vendor_id, 'vendor_name');
} else {
// Default - any
$vendor_query = '';
}
// Category Filter
if ($category) {
$category_query = "AND expense_category_id = $category";
if (!empty($_POST['category'])) {
$filter_category_id = intval($_POST['category']);
$category_query = "AND (category_id = $filter_category_id)";
$filter_summary['Category'] = getFieldById('categories', $filter_category_id, 'category_name');
} else {
// Default - any
$category_query = '';
}
//get records from database
$sql = mysqli_query($mysqli,"SELECT * FROM expenses
LEFT JOIN categories ON expense_category_id = category_id
LEFT JOIN vendors ON expense_vendor_id = vendor_id
LEFT JOIN accounts ON expense_account_id = account_id
LEFT JOIN clients ON expense_client_id = client_id
WHERE expense_vendor_id > 0
$date_query
$account_query
$vendor_query
$category_query
$access_permission_query
ORDER BY expense_date DESC
");
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename("$session_company_name-Expenses-" . date('Y-m-d_H-i-s') . ".csv");
//create a file pointer
$f = fopen('php://memory', 'w');
//set column headers
$fields = array('Date', 'Amount', 'Vendor', 'Description', 'Category', 'Account');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
//output each row of the data, format line as csv and write to file pointer
while($row = mysqli_fetch_assoc($sql)) {
$lineData = array($row['expense_date'], $row['expense_amount'], $row['vendor_name'], $row['expense_description'], $row['category_name'], $row['account_name']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
// Date Filter
$dtf = escapeSql(!empty($_POST['dtf']) ? $_POST['dtf'] : '1970-01-01');
$dtt = escapeSql(!empty($_POST['dtt']) ? $_POST['dtt'] : '2099-12-31');
$date_range = formatExportDateRange($dtf, $dtt);
if ($date_range) {
$filter_summary['Dated'] = $date_range;
}
logAudit("Expense", "Export", "$session_name exported $num_rows expense(s) to CSV file");
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
$sql = mysqli_query(
$mysqli,
"SELECT * FROM expenses
LEFT JOIN categories ON expense_category_id = category_id
LEFT JOIN vendors ON expense_vendor_id = vendor_id
LEFT JOIN accounts ON expense_account_id = account_id
LEFT JOIN clients ON expense_client_id = client_id
WHERE expense_vendor_id > 0
AND DATE(expense_date) BETWEEN '$dtf' AND '$dtt'
$vendor_query
$category_query
AND (vendor_name LIKE '%$q%' OR client_name LIKE '%$q%' OR category_name LIKE '%$q%' OR account_name LIKE '%$q%' OR expense_description LIKE '%$q%' OR expense_amount LIKE '%$q%')
$account_query
$access_permission_query
ORDER BY expense_date ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
guardExportPdfRowCount($format, $num_rows);
$export = beginExport('expenses', $format, $file_name_prepend . 'Expenses', 'Expenses', summarizeExportFilters($filter_summary));
while ($row = mysqli_fetch_assoc($sql)) {
addExportRow($export, $row);
}
finishExport($export);
}
logAudit("Expense", "Export", "$session_name exported $num_rows expense(s) to a " . strtoupper($format) . " file", $client_id);
exit;

View File

@@ -9,12 +9,14 @@ if (!defined('FROM_POST_HANDLER')) {
exit;
}
if (isset($_POST['export_income_csv'])) {
if (isset($_POST['export_income'])) {
validateCSRFToken();
enforceUserPermission('module_financial');
$format = resolveExportFormat($_POST['export_income']);
$date_from = escapeSql($_POST['date_from']);
$date_to = escapeSql($_POST['date_to']);
$account = intval($_POST['account']);
@@ -139,36 +141,19 @@ if (isset($_POST['export_income_csv'])) {
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Income-" . date('Y-m-d_H-i-s') . ".csv");
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Date', 'Type', 'Source', 'Description', 'Client', 'Amount', 'Currency', 'Payment Method', 'Reference', 'Account');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('income', $format, $file_name_prepend . 'Income', 'Income', summarizeExportFilters($filter_summary ?? []));
//output each row of the data, format line as csv and write to file pointer
while ($row = mysqli_fetch_assoc($sql)) {
$lineData = array($row['income_date'], $row['income_type'], $row['income_source'], $row['income_description'], $row['income_client'], $row['income_amount'], $row['income_currency_code'], $row['income_method'], $row['income_reference'], $row['income_account']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Income", "Export", "$session_name exported $num_rows income record(s) to CSV file");
logAudit("Income", "Export", "$session_name exported $num_rows income record(s) to a " . strtoupper($format) . " file");
exit;

View File

@@ -650,69 +650,117 @@ if (isset($_GET['email_invoice'])) {
}
if (isset($_POST['export_invoices_csv'])) {
if (isset($_POST['export_invoices'])) {
validateCSRFToken();
enforceUserPermission('module_sales');
if ($_POST['client_id']) {
$format = resolveExportFormat($_POST['export_invoices']);
// Filters inherited from the invoices page - mirrors agent/invoices.php
$filter_summary = [];
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "1=1 AND invoice_client_id = $client_id";
$client_query = "AND invoice_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
} else {
$client_query = '1=1 ';
$client_name = '';
$client_query = '';
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
}
$date_from = escapeSql($_POST['date_from']);
$date_to = escapeSql($_POST['date_to']);
if (!empty($date_from) && !empty($date_to)) {
$date_query = "DATE(invoice_date) BETWEEN '$date_from' AND '$date_to'";
$file_name_date = "$date_from-to-$date_to";
}else{
$date_query = "";
$file_name_date = date('Y-m-d_H-i-s');
// Status Filter
$overdue_query = '';
if (!empty($_POST['status']) && $_POST['status'] == 'Draft') {
$status_query = "invoice_status = 'Draft'";
$filter_summary['Status'] = 'Draft';
} elseif (!empty($_POST['status']) && $_POST['status'] == 'Unpaid') {
$status_query = "invoice_status = 'Sent' OR invoice_status = 'Viewed' OR invoice_status = 'Partial'";
$filter_summary['Status'] = 'Unpaid';
} elseif (!empty($_POST['status']) && $_POST['status'] == 'Overdue') {
$status_query = "invoice_status = 'Sent' OR invoice_status = 'Viewed' OR invoice_status = 'Partial'";
$overdue_query = "AND (invoice_due < CURDATE())";
$filter_summary['Status'] = 'Overdue';
} else {
// Default - any
$status_query = "invoice_status LIKE '%'";
}
$sql = mysqli_query($mysqli,"SELECT * FROM invoices LEFT JOIN clients ON invoice_client_id = client_id WHERE $date_query AND $client_query $access_permission_query ORDER BY invoice_number ASC");
// Category Filter
if (!empty($_POST['category'])) {
$filter_category_id = intval($_POST['category']);
$category_query = "AND (category_id = $filter_category_id)";
$filter_summary['Category'] = getFieldById('categories', $filter_category_id, 'category_name');
} else {
// Default - any
$category_query = '';
}
// Date Filter - drives the file name too, when a real range is in play
$dtf = escapeSql(!empty($_POST['dtf']) ? $_POST['dtf'] : '1970-01-01');
$dtt = escapeSql(!empty($_POST['dtt']) ? $_POST['dtt'] : '2099-12-31');
$date_range = formatExportDateRange($dtf, $dtt);
if ($date_range) {
$filter_summary['Issued'] = $date_range;
$file_name_append = "-$dtf-to-$dtt";
} else {
$file_name_append = '';
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
// Get records from database - same shape as the invoices page list query
$sql = mysqli_query(
$mysqli,
"SELECT * FROM invoices
LEFT JOIN clients ON invoice_client_id = client_id
LEFT JOIN categories ON invoice_category_id = category_id
WHERE ($status_query)
$overdue_query
$category_query
AND DATE(invoice_date) BETWEEN '$dtf' AND '$dtt'
AND (CONCAT(invoice_prefix,invoice_number) LIKE '%$q%' OR invoice_scope LIKE '%$q%' OR client_name LIKE '%$q%' OR invoice_status LIKE '%$q%' OR invoice_amount LIKE '%$q%' OR category_name LIKE '%$q%')
$access_permission_query
$client_query
ORDER BY invoice_number ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Invoices-$file_name_date.csv");
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Invoice Number', 'Scope', 'Amount', 'Issued Date', 'Due Date', 'Status');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('invoices', $format, $file_name_prepend . 'Invoices' . $file_name_append, 'Invoices', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while($row = $sql->fetch_assoc()) {
$lineData = array($row['invoice_prefix'] . $row['invoice_number'], $row['invoice_scope'], $row['invoice_amount'], $row['invoice_date'], $row['invoice_due'], $row['invoice_status'], $row['client_name']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
while ($row = mysqli_fetch_assoc($sql)) {
$row['invoice_number_display'] = $row['invoice_prefix'] . $row['invoice_number'];
// Paid / balance are opt-in columns - only pay for the lookup if asked
if (isset($export['columns']['amount_paid']) || isset($export['columns']['invoice_balance'])) {
$invoice_id = intval($row['invoice_id']);
$payment_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT SUM(payment_amount) AS amount_paid FROM payments WHERE payment_invoice_id = $invoice_id AND payment_archived_at IS NULL"));
$row['amount_paid'] = floatval($payment_row['amount_paid']);
$row['invoice_balance'] = floatval($row['invoice_amount']) - $row['amount_paid'];
}
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Invoice", "Export", "$session_name exported $num_rows invoices to CSV file");
logAudit("Invoice", "Export", "$session_name exported $num_rows invoice(s) to a " . strtoupper($format) . " file", $client_id);
exit;

View File

@@ -388,59 +388,103 @@ if (isset($_POST['bulk_delete_locations'])) {
}
if(isset($_POST['export_locations_csv'])){
if (isset($_POST['export_locations'])) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_client');
if ($_POST['client_id']) {
$format = resolveExportFormat($_POST['export_locations']);
// Filters inherited from the locations page - mirrors agent/locations.php
$filter_summary = [];
// Archived Filter
$archived = (isset($_POST['archived']) && $_POST['archived'] == 1);
if ($archived) {
$filter_summary['Archived'] = 'Archived only';
}
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "AND location_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
$archive_query = $archived ? "location_archived_at IS NOT NULL" : "location_archived_at IS NULL";
} else {
$client_query = '';
$client_id = 0;
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
// Client Filter
if (!empty($_POST['client'])) {
$filter_client_id = intval($_POST['client']);
$client_query = "AND (location_client_id = $filter_client_id)";
$filter_summary['Client'] = getFieldById('clients', $filter_client_id, 'client_name');
}
$archive_query = $archived ? "(client_archived_at IS NOT NULL OR location_archived_at IS NOT NULL)" : "(client_archived_at IS NULL AND location_archived_at IS NULL)";
}
//Locations
$sql = mysqli_query($mysqli,"SELECT * FROM locations LEFT JOIN clients ON client_id = location_client_id WHERE location_archived_at IS NULL AND client_archived_at IS NULL $client_query $access_permission_query ORDER BY location_name ASC");
// Tags Filter
if (isset($_POST['tags']) && is_array($_POST['tags']) && !empty($_POST['tags'])) {
$tag_filter = implode(",", array_map('intval', $_POST['tags']));
$tag_query = "AND tags.tag_id IN ($tag_filter)";
$tag_names = [];
$sql_tags = mysqli_query($mysqli, "SELECT tag_name FROM tags WHERE tag_id IN ($tag_filter) ORDER BY tag_name ASC");
while ($tag_row = mysqli_fetch_assoc($sql_tags)) {
$tag_names[] = $tag_row['tag_name'];
}
$filter_summary['Tags'] = implode(', ', $tag_names);
} else {
// Default - any
$tag_query = '';
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
$sql = mysqli_query(
$mysqli,
"SELECT locations.*, clients.*
FROM locations
LEFT JOIN clients ON client_id = location_client_id
LEFT JOIN location_tags ON location_tags.location_id = locations.location_id
LEFT JOIN tags ON tags.tag_id = location_tags.tag_id
WHERE $archive_query
$tag_query
AND (location_name LIKE '%$q%' OR location_description LIKE '%$q%' OR location_address LIKE '%$q%' OR location_city LIKE '%$q%' OR location_state LIKE '%$q%' OR location_zip LIKE '%$q%' OR location_country LIKE '%$q%' OR location_phone LIKE '%$q%' OR client_name LIKE '%$q%' OR tag_name LIKE '%$q%')
$access_permission_query
$client_query
GROUP BY location_id
ORDER BY location_name ASC"
);
$num_rows = mysqli_num_rows($sql);
if($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Locations-" . date('Y-m-d_H-i-s') . ".csv");
if ($num_rows > 0) {
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Name', 'Description', 'Address', 'City', 'State', 'Postal Code', 'Phone', 'Hours');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('locations', $format, $file_name_prepend . 'Locations', 'Locations', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while($row = $sql->fetch_assoc()){
$lineData = array($row['location_name'], $row['location_description'], $row['location_address'], $row['location_city'], $row['location_state'], $row['location_zip'], $row['location_phone'], $row['location_hours']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
while ($row = mysqli_fetch_assoc($sql)) {
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Location", "Export", "$session_name exported $num_rows location(s) to a CSV file", $client_id);
logAudit("Location", "Export", "$session_name exported $num_rows location(s) to a " . strtoupper($format) . " file", $client_id);
exit;

View File

@@ -172,59 +172,94 @@ if (isset($_POST['bulk_delete_networks'])) {
}
if (isset($_POST['export_networks_csv'])) {
if (isset($_POST['export_networks'])) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_support');
if ($_POST['client_id']) {
$format = resolveExportFormat($_POST['export_networks']);
// Filters inherited from the networks page - mirrors agent/networks.php
$filter_summary = [];
// Archived Filter
$archived = (isset($_POST['archived']) && $_POST['archived'] == 1);
if ($archived) {
$filter_summary['Archived'] = 'Archived only';
}
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "AND network_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
$archive_query = $archived ? "network_archived_at IS NOT NULL" : "network_archived_at IS NULL";
} else {
$client_query = '';
$client_id = 0;
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
// Client Filter
if (!empty($_POST['client'])) {
$filter_client_id = intval($_POST['client']);
$client_query = "AND (network_client_id = $filter_client_id)";
$filter_summary['Client'] = getFieldById('clients', $filter_client_id, 'client_name');
}
$archive_query = $archived ? "(client_archived_at IS NOT NULL OR network_archived_at IS NOT NULL)" : "(client_archived_at IS NULL AND network_archived_at IS NULL)";
}
$sql = mysqli_query($mysqli,"SELECT * FROM networks LEFT JOIN clients ON client_id = network_client_id WHERE network_archived_at IS NULL $client_query $access_permission_query ORDER BY network_name ASC");
// Location Filter
if (!empty($_POST['location'])) {
$filter_location_id = intval($_POST['location']);
$location_query = "AND (network_location_id = $filter_location_id)";
$filter_summary['Location'] = getFieldById('locations', $filter_location_id, 'location_name');
} else {
// Default - any
$location_query = '';
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
$sql = mysqli_query(
$mysqli,
"SELECT * FROM networks
LEFT JOIN clients ON client_id = network_client_id
LEFT JOIN locations ON location_id = network_location_id
WHERE $archive_query
AND (network_name LIKE '%$q%' OR network_description LIKE '%$q%' OR network_vlan LIKE '%$q%' OR network LIKE '%$q%' OR network_gateway LIKE '%$q%' OR network_primary_dns LIKE '%$q%' OR network_secondary_dns LIKE '%$q%' OR client_name LIKE '%$q%')
$access_permission_query
$location_query
$client_query
ORDER BY network_name ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Networks-" . date('Y-m-d_H-i-s') . ".csv");
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Name', 'Description', 'VLAN', 'Network (CIDR)', 'Gateway', 'IP Range', 'Primary DNS', 'Secondary DNS');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('networks', $format, $file_name_prepend . 'Networks', 'Networks', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while ($row = $sql->fetch_assoc()) {
$lineData = array($row['network_name'], $row['network_description'], $row['network_vlan'], $row['network'], $row['network_gateway'], $row['network_dhcp_range'], $row['network_primary_dns'], $row['network_secondary_dns']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
while ($row = mysqli_fetch_assoc($sql)) {
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Network", "Export", "$session_name deleted $num_rows network(s) to a CSV file", $client_id);
logAudit("Network", "Export", "$session_name exported $num_rows network(s) to a " . strtoupper($format) . " file", $client_id);
exit;
@@ -232,7 +267,7 @@ if (isset($_POST['export_networks_csv'])) {
// ============================================================
// Add these two blocks to agent/post/network.php
// Place them alongside the existing export_networks_csv block.
// Place them alongside the existing export_networks block.
// ============================================================
// ----------------------------------------------------------

View File

@@ -244,55 +244,90 @@ if (isset($_POST['bulk_delete_products'])) {
}
if (isset($_POST['export_products_csv'])) {
if (isset($_POST['export_products'])) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_sales');
//get records from database
$sql = mysqli_query($mysqli,"SELECT * FROM products
LEFT JOIN categories ON product_category_id = category_id
LEFT JOIN taxes ON product_tax_id = tax_id
WHERE product_archived_at IS NULL
ORDER BY product_name DESC
");
$format = resolveExportFormat($_POST['export_products']);
// Filters inherited from the products page - mirrors agent/products.php
$filter_summary = [];
// Archived Filter
$archived = (isset($_POST['archived']) && $_POST['archived'] == 1);
if ($archived) {
$filter_summary['Archived'] = 'Archived only';
}
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
// Type Filter
if (isset($_POST['type']) && $_POST['type'] === 'product') {
$type_query = "AND product_type = 'product'";
$filter_summary['Type'] = 'Product';
} elseif (isset($_POST['type']) && $_POST['type'] === 'service') {
$type_query = "AND product_type = 'service'";
$filter_summary['Type'] = 'Service';
} else {
// Default - any
$type_query = '';
}
$archive_query = $archived ? "product_archived_at IS NOT NULL" : "product_archived_at IS NULL";
// Category Filter
if (!empty($_POST['category'])) {
$filter_category_id = intval($_POST['category']);
$category_query = "AND (category_id = $filter_category_id)";
$filter_summary['Category'] = getFieldById('categories', $filter_category_id, 'category_name');
} else {
// Default - any
$category_query = '';
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
$sql = mysqli_query(
$mysqli,
"SELECT products.*, categories.*, taxes.*
FROM products
LEFT JOIN categories ON product_category_id = category_id
LEFT JOIN taxes ON product_tax_id = tax_id
WHERE (product_name LIKE '%$q%' OR product_description LIKE '%$q%' OR product_code LIKE '%$q%' OR product_location LIKE '%$q%' OR category_name LIKE '%$q%' OR product_price LIKE '%$q%' OR tax_name LIKE '%$q%')
$type_query
AND $archive_query
$category_query
GROUP BY product_id
ORDER BY product_name ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename("$session_company_name-Products-" . date('Y-m-d_H-i-s') . ".csv");
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Product', 'Description', 'Price', 'Currency', 'Category', 'Tax');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('products', $format, $file_name_prepend . 'Products', 'Products', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while($row = mysqli_fetch_assoc($sql)) {
$lineData = array($row['product_name'], $row['product_description'], $row['product_price'], $row['product_currency_code'], $row['category_name'], $row['tax_name']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
while ($row = mysqli_fetch_assoc($sql)) {
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Product", "Export", "$session_name exported $num_rows product(s) to a CSV file");
logAudit("Product", "Export", "$session_name exported $num_rows product(s) to a " . strtoupper($format) . " file", $client_id);
exit;
}
if (isset($_POST['add_product_stock'])) {

View File

@@ -685,62 +685,75 @@ if (isset($_GET['mark_quote_invoiced'])) {
}
if(isset($_POST['export_quotes_csv'])){
if (isset($_POST['export_quotes'])) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_sales');
if ($_POST['client_id']) {
$format = resolveExportFormat($_POST['export_quotes']);
// Filters inherited from the quotes page - mirrors agent/quotes.php
$filter_summary = [];
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "WHERE quote_client_id = $client_id";
// Get Client Name for logging
$client_query = "AND quote_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
} else {
$client_query = 'WHERE 1=1';
$client_name = '';
$file_name_prepend = "$session_company_name";
$client_query = '';
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
}
$sql = mysqli_query($mysqli,"SELECT * FROM quotes LEFT JOIN clients ON client_id = quote_client_id $client_query $access_permission_query ORDER BY quote_number ASC");
// Date Filter
$dtf = escapeSql(!empty($_POST['dtf']) ? $_POST['dtf'] : '1970-01-01');
$dtt = escapeSql(!empty($_POST['dtt']) ? $_POST['dtt'] : '2099-12-31');
$date_range = formatExportDateRange($dtf, $dtt);
if ($date_range) {
$filter_summary['Dated'] = $date_range;
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
$sql = mysqli_query(
$mysqli,
"SELECT * FROM quotes
LEFT JOIN clients ON quote_client_id = client_id
LEFT JOIN categories ON quote_category_id = category_id
WHERE (CONCAT(quote_prefix,quote_number) LIKE '%$q%' OR quote_scope LIKE '%$q%' OR category_name LIKE '%$q%' OR quote_status LIKE '%$q%' OR quote_amount LIKE '%$q%' OR client_name LIKE '%$q%')
AND DATE(quote_date) BETWEEN '$dtf' AND '$dtt'
$access_permission_query
$client_query
ORDER BY quote_number ASC"
);
$num_rows = mysqli_num_rows($sql);
if($num_rows > 0){
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Quotes-" . date('Y-m-d_H-i-s') . ".csv");
if ($num_rows > 0) {
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Quote Number', 'Scope', 'Amount', 'Date', 'Status');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('quotes', $format, $file_name_prepend . 'Quotes', 'Quotes', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while($row = $sql->fetch_assoc()){
$lineData = array($row['quote_prefix'] . $row['quote_number'], $row['quote_scope'], $row['quote_amount'], $row['quote_date'], $row['quote_status']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
while ($row = mysqli_fetch_assoc($sql)) {
$row['quote_number_display'] = $row['quote_prefix'] . $row['quote_number'];
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Quote", "Export", "$session_name exported $num_rows quote(s) to a CSV file");
flashAlert("Exported <strong>$num_rows</strong> quote(s)");
logAudit("Quote", "Export", "$session_name exported $num_rows quote(s) to a " . strtoupper($format) . " file", $client_id);
exit;

View File

@@ -605,55 +605,89 @@ if (isset($_POST['set_recurring_payment'])) {
}
if (isset($_POST['export_client_recurring_invoice_csv'])) {
if (isset($_POST['export_recurring_invoices'])) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_sales');
$client_id = intval($_POST['client_id']);
$format = resolveExportFormat($_POST['export_recurring_invoices']);
enforceClientAccess();
// Filters inherited from the recurring invoices page - mirrors agent/recurring_invoices.php
$filter_summary = [];
//get records from database
$sql = mysqli_query($mysqli,"SELECT client_name FROM clients WHERE client_id = $client_id");
$row = mysqli_fetch_assoc($sql);
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "AND recurring_invoice_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
$client_name = $row['client_name'];
enforceClientAccess();
} else {
$client_query = '';
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
}
$sql = mysqli_query($mysqli,"SELECT * FROM recurring_invoices WHERE recurring_invoice_client_id = $client_id ORDER BY recurring_invoice_number ASC");
// Status Filter
if (isset($_POST['status']) && $_POST['status'] === 'inactive') {
$status_query = "AND recurring_invoice_status = 0";
$filter_summary['Status'] = 'Inactive';
} elseif (isset($_POST['status']) && $_POST['status'] === 'active') {
$status_query = "AND recurring_invoice_status = 1";
$filter_summary['Status'] = 'Active';
} else {
// Default - any
$status_query = '';
}
// Date Filter
$dtf = escapeSql(!empty($_POST['dtf']) ? $_POST['dtf'] : '1970-01-01');
$dtt = escapeSql(!empty($_POST['dtt']) ? $_POST['dtt'] : '2099-12-31');
$date_range = formatExportDateRange($dtf, $dtt);
if ($date_range) {
$filter_summary['Created'] = $date_range;
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
$sql = mysqli_query(
$mysqli,
"SELECT * FROM recurring_invoices
LEFT JOIN clients ON recurring_invoice_client_id = client_id
LEFT JOIN categories ON recurring_invoice_category_id = category_id
WHERE (CONCAT(recurring_invoice_prefix,recurring_invoice_number) LIKE '%$q%' OR recurring_invoice_frequency LIKE '%$q%' OR recurring_invoice_scope LIKE '%$q%' OR client_name LIKE '%$q%' OR category_name LIKE '%$q%')
AND DATE(recurring_invoice_created_at) BETWEEN '$dtf' AND '$dtt'
$status_query
$client_query
$access_permission_query
ORDER BY recurring_invoice_number ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$filename = $client_name . "-Recurring Invoices-" . date('Y-m-d') . ".csv";
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Recurring Number', 'Scope', 'Amount', 'Frequency', 'Date Created');
fputcsv($f, $fields, $delimiter);
$export = beginExport('recurring_invoices', $format, $file_name_prepend . 'RecurringInvoices', 'Recurring Invoices', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while($row = $sql->fetch_assoc()) {
$lineData = array($row['recurring_invoice_prefix'] . $row['recurring_invoice_number'], $row['recurring_invoice_scope'], $row['recurring_invoice_amount'], ucwords($row['recurring_invoice_frequency'] . "ly"), $row['recurring_invoice_created_at']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter);
while ($row = mysqli_fetch_assoc($sql)) {
$row['recurring_invoice_number_display'] = $row['recurring_invoice_prefix'] . $row['recurring_invoice_number'];
$row['recurring_invoice_frequency_display'] = ucwords($row['recurring_invoice_frequency'] . 'ly');
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Recurring Invoice", "Export", "$session_name exported $num_rows recurring invoices to CSV file");
logAudit("Recurring Invoice", "Export", "$session_name exported $num_rows recurring invoice(s) to a " . strtoupper($format) . " file", $client_id);
exit;

View File

@@ -225,86 +225,118 @@ if (isset($_GET['delete_software'])) {
}
if (isset($_POST['export_software_csv'])) {
if (isset($_POST['export_software'])) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_support');
if ($_POST['client_id']) {
$client_id = intval($_POST['client_id']);
$client_query = "WHERE software_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
} else {
$client_query = '';
$client_id = 0; //Logging
$file_name_prepend = "$session_company_name-";
$format = resolveExportFormat($_POST['export_software']);
// Filters inherited from the software page - mirrors agent/software.php
$filter_summary = [];
// Archived Filter
$archived = (isset($_POST['archived']) && $_POST['archived'] == 1);
if ($archived) {
$filter_summary['Archived'] = 'Archived only';
}
$sql = mysqli_query($mysqli,"SELECT * FROM software LEFT JOIN client ON client_id = software_client_id WHERE software_archived_at IS NULL $client_query $access_permission_query ORDER BY software_name ASC");
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "AND software_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
$archive_query = $archived ? "software_archived_at IS NOT NULL" : "software_archived_at IS NULL";
} else {
$client_query = '';
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
// Client Filter
if (!empty($_POST['client'])) {
$filter_client_id = intval($_POST['client']);
$client_query = "AND (software_client_id = $filter_client_id)";
$filter_summary['Client'] = getFieldById('clients', $filter_client_id, 'client_name');
}
$archive_query = $archived ? "(client_archived_at IS NOT NULL OR software_archived_at IS NOT NULL)" : "(client_archived_at IS NULL AND software_archived_at IS NULL)";
}
// Expiring In Filter
if (!empty($_POST['expire_days'])) {
if ($_POST['expire_days'] == "expired") {
$expire_query = "AND (software_expire IS NOT NULL AND software_expire != '0000-00-00' AND software_expire < CURDATE())";
$filter_summary['Expiry'] = 'Expired';
} else {
$expire_days = intval($_POST['expire_days']);
$expire_query = "AND (software_expire IS NOT NULL AND software_expire != '0000-00-00' AND software_expire BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL $expire_days DAY))";
$filter_summary['Expiry'] = "Expiring within $expire_days days";
}
} else {
// Default - any
$expire_query = '';
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
$sql = mysqli_query(
$mysqli,
"SELECT * FROM software
LEFT JOIN clients ON client_id = software_client_id
LEFT JOIN vendors ON vendor_id = software_vendor_id
WHERE (software_name LIKE '%$q%' OR software_type LIKE '%$q%' OR software_key LIKE '%$q%' OR client_name LIKE '%$q%')
AND $archive_query
$access_permission_query
$client_query
$expire_query
ORDER BY software_name ASC"
);
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Software-" . date('Y-m-d_H-i-s') . ".csv");
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Name', 'Version', 'Description', 'Type', 'License Type', 'Seats', 'Key', 'Assets', 'Contacts', 'Purchased', 'Expires', 'Notes');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('software', $format, $file_name_prepend . 'Software', 'Software', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while($row = $sql->fetch_assoc()) {
// Generate asset & user license list for this software
// Asset licenses
$assigned_to_assets = '';
$asset_licenses_sql = mysqli_query($mysqli,"SELECT software_assets.asset_id, assets.asset_name
FROM software_assets
LEFT JOIN assets
ON software_assets.asset_id = assets.asset_id
WHERE software_id = $row[software_id]"
);
while($asset_row = mysqli_fetch_assoc($asset_licenses_sql)) {
$assigned_to_assets .= $asset_row['asset_name'] . ", ";
while ($row = mysqli_fetch_assoc($sql)) {
// Asset and contact licence lists, only when those columns are wanted
if (isset($export['columns']['assigned_to_assets'])) {
$software_id = intval($row['software_id']);
$assigned_to_assets = [];
$asset_licenses_sql = mysqli_query($mysqli, "SELECT asset_name FROM software_assets LEFT JOIN assets ON software_assets.asset_id = assets.asset_id WHERE software_id = $software_id");
while ($asset_row = mysqli_fetch_assoc($asset_licenses_sql)) {
$assigned_to_assets[] = $asset_row['asset_name'];
}
$row['assigned_to_assets'] = implode(', ', $assigned_to_assets);
}
// Contact Licenses
$assigned_to_contacts = '';
$contact_licenses_sql = mysqli_query($mysqli,"SELECT software_contacts.contact_id, contacts.contact_name
FROM software_contacts
LEFT JOIN contacts
ON software_contacts.contact_id = contacts.contact_id
WHERE software_id = $row[software_id]"
);
while($contact_row = mysqli_fetch_assoc($contact_licenses_sql)) {
$assigned_to_contacts .= $contact_row['contact_name'] . ", ";
if (isset($export['columns']['assigned_to_contacts'])) {
$software_id = intval($row['software_id']);
$assigned_to_contacts = [];
$contact_licenses_sql = mysqli_query($mysqli, "SELECT contact_name FROM software_contacts LEFT JOIN contacts ON software_contacts.contact_id = contacts.contact_id WHERE software_id = $software_id");
while ($contact_row = mysqli_fetch_assoc($contact_licenses_sql)) {
$assigned_to_contacts[] = $contact_row['contact_name'];
}
$row['assigned_to_contacts'] = implode(', ', $assigned_to_contacts);
}
$lineData = array($row['software_name'], $row['software_version'], $row['software_description'], $row['software_type'], $row['software_license_type'], $row['software_seats'], $row['software_key'], $assigned_to_assets, $assigned_to_contacts, $row['software_purchase'], $row['software_expire'], $row['software_notes']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Software", "Export", "$session_name exported $num_rows software(s) $software_name to a CSV file", $client_id);
logAudit("Software", "Export", "$session_name exported $num_rows software(s) to a " . strtoupper($format) . " file", $client_id);
exit;

View File

@@ -2695,59 +2695,193 @@ if (isset($_POST['add_quote_from_ticket'])) {
}
if (isset($_POST['export_tickets_csv'])) {
if (isset($_POST['export_tickets'])) {
validateCSRFToken();
enforceUserPermission('module_support', 2);
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_support');
if ($_POST['client_id']) {
$format = resolveExportFormat($_POST['export_tickets']);
// Filters inherited from the tickets page - mirrors agent/tickets.php
$filter_summary = [];
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "WHERE ticket_client_id = $client_id";
$client_query = "AND ticket_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
} else {
$client_query = '';
$client_name = '';
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
// Client Filter - the global ticket list can be narrowed to one client
if (!empty($_POST['client'])) {
$filter_client_id = intval($_POST['client']);
$client_query = "AND ticket_client_id = $filter_client_id";
$filter_summary['Client'] = getFieldById('clients', $filter_client_id, 'client_name');
}
}
// Status Filter - a set of status IDs, or the Open / Closed shorthand
if (isset($_POST['status']) && is_array($_POST['status']) && !empty($_POST['status'])) {
$status_ids = implode(",", array_map('intval', $_POST['status']));
$ticket_status_snippet = "ticket_status IN ($status_ids)";
$status_names = [];
$sql_statuses = mysqli_query($mysqli, "SELECT ticket_status_name FROM ticket_statuses WHERE ticket_status_id IN ($status_ids) ORDER BY ticket_status_name ASC");
while ($status_row = mysqli_fetch_assoc($sql_statuses)) {
$status_names[] = $status_row['ticket_status_name'];
}
$filter_summary['Status'] = implode(', ', $status_names);
} elseif (!empty($_POST['resolution']) && $_POST['resolution'] == 'Closed') {
$ticket_status_snippet = "ticket_resolved_at IS NOT NULL";
$filter_summary['Status'] = 'Closed';
} else {
// Default - open tickets
$ticket_status_snippet = "ticket_resolved_at IS NULL";
$filter_summary['Status'] = 'Open';
}
// Billable / unbilled Filter - overrides the status snippet, same as the page
if (isset($_POST['billable']) && $_POST['billable'] == 1 && isset($_POST['unbilled'])) {
$ticket_billable_snippet = "AND ticket_billable = 1 AND ticket_invoice_id = 0";
$ticket_status_snippet = '1 = 1';
$filter_summary['Billable'] = 'Billable, not yet invoiced';
} else {
$ticket_billable_snippet = '';
}
// Category Filter
if (!empty($_POST['category'])) {
$filter_category_id = intval($_POST['category']);
$category_query = "AND (ticket_category = $filter_category_id)";
$filter_summary['Category'] = getFieldById('categories', $filter_category_id, 'category_name');
} else {
// Default - any
$category_query = '';
}
// Assignment Filter
if (!empty($_POST['assigned'])) {
if ($_POST['assigned'] == 'unassigned') {
$ticket_assigned_query = 'AND ticket_assigned_to = 0';
$filter_summary['Assigned'] = 'Unassigned';
} else {
$filter_user_id = intval($_POST['assigned']);
$ticket_assigned_query = "AND ticket_assigned_to = $filter_user_id";
$filter_summary['Assigned'] = getFieldById('users', $filter_user_id, 'user_name');
}
} else {
// Default - any
$ticket_assigned_query = '';
}
// SLA State Filter
$ticket_sla_query = '';
if (!empty($_POST['sla'])) {
$sla_filter = $_POST['sla'];
if ($sla_filter == 'breached') {
$ticket_sla_query = 'AND ticket_sla_id > 0 AND (ticket_response_sla_alert_stage = 2 OR ticket_resolution_sla_alert_stage = 2 OR ticket_response_sla_met = 0 OR ticket_resolution_sla_met = 0)';
$filter_summary['SLA'] = 'SLA breached';
} elseif ($sla_filter == 'at_risk') {
$ticket_sla_query = 'AND ticket_sla_id > 0 AND COALESCE(ticket_status_pauses_sla, 0) = 0 AND (ticket_response_sla_alert_stage = 1 OR ticket_resolution_sla_alert_stage = 1)';
$filter_summary['SLA'] = 'SLA at risk';
} elseif ($sla_filter == 'paused') {
$ticket_sla_query = 'AND ticket_sla_id > 0 AND ticket_status_pauses_sla = 1';
$filter_summary['SLA'] = 'SLA paused';
} elseif ($sla_filter == 'met') {
$ticket_sla_query = 'AND ticket_sla_id > 0 AND ticket_response_sla_met = 1 AND (ticket_resolution_sla_met = 1 OR ticket_resolution_due_at IS NULL)';
$filter_summary['SLA'] = 'SLA met';
} elseif ($sla_filter == 'none') {
$ticket_sla_query = 'AND ticket_sla_id = 0';
$filter_summary['SLA'] = 'No SLA';
}
}
// Project Filter
if (!empty($_POST['project']) && $_POST['project'] > '0') {
$filter_project_id = intval($_POST['project']);
$ticket_project_snippet = "AND ticket_project_id = $filter_project_id";
$filter_summary['Project'] = getFieldById('projects', $filter_project_id, 'project_name');
} else {
// Default - any, including tickets without a project
$ticket_project_snippet = '';
}
// Client access override - the only way tickets without a client reach agents
// with restricted client access
$access_permission_query_overide = '';
if ($client_access_string) {
$access_permission_query_overide = "AND ticket_client_id IN (0,$client_access_string)";
}
// Date Filter
$dtf = escapeSql(!empty($_POST['dtf']) ? $_POST['dtf'] : '1970-01-01');
$dtt = escapeSql(!empty($_POST['dtt']) ? $_POST['dtt'] : '2099-12-31');
$date_range = formatExportDateRange($dtf, $dtt);
if ($date_range) {
$filter_summary['Opened'] = $date_range;
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
// Get records from database - same shape as the tickets page list query
$sql = mysqli_query(
$mysqli,
"SELECT * FROM tickets
LEFT JOIN clients ON ticket_client_id = client_id
LEFT JOIN contacts ON ticket_contact_id = contact_id
LEFT JOIN users ON ticket_assigned_to = user_id
LEFT JOIN assets ON ticket_asset_id = asset_id
LEFT JOIN locations ON ticket_location_id = location_id
LEFT JOIN vendors ON ticket_vendor_id = vendor_id
LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id
$client_query ORDER BY ticket_number ASC"
LEFT JOIN categories ON ticket_category = category_id
WHERE $ticket_status_snippet
$ticket_assigned_query
$category_query
AND DATE(ticket_created_at) BETWEEN '$dtf' AND '$dtt'
AND (CONCAT(ticket_prefix,ticket_number) LIKE '%$q%' OR client_name LIKE '%$q%' OR ticket_subject LIKE '%$q%' OR ticket_status_name LIKE '%$q%' OR ticket_priority LIKE '%$q%' OR user_name LIKE '%$q%' OR contact_name LIKE '%$q%' OR asset_name LIKE '%$q%' OR vendor_name LIKE '%$q%')
$ticket_sla_query
$ticket_billable_snippet
$ticket_project_snippet
$access_permission_query_overide
$client_query
ORDER BY ticket_number ASC"
);
if ($sql->num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Tickets-" . date('Y-m-d_H-i-s') . ".csv");
$num_rows = mysqli_num_rows($sql);
//create a file pointer
$f = fopen('php://memory', 'w');
if ($num_rows > 0) {
//set column headers
$fields = array('Ticket Number', 'Priority', 'Status', 'Subject', 'Date Opened', 'Date Resolved', 'Date Closed');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
guardExportPdfRowCount($format, $num_rows);
//output each row of the data, format line as csv and write to file pointer
while ($row = $sql->fetch_assoc()) {
$lineData = array($config_ticket_prefix . $row['ticket_number'], $row['ticket_priority'], $row['ticket_status_name'], $row['ticket_subject'], $row['ticket_created_at'], $row['ticket_resolved_at'], $row['ticket_closed_at']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
$export = beginExport('tickets', $format, $file_name_prepend . 'Tickets', 'Tickets', summarizeExportFilters($filter_summary));
while ($row = mysqli_fetch_assoc($sql)) {
// Per-ticket prefix where the row carries one, config default otherwise
$row['ticket_number_display'] = ($row['ticket_prefix'] ?: $config_ticket_prefix) . $row['ticket_number'];
$row['ticket_category_name'] = $row['category_name'];
$row['ticket_assigned_to'] = $row['user_name'];
$row['ticket_billable'] = $row['ticket_billable'] ? 'Yes' : 'No';
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Ticket", "Export", "$session_name exported $num_rows ticket(s) to a " . strtoupper($format) . " file", $client_id);
exit;
}

View File

@@ -9,12 +9,17 @@ if (!defined('FROM_POST_HANDLER')) {
exit;
}
if (isset($_POST['export_transactions_csv'])) {
if (isset($_POST['export_transactions'])) {
validateCSRFToken();
enforceUserPermission('module_financial');
$format = resolveExportFormat($_POST['export_transactions']);
// Human-readable filter list for the PDF header
$filter_summary = [];
$date_from = escapeSql($_POST['date_from']);
$date_to = escapeSql($_POST['date_to']);
$account = intval($_POST['account']);
@@ -24,6 +29,7 @@ if (isset($_POST['export_transactions_csv'])) {
$transaction_types_array = ['Revenue', 'Payment', 'Expense', 'Transfer In', 'Transfer Out'];
if (!empty($_POST['type']) && in_array($_POST['type'], $transaction_types_array)) {
$type_query = "AND (transaction_type = '" . escapeSql($_POST['type']) . "')";
$filter_summary['Type'] = $_POST['type'];
} else {
// Default - any
$type_query = '';
@@ -32,6 +38,8 @@ if (isset($_POST['export_transactions_csv'])) {
// Category Filter
if ($category) {
$category_query = "AND (transaction_category_id = $category)";
$category_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT category_name FROM categories WHERE category_id = $category"));
$filter_summary['Category'] = $category_row['category_name'] ?? '';
} else {
// Default - any
$category_query = '';
@@ -41,6 +49,8 @@ if (isset($_POST['export_transactions_csv'])) {
$client = intval($_POST['client']);
if ($client) {
$client_query = "AND (transaction_client_id = $client)";
$client_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT client_name FROM clients WHERE client_id = $client"));
$filter_summary['Client'] = $client_row['client_name'] ?? '';
} else {
// Default - any
$client_query = '';
@@ -49,6 +59,7 @@ if (isset($_POST['export_transactions_csv'])) {
// Payment Method Filter
if (!empty($_POST['payment_method'])) {
$payment_method_query = "AND (transaction_payment_method = '" . escapeSql($_POST['payment_method']) . "')";
$filter_summary['Payment Method'] = $_POST['payment_method'];
} else {
// Default - any
$payment_method_query = '';
@@ -57,12 +68,14 @@ if (isset($_POST['export_transactions_csv'])) {
// Amount Range Filter - matched on absolute value so direction doesn't matter
if (isset($_POST['amount_min']) && $_POST['amount_min'] != '') {
$amount_min_query = 'AND (ABS(transaction_amount) >= ' . floatval($_POST['amount_min']) . ')';
$filter_summary['Amount from'] = floatval($_POST['amount_min']);
} else {
// Default - any
$amount_min_query = '';
}
if (isset($_POST['amount_max']) && $_POST['amount_max'] != '') {
$amount_max_query = 'AND (ABS(transaction_amount) <= ' . floatval($_POST['amount_max']) . ')';
$filter_summary['Amount to'] = floatval($_POST['amount_max']);
} else {
// Default - any
$amount_max_query = '';
@@ -71,6 +84,7 @@ if (isset($_POST['export_transactions_csv'])) {
// Search Filter - mirrors the transactions page search box
$q = escapeSql($_POST['q']);
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
$search_query = "AND (transaction_description LIKE '%$q%' OR transaction_category LIKE '%$q%' OR transaction_reference LIKE '%$q%' OR transaction_other_account LIKE '%$q%' OR transaction_amount LIKE '%$q%')";
} else {
// Default - any
@@ -80,6 +94,7 @@ if (isset($_POST['export_transactions_csv'])) {
// Date Filter
if (!empty($date_from) && !empty($date_to)) {
$date_query = "AND DATE(transaction_date) BETWEEN '$date_from' AND '$date_to'";
$filter_summary['Date'] = "$date_from to $date_to";
} else {
$date_query = '';
}
@@ -178,36 +193,19 @@ if (isset($_POST['export_transactions_csv'])) {
$num_rows = mysqli_num_rows($sql);
if ($num_rows > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename("$session_company_name-$account_name-Transactions-" . date('Y-m-d_H-i-s') . ".csv");
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Date', 'Type', 'Description', 'Transfer Account', 'Reference', 'Category', 'Payment Method', 'Amount', 'Balance');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('transactions', $format, "$session_company_name-$account_name-Transactions", "$account_name - Transactions", summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while ($row = mysqli_fetch_assoc($sql)) {
$lineData = array($row['transaction_date'], $row['transaction_type'], $row['transaction_description'], $row['transaction_other_account'], $row['transaction_reference'], $row['transaction_category'], $row['transaction_payment_method'], $row['transaction_amount'], $row['transaction_balance']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Transaction", "Export", "$session_name exported $num_rows transaction(s) to CSV file");
logAudit("Transaction", "Export", "$session_name exported $num_rows transaction(s) to a " . strtoupper($format) . " file");
}

View File

@@ -84,76 +84,76 @@ if (isset($_GET['delete_trip'])) {
}
if (isset($_POST['export_trips_csv'])) {
if (isset($_POST['export_trips'])) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_financial');
if ($_POST['client_id']) {
$format = resolveExportFormat($_POST['export_trips']);
// Filters inherited from the trips page - mirrors agent/trips.php
$filter_summary = [];
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "AND trip_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
} else {
$client_query = '';
$client_name = '';
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
}
$date_from = escapeSql($_POST['date_from']);
$date_to = escapeSql($_POST['date_to']);
if (!empty($date_from) && !empty($date_to)){
$date_query = "DATE(trip_date) BETWEEN '$date_from' AND '$date_to'";
$file_name_date = "$date_from-to-$date_to";
} else {
$date_query = "trip_date IS NOT NULL";
$file_name_date = date('Y-m-d');
// Date Filter
$dtf = escapeSql(!empty($_POST['dtf']) ? $_POST['dtf'] : '1970-01-01');
$dtt = escapeSql(!empty($_POST['dtt']) ? $_POST['dtt'] : '2099-12-31');
$date_range = formatExportDateRange($dtf, $dtt);
if ($date_range) {
$filter_summary['Dated'] = $date_range;
}
//get records from database
$sql = mysqli_query($mysqli,"SELECT * FROM trips
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
$sql = mysqli_query(
$mysqli,
"SELECT * FROM trips
LEFT JOIN clients ON trip_client_id = client_id
WHERE $date_query
LEFT JOIN users ON trip_user_id = user_id
WHERE (trip_purpose LIKE '%$q%' OR trip_source LIKE '%$q%' OR trip_destination LIKE '%$q%' OR trip_miles LIKE '%$q%' OR client_name LIKE '%$q%' OR user_name LIKE '%$q%')
AND DATE(trip_date) BETWEEN '$dtf' AND '$dtt'
AND trip_archived_at IS NULL
$client_query
$access_permission_query
ORDER BY trip_date DESC"
ORDER BY trip_date ASC"
);
$count = mysqli_num_rows($sql);
$num_rows = mysqli_num_rows($sql);
if ($count > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Trips-" . date('Y-m-d_H-i-s') . ".csv");
if ($num_rows > 0) {
//create a file pointer
$f = fopen('php://memory', 'w');
guardExportPdfRowCount($format, $num_rows);
//set column headers
$fields = array('Date', 'Purpose', 'Source', 'Destination', 'Miles');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
$export = beginExport('trips', $format, $file_name_prepend . 'Trips', 'Trips', summarizeExportFilters($filter_summary));
//output each row of the data, format line as csv and write to file pointer
while($row = mysqli_fetch_assoc($sql)){
$lineData = array($row['trip_date'], $row['trip_purpose'], $row['trip_source'], $row['trip_destination'], $row['trip_miles']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
while ($row = mysqli_fetch_assoc($sql)) {
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
logAudit("Trip", "Export", "$session_name exported $count trip(s) to a CSV file");
finishExport($export);
}
logAudit("Trip", "Export", "$session_name exported $num_rows trip(s) to a " . strtoupper($format) . " file", $client_id);
exit;
}

View File

@@ -350,59 +350,74 @@ if (isset($_POST['bulk_delete_vendors'])) {
}
if (isset($_POST['export_vendors_csv'])) {
if (isset($_POST['export_vendors'])) {
validateCSRFToken();
if ($_POST['client_id']) {
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_client');
$format = resolveExportFormat($_POST['export_vendors']);
// Filters inherited from the vendors page - mirrors agent/vendors.php
$filter_summary = [];
// Archived Filter
$archived = (isset($_POST['archived']) && $_POST['archived'] == 1);
if ($archived) {
$filter_summary['Archived'] = 'Archived only';
}
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "WHERE vendor_client_id = $client_id";
$client_query = "AND vendor_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
enforceUserPermission('module_client');
$filter_summary['Client'] = $client_name;
enforceClientAccess();
} else {
$client_query = "WHERE vendor_client_id = 0";
$client_name = '';
// Global vendors only, same as the vendors page
$client_query = "AND vendor_client_id = 0";
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
enforceUserPermission('module_financial');
}
$sql = mysqli_query($mysqli,"SELECT * FROM vendors LEFT JOIN clients ON client_id = vendor_client_id $client_query ORDER BY vendor_name ASC");
$archive_query = $archived ? "vendor_archived_at IS NOT NULL" : "vendor_archived_at IS NULL";
$count = mysqli_num_rows($sql);
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
if ($count > 0) {
$delimiter = ",";
$enclosure = '"';
$escape = '\\'; // backslash
$filename = sanitizeFilename($file_name_prepend . "Vendors-" . date('Y-m-d_H-i-s') . ".csv");
$sql = mysqli_query(
$mysqli,
"SELECT * FROM vendors
LEFT JOIN clients ON client_id = vendor_client_id
WHERE $archive_query
AND (vendor_name LIKE '%$q%' OR vendor_description LIKE '%$q%' OR vendor_account_number LIKE '%$q%' OR vendor_website LIKE '%$q%' OR vendor_contact_name LIKE '%$q%' OR vendor_email LIKE '%$q%' OR vendor_phone LIKE '%$q%')
$client_query
$access_permission_query
ORDER BY vendor_name ASC"
);
//create a file pointer
$f = fopen('php://memory', 'w');
$num_rows = mysqli_num_rows($sql);
//set column headers
$fields = array('Name', 'Description', 'Contact Name', 'Phone', 'Website', 'Account Number', 'Notes');
fputcsv($f, $fields, $delimiter, $enclosure, $escape);
if ($num_rows > 0) {
//output each row of the data, format line as csv and write to file pointer
while($row = $sql->fetch_assoc()) {
$lineData = array($row['vendor_name'], $row['vendor_description'], $row['vendor_contact_name'], $row['vendor_phone'], $row['vendor_website'], $row['vendor_account_number'], $row['vendor_notes']);
fputcsv($f, array_map('escapeCsvFormula', $lineData), $delimiter, $enclosure, $escape);
guardExportPdfRowCount($format, $num_rows);
$export = beginExport('vendors', $format, $file_name_prepend . 'Vendors', 'Vendors', summarizeExportFilters($filter_summary));
while ($row = mysqli_fetch_assoc($sql)) {
addExportRow($export, $row);
}
//move back to beginning of file
fseek($f, 0);
//set headers to download file rather than displayed
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '";');
//output all remaining data on a file pointer
fpassthru($f);
finishExport($export);
}
logAudit("Vendor", "Export", "$session_name exported $count vendor(s) to a CSV file");
logAudit("Vendor", "Export", "$session_name exported $num_rows vendor(s) to a " . strtoupper($format) . " file", $client_id);
exit;

View File

@@ -70,7 +70,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button>
<div class="dropdown-menu">
<a class="dropdown-item text-dark ajax-modal"
data-modal-url="modals/product/product_export.php">
data-modal-url="<?= buildExportModalUrl('modals/product/product_export.php', ['type', 'category', 'archived', 'q']) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
</div>

View File

@@ -45,7 +45,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button>
<div class="dropdown-menu">
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/quote/quote_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/quote/quote_export.php', ['client_id', 'q'], ['dtf' => $dtf, 'dtt' => $dtt]) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
</div>

View File

@@ -50,7 +50,18 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<h3 class="card-title mt-2"><i class="fa fa-redo-alt mr-2"></i>Recurring Invoices</h3>
<?php if (lookupUserPermission("module_sales") >= 2) { ?>
<div class="card-tools">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/recurring_invoice/recurring_invoice_add.php?<?= $client_url ?>"><i class="fas fa-plus"></i><span class="d-none d-lg-inline ml-2">New Recurring Invoice</span></button>
<div class="btn-group">
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/recurring_invoice/recurring_invoice_add.php?<?= $client_url ?>"><i class="fas fa-plus"></i><span class="d-none d-lg-inline ml-2">New Recurring Invoice</span></button>
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button>
<div class="dropdown-menu">
<?php if ($num_rows[0] > 0) { ?>
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="<?= buildExportModalUrl('modals/recurring_invoice/recurring_invoice_export.php', ['client_id', 'status', 'q'], ['dtf' => $dtf, 'dtt' => $dtt]) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
<?php } ?>
</div>
</div>
</div>
<?php } ?>
</div>

View File

@@ -94,7 +94,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<?php if ($num_rows[0] > 0) { ?>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/software/software_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/software/software_export.php', ['client_id', 'client', 'expire_days', 'archived', 'q']) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
<?php } ?>

View File

@@ -203,7 +203,7 @@ $sql_categories_filter = mysqli_query(
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button>
<div class="dropdown-menu">
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/ticket/ticket_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/ticket/ticket_export.php', ['client_id', 'status', 'billable', 'unbilled', 'category', 'assigned', 'sla', 'project', 'q'], ['dtf' => $dtf, 'dtt' => $dtt]) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
</div>

View File

@@ -42,7 +42,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button>
<div class="dropdown-menu">
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/trip/trip_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/trip/trip_export.php', ['client_id', 'q'], ['dtf' => $dtf, 'dtt' => $dtt]) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
</div>

View File

@@ -50,7 +50,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
<?php if ($num_rows[0] > 0) { ?>
<div class="dropdown-divider"></div>
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="modals/vendor/vendor_export.php?<?= $client_url ?>">
data-modal-url="<?= buildExportModalUrl('modals/vendor/vendor_export.php', ['client_id', 'archived', 'q']) ?>">
<i class="fa fa-fw fa-download mr-2"></i>Export
</a>
<?php } ?>

View File

@@ -23,3 +23,4 @@ require_once __DIR__ . '/functions/app.php';
require_once __DIR__ . '/functions/db.php';
require_once __DIR__ . '/functions/payments.php';
require_once __DIR__ . '/functions/sla.php';
require_once __DIR__ . '/functions/export.php';

799
functions/export.php Normal file
View File

@@ -0,0 +1,799 @@
<?php
/*
* ITFlow - Export helpers
*
* Shared plumbing behind the list page export modals:
* - a per-export column registry (label, source field, formatting, default state)
* - the column picker that every export modal renders
* - the CSV and PDF writers
*
* Handlers keep owning their own filter parsing and SQL. They hand rows over one
* at a time, in the same loop the old fputcsv() call sat in:
*
* $export = beginExport('assets', $_POST['export_assets'], "$file_name_prepend" . 'Assets', 'Assets', $filter_summary);
* while ($row = mysqli_fetch_assoc($sql)) {
* addExportRow($export, $row);
* }
* finishExport($export);
*
* A column's 'field' is just a key on the row array handed to addExportRow(), so a
* handler that needs a computed value (a prefixed number, a decrypted secret, a
* status word) sets that key on $row before the call - see getExportColumns() notes.
*/
// PDF is a read-and-print format, not a bulk transport. Past this many rows TCPDF's
// HTML table builder gets slow enough to walk into max_execution_time, so the
// handlers bounce the request back to CSV instead of timing out mid-download.
DEFINE("EXPORT_PDF_MAX_ROWS", 2000);
/*
* The column registry - one entry per export, in the order columns should appear.
*
* 'label' Column heading in the CSV / PDF.
* 'field' Key on the row array. Defaults to the column key itself.
* 'format' '' (raw), 'phone', 'money', 'number'. Only affects presentation.
* 'default' false to leave the box unticked when the modal opens. Defaults true.
* 'weight' Relative PDF column width, default 1. Bump it for long free text.
*
* Column keys are what the modal posts back, so they are whitelisted against this
* registry on the way in - an unknown key is dropped, not queried.
*/
function getExportColumns($export_type) {
$registry = [
// Clients - handler joins the primary contact and primary location
'clients' => [
'client_name' => ['label' => 'Client Name', 'weight' => 2],
'client_type' => ['label' => 'Industry'],
'client_referral' => ['label' => 'Referral'],
'client_website' => ['label' => 'Website', 'weight' => 2],
'location_name' => ['label' => 'Primary Location Name'],
'location_phone' => ['label' => 'Location Phone', 'format' => 'phone'],
'location_address' => ['label' => 'Location Address', 'weight' => 2],
'location_city' => ['label' => 'City'],
'location_state' => ['label' => 'State'],
'location_zip' => ['label' => 'Postal Code'],
'location_country' => ['label' => 'Country'],
'contact_name' => ['label' => 'Primary Contact Name'],
'contact_title' => ['label' => 'Title'],
'contact_phone' => ['label' => 'Contact Phone', 'format' => 'phone'],
'contact_extension' => ['label' => 'Extension'],
'contact_mobile' => ['label' => 'Contact Mobile', 'format' => 'phone'],
'contact_email' => ['label' => 'Contact Email', 'weight' => 2],
'client_rate' => ['label' => 'Hourly Rate', 'format' => 'money'],
'client_currency_code' => ['label' => 'Currency'],
'client_net_terms' => ['label' => 'Payment Terms', 'format' => 'number'],
'client_tax_id_number' => ['label' => 'Tax ID'],
'client_abbreviation' => ['label' => 'Abbreviation'],
],
'contacts' => [
'contact_name' => ['label' => 'Name', 'weight' => 2],
'contact_title' => ['label' => 'Title'],
'contact_department' => ['label' => 'Department'],
'contact_email' => ['label' => 'Email', 'weight' => 2],
'contact_phone' => ['label' => 'Phone', 'format' => 'phone'],
'contact_extension' => ['label' => 'Ext'],
'contact_mobile' => ['label' => 'Mobile', 'format' => 'phone'],
'location_name' => ['label' => 'Location'],
],
'locations' => [
'location_name' => ['label' => 'Name', 'weight' => 2],
'location_description' => ['label' => 'Description', 'weight' => 3],
'location_address' => ['label' => 'Address', 'weight' => 2],
'location_city' => ['label' => 'City'],
'location_state' => ['label' => 'State'],
'location_zip' => ['label' => 'Postal Code'],
'location_phone' => ['label' => 'Phone', 'format' => 'phone'],
'location_hours' => ['label' => 'Hours'],
],
'vendors' => [
'vendor_name' => ['label' => 'Name', 'weight' => 2],
'vendor_description' => ['label' => 'Description', 'weight' => 3],
'vendor_contact_name' => ['label' => 'Contact Name'],
'vendor_phone' => ['label' => 'Phone', 'format' => 'phone'],
'vendor_website' => ['label' => 'Website', 'weight' => 2],
'vendor_account_number' => ['label' => 'Account Number'],
'vendor_notes' => ['label' => 'Notes', 'weight' => 3],
],
'assets' => [
'asset_name' => ['label' => 'Name', 'weight' => 2],
'asset_description' => ['label' => 'Description', 'weight' => 3],
'asset_type' => ['label' => 'Type'],
'asset_make' => ['label' => 'Make'],
'asset_model' => ['label' => 'Model'],
'asset_serial' => ['label' => 'Serial Number'],
'asset_os' => ['label' => 'Operating System', 'weight' => 2],
'asset_purchase_date' => ['label' => 'Purchase Date'],
'asset_warranty_expire' => ['label' => 'Warranty Expire'],
'asset_install_date' => ['label' => 'Install Date'],
'contact_name' => ['label' => 'Assigned To'],
'location_name' => ['label' => 'Location'],
'asset_physical_location' => ['label' => 'Physical Location'],
'asset_notes' => ['label' => 'Notes', 'weight' => 3],
// Available from the handler's join but not exported historically
'client_name' => ['label' => 'Client', 'default' => false],
'interface_ip' => ['label' => 'Primary IP', 'default' => false],
'interface_mac' => ['label' => 'Primary MAC', 'default' => false],
],
'asset_interfaces' => [
'interface_name' => ['label' => 'Name'],
'interface_description' => ['label' => 'Description', 'weight' => 3],
'interface_type' => ['label' => 'Type'],
'interface_mac' => ['label' => 'MAC'],
'interface_ip' => ['label' => 'IP'],
'interface_nat_ip' => ['label' => 'NAT IP'],
'interface_ipv6' => ['label' => 'IPv6', 'weight' => 2],
'network_name' => ['label' => 'Network'],
],
'networks' => [
'network_name' => ['label' => 'Name', 'weight' => 2],
'network_description' => ['label' => 'Description', 'weight' => 3],
'network_vlan' => ['label' => 'VLAN'],
'network' => ['label' => 'Network (CIDR)'],
'network_gateway' => ['label' => 'Gateway'],
'network_dhcp_range' => ['label' => 'IP Range'],
'network_primary_dns' => ['label' => 'Primary DNS'],
'network_secondary_dns' => ['label' => 'Secondary DNS'],
],
'certificates' => [
'certificate_name' => ['label' => 'Name', 'weight' => 2],
'certificate_description' => ['label' => 'Description', 'weight' => 3],
'certificate_domain' => ['label' => 'Domain', 'weight' => 2],
'certificate_issued_by' => ['label' => 'Issuer', 'weight' => 2],
'certificate_expire' => ['label' => 'Expiration Date'],
],
'domains' => [
'domain_name' => ['label' => 'Domain', 'weight' => 2],
'domain_description' => ['label' => 'Description', 'weight' => 3],
// The columns hold vendor IDs; the handler aliases the joined names
'domain_registrar' => ['label' => 'Registrar', 'field' => 'domain_registrar_name'],
'domain_webhost' => ['label' => 'Web Host', 'field' => 'domain_webhost_name'],
'domain_expire' => ['label' => 'Expiration Date'],
],
// Defaults match what this export has always emitted, secrets included.
// The handler decrypts into credential_username / credential_password.
'credentials' => [
'credential_name' => ['label' => 'Name', 'weight' => 2],
'credential_description' => ['label' => 'Description', 'weight' => 3],
'credential_username' => ['label' => 'Username'],
'credential_password' => ['label' => 'Password'],
'credential_otp_secret' => ['label' => 'TOTP'],
'credential_uri' => ['label' => 'URI', 'weight' => 2],
],
// assigned_to_assets / assigned_to_contacts are built by the handler
'software' => [
'software_name' => ['label' => 'Name', 'weight' => 2],
'software_version' => ['label' => 'Version'],
'software_description' => ['label' => 'Description', 'weight' => 3],
'software_type' => ['label' => 'Type'],
'software_license_type' => ['label' => 'License Type'],
'software_seats' => ['label' => 'Seats', 'format' => 'number'],
'software_key' => ['label' => 'Key', 'weight' => 2],
'assigned_to_assets' => ['label' => 'Assets', 'weight' => 2],
'assigned_to_contacts' => ['label' => 'Contacts', 'weight' => 2],
'software_purchase' => ['label' => 'Purchased'],
'software_expire' => ['label' => 'Expires'],
'software_notes' => ['label' => 'Notes', 'weight' => 3],
],
// ticket_number_display is prefixed by the handler
'tickets' => [
'ticket_number_display' => ['label' => 'Ticket Number'],
'ticket_priority' => ['label' => 'Priority'],
'ticket_status_name' => ['label' => 'Status'],
'ticket_subject' => ['label' => 'Subject', 'weight' => 3],
'ticket_created_at' => ['label' => 'Date Opened'],
'ticket_resolved_at' => ['label' => 'Date Resolved'],
'ticket_closed_at' => ['label' => 'Date Closed'],
'client_name' => ['label' => 'Client', 'default' => false],
'contact_name' => ['label' => 'Contact', 'default' => false],
'ticket_assigned_to' => ['label' => 'Assigned To', 'default' => false],
'ticket_category_name' => ['label' => 'Category', 'default' => false],
'ticket_billable' => ['label' => 'Billable', 'default' => false],
],
// invoice_number_display is prefix . number, built by the handler
'invoices' => [
'invoice_number_display' => ['label' => 'Invoice Number'],
'invoice_scope' => ['label' => 'Scope', 'weight' => 3],
'invoice_amount' => ['label' => 'Amount', 'format' => 'money'],
'invoice_date' => ['label' => 'Issued Date'],
'invoice_due' => ['label' => 'Due Date'],
'invoice_status' => ['label' => 'Status'],
'client_name' => ['label' => 'Client', 'weight' => 2],
'invoice_currency_code' => ['label' => 'Currency', 'default' => false],
'amount_paid' => ['label' => 'Paid', 'format' => 'money', 'default' => false],
'invoice_balance' => ['label' => 'Balance', 'format' => 'money', 'default' => false],
],
'quotes' => [
'quote_number_display' => ['label' => 'Quote Number'],
'quote_scope' => ['label' => 'Scope', 'weight' => 3],
'quote_amount' => ['label' => 'Amount', 'format' => 'money'],
'quote_date' => ['label' => 'Date'],
'quote_status' => ['label' => 'Status'],
'client_name' => ['label' => 'Client', 'weight' => 2, 'default' => false],
],
'recurring_invoices' => [
'recurring_invoice_number_display' => ['label' => 'Recurring Number'],
'recurring_invoice_scope' => ['label' => 'Scope', 'weight' => 3],
'recurring_invoice_amount' => ['label' => 'Amount', 'format' => 'money'],
'recurring_invoice_frequency_display' => ['label' => 'Frequency'],
'recurring_invoice_created_at' => ['label' => 'Date Created'],
'client_name' => ['label' => 'Client', 'weight' => 2, 'default' => false],
],
'products' => [
'product_name' => ['label' => 'Product', 'weight' => 2],
'product_description' => ['label' => 'Description', 'weight' => 3],
'product_price' => ['label' => 'Price', 'format' => 'money'],
'product_currency_code' => ['label' => 'Currency'],
'category_name' => ['label' => 'Category'],
'tax_name' => ['label' => 'Tax'],
],
'expenses' => [
'expense_date' => ['label' => 'Date'],
'expense_amount' => ['label' => 'Amount', 'format' => 'money'],
'vendor_name' => ['label' => 'Vendor', 'weight' => 2],
'expense_description' => ['label' => 'Description', 'weight' => 3],
'category_name' => ['label' => 'Category'],
'account_name' => ['label' => 'Account'],
'client_name' => ['label' => 'Client', 'weight' => 2, 'default' => false],
'expense_reference' => ['label' => 'Reference', 'default' => false],
],
'income' => [
'income_date' => ['label' => 'Date'],
'income_type' => ['label' => 'Type'],
'income_source' => ['label' => 'Source'],
'income_description' => ['label' => 'Description', 'weight' => 3],
'income_client' => ['label' => 'Client', 'weight' => 2],
'income_amount' => ['label' => 'Amount', 'format' => 'money'],
'income_currency_code' => ['label' => 'Currency'],
'income_method' => ['label' => 'Payment Method'],
'income_reference' => ['label' => 'Reference'],
'income_account' => ['label' => 'Account'],
],
'transactions' => [
'transaction_date' => ['label' => 'Date'],
'transaction_type' => ['label' => 'Type'],
'transaction_description' => ['label' => 'Description', 'weight' => 3],
'transaction_other_account' => ['label' => 'Transfer Account'],
'transaction_reference' => ['label' => 'Reference'],
'transaction_category' => ['label' => 'Category'],
'transaction_payment_method' => ['label' => 'Payment Method'],
'transaction_amount' => ['label' => 'Amount', 'format' => 'money'],
'transaction_balance' => ['label' => 'Balance', 'format' => 'money'],
],
'trips' => [
'trip_date' => ['label' => 'Date'],
'trip_purpose' => ['label' => 'Purpose', 'weight' => 3],
'trip_source' => ['label' => 'Source', 'weight' => 2],
'trip_destination' => ['label' => 'Destination', 'weight' => 2],
'trip_miles' => ['label' => 'Miles', 'format' => 'number'],
'client_name' => ['label' => 'Client', 'weight' => 2, 'default' => false],
],
// user_status_display is the status word, built by the handler
'users' => [
'user_name' => ['label' => 'Name', 'weight' => 2],
'user_email' => ['label' => 'Email', 'weight' => 2],
'role_name' => ['label' => 'Role'],
'user_status_display' => ['label' => 'Status'],
'user_created_at' => ['label' => 'Creation Date'],
],
];
return $registry[$export_type] ?? [];
}
/*
* Which columns the modal ticked, whitelisted against the registry.
* Registry order always wins, so the file layout is stable no matter what order
* the checkboxes came back in. Nothing ticked (or no picker on the form) falls
* back to the export's defaults.
*/
function resolveExportColumns($export_type) {
$available = getExportColumns($export_type);
if (empty($available)) {
return [];
}
$requested = $_POST['columns'] ?? [];
if (!is_array($requested)) {
$requested = [];
}
$selected = [];
foreach ($available as $key => $column) {
if (in_array($key, $requested, true)) {
$selected[$key] = $column;
}
}
if (empty($selected)) {
foreach ($available as $key => $column) {
if ($column['default'] ?? true) {
$selected[$key] = $column;
}
}
}
return $selected;
}
/*
* The export buttons post their format as the value of the trigger, so
* $_POST['export_assets'] is the string 'csv' or 'pdf'. Anything else is CSV.
*/
function resolveExportFormat($format) {
return ($format === 'pdf') ? 'pdf' : 'csv';
}
/*
* PDF is capped - see EXPORT_PDF_MAX_ROWS. Call this after the row count is known
* and before beginExport(); it redirects rather than returning on refusal.
*/
function guardExportPdfRowCount($format, $num_rows) {
if (resolveExportFormat($format) === 'pdf' && $num_rows > EXPORT_PDF_MAX_ROWS) {
flashAlert("That's " . number_format($num_rows) . " rows - too many for a PDF. Narrow the filters or export to CSV instead.", 'error');
redirect();
}
}
/*
* Presentation only. CSV keeps numbers raw so spreadsheets and importers still
* see a number; the PDF is for reading, so it gets thousands separators.
*/
function formatExportValue($value, $format, $output) {
// An empty field is empty, whatever its format - a blank amount is not 0.00
if ($value === null || $value === '') {
return '';
}
if ($format === 'phone') {
return formatPhoneNumber($value);
}
if ($output === 'pdf' && $format === 'money') {
return number_format(floatval($value), 2);
}
if ($output === 'pdf' && $format === 'number') {
return rtrim(rtrim(number_format(floatval($value), 2), '0'), '.');
}
return $value;
}
/*
* Renders the column picker into an export modal. Drop it in the modal body:
*
* <?php renderExportColumnPicker('assets'); ?>
*
* Posts back as columns[]. Silently renders nothing for an export type that has
* no registry entry yet, so a half-converted modal still works.
*/
function renderExportColumnPicker($export_type) {
$available = getExportColumns($export_type);
if (empty($available)) {
return;
}
// Modals get appended to the DOM, so the container id has to be unique per instance
static $instance = 0;
$instance++;
$picker_id = 'exportColumns' . $instance;
?>
<div class="form-group">
<label class="d-flex justify-content-between align-items-center">
<span>Columns</span>
<span>
<button type="button" class="btn btn-link btn-sm p-0 mr-2 export-columns-all">Select all</button>
<button type="button" class="btn btn-link btn-sm p-0 export-columns-none">None</button>
</span>
</label>
<div id="<?= $picker_id ?>" class="export-column-picker border rounded p-2" style="max-height: 220px; overflow-y: auto;">
<div class="row">
<?php foreach ($available as $column_key => $column) { ?>
<div class="col-md-6">
<label class="d-block mb-1 font-weight-normal text-truncate" title="<?= escapeHtml($column['label']) ?>">
<input type="checkbox" name="columns[]" value="<?= $column_key ?>" <?php if ($column['default'] ?? true) { echo 'checked'; } ?>>
<?= escapeHtml($column['label']) ?>
</label>
</div>
<?php } ?>
</div>
</div>
<small class="form-text text-muted export-columns-count"></small>
</div>
<script>
(function () {
var picker = document.getElementById('<?= $picker_id ?>');
if (!picker) {
return;
}
// Everything is looked up relative to the picker's own form group, so several
// pickers on one page - or several modals in a session - never cross wires
var group = picker.parentNode;
var boxes = picker.querySelectorAll('input[name="columns[]"]');
var counter = group.querySelector('.export-columns-count');
var form = picker.closest('form');
function setAll(state) {
for (var i = 0; i < boxes.length; i++) {
boxes[i].checked = state;
}
update();
}
function update() {
var checked = picker.querySelectorAll('input[name="columns[]"]:checked').length;
counter.classList.remove('text-danger');
counter.textContent = checked + ' of ' + boxes.length + ' columns selected'
+ (checked > 10 ? ' - a lot for a PDF, CSV will read better' : '');
}
picker.addEventListener('change', update);
group.querySelector('.export-columns-all').addEventListener('click', function () { setAll(true); });
group.querySelector('.export-columns-none').addEventListener('click', function () { setAll(false); });
// An empty selection would silently fall back to the defaults server side -
// stop it here so the choice stays the user's
if (form) {
form.addEventListener('submit', function (e) {
if (picker.querySelectorAll('input[name="columns[]"]:checked').length === 0) {
e.preventDefault();
counter.textContent = 'Select at least one column to export';
counter.classList.add('text-danger');
}
});
}
update();
})();
</script>
<?php
}
/*
* Builds the export modal's data-modal-url with the page's current filters attached,
* so the list page doesn't need a hand-rolled query string per export button:
*
* data-modal-url="<?= buildExportModalUrl('modals/asset/asset_export.php', ['client_id', 'type', 'q']) ?>"
*
* Empty params are dropped and array params (tags[], status[]) survive. The return
* value is escaped for use in an HTML attribute.
*/
function buildExportModalUrl($modal_path, $names, $extra = []) {
$params = [];
foreach ($names as $name) {
if (!isset($_GET[$name]) || $_GET[$name] === '' || $_GET[$name] === []) {
continue;
}
$params[$name] = $_GET[$name];
}
// Filters the page derived rather than read straight off the query string -
// filter_header.php's canned date ranges being the main one
foreach ($extra as $name => $value) {
if ($value !== '' && $value !== null && $value !== []) {
$params[$name] = $value;
}
}
if (empty($params)) {
return escapeHtml($modal_path);
}
return escapeHtml($modal_path . '?' . http_build_query($params));
}
/*
* filter_header.php falls back to 1970-01-01 / 2099-12-31 when no date range is
* chosen, so a naive summary would claim a filter that isn't really there.
* Returns '' for the all-time case, otherwise a readable range.
*/
function formatExportDateRange($date_from, $date_to) {
$all_time_from = ($date_from === '' || $date_from === null || $date_from === '1970-01-01' || $date_from === '0000-00-00');
$all_time_to = ($date_to === '' || $date_to === null || $date_to === '2099-12-31' || $date_to === '9999-00-00');
if ($all_time_from && $all_time_to) {
return '';
}
return $date_from . ' to ' . $date_to;
}
/*
* Human-readable one-liner of the same thing, for the PDF subtitle.
*/
function summarizeExportFilters($filters) {
$parts = [];
foreach ($filters as $label => $value) {
if ($value !== '' && $value !== null) {
$parts[] = $label . ': ' . $value;
}
}
return empty($parts) ? '' : 'Filters - ' . implode(' | ', $parts);
}
/*
* The tabbed shell every export modal uses - Filters on the first pane, Columns on the
* second, same pill nav as the add-client modal. Ids are per-instance because modals are
* appended to the DOM rather than living in the page.
*
* Sits between the modal header and the form:
*
* <?php exportTabsNav(); ?>
* <form ...>
* <div class="modal-body">
* <?php exportTabsFiltersOpen(); ?>
* ...filter controls...
* <?php exportTabsColumns('assets'); ?>
* </div>
*
* exportTabsColumns() closes the filters pane, renders the picker in the second pane and
* closes the tab content, so the modal body needs nothing else.
*/
function exportTabsId() {
static $instance = 0;
static $current = '';
if (func_num_args() > 0 && func_get_arg(0) === 'next') {
$instance++;
$current = 'exportTab' . $instance;
}
return $current;
}
function exportTabsNav($filters_label = 'Filters') {
$id = exportTabsId('next');
?>
<ul class="modal-header nav nav-pills nav-justified">
<li class="nav-item">
<a class="nav-link active" data-toggle="pill" href="#<?= $id ?>-filters"><?= escapeHtml($filters_label) ?></a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="pill" href="#<?= $id ?>-columns">Columns</a>
</li>
</ul>
<?php
}
function exportTabsFiltersOpen() {
$id = exportTabsId();
?>
<div class="tab-content">
<div class="tab-pane fade show active" id="<?= $id ?>-filters">
<?php
}
function exportTabsColumns($export_type) {
$id = exportTabsId();
?>
</div>
<div class="tab-pane fade" id="<?= $id ?>-columns">
<?php renderExportColumnPicker($export_type); ?>
</div>
</div>
<?php
}
/*
* Renders the CSV / PDF submit pair for an export modal footer.
* $trigger is the POST key the handler keys on, e.g. 'export_assets'.
*/
function renderExportButtons($trigger) {
?>
<button type="submit" name="<?= $trigger ?>" value="csv" class="btn btn-primary text-bold"><i class="fas fa-fw fa-file-csv mr-2"></i>Download CSV</button>
<button type="submit" name="<?= $trigger ?>" value="pdf" class="btn btn-secondary text-bold"><i class="fas fa-fw fa-file-pdf mr-2"></i>Download PDF</button>
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
<?php
}
/*
* Opens an export. $filename_base is everything before the timestamp, e.g.
* "Acme Inc-Assets"; the date and extension are appended here.
* $subtitle is the human summary of the filters in play - it prints under the
* title in the PDF and is ignored for CSV.
*/
function beginExport($export_type, $format, $filename_base, $title = '', $subtitle = '') {
$format = resolveExportFormat($format);
$columns = resolveExportColumns($export_type);
$export = [
'format' => $format,
'columns' => $columns,
'title' => $title,
'subtitle' => $subtitle,
'filename' => sanitizeFilename($filename_base . '-' . date('Y-m-d_H-i-s') . '.' . $format),
'rows' => 0,
'fp' => null,
'body' => '',
'widths' => [],
'missing' => [],
];
if ($format === 'csv') {
$export['fp'] = fopen('php://memory', 'w');
fputcsv($export['fp'], array_column($columns, 'label'), ',', '"', '\\');
} else {
// TCPDF sizes a table from the cells it sees, so every row needs the widths -
// putting them on the header alone leaves the body offset from its headings
$total_weight = 0;
foreach ($columns as $column) {
$total_weight += $column['weight'] ?? 1;
}
foreach ($columns as $column_key => $column) {
$export['widths'][$column_key] = round((($column['weight'] ?? 1) / $total_weight) * 100, 2);
}
}
return $export;
}
/*
* One row. $row is a plain associative array - normally straight out of
* mysqli_fetch_assoc(), with any handler-computed keys already set on it.
*/
function addExportRow(&$export, $row) {
$export['rows']++;
$values = [];
foreach ($export['columns'] as $column_key => $column) {
$field = $column['field'] ?? $column_key;
// A selected column whose field the query never returned would otherwise be a
// silently blank column all the way down - note it so finishExport can shout
if (!array_key_exists($field, $row)) {
$export['missing'][$field] = true;
}
$values[$column_key] = formatExportValue($row[$field] ?? '', $column['format'] ?? '', $export['format']);
}
if ($export['format'] === 'csv') {
fputcsv($export['fp'], array_map('escapeCsvFormula', $values), ',', '"', '\\');
return;
}
// PDF - buffer the row, the document is assembled in finishExport()
$stripe = ($export['rows'] % 2 === 0) ? ' bgcolor="#f4f4f4"' : '';
$cells = '';
foreach ($export['columns'] as $column_key => $column) {
$align = in_array($column['format'] ?? '', ['money', 'number'], true) ? 'right' : 'left';
$width = $export['widths'][$column_key] ?? 0;
// Blank cells read as a mistake in a printed table - an explicit dash doesn't
$cell = ($values[$column_key] === '') ? '-' : escapeHtml($values[$column_key]);
$cells .= '<td width="' . $width . '%" align="' . $align . '">' . $cell . '</td>';
}
$export['body'] .= '<tr' . $stripe . '>' . $cells . '</tr>';
}
/*
* A column whose field the query never selected produces a blank column rather than an
* error, which is exactly the kind of thing that ships unnoticed. Nothing is shown to
* the user - the file is still valid - but it lands in the PHP error log where a
* conversion mistake will be spotted.
*/
function reportMissingExportFields(&$export) {
if (!empty($export['missing'])) {
error_log('ITFlow export: no such field in result set for column(s): ' . implode(', ', array_keys($export['missing'])));
}
}
/*
* Streams the finished file. Sends its own headers, so nothing may be echoed
* before it; the caller still owns the logAudit() call and the exit.
*/
function finishExport(&$export) {
global $session_company_name;
reportMissingExportFields($export);
if ($export['format'] === 'csv') {
fseek($export['fp'], 0);
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $export['filename'] . '";');
fpassthru($export['fp']);
fclose($export['fp']);
return $export['rows'];
}
require_once __DIR__ . '/../libs/TCPDF/tcpdf.php';
$column_count = count($export['columns']);
// Anything past a handful of columns needs the long edge
$orientation = $column_count > 5 ? 'L' : 'P';
// Shrink type as columns pile up rather than letting TCPDF wrap every cell
if ($column_count <= 6) {
$font_size = 8;
} elseif ($column_count <= 9) {
$font_size = 7;
} elseif ($column_count <= 12) {
$font_size = 6;
} else {
$font_size = 5;
}
$pdf = new TCPDF($orientation, 'mm', 'A4', true, 'UTF-8', false);
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor($session_company_name);
$pdf->SetTitle($export['title']);
$pdf->SetPrintHeader(false);
$pdf->SetMargins(8, 8, 8);
$pdf->SetAutoPageBreak(true, 12);
$pdf->AddPage();
$pdf->SetFont('freeserif', '', $font_size);
// Same widths the body rows used - see beginExport()
$head = '';
foreach ($export['columns'] as $column_key => $column) {
$width = $export['widths'][$column_key] ?? 0;
$head .= '<th width="' . $width . '%">' . escapeHtml($column['label']) . '</th>';
}
$html = '
<style>
h1 { font-size: ' . ($font_size + 6) . 'pt; margin: 0; }
p.meta { font-size: ' . $font_size . 'pt; color: #555555; margin: 2px 0 0 0; }
table { width: 100%; border-collapse: collapse; }
table, th, td { border: 0.5px solid #999999; }
th { background-color: #343a40; color: #ffffff; text-align: left; font-weight: bold; padding: 3px; }
td { padding: 3px; }
thead { display: table-header-group; }
</style>
<h1>' . escapeHtml($export['title']) . '</h1>
<p class="meta">' . escapeHtml($session_company_name) . '</p>';
if (!empty($export['subtitle'])) {
$html .= '<p class="meta">' . escapeHtml($export['subtitle']) . '</p>';
}
$html .= '<p class="meta">' . $export['rows'] . ' record(s) - generated ' . date('Y-m-d H:i') . '</p><br>
<table cellspacing="0" cellpadding="2">
<thead><tr>' . $head . '</tr></thead>
<tbody>' . $export['body'] . '</tbody>
</table>';
$pdf->writeHTML($html, true, false, true, false, '');
$pdf->Output($export['filename'], 'D');
return $export['rows'];
}