row]. */
function domainsForBulk($level) {
global $client_id;
enforceUserPermission('module_support', $level);
$rows = [];
foreach (array_unique(array_map('intval', (array) ($_POST['domain_ids'] ?? []))) as $domain_id) {
$row = domainRow($domain_id);
if (!$row) {
continue;
}
$client_id = intval($row['domain_client_id']);
enforceClientAccess();
$rows[$domain_id] = $row;
}
return $rows;
}
/** Look a domain up and save what came back, with its history. True when something answered. */
function domainRefresh($row) {
$domain_id = intval($row['domain_id']);
$lookup = domainLookup($row['domain_name']);
if (!$lookup) {
return false;
}
$before = domainHistorySnapshot($domain_id);
domainApplyLookup($domain_id, $lookup);
domainWriteHistory($domain_id, $before, domainHistorySnapshot($domain_id));
return true;
}
if (isset($_POST['add_domain'])) {
validateCSRFToken();
enforceUserPermission('module_support', 2);
$client_id = intval($_POST['client_id'] ?? 0);
enforceClientAccess();
if (!$client_id || !getFieldById('clients', $client_id, 'client_name')) {
flashAlert('Pick the client this domain is for.', 'error');
redirect();
}
// Several at once - a pasted list, one per line (or separated by spaces or commas)
$names_input = trim((string) ($_POST['names'] ?? ''));
if ($names_input !== '') {
set_time_limit(0);
$added = [];
$skipped_invalid = $skipped_existing = 0;
foreach (array_unique(array_filter(preg_split('/[\s,;]+/', $names_input), 'strlen')) as $input) {
$post = $_POST;
$post['name'] = $input;
$post['expire'] = '';
$form = domainFormSet($post, $client_id, 0, $error);
if ($form === null) {
if (domainNormalizeName($input) === '') {
$skipped_invalid++;
} else {
$skipped_existing++;
}
continue;
}
if (isset($added[$form['name']])) {
continue;
}
mysqli_query($mysqli, "INSERT INTO domains SET {$form['set']}, domain_client_id = $client_id");
$domain_id = mysqli_insert_id($mysqli);
domainSetTags($domain_id, $_POST['tags'] ?? []);
$added[$form['name']] = $domain_id;
logAudit("Domain", "Create", "$session_name created domain " . escapeSql($form['name']), $client_id, $domain_id);
}
// Look up what there is time for now; the refresher takes the rest first on its next run
$looked_up = 0;
$started = time();
foreach ($added as $name => $domain_id) {
if (time() - $started > 20) {
break;
}
if (domainApplyLookup($domain_id, domainLookup($name))) {
$looked_up++;
}
}
$count = count($added);
$message = "$count domain" . ($count === 1 ? '' : 's') . " added";
if ($count) {
$message .= $looked_up === $count ? ' and looked up' : ", $looked_up looked up now - the rest are checked on the refresher's next run";
}
if ($skipped_existing) {
$message .= ", $skipped_existing already on this client";
}
if ($skipped_invalid) {
$message .= ", $skipped_invalid not domain names";
}
logAudit("Domain", "Bulk Create", "$session_name added $count domain(s)", $client_id);
flashAlert($message, $count ? 'success' : 'error');
redirect();
}
$form = domainFormSet($_POST, $client_id, 0, $error);
if ($form === null) {
flashAlert($error, 'error');
redirect();
}
mysqli_query($mysqli, "INSERT INTO domains SET {$form['set']}, domain_client_id = $client_id");
$domain_id = mysqli_insert_id($mysqli);
domainSetTags($domain_id, $_POST['tags'] ?? []);
// Its records, registration and expiry - and the certificate on its website
$found = domainApplyLookup($domain_id, domainLookup($form['name']));
$with_certificate = $found && domainAddCertificate($domain_id, $form['name'], $client_id);
$name = $form['name'];
logAudit("Domain", "Create", "$session_name created domain " . escapeSql($name) . ($with_certificate ? ', with its SSL certificate' : ''), $client_id, $domain_id);
flashAlert("Domain $name created" . ($found ? ($with_certificate ? ', with its SSL certificate' : '') : ' - nothing answered for it in DNS, so check the name'), $found ? 'success' : 'warning');
redirect();
}
if (isset($_POST['edit_domain'])) {
validateCSRFToken();
$row = domainForRequest($_POST['domain_id'] ?? 0, 2);
$domain_id = intval($row['domain_id']);
$form = domainFormSet($_POST, $client_id, $domain_id, $error);
if ($form === null) {
flashAlert($error, 'error');
redirect();
}
$before = domainHistorySnapshot($domain_id);
mysqli_query($mysqli, "UPDATE domains SET {$form['set']} WHERE domain_id = $domain_id");
// Only a form that showed the tags changes them
if (isset($_POST['tags_form'])) {
domainSetTags($domain_id, $_POST['tags'] ?? []);
}
// A new name is a new domain to look up; otherwise saving stays quick
if ($form['name'] !== $row['domain_name']) {
domainApplyLookup($domain_id, domainLookup($form['name']));
}
domainWriteHistory($domain_id, $before, domainHistorySnapshot($domain_id));
$name = $form['name'];
logAudit("Domain", "Edit", "$session_name edited domain " . escapeSql($name), $client_id, $domain_id);
flashAlert("Domain $name updated");
redirect();
}
if (isset($_GET['refresh_domain'])) {
validateCSRFToken();
$row = domainForRequest($_GET['refresh_domain'], 2);
$domain_id = intval($row['domain_id']);
$name = $row['domain_name'];
if (!domainRefresh($row)) {
flashAlert("Nothing answered for $name in DNS - nothing was changed. Check the name, or whether it has lapsed.", 'error');
redirect();
}
logAudit("Domain", "Refresh", "$session_name looked up domain " . escapeSql($name), $client_id, $domain_id);
flashAlert("Looked up $name");
redirect();
}
if (isset($_GET['favorite_domain']) || isset($_GET['unfavorite_domain'])) {
validateCSRFToken();
$favorite = isset($_GET['favorite_domain']) ? 1 : 0;
$row = domainForRequest($favorite ? $_GET['favorite_domain'] : $_GET['unfavorite_domain'], 2);
$domain_id = intval($row['domain_id']);
mysqli_query($mysqli, "UPDATE domains SET domain_favorite = $favorite WHERE domain_id = $domain_id");
logAudit("Domain", "Edit", "$session_name " . ($favorite ? 'starred' : 'unstarred') . " domain " . escapeSql($row['domain_name']), $client_id, $domain_id);
redirect();
}
if (isset($_GET['archive_domain'])) {
validateCSRFToken();
$row = domainForRequest($_GET['archive_domain'], 2);
$domain_id = intval($row['domain_id']);
mysqli_query($mysqli, "UPDATE domains SET domain_archived_at = NOW() WHERE domain_id = $domain_id");
logAudit("Domain", "Archive", "$session_name archived domain " . escapeSql($row['domain_name']), $client_id, $domain_id);
flashAlert("Domain {$row['domain_name']} archived", 'error');
redirect();
}
if (isset($_GET['restore_domain'])) {
validateCSRFToken();
$row = domainForRequest($_GET['restore_domain'], 2);
$domain_id = intval($row['domain_id']);
mysqli_query($mysqli, "UPDATE domains SET domain_archived_at = NULL WHERE domain_id = $domain_id");
logAudit("Domain", "Restore", "$session_name restored domain " . escapeSql($row['domain_name']), $client_id, $domain_id);
flashAlert("Domain {$row['domain_name']} restored");
redirect();
}
if (isset($_GET['delete_domain'])) {
validateCSRFToken();
$row = domainForRequest($_GET['delete_domain'], 3);
domainDeleteForGood($row['domain_id']);
logAudit("Domain", "Delete", "$session_name deleted domain " . escapeSql($row['domain_name']), $client_id);
flashAlert("Domain {$row['domain_name']} deleted", 'error');
redirect();
}
if (isset($_POST['bulk_favorite_domains']) || isset($_POST['bulk_unfavorite_domains'])) {
validateCSRFToken();
$favorite = isset($_POST['bulk_favorite_domains']) ? 1 : 0;
$count = 0;
foreach (domainsForBulk(2) as $domain_id => $row) {
mysqli_query($mysqli, "UPDATE domains SET domain_favorite = $favorite WHERE domain_id = $domain_id");
logAudit("Domain", "Edit", "$session_name " . ($favorite ? 'starred' : 'unstarred') . " domain " . escapeSql($row['domain_name']), intval($row['domain_client_id']), $domain_id);
$count++;
}
flashAlert(($favorite ? 'Starred' : 'Unstarred') . " $count domain" . ($count === 1 ? '' : 's'));
redirect();
}
// Who registers or hosts them - one vendor into one role for several domains at once
if (isset($_POST['bulk_set_domain_vendor'])) {
validateCSRFToken();
$role = $_POST['role'] ?? '';
if (!isset(DOMAIN_ROLES[$role])) {
flashAlert('Pick what the vendor does for them.', 'error');
redirect();
}
$column = DOMAIN_ROLES[$role]['column'];
$vendor_input = intval($_POST['vendor_id'] ?? 0);
$count = $skipped = 0;
foreach (domainsForBulk(2) as $domain_id => $row) {
$vendor_id = domainVendorId($vendor_input, $row['domain_client_id']);
if ($vendor_input && !$vendor_id) {
// That vendor is another client's
$skipped++;
continue;
}
$before = domainHistorySnapshot($domain_id);
mysqli_query($mysqli, "UPDATE domains SET $column = $vendor_id WHERE domain_id = $domain_id");
domainWriteHistory($domain_id, $before, domainHistorySnapshot($domain_id));
logAudit("Domain", "Edit", "$session_name set the " . strtolower(DOMAIN_ROLES[$role]['label']) . " vendor on domain " . escapeSql($row['domain_name']), intval($row['domain_client_id']), $domain_id);
$count++;
}
flashAlert("Set the " . strtolower(DOMAIN_ROLES[$role]['label']) . ($role === 'registrar' ? '' : ' host') . " on $count domain" . ($count === 1 ? '' : 's') . ($skipped ? ", $skipped skipped - that vendor is another client's" : ''));
redirect();
}
if (isset($_POST['bulk_refresh_domains'])) {
validateCSRFToken();
// Lookups are slow - don't time out mid-batch
set_time_limit(0);
$count = $found = 0;
foreach (domainsForBulk(2) as $domain_id => $row) {
$count++;
if (domainRefresh($row)) {
$found++;
logAudit("Domain", "Refresh", "$session_name looked up domain " . escapeSql($row['domain_name']), intval($row['domain_client_id']), $domain_id);
}
// Be gentle on the registries
usleep(500000);
}
$missed = $count - $found;
flashAlert("Looked up $found domain" . ($found === 1 ? '' : 's') . ($missed ? ", nothing answered for $missed - left as they were" : ''), $missed ? 'warning' : 'success');
redirect();
}
if (isset($_POST['bulk_archive_domains'])) {
validateCSRFToken();
$count = 0;
foreach (domainsForBulk(2) as $domain_id => $row) {
mysqli_query($mysqli, "UPDATE domains SET domain_archived_at = NOW() WHERE domain_id = $domain_id");
logAudit("Domain", "Archive", "$session_name archived domain " . escapeSql($row['domain_name']), intval($row['domain_client_id']), $domain_id);
$count++;
}
flashAlert("Archived $count domain" . ($count === 1 ? '' : 's'), 'error');
redirect();
}
if (isset($_POST['bulk_restore_domains'])) {
validateCSRFToken();
$count = 0;
foreach (domainsForBulk(2) as $domain_id => $row) {
mysqli_query($mysqli, "UPDATE domains SET domain_archived_at = NULL WHERE domain_id = $domain_id");
logAudit("Domain", "Restore", "$session_name restored domain " . escapeSql($row['domain_name']), intval($row['domain_client_id']), $domain_id);
$count++;
}
flashAlert("Restored $count domain" . ($count === 1 ? '' : 's'));
redirect();
}
if (isset($_POST['bulk_delete_domains'])) {
validateCSRFToken();
$count = 0;
foreach (domainsForBulk(3) as $domain_id => $row) {
domainDeleteForGood($domain_id);
logAudit("Domain", "Delete", "$session_name deleted domain " . escapeSql($row['domain_name']), intval($row['domain_client_id']));
$count++;
}
flashAlert("Deleted $count domain" . ($count === 1 ? '' : 's'), 'error');
redirect();
}
if (isExportRequest('export_domains')) {
validateCSRFToken();
// Exports are reads - see CONTRIBUTING.md
enforceUserPermission('module_support');
$format = resolveExportFormat($_POST['export_domains']);
// Filters inherited from the domains page - mirrors agent/domains.php
$filter_summary = [];
// Archived Filter
$archived = (isset($_POST['archived']) && $_POST['archived'] == 1);
if ($archived) {
$filter_summary['Archived'] = 'Archived only';
}
if (!empty($_POST['client_id'])) {
$client_id = intval($_POST['client_id']);
$client_query = "AND domain_client_id = $client_id";
$client_name = getFieldById('clients', $client_id, 'client_name');
$file_name_prepend = "$client_name-";
$filter_summary['Client'] = $client_name;
enforceClientAccess();
$archive_query = $archived ? "domain_archived_at IS NOT NULL" : "domain_archived_at IS NULL";
} else {
$client_query = '';
$client_id = 0; // for Logging
$file_name_prepend = "$session_company_name-";
// Client Filter
if (!empty($_POST['client'])) {
$filter_client_id = intval($_POST['client']);
$client_query = "AND (domain_client_id = $filter_client_id)";
$filter_summary['Client'] = exportClientName($filter_client_id);
}
$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)";
}
// Expiring In Filter
$expire_query = '';
if (!empty($_POST['expire_days'])) {
if ($_POST['expire_days'] == "expired") {
$expire_query = "AND (domain_expire IS NOT NULL AND domain_expire < CURDATE())";
$filter_summary['Expiry'] = 'Expired';
} else {
$expire_days = intval($_POST['expire_days']);
$expire_query = "AND (domain_expire IS NOT NULL AND domain_expire BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL $expire_days DAY))";
$filter_summary['Expiry'] = "Expiring within $expire_days days";
}
}
// Tags Filter - a domain with any of them
$tag_query = '';
if (isset($_POST['tags']) && is_array($_POST['tags']) && !empty($_POST['tags'])) {
$tag_filter = implode(",", array_map('intval', $_POST['tags']));
$tag_query = "AND domains.domain_id IN (SELECT domain_id FROM domain_tags WHERE tag_id IN ($tag_filter))";
}
// Vendor Filter - registrar or any host
$vendor_query = '';
if (!empty($_POST['vendor'])) {
$vendor_filter = intval($_POST['vendor']);
$vendor_query = "AND $vendor_filter IN (domain_registrar, domain_dnshost, domain_webhost, domain_mailhost)";
$filter_summary['Vendor'] = exportRecordName('vendors', $vendor_filter, 'vendor_name', 'vendor_client_id');
}
// Search Filter
$q = escapeSql($_POST['q'] ?? '');
if (!empty($q)) {
$filter_summary['Search'] = $_POST['q'];
}
[$domain_columns, $domain_joins] = domainSelectSql();
$sql = mysqli_query(
$mysqli,
"SELECT $domain_columns,
registrar.vendor_name AS domain_registrar_name, webhost.vendor_name AS domain_webhost_name,
dnshost.vendor_name AS domain_dnshost_name, mailhost.vendor_name AS domain_mailhost_name,
IF(domain_auto_renew = 1, 'Yes', 'No') AS domain_auto_renew_label,
(SELECT GROUP_CONCAT(tag_name ORDER BY tag_name SEPARATOR ', ') FROM domain_tags dt JOIN tags t ON t.tag_id = dt.tag_id WHERE dt.domain_id = domains.domain_id) AS domain_tags
FROM domains $domain_joins
WHERE (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 webhost.vendor_name LIKE '%$q%' OR mailhost.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%'))
AND $archive_query
" . clientScopeSql('domain_client_id') . "
$client_query
$expire_query
$tag_query
$vendor_query
ORDER BY client_name ASC, domains.domain_name ASC"
);
$num_rows = mysqli_num_rows($sql);
guardExportHasRows($num_rows, 'domains');
if ($num_rows > 0) {
guardExportPdfRowCount($format, $num_rows);
$export = beginExport('domains', $format, $file_name_prepend . 'Domains', 'Domains', summarizeExportFilters($filter_summary));
while ($row = mysqli_fetch_assoc($sql)) {
addExportRow($export, $row);
}
finishExport($export);
}
logAudit("Domain", "Export", "$session_name exported $num_rows domain(s) to a " . strtoupper($format) . " file", $client_id);
exit;
}