@@ -97,18 +92,18 @@ if (!isset($logs) || !is_array($logs)) {
-
-
+
+ domain); ?>
|
-
+ active == 1): ?>
@@ -116,11 +111,11 @@ if (!isset($logs) || !is_array($logs)) {
|
-
+ active != 1): ?>
+ ), 'delete' . $domain->domain); ?>" class="button button-small button-link-delete" onclick="return confirm('');">
@@ -160,24 +155,3 @@ if (!isset($logs) || !is_array($logs)) {
-
-
diff --git a/assets/css/admin.css b/assets/css/admin.css
index fb2fd4c..cf62040 100644
--- a/assets/css/admin.css
+++ b/assets/css/admin.css
@@ -1,19 +1,26 @@
-.card {
+/* WP Domain Mapping Admin Styles */
+
+/* Main cards */
+.domain-mapping-card {
background: #fff;
border: 1px solid #ccd0d4;
border-radius: 4px;
max-width: unset;
margin-top: 20px;
padding: 20px;
+ box-shadow: 0 1px 1px rgba(0,0,0,.04);
}
-.styles-sync-tabs {
+
+/* Tabs */
+.domain-mapping-tabs {
display: flex;
flex-wrap: wrap;
gap: 5px;
border-bottom: 1px solid #c3c4c7;
margin-bottom: 20px;
}
-.styles-tab {
+
+.domain-mapping-tab {
padding: 8px 16px;
border: none;
background: none;
@@ -21,44 +28,362 @@
font-size: 14px;
border-bottom: 2px solid transparent;
}
-.styles-tab.active {
+
+.domain-mapping-tab.active {
border-bottom: 2px solid #007cba;
font-weight: 600;
background: #f0f0f1;
}
-.styles-tab:hover:not(.active) {
+
+.domain-mapping-tab:hover:not(.active) {
background: #f0f0f1;
border-bottom-color: #dcdcde;
}
-.styles-sync-content { flex: 1; }
-.tablenav { margin: 10px 0; }
-.tablenav-pages { float: right; }
-.tablenav-pages a, .tablenav-pages span { padding: 5px 10px; }
-.form-table th { width: 200px; }
-.form-table td { padding: 10px
-System: 0; }
-.description { color: #666; font-size: 12px; }
-.notice { padding: 8px 12px; border-radius: 3px; }
-.notice-success { background-color: #dff0d8; border-left: 4px solid #46b450; }
-.notice-error { background-color: #f2dede; border-left: 4px solid #dc3232; }
+.domain-mapping-content {
+ flex: 1;
+}
-/* Responsive Styles */
+/* Search form */
+.search-form {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: flex-end;
+ gap: 15px;
+ margin-bottom: 15px;
+}
+
+.search-form-field {
+ display: flex;
+ flex-direction: column;
+ min-width: 180px;
+}
+
+.search-form-field label {
+ margin-bottom: 5px;
+ font-weight: 500;
+}
+
+.search-form-submit {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+/* Tables */
+.tablenav {
+ margin: 10px 0;
+ display: flex;
+ align-items: center;
+}
+
+.tablenav-pages {
+ margin-left: auto;
+}
+
+.tablenav-pages a,
+.tablenav-pages span.current {
+ display: inline-block;
+ min-width: 17px;
+ padding: 3px 5px 7px;
+ background: #f0f0f1;
+ font-size: 16px;
+ line-height: 1;
+ font-weight: 400;
+ text-align: center;
+ text-decoration: none;
+}
+
+.tablenav-pages span.current {
+ background: #007cba;
+ color: #fff;
+ border-color: #007cba;
+}
+
+.displaying-num {
+ margin-left: 10px;
+ color: #555;
+}
+
+.domains-table th,
+.logs-table th,
+.domains-health-table th {
+ font-weight: 600;
+}
+
+.column-site-id {
+ width: 80px;
+}
+
+.column-site-name {
+ width: 20%;
+}
+
+.column-primary {
+ width: 80px;
+}
+
+.column-actions {
+ width: 120px;
+}
+
+/* Health table specific */
+.column-domain {
+ width: 20%;
+}
+
+.column-site {
+ width: 20%;
+}
+
+.column-dns,
+.column-ssl,
+.column-status {
+ width: 10%;
+}
+
+.column-last-check {
+ width: 15%;
+}
+
+/* Site ID Badge */
+.dm-site-id-badge {
+ display: inline-block;
+ background: #2271b1;
+ color: #fff;
+ font-weight: 500;
+ padding: 0 5px;
+ border-radius: 3px;
+ font-size: 12px;
+}
+
+/* Form elements */
+.form-table th {
+ width: 200px;
+ padding: 15px 10px 15px 0;
+}
+
+.form-table td {
+ padding: 15px 0;
+}
+
+.description {
+ color: #666;
+ font-size: 13px;
+ margin-top: 4px;
+}
+
+/* Notices */
+.notice {
+ padding: 8px 12px;
+ border-radius: 3px;
+ margin: 5px 0 15px;
+}
+
+.notice p {
+ margin: 0.5em 0;
+ padding: 2px;
+}
+
+.notice-success {
+ background-color: #f0f9eb;
+ border-left: 4px solid #46b450;
+}
+
+.notice-error {
+ background-color: #fef0f0;
+ border-left: 4px solid #dc3232;
+}
+
+.notice-warning {
+ background-color: #fff8e5;
+ border-left: 4px solid #ffb900;
+}
+
+.notice-info {
+ background-color: #f0f6fa;
+ border-left: 4px solid #00a0d2;
+}
+
+/* DNS instructions */
+.dns-instructions {
+ margin-top: 10px;
+}
+
+.dns-example {
+ background: #f9f9f9;
+ border: 1px solid #e5e5e5;
+ border-radius: 3px;
+ padding: 15px;
+ margin: 15px 0;
+}
+
+.dns-example h4 {
+ margin-top: 0;
+}
+
+.dns-tips {
+ list-style: disc;
+ margin-left: 20px;
+}
+
+.dns-tips li {
+ margin-bottom: 8px;
+}
+
+/* Status indicators */
+.dashicons-yes-alt,
+.dashicons-no-alt {
+ font-size: 24px;
+ width: 24px;
+ height: 24px;
+}
+
+/* Logs table styles */
+.logs-filter {
+ display: flex;
+ align-items: center;
+ margin-bottom: 10px;
+}
+
+.logs-table .column-user,
+.logs-table .column-site {
+ width: 15%;
+}
+
+.logs-table .column-action {
+ width: 10%;
+}
+
+.logs-table .column-date {
+ width: 15%;
+}
+
+.log-action {
+ display: inline-block;
+ padding: 2px 8px;
+ border-radius: 3px;
+ font-size: 12px;
+ font-weight: bold;
+}
+
+.log-action-add {
+ background-color: #dff0d8;
+ color: #3c763d;
+}
+
+.log-action-edit {
+ background-color: #d9edf7;
+ color: #31708f;
+}
+
+.log-action-delete {
+ background-color: #f2dede;
+ color: #a94442;
+}
+
+/* Health details */
+.health-details-content {
+ padding: 15px;
+ background: #f9f9f9;
+ border-radius: 3px;
+}
+
+.health-details-content h4 {
+ margin-top: 0;
+}
+
+/* Progress bar */
+.progress-bar-outer {
+ background-color: #f0f0f1;
+ border-radius: 4px;
+ height: 20px;
+ width: 100%;
+ overflow: hidden;
+ margin: 10px 0;
+}
+
+.progress-bar-inner {
+ background-color: #2271b1;
+ height: 100%;
+ width: 0%;
+ transition: width 0.3s ease;
+}
+
+.progress-text {
+ text-align: center;
+ font-weight: 500;
+}
+
+/* Import/Export specific */
+.import-summary,
+.import-details {
+ margin-top: 15px;
+}
+
+.import-details table {
+ margin-top: 10px;
+}
+
+.import-details .notice-success {
+ background-color: #f0f9eb;
+}
+
+.import-details .notice-error {
+ background-color: #fef0f0;
+}
+
+.import-details .notice-warning {
+ background-color: #fff8e5;
+}
+
+/* Responsive */
@media screen and (max-width: 782px) {
+ .search-form {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .search-form-field {
+ min-width: 100%;
+ }
+
.form-table th {
width: 100%;
display: block;
}
+
.form-table td {
display: block;
- padding: 5px 0 15px;
+ padding: 0 0 15px;
}
- .wp-list-table th.column-primary ~ th,
- .wp-list-table td.column-primary ~ td {
+
+ .domain-mapping-tabs {
+ flex-direction: column;
+ }
+
+ .domain-mapping-tab {
+ width: 100%;
+ text-align: left;
+ border-bottom: 1px solid #ddd;
+ border-radius: 0;
+ }
+
+ .domain-mapping-tab.active {
+ border-bottom: 1px solid #007cba;
+ border-left: 4px solid #007cba;
+ }
+
+ /* Hide less important columns on mobile */
+ .wp-list-table th.column-primary ~ th:not(.column-actions),
+ .wp-list-table td.column-primary ~ td:not(.column-actions) {
display: none;
}
+
.wp-list-table th.column-primary,
- .wp-list-table td.column-primary {
+ .wp-list-table td.column-primary,
+ .wp-list-table th.column-actions,
+ .wp-list-table td.column-actions {
display: table-cell;
}
-}
\ No newline at end of file
+}
diff --git a/assets/js/admin.js b/assets/js/admin.js
index 8dab280..e8af937 100644
--- a/assets/js/admin.js
+++ b/assets/js/admin.js
@@ -2,60 +2,72 @@
* WP Domain Mapping admin JavaScript
*/
jQuery(document).ready(function($) {
- // Tab switching
- $('.styles-tab').on('click', function() {
- $('.styles-tab').removeClass('active');
- $(this).addClass('active');
- var tab = $(this).data('tab');
- $('.styles-section').hide();
- $('.styles-section[data-section="' + tab + '"]').show();
- });
+
+ // Helper function for showing notices
+ function showNotice(selector, message, type) {
+ $(selector)
+ .removeClass('notice-success notice-error notice-warning notice-info')
+ .addClass('notice-' + type)
+ .html(' ' + message + ' ')
+ .show()
+ .delay(5000)
+ .fadeOut();
+ }
// Domain form validation
$('#edit-domain-form').on('submit', function(e) {
- var domain = $('#domain').val();
- var blogId = $('#blog_id').val();
-
+ var domain = $('#domain').val().trim();
+ var blogId = $('#blog_id').val().trim();
+
if (!domain) {
e.preventDefault();
- alert(wpDomainMapping.messages.domainRequired);
+ showNotice('#edit-domain-status', wpDomainMapping.messages.domainRequired, 'error');
$('#domain').focus();
return false;
}
-
+
if (!blogId) {
e.preventDefault();
- alert(wpDomainMapping.messages.siteRequired);
+ showNotice('#edit-domain-status', wpDomainMapping.messages.siteRequired, 'error');
$('#blog_id').focus();
return false;
}
+
+ // Basic domain format validation
+ var domainPattern = /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\.[a-zA-Z]{2,}$/;
+ if (!domainPattern.test(domain)) {
+ e.preventDefault();
+ showNotice('#edit-domain-status', 'Please enter a valid domain format (e.g., example.com)', 'error');
+ $('#domain').focus();
+ return false;
+ }
});
- // Check all domains
+ // Check all domains functionality
$('#select-all').on('change', function() {
$('.domain-checkbox').prop('checked', this.checked);
});
- // AJAX domain operations
- function showNotice(selector, message, type) {
- $(selector).removeClass('notice-success notice-error')
- .addClass('notice-' + type)
- .html('' + message + ' ')
- .show()
- .delay(3000)
- .fadeOut();
- }
+ // Update select all when individual checkboxes change
+ $('.domain-checkbox').on('change', function() {
+ if (!this.checked) {
+ $('#select-all').prop('checked', false);
+ } else if ($('.domain-checkbox:checked').length === $('.domain-checkbox').length) {
+ $('#select-all').prop('checked', true);
+ }
+ });
- // Save domain
+ // Handle domain edit/add form submission via AJAX
$('#edit-domain-form').on('submit', function(e) {
e.preventDefault();
+
var formData = $(this).serializeArray();
formData.push({name: 'action', value: 'dm_handle_actions'});
formData.push({name: 'action_type', value: 'save'});
formData.push({name: 'nonce', value: wpDomainMapping.nonce});
- $('#edit-domain-status').text(wpDomainMapping.messages.saving).show();
-
+ $('#edit-domain-status').html('' + wpDomainMapping.messages.saving + ' ').show();
+
$.ajax({
url: wpDomainMapping.ajaxUrl,
type: 'POST',
@@ -63,7 +75,12 @@ jQuery(document).ready(function($) {
success: function(response) {
if (response.success) {
showNotice('#edit-domain-status', response.data, 'success');
- setTimeout(function() { location.reload(); }, 1000);
+ setTimeout(function() {
+ // Redirect to domains page without edit parameter
+ var url = new URL(window.location);
+ url.searchParams.delete('edit_domain');
+ window.location.href = url.toString();
+ }, 1500);
} else {
showNotice('#edit-domain-status', response.data || wpDomainMapping.messages.error, 'error');
}
@@ -74,61 +91,498 @@ jQuery(document).ready(function($) {
});
});
- // Bulk actions
+ // Handle bulk domain actions
$('#domain-list-form').on('submit', function(e) {
e.preventDefault();
+
var selectedDomains = [];
$('.domain-checkbox:checked').each(function() {
selectedDomains.push($(this).val());
});
-
+
if (selectedDomains.length === 0) {
showNotice('#domain-status', wpDomainMapping.messages.noSelection, 'error');
return;
}
var action = $('#bulk-action-selector-top').val();
- if (action === '-1') return;
+ if (action === '-1') {
+ showNotice('#domain-status', 'Please select an action.', 'error');
+ return;
+ }
- if (confirm('Are you sure you want to delete the selected domains?')) {
- $('#domain-status').text(wpDomainMapping.messages.processing).show();
-
- $.ajax({
- url: wpDomainMapping.ajaxUrl,
- type: 'POST',
- data: {
- action: 'dm_handle_actions',
- action_type: 'delete',
- domains: selectedDomains,
- nonce: wpDomainMapping.nonce
- },
- success: function(response) {
- if (response.success) {
- showNotice('#domain-status', response.data, 'success');
- setTimeout(function() { location.reload(); }, 1000);
- } else {
- showNotice('#domain-status', response.data || wpDomainMapping.messages.error, 'error');
- }
- },
- error: function() {
- showNotice('#domain-status', wpDomainMapping.messages.error, 'error');
+ if (action === 'delete' && !confirm('Are you sure you want to delete the selected domains? This action cannot be undone.')) {
+ return;
+ }
+
+ $('#domain-status').html('' + wpDomainMapping.messages.processing + ' ').show();
+
+ $.ajax({
+ url: wpDomainMapping.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'dm_handle_actions',
+ action_type: action,
+ domains: selectedDomains,
+ nonce: wpDomainMapping.nonce
+ },
+ success: function(response) {
+ if (response.success) {
+ showNotice('#domain-status', response.data, 'success');
+ setTimeout(function() {
+ location.reload();
+ }, 1500);
+ } else {
+ showNotice('#domain-status', response.data || wpDomainMapping.messages.error, 'error');
}
- });
+ },
+ error: function() {
+ showNotice('#domain-status', wpDomainMapping.messages.error, 'error');
+ }
+ });
+ });
+
+ // Single domain delete functionality
+ $('.domain-delete-button').on('click', function(e) {
+ e.preventDefault();
+
+ if (!confirm('Are you sure you want to delete this domain? This action cannot be undone.')) {
+ return;
+ }
+
+ var domain = $(this).data('domain');
+ var $row = $(this).closest('tr');
+
+ $.ajax({
+ url: wpDomainMapping.ajaxUrl,
+ type: 'POST',
+ data: {
+ action: 'dm_handle_actions',
+ action_type: 'delete',
+ domain: domain,
+ nonce: wpDomainMapping.nonce
+ },
+ success: function(response) {
+ if (response.success) {
+ $row.fadeOut(function() {
+ $(this).remove();
+ });
+ showNotice('#domain-status', response.data, 'success');
+ } else {
+ showNotice('#domain-status', response.data || wpDomainMapping.messages.error, 'error');
+ }
+ },
+ error: function() {
+ showNotice('#domain-status', wpDomainMapping.messages.error, 'error');
+ }
+ });
+ });
+
+ // Tab switching functionality (for admin settings page)
+ $('.domain-mapping-tab').on('click', function() {
+ var tab = $(this).data('tab');
+
+ // Update active tab
+ $('.domain-mapping-tab').removeClass('active');
+ $(this).addClass('active');
+
+ // Show corresponding content
+ $('.domain-mapping-section').hide();
+ $('.domain-mapping-section[data-section="' + tab + '"]').show();
+
+ // Update URL without reloading
+ if (history.pushState) {
+ var url = new URL(window.location);
+ url.searchParams.set('tab', tab);
+ window.history.pushState({}, '', url);
}
});
- // Copy to clipboard functionality
- $('.copy-to-clipboard').on('click', function() {
- var text = $(this).data('text');
+ // Auto-fill server IP if detected
+ if ($('#ipaddress').length && !$('#ipaddress').val()) {
+ // This would be populated server-side, but we can enhance it client-side if needed
+ }
+
+ // Domain input formatting - remove protocols and trailing slashes
+ $('#domain').on('blur', function() {
+ var domain = $(this).val().trim();
+ // Remove http:// or https://
+ domain = domain.replace(/^https?:\/\//, '');
+ // Remove trailing slash
+ domain = domain.replace(/\/$/, '');
+ // Convert to lowercase
+ domain = domain.toLowerCase();
+ $(this).val(domain);
+ });
+
+ // Copy to clipboard functionality for DNS instructions
+ $('.copy-to-clipboard').on('click', function(e) {
+ e.preventDefault();
+
+ var text = $(this).data('text') || $(this).prev('code').text();
+
+ // Create temporary input
var tempInput = $('');
$('body').append(tempInput);
tempInput.val(text).select();
- document.execCommand('copy');
+
+ try {
+ document.execCommand('copy');
+ var $btn = $(this);
+ var originalText = $btn.text();
+ $btn.text('Copied!').addClass('copied');
+
+ setTimeout(function() {
+ $btn.text(originalText).removeClass('copied');
+ }, 2000);
+ } catch (err) {
+ console.log('Copy failed');
+ }
+
tempInput.remove();
-
- var $btn = $(this);
- var originalText = $btn.text();
- $btn.text('Copied!');
- setTimeout(function() { $btn.text(originalText); }, 2000);
});
-});
\ No newline at end of file
+
+ // Form validation enhancement
+ $('form').on('submit', function() {
+ var $submitBtn = $(this).find('input[type="submit"], button[type="submit"]');
+ var originalText = $submitBtn.val() || $submitBtn.text();
+
+ // Disable submit button to prevent double submission
+ $submitBtn.prop('disabled', true);
+
+ // Re-enable after a delay
+ setTimeout(function() {
+ $submitBtn.prop('disabled', false);
+ }, 3000);
+ });
+
+ // Enhanced table row highlighting
+ $('.wp-list-table tbody tr').hover(
+ function() {
+ $(this).addClass('hover');
+ },
+ function() {
+ $(this).removeClass('hover');
+ }
+ );
+
+ // Auto-hide notices after delay
+ $('.notice.is-dismissible').delay(8000).fadeOut();
+
+ // Confirmation for destructive actions
+ $('a[href*="action=delete"], .delete a').on('click', function(e) {
+ if (!confirm('Are you sure you want to delete this item? This action cannot be undone.')) {
+ e.preventDefault();
+ return false;
+ }
+ });
+
+ // Enhanced search functionality
+ $('#domain-filter-form input[type="text"]').on('keypress', function(e) {
+ if (e.which === 13) { // Enter key
+ $(this).closest('form').submit();
+ }
+ });
+
+ // Tooltips for status indicators
+ if ($.fn.tooltip) {
+ $('.dashicons[title]').tooltip();
+ }
+
+ // Progress indicator for long-running operations
+ function showProgress(message) {
+ var $progress = $('' +
+ ' ' + message + ' ' +
+ ' ' +
+ ' ');
+
+ $('body').append($progress);
+ return $progress;
+ }
+
+ function hideProgress($progress) {
+ if ($progress) {
+ $progress.fadeOut(function() {
+ $(this).remove();
+ });
+ }
+ }
+
+ // Enhanced error handling
+ $(document).ajaxError(function(event, xhr, settings, thrownError) {
+ if (xhr.status === 403) {
+ showNotice('.wrap', 'Permission denied. Please refresh the page and try again.', 'error');
+ } else if (xhr.status === 500) {
+ showNotice('.wrap', 'Server error occurred. Please try again later.', 'error');
+ }
+ });
+
+ // Initialize any existing tab from URL
+ if (typeof URLSearchParams !== 'undefined') {
+ var urlParams = new URLSearchParams(window.location.search);
+ var currentTab = urlParams.get('tab');
+ if (currentTab && $('.domain-mapping-tab[data-tab="' + currentTab + '"]').length) {
+ $('.domain-mapping-tab[data-tab="' + currentTab + '"]').click();
+ }
+ }
+
+ // Smooth scrolling for anchor links
+ $('a[href^="#"]').on('click', function(e) {
+ var target = $(this.getAttribute('href'));
+ if (target.length) {
+ e.preventDefault();
+ $('html, body').stop().animate({
+ scrollTop: target.offset().top - 100
+ }, 500);
+ }
+ });
+
+ // Auto-refresh functionality for health checks
+ var autoRefreshInterval;
+
+ function startAutoRefresh() {
+ if (autoRefreshInterval) {
+ clearInterval(autoRefreshInterval);
+ }
+
+ // Only auto-refresh on health tab
+ if ($('.domain-mapping-section[data-section="health"]').is(':visible')) {
+ autoRefreshInterval = setInterval(function() {
+ // Check if any health checks are running
+ if ($('.check-domain-health:disabled').length === 0) {
+ // Optionally auto-refresh health status every 5 minutes
+ // location.reload();
+ }
+ }, 300000); // 5 minutes
+ }
+ }
+
+ function stopAutoRefresh() {
+ if (autoRefreshInterval) {
+ clearInterval(autoRefreshInterval);
+ }
+ }
+
+ // Start auto-refresh when health tab is active
+ $('.domain-mapping-tab[data-tab="health"]').on('click', startAutoRefresh);
+
+ // Stop auto-refresh when leaving health tab
+ $('.domain-mapping-tab:not([data-tab="health"])').on('click', stopAutoRefresh);
+
+ // Initialize auto-refresh if health tab is already active
+ if ($('.domain-mapping-tab[data-tab="health"]').hasClass('active')) {
+ startAutoRefresh();
+ }
+
+ // Clean up on page unload
+ $(window).on('beforeunload', function() {
+ stopAutoRefresh();
+ });
+
+ // Domain tabs switching
+ $('.domain-mapping-tab').on('click', function() {
+ $('.domain-mapping-tab').removeClass('active');
+ $(this).addClass('active');
+
+ var tab = $(this).data('tab');
+ $('.domain-mapping-section').hide();
+ $('.domain-mapping-section[data-section="' + tab + '"]').show();
+ });
+
+ // Health check single domain
+ $(document).on('click', '.check-domain-health', function() {
+ var $button = $(this);
+ var domain = $button.data('domain');
+ var originalText = $button.text();
+
+ $button.prop('disabled', true).text('Checking...');
+
+ $.ajax({
+ url: ajaxurl,
+ type: 'POST',
+ data: {
+ action: 'dm_check_domain_health',
+ domain: domain,
+ nonce: $('input[name="dm_manual_health_check_nonce"]').val() || wpDomainMapping.nonce
+ },
+ success: function(response) {
+ if (response.success) {
+ // Refresh page to show updated results
+ location.reload();
+ } else {
+ alert(response.data || 'An error occurred during the health check.');
+ $button.prop('disabled', false).text(originalText);
+ }
+ },
+ error: function() {
+ alert('An error occurred during the health check.');
+ $button.prop('disabled', false).text(originalText);
+ }
+ });
+ });
+
+ // Import form handling
+ $('#domain-mapping-import-form').on('submit', function(e) {
+ e.preventDefault();
+
+ var formData = new FormData(this);
+ formData.append('action', 'dm_import_csv');
+
+ // Show progress bar
+ $('#import-progress').show();
+ $('#import-results').hide();
+
+ $.ajax({
+ url: ajaxurl,
+ type: 'POST',
+ data: formData,
+ dataType: 'json',
+ contentType: false,
+ processData: false,
+ success: function(response) {
+ $('#import-progress').hide();
+ $('#import-results').show();
+
+ if (response.success) {
+ $('.import-summary').html(
+ '' +
+ response.data.message +
+ ' '
+ );
+
+ if (response.data.details && response.data.details.length > 0) {
+ var details = '' +
+ '' +
+ 'Status | ' +
+ 'Details | ' +
+ ' ';
+
+ $.each(response.data.details, function(i, item) {
+ var statusClass = '';
+ if (item.status === 'error') {
+ statusClass = 'style="background-color: #fef0f0;"';
+ } else if (item.status === 'warning') {
+ statusClass = 'style="background-color: #fff8e5;"';
+ } else if (item.status === 'success') {
+ statusClass = 'style="background-color: #f0f9eb;"';
+ }
+
+ details += '' +
+ '' + item.status.toUpperCase() + ' | ' +
+ '' + item.message + ' | ' +
+ ' ';
+ });
+
+ details += ' ';
+ $('.import-details').html(details);
+ }
+ } else {
+ $('.import-summary').html(
+ '' +
+ (response.data || 'Import failed.') +
+ ' '
+ );
+ }
+ },
+ error: function() {
+ $('#import-progress').hide();
+ $('#import-results').show();
+ $('.import-summary').html(
+ '' +
+ 'An error occurred during import.' +
+ ' '
+ );
+ },
+ xhr: function() {
+ var xhr = new window.XMLHttpRequest();
+
+ xhr.upload.addEventListener('progress', function(evt) {
+ if (evt.lengthComputable) {
+ var percentComplete = (evt.loaded / evt.total) * 100;
+ $('.progress-bar-inner').css('width', percentComplete + '%');
+ $('.progress-text').text(Math.round(percentComplete) + '%');
+ }
+ }, false);
+
+ return xhr;
+ }
+ });
+ });
+
+ // Enhanced domain validation
+ $('#domain').on('input', function() {
+ var domain = $(this).val();
+ var $feedback = $('#domain-feedback');
+
+ if (!$feedback.length) {
+ $(this).after('');
+ $feedback = $('#domain-feedback');
+ }
+
+ if (domain.length > 0) {
+ // Check for common issues
+ if (domain.indexOf('http') === 0) {
+ $feedback.html('Please remove http:// or https://').show();
+ } else if (domain.indexOf('/') !== -1) {
+ $feedback.html('Please remove any paths or slashes').show();
+ } else if (!/^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\.[a-zA-Z]{2,}$/.test(domain)) {
+ $feedback.html('Please enter a valid domain format').show();
+ } else {
+ $feedback.html('Domain format looks good').show();
+ }
+ } else {
+ $feedback.hide();
+ }
+ });
+
+ // Keyboard shortcuts
+ $(document).on('keydown', function(e) {
+ // Ctrl/Cmd + S to save forms
+ if ((e.ctrlKey || e.metaKey) && e.which === 83) {
+ var $activeForm = $('form:visible').first();
+ if ($activeForm.length) {
+ e.preventDefault();
+ $activeForm.submit();
+ }
+ }
+
+ // Escape key to close modals or cancel actions
+ if (e.which === 27) {
+ $('.notice.is-dismissible').fadeOut();
+ }
+ });
+
+ // Real-time search for domain filter
+ var searchTimeout;
+ $('#domain-filter-form input[name="s"]').on('input', function() {
+ clearTimeout(searchTimeout);
+ var searchTerm = $(this).val();
+
+ searchTimeout = setTimeout(function() {
+ if (searchTerm.length > 2 || searchTerm.length === 0) {
+ // Auto-submit form after delay
+ $('#domain-filter-form').submit();
+ }
+ }, 1000);
+ });
+
+ // Initialize page based on current state
+ function initializePage() {
+ // Check if we're on the domains page and have messages to show
+ if (window.location.href.indexOf('page=domains') > -1) {
+ if (window.location.href.indexOf('updated=add') > -1) {
+ showNotice('#domain-status', 'Domain added successfully.', 'success');
+ } else if (window.location.href.indexOf('updated=del') > -1) {
+ showNotice('#domain-status', 'Domain deleted successfully.', 'success');
+ }
+ }
+
+ // Auto-focus first input field
+ $('input[type="text"]:visible:first').focus();
+ }
+
+ // Initialize when document is ready
+ initializePage();
+});
diff --git a/includes/class-admin.php b/includes/class-admin.php
new file mode 100644
index 0000000..a8c1510
--- /dev/null
+++ b/includes/class-admin.php
@@ -0,0 +1,740 @@
+tables = dm_get_table_names();
+ $this->init();
+ }
+
+ /**
+ * Initialize admin functionality
+ */
+ private function init() {
+ // Setup admin-related hooks
+ $this->setup_admin_hooks();
+
+ // Load admin pages
+ require_once WP_DOMAIN_MAPPING_DIR_PATH . 'admin/pages.php';
+ }
+
+ /**
+ * Setup admin hooks
+ */
+ private function setup_admin_hooks() {
+ // Add menu pages
+ add_action( 'admin_menu', array( $this, 'add_admin_pages' ) );
+ add_action( 'network_admin_menu', array( $this, 'add_network_admin_pages' ) );
+
+ // Add AJAX handlers
+ add_action( 'wp_ajax_dm_handle_actions', array( $this, 'ajax_handle_actions' ) );
+
+ // Add domain link to sites list
+ add_filter( 'manage_sites_action_links', array( $this, 'add_domain_link_to_sites' ), 10, 2 );
+
+ // Add custom columns
+ add_filter( 'wpmu_blogs_columns', array( $this, 'add_domain_mapping_columns' ) );
+ add_action( 'manage_blogs_custom_column', array( $this, 'display_domain_mapping_column' ), 1, 3 );
+ add_action( 'manage_sites_custom_column', array( $this, 'display_domain_mapping_column' ), 1, 3 );
+
+ // Handle user domain mapping actions
+ if ( isset( $_GET['page'] ) && 'domainmapping' === $_GET['page'] ) {
+ add_action( 'admin_init', array( $this, 'handle_user_domain_actions' ) );
+ }
+
+ // Enqueue admin scripts and styles
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
+ }
+
+ /**
+ * Add admin menu pages
+ */
+ public function add_admin_pages() {
+ global $current_site, $wpdb, $wp_db_version;
+
+ if ( ! isset( $current_site ) && $wp_db_version >= 15260 ) {
+ add_action( 'admin_notices', array( $this, 'domain_mapping_warning' ) );
+ return false;
+ }
+
+ if ( isset($current_site->path) && $current_site->path != "/" ) {
+ wp_die( esc_html__( "The domain mapping plugin only works if the site is installed in /. This is a limitation of how virtual servers work and is very difficult to work around.", 'wp-domain-mapping' ) );
+ }
+
+ if ( get_site_option( 'dm_user_settings' ) && $current_site->blog_id != $wpdb->blogid && ! $this->sunrise_warning( false ) ) {
+ add_management_page(
+ __( 'Domain Mapping', 'wp-domain-mapping' ),
+ __( 'Domain Mapping', 'wp-domain-mapping' ),
+ 'manage_options',
+ 'domainmapping',
+ array( $this, 'render_manage_page' )
+ );
+ }
+ }
+
+ /**
+ * Add network admin menu pages
+ */
+ public function add_network_admin_pages() {
+ add_submenu_page(
+ 'settings.php',
+ __( 'Domain Mapping', 'wp-domain-mapping' ),
+ __( 'Domain Mapping', 'wp-domain-mapping' ),
+ 'manage_network',
+ 'domain-mapping',
+ array( $this, 'render_admin_page' )
+ );
+
+ add_submenu_page(
+ 'sites.php',
+ __( 'Domains', 'wp-domain-mapping' ),
+ __( 'Domains', 'wp-domain-mapping' ),
+ 'manage_network',
+ 'domains',
+ array( $this, 'render_domains_admin' )
+ );
+ }
+
+ /**
+ * Enqueue admin scripts and styles
+ */
+ public function enqueue_admin_assets( $hook ) {
+ // Only load on our admin pages
+ if ( ! in_array( $hook, array( 'sites_page_domains', 'settings_page_domain-mapping', 'tools_page_domainmapping' ) ) ) {
+ return;
+ }
+
+ wp_enqueue_style(
+ 'wp-domain-mapping-admin',
+ WP_DOMAIN_MAPPING_DIR_URL . 'assets/css/admin.css',
+ array(),
+ WP_DOMAIN_MAPPING_VERSION
+ );
+
+ wp_enqueue_script(
+ 'wp-domain-mapping-admin',
+ WP_DOMAIN_MAPPING_DIR_URL . 'assets/js/admin.js',
+ array( 'jquery' ),
+ WP_DOMAIN_MAPPING_VERSION,
+ true
+ );
+
+ wp_localize_script( 'wp-domain-mapping-admin', 'wpDomainMapping', array(
+ 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
+ 'nonce' => wp_create_nonce( 'domain_mapping' ),
+ 'messages' => array(
+ 'domainRequired' => __( 'Domain is required.', 'wp-domain-mapping' ),
+ 'siteRequired' => __( 'Site ID is required.', 'wp-domain-mapping' ),
+ 'saving' => __( 'Saving...', 'wp-domain-mapping' ),
+ 'processing' => __( 'Processing...', 'wp-domain-mapping' ),
+ 'error' => __( 'An error occurred.', 'wp-domain-mapping' ),
+ 'noSelection' => __( 'Please select at least one domain.', 'wp-domain-mapping' ),
+ )
+ ));
+ }
+
+ /**
+ * Check if sunrise.php is properly configured
+ *
+ * @param bool $die Whether to die with an error message
+ * @return bool True if there's a problem, false if everything is okay
+ */
+ public function sunrise_warning( $die = true ) {
+ if ( ! file_exists( WP_CONTENT_DIR . '/sunrise.php' ) ) {
+ if ( ! $die ) return true;
+ if ( dm_is_site_admin() ) {
+ wp_die(
+ sprintf(
+ /* translators: %1$s: Content directory, %2$s: WordPress install path */
+ esc_html__( 'Please copy sunrise.php to %1$s/sunrise.php and ensure the SUNRISE definition is in %2$swp-config.php', 'wp-domain-mapping' ),
+ esc_html( WP_CONTENT_DIR ),
+ esc_html( ABSPATH )
+ )
+ );
+ } else {
+ wp_die( esc_html__( "This plugin has not been configured correctly yet.", 'wp-domain-mapping' ) );
+ }
+ } elseif ( ! defined( 'SUNRISE' ) ) {
+ if ( ! $die ) return true;
+ if ( dm_is_site_admin() ) {
+ wp_die(
+ sprintf(
+ /* translators: %s: WordPress install path */
+ esc_html__( 'Please uncomment the line define( \'SUNRISE\', \'on\' ); or add it to your %swp-config.php', 'wp-domain-mapping' ),
+ esc_html( ABSPATH )
+ )
+ );
+ } else {
+ wp_die( esc_html__( "This plugin has not been configured correctly yet.", 'wp-domain-mapping' ) );
+ }
+ } elseif ( ! defined( 'SUNRISE_LOADED' ) ) {
+ if ( ! $die ) return true;
+ if ( dm_is_site_admin() ) {
+ wp_die(
+ sprintf(
+ /* translators: %s: WordPress install path */
+ esc_html__( 'Please edit your %swp-config.php and move the line define( \'SUNRISE\', \'on\' ); above the last require_once() in that file or make sure you updated sunrise.php.', 'wp-domain-mapping' ),
+ esc_html( ABSPATH )
+ )
+ );
+ } else {
+ wp_die( esc_html__( "This plugin has not been configured correctly yet.", 'wp-domain-mapping' ) );
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Display warning message for network configuration
+ */
+ public function domain_mapping_warning() {
+ echo '' . esc_html__( 'Domain Mapping Disabled.', 'wp-domain-mapping' ) . ' ' .
+ sprintf(
+ /* translators: %s: URL to WordPress network creation documentation */
+ wp_kses(
+ __( 'You must create a network for it to work.', 'wp-domain-mapping' ),
+ array( 'a' => array( 'href' => array() ) )
+ ),
+ 'http://codex.wordpress.org/Create_A_Network'
+ ) . ' ';
+ }
+
+ /**
+ * AJAX handler for domain actions
+ */
+ public function ajax_handle_actions() {
+ check_ajax_referer( 'domain_mapping', 'nonce' );
+
+ if ( ! current_user_can( 'manage_network' ) ) {
+ wp_send_json_error( __( 'Permission denied.', 'wp-domain-mapping' ) );
+ }
+
+ global $wpdb;
+
+ $action = sanitize_text_field( $_POST['action_type'] );
+ $domain = dm_clean_domain( sanitize_text_field( isset( $_POST['domain'] ) ? strtolower( $_POST['domain'] ) : '' ) );
+ $blog_id = isset( $_POST['blog_id'] ) ? absint( $_POST['blog_id'] ) : 0;
+ $active = isset( $_POST['active'] ) ? absint( $_POST['active'] ) : 0;
+ $orig_domain = isset( $_POST['orig_domain'] ) ? dm_clean_domain( sanitize_text_field( $_POST['orig_domain'] ) ) : '';
+ $current_user_id = get_current_user_id();
+
+ switch ( $action ) {
+ case 'save':
+ if ( $blog_id != 0 && $blog_id != 1 ) {
+ // Validate domain format
+ if ( ! dm_validate_domain( $domain ) ) {
+ wp_send_json_error( __( 'Invalid domain format.', 'wp-domain-mapping' ) );
+ }
+
+ // Check if domain exists for another blog
+ $exists = $wpdb->get_var( $wpdb->prepare(
+ "SELECT domain FROM {$this->tables['domains']} WHERE blog_id != %d AND domain = %s",
+ $blog_id, $domain
+ ));
+
+ if ( null == $exists ) {
+ $wpdb->query( 'START TRANSACTION' );
+
+ try {
+ if ( empty( $orig_domain ) ) {
+ // Insert new domain
+ $success = $wpdb->insert(
+ $this->tables['domains'],
+ array(
+ 'blog_id' => $blog_id,
+ 'domain' => $domain,
+ 'active' => $active
+ ),
+ array( '%d', '%s', '%d' )
+ );
+
+ if ( $success ) {
+ // Log the action
+ dm_log_action( 'add', $domain, $blog_id, $current_user_id );
+
+ $wpdb->query( 'COMMIT' );
+ wp_send_json_success( __( 'Domain added successfully.', 'wp-domain-mapping' ) );
+ } else {
+ $wpdb->query( 'ROLLBACK' );
+ wp_send_json_error( __( 'Failed to add domain.', 'wp-domain-mapping' ) );
+ }
+ } else {
+ // Update existing domain
+ $success = $wpdb->update(
+ $this->tables['domains'],
+ array(
+ 'blog_id' => $blog_id,
+ 'domain' => $domain,
+ 'active' => $active
+ ),
+ array( 'domain' => $orig_domain ),
+ array( '%d', '%s', '%d' ),
+ array( '%s' )
+ );
+
+ if ( $success !== false ) {
+ // Log the action
+ dm_log_action( 'edit', $domain, $blog_id, $current_user_id );
+
+ $wpdb->query( 'COMMIT' );
+ wp_send_json_success( __( 'Domain updated successfully.', 'wp-domain-mapping' ) );
+ } else {
+ $wpdb->query( 'ROLLBACK' );
+ wp_send_json_error( __( 'No changes were made or update failed.', 'wp-domain-mapping' ) );
+ }
+ }
+ } catch ( Exception $e ) {
+ $wpdb->query( 'ROLLBACK' );
+ wp_send_json_error( __( 'An error occurred while saving domain.', 'wp-domain-mapping' ) );
+ }
+ } else {
+ wp_send_json_error( __( 'Domain already exists for another site.', 'wp-domain-mapping' ) );
+ }
+ } else {
+ wp_send_json_error( __( 'Invalid site ID.', 'wp-domain-mapping' ) );
+ }
+ break;
+
+ case 'delete':
+ $domains = isset( $_POST['domains'] ) ? array_map( 'sanitize_text_field', (array) $_POST['domains'] ) : array( $domain );
+
+ $wpdb->query( 'START TRANSACTION' );
+ $deleted = 0;
+
+ try {
+ foreach ( $domains as $del_domain ) {
+ if ( empty( $del_domain ) ) continue;
+
+ // Get blog_id before deletion for logging
+ $affected_blog_id = $wpdb->get_var( $wpdb->prepare(
+ "SELECT blog_id FROM {$this->tables['domains']} WHERE domain = %s",
+ $del_domain
+ ));
+
+ if ( $affected_blog_id ) {
+ // Delete the domain
+ $result = $wpdb->delete(
+ $this->tables['domains'],
+ array( 'domain' => $del_domain ),
+ array( '%s' )
+ );
+
+ if ( $result ) {
+ $deleted++;
+
+ // Log the action
+ dm_log_action( 'delete', $del_domain, $affected_blog_id, $current_user_id );
+ }
+ }
+ }
+
+ if ( $deleted > 0 ) {
+ $wpdb->query( 'COMMIT' );
+ $message = sprintf(
+ _n(
+ 'Domain deleted successfully.',
+ '%d domains deleted successfully.',
+ $deleted,
+ 'wp-domain-mapping'
+ ),
+ $deleted
+ );
+ wp_send_json_success( $message );
+ } else {
+ $wpdb->query( 'ROLLBACK' );
+ wp_send_json_error( __( 'No domains were deleted.', 'wp-domain-mapping' ) );
+ }
+ } catch ( Exception $e ) {
+ $wpdb->query( 'ROLLBACK' );
+ wp_send_json_error( __( 'An error occurred while deleting domains.', 'wp-domain-mapping' ) );
+ }
+ break;
+
+ default:
+ wp_send_json_error( __( 'Invalid action.', 'wp-domain-mapping' ) );
+ }
+ }
+
+ /**
+ * Handle user domain mapping actions
+ */
+ public function handle_user_domain_actions() {
+ global $wpdb, $parent_file;
+
+ $url = add_query_arg( array( 'page' => 'domainmapping' ), admin_url( $parent_file ) );
+
+ if ( ! empty( $_POST['action'] ) ) {
+ $domain = isset( $_POST['domain'] ) ? sanitize_text_field( $_POST['domain'] ) : '';
+
+ if ( empty( $domain ) ) {
+ wp_die( esc_html__( "You must enter a domain", 'wp-domain-mapping' ) );
+ }
+
+ check_admin_referer( 'domain_mapping' );
+
+ do_action( 'dm_handle_actions_init', $domain );
+
+ switch ( $_POST['action'] ) {
+ case "add":
+ do_action( 'dm_handle_actions_add', $domain );
+
+ // Validate domain format
+ if ( ! dm_validate_domain( $domain ) ) {
+ wp_die( esc_html__( "Invalid domain format", 'wp-domain-mapping' ) );
+ }
+
+ // Check if domain already exists
+ $domain_exists = $wpdb->get_row( $wpdb->prepare(
+ "SELECT blog_id FROM {$wpdb->blogs} WHERE domain = %s OR (SELECT blog_id FROM {$this->tables['domains']} WHERE domain = %s)",
+ $domain, $domain
+ ));
+
+ if ( null == $domain_exists ) {
+ // If primary, reset other domains to not primary
+ if ( isset( $_POST['primary'] ) && $_POST['primary'] ) {
+ $wpdb->update(
+ $this->tables['domains'],
+ array( 'active' => 0 ),
+ array( 'blog_id' => $wpdb->blogid ),
+ array( '%d' ),
+ array( '%d' )
+ );
+ }
+
+ // Insert new domain
+ $wpdb->insert(
+ $this->tables['domains'],
+ array(
+ 'blog_id' => $wpdb->blogid,
+ 'domain' => $domain,
+ 'active' => isset( $_POST['primary'] ) ? 1 : 0
+ ),
+ array( '%d', '%s', '%d' )
+ );
+
+ wp_redirect( add_query_arg( array( 'updated' => 'add' ), $url ) );
+ exit;
+ } else {
+ wp_redirect( add_query_arg( array( 'updated' => 'exists' ), $url ) );
+ exit;
+ }
+ break;
+
+ case "primary":
+ do_action( 'dm_handle_actions_primary', $domain );
+
+ // Reset all domains to not primary
+ $wpdb->update(
+ $this->tables['domains'],
+ array( 'active' => 0 ),
+ array( 'blog_id' => $wpdb->blogid ),
+ array( '%d' ),
+ array( '%d' )
+ );
+
+ // Check if domain is not the original domain
+ $core = WP_Domain_Mapping_Core::get_instance();
+ $orig_url = parse_url( $core->get_original_url( 'siteurl' ) );
+
+ if ( $domain != $orig_url['host'] ) {
+ // Set the selected domain as primary
+ $wpdb->update(
+ $this->tables['domains'],
+ array( 'active' => 1 ),
+ array(
+ 'domain' => $domain,
+ 'blog_id' => $wpdb->blogid
+ ),
+ array( '%d' ),
+ array( '%s', '%d' )
+ );
+ }
+
+ wp_redirect( add_query_arg( array( 'updated' => 'primary' ), $url ) );
+ exit;
+ break;
+ }
+ } elseif ( isset( $_GET['action'] ) && $_GET['action'] == 'delete' ) {
+ $domain = sanitize_text_field( $_GET['domain'] );
+
+ if ( empty( $domain ) ) {
+ wp_die( esc_html__( "You must enter a domain", 'wp-domain-mapping' ) );
+ }
+
+ check_admin_referer( "delete" . $_GET['domain'] );
+
+ do_action( 'dm_handle_actions_del', $domain );
+
+ // Delete the domain
+ $wpdb->delete(
+ $this->tables['domains'],
+ array( 'domain' => $domain ),
+ array( '%s' )
+ );
+
+ wp_redirect( add_query_arg( array( 'updated' => 'del' ), $url ) );
+ exit;
+ }
+ }
+
+ /**
+ * Render domains admin page
+ */
+ public function render_domains_admin() {
+ if ( ! dm_is_site_admin() ) {
+ return false;
+ }
+
+ $this->sunrise_warning();
+
+ // Include the admin page template
+ dm_render_domains_page();
+ }
+
+ /**
+ * Render admin configuration page
+ */
+ public function render_admin_page() {
+ if ( ! dm_is_site_admin() ) {
+ return false;
+ }
+
+ $this->sunrise_warning();
+
+ global $current_site;
+ if ( isset($current_site->path) && $current_site->path != "/" ) {
+ wp_die( sprintf(
+ esc_html__( "Warning! This plugin will only work if WordPress is installed in the root directory of your webserver. It is currently installed in '%s'.", "wp-domain-mapping" ),
+ esc_html( $current_site->path )
+ ));
+ }
+
+ // Initialize options if needed
+ if ( get_site_option( 'dm_remote_login', 'NA' ) == 'NA' ) {
+ add_site_option( 'dm_remote_login', 1 );
+ }
+
+ if ( get_site_option( 'dm_redirect_admin', 'NA' ) == 'NA' ) {
+ add_site_option( 'dm_redirect_admin', 1 );
+ }
+
+ if ( get_site_option( 'dm_user_settings', 'NA' ) == 'NA' ) {
+ add_site_option( 'dm_user_settings', 1 );
+ }
+
+ // Handle form submission
+ if ( ! empty( $_POST['action'] ) && $_POST['action'] == 'update' ) {
+ check_admin_referer( 'domain_mapping' );
+
+ // Validate and save IP addresses
+ $ipok = true;
+ $ipaddresses = explode( ',', sanitize_text_field( $_POST['ipaddress'] ) );
+
+ foreach ( $ipaddresses as $address ) {
+ if ( ( $ip = trim( $address ) ) && ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
+ $ipok = false;
+ break;
+ }
+ }
+
+ if ( $ipok ) {
+ update_site_option( 'dm_ipaddress', sanitize_text_field( $_POST['ipaddress'] ) );
+ }
+
+ // Save remote login option
+ if ( intval( $_POST['always_redirect_admin'] ) == 0 ) {
+ $_POST['dm_remote_login'] = 0;
+ }
+
+ update_site_option( 'dm_remote_login', intval( $_POST['dm_remote_login'] ) );
+
+ // Validate and save CNAME
+ if ( ! preg_match( '/(--|\.\.)/', $_POST['cname'] ) && preg_match( '|^([a-zA-Z0-9-\.])+$|', $_POST['cname'] ) ) {
+ update_site_option( 'dm_cname', sanitize_text_field( $_POST['cname'] ) );
+ } else {
+ update_site_option( 'dm_cname', '' );
+ }
+
+ // Save other options
+ update_site_option( 'dm_301_redirect', isset( $_POST['permanent_redirect'] ) ? intval( $_POST['permanent_redirect'] ) : 0 );
+ update_site_option( 'dm_redirect_admin', isset( $_POST['always_redirect_admin'] ) ? intval( $_POST['always_redirect_admin'] ) : 0 );
+ update_site_option( 'dm_user_settings', isset( $_POST['dm_user_settings'] ) ? intval( $_POST['dm_user_settings'] ) : 0 );
+ update_site_option( 'dm_no_primary_domain', isset( $_POST['dm_no_primary_domain'] ) ? intval( $_POST['dm_no_primary_domain'] ) : 0 );
+
+ // Add settings saved notice
+ add_settings_error(
+ 'dm_settings',
+ 'settings_updated',
+ __( 'Settings saved.', 'wp-domain-mapping' ),
+ 'updated'
+ );
+ }
+
+ // Include the admin page template
+ dm_render_admin_page();
+ }
+
+ /**
+ * Render user management page
+ */
+ public function render_manage_page() {
+ global $wpdb, $parent_file;
+
+ if ( isset( $_GET['updated'] ) ) {
+ do_action( 'dm_echo_updated_msg' );
+ }
+
+ $this->sunrise_warning();
+
+ if ( ! get_site_option( 'dm_ipaddress' ) && ! get_site_option( 'dm_cname' ) ) {
+ if ( dm_is_site_admin() ) {
+ echo wp_kses(
+ __( "Please set the IP address or CNAME of your server in the site admin page.", 'wp-domain-mapping' ),
+ array( 'a' => array( 'href' => array() ) )
+ );
+ } else {
+ esc_html_e( "This plugin has not been configured correctly yet.", 'wp-domain-mapping' );
+ }
+ return false;
+ }
+
+ $protocol = is_ssl() ? 'https://' : 'http://';
+ $domains = dm_get_domains_by_blog_id( $wpdb->blogid );
+
+ // Include the user page template
+ dm_render_user_page( $protocol, $domains );
+ }
+
+ /**
+ * Add "Domains" link to sites list
+ *
+ * @param array $actions The row actions
+ * @param int $blog_id The blog ID
+ * @return array Modified actions
+ */
+ public function add_domain_link_to_sites( $actions, $blog_id ) {
+ $domains_url = add_query_arg(
+ array( 'page' => 'domains', 'blog_id' => $blog_id ),
+ network_admin_url( 'sites.php' )
+ );
+
+ $actions['domains'] = '' .
+ esc_html__( 'Domains', 'wp-domain-mapping' ) . '';
+
+ return $actions;
+ }
+
+ /**
+ * Add domain mapping columns to sites list
+ *
+ * @param array $columns The columns
+ * @return array Modified columns
+ */
+ public function add_domain_mapping_columns( $columns ) {
+ $columns['map'] = __( 'Mapping' );
+ return $columns;
+ }
+
+ /**
+ * Display domain mapping column content
+ *
+ * @param string $column The column name
+ * @param int $blog_id The blog ID
+ */
+ public function display_domain_mapping_column( $column, $blog_id ) {
+ global $wpdb;
+ static $maps = false;
+
+ if ( $column == 'map' ) {
+ if ( $maps === false ) {
+ $work = $wpdb->get_results( "SELECT blog_id, domain FROM {$this->tables['domains']} ORDER BY blog_id" );
+ $maps = array();
+
+ if ( $work ) {
+ foreach ( $work as $blog ) {
+ $maps[$blog->blog_id][] = $blog->domain;
+ }
+ }
+ }
+
+ if ( ! empty( $maps[$blog_id] ) && is_array( $maps[$blog_id] ) ) {
+ foreach ( $maps[$blog_id] as $blog ) {
+ echo esc_html( $blog ) . ' ';
+ }
+ }
+ }
+ }
+
+ /**
+ * Default updated messages
+ */
+ public function echo_default_updated_msg() {
+ if ( ! isset( $_GET['updated'] ) ) {
+ return;
+ }
+
+ switch ( $_GET['updated'] ) {
+ case "add":
+ $msg = __( 'New domain added.', 'wp-domain-mapping' );
+ break;
+ case "exists":
+ $msg = __( 'New domain already exists.', 'wp-domain-mapping' );
+ break;
+ case "primary":
+ $msg = __( 'New primary domain.', 'wp-domain-mapping' );
+ break;
+ case "del":
+ $msg = __( 'Domain deleted.', 'wp-domain-mapping' );
+ break;
+ default:
+ return;
+ }
+
+ echo "";
+ }
+}
+
+// Add default updated messages action
+add_action( 'dm_echo_updated_msg', array( WP_Domain_Mapping_Admin::class, 'echo_default_updated_msg' ) );
diff --git a/includes/class-core.php b/includes/class-core.php
new file mode 100644
index 0000000..10e1c4e
--- /dev/null
+++ b/includes/class-core.php
@@ -0,0 +1,593 @@
+tables = dm_get_table_names();
+ $this->init();
+ }
+
+ /**
+ * Initialize core functionality
+ */
+ private function init() {
+ // Setup general hooks
+ $this->setup_general_hooks();
+
+ // Setup domain mapping filters if DOMAIN_MAPPING is defined
+ if ( defined( 'DOMAIN_MAPPING' ) ) {
+ $this->setup_domain_mapping_filters();
+ } else {
+ add_filter( 'admin_url', array( $this, 'domain_mapping_adminurl' ), 10, 3 );
+ }
+ }
+
+ /**
+ * Setup general hooks
+ */
+ private function setup_general_hooks() {
+ // Delete domain mappings when a blog is deleted
+ add_action( 'delete_blog', array( $this, 'delete_blog_domain_mapping' ), 1, 2 );
+
+ // Handle domain redirection
+ add_action( 'template_redirect', array( $this, 'redirect_to_mapped_domain' ) );
+
+ // Handle admin area redirection
+ add_action( 'admin_init', array( $this, 'redirect_admin' ) );
+
+ // Handle remote login
+ if ( isset( $_GET['dm'] ) ) {
+ add_action( 'template_redirect', array( $this, 'remote_login_js' ) );
+ }
+ }
+
+ /**
+ * Setup domain mapping filters when DOMAIN_MAPPING is defined
+ */
+ private function setup_domain_mapping_filters() {
+ add_filter( 'plugins_url', array( $this, 'domain_mapping_plugins_uri' ), 1 );
+ add_filter( 'theme_root_uri', array( $this, 'domain_mapping_themes_uri' ), 1 );
+ add_filter( 'pre_option_siteurl', array( $this, 'domain_mapping_siteurl' ) );
+ add_filter( 'pre_option_home', array( $this, 'domain_mapping_siteurl' ) );
+ add_filter( 'the_content', array( $this, 'domain_mapping_post_content' ) );
+ add_action( 'wp_head', array( $this, 'remote_login_js_loader' ) );
+ add_action( 'login_head', array( $this, 'redirect_login_to_orig' ) );
+ add_action( 'wp_logout', array( $this, 'remote_logout_loader' ), 9999 );
+
+ add_filter( 'stylesheet_uri', array( $this, 'domain_mapping_post_content' ) );
+ add_filter( 'stylesheet_directory', array( $this, 'domain_mapping_post_content' ) );
+ add_filter( 'stylesheet_directory_uri', array( $this, 'domain_mapping_post_content' ) );
+ add_filter( 'template_directory', array( $this, 'domain_mapping_post_content' ) );
+ add_filter( 'template_directory_uri', array( $this, 'domain_mapping_post_content' ) );
+ add_filter( 'plugins_url', array( $this, 'domain_mapping_post_content' ) );
+ }
+
+ /**
+ * Create required database tables
+ */
+ public function create_tables() {
+ global $wpdb;
+
+ // Only network admins can create tables
+ if ( ! dm_is_site_admin() ) {
+ return;
+ }
+
+ // Initialize remote login hash
+ $this->get_hash();
+
+ // Use static variable to prevent repeated execution
+ static $tables_created = false;
+ if ( $tables_created ) {
+ return;
+ }
+
+ $created = 0;
+ $charset_collate = $wpdb->get_charset_collate();
+
+ // Create domain_mapping table
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '{$this->tables['domains']}'" ) != $this->tables['domains'] ) {
+ $wpdb->query( "CREATE TABLE IF NOT EXISTS `{$this->tables['domains']}` (
+ `id` bigint(20) NOT NULL auto_increment,
+ `blog_id` bigint(20) NOT NULL,
+ `domain` varchar(255) NOT NULL,
+ `active` tinyint(4) default '1',
+ PRIMARY KEY (`id`),
+ KEY `blog_id` (`blog_id`,`domain`,`active`)
+ ) $charset_collate;" );
+ $created = 1;
+ }
+
+ // Create domain_mapping_logins table
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '{$this->tables['logins']}'" ) != $this->tables['logins'] ) {
+ $wpdb->query( "CREATE TABLE IF NOT EXISTS `{$this->tables['logins']}` (
+ `id` varchar(32) NOT NULL,
+ `user_id` bigint(20) NOT NULL,
+ `blog_id` bigint(20) NOT NULL,
+ `t` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`)
+ ) $charset_collate;" );
+ $created = 1;
+ }
+
+ // Create domain_mapping_logs table
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '{$this->tables['logs']}'" ) != $this->tables['logs'] ) {
+ $wpdb->query( "CREATE TABLE IF NOT EXISTS `{$this->tables['logs']}` (
+ `id` bigint(20) NOT NULL auto_increment,
+ `user_id` bigint(20) NOT NULL,
+ `action` varchar(50) NOT NULL,
+ `domain` varchar(255) NOT NULL,
+ `blog_id` bigint(20) NOT NULL,
+ `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`)
+ ) $charset_collate;" );
+ $created = 1;
+ }
+
+ // If any table was created, show success message and mark as created
+ if ( $created ) {
+ add_action( 'admin_notices', function() {
+ echo '' .
+ esc_html__( 'Domain mapping database tables created.', 'wp-domain-mapping' ) .
+ ' ';
+ });
+ $tables_created = true;
+ }
+ }
+
+ /**
+ * Delete blog domain mappings when a blog is deleted
+ *
+ * @param int $blog_id The blog ID
+ * @param bool $drop Whether to drop tables
+ */
+ public function delete_blog_domain_mapping( $blog_id, $drop ) {
+ global $wpdb;
+
+ if ( $blog_id && $drop ) {
+ $domains = $wpdb->get_col( $wpdb->prepare(
+ "SELECT domain FROM {$this->tables['domains']} WHERE blog_id = %d",
+ $blog_id
+ ));
+
+ do_action( 'dm_delete_blog_domain_mappings', $domains );
+
+ $wpdb->delete(
+ $this->tables['domains'],
+ array( 'blog_id' => $blog_id ),
+ array( '%d' )
+ );
+ }
+ }
+
+ /**
+ * Redirect to mapped domain
+ */
+ public function redirect_to_mapped_domain() {
+ global $current_blog, $wpdb;
+
+ if ( is_main_site() || ( isset( $_GET['preview'] ) && $_GET['preview'] == 'true' ) ||
+ ( isset( $_POST['customize'] ) && isset( $_POST['theme'] ) && $_POST['customize'] == 'on' ) ) {
+ return;
+ }
+
+ $protocol = is_ssl() ? 'https://' : 'http://';
+ $url = $this->domain_mapping_siteurl( false );
+
+ if ( $url && $url != untrailingslashit( $protocol . $current_blog->domain . $current_blog->path ) ) {
+ $redirect = get_site_option( 'dm_301_redirect' ) ? '301' : '302';
+
+ if ( ( defined( 'VHOST' ) && constant("VHOST") != 'yes' ) ||
+ ( defined( 'SUBDOMAIN_INSTALL' ) && constant( 'SUBDOMAIN_INSTALL' ) == false ) ) {
+ $_SERVER['REQUEST_URI'] = str_replace( $current_blog->path, '/', $_SERVER['REQUEST_URI'] );
+ }
+
+ wp_redirect( $url . $_SERVER['REQUEST_URI'], $redirect );
+ exit;
+ }
+ }
+
+ /**
+ * Redirect admin area if needed
+ */
+ public function redirect_admin() {
+ if ( strpos( $_SERVER['REQUEST_URI'], 'wp-admin/admin-ajax.php' ) !== false ) {
+ return;
+ }
+
+ if ( get_site_option( 'dm_redirect_admin' ) ) {
+ $url = $this->get_original_url( 'siteurl' );
+
+ if ( false === strpos( $url, $_SERVER['HTTP_HOST'] ) ) {
+ wp_redirect( untrailingslashit( $url ) . $_SERVER['REQUEST_URI'] );
+ exit;
+ }
+ } else {
+ global $current_blog;
+ $url = $this->domain_mapping_siteurl( false );
+ $request_uri = str_replace( $current_blog->path, '/', $_SERVER['REQUEST_URI'] );
+
+ if ( false === strpos( $url, $_SERVER['HTTP_HOST'] ) ) {
+ wp_redirect( str_replace( '//wp-admin', '/wp-admin', trailingslashit( $url ) . $request_uri ) );
+ exit;
+ }
+ }
+ }
+
+ /**
+ * Map domain for site URL
+ *
+ * @param string $setting The setting value
+ * @return string The mapped URL
+ */
+ public function domain_mapping_siteurl( $setting ) {
+ global $wpdb, $current_blog;
+ static $return_url = array();
+
+ // Check if already cached
+ if ( isset( $return_url[$wpdb->blogid] ) ) {
+ if ( $return_url[$wpdb->blogid] !== false ) {
+ return $return_url[$wpdb->blogid];
+ }
+ return $setting;
+ }
+
+ $s = $wpdb->suppress_errors();
+
+ // Handle primary domain checking based on settings
+ if ( get_site_option( 'dm_no_primary_domain' ) == 1 ) {
+ if ( isset( $_SERVER['HTTP_HOST'] ) ) {
+ $domain = $wpdb->get_var( $wpdb->prepare(
+ "SELECT domain FROM {$this->tables['domains']} WHERE blog_id = %d AND domain = %s LIMIT 1",
+ $wpdb->blogid, $_SERVER['HTTP_HOST']
+ ));
+
+ if ( null == $domain ) {
+ $return_url[$wpdb->blogid] = untrailingslashit( $this->get_original_url( "siteurl" ) );
+ return $return_url[$wpdb->blogid];
+ }
+ }
+ } else {
+ $domain = $wpdb->get_var( $wpdb->prepare(
+ "SELECT domain FROM {$this->tables['domains']} WHERE blog_id = %d AND active = 1 LIMIT 1",
+ $wpdb->blogid
+ ));
+
+ if ( null == $domain ) {
+ $return_url[$wpdb->blogid] = untrailingslashit( $this->get_original_url( "siteurl" ) );
+ return $return_url[$wpdb->blogid];
+ }
+ }
+
+ $wpdb->suppress_errors( $s );
+
+ $protocol = is_ssl() ? 'https://' : 'http://';
+
+ if ( $domain ) {
+ $return_url[$wpdb->blogid] = untrailingslashit( $protocol . $domain );
+ $setting = $return_url[$wpdb->blogid];
+ } else {
+ $return_url[$wpdb->blogid] = false;
+ }
+
+ return $setting;
+ }
+
+ /**
+ * Get the original URL for a site
+ *
+ * @param string $url_type The URL type (siteurl or home)
+ * @param int $blog_id The blog ID
+ * @return string The original URL
+ */
+ public function get_original_url( $url_type, $blog_id = 0 ) {
+ global $wpdb;
+ $id = $blog_id ?: $wpdb->blogid;
+ static $orig_urls = array();
+
+ if ( ! isset( $orig_urls[$id] ) ) {
+ // Remove filter to avoid infinite loop
+ if ( defined( 'DOMAIN_MAPPING' ) ) {
+ remove_filter( 'pre_option_' . $url_type, array( $this, 'domain_mapping_siteurl' ) );
+ }
+
+ $orig_url = $blog_id == 0 ? get_option( $url_type ) : get_blog_option( $blog_id, $url_type );
+ $orig_url = is_ssl() ? str_replace( "http://", "https://", $orig_url ) : str_replace( "https://", "http://", $orig_url );
+ $orig_urls[$id] = $orig_url;
+
+ // Restore filter
+ if ( defined( 'DOMAIN_MAPPING' ) ) {
+ add_filter( 'pre_option_' . $url_type, array( $this, 'domain_mapping_siteurl' ) );
+ }
+ }
+
+ return $orig_urls[$id];
+ }
+
+ /**
+ * Map domain for admin URL
+ *
+ * @param string $url The URL
+ * @param string $path The path
+ * @param int $blog_id The blog ID
+ * @return string The mapped URL
+ */
+ public function domain_mapping_adminurl( $url, $path, $blog_id = 0 ) {
+ $index = strpos( $url, '/wp-admin' );
+
+ if ( $index !== false ) {
+ $url = $this->get_original_url( 'siteurl', $blog_id ) . substr( $url, $index );
+
+ if ( ( is_ssl() || force_ssl_admin() ) && 0 === strpos( $url, 'http://' ) ) {
+ $url = 'https://' . substr( $url, 7 );
+ }
+ }
+
+ return $url;
+ }
+
+ /**
+ * Map domains in post content
+ *
+ * @param string $post_content The post content
+ * @return string The mapped content
+ */
+ public function domain_mapping_post_content( $post_content ) {
+ $orig_url = $this->get_original_url( 'siteurl' );
+ $url = $this->domain_mapping_siteurl( 'NA' );
+
+ if ( $url == 'NA' ) {
+ return $post_content;
+ }
+
+ return str_replace( $orig_url, $url, $post_content );
+ }
+
+ /**
+ * Map domains in plugin URLs
+ *
+ * @param string $full_url The full URL
+ * @param string $path The path
+ * @param string $plugin The plugin
+ * @return string The mapped URL
+ */
+ public function domain_mapping_plugins_uri( $full_url, $path = null, $plugin = null ) {
+ return get_option( 'siteurl' ) . substr( $full_url, stripos( $full_url, PLUGINDIR ) - 1 );
+ }
+
+ /**
+ * Map domains in theme URLs
+ *
+ * @param string $full_url The full URL
+ * @return string The mapped URL
+ */
+ public function domain_mapping_themes_uri( $full_url ) {
+ return str_replace( $this->get_original_url( 'siteurl' ), get_option( 'siteurl' ), $full_url );
+ }
+
+ /**
+ * Get the domain mapping hash
+ *
+ * @return string The hash
+ */
+ public function get_hash() {
+ $remote_login_hash = get_site_option( 'dm_hash' );
+
+ if ( null == $remote_login_hash ) {
+ $remote_login_hash = md5( time() );
+ update_site_option( 'dm_hash', $remote_login_hash );
+ }
+
+ return $remote_login_hash;
+ }
+
+ /**
+ * Redirect login to original domain
+ */
+ public function redirect_login_to_orig() {
+ if ( ! get_site_option( 'dm_remote_login' ) ||
+ ( isset( $_GET['action'] ) && $_GET['action'] == 'logout' ) ||
+ isset( $_GET['loggedout'] ) ) {
+ return;
+ }
+
+ $url = $this->get_original_url( 'siteurl' );
+
+ if ( $url != site_url() ) {
+ $url .= "/wp-login.php";
+ echo "";
+ }
+ }
+
+ /**
+ * Handle logout across domains
+ */
+ public function remote_logout_loader() {
+ global $current_site, $current_blog, $wpdb;
+
+ $protocol = is_ssl() ? 'https://' : 'http://';
+ $hash = $this->get_hash();
+ $key = md5( time() );
+
+ $wpdb->insert(
+ $this->tables['logins'],
+ array(
+ 'id' => $key,
+ 'user_id' => 0,
+ 'blog_id' => $current_blog->blog_id,
+ 't' => current_time( 'mysql' )
+ ),
+ array( '%s', '%d', '%d', '%s' )
+ );
+
+ if ( get_site_option( 'dm_redirect_admin' ) ) {
+ wp_redirect( $protocol . $current_site->domain . $current_site->path .
+ "?dm={$hash}&action=logout&blogid={$current_blog->blog_id}&k={$key}&t=" . mt_rand() );
+ exit;
+ }
+ }
+
+ /**
+ * Handle remote login JS
+ */
+ public function remote_login_js() {
+ global $current_blog, $current_user, $wpdb;
+
+ if ( 0 == get_site_option( 'dm_remote_login' ) ) {
+ return;
+ }
+
+ $hash = $this->get_hash();
+ $protocol = is_ssl() ? 'https://' : 'http://';
+
+ if ( $_GET['dm'] == $hash ) {
+ if ( $_GET['action'] == 'load' ) {
+ if ( ! is_user_logged_in() ) {
+ exit;
+ }
+
+ $key = md5( time() . mt_rand() );
+
+ $wpdb->insert(
+ $this->tables['logins'],
+ array(
+ 'id' => $key,
+ 'user_id' => $current_user->ID,
+ 'blog_id' => $_GET['blogid'],
+ 't' => current_time( 'mysql' )
+ ),
+ array( '%s', '%d', '%d', '%s' )
+ );
+
+ $url = add_query_arg(
+ array(
+ 'action' => 'login',
+ 'dm' => $hash,
+ 'k' => $key,
+ 't' => mt_rand()
+ ),
+ $_GET['back']
+ );
+
+ echo "window.location = '" . esc_url( $url ) . "'";
+ exit;
+
+ } elseif ( $_GET['action'] == 'login' ) {
+ $details = $wpdb->get_row( $wpdb->prepare(
+ "SELECT * FROM {$this->tables['logins']} WHERE id = %s AND blog_id = %d",
+ $_GET['k'], $wpdb->blogid
+ ));
+
+ if ( $details ) {
+ if ( $details->blog_id == $wpdb->blogid ) {
+ $wpdb->delete(
+ $this->tables['logins'],
+ array( 'id' => $_GET['k'] ),
+ array( '%s' )
+ );
+
+ $wpdb->query( $wpdb->prepare(
+ "DELETE FROM {$this->tables['logins']} WHERE t < %s",
+ date( 'Y-m-d H:i:s', time() - 120 )
+ ));
+
+ wp_set_auth_cookie( $details->user_id );
+
+ wp_redirect( remove_query_arg(
+ array( 'dm', 'action', 'k', 't' ),
+ $protocol . $current_blog->domain . $_SERVER['REQUEST_URI']
+ ));
+ exit;
+
+ } else {
+ wp_die( esc_html__( "Incorrect or out of date login key", 'wp-domain-mapping' ) );
+ }
+ } else {
+ wp_die( esc_html__( "Unknown login key", 'wp-domain-mapping' ) );
+ }
+
+ } elseif ( $_GET['action'] == 'logout' ) {
+ $details = $wpdb->get_row( $wpdb->prepare(
+ "SELECT * FROM {$this->tables['logins']} WHERE id = %s AND blog_id = %d",
+ $_GET['k'], $_GET['blogid']
+ ));
+
+ if ( $details ) {
+ $wpdb->delete(
+ $this->tables['logins'],
+ array( 'id' => $_GET['k'] ),
+ array( '%s' )
+ );
+
+ $blog = get_blog_details( $_GET['blogid'] );
+ wp_clear_auth_cookie();
+ wp_redirect( trailingslashit( $blog->siteurl ) . "wp-login.php?loggedout=true" );
+ exit;
+ } else {
+ wp_die( esc_html__( "Unknown logout key", 'wp-domain-mapping' ) );
+ }
+ }
+ }
+ }
+
+ /**
+ * Add JS loader for remote login
+ */
+ public function remote_login_js_loader() {
+ global $current_site, $current_blog;
+
+ if ( 0 == get_site_option( 'dm_remote_login' ) || is_user_logged_in() ) {
+ return;
+ }
+
+ $protocol = is_ssl() ? 'https://' : 'http://';
+ $hash = $this->get_hash();
+
+ echo "";
+ }
+}
diff --git a/includes/class-tools.php b/includes/class-tools.php
index 31efabe..449bed5 100644
--- a/includes/class-tools.php
+++ b/includes/class-tools.php
@@ -1,111 +1,162 @@
db = WP_Domain_Mapping_DB::get_instance();
- $this->core = WP_Domain_Mapping_Core::get_instance();
-
+ $this->tables = dm_get_table_names();
+ $this->init();
+ }
+
+ /**
+ * Initialize tools functionality
+ */
+ private function init() {
// Setup hooks
$this->setup_hooks();
+
+ // Initialize cron for health checks
+ $this->initialize_cron();
}
-
+
+ /**
+ * Setup hooks
+ */
private function setup_hooks() {
- // Add menu pages for tools
- add_action('network_admin_menu', array($this, 'add_menu_pages'), 20);
-
- // Add Site ID to toolbar
- add_action('admin_bar_menu', array($this, 'add_site_id_to_toolbar'), 100);
-
- // Add Site ID column to sites list
- add_filter('manage_sites-network_columns', array($this, 'add_site_id_column'), 20);
- add_action('manage_sites_custom_column', array($this, 'display_site_id_column'), 10, 2);
- add_action('admin_print_styles', array($this, 'site_id_column_style'));
-
+ // Add Site ID to toolbar (only in multisite)
+ if ( is_multisite() ) {
+ add_action( 'admin_bar_menu', array( $this, 'add_site_id_to_toolbar' ), 100 );
+
+ // Add Site ID column to sites list
+ add_filter( 'manage_sites-network_columns', array( $this, 'add_site_id_column' ), 20 );
+ add_action( 'manage_sites_custom_column', array( $this, 'display_site_id_column' ), 10, 2 );
+ add_action( 'admin_print_styles', array( $this, 'site_id_column_style' ) );
+ }
+
// AJAX handlers
- add_action('wp_ajax_dm_check_domain_health', array($this, 'ajax_check_domain_health'));
- add_action('wp_ajax_dm_import_csv', array($this, 'ajax_import_csv'));
-
+ add_action( 'wp_ajax_dm_check_domain_health', array( $this, 'ajax_check_domain_health' ) );
+ add_action( 'wp_ajax_dm_import_csv', array( $this, 'ajax_import_csv' ) );
+
// Scheduled tasks
- add_action('dm_domain_health_check', array($this, 'scheduled_health_check'));
-
+ add_action( 'dm_domain_health_check', array( $this, 'scheduled_health_check' ) );
+
// Admin init actions
- add_action('admin_init', array($this, 'handle_export'));
- add_action('admin_init', array($this, 'handle_health_manual_check'));
- add_action('admin_init', array($this, 'handle_health_settings_save'));
+ add_action( 'admin_init', array( $this, 'handle_export' ) );
+ add_action( 'admin_init', array( $this, 'handle_health_manual_check' ) );
+ add_action( 'admin_init', array( $this, 'handle_health_settings_save' ) );
}
-
- public function add_menu_pages() {
- add_submenu_page(
- 'settings.php',
- __('Domain Health', 'wp-domain-mapping'),
- __('Domain Health', 'wp-domain-mapping'),
- 'manage_network',
- 'domain-mapping-health',
- 'dm_render_health_page'
- );
-
- add_submenu_page(
- 'settings.php',
- __('Import/Export Domains', 'wp-domain-mapping'),
- __('Import/Export Domains', 'wp-domain-mapping'),
- 'manage_network',
- 'domain-mapping-import-export',
- 'dm_render_import_export_page'
- );
+
+ /**
+ * Initialize cron for health checks
+ */
+ private function initialize_cron() {
+ // Register activation/deactivation hooks
+ register_activation_hook( WP_DOMAIN_MAPPING_BASENAME, array( $this, 'schedule_health_check' ) );
+ register_deactivation_hook( WP_DOMAIN_MAPPING_BASENAME, array( $this, 'unschedule_health_check' ) );
}
-
+
+ // ========================================
// Site ID Display Functions
- public function add_site_id_to_toolbar($admin_bar) {
+ // ========================================
+
+ /**
+ * Add site ID to WordPress toolbar
+ *
+ * @param WP_Admin_Bar $admin_bar WordPress toolbar object
+ */
+ public function add_site_id_to_toolbar( $admin_bar ) {
// Only show to admins or network admins
- if (!current_user_can('manage_options') && !is_super_admin()) {
+ if ( ! current_user_can( 'manage_options' ) && ! is_super_admin() ) {
return;
}
$blog_id = get_current_blog_id();
- $admin_bar->add_menu(array(
+ $admin_bar->add_menu( array(
'id' => 'wp-site-ID',
- 'title' => sprintf(__('Site ID: %d', 'wp-domain-mapping'), $blog_id),
- 'href' => is_super_admin() ? esc_url(network_admin_url('site-info.php?id=' . $blog_id)) : '#',
+ 'title' => sprintf( __( 'Site ID: %d', 'wp-domain-mapping' ), $blog_id ),
+ 'href' => is_super_admin() ? esc_url( network_admin_url( 'site-info.php?id=' . $blog_id ) ) : '#',
'meta' => array(
- 'title' => is_super_admin() ? __('Edit this site', 'wp-domain-mapping') : __('Current site ID', 'wp-domain-mapping'),
+ 'title' => is_super_admin() ? __( 'Edit this site', 'wp-domain-mapping' ) : __( 'Current site ID', 'wp-domain-mapping' ),
'class' => 'dm-site-id-menu'
),
- ));
+ ) );
}
-
- public function add_site_id_column($columns) {
+
+ /**
+ * Add site ID column to sites list
+ *
+ * @param array $columns Current columns
+ * @return array Modified columns
+ */
+ public function add_site_id_column( $columns ) {
// Add site ID column after first column
- $columns = array_slice($columns, 0, 1, true) +
- array('dm_site_id' => __('Site ID', 'wp-domain-mapping')) +
- array_slice($columns, 1, count($columns) - 1, true);
+ $columns = array_slice( $columns, 0, 1, true ) +
+ array( 'dm_site_id' => __( 'Site ID', 'wp-domain-mapping' ) ) +
+ array_slice( $columns, 1, count( $columns ) - 1, true );
return $columns;
}
-
- public function display_site_id_column($column, $blog_id) {
- if ('dm_site_id' == $column) {
- echo '' . esc_html($blog_id) . '';
+
+ /**
+ * Display site ID column content
+ *
+ * @param string $column Column name
+ * @param int $blog_id Site ID
+ */
+ public function display_site_id_column( $column, $blog_id ) {
+ if ( 'dm_site_id' == $column ) {
+ echo '' . esc_html( $blog_id ) . '';
}
}
-
+
+ /**
+ * Add custom styles for site ID column
+ */
public function site_id_column_style() {
- if ('sites-network' == get_current_screen()->id) {
+ if ( 'sites-network' == get_current_screen()->id ) {
?>
|