mirror of
https://github.com/itflow-org/itflow
synced 2026-11-11 23:17:23 +00:00
Assets makeover: list with type icons, coloured status pills, OS icons, copy buttons on serial and IP, connect chips by scheme (Web/SSH/RDP), expiring and expired warranty flags, rack position, type tabs with counts, tag search and more sortable columns; asset page with a header (photo, status, tags, connect buttons, New / Link / Edit / Copy / Archive), Network / Hardware / Where & who / Quick notes cards, vendor and physical location shown, and related records in counted tabs that remember the last one; one sectioned Add/Edit/Copy form with a DHCP toggle, photo removal and a History tab; quick look gets the status pill, copy buttons and warranty in words. Fixes OS/firmware and purchase/warranty fields wiped on edit for some asset types, location/contact/vendor/network picks now limited to the asset's own client, credential audit entry missing the asset name, warning when replacing a missing photo, add form script broken with no OS values, recurring ticket menu markup, dead Files view toggle, per-row and per-license queries, error on unknown sort column, quick-look Credentials tab shown without permission, ticket queries trimmed to the columns used, edit/delete actions gated consistently. Adds functions/asset.php. No DB change
This commit is contained in:
2418
agent/asset.php
2418
agent/asset.php
File diff suppressed because it is too large
Load Diff
808
agent/assets.php
808
agent/assets.php
File diff suppressed because it is too large
Load Diff
@@ -286,7 +286,7 @@ if (isset($_GET['contact_id'])) {
|
||||
<i class="fa fa-fw fa-recycle me-2"></i>New Recurring Ticket
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-dark ajax-modal" href="#" data-modal-url="modals/asset/asset_add.php?<?= $client_url ?>&contact_id=<?= $contact_id ?>">
|
||||
<a class="dropdown-item text-dark ajax-modal" href="#" data-modal-size="lg" data-modal-url="modals/asset/asset_add.php?<?= $client_url ?>&contact_id=<?= $contact_id ?>">
|
||||
<i class="fa fa-fw fa-desktop me-2"></i>New Asset
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
@@ -457,11 +457,11 @@ if (isset($_GET['contact_id'])) {
|
||||
<button class="btn btn-secondary btn-sm" title="More actions" type="button" data-bs-toggle="dropdown"><i class="fas fa-ellipsis-h"></i></button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item ajax-modal" href="#"
|
||||
data-modal-url="modals/asset/asset_edit.php?id=<?= $asset_id ?>">
|
||||
data-modal-size="lg" data-modal-url="modals/asset/asset_edit.php?id=<?= $asset_id ?>">
|
||||
<i class="fas fa-fw fa-edit me-2"></i>Edit
|
||||
</a>
|
||||
<a class="dropdown-item ajax-modal" href="#"
|
||||
data-modal-url="modals/asset/asset_copy.php?id=<?= $asset_id ?>">
|
||||
data-modal-size="lg" data-modal-url="modals/asset/asset_copy.php?id=<?= $asset_id ?>">
|
||||
<i class="fas fa-fw fa-copy me-2"></i>Copy
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
|
||||
@@ -154,18 +154,19 @@ $sql_related_credentials = mysqli_query($mysqli, "
|
||||
credentials.credential_contact_id,
|
||||
credentials.credential_asset_id
|
||||
FROM credentials
|
||||
LEFT JOIN credential_tags ON credential_tags.credential_id = credentials.credential_id
|
||||
LEFT JOIN tags ON tags.tag_id = credential_tags.tag_id
|
||||
WHERE credential_asset_id = $asset_id
|
||||
AND credential_archived_at IS NULL
|
||||
GROUP BY credentials.credential_id
|
||||
ORDER BY credential_name DESC
|
||||
");
|
||||
$credential_count = mysqli_num_rows($sql_related_credentials);
|
||||
// No module_credential, no Credentials tab - the pane below was already gated, the tab wasn't
|
||||
$credential_count = lookupUserPermission('module_credential') ? mysqli_num_rows($sql_related_credentials) : 0;
|
||||
|
||||
// Related Tickets Query
|
||||
$sql_related_tickets = mysqli_query($mysqli, "
|
||||
SELECT tickets.*, users.*, ticket_statuses.*
|
||||
SELECT tickets.ticket_id, ticket_prefix, ticket_number, ticket_subject, ticket_priority, ticket_created_at,
|
||||
ticket_updated_at, ticket_closed_at, ticket_assigned_to, user_name, user_avatar,
|
||||
ticket_status_id, ticket_status_name, ticket_status_color
|
||||
FROM tickets
|
||||
LEFT JOIN users ON ticket_assigned_to = user_id
|
||||
LEFT JOIN ticket_statuses ON ticket_status_id = ticket_status
|
||||
@@ -249,6 +250,7 @@ ob_start();
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-<?= $device_icon ?> me-2"></i><strong><?= $asset_name ?></strong>
|
||||
<?php if ($asset_favorite) { ?><i class="fas fa-fw text-warning fa-star" title="Favorite"></i><?php } ?>
|
||||
<span class="fs-6 ms-1 align-middle"><?= assetStatusPill(html_entity_decode($asset_status)) ?></span>
|
||||
</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
@@ -348,10 +350,10 @@ ob_start();
|
||||
<div class="mt-2"><i class="fa fa-fw fa-circle text-secondary me-2"></i><?= "$asset_make $asset_model" ?></div>
|
||||
<?php }
|
||||
if ($asset_os) { ?>
|
||||
<div class="mt-2"><i class="fab fa-fw fa-windows text-secondary me-2"></i><?= "$asset_os" ?></div>
|
||||
<div class="mt-2"><i class="<?= assetOsIcon(html_entity_decode($asset_os)) ?> fa-fw text-secondary me-2"></i><?= $asset_os ?></div>
|
||||
<?php }
|
||||
if ($asset_serial) { ?>
|
||||
<div class="mt-2"><i class="fa fa-fw fa-barcode text-secondary me-2"></i><span class="font-monospace"><?= $asset_serial ?></span></div>
|
||||
<div class="mt-2 itflow-copy-always"><i class="fa fa-fw fa-barcode text-secondary me-2"></i><span class="font-monospace"><?= $asset_serial ?></span><button type="button" class="itflow-copy clipboardjs" title="Copy serial" data-clipboard-text="<?= $asset_serial ?>"><i class="far fa-copy"></i></button></div>
|
||||
<?php }
|
||||
if ($asset_purchase_date) { ?>
|
||||
<div class="mt-2"><i class="fa fa-fw fa-shopping-cart text-secondary me-2"></i><?= date('Y-m-d', strtotime($asset_purchase_date)) ?></div>
|
||||
@@ -359,8 +361,8 @@ ob_start();
|
||||
if ($asset_install_date) { ?>
|
||||
<div class="mt-2"><i class="fa fa-fw fa-calendar-check text-secondary me-2"></i><?= date('Y-m-d', strtotime($asset_install_date)) ?></div>
|
||||
<?php }
|
||||
if ($asset_warranty_expire) { ?>
|
||||
<div class="mt-2"><i class="fa fa-fw fa-exclamation-triangle text-secondary me-2"></i><?= date('Y-m-d', strtotime($asset_warranty_expire)) ?></div>
|
||||
if ($asset_warranty = assetWarranty(html_entity_decode($asset_warranty_expire))) { ?>
|
||||
<div class="mt-2"><i class="fa fa-fw fa-shield-alt text-secondary me-2"></i><span class="text-<?= $asset_warranty['tone'] ?>-emphasis">Warranty <?= strtolower(escapeHtml($asset_warranty['text'])) ?></span><?= $asset_warranty['tone'] !== 'success' ? ' <span class="text-secondary">(' . escapeHtml($asset_warranty['date']) . ')</span>' : '' ?></div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -371,13 +373,13 @@ ob_start();
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if ($asset_ip) { ?>
|
||||
<div><i class="fa fa-fw fa-globe text-secondary me-2"></i><span class="font-monospace"><?= $asset_ip ?></span></div>
|
||||
<div class="itflow-copy-always"><i class="fa fa-fw fa-globe text-secondary me-2"></i><span class="font-monospace"><?= $asset_ip ?></span><?php if ($asset_ip !== 'DHCP') { ?><button type="button" class="itflow-copy clipboardjs" title="Copy IP" data-clipboard-text="<?= $asset_ip ?>"><i class="far fa-copy"></i></button><?php } ?></div>
|
||||
<?php } ?>
|
||||
<?php if ($asset_nat_ip) { ?>
|
||||
<div class="mt-2"><i class="fa fa-fw fa-random text-secondary me-2"></i><span class="font-monospace"><?= $asset_nat_ip ?></span></div>
|
||||
<div class="mt-2 itflow-copy-always"><i class="fa fa-fw fa-random text-secondary me-2"></i><span class="font-monospace"><?= $asset_nat_ip ?></span><button type="button" class="itflow-copy clipboardjs" title="Copy NAT IP" data-clipboard-text="<?= $asset_nat_ip ?>"><i class="far fa-copy"></i></button></div>
|
||||
<?php }
|
||||
if ($asset_mac) { ?>
|
||||
<div class="mt-2"><i class="fa fa-fw fa-ethernet text-secondary me-2"></i><span class="font-monospace"><?= $asset_mac ?></span></div>
|
||||
<div class="mt-2 itflow-copy-always"><i class="fa fa-fw fa-ethernet text-secondary me-2"></i><span class="font-monospace"><?= $asset_mac ?></span><button type="button" class="itflow-copy clipboardjs" title="Copy MAC" data-clipboard-text="<?= $asset_mac ?>"><i class="far fa-copy"></i></button></div>
|
||||
<?php }
|
||||
if ($asset_uri) { ?>
|
||||
<div class="mt-2"><i class="fa fa-fw fa-link text-secondary me-2"></i><a href="<?= $asset_uri ?>" target="_blank" title="<?= $asset_uri ?>"><?= truncate($asset_uri, 20) ?></a></div>
|
||||
@@ -966,9 +968,11 @@ ob_start();
|
||||
<a href="asset.php?client_id=<?= $client_id ?>&asset_id=<?= $asset_id ?>"
|
||||
class="btn btn-primary text-bold"><span class="text-white"><i class="fas fa-info-circle me-2"></i>More Details</span>
|
||||
</a>
|
||||
<a href="#" class="btn btn-secondary ajax-modal" data-modal-url="modals/asset/asset_edit.php?id=<?= $asset_id ?>">
|
||||
<span class="text-white"><i class="fas fa-edit me-2"></i>Edit</span>
|
||||
</a>
|
||||
<?php if (lookupUserPermission('module_support') >= 2) { ?>
|
||||
<a href="#" class="btn btn-secondary ajax-modal" data-modal-size="lg" data-modal-url="modals/asset/asset_edit.php?id=<?= $asset_id ?>">
|
||||
<span class="text-white"><i class="fas fa-edit me-2"></i>Edit</span>
|
||||
</a>
|
||||
<?php } ?>
|
||||
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Close</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,456 +2,45 @@
|
||||
|
||||
require_once '../../../includes/modal_header.php';
|
||||
|
||||
enforceUserPermission('module_support', 2);
|
||||
|
||||
$client_id = intval($_GET['client_id'] ?? 0);
|
||||
$contact_id = intval($_GET['contact_id'] ?? 0);
|
||||
$type = escapeHtml(ucwords($_GET['type']) ?? '');
|
||||
|
||||
if ($client_id) {
|
||||
$sql_network_select = mysqli_query($mysqli, "SELECT network, network_id, network_name FROM networks WHERE network_archived_at IS NULL AND network_client_id = $client_id ORDER BY network_name ASC");
|
||||
$sql_vendor_select = mysqli_query($mysqli, "SELECT vendor_id, vendor_name FROM vendors WHERE vendor_archived_at IS NULL AND vendor_client_id = $client_id ORDER BY vendor_name ASC");
|
||||
$sql_location_select = mysqli_query($mysqli, "SELECT location_id, location_name FROM locations WHERE location_archived_at IS NULL AND location_client_id = $client_id ORDER BY location_name ASC");
|
||||
$sql_contact_select = mysqli_query($mysqli, "SELECT contact_id, contact_name FROM contacts WHERE contact_archived_at IS NULL AND contact_client_id = $client_id ORDER BY contact_name ASC");
|
||||
} else {
|
||||
$sql_client_select = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL " . clientScopeSql('clients.client_id') . " ORDER BY client_name ASC");
|
||||
enforceClientAccess();
|
||||
}
|
||||
|
||||
// OS typeahead suggestions
|
||||
$os_sql = mysqli_query($mysqli, "SELECT DISTINCT asset_os AS label FROM assets WHERE asset_archived_at IS NULL");
|
||||
if ($os_sql && mysqli_num_rows($os_sql) > 0) {
|
||||
$os_arr = [];
|
||||
while ($row = mysqli_fetch_assoc($os_sql)) {
|
||||
// jQuery UI Autocomplete expects {label: "...", value: "..."}
|
||||
$label = $row['label'];
|
||||
$os_arr[] = ['label' => $label, 'value' => $label];
|
||||
}
|
||||
$json_os = json_encode($os_arr);
|
||||
}
|
||||
// The assets list passes the tab it was on - start on that kind of asset
|
||||
$type_presets = ['workstation' => 'Desktop', 'server' => 'Server', 'virtual' => 'Virtual Machine', 'network' => 'Switch'];
|
||||
$type_param = strtolower((string) ($_GET['type'] ?? ''));
|
||||
|
||||
$sql_tags_select = mysqli_query($mysqli, "SELECT tag_id, tag_name FROM tags WHERE tag_type = 5 ORDER BY tag_name ASC");
|
||||
$asset_form_mode = 'add';
|
||||
$asset_id = 0;
|
||||
$asset_type = $type_presets[$type_param] ?? '';
|
||||
$asset_name = $asset_description = $asset_make = $asset_model = $asset_serial = $asset_os = '';
|
||||
$asset_ip = $asset_ipv6 = $asset_nat_ip = $asset_mac = $asset_uri = $asset_uri_2 = $asset_uri_client = '';
|
||||
$asset_status = $asset_purchase_reference = $asset_purchase_date = $asset_install_date = $asset_warranty_expire = '';
|
||||
$asset_physical_location = $asset_notes = $asset_photo = '';
|
||||
$asset_favorite = 0;
|
||||
$asset_location_id = $asset_vendor_id = $asset_network_id = 0;
|
||||
$asset_contact_id = intval($_GET['contact_id'] ?? 0);
|
||||
$asset_tag_id_array = [];
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-desktop me-2"></i>New <?php if ($type) { echo $type; } ?> Asset</h5>
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-desktop me-2"></i>New Asset</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
|
||||
<?php if ($client_id) { ?>
|
||||
<input type="hidden" name="client_id" value="<?= $client_id ?>">
|
||||
<?php } ?>
|
||||
|
||||
<div class="modal-body ui-front">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-bs-toggle="pill" href="#pills-asset-details">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pills-asset-assignment">Assignment</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pills-asset-network">Network</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pills-asset-purchase">Purchase</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pills-asset-login">Login</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pills-asset-notes">Notes</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-asset-details">
|
||||
|
||||
<?php if ($client_id) { ?>
|
||||
<input type="hidden" name="client_id" value="<?= $client_id ?>">
|
||||
<?php } else { ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Client <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
|
||||
<select class="form-select select2" name="client_id" required>
|
||||
<option value="">- Select Client -</option>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_assoc($sql_client_select)) {
|
||||
$client_id_select = intval($row['client_id']);
|
||||
$client_name = escapeHtml($row['client_name']); ?>
|
||||
<option <?php if ($client_id == $client_id_select) { echo "selected"; } ?> value="<?= $client_id_select ?>"><?= $client_name ?></option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Type <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-layer-group"></i></span>
|
||||
<select class="form-select select2" name="type" required>
|
||||
<option value="">- Select Type -</option>
|
||||
<?php foreach($asset_types_array as $asset_type => $asset_icon) { ?>
|
||||
<option><?= $asset_type ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Name <strong class="text-danger">*</strong> / <span class="text-secondary" title="Pin to Overview">Favorite</span></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-fw fa-tag"></i></span>
|
||||
<input type="text" class="form-control" name="name" placeholder="e.g. SRV-DC01" maxlength="200" required autofocus>
|
||||
<div class="input-group-text">
|
||||
<label class="star-toggle mb-0" title="Favorite">
|
||||
<input class="form-check-input" type="checkbox" name="favorite" value="1"><i class="far fa-star"></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php //Do not display Make Model or Serial if Virtual is selected
|
||||
if ($type !== 'Virtual') { ?>
|
||||
<div class="mb-3">
|
||||
<label>Make</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
<input type="text" class="form-control" name="make" placeholder="e.g. Dell, HP, Lenovo" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Model</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
|
||||
<input type="text" class="form-control" name="model" placeholder="e.g. PowerEdge R740" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Serial Number</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-barcode"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="serial" placeholder="e.g. ABC1234XYZ" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($type !== 'Network' && $type !== 'Other') { ?>
|
||||
<div class="mb-3">
|
||||
<label>Operating System</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-fw fa-laptop-code"></i></span>
|
||||
<input type="text" class="form-control" name="os" id="os" placeholder="e.g. Windows 11 Pro, Ubuntu 24.04" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-align-left"></i></span>
|
||||
<input type="text" class="form-control" name="description" placeholder="e.g. Domain controller for HQ" maxlength="255">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-asset-assignment">
|
||||
|
||||
<?php if ($client_id) { ?>
|
||||
<div class="mb-3">
|
||||
<label>Location</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
<select class="form-select select2" name="location">
|
||||
<option value="">- Select Location -</option>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_assoc($sql_location_select)) {
|
||||
$location_id = intval($row['location_id']);
|
||||
$location_name = escapeHtml($row['location_name']);
|
||||
?>
|
||||
<option value="<?= $location_id ?>"><?= $location_name ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Physical Location</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
|
||||
<input type="text" class="form-control" name="physical_location" placeholder="e.g. Floor 2, Closet B" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($client_id) { ?>
|
||||
<div class="mb-3">
|
||||
<label>Assign To</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user-check"></i></span>
|
||||
<select class="form-select select2" name="contact">
|
||||
<option value="">- Select Contact -</option>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_assoc($sql_contact_select)) {
|
||||
$contact_id_select = intval($row['contact_id']);
|
||||
$contact_name = escapeHtml($row['contact_name']);
|
||||
?>
|
||||
<option
|
||||
<?php if ($contact_id == $contact_id_select) {
|
||||
echo "selected"; }
|
||||
?>
|
||||
value="<?= $contact_id_select ?>"><?= $contact_name ?>
|
||||
</option>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Status</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-circle"></i></span>
|
||||
<select class="form-select select2" name="status">
|
||||
<option value="">- Select Status -</option>
|
||||
<?php
|
||||
$sql_interface_types_select = mysqli_query($mysqli, "
|
||||
SELECT category_name, category_color, category_icon FROM categories
|
||||
WHERE category_type = 'asset_status'
|
||||
AND category_archived_at IS NULL
|
||||
ORDER BY category_order ASC, category_name ASC
|
||||
");
|
||||
while ($row = mysqli_fetch_assoc($sql_interface_types_select)) {
|
||||
$asset_status_select = escapeHtml($row['category_name']);
|
||||
?>
|
||||
<option<?= selectOptionColor($row['category_color'] ?? '', $row['category_icon'] ?? '') ?>><?= $asset_status_select ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-asset-network">
|
||||
<?php if ($client_id) { ?>
|
||||
<div class="mb-3">
|
||||
<label>Network</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-network-wired"></i></span>
|
||||
<select class="form-select select2" name="network">
|
||||
<option value="">- Select Network -</option>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_assoc($sql_network_select)) {
|
||||
$network_id = intval($row['network_id']);
|
||||
$network_name = escapeHtml($row['network_name']);
|
||||
$network = escapeHtml($row['network']);
|
||||
|
||||
?>
|
||||
<option value="<?= $network_id ?>"><?= $network_name ?> - <?= $network ?></option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>IPv4 Address / <span class="text-muted">DHCP</span></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="ip" placeholder="e.g. 192.168.1.10" maxlength="200" data-inputmask="'alias': 'ip'" data-mask>
|
||||
<div class="input-group-text">
|
||||
<input class="form-check-input" type="checkbox" name="dhcp" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>MAC Address</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="mac" placeholder="e.g. 00:1A:2B:3C:4D:5E" data-inputmask="'alias': 'mac'" maxlength="200" data-mask>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>IPv6 Address</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="ipv6" placeholder="e.g. 2001:db8::1" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>NAT Address</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-exchange-alt"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="nat_ip" placeholder="e.g. 203.0.113.10 or 10.0.0.5" data-inputmask="'alias': 'ip'" maxlength="200" data-mask>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>URI</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
|
||||
<input type="text" class="form-control" name="uri" placeholder="e.g. https:// or ssh://" maxlength="500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>URI 2</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
|
||||
<input type="text" class="form-control" name="uri_2" placeholder="e.g. https:// or ssh://" maxlength="500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Client URI</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
|
||||
<input type="text" class="form-control" name="uri_client" placeholder="e.g. https:// or ssh://" maxlength="500">
|
||||
</div>
|
||||
<small class="text-muted">Viewable in client portal.</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-asset-purchase">
|
||||
|
||||
<?php if ($client_id) { ?>
|
||||
<div class="mb-3">
|
||||
<label>Vendor</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
<select class="form-select select2" name="vendor">
|
||||
<option value="">- Select Vendor -</option>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_assoc($sql_vendor_select)) {
|
||||
$vendor_id = intval($row['vendor_id']);
|
||||
$vendor_name = escapeHtml($row['vendor_name']);
|
||||
?>
|
||||
<option value="<?= $vendor_id ?>"><?= $vendor_name ?></option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($type !== 'Virtual') { ?>
|
||||
<div class="mb-3">
|
||||
<label>Purchase Reference</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-receipt"></i></span>
|
||||
<input type="text" class="form-control" name="purchase_reference" placeholder="e.g. INV-1045 or PO-7782" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Purchase Date</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
|
||||
<input type="date" class="form-control" name="purchase_date" max="2999-12-31">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Install Date</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar-check"></i></span>
|
||||
<input type="date" class="form-control" name="install_date" max="2999-12-31">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($type !== 'Virtual') { ?>
|
||||
<div class="mb-3">
|
||||
<label>Warranty Expire</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar-times"></i></span>
|
||||
<input type="date" class="form-control" name="warranty_expire" max="2999-12-31">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-asset-login">
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Username</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
|
||||
<input type="text" class="form-control" name="username" placeholder="e.g. admin" maxlength="350">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Password</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
|
||||
<input type="text" class="form-control" name="password" placeholder="Password" maxlength="350" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-asset-notes">
|
||||
|
||||
<div class="mb-3">
|
||||
<textarea class="form-control" rows="8" placeholder="Additional notes or configuration details" name="notes"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Upload Photo</label>
|
||||
<input type="file" class="form-control" name="file" accept="image/*">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Tags</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
|
||||
<select class="form-select select2" name="tags[]" data-placeholder="Add some tags" multiple>
|
||||
<?php
|
||||
|
||||
while ($row = mysqli_fetch_assoc($sql_tags_select)) {
|
||||
$tag_id = intval($row['tag_id']);
|
||||
$tag_name = escapeHtml($row['tag_name']);
|
||||
?>
|
||||
<option value="<?= $tag_id ?>"><?= $tag_name ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
<button class="btn btn-secondary ajax-modal" title="New tag" type="button"
|
||||
data-modal-url="../admin/modals/tag/tag_add.php?type=5">
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<?php require 'asset_form.php'; ?>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_asset" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Create</button>
|
||||
@@ -459,28 +48,6 @@ ob_start();
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- JSON Autocomplete / type ahead -->
|
||||
<script>
|
||||
itflowReady(function () {
|
||||
|
||||
var operatingSystems = <?= $json_os ?>;
|
||||
|
||||
var osInput = document.getElementById('os');
|
||||
if (!osInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
itflowAutocomplete(osInput, {
|
||||
minLength: 1,
|
||||
source: operatingSystems,
|
||||
onSelect: function (item) {
|
||||
osInput.value = item.label;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
|
||||
require_once '../../../includes/modal_footer.php';
|
||||
|
||||
@@ -4,408 +4,80 @@ require_once '../../../includes/modal_header.php';
|
||||
|
||||
enforceUserPermission('module_support', 2);
|
||||
|
||||
$asset_id = intval($_GET['id']);
|
||||
$source_asset_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT asset_archived_at, asset_client_id, asset_contact_id, asset_created_at, asset_description,
|
||||
asset_id, asset_install_date, asset_location_id, asset_make, asset_model, asset_name,
|
||||
asset_notes, asset_os, asset_photo, asset_physical_location, asset_purchase_date,
|
||||
asset_purchase_reference, asset_serial, asset_status, asset_type, asset_uri, asset_uri_2,
|
||||
asset_vendor_id, asset_warranty_expire, interface_ip, interface_ipv6, interface_mac,
|
||||
interface_nat_ip, interface_network_id FROM assets
|
||||
$sql = mysqli_query($mysqli, "SELECT asset_client_id, asset_contact_id, asset_description,
|
||||
asset_install_date, asset_location_id, asset_make, asset_model, asset_name,
|
||||
asset_notes, asset_os, asset_physical_location, asset_purchase_date,
|
||||
asset_purchase_reference, asset_status, asset_type, asset_vendor_id, asset_warranty_expire,
|
||||
interface_network_id FROM assets
|
||||
LEFT JOIN asset_interfaces ON interface_asset_id = asset_id AND interface_primary = 1
|
||||
WHERE asset_id = $asset_id LIMIT 1"
|
||||
WHERE asset_id = $source_asset_id LIMIT 1"
|
||||
);
|
||||
|
||||
$row = mysqli_fetch_assoc($sql);
|
||||
if (!$row) {
|
||||
echo json_encode(['error' => 'That asset no longer exists.']);
|
||||
exit;
|
||||
}
|
||||
$client_id = intval($row['asset_client_id']);
|
||||
$asset_id = intval($row['asset_id']);
|
||||
|
||||
enforceClientAccess();
|
||||
|
||||
/*
|
||||
* A copy is the next unit of the same kind - same model, place and paperwork,
|
||||
* but its own serial, addresses, links and photo, so those start empty.
|
||||
*/
|
||||
$asset_form_mode = 'copy';
|
||||
$asset_id = 0;
|
||||
$asset_type = escapeHtml($row['asset_type']);
|
||||
$asset_name = escapeHtml($row['asset_name']);
|
||||
$asset_description = escapeHtml($row['asset_description']);
|
||||
$asset_make = escapeHtml($row['asset_make']);
|
||||
$asset_model = escapeHtml($row['asset_model']);
|
||||
$asset_serial = escapeHtml($row['asset_serial']);
|
||||
$asset_os = escapeHtml($row['asset_os']);
|
||||
$asset_ip = escapeHtml($row['interface_ip']);
|
||||
$asset_ipv6 = escapeHtml($row['interface_ipv6']);
|
||||
$asset_nat_ip = escapeHtml($row['interface_nat_ip']);
|
||||
$asset_mac = escapeHtml($row['interface_mac']);
|
||||
$asset_uri = escapeHtml($row['asset_uri']);
|
||||
$asset_uri_2 = escapeHtml($row['asset_uri_2']);
|
||||
$asset_status = escapeHtml($row['asset_status']);
|
||||
$asset_purchase_reference = escapeHtml($row['asset_purchase_reference']);
|
||||
$asset_purchase_date = escapeHtml($row['asset_purchase_date']);
|
||||
$asset_warranty_expire = escapeHtml($row['asset_warranty_expire']);
|
||||
$asset_install_date = escapeHtml($row['asset_install_date']);
|
||||
$asset_photo = escapeHtml($row['asset_photo']);
|
||||
$asset_physical_location = escapeHtml($row['asset_physical_location']);
|
||||
$asset_notes = escapeHtml($row['asset_notes']);
|
||||
$asset_created_at = escapeHtml($row['asset_created_at']);
|
||||
$asset_archived_at = escapeHtml($row['asset_archived_at']);
|
||||
$asset_vendor_id = intval($row['asset_vendor_id']);
|
||||
$asset_location_id = intval($row['asset_location_id']);
|
||||
$asset_contact_id = intval($row['asset_contact_id']);
|
||||
$asset_network_id = intval($row['interface_network_id']);
|
||||
$device_icon = getAssetIcon($asset_type);
|
||||
$asset_serial = $asset_ip = $asset_ipv6 = $asset_nat_ip = $asset_mac = '';
|
||||
$asset_uri = $asset_uri_2 = $asset_uri_client = $asset_photo = '';
|
||||
$asset_favorite = 0;
|
||||
|
||||
enforceClientAccess();
|
||||
$device_icon = getAssetIcon($row['asset_type']);
|
||||
|
||||
// Tags come along
|
||||
$asset_tag_id_array = array();
|
||||
$sql_asset_tags = mysqli_query($mysqli, "SELECT asset_tag_tag_id FROM asset_tags WHERE asset_tag_asset_id = $source_asset_id");
|
||||
while ($row = mysqli_fetch_assoc($sql_asset_tags)) {
|
||||
$asset_tag_id_array[] = intval($row['asset_tag_tag_id']);
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class='fa fa-fw fa-<?= $device_icon ?> me-2'></i>Copying asset: <strong><?= $asset_name ?></strong></h5>
|
||||
<h5 class="modal-title text-truncate"><i class='fa fa-fw fa-<?= $device_icon ?> me-2'></i>Copying asset: <strong><?= $asset_name ?></strong></h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" 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">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-bs-toggle="pill" href="#pillsDetailsCopy<?= $asset_id ?>">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pillsAssignmentCopy<?= $asset_id ?>">Assignment</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pillsNetworkCopy<?= $asset_id ?>">Network</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pillsPurchaseCopy<?= $asset_id ?>">Purchase</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pillsLoginCopy<?= $asset_id ?>">Login</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pillsNotesCopy<?= $asset_id ?>">Notes</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane fade show active" id="pillsDetailsCopy<?= $asset_id ?>">
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Type <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-layer-group"></i></span>
|
||||
<select class="form-select select2" name="type" required>
|
||||
<?php foreach($asset_types_array as $asset_type_select => $asset_icon_select) { ?>
|
||||
<option <?php if ($asset_type_select == $asset_type) { echo "selected"; } ?>><?= $asset_type_select ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tag"></i></span>
|
||||
<input type="text" class="form-control" name="name" placeholder="Name the asset" maxlength="200" value="<?= $asset_name ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php //Do not display Make Model or Serial if Virtual is selected
|
||||
if ($asset_type !== 'virtual') { ?>
|
||||
<div class="mb-3">
|
||||
<label>Make</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
<input type="text" class="form-control" name="make" placeholder="Manufacturer" maxlength="200" value="<?= $asset_make ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Model</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
|
||||
<input type="text" class="form-control" name="model" placeholder="Model Number" maxlength="200" value="<?= $asset_model ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Serial Number</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-barcode"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="serial" placeholder="Serial number" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($asset_type !== 'Phone' && $asset_type !== 'Mobile Phone' && $asset_type !== 'Tablet' && $asset_type !== 'Access Point' && $asset_type !== 'Printer' && $asset_type !== 'Camera' && $asset_type !== 'TV' && $asset_type !== 'Other') { ?>
|
||||
<div class="mb-3">
|
||||
<label>Operating System</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-fw fa-laptop-code"></i></span>
|
||||
<input type="text" class="form-control" name="os" placeholder="ex Windows 10 Pro" maxlength="200" value="<?= $asset_os ?>">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-align-left"></i></span>
|
||||
<input type="text" class="form-control" name="description" placeholder="Description of the asset" maxlength="255" value="<?= $asset_description ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pillsAssignmentCopy<?= $asset_id ?>">
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Location</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
<select class="form-select select2" name="location">
|
||||
<option value="">- Select Location -</option>
|
||||
<?php
|
||||
|
||||
$sql_locations = mysqli_query($mysqli, "SELECT location_id, location_name FROM locations WHERE location_archived_at IS NULL AND location_client_id = $client_id ORDER BY location_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql_locations)) {
|
||||
$location_id_select = intval($row['location_id']);
|
||||
$location_name_select = escapeHtml($row['location_name']);
|
||||
?>
|
||||
<option <?php if ($asset_location_id == $location_id_select) { echo "selected"; } ?> value="<?= $location_id_select ?>"><?= $location_name_select ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Physical Location</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
|
||||
<input type="text" class="form-control" name="physical_location" placeholder="Physical location eg. Floor 2, Closet B" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Assign To</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user-check"></i></span>
|
||||
<select class="form-select select2" name="contact">
|
||||
<option value="">- Select Contact -</option>
|
||||
<?php
|
||||
|
||||
$sql_contacts = mysqli_query($mysqli, "SELECT contact_id, contact_name, contact_client_id, contact_photo, contact_title FROM contacts WHERE contact_archived_at IS NULL AND contact_client_id = $client_id ORDER BY contact_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql_contacts)) {
|
||||
$contact_id_select = intval($row['contact_id']);
|
||||
$contact_name_select = escapeHtml($row['contact_name']);
|
||||
?>
|
||||
<option value="<?= $contact_id_select ?>"<?= selectOptionContact($row['contact_client_id'] ?? 0, $row['contact_name'], $row['contact_photo'] ?? '', $row['contact_title'] ?? '') ?>><?= $contact_name_select ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Status</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-circle"></i></span>
|
||||
<select class="form-select select2" name="status">
|
||||
<option value="">- Select Status -</option>
|
||||
<?php
|
||||
$sql_interface_types_select = mysqli_query($mysqli, "
|
||||
SELECT category_name, category_color, category_icon FROM categories
|
||||
WHERE category_type = 'asset_status'
|
||||
AND category_archived_at IS NULL
|
||||
ORDER BY category_order ASC, category_name ASC
|
||||
");
|
||||
while ($row = mysqli_fetch_assoc($sql_interface_types_select)) {
|
||||
$asset_status_select = escapeHtml($row['category_name']);
|
||||
?>
|
||||
<option <?php if ($asset_status_select == $asset_status) { echo "selected"; } ?><?= selectOptionColor($row['category_color'] ?? '', $row['category_icon'] ?? '') ?>>
|
||||
<?= $asset_status_select ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pillsNetworkCopy<?= $asset_id ?>">
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Network</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-network-wired"></i></span>
|
||||
<select class="form-select select2" name="network">
|
||||
<option value="">- Select Network -</option>
|
||||
<?php
|
||||
|
||||
$sql_networks = mysqli_query($mysqli, "SELECT network, network_id, network_name FROM networks WHERE network_archived_at IS NULL AND network_client_id = $client_id ORDER BY network_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql_networks)) {
|
||||
$network_id_select = intval($row['network_id']);
|
||||
$network_name_select = escapeHtml($row['network_name']);
|
||||
$network_select = escapeHtml($row['network']);
|
||||
|
||||
?>
|
||||
<option <?php if ($asset_network_id == $network_id_select) { echo "selected"; } ?> value="<?= $network_id_select ?>"><?= $network_name_select ?> - <?= $network_select ?></option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>IP Address / <span class="text-muted">DHCP</span></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="ip" placeholder="192.168.10.250" maxlength="200" data-inputmask="'alias': 'ip'" data-mask>
|
||||
<div class="input-group-text">
|
||||
<input class="form-check-input" type="checkbox" name="dhcp" value="1" <?php if($asset_ip == 'DHCP'){ echo "checked"; } ?>>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>MAC Address</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="mac" placeholder="00:11:22:AA:BB:CC" maxlength="200" data-inputmask="'alias': 'mac'" data-mask>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>IPv6 Address</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="ipv6" value="<?= $asset_ipv6 ?>" placeholder="2001:0db8:1000::3" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>NAT Address</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-exchange-alt"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="nat_ip" placeholder="10.52.4.55" maxlength="200" data-inputmask="'alias': 'ip'" data-mask>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>URI</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
|
||||
<input type="text" class="form-control" name="uri" placeholder="URI http:// ftp:// ssh: etc" maxlength="500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>URI 2</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
|
||||
<input type="text" class="form-control" name="uri_2" placeholder="URI http:// ftp:// ssh: etc" maxlength="500">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pillsPurchaseCopy<?= $asset_id ?>">
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Vendor</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
<select class="form-select select2" name="vendor">
|
||||
<option value="">- Select Vendor -</option>
|
||||
<?php
|
||||
|
||||
$sql_vendors = mysqli_query($mysqli, "SELECT vendor_id, vendor_name FROM vendors WHERE vendor_archived_at IS NULL AND vendor_client_id = $client_id ORDER BY vendor_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql_vendors)) {
|
||||
$vendor_id_select = intval($row['vendor_id']);
|
||||
$vendor_name_select = escapeHtml($row['vendor_name']);
|
||||
?>
|
||||
<option <?php if ($asset_vendor_id == $vendor_id_select) { echo "selected"; } ?> value="<?= $vendor_id_select ?>"><?= $vendor_name_select ?></option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Install Date</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar-check"></i></span>
|
||||
<input type="date" class="form-control" name="install_date" max="2999-12-31" value="<?= $asset_install_date ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($asset_type !== 'Virtual Machine') { ?>
|
||||
<div class="mb-3">
|
||||
<label>Purchase Reference</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-shopping-cart"></i></span>
|
||||
<input type="text" class="form-control" name="purchase_reference" placeholder="eg. Invoice, PO Number" maxlength="200" value="<?= $asset_purchase_reference ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Purchase Date</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-receipt"></i></span>
|
||||
<input type="date" class="form-control" name="purchase_date" max="2999-12-31" value="<?= $asset_purchase_date ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Warranty Expire</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar-times"></i></span>
|
||||
<input type="date" class="form-control" name="warranty_expire" max="2999-12-31" value="<?= $asset_warranty_expire ?>">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pillsLoginCopy<?= $asset_id ?>">
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Username</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user"></i></span>
|
||||
<input type="text" class="form-control" name="username" placeholder="Username" maxlength="350">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Password</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-lock"></i></span>
|
||||
<input type="text" class="form-control" name="password" placeholder="Password" maxlength="350" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pillsNotesCopy<?= $asset_id ?>">
|
||||
|
||||
<div class="mb-3">
|
||||
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"><?= $asset_notes ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Upload Photo</label>
|
||||
<input type="file" class="form-control" name="file">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-body ui-front">
|
||||
<div class="alert alert-info small py-2"><i class="fas fa-info-circle me-2"></i>Serial, addresses, links and photo start empty - they belong to the unit you're copying.</div>
|
||||
<?php require 'asset_form.php'; ?>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="add_asset" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Copy</button>
|
||||
<button type="submit" name="add_asset" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Create Copy</button>
|
||||
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -6,7 +6,7 @@ enforceUserPermission('module_support', 2);
|
||||
|
||||
$asset_id = intval($_GET['id']);
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT asset_archived_at, asset_client_id, asset_contact_id, asset_created_at, asset_description,
|
||||
$sql = mysqli_query($mysqli, "SELECT asset_client_id, asset_contact_id, asset_description,
|
||||
asset_favorite, asset_id, asset_install_date, asset_location_id, asset_make, asset_model,
|
||||
asset_name, asset_notes, asset_os, asset_photo, asset_physical_location,
|
||||
asset_purchase_date, asset_purchase_reference, asset_serial, asset_status, asset_type,
|
||||
@@ -17,8 +17,15 @@ $sql = mysqli_query($mysqli, "SELECT asset_archived_at, asset_client_id, asset_c
|
||||
);
|
||||
|
||||
$row = mysqli_fetch_assoc($sql);
|
||||
if (!$row) {
|
||||
echo json_encode(['error' => 'That asset no longer exists.']);
|
||||
exit;
|
||||
}
|
||||
$client_id = intval($row['asset_client_id']);
|
||||
$asset_id = intval($row['asset_id']);
|
||||
|
||||
enforceClientAccess();
|
||||
|
||||
$asset_form_mode = 'edit';
|
||||
$asset_type = escapeHtml($row['asset_type']);
|
||||
$asset_name = escapeHtml($row['asset_name']);
|
||||
$asset_description = escapeHtml($row['asset_description']);
|
||||
@@ -42,460 +49,41 @@ $asset_photo = escapeHtml($row['asset_photo']);
|
||||
$asset_physical_location = escapeHtml($row['asset_physical_location']);
|
||||
$asset_notes = escapeHtml($row['asset_notes']);
|
||||
$asset_favorite = intval($row['asset_favorite']);
|
||||
$asset_created_at = escapeHtml($row['asset_created_at']);
|
||||
$asset_archived_at = escapeHtml($row['asset_archived_at']);
|
||||
$asset_vendor_id = intval($row['asset_vendor_id']);
|
||||
$asset_location_id = intval($row['asset_location_id']);
|
||||
$asset_contact_id = intval($row['asset_contact_id']);
|
||||
$asset_network_id = intval($row['interface_network_id']);
|
||||
|
||||
$device_icon = getAssetIcon($asset_type);
|
||||
$device_icon = getAssetIcon($row['asset_type']);
|
||||
|
||||
// Asset History Query
|
||||
// History, newest first
|
||||
$sql_asset_history = mysqli_query($mysqli, "SELECT asset_history_created_at, asset_history_description, asset_history_status FROM asset_history
|
||||
WHERE asset_history_asset_id = $asset_id
|
||||
ORDER BY asset_history_id
|
||||
DESC LIMIT 10"
|
||||
ORDER BY asset_history_id DESC
|
||||
LIMIT 25"
|
||||
);
|
||||
|
||||
// Tags
|
||||
$asset_tag_id_array = array();
|
||||
$sql_asset_tags = mysqli_query($mysqli, "SELECT asset_tag_tag_id FROM asset_tags WHERE asset_tag_asset_id = $asset_id");
|
||||
while ($row = mysqli_fetch_assoc($sql_asset_tags)) {
|
||||
$asset_tag_tag_id = intval($row['asset_tag_tag_id']);
|
||||
$asset_tag_id_array[] = $asset_tag_tag_id;
|
||||
$asset_tag_id_array[] = intval($row['asset_tag_tag_id']);
|
||||
}
|
||||
|
||||
enforceClientAccess();
|
||||
|
||||
ob_start();
|
||||
|
||||
?>
|
||||
|
||||
<div class="modal-header bg-dark">
|
||||
<h5 class="modal-title"><i class='fa fa-fw fa-<?= $device_icon ?> me-2'></i>Editing asset: <strong><?= $asset_name ?></strong></h5>
|
||||
<h5 class="modal-title text-truncate"><i class='fa fa-fw fa-<?= $device_icon ?> me-2'></i>Editing asset: <strong><?= $asset_name ?></strong></h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<form action="post.php" method="post" enctype="multipart/form-data" autocomplete="off">
|
||||
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
|
||||
<input type="hidden" name="asset_id" value="<?= $asset_id ?>">
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<ul class="nav nav-pills nav-justified mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-bs-toggle="pill" href="#pills-details">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pills-assignment">Assignment</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pills-network">Network</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pills-purchase">Purchase</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pills-notes">Notes</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#pills-history">History</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="tab-content" <?php if (lookupUserPermission('module_support') <= 1) { echo 'inert'; } ?>>
|
||||
|
||||
<div class="tab-pane fade show active" id="pills-details">
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Type <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-layer-group"></i></span>
|
||||
<select class="form-select select2" name="type" required>
|
||||
<?php foreach($asset_types_array as $asset_type_select => $asset_icon_select) { ?>
|
||||
<option <?php if ($asset_type_select == $asset_type) { echo "selected"; } ?>><?= $asset_type_select ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-fw fa-tag"></i></span>
|
||||
<input type="text" class="form-control" name="name" placeholder="Asset name or asset tag" maxlength="200" value="<?= $asset_name ?>" required>
|
||||
<div class="input-group-text">
|
||||
<label class="star-toggle mb-0" title="Favorite">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
name="favorite"
|
||||
value="1"
|
||||
<?php if($asset_favorite) { echo 'checked'; } ?>>
|
||||
<i class="far fa-star"></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php //Do not display Make Model or Serial if Virtual is selected
|
||||
if ($asset_type !== 'virtual') { ?>
|
||||
<div class="mb-3">
|
||||
<label>Make</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
<input type="text" class="form-control" name="make" placeholder="Manufacturer" maxlength="200" value="<?= $asset_make ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Model</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-cube"></i></span>
|
||||
<input type="text" class="form-control" name="model" placeholder="Model Number" maxlength="200" value="<?= $asset_model ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Serial Number</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-barcode"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="serial" placeholder="Serial number" maxlength="200" value="<?= $asset_serial ?>">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if ($asset_type !== 'Phone' && $asset_type !== 'Mobile Phone' && $asset_type !== 'Tablet' && $asset_type !== 'Access Point' && $asset_type !== 'Printer' && $asset_type !== 'Camera' && $asset_type !== 'TV' && $asset_type !== 'Other') { ?>
|
||||
<div class="mb-3">
|
||||
<label>Operating System</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-fw fa-laptop-code"></i></span>
|
||||
<input type="text" class="form-control" name="os" id="os" placeholder="ex Windows 10 Pro" maxlength="200" value="<?= $asset_os ?>">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Description</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-align-left"></i></span>
|
||||
<input type="text" class="form-control" name="description" placeholder="Description of the asset" maxlength="255" value="<?= $asset_description ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-assignment">
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Location</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
<select class="form-select select2" name="location">
|
||||
<option value="">- Select Location -</option>
|
||||
<?php
|
||||
|
||||
$sql_locations = mysqli_query($mysqli, "SELECT location_archived_at, location_id, location_name FROM locations WHERE location_id = $asset_location_id OR location_archived_at IS NULL AND location_client_id = $client_id ORDER BY location_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql_locations)) {
|
||||
$location_id_select = intval($row['location_id']);
|
||||
$location_name_select = escapeHtml($row['location_name']);
|
||||
$location_archived_at = escapeHtml($row['location_archived_at']);
|
||||
if ($location_archived_at) {
|
||||
$location_name_select_display = "($location_name_select) - ARCHIVED";
|
||||
} else {
|
||||
$location_name_select_display = $location_name_select;
|
||||
}
|
||||
?>
|
||||
<option <?php if ($asset_location_id == $location_id_select) { echo "selected"; } ?> value="<?= $location_id_select ?>"><?= $location_name_select_display ?></option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Physical Location</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-map-marker-alt"></i></span>
|
||||
<input type="text" class="form-control" name="physical_location" placeholder="Physical location eg. Floor 2, Closet B" maxlength="200" value="<?= $asset_physical_location ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Assign To</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-user-check"></i></span>
|
||||
<select class="form-select select2" name="contact">
|
||||
<option value="">- Select Contact -</option>
|
||||
<?php
|
||||
|
||||
$sql_contacts = mysqli_query($mysqli, "SELECT contact_archived_at, contact_id, contact_name, contact_client_id, contact_photo, contact_title FROM contacts WHERE contact_id = $asset_contact_id OR contact_archived_at IS NULL AND contact_client_id = $client_id ORDER BY contact_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql_contacts)) {
|
||||
$contact_id_select = intval($row['contact_id']);
|
||||
$contact_name_select = escapeHtml($row['contact_name']);
|
||||
$contact_archived_at = escapeHtml($row['contact_archived_at']);
|
||||
if ($contact_archived_at) {
|
||||
$contact_name_select_display = "($contact_name_select) - ARCHIVED";
|
||||
} else {
|
||||
$contact_name_select_display = $contact_name_select;
|
||||
}
|
||||
?>
|
||||
<option <?php if ($asset_contact_id == $contact_id_select) { echo "selected"; } ?> value="<?= $contact_id_select ?>"<?= selectOptionContact($row['contact_client_id'] ?? 0, $row['contact_name'], $row['contact_photo'] ?? '', $row['contact_title'] ?? '') ?>>
|
||||
<?= $contact_name_select_display ?>
|
||||
</option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Status</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-circle"></i></span>
|
||||
<select class="form-select select2" name="status">
|
||||
<option value="">- Select Status -</option>
|
||||
<?php
|
||||
$sql_interface_types_select = mysqli_query($mysqli, "
|
||||
SELECT category_name, category_color, category_icon FROM categories
|
||||
WHERE category_type = 'asset_status'
|
||||
AND category_archived_at IS NULL
|
||||
ORDER BY category_order ASC, category_name ASC
|
||||
");
|
||||
while ($row = mysqli_fetch_assoc($sql_interface_types_select)) {
|
||||
$asset_status_select = escapeHtml($row['category_name']);
|
||||
?>
|
||||
<option <?php if ($asset_status_select == $asset_status) { echo "selected"; } ?><?= selectOptionColor($row['category_color'] ?? '', $row['category_icon'] ?? '') ?>>
|
||||
<?= $asset_status_select ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-network">
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Network</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-network-wired"></i></span>
|
||||
<select class="form-select select2" name="network">
|
||||
<option value="">- Select Network -</option>
|
||||
<?php
|
||||
|
||||
$sql_networks = mysqli_query($mysqli, "SELECT network, network_archived_at, network_id, network_name FROM networks WHERE network_id = $asset_network_id OR network_archived_at IS NULL AND network_client_id = $client_id ORDER BY network_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql_networks)) {
|
||||
$network_id_select = intval($row['network_id']);
|
||||
$network_name_select = escapeHtml($row['network_name']);
|
||||
$network_select = escapeHtml($row['network']);
|
||||
$network_archived_at = escapeHtml($row['network_archived_at']);
|
||||
if ($network_archived_at) {
|
||||
$network_name_select_display = "($network_name_select - $network_select) - ARCHIVED";
|
||||
} else {
|
||||
$network_name_select_display = "$network_name_select - $network_select";
|
||||
}
|
||||
|
||||
?>
|
||||
<option <?php if ($asset_network_id == $network_id_select) { echo "selected"; } ?> value="<?= $network_id_select ?>"><?= $network_name_select_display ?></option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>IPv4 Address / <span class="text-muted">DHCP</span></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="ip" value="<?= $asset_ip ?>" placeholder="192.168.10.250" data-inputmask="'alias': 'ip'" maxlength="200" data-mask>
|
||||
<div class="input-group-text">
|
||||
<input class="form-check-input" type="checkbox" name="dhcp" value="1" <?php if($asset_ip == 'DHCP'){ echo "checked"; } ?>>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>MAC Address</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="mac" value="<?= $asset_mac ?>" placeholder="00:11:22:AA:BB:CC" data-inputmask="'alias': 'mac'" maxlength="200" data-mask>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>IPv6 Address</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-ethernet"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="ipv6" value="<?= $asset_ipv6 ?>" placeholder="2001:0db8:1000::3" maxlength="200">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>NAT Address</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-exchange-alt"></i></span>
|
||||
<input type="text" class="form-control font-monospace" name="nat_ip" value="<?= $asset_nat_ip ?>" placeholder="10.52.4.55" data-inputmask="'alias': 'ip'" maxlength="200" data-mask>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>URI</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
|
||||
<input type="text" class="form-control" name="uri" placeholder="URI http:// ftp:// ssh: etc" maxlength="500" value="<?= $asset_uri ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>URI 2</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
|
||||
<input type="text" class="form-control" name="uri_2" placeholder="URI http:// ftp:// ssh: etc" maxlength="500" value="<?= $asset_uri_2 ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label>Client URI</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-globe"></i></span>
|
||||
<input type="text" class="form-control" name="uri_client" placeholder="URI http:// ftp:// ssh: etc -- viewable in Client Portal" maxlength="500" value="<?= $asset_uri_client ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-purchase">
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Vendor</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-building"></i></span>
|
||||
<select class="form-select select2" name="vendor">
|
||||
<option value="">- Select Vendor -</option>
|
||||
<?php
|
||||
|
||||
$sql_vendors = mysqli_query($mysqli, "SELECT vendor_archived_at, vendor_id, vendor_name FROM vendors WHERE vendor_id = $asset_vendor_id OR vendor_archived_at IS NULL AND vendor_client_id = $client_id ORDER BY vendor_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql_vendors)) {
|
||||
$vendor_id_select = intval($row['vendor_id']);
|
||||
$vendor_name_select = escapeHtml($row['vendor_name']);
|
||||
$vendor_archived_at = escapeHtml($row['vendor_archived_at']);
|
||||
if ($vendor_archived_at) {
|
||||
$vendor_name_select_display = "($vendor_name_select) - ARCHIVED";
|
||||
} else {
|
||||
$vendor_name_select_display = $vendor_name_select;
|
||||
}
|
||||
?>
|
||||
<option <?php if ($asset_vendor_id == $vendor_id_select) { echo "selected"; } ?> value="<?= $vendor_id_select ?>"><?= $vendor_name_select_display ?></option>
|
||||
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($asset_type !== 'Virtual Machine') { ?>
|
||||
<div class="mb-3">
|
||||
<label>Purchase Reference</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-receipt"></i></span>
|
||||
<input type="text" class="form-control" name="purchase_reference" placeholder="eg. Invoice, PO Number" maxlength="200" value="<?= $asset_purchase_reference ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Purchase Date</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
|
||||
<input type="date" class="form-control" name="purchase_date" max="2999-12-31" value="<?= $asset_purchase_date ?>">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Install Date</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar-check"></i></span>
|
||||
<input type="date" class="form-control" name="install_date" max="2999-12-31" value="<?= $asset_install_date ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($asset_type !== 'Virtual Machine') { ?>
|
||||
<div class="mb-3">
|
||||
<label>Warranty Expire</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar-times"></i></span>
|
||||
<input type="date" class="form-control" name="warranty_expire" max="2999-12-31" value="<?= $asset_warranty_expire ?>">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pills-notes">
|
||||
|
||||
<div class="mb-3 text-center">
|
||||
<?php if ($asset_photo) { ?>
|
||||
<img class="img-fluid" alt="asset_photo" src="<?= "../uploads/clients/$client_id/$asset_photo" ?>">
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Upload / Replace Photo</label>
|
||||
<input type="file" class="form-control" name="file" accept="image/*">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<textarea class="form-control" rows="8" placeholder="Enter some notes" name="notes"><?= $asset_notes ?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Tags</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-tags"></i></span>
|
||||
<select class="form-select select2" name="tags[]" data-placeholder="Add some tags" multiple>
|
||||
<?php
|
||||
|
||||
$sql_tags_select = mysqli_query($mysqli, "SELECT tag_id, tag_name, tag_color, tag_icon FROM tags WHERE tag_type = 5 ORDER BY tag_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql_tags_select)) {
|
||||
$tag_id_select = intval($row['tag_id']);
|
||||
$tag_name_select = escapeHtml($row['tag_name']);
|
||||
?>
|
||||
<option value="<?= $tag_id_select ?>" <?php if (in_array($tag_id_select, $asset_tag_id_array)) { echo "selected"; } ?><?= selectOptionColor($row['tag_color'] ?? '', $row['tag_icon'] ?? '') ?>><?= $tag_name_select ?></option>
|
||||
<?php } ?>
|
||||
|
||||
</select>
|
||||
<button class="btn btn-secondary ajax-modal" title="New tag" type="button"
|
||||
data-modal-url="../admin/modals/tag/tag_add.php?type=5">
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-muted text-end">Asset ID: <span class="font-monospace"><?= $asset_id ?></span></p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane fade" id="pills-history">
|
||||
|
||||
<div class="mb-3">
|
||||
<label>Asset History</label>
|
||||
<ul>
|
||||
<?php
|
||||
while ($row = mysqli_fetch_assoc($sql_asset_history)) {
|
||||
$asset_history_status = escapeHtml($row['asset_history_status']);
|
||||
$asset_history_description = escapeHtml($row['asset_history_description']);
|
||||
$asset_history_created_at = escapeHtml($row['asset_history_created_at']);
|
||||
echo "<li><span class='font-monospace'>$asset_history_created_at</span> <span class='badge rounded-pill bg-secondary mt-1'>$asset_history_status</span><br/>$asset_history_description</li><br/>";
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-body ui-front">
|
||||
<?php require 'asset_form.php'; ?>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" name="edit_asset" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Save</button>
|
||||
|
||||
390
agent/modals/asset/asset_form.php
Normal file
390
agent/modals/asset/asset_form.php
Normal file
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
/*
|
||||
* Asset form fields - shared by asset_add.php, asset_edit.php and
|
||||
* asset_copy.php, which set $asset_form_mode ('add' | 'edit' | 'copy'),
|
||||
* $client_id (0 = pick the client here) and the $asset_* values (escaped,
|
||||
* empty for a new asset) before including this. Posts the field set
|
||||
* agent/post/asset_model.php reads.
|
||||
*
|
||||
* One page of sections instead of six tabs. Every field is always in the
|
||||
* form: fields that came and went with the asset type used to drop out of the
|
||||
* POST entirely, and the handler then saved them as empty - editing an access
|
||||
* point wiped its firmware version.
|
||||
*
|
||||
* Should not be accessed directly.
|
||||
*/
|
||||
|
||||
isset($mysqli) || die("Direct file access is not allowed");
|
||||
|
||||
// Status - from Admin > Categories, keeping a retired value that is still set
|
||||
$status_options = [];
|
||||
$sql_status_select = mysqli_query($mysqli, "SELECT category_name, category_color, category_icon FROM categories
|
||||
WHERE category_type = 'asset_status' AND category_archived_at IS NULL
|
||||
ORDER BY category_order ASC, category_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql_status_select)) {
|
||||
$status_options[escapeHtml($row['category_name'])] = selectOptionColor($row['category_color'] ?? '', $row['category_icon'] ?? '');
|
||||
}
|
||||
if ($asset_status !== '' && !isset($status_options[$asset_status])) {
|
||||
$status_options[$asset_status] = '';
|
||||
}
|
||||
|
||||
// The client's own lists - only once the client is known
|
||||
$location_options = $contact_options = $network_options = $vendor_options = [];
|
||||
if ($client_id) {
|
||||
$sql = mysqli_query($mysqli, "SELECT location_archived_at, location_id, location_name FROM locations
|
||||
WHERE location_client_id = $client_id AND (location_archived_at IS NULL OR location_id = $asset_location_id)
|
||||
ORDER BY location_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql)) {
|
||||
$location_options[] = $row;
|
||||
}
|
||||
$sql = mysqli_query($mysqli, "SELECT contact_archived_at, contact_id, contact_name, contact_client_id, contact_photo, contact_title FROM contacts
|
||||
WHERE contact_client_id = $client_id AND (contact_archived_at IS NULL OR contact_id = $asset_contact_id)
|
||||
ORDER BY contact_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql)) {
|
||||
$contact_options[] = $row;
|
||||
}
|
||||
$sql = mysqli_query($mysqli, "SELECT network, network_archived_at, network_id, network_name FROM networks
|
||||
WHERE network_client_id = $client_id AND (network_archived_at IS NULL OR network_id = $asset_network_id)
|
||||
ORDER BY network_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql)) {
|
||||
$network_options[] = $row;
|
||||
}
|
||||
$sql = mysqli_query($mysqli, "SELECT vendor_archived_at, vendor_id, vendor_name FROM vendors
|
||||
WHERE vendor_client_id = $client_id AND (vendor_archived_at IS NULL OR vendor_id = $asset_vendor_id)
|
||||
ORDER BY vendor_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql)) {
|
||||
$vendor_options[] = $row;
|
||||
}
|
||||
} else {
|
||||
$client_options = [];
|
||||
$sql = mysqli_query($mysqli, "SELECT client_id, client_name, client_abbreviation FROM clients WHERE client_archived_at IS NULL " . clientScopeSql('clients.client_id') . " ORDER BY client_name ASC");
|
||||
while ($row = mysqli_fetch_assoc($sql)) {
|
||||
$client_options[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
// OS suggestions - what's already in use, most common first
|
||||
$os_suggestions = [];
|
||||
$sql = mysqli_query($mysqli, "SELECT asset_os FROM assets WHERE asset_archived_at IS NULL AND asset_os IS NOT NULL AND asset_os != '' GROUP BY asset_os ORDER BY COUNT(*) DESC LIMIT 200");
|
||||
while ($row = mysqli_fetch_assoc($sql)) {
|
||||
$os_suggestions[] = ['label' => $row['asset_os'], 'value' => $row['asset_os']];
|
||||
}
|
||||
|
||||
$sql_tags_select = mysqli_query($mysqli, "SELECT tag_id, tag_name, tag_color, tag_icon FROM tags WHERE tag_type = 5 ORDER BY tag_name ASC");
|
||||
|
||||
$asset_ip_is_dhcp = $asset_ip === 'DHCP';
|
||||
|
||||
?>
|
||||
|
||||
<?php if ($asset_form_mode === 'edit') { ?>
|
||||
<ul class="nav nav-pills mb-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-bs-toggle="pill" href="#assetFormDetails">Details</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-bs-toggle="pill" href="#assetFormHistory">History</a>
|
||||
</li>
|
||||
</ul>
|
||||
<?php } ?>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="assetFormDetails">
|
||||
|
||||
<div class="itflow-form-section">Basics</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<?php if (!$client_id) { ?>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Client <strong class="text-danger">*</strong></label>
|
||||
<select class="form-select select2" name="client_id" required>
|
||||
<option value="">- Select Client -</option>
|
||||
<?php foreach ($client_options as $client_option) { ?>
|
||||
<option value="<?= intval($client_option['client_id']) ?>"<?= selectOptionClient($client_option['client_id'], $client_option['client_name'], $client_option['client_abbreviation'] ?? '') ?>><?= escapeHtml($client_option['client_name']) ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
<div class="form-text">Pick the asset's location, contact, network and vendor from its page once it's saved.</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Type <strong class="text-danger">*</strong></label>
|
||||
<select class="form-select select2" name="type" required>
|
||||
<option value="">- Select Type -</option>
|
||||
<?php foreach ($asset_types_array as $asset_type_select => $asset_icon_select) { ?>
|
||||
<option <?php if ($asset_type_select === html_entity_decode($asset_type)) { echo "selected"; } ?><?= selectOptionData(['icon' => "fas $asset_icon_select"]) ?>><?= $asset_type_select ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Name <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" name="name" placeholder="SRV-DC01" maxlength="200" value="<?= $asset_name ?>" required <?= $asset_form_mode === 'edit' ? '' : 'autofocus' ?>>
|
||||
<div class="input-group-text">
|
||||
<label class="star-toggle mb-0" title="Favorite - pinned to the client overview">
|
||||
<input class="form-check-input" type="checkbox" name="favorite" value="1" <?= $asset_favorite ? 'checked' : '' ?>><i class="far fa-star"></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Status</label>
|
||||
<select class="form-select select2" name="status">
|
||||
<option value="">- Status -</option>
|
||||
<?php foreach ($status_options as $status_option => $status_option_data) { ?>
|
||||
<option <?php if ($status_option === $asset_status) { echo "selected"; } ?><?= $status_option_data ?>><?= $status_option ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<label class="form-label">Description</label>
|
||||
<input type="text" class="form-control" name="description" placeholder="Domain controller for HQ" maxlength="255" value="<?= $asset_description ?>">
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Tags</label>
|
||||
<div class="input-group">
|
||||
<select class="form-select select2" name="tags[]" data-placeholder="Add some tags" multiple>
|
||||
<?php while ($row = mysqli_fetch_assoc($sql_tags_select)) {
|
||||
$tag_id_select = intval($row['tag_id']); ?>
|
||||
<option value="<?= $tag_id_select ?>" <?php if (in_array($tag_id_select, $asset_tag_id_array)) { echo "selected"; } ?><?= selectOptionColor($row['tag_color'] ?? '', $row['tag_icon'] ?? '') ?>><?= escapeHtml($row['tag_name']) ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
<button class="btn btn-secondary ajax-modal" title="New tag" type="button" data-modal-url="../admin/modals/tag/tag_add.php?type=5">
|
||||
<i class="fas fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="itflow-form-section">Hardware</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Make</label>
|
||||
<input type="text" class="form-control" name="make" placeholder="Dell" maxlength="200" value="<?= $asset_make ?>">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Model</label>
|
||||
<input type="text" class="form-control" name="model" placeholder="PowerEdge R650" maxlength="200" value="<?= $asset_model ?>">
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Serial number</label>
|
||||
<input type="text" class="form-control font-monospace" name="serial" placeholder="ABC1234XYZ" maxlength="200" value="<?= $asset_serial ?>">
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Operating system / firmware</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-fw fa-laptop-code"></i></span>
|
||||
<input type="text" class="form-control" name="os" id="assetFormOs" placeholder="Windows 11 Pro 24H2, Ubuntu 24.04, UniFi firmware 7.0" maxlength="200" value="<?= $asset_os ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="itflow-form-section">Network <span class="fw-normal text-body-secondary text-lowercase">· the primary interface</span></div>
|
||||
<div class="row g-3 mb-4">
|
||||
<?php if ($client_id) { ?>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Network</label>
|
||||
<select class="form-select select2" name="network">
|
||||
<option value="">- Select Network -</option>
|
||||
<?php foreach ($network_options as $network_option) {
|
||||
$network_option_id = intval($network_option['network_id']); ?>
|
||||
<option value="<?= $network_option_id ?>" <?php if ($asset_network_id === $network_option_id) { echo "selected"; } ?><?= selectOptionData(['description' => $network_option['network']]) ?>><?= escapeHtml($network_option['network_name']) ?><?= $network_option['network_archived_at'] ? ' (archived)' : '' ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">IPv4 address</label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control font-monospace" name="ip" id="assetFormIp" placeholder="192.168.1.10" maxlength="200" value="<?= $asset_ip_is_dhcp ? '' : $asset_ip ?>" data-inputmask="'alias': 'ip'" data-mask <?= $asset_ip_is_dhcp ? 'disabled' : '' ?>>
|
||||
<div class="input-group-text">
|
||||
<div class="form-check mb-0">
|
||||
<input class="form-check-input" type="checkbox" name="dhcp" value="1" id="assetFormDhcp" <?= $asset_ip_is_dhcp ? 'checked' : '' ?>>
|
||||
<label class="form-check-label small" for="assetFormDhcp">DHCP</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">MAC address</label>
|
||||
<input type="text" class="form-control font-monospace" name="mac" placeholder="00:1A:2B:3C:4D:5E" maxlength="200" value="<?= $asset_mac ?>" data-inputmask="'alias': 'mac'" data-mask>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">NAT address</label>
|
||||
<input type="text" class="form-control font-monospace" name="nat_ip" placeholder="203.0.113.10" maxlength="200" value="<?= $asset_nat_ip ?>" data-inputmask="'alias': 'ip'" data-mask>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">IPv6 address</label>
|
||||
<input type="text" class="form-control font-monospace" name="ipv6" placeholder="2001:db8::1" maxlength="200" value="<?= $asset_ipv6 ?>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Link</label>
|
||||
<input type="text" class="form-control" name="uri" placeholder="https://, ssh://, rdp://" maxlength="500" value="<?= $asset_uri ?>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Second link</label>
|
||||
<input type="text" class="form-control" name="uri_2" placeholder="https://, ssh://, rdp://" maxlength="500" value="<?= $asset_uri_2 ?>">
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Client link</label>
|
||||
<input type="text" class="form-control" name="uri_client" placeholder="https://" maxlength="500" value="<?= $asset_uri_client ?>">
|
||||
<div class="form-text">Shown to the client in their portal.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($client_id) { ?>
|
||||
<div class="itflow-form-section">Where & who</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Location</label>
|
||||
<select class="form-select select2" name="location">
|
||||
<option value="">- Select Location -</option>
|
||||
<?php foreach ($location_options as $location_option) {
|
||||
$location_option_id = intval($location_option['location_id']); ?>
|
||||
<option value="<?= $location_option_id ?>" <?php if ($asset_location_id === $location_option_id) { echo "selected"; } ?>><?= escapeHtml($location_option['location_name']) ?><?= $location_option['location_archived_at'] ? ' (archived)' : '' ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Where in the building</label>
|
||||
<input type="text" class="form-control" name="physical_location" placeholder="Floor 2, comms closet B" maxlength="200" value="<?= $asset_physical_location ?>">
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Assigned to</label>
|
||||
<select class="form-select select2" name="contact">
|
||||
<option value="">- Nobody -</option>
|
||||
<?php foreach ($contact_options as $contact_option) {
|
||||
$contact_option_id = intval($contact_option['contact_id']); ?>
|
||||
<option value="<?= $contact_option_id ?>" <?php if ($asset_contact_id === $contact_option_id) { echo "selected"; } ?><?= selectOptionContact($contact_option['contact_client_id'] ?? 0, $contact_option['contact_name'], $contact_option['contact_photo'] ?? '', $contact_option['contact_title'] ?? '') ?>><?= escapeHtml($contact_option['contact_name']) ?><?= $contact_option['contact_archived_at'] ? ' (archived)' : '' ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<?php } else { ?>
|
||||
<div class="itflow-form-section">Where</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-12">
|
||||
<label class="form-label">Where in the building</label>
|
||||
<input type="text" class="form-control" name="physical_location" placeholder="Floor 2, comms closet B" maxlength="200" value="<?= $asset_physical_location ?>">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="itflow-form-section">Purchase & warranty</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<?php if ($client_id) { ?>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Vendor</label>
|
||||
<select class="form-select select2" name="vendor">
|
||||
<option value="">- Select Vendor -</option>
|
||||
<?php foreach ($vendor_options as $vendor_option) {
|
||||
$vendor_option_id = intval($vendor_option['vendor_id']); ?>
|
||||
<option value="<?= $vendor_option_id ?>" <?php if ($asset_vendor_id === $vendor_option_id) { echo "selected"; } ?>><?= escapeHtml($vendor_option['vendor_name']) ?><?= $vendor_option['vendor_archived_at'] ? ' (archived)' : '' ?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Order / invoice reference</label>
|
||||
<input type="text" class="form-control" name="purchase_reference" placeholder="INV-1045, PO-7782" maxlength="200" value="<?= $asset_purchase_reference ?>">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Purchased</label>
|
||||
<input type="date" class="form-control" name="purchase_date" max="2999-12-31" value="<?= $asset_purchase_date ?>">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Installed</label>
|
||||
<input type="date" class="form-control" name="install_date" max="2999-12-31" value="<?= $asset_install_date ?>">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Warranty expires</label>
|
||||
<input type="date" class="form-control" name="warranty_expire" max="2999-12-31" value="<?= $asset_warranty_expire ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($asset_form_mode !== 'edit') { ?>
|
||||
<div class="itflow-form-section">Login <span class="fw-normal text-body-secondary text-lowercase">· saved as a credential on the asset</span></div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Username</label>
|
||||
<input type="text" class="form-control" name="username" placeholder="admin" maxlength="350" autocomplete="off">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Password</label>
|
||||
<input type="text" class="form-control" name="password" placeholder="Leave empty to skip" maxlength="350" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="itflow-form-section">Photo & notes</div>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Photo</label>
|
||||
<?php if ($asset_photo) { ?>
|
||||
<div class="mb-2"><img class="img-thumbnail" style="max-height: 7rem" alt="Current photo" src="<?= "../uploads/clients/$client_id/$asset_photo" ?>"></div>
|
||||
<?php } ?>
|
||||
<input type="file" class="form-control" name="file" accept="image/jpeg,image/png,image/gif,image/webp">
|
||||
<?php if ($asset_photo) { ?>
|
||||
<div class="form-check mt-2">
|
||||
<input class="form-check-input" type="checkbox" name="remove_photo" value="1" id="assetFormRemovePhoto">
|
||||
<label class="form-check-label" for="assetFormRemovePhoto">Remove this photo</label>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<label class="form-label">Notes</label>
|
||||
<textarea class="form-control" rows="4" placeholder="Configuration details, quirks, anything worth knowing" name="notes"><?= $asset_notes ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($asset_form_mode === 'edit') { ?>
|
||||
<p class="text-body-secondary small text-end mt-3 mb-0">Asset ID <span class="font-monospace"><?= $asset_id ?></span></p>
|
||||
<?php } ?>
|
||||
|
||||
</div>
|
||||
|
||||
<?php if ($asset_form_mode === 'edit') { ?>
|
||||
<div class="tab-pane fade" id="assetFormHistory">
|
||||
<?php if (mysqli_num_rows($sql_asset_history)) { ?>
|
||||
<ul class="list-group list-group-flush">
|
||||
<?php while ($row = mysqli_fetch_assoc($sql_asset_history)) { ?>
|
||||
<li class="list-group-item px-0">
|
||||
<div class="d-flex justify-content-between gap-2">
|
||||
<span><?= escapeHtml($row['asset_history_description']) ?></span>
|
||||
<?php if ($row['asset_history_status']) { ?>
|
||||
<?= assetStatusPill($row['asset_history_status']) ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<div class="small text-body-secondary" title="<?= escapeHtml($row['asset_history_created_at']) ?>"><?= timeAgo($row['asset_history_created_at']) ?> · <?= escapeHtml($row['asset_history_created_at']) ?></div>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<?php } else { ?>
|
||||
<p class="text-body-secondary text-center my-4">No history recorded yet.</p>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
itflowReady(function () {
|
||||
var form = document.getElementById('assetFormOs').closest('form');
|
||||
|
||||
// OS / firmware suggestions from what's already in use
|
||||
var osInput = form.querySelector('#assetFormOs');
|
||||
itflowAutocomplete(osInput, {
|
||||
minLength: 1,
|
||||
source: <?= json_encode($os_suggestions, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>,
|
||||
onSelect: function (item) {
|
||||
osInput.value = item.label;
|
||||
}
|
||||
});
|
||||
|
||||
// DHCP means no fixed address to type
|
||||
var dhcp = form.querySelector('#assetFormDhcp');
|
||||
var ip = form.querySelector('#assetFormIp');
|
||||
dhcp.addEventListener('change', function () {
|
||||
ip.disabled = dhcp.checked;
|
||||
if (!dhcp.checked) {
|
||||
ip.focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -18,6 +18,12 @@ if (isset($_POST['add_asset'])) {
|
||||
|
||||
enforceClientAccess();
|
||||
|
||||
// The picks have to be this client's own
|
||||
$location = assetClientOwnedId('location', $location, $client_id);
|
||||
$contact = assetClientOwnedId('contact', $contact, $client_id);
|
||||
$vendor = assetClientOwnedId('vendor', $vendor, $client_id);
|
||||
$network = assetClientOwnedId('network', $network, $client_id);
|
||||
|
||||
// Only the two credential fields this handler writes - name/description/uri here
|
||||
// belong to the asset, not the credential, and have their own column widths.
|
||||
// Checked before the asset is created, so an overlong credential can't leave a
|
||||
@@ -74,7 +80,7 @@ if (isset($_POST['add_asset'])) {
|
||||
|
||||
$credential_id = mysqli_insert_id($mysqli);
|
||||
|
||||
logAudit("Credential", "Create", "$session_name created login credential for asset $asset_name", $client_id, $credential_id);
|
||||
logAudit("Credential", "Create", "$session_name created login credential for asset $name", $client_id, $credential_id);
|
||||
|
||||
$alert_extended = " along with login credentials";
|
||||
|
||||
@@ -104,11 +110,17 @@ if (isset($_POST['edit_asset'])) {
|
||||
// Get Existing Photo and assigned client_id
|
||||
$sql = mysqli_query($mysqli,"SELECT asset_photo, asset_client_id FROM assets WHERE asset_id = $asset_id");
|
||||
$row = mysqli_fetch_assoc($sql);
|
||||
$existing_file_name = escapeSql($row['asset_photo']);
|
||||
$existing_file_name = $row['asset_photo'] ?? '';
|
||||
$client_id = intval($row['asset_client_id']);
|
||||
|
||||
enforceClientAccess();
|
||||
|
||||
// The picks have to be this client's own
|
||||
$location = assetClientOwnedId('location', $location, $client_id);
|
||||
$contact = assetClientOwnedId('contact', $contact, $client_id);
|
||||
$vendor = assetClientOwnedId('vendor', $vendor, $client_id);
|
||||
$network = assetClientOwnedId('network', $network, $client_id);
|
||||
|
||||
mysqli_query($mysqli,"UPDATE assets SET asset_name = '$name', asset_description = '$description', asset_type = '$type', asset_make = '$make', asset_model = '$model', asset_serial = '$serial', asset_os = '$os', asset_uri = '$uri', asset_uri_2 = '$uri_2', asset_uri_client = '$uri_client', asset_location_id = $location, asset_vendor_id = $vendor, asset_contact_id = $contact, asset_status = '$status', asset_purchase_reference = '$purchase_reference', asset_purchase_date = $purchase_date, asset_warranty_expire = $warranty_expire, asset_install_date = $install_date, asset_physical_location = '$physical_location', asset_notes = '$notes', asset_favorite = $favorite WHERE asset_id = $asset_id");
|
||||
|
||||
$sql_interfaces = mysqli_query($mysqli, "SELECT 1 FROM asset_interfaces WHERE interface_asset_id = $asset_id AND interface_primary = 1");
|
||||
@@ -121,20 +133,25 @@ if (isset($_POST['edit_asset'])) {
|
||||
mysqli_query($mysqli,"UPDATE asset_interfaces SET interface_mac = '$mac', interface_ip = '$ip', interface_nat_ip = '$nat_ip', interface_ipv6 = '$ipv6', interface_network_id = $network WHERE interface_asset_id = $asset_id AND interface_primary = 1");
|
||||
}
|
||||
|
||||
// Update Photo
|
||||
if ($new_file_name = checkFileUpload($_FILES['file'], array('jpg', 'jpeg', 'gif', 'png', 'webp'))) {
|
||||
// Photo - a new upload replaces the old file; Remove only counts when nothing new came in
|
||||
$existing_photo_path = $existing_file_name !== '' ? "../uploads/clients/$client_id/" . basename($existing_file_name) : '';
|
||||
if (isset($_FILES['file']) && $new_file_name = checkFileUpload($_FILES['file'], array('jpg', 'jpeg', 'gif', 'png', 'webp'))) {
|
||||
|
||||
// Set directory in which the uploaded file will be moved
|
||||
$file_tmp_path = $_FILES['file']['tmp_name'];
|
||||
$upload_file_dir = "../uploads/clients/$client_id/";
|
||||
$dest_path = $upload_file_dir . $new_file_name;
|
||||
if (!is_dir("../uploads/clients/$client_id")) {
|
||||
mkdir("../uploads/clients/$client_id", 0755, true);
|
||||
}
|
||||
move_uploaded_file($_FILES['file']['tmp_name'], "../uploads/clients/$client_id/$new_file_name");
|
||||
|
||||
move_uploaded_file($file_tmp_path, $dest_path);
|
||||
|
||||
//Delete old file
|
||||
unlink("../uploads/clients/$client_id/$existing_file_name");
|
||||
if ($existing_photo_path && is_file($existing_photo_path)) {
|
||||
unlink($existing_photo_path);
|
||||
}
|
||||
|
||||
mysqli_query($mysqli,"UPDATE assets SET asset_photo = '$new_file_name' WHERE asset_id = $asset_id");
|
||||
} elseif (!empty($_POST['remove_photo']) && $existing_photo_path) {
|
||||
if (is_file($existing_photo_path)) {
|
||||
unlink($existing_photo_path);
|
||||
}
|
||||
mysqli_query($mysqli,"UPDATE assets SET asset_photo = NULL WHERE asset_id = $asset_id");
|
||||
}
|
||||
|
||||
// Tags
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
<?php
|
||||
defined('FROM_POST_HANDLER') || die("Direct file access is not allowed");
|
||||
|
||||
$name = escapeSql($_POST['name']);
|
||||
$description = escapeSql($_POST['description']);
|
||||
$type = escapeSql($_POST['type']);
|
||||
$make = escapeSql($_POST['make']);
|
||||
$model = escapeSql($_POST['model']);
|
||||
$serial = escapeSql($_POST['serial']);
|
||||
$os = escapeSql($_POST['os']);
|
||||
$ip = escapeSql($_POST['ip']);
|
||||
$name = escapeSql($_POST['name'] ?? '');
|
||||
$description = escapeSql($_POST['description'] ?? '');
|
||||
$type = escapeSql($_POST['type'] ?? '');
|
||||
$make = escapeSql($_POST['make'] ?? '');
|
||||
$model = escapeSql($_POST['model'] ?? '');
|
||||
$serial = escapeSql($_POST['serial'] ?? '');
|
||||
$os = escapeSql($_POST['os'] ?? '');
|
||||
$ip = escapeSql($_POST['ip'] ?? '');
|
||||
$dhcp = intval($_POST['dhcp'] ?? 0);
|
||||
if ($dhcp == 1) {
|
||||
$ip = 'DHCP';
|
||||
}
|
||||
$ipv6 = escapeSql($_POST['ipv6']);
|
||||
$nat_ip = escapeSql($_POST['nat_ip']);
|
||||
$mac = escapeSql($_POST['mac']);
|
||||
$uri = escapeSql($_POST['uri']);
|
||||
$uri_2 = escapeSql($_POST['uri_2']);
|
||||
$uri_client = escapeSql($_POST['uri_client']);
|
||||
$status = escapeSql($_POST['status']);
|
||||
$ipv6 = escapeSql($_POST['ipv6'] ?? '');
|
||||
$nat_ip = escapeSql($_POST['nat_ip'] ?? '');
|
||||
$mac = escapeSql($_POST['mac'] ?? '');
|
||||
$uri = escapeSql($_POST['uri'] ?? '');
|
||||
$uri_2 = escapeSql($_POST['uri_2'] ?? '');
|
||||
$uri_client = escapeSql($_POST['uri_client'] ?? '');
|
||||
$status = escapeSql($_POST['status'] ?? '');
|
||||
$location = intval($_POST['location'] ?? 0);
|
||||
$physical_location = escapeSql($_POST['physical_location']);
|
||||
$physical_location = escapeSql($_POST['physical_location'] ?? '');
|
||||
$vendor = intval($_POST['vendor'] ?? 0);
|
||||
$contact = intval($_POST['contact'] ?? 0);
|
||||
$network = intval($_POST['network'] ?? 0);
|
||||
$purchase_reference = escapeSql($_POST['purchase_reference']);
|
||||
$purchase_date = escapeSql($_POST['purchase_date']);
|
||||
$purchase_reference = escapeSql($_POST['purchase_reference'] ?? '');
|
||||
$purchase_date = escapeSql($_POST['purchase_date'] ?? '');
|
||||
if (empty($purchase_date)) {
|
||||
$purchase_date = "NULL";
|
||||
} else {
|
||||
$purchase_date = "'" . $purchase_date . "'";
|
||||
}
|
||||
$warranty_expire = escapeSql($_POST['warranty_expire']);
|
||||
$warranty_expire = escapeSql($_POST['warranty_expire'] ?? '');
|
||||
if (empty($warranty_expire)) {
|
||||
$warranty_expire = "NULL";
|
||||
} else {
|
||||
$warranty_expire = "'" . $warranty_expire . "'";
|
||||
}
|
||||
$install_date = escapeSql($_POST['install_date']);
|
||||
$install_date = escapeSql($_POST['install_date'] ?? '');
|
||||
if (empty($install_date)) {
|
||||
$install_date = "NULL";
|
||||
} else {
|
||||
$install_date = "'" . $install_date . "'";
|
||||
}
|
||||
$notes = escapeSql($_POST['notes']);
|
||||
$notes = escapeSql($_POST['notes'] ?? '');
|
||||
$favorite = intval($_POST['favorite'] ?? 0);
|
||||
|
||||
@@ -3542,3 +3542,206 @@ button.itflow-rack-item-body:hover {
|
||||
break-inside: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assets - agent/assets.php, agent/asset.php, agent/modals/asset/
|
||||
|
||||
Each kind of asset has a colour (functions/asset.php assetTypeTone()), used
|
||||
for the icon tile on the list, the asset page header and the related-asset
|
||||
chips. The tile is the colour at low strength with the icon at full, so it
|
||||
reads in light and dark alike. */
|
||||
.itflow-asset-tone-workstation { --itflow-asset-tone: #3d72bb; }
|
||||
.itflow-asset-tone-server { --itflow-asset-tone: #6d5fc4; }
|
||||
.itflow-asset-tone-virtual { --itflow-asset-tone: #9a5cc6; }
|
||||
.itflow-asset-tone-network { --itflow-asset-tone: #23897a; }
|
||||
.itflow-asset-tone-firewall { --itflow-asset-tone: #c04e4e; }
|
||||
.itflow-asset-tone-wireless { --itflow-asset-tone: #1b8fc2; }
|
||||
.itflow-asset-tone-phone { --itflow-asset-tone: #b8862f; }
|
||||
.itflow-asset-tone-peripheral { --itflow-asset-tone: #5b6f8a; }
|
||||
.itflow-asset-tone-other { --itflow-asset-tone: #6c757d; }
|
||||
[data-bs-theme="dark"] .itflow-asset-tone-workstation { --itflow-asset-tone: #6f9fe0; }
|
||||
[data-bs-theme="dark"] .itflow-asset-tone-server { --itflow-asset-tone: #a197ee; }
|
||||
[data-bs-theme="dark"] .itflow-asset-tone-virtual { --itflow-asset-tone: #c595ea; }
|
||||
[data-bs-theme="dark"] .itflow-asset-tone-network { --itflow-asset-tone: #4fc4b0; }
|
||||
[data-bs-theme="dark"] .itflow-asset-tone-firewall { --itflow-asset-tone: #ec8585; }
|
||||
[data-bs-theme="dark"] .itflow-asset-tone-wireless { --itflow-asset-tone: #5cc0ec; }
|
||||
[data-bs-theme="dark"] .itflow-asset-tone-phone { --itflow-asset-tone: #e3b35c; }
|
||||
[data-bs-theme="dark"] .itflow-asset-tone-peripheral { --itflow-asset-tone: #9fb2cc; }
|
||||
[data-bs-theme="dark"] .itflow-asset-tone-other { --itflow-asset-tone: #adb5bd; }
|
||||
.itflow-asset-icon {
|
||||
display: inline-flex;
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border-radius: var(--bs-border-radius-lg);
|
||||
background-color: color-mix(in srgb, var(--itflow-asset-tone) 14%, transparent);
|
||||
color: var(--itflow-asset-tone);
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
.itflow-asset-icon-lg {
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.itflow-asset-icon-sm {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
border-radius: var(--bs-border-radius);
|
||||
font-size: .8rem;
|
||||
}
|
||||
img.itflow-asset-icon {
|
||||
object-fit: cover;
|
||||
background: none;
|
||||
}
|
||||
|
||||
/* Kind-of-asset tabs above the filter bar */
|
||||
.itflow-asset-tabs .nav-link {
|
||||
padding: .3rem .75rem;
|
||||
color: var(--bs-secondary-color);
|
||||
}
|
||||
.itflow-asset-tabs .nav-link .badge {
|
||||
background-color: var(--bs-secondary-bg);
|
||||
color: var(--bs-secondary-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
.itflow-asset-tabs .nav-link.active .badge {
|
||||
background-color: rgba(255, 255, 255, .25);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* List rows */
|
||||
.itflow-asset-table td {
|
||||
padding-top: .6rem;
|
||||
padding-bottom: .6rem;
|
||||
}
|
||||
.itflow-asset-name-cell {
|
||||
min-width: 15rem;
|
||||
}
|
||||
.itflow-asset-desc {
|
||||
max-width: 18rem;
|
||||
}
|
||||
.itflow-asset-model-cell {
|
||||
min-width: 9rem;
|
||||
}
|
||||
/* Tag pills and other controls inside a cell with a stretched-link stay clickable */
|
||||
.itflow-above-stretch {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.btn.btn-xs {
|
||||
--bs-btn-padding-y: 0;
|
||||
--bs-btn-padding-x: .4rem;
|
||||
--bs-btn-font-size: .7rem;
|
||||
--bs-btn-border-radius: var(--bs-border-radius-sm);
|
||||
font-size: .7rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Copy button beside an IP / MAC / serial - quiet until the row is pointed at */
|
||||
.itflow-copy {
|
||||
margin-left: .3rem;
|
||||
padding: 0 .15rem;
|
||||
border: 0;
|
||||
background: none;
|
||||
font-size: .8em;
|
||||
color: var(--bs-secondary-color);
|
||||
opacity: 0;
|
||||
transition: opacity .12s ease-in-out;
|
||||
}
|
||||
tr:hover .itflow-copy,
|
||||
.itflow-copy-always .itflow-copy,
|
||||
.itflow-copy:focus-visible {
|
||||
opacity: 1;
|
||||
}
|
||||
.itflow-copy:hover {
|
||||
color: var(--bs-emphasis-color);
|
||||
}
|
||||
@media (hover: none) {
|
||||
.itflow-copy {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Asset page */
|
||||
.itflow-asset-dl {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
gap: .45rem 1rem;
|
||||
font-size: .875rem;
|
||||
}
|
||||
.itflow-asset-dl dt {
|
||||
font-weight: 500;
|
||||
color: var(--bs-secondary-color);
|
||||
}
|
||||
.itflow-asset-dl dd {
|
||||
margin: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.itflow-asset-pills {
|
||||
gap: .25rem;
|
||||
}
|
||||
.itflow-asset-related .itflow-asset-pills .nav-link {
|
||||
padding: .3rem .7rem;
|
||||
font-size: .875rem;
|
||||
}
|
||||
.itflow-asset-related .itflow-asset-pills .nav-link:not(.active) {
|
||||
color: var(--bs-body-color);
|
||||
}
|
||||
.itflow-asset-related .itflow-asset-pills .nav-link.is-empty:not(.active) {
|
||||
color: var(--bs-secondary-color);
|
||||
}
|
||||
.itflow-asset-pills .nav-link .badge {
|
||||
background-color: var(--bs-secondary-bg);
|
||||
color: var(--bs-secondary-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
.itflow-asset-pills .nav-link .badge.text-bg-danger {
|
||||
background-color: var(--bs-danger) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.itflow-asset-pills .nav-link.active .badge {
|
||||
background-color: rgba(255, 255, 255, .25);
|
||||
color: inherit;
|
||||
}
|
||||
.itflow-asset-pane-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: .5rem;
|
||||
padding: .75rem 1rem;
|
||||
border-bottom: 1px solid var(--bs-border-color-translucent);
|
||||
}
|
||||
.itflow-asset-related .table > :not(caption) > * > :first-child {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.itflow-asset-related .table > :not(caption) > * > :last-child {
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.itflow-asset-related .table td:first-child {
|
||||
min-width: 11rem;
|
||||
}
|
||||
.itflow-asset-related .table td.checkbox-column {
|
||||
min-width: 0;
|
||||
}
|
||||
.itflow-asset-empty {
|
||||
padding: 2.5rem 1rem;
|
||||
text-align: center;
|
||||
color: var(--bs-secondary-color);
|
||||
}
|
||||
.itflow-asset-dl .font-monospace {
|
||||
font-size: .82rem;
|
||||
}
|
||||
|
||||
/* Section heading inside a long form (asset add/edit) */
|
||||
.itflow-form-section {
|
||||
margin-bottom: .75rem;
|
||||
padding-bottom: .35rem;
|
||||
border-bottom: 1px solid var(--bs-border-color-translucent);
|
||||
font-size: .75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: .06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--bs-secondary-color);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ require_once __DIR__ . '/functions/files.php';
|
||||
require_once __DIR__ . '/functions/domain.php';
|
||||
require_once __DIR__ . '/functions/network.php';
|
||||
require_once __DIR__ . '/functions/rack.php';
|
||||
require_once __DIR__ . '/functions/asset.php';
|
||||
require_once __DIR__ . '/functions/auth.php';
|
||||
require_once __DIR__ . '/functions/logging.php';
|
||||
require_once __DIR__ . '/functions/app.php';
|
||||
|
||||
241
functions/asset.php
Normal file
241
functions/asset.php
Normal file
@@ -0,0 +1,241 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Assets - how an asset is shown: the colour of its type, the badge for its
|
||||
* status, the icon for its operating system and how its warranty and age read.
|
||||
* Shared by the assets list (agent/assets.php), the asset page
|
||||
* (agent/asset.php) and the quick-look modal (agent/modals/asset/asset.php).
|
||||
*
|
||||
* Everything here takes raw values and returns plain strings or arrays of
|
||||
* them - escape anything that came from the database before echoing it.
|
||||
*/
|
||||
|
||||
/** The colour family an asset type is drawn in - a CSS class suffix (itflow-asset-tone-*). */
|
||||
function assetTypeTone($asset_type) {
|
||||
$tones = [
|
||||
'Laptop' => 'workstation',
|
||||
'Desktop' => 'workstation',
|
||||
'Server' => 'server',
|
||||
'Virtual Machine' => 'virtual',
|
||||
'Switch' => 'network',
|
||||
'Firewall/Router' => 'firewall',
|
||||
'Access Point' => 'wireless',
|
||||
'Phone' => 'phone',
|
||||
'Mobile Phone' => 'phone',
|
||||
'Tablet' => 'phone',
|
||||
'Printer' => 'peripheral',
|
||||
'Display' => 'peripheral',
|
||||
'Camera' => 'peripheral',
|
||||
];
|
||||
return $tones[(string) $asset_type] ?? 'other';
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap colour for an asset status. A colour set on the status in
|
||||
* Admin > Categories wins; otherwise the built-in statuses get a sensible
|
||||
* default and anything else is grey.
|
||||
*
|
||||
* Returns ['class' => bootstrap tone or '', 'style' => inline background or '']
|
||||
*/
|
||||
function assetStatusBadge($status, $category_color = '') {
|
||||
$category_color = trim((string) $category_color);
|
||||
if (preg_match('/^(#[0-9a-f]{3,8}|[a-z]{3,24})$/i', $category_color)) {
|
||||
return ['class' => '', 'style' => "background-color: $category_color"];
|
||||
}
|
||||
|
||||
$defaults = [
|
||||
'Deployed' => 'success',
|
||||
'Ready to Deploy' => 'info',
|
||||
'Out for Repair' => 'warning',
|
||||
'Lost' => 'danger',
|
||||
'Stolen' => 'danger',
|
||||
'Retired' => 'secondary',
|
||||
];
|
||||
return ['class' => $defaults[(string) $status] ?? 'secondary', 'style' => ''];
|
||||
}
|
||||
|
||||
/** Status colours from Admin > Categories, keyed by status name (raw). One query per page. */
|
||||
function assetStatusColors() {
|
||||
global $mysqli;
|
||||
static $colors = null;
|
||||
|
||||
if ($colors === null) {
|
||||
$colors = [];
|
||||
$sql = mysqli_query($mysqli, "SELECT category_name, category_color FROM categories WHERE category_type = 'asset_status' AND category_archived_at IS NULL");
|
||||
while ($row = mysqli_fetch_assoc($sql)) {
|
||||
$colors[$row['category_name']] = (string) $row['category_color'];
|
||||
}
|
||||
}
|
||||
|
||||
return $colors;
|
||||
}
|
||||
|
||||
/** The status badge, ready to echo. */
|
||||
function assetStatusPill($status) {
|
||||
$status = (string) $status;
|
||||
if ($status === '') {
|
||||
return '';
|
||||
}
|
||||
$badge = assetStatusBadge($status, assetStatusColors()[$status] ?? '');
|
||||
$class = $badge['class'] ? "text-bg-{$badge['class']}" : 'text-white';
|
||||
$style = $badge['style'] ? " style=\"{$badge['style']}\"" : '';
|
||||
return "<span class=\"badge rounded-pill fw-normal $class\"$style>" . escapeHtml($status) . "</span>";
|
||||
}
|
||||
|
||||
/** A brand icon for the operating system, where one is recognisable (Font Awesome 5 class). */
|
||||
function assetOsIcon($os) {
|
||||
$os = strtolower((string) $os);
|
||||
$icons = [
|
||||
'windows' => 'fab fa-windows',
|
||||
'macos' => 'fab fa-apple',
|
||||
'mac os' => 'fab fa-apple',
|
||||
'ios' => 'fab fa-apple',
|
||||
'ipados' => 'fab fa-apple',
|
||||
'android' => 'fab fa-android',
|
||||
'ubuntu' => 'fab fa-ubuntu',
|
||||
'red hat' => 'fab fa-redhat',
|
||||
'rhel' => 'fab fa-redhat',
|
||||
'centos' => 'fab fa-centos',
|
||||
'fedora' => 'fab fa-fedora',
|
||||
'suse' => 'fab fa-suse',
|
||||
'freebsd' => 'fab fa-freebsd',
|
||||
'pfsense' => 'fab fa-freebsd',
|
||||
'opnsense' => 'fab fa-freebsd',
|
||||
'truenas' => 'fab fa-freebsd',
|
||||
'raspberry' => 'fab fa-raspberry-pi',
|
||||
'linux' => 'fab fa-linux',
|
||||
'debian' => 'fab fa-linux',
|
||||
'mint' => 'fab fa-linux',
|
||||
'proxmox' => 'fab fa-linux',
|
||||
'pop!_os' => 'fab fa-linux',
|
||||
'firmware' => 'fas fa-microchip',
|
||||
'routeros' => 'fas fa-microchip',
|
||||
];
|
||||
foreach ($icons as $needle => $icon) {
|
||||
if (strpos($os, $needle) !== false) {
|
||||
return $icon;
|
||||
}
|
||||
}
|
||||
return 'fas fa-compact-disc';
|
||||
}
|
||||
|
||||
/**
|
||||
* Where the warranty stands, or null when there's no date.
|
||||
*
|
||||
* Returns ['tone' => danger|warning|success, 'short' => "Expired" / "23 days left" / "Covered",
|
||||
* 'text' => "Expired 3 months ago" / "Expires in 23 days" / "Until 2028-11-08",
|
||||
* 'days' => days left (negative once expired)]
|
||||
*/
|
||||
function assetWarranty($warranty_expire, $soon_days = 90) {
|
||||
$warranty_expire = trim((string) $warranty_expire);
|
||||
if ($warranty_expire === '' || strpos($warranty_expire, '0000-00-00') === 0) {
|
||||
return null;
|
||||
}
|
||||
$expires = DateTime::createFromFormat('!Y-m-d', substr($warranty_expire, 0, 10));
|
||||
if (!$expires) {
|
||||
return null;
|
||||
}
|
||||
$today = new DateTime('today');
|
||||
$days = (int) $today->diff($expires)->format('%r%a');
|
||||
$date = $expires->format('Y-m-d');
|
||||
|
||||
if ($days < 0) {
|
||||
return ['tone' => 'danger', 'short' => 'Expired', 'text' => 'Expired ' . timeAgo($date), 'days' => $days, 'date' => $date];
|
||||
}
|
||||
if ($days <= $soon_days) {
|
||||
$left = $days === 0 ? 'today' : ($days === 1 ? '1 day left' : "$days days left");
|
||||
return ['tone' => 'warning', 'short' => $left, 'text' => $days === 0 ? 'Expires today' : "Expires in $days day" . ($days === 1 ? '' : 's'), 'days' => $days, 'date' => $date];
|
||||
}
|
||||
return ['tone' => 'success', 'short' => 'Covered', 'text' => "Until $date", 'days' => $days, 'date' => $date];
|
||||
}
|
||||
|
||||
/** "3 yrs 2 mos" since a date, or '' without one. For purchase / install dates. */
|
||||
function assetAge($date) {
|
||||
$date = trim((string) $date);
|
||||
if ($date === '' || strpos($date, '0000-00-00') === 0) {
|
||||
return '';
|
||||
}
|
||||
$from = DateTime::createFromFormat('!Y-m-d', substr($date, 0, 10));
|
||||
if (!$from) {
|
||||
return '';
|
||||
}
|
||||
$diff = $from->diff(new DateTime('today'));
|
||||
if ($diff->invert) {
|
||||
return '';
|
||||
}
|
||||
$parts = [];
|
||||
if ($diff->y) {
|
||||
$parts[] = $diff->y . ' yr' . ($diff->y === 1 ? '' : 's');
|
||||
}
|
||||
if ($diff->m) {
|
||||
$parts[] = $diff->m . ' mo' . ($diff->m === 1 ? '' : 's');
|
||||
}
|
||||
if (!$parts) {
|
||||
return $diff->d <= 1 ? 'new' : $diff->d . ' days';
|
||||
}
|
||||
return implode(' ', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* The asset's links (URI, URI 2, client URI) as [label, url] pairs, skipping
|
||||
* empty ones. Raw - escapeUrl() each url before it goes in an href.
|
||||
*/
|
||||
function assetLinks($uri, $uri_2, $uri_client) {
|
||||
$links = [];
|
||||
foreach ([['Open', $uri], ['Open 2', $uri_2], ['Client link', $uri_client]] as [$label, $url]) {
|
||||
$url = trim((string) $url);
|
||||
if ($url !== '') {
|
||||
$links[] = [$label, $url];
|
||||
}
|
||||
}
|
||||
return $links;
|
||||
}
|
||||
|
||||
/** A short name for a link, by what it opens: "Web", "SSH", "RDP"... The full URL goes in its title. */
|
||||
function assetLinkLabel($url) {
|
||||
$scheme = strtolower((string) parse_url((string) $url, PHP_URL_SCHEME));
|
||||
$labels = ['http' => 'Web', 'https' => 'Web', 'ssh' => 'SSH', 'telnet' => 'Telnet', 'rdp' => 'RDP', 'vnc' => 'VNC',
|
||||
'ftp' => 'FTP', 'sftp' => 'SFTP', 'smb' => 'SMB'];
|
||||
return $labels[$scheme] ?? ($scheme !== '' ? strtoupper($scheme) : 'Link');
|
||||
}
|
||||
|
||||
/** Icon for a link by its scheme. */
|
||||
function assetLinkIcon($url) {
|
||||
$scheme = strtolower((string) parse_url((string) $url, PHP_URL_SCHEME));
|
||||
$icons = [
|
||||
'ssh' => 'fas fa-terminal',
|
||||
'telnet' => 'fas fa-terminal',
|
||||
'rdp' => 'fas fa-desktop',
|
||||
'vnc' => 'fas fa-desktop',
|
||||
'ftp' => 'fas fa-folder-open',
|
||||
'sftp' => 'fas fa-folder-open',
|
||||
'smb' => 'fas fa-folder-open',
|
||||
'http' => 'fas fa-globe',
|
||||
'https' => 'fas fa-globe',
|
||||
];
|
||||
return $icons[$scheme] ?? 'fas fa-external-link-alt';
|
||||
}
|
||||
|
||||
/**
|
||||
* $id if that location / contact / vendor / network belongs to the client,
|
||||
* else 0. The asset handlers run their picks through this so a hand-edited
|
||||
* POST can't hang another client's contact or location off an asset.
|
||||
*/
|
||||
function assetClientOwnedId($kind, $id, $client_id) {
|
||||
global $mysqli;
|
||||
|
||||
$tables = [
|
||||
'location' => ['locations', 'location_id', 'location_client_id'],
|
||||
'contact' => ['contacts', 'contact_id', 'contact_client_id'],
|
||||
'vendor' => ['vendors', 'vendor_id', 'vendor_client_id'],
|
||||
'network' => ['networks', 'network_id', 'network_client_id'],
|
||||
];
|
||||
$id = intval($id);
|
||||
$client_id = intval($client_id);
|
||||
if (!$id || !isset($tables[$kind])) {
|
||||
return 0;
|
||||
}
|
||||
[$table, $id_column, $client_column] = $tables[$kind];
|
||||
$sql = mysqli_query($mysqli, "SELECT 1 FROM $table WHERE $id_column = $id AND $client_column = $client_id LIMIT 1");
|
||||
return mysqli_num_rows($sql) ? $id : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user