Files
itflow/agent/domains.php
johnnyq b5dc8883f1 Tags stage 1: Admin > Tags rebuilt with the seven kinds in a side nav with counts, each tag shown as it looks with its description, how many records have it (and how many of those are archived) linking to the list filtered by it, and the recurring invoice lines that count by it, search by name or description, sort by name or most used, Current and Archived tabs, Edit, Merge into, Archive, Restore and Delete per tag and bulk merge, archive, restore and delete; new shared tag window with live preview, description, color swatches or any color or hex, searchable icon grid or any Font Awesome icon by name, and kind changeable only while unused; merge window that moves records onto the kept tag once each and repoints recurring seat lines; archiving now works, with archived tags kept on their records but left out of record forms, the tag picker and the list and export filters unless already chosen; tag names unique within their kind, though tags that already share a name can still be saved; delete refused while a recurring seat line counts by the tag; record forms let admins type a new tag inline, replacing the + admin window that reloaded the page and failed for non-admins, and every kind now saves through one tagSetForRecord that keeps only its own kind's tags, each once, fixing the error on a tag sent twice for clients, contacts and assets; typing an archived tag's name restores it, and the picker refuses putting an archived tag on more records; recurring line window keeps its tag once archived; pills pick dark or white text by luminance and carry the description; removes the legacy agent tag handler and tag_model files; database 3.1.3 adds tag_description and normalises stored tag colors to hex and icons to bare names; changelog updated
2026-10-17 13:59:13 -04:00

505 lines
31 KiB
PHP

<?php
/*
* Domains - one client's, or every domain the agent can see from All Client
* Docs. Starred first. Each row has when it expires (and whether it renews
* by itself), who registers it and who hosts DNS, the website and email, and
* how its email is protected (SPF, DMARC, DKIM). The pills along the top
* count and filter the ones to act on: expiring soon, expired, email gaps.
*/
// Default Column Sortby Filter
$sort = "domain_name";
$order = "ASC";
// If client_id is in URI then show client Side Bar and client header
if (isset($_GET['client_id'])) {
require_once "includes/inc_all_client.php";
$client_query = "AND domain_client_id = $client_id";
$client_url = "client_id=$client_id&";
$archived = isset($_GET['archived']) && $_GET['archived'] == 1 ? 1 : 0;
$archive_query = $archived ? "domain_archived_at IS NOT NULL" : "domain_archived_at IS NULL";
} else {
require_once "includes/inc_client_overview_all.php";
$client_query = '';
$client_url = '';
$archived = isset($_GET['archived']) && $_GET['archived'] == 1 ? 1 : 0;
$archive_query = $archived ? "(client_archived_at IS NOT NULL OR domain_archived_at IS NOT NULL)" : "(client_archived_at IS NULL AND domain_archived_at IS NULL)";
}
// Perms
enforceUserPermission('module_support');
$can_edit = domainCan(2);
$can_delete = domainCan(3) && !empty($config_destructive_deletes_enable);
// Client Filter (all clients)
$client = '';
if (!$client_url && !empty($_GET['client'])) {
$client = intval($_GET['client']);
$client_query = "AND domain_client_id = $client";
}
// Vendor Filter - the registrar or any of the hosts
$vendor_filter = intval($_GET['vendor'] ?? 0);
$vendor_query = $vendor_filter ? "AND $vendor_filter IN (domain_registrar, domain_dnshost, domain_webhost, domain_mailhost)" : '';
// Tags Filter - a domain with any of them
if (isset($_GET['tags']) && is_array($_GET['tags']) && !empty($_GET['tags'])) {
$tag_filter = implode(",", array_map('intval', $_GET['tags']));
$tag_query = "AND domains.domain_id IN (SELECT domain_id FROM domain_tags WHERE tag_id IN ($tag_filter))";
} else {
$tag_filter = 0;
$tag_query = '';
}
// Which ones - the pills along the top. The old "Expiring in" filter
// (?expire_days=30 from the dashboard and the expiry notifications) still works.
$soon_days = DOMAIN_SOON_DAYS;
$shows = [
'' => ['label' => 'All', 'where' => ''],
'starred' => ['label' => 'Starred', 'icon' => 'fa-star', 'where' => 'domains.domain_favorite = 1'],
'soon' => ['label' => "Expiring in $soon_days days", 'icon' => 'fa-hourglass-half', 'where' => "domains.domain_expire BETWEEN CURDATE() AND CURDATE() + INTERVAL $soon_days DAY"],
'expired' => ['label' => 'Expired', 'icon' => 'fa-calendar-times', 'where' => 'domains.domain_expire < CURDATE()'],
'noauto' => ['label' => 'No auto-renew', 'icon' => 'fa-hand-paper', 'where' => 'domains.domain_auto_renew = 0'],
'mail' => ['label' => 'Email gaps', 'icon' => 'fa-envelope-open-text', 'where' => domainMailGapSql()],
];
$show = isset($shows[$_GET['show'] ?? '']) ? ($_GET['show'] ?? '') : '';
$show_query = $shows[$show]['where'] ? 'AND ' . $shows[$show]['where'] : '';
$expire_days = $_GET['expire_days'] ?? '';
$expire_query = '';
if ($expire_days === 'expired') {
$expire_query = 'AND domains.domain_expire < CURDATE()';
} elseif (intval($expire_days) > 0) {
$expire_query = 'AND domains.domain_expire BETWEEN CURDATE() AND CURDATE() + INTERVAL ' . intval($expire_days) . ' DAY';
}
// Only these sort - filter_header.php will hand through any lowercase word, and
// an unknown column takes the page down with a mysqli exception
$sortable_columns = ['domain_name' => 'domains.domain_name', 'domain_expire' => 'domains.domain_expire', 'registrar_name' => 'registrar.vendor_name',
'client_name' => 'client_name', 'domain_created_at' => 'domains.domain_created_at', 'domain_refreshed_at' => 'domains.domain_refreshed_at'];
if (!isset($sortable_columns[$sort])) {
$sort = 'domain_name';
}
// Domains without an expiry go last whichever way the expiry is sorted
$sort_sql = ($sort === 'domain_expire' ? 'domains.domain_expire IS NULL, ' : '') . $sortable_columns[$sort] . " $order";
[$domain_columns, $domain_joins] = domainSelectSql();
$search_query = "AND (domains.domain_name LIKE '%$q%' OR domains.domain_description LIKE '%$q%' OR domains.domain_notes LIKE '%$q%'
OR registrar.vendor_name LIKE '%$q%' OR dnshost.vendor_name LIKE '%$q%' OR mailhost.vendor_name LIKE '%$q%' OR webhost.vendor_name LIKE '%$q%' OR client_name LIKE '%$q%'
OR domains.domain_id IN (SELECT domain_id FROM domain_tags JOIN tags USING (tag_id) WHERE tag_name LIKE '%$q%'))";
$scope_query = clientScopeSql('domain_client_id');
$filters = "$archive_query $tag_query $vendor_query $search_query $expire_query $scope_query $client_query";
$sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS $domain_columns
FROM domains $domain_joins
WHERE $filters $show_query
ORDER BY domains.domain_favorite DESC, $sort_sql, domains.domain_name ASC LIMIT $record_from, $record_to");
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
$domains = [];
while ($row = mysqli_fetch_assoc($sql)) {
$domains[intval($row['domain_id'])] = $row;
}
$domains_tags = tagsForRecords('domain', array_keys($domains));
// How many of each, for the pills - everything in scope, before the pill itself
$count_sql = [];
foreach ($shows as $key => $s) {
$count_sql[] = $key === '' ? 'COUNT(*)' : "COALESCE(SUM({$s['where']}), 0)";
}
$row = mysqli_fetch_row(mysqli_query($mysqli, "SELECT " . implode(', ', $count_sql) . " FROM domains $domain_joins WHERE $filters"));
$counts = [];
foreach (array_keys($shows) as $i => $key) {
$counts[$key] = intval($row[$i]);
}
$filter_url = function ($changes) {
$params = array_merge($_GET, $changes);
unset($params['page']);
return '?' . http_build_query(array_filter($params, function ($v) { return $v !== '' && $v !== null; }));
};
// Vendors for the filter - the ones registering or hosting a domain in this scope
$sql_vendors_filter = mysqli_query($mysqli, "SELECT DISTINCT vendor_id, vendor_name, vendor_description, vendor_client_id FROM vendors
JOIN domains ON vendor_id IN (domain_registrar, domain_dnshost, domain_webhost, domain_mailhost)
LEFT JOIN clients ON client_id = domain_client_id
WHERE $archive_query $scope_query $client_query
ORDER BY vendor_name ASC");
// Vendors the bulk "set registrar or host" offers - the client's and ours, or only ours across clients
$bulk_vendor_options = $can_edit ? domainVendorChoices($client_url ? $client_id : 0) : [];
?>
<div class="card">
<div class="card-header bg-dark py-2">
<h3 class="card-title mt-2"><i class="fa fa-fw fa-globe me-2"></i>Domains</h3>
<div class="card-tools">
<?php if ($can_edit || $num_rows[0]) { ?>
<div class="btn-group">
<?php if ($can_edit) { ?>
<button type="button" class="btn btn-primary ajax-modal" data-modal-size="lg" data-modal-url="modals/domain/domain_add.php?<?= $client_url ?>">
<i class="fas fa-plus me-2"></i>New Domain
</button>
<?php } ?>
<?php if ($num_rows[0] > 0) { ?>
<button type="button" class="btn btn-primary dropdown-toggle <?= $can_edit ? 'dropdown-toggle-split' : '' ?>" data-bs-toggle="dropdown"><?= $can_edit ? '' : '<i class="fas fa-ellipsis-h"></i>' ?></button>
<div class="dropdown-menu dropdown-menu-end">
<a class="dropdown-item text-dark ajax-modal" href="#"
data-modal-url="<?= buildExportModalUrl('modals/domain/domain_export.php', ['client_id', 'client', 'expire_days', 'archived', 'q', 'tags', 'vendor']) ?>">
<i class="fa fa-fw fa-download me-2"></i>Export
</a>
</div>
<?php } ?>
</div>
<?php } ?>
</div>
</div>
<div class="card-header py-3">
<form autocomplete="off">
<?php if ($client_url) { ?>
<input type="hidden" name="client_id" value="<?= $client_id ?>">
<?php } ?>
<input type="hidden" name="archived" value="<?= $archived ?>">
<input type="hidden" name="show" value="<?= escapeHtml($show) ?>">
<div class="row g-2 align-items-center">
<div class="col-lg-3 col-md-6">
<div class="input-group">
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(escapeHtml($q)); } ?>" placeholder="Search domain, registrar, host, notes...">
<button class="btn btn-dark" title="Search"><i class="fa fa-search"></i></button>
</div>
</div>
<div class="col-lg-3 col-md-6">
<select onchange="this.form.submit()" class="form-select select2" name="tags[]" data-placeholder="- Tags -" multiple>
<?php
// Tags in use on domains in this scope, plus any already picked
$sql_tags_filter = mysqli_query($mysqli, "
SELECT tag_id, tag_name, tag_color, tag_icon FROM tags
WHERE tag_type = 7 AND (tag_archived_at IS NULL OR tag_id IN ($tag_filter))
AND (
EXISTS (SELECT 1 FROM domain_tags JOIN domains ON domains.domain_id = domain_tags.domain_id
WHERE domain_tags.tag_id = tags.tag_id " . clientScopeSql('domain_client_id') . " $client_query)
OR tag_id IN ($tag_filter)
)
ORDER BY tag_name ASC
");
while ($row = mysqli_fetch_assoc($sql_tags_filter)) {
$tag_id = intval($row['tag_id']); ?>
<option value="<?= $tag_id ?>" <?php if (isset($_GET['tags']) && is_array($_GET['tags']) && in_array($tag_id, $_GET['tags'])) { echo 'selected'; } ?><?= selectOptionColor($row['tag_color'] ?? '', $row['tag_icon'] ?? '') ?>><?= escapeHtml($row['tag_name']) ?></option>
<?php } ?>
</select>
</div>
<div class="<?= $client_url ? 'col-lg-4' : 'col-lg-2' ?> col-md-6">
<select class="form-select select2" name="vendor" onchange="this.form.submit()">
<option value="">- Any registrar or host -</option>
<?php while ($row = mysqli_fetch_assoc($sql_vendors_filter)) {
$vendor_id = intval($row['vendor_id']); ?>
<option <?= $vendor_filter === $vendor_id ? 'selected' : '' ?> value="<?= $vendor_id ?>"<?= selectOptionVendor($row['vendor_name'], intval($row['vendor_client_id']) === 0 ? 'Ours' : $row['vendor_description']) ?>><?= escapeHtml($row['vendor_name']) ?></option>
<?php } ?>
</select>
</div>
<?php if (!$client_url) { ?>
<div class="col-lg-2 col-md-6">
<select class="form-select select2" name="client" onchange="this.form.submit()">
<option value="" <?php if ($client == "") { echo "selected"; } ?>>- All Clients -</option>
<?php
$sql_clients_filter = mysqli_query($mysqli, "
SELECT DISTINCT client_id, client_name, client_abbreviation
FROM clients
JOIN domains ON domain_client_id = client_id
WHERE $archive_query
" . clientScopeSql('clients.client_id') . "
ORDER BY client_name ASC
");
while ($row = mysqli_fetch_assoc($sql_clients_filter)) {
$client_id_filter = intval($row['client_id']); ?>
<option <?php if ($client == $client_id_filter) { echo "selected"; } ?> value="<?= $client_id_filter ?>"<?= selectOptionClient($row['client_id'], $row['client_name'], $row['client_abbreviation'] ?? '') ?>><?= escapeHtml($row['client_name']) ?></option>
<?php } ?>
</select>
</div>
<?php } ?>
<div class="col-lg-2 col-md-6">
<div class="d-flex justify-content-end gap-2">
<a href="<?= escapeHtml($filter_url(['archived' => $archived ? '' : 1])) ?>"
class="btn <?= $archived ? 'btn-dark' : 'btn-outline-dark' ?>" title="<?= $archived ? 'Back to active domains' : 'Show archived domains' ?>">
<i class="fa fa-fw fa-archive"></i><span class="d-lg-none ms-2">Archived</span>
</a>
<?php if ($can_edit) { ?>
<div class="dropdown" id="bulkActionButton" hidden>
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
<i class="fas fa-fw fa-layer-group me-2"></i>Bulk Action (<span id="selectedCount">0</span>)
</button>
<div class="dropdown-menu dropdown-menu-end">
<?php if (!$archived) { ?>
<button class="dropdown-item" type="submit" form="bulkActions" name="bulk_refresh_domains">
<i class="fas fa-fw fa-sync-alt me-2"></i>Look up now
</button>
<a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#domainBulkVendorModal">
<i class="fas fa-fw fa-building me-2"></i>Set registrar or host
</a>
<?php } ?>
<a class="dropdown-item" href="#" data-tag-picker data-tag-entity="domain" data-tag-bulk data-csrf="<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-tags me-2"></i>Tags
</a>
<button class="dropdown-item" type="submit" form="bulkActions" name="bulk_favorite_domains">
<i class="fas fa-fw fa-star text-warning me-2"></i>Star
</button>
<button class="dropdown-item" type="submit" form="bulkActions" name="bulk_unfavorite_domains">
<i class="far fa-fw fa-star me-2"></i>Unstar
</button>
<div class="dropdown-divider"></div>
<?php if ($archived) { ?>
<button class="dropdown-item text-info" type="submit" form="bulkActions" name="bulk_restore_domains">
<i class="fas fa-fw fa-redo me-2"></i>Restore
</button>
<?php if ($can_delete) { ?>
<button class="dropdown-item text-danger text-bold confirm-link" type="submit" form="bulkActions" name="bulk_delete_domains"
data-confirm-text="The domains go for good, with their history. Services and certificates stop pointing at them.">
<i class="fas fa-fw fa-trash me-2"></i>Delete
</button>
<?php } ?>
<?php } else { ?>
<button class="dropdown-item text-danger confirm-link" type="submit" form="bulkActions" name="bulk_archive_domains">
<i class="fas fa-fw fa-archive me-2"></i>Archive
</button>
<?php } ?>
</div>
</div>
<?php } ?>
</div>
</div>
</div>
</form>
<!-- Which ones - counted over what the filters above leave -->
<nav class="nav nav-pills itflow-asset-tabs mt-2 flex-nowrap overflow-auto">
<?php foreach ($shows as $key => $s) {
$alarm = $counts[$key] && in_array($key, ['soon', 'expired', 'mail'], true); ?>
<a class="nav-link text-nowrap <?= $show === $key ? 'active' : '' ?>" href="<?= escapeHtml($filter_url(['show' => $key, 'expire_days' => ''])) ?>">
<?php if (!empty($s['icon'])) { ?><i class="fas fa-fw <?= $s['icon'] ?> me-1"></i><?php } ?><?= $s['label'] ?>
<span class="badge rounded-pill ms-1 <?= $alarm ? ($key === 'expired' ? 'text-bg-danger' : 'text-bg-warning') : '' ?>"><?= $counts[$key] ?></span>
</a>
<?php } ?>
</nav>
<?php if ($expire_query) { ?>
<div class="small text-body-secondary mt-2">
<i class="fas fa-filter me-1"></i><?= $expire_days === 'expired' ? 'Expired' : 'Expiring in ' . intval($expire_days) . ' days' ?>
<a class="ms-2" href="<?= escapeHtml($filter_url(['expire_days' => ''])) ?>">Clear</a>
</div>
<?php } ?>
</div>
<form id="bulkActions" action="post.php" method="post">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0 itflow-asset-table">
<thead class="<?php if (!$num_rows[0]) { echo "d-none"; } ?> text-nowrap table-light">
<tr>
<?php if ($can_edit) { ?>
<th class="checkbox-column border-end">
<div class="form-check">
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)" title="Select all on this page">
</div>
</th>
<?php } ?>
<th>
<a class="text-reset text-decoration-none" href="?<?= $url_query_strings_sort ?>&sort=domain_name&order=<?= $disp ?>">
Domain <?php if ($sort == 'domain_name') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-reset text-decoration-none" href="?<?= $url_query_strings_sort ?>&sort=domain_expire&order=<?= $disp ?>">
Expires <?php if ($sort == 'domain_expire') { echo $order_icon; } ?>
</a>
</th>
<th>
<a class="text-reset text-decoration-none" href="?<?= $url_query_strings_sort ?>&sort=registrar_name&order=<?= $disp ?>">
Registrar and hosts <?php if ($sort == 'registrar_name') { echo $order_icon; } ?>
</a>
</th>
<th>Email</th>
<?php if (!$client_url) { ?>
<th>
<a class="text-reset text-decoration-none" href="?<?= $url_query_strings_sort ?>&sort=client_name&order=<?= $disp ?>">
Client <?php if ($sort == 'client_name') { echo $order_icon; } ?>
</a>
</th>
<?php } ?>
<th></th>
</tr>
</thead>
<tbody>
<?php
foreach ($domains as $domain_id => $row) {
$row_client_id = intval($row['domain_client_id']);
$domain_name = escapeHtml($row['domain_name']);
$domain_description = escapeHtml($row['domain_description']);
$domain_favorite = intval($row['domain_favorite']);
$domain_archived_at = escapeHtml($row['domain_archived_at']);
$domain_tags = $domains_tags[$domain_id] ?? [];
$expiry = domainExpiry($row['domain_expire'], $row['domain_auto_renew']);
$quick_url = "modals/domain/domain.php?id=$domain_id";
?>
<tr>
<?php if ($can_edit) { ?>
<td class="checkbox-column bg-light border-end">
<div class="form-check">
<input class="form-check-input bulk-select" type="checkbox" name="domain_ids[]" value="<?= $domain_id ?>" aria-label="Select <?= $domain_name ?>">
</div>
</td>
<?php } ?>
<td class="itflow-asset-name-cell">
<div class="d-flex align-items-center gap-3">
<?= domainTile() ?>
<div class="min-w-0">
<a class="fw-semibold text-body ajax-modal" href="#" data-modal-size="lg" data-modal-url="<?= $quick_url ?>"><?= $domain_name ?></a>
<?php if ($domain_favorite) { ?><i class="fas fa-star text-warning small ms-1" title="Starred"></i><?php } ?>
<?php if ($domain_archived_at) { ?><span class="badge rounded-pill text-bg-secondary fw-normal ms-1">Archived</span><?php } ?>
<?php if ($domain_description) { ?>
<div class="small text-body-secondary text-truncate itflow-asset-desc" title="<?= $domain_description ?>"><?= $domain_description ?></div>
<?php } ?>
<?php if ($domain_tags || $can_edit) { ?>
<div class="mt-1"><?= tagPills('domain', $domain_id, $domain_tags, "domains.php?$client_url", $can_edit) ?></div>
<?php } ?>
</div>
</div>
</td>
<td class="text-nowrap">
<?= domainExpiryPill($expiry) ?>
<?php if ($expiry) { ?>
<div class="small text-body-secondary mt-1"><?= escapeHtml($expiry['date']) ?></div>
<?php } ?>
</td>
<td class="small itflow-domain-who">
<?php $who = domainVendorsHtml($row); ?>
<?= $who ? '<div class="d-flex flex-column gap-1">' . $who . '</div>' : '<span class="text-body-secondary">-</span>' ?>
</td>
<td class="itflow-domain-mail"><?= domainMailPillsHtml(array_diff_key(domainMailChecks($row), ['mx' => 1])) ?></td>
<?php if (!$client_url) { ?>
<td><a href="domains.php?client_id=<?= $row_client_id ?>"><?= escapeHtml($row['client_name']) ?></a></td>
<?php } ?>
<td class="text-end">
<div class="dropdown dropstart">
<button class="btn btn-sm btn-light" title="More actions" type="button" data-bs-toggle="dropdown"><i class="fas fa-fw fa-ellipsis-v"></i></button>
<div class="dropdown-menu">
<a class="dropdown-item ajax-modal" href="#" data-modal-size="lg" data-modal-url="<?= $quick_url ?>">
<i class="fas fa-fw fa-eye text-secondary me-2"></i>Quick look
</a>
<a class="dropdown-item" href="https://<?= $domain_name ?>" target="_blank" rel="noopener noreferrer">
<i class="fas fa-fw fa-external-link-alt text-secondary me-2"></i>Open the website
</a>
<?php if ($can_edit && !$domain_archived_at) { ?>
<a class="dropdown-item ajax-modal" href="#" data-modal-size="lg" data-modal-url="modals/domain/domain_edit.php?id=<?= $domain_id ?>">
<i class="fas fa-fw fa-edit text-secondary me-2"></i>Edit
</a>
<a class="dropdown-item" href="post.php?refresh_domain=<?= $domain_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-sync-alt text-secondary me-2"></i>Look up now
</a>
<a class="dropdown-item" href="post.php?<?= $domain_favorite ? 'unfavorite_domain' : 'favorite_domain' ?>=<?= $domain_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="<?= $domain_favorite ? 'far' : 'fas' ?> fa-fw fa-star text-secondary me-2"></i><?= $domain_favorite ? 'Unstar' : 'Star' ?>
</a>
<?php } ?>
<?php if ($can_edit) { ?>
<div class="dropdown-divider"></div>
<?php if ($domain_archived_at) { ?>
<a class="dropdown-item" href="post.php?restore_domain=<?= $domain_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-redo text-secondary me-2"></i>Restore
</a>
<?php if ($can_delete) { ?>
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_domain=<?= $domain_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>"
data-confirm-text="<?= $domain_name ?> goes for good, with its history. Services and certificates stop pointing at it.">
<i class="fas fa-fw fa-trash me-2"></i>Delete
</a>
<?php } ?>
<?php } else { ?>
<a class="dropdown-item text-danger confirm-link" href="post.php?archive_domain=<?= $domain_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-archive me-2"></i>Archive
</a>
<?php } ?>
<?php } ?>
</div>
</div>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</form>
<?php
$empty_state = [
'thing' => 'domains',
'icon' => 'fa-globe',
'filtered' => $q || $tag_query || $vendor_query || $expire_query,
'views' => ['show'],
'view' => emptyStatePill($shows, $show, '', ['soon', 'expired', 'noauto', 'mail']),
'text' => 'Their domain names, where each is registered and when it expires.',
'actions' => $can_edit ? [['label' => 'New Domain', 'modal' => "modals/domain/domain_add.php?$client_url", 'size' => 'lg']] : [],
];
require_once "../includes/filter_footer.php";
?>
</div>
<?php if ($can_edit && !$archived) { ?>
<!-- Bulk: who registers or hosts the domains picked -->
<div class="modal fade" id="domainBulkVendorModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-dark">
<h5 class="modal-title"><i class="fas fa-fw fa-building me-2"></i>Set registrar or host</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<label class="form-label">What they do</label>
<div class="btn-group w-100 mb-3" role="group">
<?php foreach (DOMAIN_ROLES as $role => $info) { ?>
<input type="radio" class="btn-check" form="bulkActions" name="role" value="<?= $role ?>" id="domainBulkRole<?= $role ?>" <?= $role === 'registrar' ? 'checked' : '' ?>>
<label class="btn btn-outline-secondary" for="domainBulkRole<?= $role ?>"><i class="fas <?= $info['icon'] ?> me-1"></i><?= $info['label'] ?></label>
<?php } ?>
</div>
<label class="form-label">Vendor</label>
<select class="form-select select2" form="bulkActions" name="vendor_id">
<option value="0">- Nobody -</option>
<?php foreach ($bulk_vendor_options as $vendor) { ?>
<option value="<?= intval($vendor['vendor_id']) ?>"<?= selectOptionVendor($vendor['vendor_name'], intval($vendor['vendor_client_id']) === 0 ? 'Ours' : $vendor['vendor_description']) ?>><?= escapeHtml($vendor['vendor_name']) ?></option>
<?php } ?>
</select>
<div class="form-text"><?= $client_url ? "The client's vendors, and ours." : 'Across clients only our own vendors can be set - open a client to use theirs.' ?></div>
</div>
<div class="modal-footer">
<button type="submit" form="bulkActions" name="bulk_set_domain_vendor" class="btn btn-primary text-bold"><i class="fa fa-check me-2"></i>Set it</button>
<button type="button" class="btn btn-light" data-bs-dismiss="modal"><i class="fa fa-times me-2"></i>Cancel</button>
</div>
</div>
</div>
</div>
<?php } ?>
<?php if (!empty($_GET['open'])) { ?>
<script>
// Opened from search or the overview - straight to that domain's quick look
document.addEventListener('DOMContentLoaded', function () {
var open = document.createElement('a');
open.className = 'ajax-modal';
open.href = '#';
open.dataset.modalSize = 'lg';
open.dataset.modalUrl = 'modals/domain/domain.php?id=<?= intval($_GET['open']) ?>';
document.body.appendChild(open);
open.click();
open.remove();
});
</script>
<?php } ?>
<script src="../js/bulk_actions.js"></script>
<?php require_once "../includes/footer.php";