Add tags support for domains with filtering and bulk actions
Introduces a 'tags' field to the domains table and UI, allowing users to organize domains with custom tags. Adds tag input and display to create, edit, bulk-add, and view pages, as well as tag-based filtering and bulk tag management (add/remove) in the domain list. Updates backend validation, controller logic, and migrations to support tags, including a new migration and index for efficient tag searches.
This commit is contained in:
@@ -26,6 +26,7 @@ class DomainController extends Controller
|
||||
$search = \App\Helpers\InputValidator::sanitizeSearch($_GET['search'] ?? '', 100);
|
||||
$status = $_GET['status'] ?? '';
|
||||
$groupId = $_GET['group'] ?? '';
|
||||
$tag = $_GET['tag'] ?? '';
|
||||
$sortBy = $_GET['sort'] ?? 'domain_name';
|
||||
$sortOrder = $_GET['order'] ?? 'asc';
|
||||
$page = max(1, (int)($_GET['page'] ?? 1));
|
||||
@@ -40,7 +41,8 @@ class DomainController extends Controller
|
||||
$filters = [
|
||||
'search' => $search,
|
||||
'status' => $status,
|
||||
'group' => $groupId
|
||||
'group' => $groupId,
|
||||
'tag' => $tag
|
||||
];
|
||||
|
||||
// Get filtered and paginated domains using model
|
||||
@@ -48,16 +50,21 @@ class DomainController extends Controller
|
||||
|
||||
$groups = $this->groupModel->all();
|
||||
|
||||
// Get all unique tags for filter dropdown
|
||||
$allTags = $this->domainModel->getAllTags();
|
||||
|
||||
// Format domains for display
|
||||
$formattedDomains = \App\Helpers\DomainHelper::formatMultiple($result['domains']);
|
||||
|
||||
$this->view('domains/index', [
|
||||
'domains' => $formattedDomains,
|
||||
'groups' => $groups,
|
||||
'allTags' => $allTags,
|
||||
'filters' => [
|
||||
'search' => $search,
|
||||
'status' => $status,
|
||||
'group' => $groupId,
|
||||
'tag' => $tag,
|
||||
'sort' => $sortBy,
|
||||
'order' => $sortOrder
|
||||
],
|
||||
@@ -88,6 +95,7 @@ class DomainController extends Controller
|
||||
|
||||
$domainName = trim($_POST['domain_name'] ?? '');
|
||||
$groupId = !empty($_POST['notification_group_id']) ? (int)$_POST['notification_group_id'] : null;
|
||||
$tagsInput = trim($_POST['tags'] ?? '');
|
||||
|
||||
// Validate
|
||||
if (empty($domainName)) {
|
||||
@@ -103,6 +111,15 @@ class DomainController extends Controller
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate tags
|
||||
$tagValidation = \App\Helpers\InputValidator::validateTags($tagsInput);
|
||||
if (!$tagValidation['valid']) {
|
||||
$_SESSION['error'] = $tagValidation['error'];
|
||||
$this->redirect('/domains/create');
|
||||
return;
|
||||
}
|
||||
$tags = $tagValidation['tags'];
|
||||
|
||||
// Check if domain already exists
|
||||
if ($this->domainModel->existsByDomain($domainName)) {
|
||||
$_SESSION['error'] = 'Domain already exists';
|
||||
@@ -130,6 +147,7 @@ class DomainController extends Controller
|
||||
$id = $this->domainModel->create([
|
||||
'domain_name' => $domainName,
|
||||
'notification_group_id' => $groupId,
|
||||
'tags' => $tags,
|
||||
'registrar' => $whoisData['registrar'],
|
||||
'registrar_url' => $whoisData['registrar_url'] ?? null,
|
||||
'expiration_date' => $whoisData['expiration_date'],
|
||||
@@ -188,6 +206,16 @@ class DomainController extends Controller
|
||||
|
||||
$groupId = !empty($_POST['notification_group_id']) ? (int)$_POST['notification_group_id'] : null;
|
||||
$isActive = isset($_POST['is_active']) ? 1 : 0;
|
||||
$tagsInput = trim($_POST['tags'] ?? '');
|
||||
|
||||
// Validate tags
|
||||
$tagValidation = \App\Helpers\InputValidator::validateTags($tagsInput);
|
||||
if (!$tagValidation['valid']) {
|
||||
$_SESSION['error'] = $tagValidation['error'];
|
||||
$this->redirect('/domains/' . $id . '/edit');
|
||||
return;
|
||||
}
|
||||
$tags = $tagValidation['tags'];
|
||||
|
||||
// Check if monitoring status changed
|
||||
$statusChanged = ($domain['is_active'] != $isActive);
|
||||
@@ -195,6 +223,7 @@ class DomainController extends Controller
|
||||
|
||||
$this->domainModel->update($id, [
|
||||
'notification_group_id' => $groupId,
|
||||
'tags' => $tags,
|
||||
'is_active' => $isActive
|
||||
]);
|
||||
|
||||
@@ -362,6 +391,7 @@ class DomainController extends Controller
|
||||
// POST - Process bulk add
|
||||
$domainsText = trim($_POST['domains'] ?? '');
|
||||
$groupId = !empty($_POST['notification_group_id']) ? (int)$_POST['notification_group_id'] : null;
|
||||
$tagsInput = trim($_POST['tags'] ?? '');
|
||||
|
||||
if (empty($domainsText)) {
|
||||
$_SESSION['error'] = 'Please enter at least one domain';
|
||||
@@ -369,6 +399,15 @@ class DomainController extends Controller
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate tags
|
||||
$tagValidation = \App\Helpers\InputValidator::validateTags($tagsInput);
|
||||
if (!$tagValidation['valid']) {
|
||||
$_SESSION['error'] = $tagValidation['error'];
|
||||
$this->redirect('/domains/bulk-add');
|
||||
return;
|
||||
}
|
||||
$tags = $tagValidation['tags'];
|
||||
|
||||
// Split by new lines and clean
|
||||
$domainNames = array_filter(array_map('trim', explode("\n", $domainsText)));
|
||||
|
||||
@@ -402,6 +441,7 @@ class DomainController extends Controller
|
||||
$this->domainModel->create([
|
||||
'domain_name' => $domainName,
|
||||
'notification_group_id' => $groupId,
|
||||
'tags' => $tags,
|
||||
'registrar' => $whoisData['registrar'],
|
||||
'registrar_url' => $whoisData['registrar_url'] ?? null,
|
||||
'expiration_date' => $whoisData['expiration_date'],
|
||||
@@ -638,5 +678,83 @@ class DomainController extends Controller
|
||||
$_SESSION['success'] = 'Notes updated successfully';
|
||||
$this->redirect('/domains/' . $id);
|
||||
}
|
||||
|
||||
public function bulkAddTags()
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
$this->redirect('/domains');
|
||||
return;
|
||||
}
|
||||
|
||||
// CSRF Protection
|
||||
$this->verifyCsrf('/domains');
|
||||
|
||||
$domainIds = $_POST['domain_ids'] ?? [];
|
||||
$tagToAdd = trim($_POST['tag'] ?? '');
|
||||
|
||||
if (empty($domainIds) || empty($tagToAdd)) {
|
||||
$_SESSION['error'] = 'Invalid request';
|
||||
$this->redirect('/domains');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate tag format
|
||||
if (!preg_match('/^[a-z0-9-]+$/', $tagToAdd)) {
|
||||
$_SESSION['error'] = 'Invalid tag format (use only letters, numbers, and hyphens)';
|
||||
$this->redirect('/domains');
|
||||
return;
|
||||
}
|
||||
|
||||
$updated = 0;
|
||||
foreach ($domainIds as $id) {
|
||||
$domain = $this->domainModel->find($id);
|
||||
if (!$domain) continue;
|
||||
|
||||
// Get existing tags
|
||||
$existingTags = !empty($domain['tags']) ? explode(',', $domain['tags']) : [];
|
||||
|
||||
// Add new tag if it doesn't exist
|
||||
if (!in_array($tagToAdd, $existingTags)) {
|
||||
$existingTags[] = $tagToAdd;
|
||||
$newTags = implode(',', $existingTags);
|
||||
|
||||
if ($this->domainModel->update($id, ['tags' => $newTags])) {
|
||||
$updated++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$_SESSION['success'] = "Tag '$tagToAdd' added to $updated domain(s)";
|
||||
$this->redirect('/domains');
|
||||
}
|
||||
|
||||
public function bulkRemoveTags()
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
$this->redirect('/domains');
|
||||
return;
|
||||
}
|
||||
|
||||
// CSRF Protection
|
||||
$this->verifyCsrf('/domains');
|
||||
|
||||
$domainIds = $_POST['domain_ids'] ?? [];
|
||||
|
||||
if (empty($domainIds)) {
|
||||
$_SESSION['error'] = 'No domains selected';
|
||||
$this->redirect('/domains');
|
||||
return;
|
||||
}
|
||||
|
||||
$updated = 0;
|
||||
foreach ($domainIds as $id) {
|
||||
if ($this->domainModel->update($id, ['tags' => ''])) {
|
||||
$updated++;
|
||||
}
|
||||
}
|
||||
|
||||
$_SESSION['success'] = "Tags removed from $updated domain(s)";
|
||||
$this->redirect('/domains');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ class InstallerController extends Controller
|
||||
'013_create_user_notifications_table.sql',
|
||||
'014_add_captcha_settings.sql',
|
||||
'015_create_error_logs_table.sql',
|
||||
'016_add_tags_to_domains.sql',
|
||||
];
|
||||
|
||||
try {
|
||||
@@ -264,7 +265,8 @@ class InstallerController extends Controller
|
||||
'012_link_remember_tokens_to_sessions.sql',
|
||||
'013_create_user_notifications_table.sql',
|
||||
'014_add_captcha_settings.sql',
|
||||
'015_create_error_logs_table.sql'
|
||||
'015_create_error_logs_table.sql',
|
||||
'016_add_tags_to_domains.sql',
|
||||
];
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO migrations (migration) VALUES (?) ON DUPLICATE KEY UPDATE migration=migration");
|
||||
|
||||
Reference in New Issue
Block a user