From 31bf7145ea0e6017c20bfbb6e24b6c80e53a793f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=96=87=E6=B4=BE=E5=A4=87=E6=A1=88?=
<130886204+modiqi@users.noreply.github.com>
Date: Tue, 20 May 2025 22:52:24 +0800
Subject: [PATCH] =?UTF-8?q?v2.0=20=E9=87=8D=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
admin/domains-page.php | 514 ++++++++
admin/logs-table.php | 288 +++++
admin/settings-page.php | 522 ++++++++
admin/templates.php | 964 ++++++++++++++
admin/user-page.php | 183 +++
assets/css/admin.css | 64 +
assets/js/admin.js | 134 ++
includes/admin-ui.php | 389 ++++++
includes/class-health.php | 897 +++++++++++++
includes/class-importer.php | 592 +++++++++
includes/class-site-id.php | 132 ++
includes/class-tools.php | 709 ++++++++++
sunrise.php | 96 +-
wp-domain-mapping.php | 2434 ++++++++++++++++++++---------------
14 files changed, 6815 insertions(+), 1103 deletions(-)
create mode 100644 admin/domains-page.php
create mode 100644 admin/logs-table.php
create mode 100644 admin/settings-page.php
create mode 100644 admin/templates.php
create mode 100644 admin/user-page.php
create mode 100644 assets/css/admin.css
create mode 100644 assets/js/admin.js
create mode 100644 includes/admin-ui.php
create mode 100644 includes/class-health.php
create mode 100644 includes/class-importer.php
create mode 100644 includes/class-site-id.php
create mode 100644 includes/class-tools.php
diff --git a/admin/domains-page.php b/admin/domains-page.php
new file mode 100644
index 0000000..2899122
--- /dev/null
+++ b/admin/domains-page.php
@@ -0,0 +1,514 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ get_var( "SELECT COUNT(DISTINCT blog_id) FROM {$wpdb->base_prefix}" . WP_DOMAIN_MAPPING_TABLE_DOMAINS );
+ echo esc_html( $sites_with_domains );
+ ?>
+
+
+
+
+
+
+
+
+
+
diff --git a/admin/logs-table.php b/admin/logs-table.php
new file mode 100644
index 0000000..739649b
--- /dev/null
+++ b/admin/logs-table.php
@@ -0,0 +1,288 @@
+prepare( "action = %s", $action_filter );
+}
+
+$where_sql = $where ? ' WHERE ' . implode( ' AND ', $where ) : '';
+
+// Count total logs for pagination
+$total_logs = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->base_prefix}" . WP_DOMAIN_MAPPING_TABLE_LOGS . $where_sql );
+$total_pages = ceil( $total_logs / $per_page );
+
+// Get logs with pagination
+$logs = $wpdb->get_results( $wpdb->prepare(
+ "SELECT * FROM {$wpdb->base_prefix}" . WP_DOMAIN_MAPPING_TABLE_LOGS . $where_sql . " ORDER BY timestamp DESC LIMIT %d, %d",
+ $offset,
+ $per_page
+));
+
+// Get available actions for filter
+$actions = $wpdb->get_col( "SELECT DISTINCT action FROM {$wpdb->base_prefix}" . WP_DOMAIN_MAPPING_TABLE_LOGS );
+
+if ( ! $logs ) {
+ echo '' . esc_html__( 'No domain mapping logs available.', 'wp-domain-mapping' ) . '
';
+ return;
+}
+?>
+
+
+
+
+ 1 ) : ?>
+
+ add_query_arg( 'logs_paged', '%#%' ),
+ 'format' => '',
+ 'prev_text' => __( '«' ),
+ 'next_text' => __( '»' ),
+ 'total' => $total_pages,
+ 'current' => $paged,
+ 'mid_size' => 2,
+ 'end_size' => 1,
+ ) );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ user_id );
+ $username = $user_data
+ ? sprintf(
+ '%s ',
+ esc_url( network_admin_url( 'user-edit.php?user_id=' . $log->user_id ) ),
+ esc_html( $user_data->user_login )
+ )
+ : sprintf( esc_html__( 'User #%d', 'wp-domain-mapping' ), $log->user_id );
+
+ // Format action for display
+ switch ( $log->action ) {
+ case 'add':
+ $action_display = '' . esc_html__( 'Added', 'wp-domain-mapping' ) . ' ';
+ break;
+ case 'edit':
+ $action_display = '' . esc_html__( 'Updated', 'wp-domain-mapping' ) . ' ';
+ break;
+ case 'delete':
+ $action_display = '' . esc_html__( 'Deleted', 'wp-domain-mapping' ) . ' ';
+ break;
+ default:
+ $action_display = '' . esc_html( ucfirst( $log->action ) ) . ' ';
+ }
+
+ // Get site name
+ $site_name = get_blog_option( $log->blog_id, 'blogname', '' );
+ $site_link = ! empty( $site_name )
+ ? sprintf(
+ '%s ',
+ esc_url( network_admin_url( 'site-info.php?id=' . $log->blog_id ) ),
+ esc_html( $site_name )
+ )
+ : sprintf( esc_html__( 'Site #%d', 'wp-domain-mapping' ), $log->blog_id );
+
+ // Format timestamp for display
+ $timestamp = mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $log->timestamp );
+ $time_diff = human_time_diff( strtotime( $log->timestamp ), current_time( 'timestamp' ) );
+ ?>
+
+
+
+
+
+ domain ); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1 ) : ?>
+
+
+ add_query_arg( 'logs_paged', '%#%' ),
+ 'format' => '',
+ 'prev_text' => __( '«' ),
+ 'next_text' => __( '»' ),
+ 'total' => $total_pages,
+ 'current' => $paged,
+ 'mid_size' => 2,
+ 'end_size' => 1,
+ ) );
+ ?>
+
+
+
+
+
+
+
+
diff --git a/admin/settings-page.php b/admin/settings-page.php
new file mode 100644
index 0000000..229d0a0
--- /dev/null
+++ b/admin/settings-page.php
@@ -0,0 +1,522 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html( $dm_cname ) . ''
+ );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CNAME
+ @
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html( $dm_ipaddress ) . ''
+ );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ $ip ) :
+ ?>
+
+ A
+ @
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html( WP_CONTENT_DIR ) . ''
+ );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html( SUNRISE ) . ''
+ );
+ ?>
+
+
+ define( 'SUNRISE', 'on' );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html( COOKIE_DOMAIN ) . ''
+ );
+ ?>
+
+
+
+
+
+ base_prefix . WP_DOMAIN_MAPPING_TABLE_DOMAINS,
+ $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_LOGINS,
+ $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_LOGS,
+ ) as $table ) {
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '$table'" ) != $table ) {
+ $tables_exist = false;
+ break;
+ }
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/admin/templates.php b/admin/templates.php
new file mode 100644
index 0000000..b2ea2b9
--- /dev/null
+++ b/admin/templates.php
@@ -0,0 +1,964 @@
+
+
+
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html($dm_cname) . ''
+ );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CNAME
+ @
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html($dm_ipaddress) . ''
+ );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+ $ip) :
+ ?>
+
+ A
+ @
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html(WP_CONTENT_DIR) . ''
+ );
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html(SUNRISE) . ''
+ );
+ ?>
+
+
+ define( 'SUNRISE', 'on' );
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . esc_html(COOKIE_DOMAIN) . ''
+ );
+ ?>
+
+
+
+
+
+ base_prefix . WP_DOMAIN_MAPPING_TABLE_DOMAINS,
+ $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_LOGINS,
+ $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_LOGS,
+ ) as $table) {
+ if ($wpdb->get_var("SHOW TABLES LIKE '$table'") != $table) {
+ $tables_exist = false;
+ break;
+ }
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ get_domains_by_blog_id($wpdb->blogid);
+
+ // Display updated messages
+ if (isset($_GET['updated'])) {
+ echo '';
+ switch ($_GET['updated']) {
+ case 'add':
+ _e('Domain added successfully.', 'wp-domain-mapping');
+ break;
+ case 'exists':
+ _e('This domain is already mapped to a site.', 'wp-domain-mapping');
+ break;
+ case 'primary':
+ _e('Primary domain updated.', 'wp-domain-mapping');
+ break;
+ case 'del':
+ _e('Domain deleted successfully.', 'wp-domain-mapping');
+ break;
+ }
+ echo '
';
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ domain); ?>
+
+
+
+
+ active == 1): ?>
+
+
+
+
+
+
+
+
+
+ active != 1): ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
%s', 'wp-domain-mapping'), esc_html($cname)); ?>
+
+
+
+
+
%s', 'wp-domain-mapping'), esc_html($ipaddress)); ?>
+
+
+
+
+
+
+ base_prefix . WP_DOMAIN_MAPPING_TABLE_DOMAINS;
+ $domains = $wpdb->get_results("
+ SELECT d.*, b.domain as original_domain, b.path
+ FROM {$table} d
+ JOIN {$wpdb->blogs} b ON d.blog_id = b.blog_id
+ ORDER BY d.blog_id ASC, d.active DESC
+ ");
+
+ // Get health check results
+ $health_results = get_site_option('dm_domain_health_results', array());
+
+ // Display page
+ ?>
+
+
+
+
' .
+ __('Domain health check completed.', 'wp-domain-mapping') .
+ '
';
+ }
+
+ if (isset($_GET['settings-updated']) && $_GET['settings-updated']) {
+ echo '' .
+ __('Settings saved.', 'wp-domain-mapping') .
+ '
';
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ domain);
+ $health_data = isset($health_results[$domain_key]) ? $health_results[$domain_key] : null;
+ $site_name = get_blog_option($domain->blog_id, 'blogname', __('Unknown', 'wp-domain-mapping'));
+ ?>
+
+
+ domain); ?>
+ active) : ?>
+
+
+
+
+
+
+
+ original_domain . $domain->path); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
domain)); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = 200 && $health_data['response_code'] < 400) {
+ echo ' ';
+ } else {
+ echo ' ';
+ }
+ } else {
+ echo '-';
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' .
+ sprintf(
+ _n(
+ '%d domain mapping imported successfully.',
+ '%d domain mappings imported successfully.',
+ $count,
+ 'wp-domain-mapping'
+ ),
+ $count
+ ) .
+ '
';
+ }
+
+ if (isset($_GET['export']) && $_GET['export'] == 'success') {
+ echo '' .
+ __('Domain mappings exported successfully.', 'wp-domain-mapping') .
+ '
';
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ blog_id
+ domain
+ active
+
+
+
+
+ 1
+ example.com
+ 1
+
+
+ 2
+ example.org
+ 0
+
+
+
+
+
+ blog_id :
+ domain :
+ active :
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
%s', 'wp-domain-mapping'), esc_html($cname)); ?>
+
+
+
+
+
%s', 'wp-domain-mapping'), esc_html($ipaddress)); ?>
+
+
+
+
+
+
+
+
diff --git a/assets/css/admin.css b/assets/css/admin.css
new file mode 100644
index 0000000..fb2fd4c
--- /dev/null
+++ b/assets/css/admin.css
@@ -0,0 +1,64 @@
+.card {
+ background: #fff;
+ border: 1px solid #ccd0d4;
+ border-radius: 4px;
+ max-width: unset;
+ margin-top: 20px;
+ padding: 20px;
+}
+.styles-sync-tabs {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+ border-bottom: 1px solid #c3c4c7;
+ margin-bottom: 20px;
+}
+.styles-tab {
+ padding: 8px 16px;
+ border: none;
+ background: none;
+ cursor: pointer;
+ font-size: 14px;
+ border-bottom: 2px solid transparent;
+}
+.styles-tab.active {
+ border-bottom: 2px solid #007cba;
+ font-weight: 600;
+ background: #f0f0f1;
+}
+.styles-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; }
+
+/* Responsive Styles */
+@media screen and (max-width: 782px) {
+ .form-table th {
+ width: 100%;
+ display: block;
+ }
+ .form-table td {
+ display: block;
+ padding: 5px 0 15px;
+ }
+ .wp-list-table th.column-primary ~ th,
+ .wp-list-table td.column-primary ~ td {
+ display: none;
+ }
+ .wp-list-table th.column-primary,
+ .wp-list-table td.column-primary {
+ display: table-cell;
+ }
+}
\ No newline at end of file
diff --git a/assets/js/admin.js b/assets/js/admin.js
new file mode 100644
index 0000000..8dab280
--- /dev/null
+++ b/assets/js/admin.js
@@ -0,0 +1,134 @@
+/**
+ * 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();
+ });
+
+ // Domain form validation
+ $('#edit-domain-form').on('submit', function(e) {
+ var domain = $('#domain').val();
+ var blogId = $('#blog_id').val();
+
+ if (!domain) {
+ e.preventDefault();
+ alert(wpDomainMapping.messages.domainRequired);
+ $('#domain').focus();
+ return false;
+ }
+
+ if (!blogId) {
+ e.preventDefault();
+ alert(wpDomainMapping.messages.siteRequired);
+ $('#blog_id').focus();
+ return false;
+ }
+ });
+
+ // Check all domains
+ $('#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();
+ }
+
+ // Save domain
+ $('#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();
+
+ $.ajax({
+ url: wpDomainMapping.ajaxUrl,
+ type: 'POST',
+ data: formData,
+ success: function(response) {
+ if (response.success) {
+ showNotice('#edit-domain-status', response.data, 'success');
+ setTimeout(function() { location.reload(); }, 1000);
+ } else {
+ showNotice('#edit-domain-status', response.data || wpDomainMapping.messages.error, 'error');
+ }
+ },
+ error: function() {
+ showNotice('#edit-domain-status', wpDomainMapping.messages.error, 'error');
+ }
+ });
+ });
+
+ // Bulk 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 (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');
+ }
+ });
+ }
+ });
+
+ // Copy to clipboard functionality
+ $('.copy-to-clipboard').on('click', function() {
+ var text = $(this).data('text');
+ var tempInput = $(' ');
+ $('body').append(tempInput);
+ tempInput.val(text).select();
+ document.execCommand('copy');
+ tempInput.remove();
+
+ var $btn = $(this);
+ var originalText = $btn.text();
+ $btn.text('Copied!');
+ setTimeout(function() { $btn.text(originalText); }, 2000);
+ });
+});
\ No newline at end of file
diff --git a/includes/admin-ui.php b/includes/admin-ui.php
new file mode 100644
index 0000000..7d6b93f
--- /dev/null
+++ b/includes/admin-ui.php
@@ -0,0 +1,389 @@
+blog_id = '';
+ $row->domain = '';
+ $row->active = 1;
+ }
+ ?>
+
+ ' . esc_html__( 'No domains found.', 'wp-domain-mapping' ) . '
';
+ return;
+ }
+
+ $edit_url = network_admin_url(
+ file_exists( ABSPATH . 'wp-admin/network/site-info.php' )
+ ? 'site-info.php'
+ : ( file_exists( ABSPATH . 'wp-admin/ms-sites.php' ) ? 'ms-sites.php' : 'wpmu-blogs.php' )
+ );
+ ?>
+
+
+
+
+
+ ' .
+ esc_html__( 'Warning! Primary domains are currently disabled in network settings.', 'wp-domain-mapping' ) .
+ '
';
+ }
+}
+
+/**
+ * Ensure URL has a protocol
+ *
+ * @param string $domain Domain name
+ * @return string Domain with protocol
+ */
+function dm_ensure_protocol( $domain ) {
+ if ( preg_match( '#^https?://#', $domain ) ) {
+ return $domain;
+ }
+ return 'http://' . $domain;
+}
+
+/**
+ * Clean domain name (remove protocol and trailing slash)
+ *
+ * @param string $domain Domain name
+ * @return string Cleaned domain
+ */
+function dm_clean_domain( $domain ) {
+ // Remove protocol
+ $domain = preg_replace( '#^https?://#', '', $domain );
+
+ // Remove trailing slash
+ $domain = rtrim( $domain, '/' );
+
+ // Convert IDN to ASCII (Punycode)
+ if ( function_exists( 'idn_to_ascii' ) && preg_match( '/[^a-z0-9\-\.]/i', $domain ) ) {
+ if (defined('INTL_IDNA_VARIANT_UTS46')) {
+ // PHP 7.2+
+ $domain = idn_to_ascii( $domain, 0, INTL_IDNA_VARIANT_UTS46 );
+ } else {
+ // PHP < 7.2
+ $domain = idn_to_ascii( $domain );
+ }
+ }
+
+ return $domain;
+}
+
+/**
+ * Display IDN warning message
+ *
+ * @return string Warning message
+ */
+function dm_idn_warning() {
+ return sprintf(
+ /* translators: %s: URL to punycode converter */
+ wp_kses(
+ __( 'International Domain Names should be in punycode format.', 'wp-domain-mapping' ),
+ array( 'a' => array( 'href' => array(), 'target' => array() ) )
+ ),
+ 'https://www.punycoder.com/'
+ );
+}
+
+/**
+ * Render domain logs table
+ */
+function dm_domain_logs() {
+ global $wpdb;
+
+ $table_logs = $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_LOGS;
+
+ // Make sure the table exists
+ if ($wpdb->get_var("SHOW TABLES LIKE '$table_logs'") != $table_logs) {
+ echo '' .
+ esc_html__('Domain mapping logs table is missing. Please deactivate and reactivate the plugin.', 'wp-domain-mapping') .
+ '
';
+ return;
+ }
+
+ $logs = $wpdb->get_results( "SELECT * FROM {$table_logs} ORDER BY timestamp DESC LIMIT 50" );
+
+ if ( ! $logs ) {
+ echo '' . esc_html__( 'No logs available.', 'wp-domain-mapping' ) . '
';
+ return;
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+ user_id );
+ $username = $user_data ? $user_data->user_login : sprintf( __( 'User #%d', 'wp-domain-mapping' ), $log->user_id );
+
+ // Format action for display
+ switch ( $log->action ) {
+ case 'add':
+ $action_display = '' . __( 'Added', 'wp-domain-mapping' ) . ' ';
+ break;
+ case 'edit':
+ $action_display = '' . __( 'Updated', 'wp-domain-mapping' ) . ' ';
+ break;
+ case 'delete':
+ $action_display = '' . __( 'Deleted', 'wp-domain-mapping' ) . ' ';
+ break;
+ default:
+ $action_display = '' . esc_html( $log->action ) . ' ';
+ }
+
+ // Format timestamp for display
+ $timestamp = mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $log->timestamp );
+ ?>
+
+
+
+ domain ); ?>
+
+
+ blog_id ); ?>
+
+
+
+
+
+
+
+
+
+
+
+ db = WP_Domain_Mapping_DB::get_instance();
+
+ // 添加菜单页面
+ add_action('network_admin_menu', array($this, 'add_menu_page'), 30);
+
+ // 添加AJAX处理程序
+ add_action('wp_ajax_dm_check_domain_health', array($this, 'ajax_check_domain_health'));
+
+ // 添加计划任务钩子
+ add_action('dm_domain_health_check', array($this, 'scheduled_health_check'));
+
+ // 初始化计划任务
+ $this->initialize_cron();
+
+ // 处理手动健康检查
+ add_action('admin_init', array($this, 'handle_manual_check'));
+
+ // 处理设置保存
+ add_action('admin_init', array($this, 'handle_settings_save'));
+ }
+
+ /**
+ * 初始化定时健康检查
+ */
+ private function initialize_cron() {
+ // 注册激活时的钩子
+ register_activation_hook(WP_DOMAIN_MAPPING_BASENAME, array($this, 'schedule_health_check'));
+
+ // 注册停用时的钩子
+ register_deactivation_hook(WP_DOMAIN_MAPPING_BASENAME, array($this, 'unschedule_health_check'));
+ }
+
+ /**
+ * 添加健康监控菜单
+ */
+ public function add_menu_page() {
+ add_submenu_page(
+ 'settings.php',
+ __('Domain Health', 'wp-domain-mapping'),
+ __('Domain Health', 'wp-domain-mapping'),
+ 'manage_network',
+ 'domain-mapping-health',
+ array($this, 'render_page')
+ );
+ }
+
+ /**
+ * 计划健康检查任务
+ */
+ public function schedule_health_check() {
+ if (!wp_next_scheduled('dm_domain_health_check')) {
+ wp_schedule_event(time(), 'daily', 'dm_domain_health_check');
+ }
+ }
+
+ /**
+ * 取消健康检查任务
+ */
+ public function unschedule_health_check() {
+ $timestamp = wp_next_scheduled('dm_domain_health_check');
+ if ($timestamp) {
+ wp_unschedule_event($timestamp, 'dm_domain_health_check');
+ }
+ }
+
+ /**
+ * 处理手动健康检查
+ */
+ public function handle_manual_check() {
+ if (isset($_POST['dm_manual_health_check']) && $_POST['dm_manual_health_check']) {
+ // 验证nonce
+ if (!isset($_POST['dm_manual_health_check_nonce']) || !wp_verify_nonce($_POST['dm_manual_health_check_nonce'], 'dm_manual_health_check')) {
+ wp_die(__('Security check failed.', 'wp-domain-mapping'));
+ }
+
+ // 检查权限
+ if (!current_user_can('manage_network')) {
+ wp_die(__('You do not have sufficient permissions to perform this action.', 'wp-domain-mapping'));
+ }
+
+ // 执行健康检查
+ $this->run_health_check_for_all_domains();
+
+ // 重定向回健康页面
+ wp_redirect(add_query_arg(array('page' => 'domain-mapping-health', 'checked' => 1), network_admin_url('settings.php')));
+ exit;
+ }
+ }
+
+ /**
+ * 处理设置保存
+ */
+ public function handle_settings_save() {
+ if (isset($_POST['dm_health_settings']) && $_POST['dm_health_settings']) {
+ // 验证nonce
+ if (!isset($_POST['dm_health_settings_nonce']) || !wp_verify_nonce($_POST['dm_health_settings_nonce'], 'dm_health_settings')) {
+ wp_die(__('Security check failed.', 'wp-domain-mapping'));
+ }
+
+ // 检查权限
+ if (!current_user_can('manage_network')) {
+ wp_die(__('You do not have sufficient permissions to perform this action.', 'wp-domain-mapping'));
+ }
+
+ // 保存设置
+ $health_check_enabled = isset($_POST['health_check_enabled']) ? (bool) $_POST['health_check_enabled'] : false;
+ $health_notifications_enabled = isset($_POST['health_notifications_enabled']) ? (bool) $_POST['health_notifications_enabled'] : false;
+ $notification_email = isset($_POST['notification_email']) ? sanitize_email($_POST['notification_email']) : '';
+ $ssl_expiry_threshold = isset($_POST['ssl_expiry_threshold']) ? intval($_POST['ssl_expiry_threshold']) : 14;
+
+ update_site_option('dm_health_check_enabled', $health_check_enabled);
+ update_site_option('dm_health_notifications_enabled', $health_notifications_enabled);
+ update_site_option('dm_notification_email', $notification_email);
+ update_site_option('dm_ssl_expiry_threshold', $ssl_expiry_threshold);
+
+ // 如果启用了自动检查,确保计划任务已设置
+ if ($health_check_enabled) {
+ $this->schedule_health_check();
+ } else {
+ $this->unschedule_health_check();
+ }
+
+ // 重定向回健康页面
+ wp_redirect(add_query_arg(array('page' => 'domain-mapping-health', 'settings-updated' => 1), network_admin_url('settings.php')));
+ exit;
+ }
+ }
+
+ /**
+ * 渲染健康监控页面
+ */
+ public function render_page() {
+ // 检查权限
+ if (!current_user_can('manage_network')) {
+ wp_die(__('You do not have sufficient permissions to access this page.', 'wp-domain-mapping'));
+ }
+
+ // 获取所有域名
+ global $wpdb;
+ $table = $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_DOMAINS;
+ $domains = $wpdb->get_results("
+ SELECT d.*, b.domain as original_domain, b.path
+ FROM {$table} d
+ JOIN {$wpdb->blogs} b ON d.blog_id = b.blog_id
+ ORDER BY d.blog_id ASC, d.active DESC
+ ");
+
+ // 获取健康检查结果(如果有)
+ $health_results = get_site_option('dm_domain_health_results', array());
+
+ // 渲染页面
+ ?>
+
+
+
+
' .
+ __('Domain health check completed.', 'wp-domain-mapping') .
+ '
';
+ }
+
+ if (isset($_GET['settings-updated']) && $_GET['settings-updated']) {
+ echo '' .
+ __('Settings saved.', 'wp-domain-mapping') .
+ '
';
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ domain);
+ $health_data = isset($health_results[$domain_key]) ? $health_results[$domain_key] : null;
+ $site_name = get_blog_option($domain->blog_id, 'blogname', __('Unknown', 'wp-domain-mapping'));
+ ?>
+
+
+ domain); ?>
+ active) : ?>
+
+
+
+
+
+
+
+ original_domain . $domain->path); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
domain)); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = 200 && $health_data['response_code'] < 400) {
+ echo ' ';
+ } else {
+ echo ' ';
+ }
+ } else {
+ echo '-';
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ check_domain_health($domain);
+
+ // 保存结果
+ $this->save_health_check_result($domain, $result);
+
+ // 返回结果
+ wp_send_json_success($result);
+ }
+
+ /**
+ * 计划任务:执行所有域名健康检查
+ */
+ public function scheduled_health_check() {
+ // 检查是否启用了自动健康检查
+ if (!get_site_option('dm_health_check_enabled', true)) {
+ return;
+ }
+
+ $this->run_health_check_for_all_domains();
+ }
+
+ /**
+ * 对所有域名执行健康检查
+ */
+ private function run_health_check_for_all_domains() {
+ global $wpdb;
+ $table = $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_DOMAINS;
+
+ // 获取所有域名
+ $domains = $wpdb->get_col("SELECT domain FROM {$table}");
+
+ // 初始化通知信息
+ $issues = array();
+
+ // 检查每个域名
+ foreach ($domains as $domain) {
+ $result = $this->check_domain_health($domain);
+ $this->save_health_check_result($domain, $result);
+
+ // 检查是否有问题
+ if ($this->has_health_issues($result)) {
+ $issues[$domain] = $result;
+ }
+ }
+
+ // 如果启用了通知并且有问题,发送通知
+ if (!empty($issues) && get_site_option('dm_health_notifications_enabled', true)) {
+ $this->send_health_notification($issues);
+ }
+
+ return true;
+ }
+
+ /**
+ * 检查域名健康状态
+ *
+ * @param string $domain 要检查的域名
+ * @return array 健康检查结果
+ */
+ private function check_domain_health($domain) {
+ $result = array(
+ 'domain' => $domain,
+ 'last_check' => current_time('mysql'),
+ 'dns_status' => 'error',
+ 'dns_message' => __('DNS check not performed', 'wp-domain-mapping'),
+ 'resolved_ip' => '',
+ 'ssl_valid' => false,
+ 'ssl_expiry' => '',
+ 'accessible' => false,
+ 'response_code' => 0
+ );
+
+ // 获取服务器IP或CNAME
+ $server_ip = get_site_option('dm_ipaddress', '');
+ $server_cname = get_site_option('dm_cname', '');
+
+ // 检查DNS设置
+ $domain_ip = gethostbyname($domain);
+ $result['resolved_ip'] = $domain_ip;
+
+ if ($domain_ip && $domain_ip !== $domain) {
+ if ($server_ip && strpos($server_ip, $domain_ip) !== false) {
+ $result['dns_status'] = 'success';
+ $result['dns_message'] = __('Domain A record is correctly pointing to server IP.', 'wp-domain-mapping');
+ } elseif ($server_cname && function_exists('dns_get_record')) {
+ $dns_records = @dns_get_record($domain, DNS_CNAME);
+ $has_valid_cname = false;
+
+ if ($dns_records) {
+ foreach ($dns_records as $record) {
+ if (isset($record['target']) &&
+ (
+ $record['target'] === $server_cname ||
+ strpos($record['target'], $server_cname) !== false
+ )) {
+ $has_valid_cname = true;
+ break;
+ }
+ }
+ }
+
+ if ($has_valid_cname) {
+ $result['dns_status'] = 'success';
+ $result['dns_message'] = __('Domain CNAME record is correctly configured.', 'wp-domain-mapping');
+ } else {
+ $result['dns_message'] = __('Domain is not pointing to the correct server.', 'wp-domain-mapping');
+ }
+ } else {
+ $result['dns_message'] = __('Cannot verify DNS configuration.', 'wp-domain-mapping');
+ }
+ } else {
+ $result['dns_message'] = __('Domain does not resolve to an IP address.', 'wp-domain-mapping');
+ }
+
+ // 检查网站可访问性和SSL
+ $response = $this->test_domain_connection($domain);
+
+ if ($response) {
+ $result['accessible'] = $response['accessible'];
+ $result['response_code'] = $response['response_code'];
+ $result['ssl_valid'] = $response['ssl_valid'];
+ $result['ssl_expiry'] = $response['ssl_expiry'];
+ }
+
+ return $result;
+ }
+
+ /**
+ * 测试域名连接
+ *
+ * @param string $domain 域名
+ * @return array|false 连接测试结果
+ */
+ private function test_domain_connection($domain) {
+ if (!function_exists('curl_init')) {
+ return false;
+ }
+
+ $result = array(
+ 'accessible' => false,
+ 'response_code' => 0,
+ 'ssl_valid' => false,
+ 'ssl_expiry' => ''
+ );
+
+ // 测试HTTPS连接
+ $ch = curl_init('https://' . $domain);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_HEADER, true);
+ curl_setopt($ch, CURLOPT_NOBODY, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
+
+ $response = curl_exec($ch);
+ $response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $result['response_code'] = $response_code;
+
+ if ($response !== false && $response_code > 0) {
+ $result['accessible'] = ($response_code >= 200 && $response_code < 400);
+ $result['ssl_valid'] = ($response !== false);
+
+ // 获取SSL证书信息
+ $ssl_info = curl_getinfo($ch, CURLINFO_CERTINFO);
+ if (!empty($ssl_info) && isset($ssl_info[0]['Expire date'])) {
+ $result['ssl_expiry'] = $ssl_info[0]['Expire date'];
+ }
+ }
+
+ curl_close($ch);
+
+ // 如果HTTPS失败,尝试HTTP
+ if (!$result['accessible']) {
+ $ch = curl_init('http://' . $domain);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_HEADER, true);
+ curl_setopt($ch, CURLOPT_NOBODY, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+
+ $response = curl_exec($ch);
+ $response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+ if ($response !== false && $response_code > 0) {
+ $result['accessible'] = ($response_code >= 200 && $response_code < 400);
+ $result['response_code'] = $response_code;
+ }
+
+ curl_close($ch);
+ }
+
+ return $result;
+ }
+
+ /**
+ * 保存健康检查结果
+ *
+ * @param string $domain 域名
+ * @param array $result 健康检查结果
+ */
+ private function save_health_check_result($domain, $result) {
+ $health_results = get_site_option('dm_domain_health_results', array());
+ $domain_key = md5($domain);
+
+ $health_results[$domain_key] = $result;
+ update_site_option('dm_domain_health_results', $health_results);
+ }
+
+ /**
+ * 检查结果是否有健康问题
+ *
+ * @param array $result 健康检查结果
+ * @return bool 是否有问题
+ */
+ private function has_health_issues($result) {
+ // 检查DNS问题
+ if ($result['dns_status'] !== 'success') {
+ return true;
+ }
+
+ // 检查SSL问题
+ if (!$result['ssl_valid']) {
+ return true;
+ }
+
+ // 检查SSL即将到期
+ if (!empty($result['ssl_expiry'])) {
+ $expiry_date = strtotime($result['ssl_expiry']);
+ $threshold = get_site_option('dm_ssl_expiry_threshold', 14);
+ $threshold_date = strtotime('+' . $threshold . ' days');
+
+ if ($expiry_date <= $threshold_date) {
+ return true;
+ }
+ }
+
+ // 检查可访问性问题
+ if (!$result['accessible']) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * 发送健康问题通知
+ *
+ * @param array $issues 有问题的域名及其健康检查结果
+ */
+ private function send_health_notification($issues) {
+ $notification_email = get_site_option('dm_notification_email', get_option('admin_email'));
+
+ if (empty($notification_email)) {
+ return;
+ }
+
+ $site_name = get_bloginfo('name');
+ $subject = sprintf(__('[%s] Domain Mapping Health Alert', 'wp-domain-mapping'), $site_name);
+
+ // 构建邮件内容
+ $message = sprintf(__('Domain health issues were detected on %s.', 'wp-domain-mapping'), $site_name) . "\n\n";
+ $message .= __('The following domains have issues:', 'wp-domain-mapping') . "\n\n";
+
+ foreach ($issues as $domain => $result) {
+ $message .= sprintf(__('Domain: %s', 'wp-domain-mapping'), $domain) . "\n";
+
+ // 添加DNS状态
+ if ($result['dns_status'] !== 'success') {
+ $message .= " - " . sprintf(__('DNS Issue: %s', 'wp-domain-mapping'), $result['dns_message']) . "\n";
+ }
+
+ // 添加SSL状态
+ if (!$result['ssl_valid']) {
+ $message .= " - " . __('SSL Certificate is invalid or missing.', 'wp-domain-mapping') . "\n";
+ } elseif (!empty($result['ssl_expiry'])) {
+ $expiry_date = strtotime($result['ssl_expiry']);
+ $threshold = get_site_option('dm_ssl_expiry_threshold', 14);
+ $threshold_date = strtotime('+' . $threshold . ' days');
+
+ if ($expiry_date <= $threshold_date) {
+ $message .= " - " . sprintf(
+ __('SSL Certificate expires on %s (within %d days).', 'wp-domain-mapping'),
+ date('Y-m-d', $expiry_date),
+ $threshold
+ ) . "\n";
+ }
+ }
+
+ // 添加可访问性状态
+ if (!$result['accessible']) {
+ $message .= " - " . __('Site is not accessible.', 'wp-domain-mapping') . "\n";
+ if ($result['response_code'] > 0) {
+ $message .= " " . sprintf(__('HTTP Response Code: %d', 'wp-domain-mapping'), $result['response_code']) . "\n";
+ }
+ }
+
+ $message .= "\n";
+ }
+
+ // 添加解决方案链接
+ $message .= sprintf(
+ __('To view and manage these issues, please visit: %s', 'wp-domain-mapping'),
+ network_admin_url('settings.php?page=domain-mapping-health')
+ ) . "\n";
+
+ // 发送邮件
+ wp_mail($notification_email, $subject, $message);
+ }
+}
diff --git a/includes/class-importer.php b/includes/class-importer.php
new file mode 100644
index 0000000..16feba3
--- /dev/null
+++ b/includes/class-importer.php
@@ -0,0 +1,592 @@
+db = WP_Domain_Mapping_DB::get_instance();
+
+ // 添加菜单页面
+ add_action('network_admin_menu', array($this, 'add_menu_page'), 20);
+
+ // 处理表单提交
+ add_action('admin_init', array($this, 'handle_form_submission'));
+
+ // AJAX处理程序
+ add_action('wp_ajax_dm_import_csv', array($this, 'ajax_import_csv'));
+ }
+
+ /**
+ * 添加导入/导出菜单
+ */
+ public function add_menu_page() {
+ add_submenu_page(
+ 'settings.php',
+ __('Import/Export Domains', 'wp-domain-mapping'),
+ __('Import/Export Domains', 'wp-domain-mapping'),
+ 'manage_network',
+ 'domain-mapping-import-export',
+ array($this, 'render_page')
+ );
+ }
+
+ /**
+ * 渲染导入/导出页面
+ */
+ public function render_page() {
+ // 检查权限
+ if (!current_user_can('manage_network')) {
+ wp_die(__('You do not have sufficient permissions to access this page.', 'wp-domain-mapping'));
+ }
+
+ // 输出成功消息(如果有)
+ if (isset($_GET['imported']) && $_GET['imported']) {
+ $count = intval($_GET['imported']);
+ echo '' .
+ sprintf(
+ _n(
+ '%d domain mapping imported successfully.',
+ '%d domain mappings imported successfully.',
+ $count,
+ 'wp-domain-mapping'
+ ),
+ $count
+ ) .
+ '
';
+ }
+
+ if (isset($_GET['export']) && $_GET['export'] == 'success') {
+ echo '' .
+ __('Domain mappings exported successfully.', 'wp-domain-mapping') .
+ '
';
+ }
+
+ // 显示页面
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ blog_id
+ domain
+ active
+
+
+
+
+ 1
+ example.com
+ 1
+
+
+ 2
+ example.org
+ 0
+
+
+
+
+
+ blog_id :
+ domain :
+ active :
+
+
+
+
+
+ handle_export();
+ }
+ }
+
+ /**
+ * 处理CSV导出
+ */
+ private function handle_export() {
+ // 检查权限
+ if (!current_user_can('manage_network')) {
+ wp_die(__('You do not have sufficient permissions to export data.', 'wp-domain-mapping'));
+ }
+
+ // 验证nonce
+ if (!isset($_POST['domain_mapping_export_nonce']) || !wp_verify_nonce($_POST['domain_mapping_export_nonce'], 'domain_mapping_export')) {
+ wp_die(__('Invalid security token. Please try again.', 'wp-domain-mapping'));
+ }
+
+ // 获取选项
+ $include_header = isset($_POST['include_header']) ? (bool) $_POST['include_header'] : false;
+ $blog_id_filter = isset($_POST['blog_id_filter']) && !empty($_POST['blog_id_filter']) ? intval($_POST['blog_id_filter']) : 0;
+
+ // 获取域名映射数据
+ global $wpdb;
+ $table = $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_DOMAINS;
+ $sql = "SELECT blog_id, domain, active FROM {$table}";
+
+ if ($blog_id_filter > 0) {
+ $sql .= $wpdb->prepare(" WHERE blog_id = %d", $blog_id_filter);
+ }
+
+ $domains = $wpdb->get_results($sql, ARRAY_A);
+
+ if (empty($domains)) {
+ // 没有数据
+ wp_redirect(add_query_arg(array('page' => 'domain-mapping-import-export', 'export' => 'empty'), network_admin_url('settings.php')));
+ exit;
+ }
+
+ // 设置CSV输出
+ $filename = 'domain-mappings-' . date('Y-m-d') . '.csv';
+
+ header('Content-Type: text/csv; charset=utf-8');
+ header('Content-Disposition: attachment; filename=' . $filename);
+
+ $output = fopen('php://output', 'w');
+
+ // 添加标题行
+ if ($include_header) {
+ fputcsv($output, array('blog_id', 'domain', 'active'));
+ }
+
+ // 添加数据行
+ foreach ($domains as $domain) {
+ fputcsv($output, $domain);
+ }
+
+ fclose($output);
+ exit;
+ }
+
+ /**
+ * AJAX处理CSV导入
+ */
+ public function ajax_import_csv() {
+ // 检查权限
+ if (!current_user_can('manage_network')) {
+ wp_send_json_error(__('You do not have sufficient permissions to import data.', 'wp-domain-mapping'));
+ }
+
+ // 验证nonce
+ if (!isset($_POST['domain_mapping_import_nonce']) || !wp_verify_nonce($_POST['domain_mapping_import_nonce'], 'domain_mapping_import')) {
+ wp_send_json_error(__('Invalid security token. Please try again.', 'wp-domain-mapping'));
+ }
+
+ // 检查文件
+ if (!isset($_FILES['csv_file']) || $_FILES['csv_file']['error'] != UPLOAD_ERR_OK) {
+ wp_send_json_error(__('No file uploaded or upload error.', 'wp-domain-mapping'));
+ }
+
+ // 获取选项
+ $has_header = isset($_POST['has_header']) ? (bool) $_POST['has_header'] : false;
+ $update_existing = isset($_POST['update_existing']) ? (bool) $_POST['update_existing'] : false;
+ $validate_sites = isset($_POST['validate_sites']) ? (bool) $_POST['validate_sites'] : true;
+
+ // 打开文件
+ $file = fopen($_FILES['csv_file']['tmp_name'], 'r');
+ if (!$file) {
+ wp_send_json_error(__('Could not open the uploaded file.', 'wp-domain-mapping'));
+ }
+
+ // 初始化计数器和日志
+ $imported = 0;
+ $skipped = 0;
+ $errors = 0;
+ $log = array();
+
+ // 跳过标题行
+ if ($has_header) {
+ fgetcsv($file);
+ }
+
+ // 处理每一行
+ $core = WP_Domain_Mapping_Core::get_instance();
+ $row_num = $has_header ? 2 : 1; // 考虑标题行
+
+ while (($data = fgetcsv($file)) !== false) {
+ // 检查数据格式
+ if (count($data) < 3) {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Invalid format. Expected at least 3 columns.', 'wp-domain-mapping'), $row_num)
+ );
+ $errors++;
+ $row_num++;
+ continue;
+ }
+
+ // 解析数据
+ $blog_id = intval($data[0]);
+ $domain = $core->clean_domain(trim($data[1]));
+ $active = intval($data[2]);
+
+ // 验证blog_id
+ if ($blog_id <= 0) {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Invalid blog ID: %d', 'wp-domain-mapping'), $row_num, $blog_id)
+ );
+ $errors++;
+ $row_num++;
+ continue;
+ }
+
+ // 验证站点是否存在
+ if ($validate_sites && !get_blog_details($blog_id)) {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Site ID %d does not exist.', 'wp-domain-mapping'), $row_num, $blog_id)
+ );
+ $errors++;
+ $row_num++;
+ continue;
+ }
+
+ // 验证域名格式
+ if (!$core->validate_domain($domain)) {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Invalid domain format: %s', 'wp-domain-mapping'), $row_num, $domain)
+ );
+ $errors++;
+ $row_num++;
+ continue;
+ }
+
+ // 检查域名是否已经存在于其他站点
+ global $wpdb;
+ $table = $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_DOMAINS;
+ $existing = $wpdb->get_row($wpdb->prepare(
+ "SELECT * FROM $table WHERE domain = %s",
+ $domain
+ ));
+
+ if ($existing) {
+ if ($existing->blog_id != $blog_id) {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Domain %s is already mapped to blog ID %d.', 'wp-domain-mapping'),
+ $row_num, $domain, $existing->blog_id)
+ );
+ $errors++;
+ } elseif (!$update_existing) {
+ $log[] = array(
+ 'status' => 'warning',
+ 'message' => sprintf(__('Row %d: Domain %s already exists for blog ID %d. Skipped.', 'wp-domain-mapping'),
+ $row_num, $domain, $blog_id)
+ );
+ $skipped++;
+ } else {
+ // 更新现有域名
+ $result = $wpdb->update(
+ $table,
+ array('active' => $active),
+ array('domain' => $domain),
+ array('%d'),
+ array('%s')
+ );
+
+ if ($result !== false) {
+ $log[] = array(
+ 'status' => 'success',
+ 'message' => sprintf(__('Row %d: Updated domain %s for blog ID %d.', 'wp-domain-mapping'),
+ $row_num, $domain, $blog_id)
+ );
+ $imported++;
+ } else {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Failed to update domain %s for blog ID %d.', 'wp-domain-mapping'),
+ $row_num, $domain, $blog_id)
+ );
+ $errors++;
+ }
+ }
+ } else {
+ // 添加新域名
+ $result = $wpdb->insert(
+ $table,
+ array(
+ 'blog_id' => $blog_id,
+ 'domain' => $domain,
+ 'active' => $active
+ ),
+ array('%d', '%s', '%d')
+ );
+
+ if ($result) {
+ // 记录操作
+ $this->db->log_action('import', $domain, $blog_id);
+
+ // 清除缓存
+ $this->db->invalidate_domain_cache($blog_id);
+
+ $log[] = array(
+ 'status' => 'success',
+ 'message' => sprintf(__('Row %d: Added domain %s for blog ID %d.', 'wp-domain-mapping'),
+ $row_num, $domain, $blog_id)
+ );
+ $imported++;
+ } else {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Failed to add domain %s for blog ID %d.', 'wp-domain-mapping'),
+ $row_num, $domain, $blog_id)
+ );
+ $errors++;
+ }
+ }
+
+ $row_num++;
+ }
+
+ fclose($file);
+
+ // 构建响应
+ $message = sprintf(
+ __('Import completed: %d imported, %d skipped, %d errors.', 'wp-domain-mapping'),
+ $imported, $skipped, $errors
+ );
+
+ wp_send_json_success(array(
+ 'message' => $message,
+ 'imported' => $imported,
+ 'skipped' => $skipped,
+ 'errors' => $errors,
+ 'details' => $log
+ ));
+ }
+}
diff --git a/includes/class-site-id.php b/includes/class-site-id.php
new file mode 100644
index 0000000..4dd9ca8
--- /dev/null
+++ b/includes/class-site-id.php
@@ -0,0 +1,132 @@
+id) {
+ ?>
+
+ 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)) : '#',
+ 'meta' => array(
+ 'title' => is_super_admin() ? __('Edit this site', 'wp-domain-mapping') : __('Current site ID', 'wp-domain-mapping'),
+ 'class' => 'dm-site-id-menu'
+ ),
+ ));
+ }
+
+ /**
+ * 向站点列表添加ID列
+ *
+ * @param array $columns 当前列
+ * @return array 修改后的列
+ */
+ public function manage_sites_columns($columns) {
+ // 在第一列后添加站点ID列
+ $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;
+ }
+
+ /**
+ * 显示站点ID列的内容
+ *
+ * @param string $column 列名
+ * @param int $blog_id 站点ID
+ */
+ public function show_site_id($column, $blog_id) {
+ if ('dm_site_id' == $column) {
+ echo '' . esc_html($blog_id) . ' ';
+ }
+ }
+}
diff --git a/includes/class-tools.php b/includes/class-tools.php
new file mode 100644
index 0000000..31efabe
--- /dev/null
+++ b/includes/class-tools.php
@@ -0,0 +1,709 @@
+db = WP_Domain_Mapping_DB::get_instance();
+ $this->core = WP_Domain_Mapping_Core::get_instance();
+
+ // Setup hooks
+ $this->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'));
+
+ // 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'));
+
+ // Scheduled tasks
+ 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'));
+ }
+
+ 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'
+ );
+ }
+
+ // Site ID Display Functions
+ 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()) {
+ return;
+ }
+
+ $blog_id = get_current_blog_id();
+
+ $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)) : '#',
+ 'meta' => array(
+ '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 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);
+
+ return $columns;
+ }
+
+ public function display_site_id_column($column, $blog_id) {
+ if ('dm_site_id' == $column) {
+ echo '' . esc_html($blog_id) . ' ';
+ }
+ }
+
+ public function site_id_column_style() {
+ if ('sites-network' == get_current_screen()->id) {
+ ?>
+
+ run_health_check_for_all_domains();
+
+ // Redirect back to health page
+ wp_redirect(add_query_arg(array('page' => 'domain-mapping-health', 'checked' => 1), network_admin_url('settings.php')));
+ exit;
+ }
+ }
+
+ public function handle_health_settings_save() {
+ if (isset($_POST['dm_health_settings']) && $_POST['dm_health_settings']) {
+ // Verify nonce
+ if (!isset($_POST['dm_health_settings_nonce']) || !wp_verify_nonce($_POST['dm_health_settings_nonce'], 'dm_health_settings')) {
+ wp_die(__('Security check failed.', 'wp-domain-mapping'));
+ }
+
+ // Check permissions
+ if (!current_user_can('manage_network')) {
+ wp_die(__('You do not have sufficient permissions to perform this action.', 'wp-domain-mapping'));
+ }
+
+ // Save settings
+ $health_check_enabled = isset($_POST['health_check_enabled']) ? (bool) $_POST['health_check_enabled'] : false;
+ $health_notifications_enabled = isset($_POST['health_notifications_enabled']) ? (bool) $_POST['health_notifications_enabled'] : false;
+ $notification_email = isset($_POST['notification_email']) ? sanitize_email($_POST['notification_email']) : '';
+ $ssl_expiry_threshold = isset($_POST['ssl_expiry_threshold']) ? intval($_POST['ssl_expiry_threshold']) : 14;
+
+ update_site_option('dm_health_check_enabled', $health_check_enabled);
+ update_site_option('dm_health_notifications_enabled', $health_notifications_enabled);
+ update_site_option('dm_notification_email', $notification_email);
+ update_site_option('dm_ssl_expiry_threshold', $ssl_expiry_threshold);
+
+ // If auto check is enabled, ensure cron is set
+ if ($health_check_enabled) {
+ $this->schedule_health_check();
+ } else {
+ $this->unschedule_health_check();
+ }
+
+ // Redirect back to health page
+ wp_redirect(add_query_arg(array('page' => 'domain-mapping-health', 'settings-updated' => 1), network_admin_url('settings.php')));
+ exit;
+ }
+ }
+
+ public function ajax_check_domain_health() {
+ // Check permissions
+ if (!current_user_can('manage_network')) {
+ wp_send_json_error(__('You do not have sufficient permissions to perform this action.', 'wp-domain-mapping'));
+ }
+
+ // Verify nonce
+ if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'dm_check_domain_health')) {
+ wp_send_json_error(__('Security check failed.', 'wp-domain-mapping'));
+ }
+
+ // Get domain
+ $domain = isset($_POST['domain']) ? sanitize_text_field($_POST['domain']) : '';
+ if (empty($domain)) {
+ wp_send_json_error(__('No domain specified.', 'wp-domain-mapping'));
+ }
+
+ // Run health check
+ $result = $this->check_domain_health($domain);
+
+ // Save result
+ $this->db->save_health_result($domain, $result);
+
+ // Return result
+ wp_send_json_success($result);
+ }
+
+ public function scheduled_health_check() {
+ // Check if health checks are enabled
+ if (!get_site_option('dm_health_check_enabled', true)) {
+ return;
+ }
+
+ $this->run_health_check_for_all_domains();
+ }
+
+ private function run_health_check_for_all_domains() {
+ global $wpdb;
+ $table = $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_DOMAINS;
+
+ // Get all domains
+ $domains = $wpdb->get_col("SELECT domain FROM {$table}");
+
+ // Initialize issues array
+ $issues = array();
+
+ // Check each domain
+ foreach ($domains as $domain) {
+ $result = $this->check_domain_health($domain);
+ $this->db->save_health_result($domain, $result);
+
+ // Check for issues
+ if ($this->has_health_issues($result)) {
+ $issues[$domain] = $result;
+ }
+ }
+
+ // Send notifications if enabled and issues exist
+ if (!empty($issues) && get_site_option('dm_health_notifications_enabled', true)) {
+ $this->send_health_notification($issues);
+ }
+
+ return true;
+ }
+
+ private function check_domain_health($domain) {
+ $result = array(
+ 'domain' => $domain,
+ 'last_check' => current_time('mysql'),
+ 'dns_status' => 'error',
+ 'dns_message' => __('DNS check not performed', 'wp-domain-mapping'),
+ 'resolved_ip' => '',
+ 'ssl_valid' => false,
+ 'ssl_expiry' => '',
+ 'accessible' => false,
+ 'response_code' => 0
+ );
+
+ // Get server IP or CNAME
+ $server_ip = get_site_option('dm_ipaddress', '');
+ $server_cname = get_site_option('dm_cname', '');
+
+ // Check DNS setting
+ $domain_ip = gethostbyname($domain);
+ $result['resolved_ip'] = $domain_ip;
+
+ if ($domain_ip && $domain_ip !== $domain) {
+ if ($server_ip && strpos($server_ip, $domain_ip) !== false) {
+ $result['dns_status'] = 'success';
+ $result['dns_message'] = __('Domain A record is correctly pointing to server IP.', 'wp-domain-mapping');
+ } elseif ($server_cname && function_exists('dns_get_record')) {
+ $dns_records = @dns_get_record($domain, DNS_CNAME);
+ $has_valid_cname = false;
+
+ if ($dns_records) {
+ foreach ($dns_records as $record) {
+ if (isset($record['target']) &&
+ (
+ $record['target'] === $server_cname ||
+ strpos($record['target'], $server_cname) !== false
+ )) {
+ $has_valid_cname = true;
+ break;
+ }
+ }
+ }
+
+ if ($has_valid_cname) {
+ $result['dns_status'] = 'success';
+ $result['dns_message'] = __('Domain CNAME record is correctly configured.', 'wp-domain-mapping');
+ } else {
+ $result['dns_message'] = __('Domain is not pointing to the correct server.', 'wp-domain-mapping');
+ }
+ } else {
+ $result['dns_message'] = __('Cannot verify DNS configuration.', 'wp-domain-mapping');
+ }
+ } else {
+ $result['dns_message'] = __('Domain does not resolve to an IP address.', 'wp-domain-mapping');
+ }
+
+ // Check site accessibility and SSL
+ $response = $this->test_domain_connection($domain);
+
+ if ($response) {
+ $result['accessible'] = $response['accessible'];
+ $result['response_code'] = $response['response_code'];
+ $result['ssl_valid'] = $response['ssl_valid'];
+ $result['ssl_expiry'] = $response['ssl_expiry'];
+ }
+
+ return $result;
+ }
+
+ private function test_domain_connection($domain) {
+ if (!function_exists('curl_init')) {
+ return false;
+ }
+
+ $result = array(
+ 'accessible' => false,
+ 'response_code' => 0,
+ 'ssl_valid' => false,
+ 'ssl_expiry' => ''
+ );
+
+ // Test HTTPS connection
+ $ch = curl_init('https://' . $domain);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_HEADER, true);
+ curl_setopt($ch, CURLOPT_NOBODY, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
+ curl_setopt($ch, CURLOPT_CERTINFO, true);
+
+ $response = curl_exec($ch);
+ $response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ $result['response_code'] = $response_code;
+
+ if ($response !== false && $response_code > 0) {
+ $result['accessible'] = ($response_code >= 200 && $response_code < 400);
+ $result['ssl_valid'] = ($response !== false);
+
+ // Get SSL certificate info
+ $cert_info = curl_getinfo($ch, CURLINFO_CERTINFO);
+ if (!empty($cert_info) && isset($cert_info[0]['Expire date'])) {
+ $result['ssl_expiry'] = $cert_info[0]['Expire date'];
+ }
+ }
+
+ curl_close($ch);
+
+ // If HTTPS failed, try HTTP
+ if (!$result['accessible']) {
+ $ch = curl_init('http://' . $domain);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_HEADER, true);
+ curl_setopt($ch, CURLOPT_NOBODY, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+
+ $response = curl_exec($ch);
+ $response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+ if ($response !== false && $response_code > 0) {
+ $result['accessible'] = ($response_code >= 200 && $response_code < 400);
+ $result['response_code'] = $response_code;
+ }
+
+ curl_close($ch);
+ }
+
+ return $result;
+ }
+
+ private function has_health_issues($result) {
+ // Check DNS issues
+ if ($result['dns_status'] !== 'success') {
+ return true;
+ }
+
+ // Check SSL issues
+ if (!$result['ssl_valid']) {
+ return true;
+ }
+
+ // Check SSL expiry
+ if (!empty($result['ssl_expiry'])) {
+ $expiry_date = strtotime($result['ssl_expiry']);
+ $threshold = get_site_option('dm_ssl_expiry_threshold', 14);
+ $threshold_date = strtotime('+' . $threshold . ' days');
+
+ if ($expiry_date <= $threshold_date) {
+ return true;
+ }
+ }
+
+ // Check accessibility issues
+ if (!$result['accessible']) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private function send_health_notification($issues) {
+ $notification_email = get_site_option('dm_notification_email', get_option('admin_email'));
+
+ if (empty($notification_email)) {
+ return;
+ }
+
+ $site_name = get_bloginfo('name');
+ $subject = sprintf(__('[%s] Domain Mapping Health Alert', 'wp-domain-mapping'), $site_name);
+
+ // Build email content
+ $message = sprintf(__('Domain health issues were detected on %s.', 'wp-domain-mapping'), $site_name) . "\n\n";
+ $message .= __('The following domains have issues:', 'wp-domain-mapping') . "\n\n";
+
+ foreach ($issues as $domain => $result) {
+ $message .= sprintf(__('Domain: %s', 'wp-domain-mapping'), $domain) . "\n";
+
+ // Add DNS status
+ if ($result['dns_status'] !== 'success') {
+ $message .= " - " . sprintf(__('DNS Issue: %s', 'wp-domain-mapping'), $result['dns_message']) . "\n";
+ }
+
+ // Add SSL status
+ if (!$result['ssl_valid']) {
+ $message .= " - " . __('SSL Certificate is invalid or missing.', 'wp-domain-mapping') . "\n";
+ } elseif (!empty($result['ssl_expiry'])) {
+ $expiry_date = strtotime($result['ssl_expiry']);
+ $threshold = get_site_option('dm_ssl_expiry_threshold', 14);
+ $threshold_date = strtotime('+' . $threshold . ' days');
+
+ if ($expiry_date <= $threshold_date) {
+ $message .= " - " . sprintf(
+ __('SSL Certificate expires on %s (within %d days).', 'wp-domain-mapping'),
+ date('Y-m-d', $expiry_date),
+ $threshold
+ ) . "\n";
+ }
+ }
+
+ // Add accessibility status
+ if (!$result['accessible']) {
+ $message .= " - " . __('Site is not accessible.', 'wp-domain-mapping') . "\n";
+ if ($result['response_code'] > 0) {
+ $message .= " " . sprintf(__('HTTP Response Code: %d', 'wp-domain-mapping'), $result['response_code']) . "\n";
+ }
+ }
+
+ $message .= "\n";
+ }
+
+ // Add resolution link
+ $message .= sprintf(
+ __('To view and manage these issues, please visit: %s', 'wp-domain-mapping'),
+ network_admin_url('settings.php?page=domain-mapping-health')
+ ) . "\n";
+
+ // Send email
+ wp_mail($notification_email, $subject, $message);
+ }
+
+ // Import/Export Functions
+ public function handle_export() {
+ if (!isset($_POST['domain_mapping_export']) || !$_POST['domain_mapping_export']) {
+ return;
+ }
+
+ // Check permissions
+ if (!current_user_can('manage_network')) {
+ wp_die(__('You do not have sufficient permissions to export data.', 'wp-domain-mapping'));
+ }
+
+ // Verify nonce
+ if (!isset($_POST['domain_mapping_export_nonce']) || !wp_verify_nonce($_POST['domain_mapping_export_nonce'], 'domain_mapping_export')) {
+ wp_die(__('Invalid security token. Please try again.', 'wp-domain-mapping'));
+ }
+
+ // Get options
+ $include_header = isset($_POST['include_header']) ? (bool) $_POST['include_header'] : false;
+ $blog_id_filter = isset($_POST['blog_id_filter']) && !empty($_POST['blog_id_filter']) ? intval($_POST['blog_id_filter']) : 0;
+
+ // Get domain mapping data
+ global $wpdb;
+ $table = $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_DOMAINS;
+ $sql = "SELECT blog_id, domain, active FROM {$table}";
+
+ if ($blog_id_filter > 0) {
+ $sql .= $wpdb->prepare(" WHERE blog_id = %d", $blog_id_filter);
+ }
+
+ $domains = $wpdb->get_results($sql, ARRAY_A);
+
+ if (empty($domains)) {
+ // No data
+ wp_redirect(add_query_arg(array('page' => 'domain-mapping-import-export', 'export' => 'empty'), network_admin_url('settings.php')));
+ exit;
+ }
+
+ // Set up CSV output
+ $filename = 'domain-mappings-' . date('Y-m-d') . '.csv';
+
+ header('Content-Type: text/csv; charset=utf-8');
+ header('Content-Disposition: attachment; filename=' . $filename);
+
+ $output = fopen('php://output', 'w');
+
+ // Add header row
+ if ($include_header) {
+ fputcsv($output, array('blog_id', 'domain', 'active'));
+ }
+
+ // Add data rows
+ foreach ($domains as $domain) {
+ fputcsv($output, $domain);
+ }
+
+ fclose($output);
+ exit;
+ }
+
+ public function ajax_import_csv() {
+ // Check permissions
+ if (!current_user_can('manage_network')) {
+ wp_send_json_error(__('You do not have sufficient permissions to import data.', 'wp-domain-mapping'));
+ }
+
+ // Verify nonce
+ if (!isset($_POST['domain_mapping_import_nonce']) || !wp_verify_nonce($_POST['domain_mapping_import_nonce'], 'domain_mapping_import')) {
+ wp_send_json_error(__('Invalid security token. Please try again.', 'wp-domain-mapping'));
+ }
+
+ // Check file
+ if (!isset($_FILES['csv_file']) || $_FILES['csv_file']['error'] != UPLOAD_ERR_OK) {
+ wp_send_json_error(__('No file uploaded or upload error.', 'wp-domain-mapping'));
+ }
+
+ // Get options
+ $has_header = isset($_POST['has_header']) ? (bool) $_POST['has_header'] : false;
+ $update_existing = isset($_POST['update_existing']) ? (bool) $_POST['update_existing'] : false;
+ $validate_sites = isset($_POST['validate_sites']) ? (bool) $_POST['validate_sites'] : true;
+
+ // Open file
+ $file = fopen($_FILES['csv_file']['tmp_name'], 'r');
+ if (!$file) {
+ wp_send_json_error(__('Could not open the uploaded file.', 'wp-domain-mapping'));
+ }
+
+ // Initialize counters and log
+ $imported = 0;
+ $skipped = 0;
+ $errors = 0;
+ $log = array();
+
+ // Skip header row
+ if ($has_header) {
+ fgetcsv($file);
+ }
+
+ // Process each row
+ $row_num = $has_header ? 2 : 1; // Account for header row
+
+ while (($data = fgetcsv($file)) !== false) {
+ // Check data format
+ if (count($data) < 3) {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Invalid format. Expected at least 3 columns.', 'wp-domain-mapping'), $row_num)
+ );
+ $errors++;
+ $row_num++;
+ continue;
+ }
+
+ // Parse data
+ $blog_id = intval($data[0]);
+ $domain = $this->core->clean_domain(trim($data[1]));
+ $active = intval($data[2]);
+
+ // Validate blog_id
+ if ($blog_id <= 0) {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Invalid blog ID: %d', 'wp-domain-mapping'), $row_num, $blog_id)
+ );
+ $errors++;
+ $row_num++;
+ continue;
+ }
+
+ // Validate site exists
+ if ($validate_sites && !get_blog_details($blog_id)) {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Site ID %d does not exist.', 'wp-domain-mapping'), $row_num, $blog_id)
+ );
+ $errors++;
+ $row_num++;
+ continue;
+ }
+
+ // Validate domain format
+ if (!$this->core->validate_domain($domain)) {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Invalid domain format: %s', 'wp-domain-mapping'), $row_num, $domain)
+ );
+ $errors++;
+ $row_num++;
+ continue;
+ }
+
+ // Check if domain already exists
+ $existing = $this->db->get_domain_by_name($domain);
+
+ if ($existing) {
+ if ($existing->blog_id != $blog_id) {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Domain %s is already mapped to blog ID %d.', 'wp-domain-mapping'),
+ $row_num, $domain, $existing->blog_id)
+ );
+ $errors++;
+ } elseif (!$update_existing) {
+ $log[] = array(
+ 'status' => 'warning',
+ 'message' => sprintf(__('Row %d: Domain %s already exists for blog ID %d. Skipped.', 'wp-domain-mapping'),
+ $row_num, $domain, $blog_id)
+ );
+ $skipped++;
+ } else {
+ // Update existing domain
+ $success = $this->db->update_domain($domain, $blog_id, $active);
+
+ if ($success) {
+ $log[] = array(
+ 'status' => 'success',
+ 'message' => sprintf(__('Row %d: Updated domain %s for blog ID %d.', 'wp-domain-mapping'),
+ $row_num, $domain, $blog_id)
+ );
+ $imported++;
+ } else {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Failed to update domain %s for blog ID %d.', 'wp-domain-mapping'),
+ $row_num, $domain, $blog_id)
+ );
+ $errors++;
+ }
+ }
+ } else {
+ // Add new domain
+ $success = $this->db->add_domain($blog_id, $domain, $active);
+
+ if ($success) {
+ $log[] = array(
+ 'status' => 'success',
+ 'message' => sprintf(__('Row %d: Added domain %s for blog ID %d.', 'wp-domain-mapping'),
+ $row_num, $domain, $blog_id)
+ );
+ $imported++;
+ } else {
+ $log[] = array(
+ 'status' => 'error',
+ 'message' => sprintf(__('Row %d: Failed to add domain %s for blog ID %d.', 'wp-domain-mapping'),
+ $row_num, $domain, $blog_id)
+ );
+ $errors++;
+ }
+ }
+
+ $row_num++;
+ }
+
+ fclose($file);
+
+ // Build response
+ $message = sprintf(
+ __('Import completed: %d imported, %d skipped, %d errors.', 'wp-domain-mapping'),
+ $imported, $skipped, $errors
+ );
+
+ wp_send_json_success(array(
+ 'message' => $message,
+ 'imported' => $imported,
+ 'skipped' => $skipped,
+ 'errors' => $errors,
+ 'details' => $log
+ ));
+ }
+}
diff --git a/sunrise.php b/sunrise.php
index 6d39824..8c3198f 100644
--- a/sunrise.php
+++ b/sunrise.php
@@ -1,53 +1,61 @@
dmtable = $wpdb->base_prefix . 'domain_mapping';
-$dm_domain = $_SERVER[ 'HTTP_HOST' ];
+// Enable domain mapping
+define('DOMAIN_MAPPING', 1);
-if( ( $nowww = preg_replace( '|^www\.|', '', $dm_domain ) ) != $dm_domain )
- $where = $wpdb->prepare( 'domain IN (%s,%s)', $dm_domain, $nowww );
-else
- $where = $wpdb->prepare( 'domain = %s', $dm_domain );
-
-$wpdb->suppress_errors();
-$domain_mapping_id = $wpdb->get_var( "SELECT blog_id FROM {$wpdb->dmtable} WHERE {$where} ORDER BY CHAR_LENGTH(domain) DESC LIMIT 1" );
-$wpdb->suppress_errors( false );
-if( $domain_mapping_id ) {
- $current_blog = $wpdb->get_row("SELECT * FROM {$wpdb->blogs} WHERE blog_id = '$domain_mapping_id' LIMIT 1");
- $current_blog->domain = $dm_domain;
- $current_blog->path = '/';
- $blog_id = $domain_mapping_id;
- $site_id = $current_blog->site_id;
-
- define( 'COOKIE_DOMAIN', $dm_domain );
-
- $current_site = $wpdb->get_row( "SELECT * from {$wpdb->site} WHERE id = '{$current_blog->site_id}' LIMIT 0,1" );
- $current_site->blog_id = $wpdb->get_var( "SELECT blog_id FROM {$wpdb->blogs} WHERE domain='{$current_site->domain}' AND path='{$current_site->path}'" );
- if ( function_exists( 'get_site_option' ) )
- $current_site->site_name = get_site_option( 'site_name' );
- elseif ( function_exists( 'get_current_site_name' ) )
- $current_site = get_current_site_name( $current_site );
-
- define( 'DOMAIN_MAPPING', 1 );
+// Check if we're on the main site already
+if (defined('COOKIE_DOMAIN') && COOKIE_DOMAIN == $_SERVER['HTTP_HOST']) {
+ return;
+}
+
+global $wpdb, $current_blog, $current_site;
+
+// Get domains table name
+$domain_mapping_table = $wpdb->base_prefix . 'domain_mapping';
+
+// Check for the current domain in the domain mapping table
+$domain = $wpdb->escape($_SERVER['HTTP_HOST']);
+$blog_id = $wpdb->get_var("SELECT blog_id FROM {$domain_mapping_table} WHERE domain = '{$domain}' LIMIT 1");
+
+// If we found a mapped domain, override current_blog
+if (!empty($blog_id)) {
+ // Get the mapped blog details
+ $mapped_blog = $wpdb->get_row("SELECT * FROM {$wpdb->blogs} WHERE blog_id = '{$blog_id}' LIMIT 1");
+
+ if ($mapped_blog) {
+ // Override current_blog
+ $current_blog = $mapped_blog;
+
+ // Also set the cookie domain to the current domain
+ define('COOKIE_DOMAIN', $_SERVER['HTTP_HOST']);
+
+ // Define the mapped domain constant
+ define('MAPPED_DOMAIN', true);
+
+ // Allow other plugins to know this is a mapped domain
+ $GLOBALS['dm_domain'] = array(
+ 'original' => $current_blog->domain,
+ 'mapped' => $_SERVER['HTTP_HOST']
+ );
+
+ // Fix request URI for path sites
+ if ($current_blog->path != '/' && ($current_blog->path != '/wp/' || strpos($_SERVER['REQUEST_URI'], '/wp/') === false)) {
+ $current_blog->path = '/';
+ }
+ }
}
diff --git a/wp-domain-mapping.php b/wp-domain-mapping.php
index d327406..b509564 100644
--- a/wp-domain-mapping.php
+++ b/wp-domain-mapping.php
@@ -3,7 +3,7 @@
* Plugin Name: WP Domain Mapping
* Plugin URI: https://wenpai.org/plugins/wp-domain-mapping/
* Description: Map any site on a WordPress website to another domain with enhanced management features.
- * Version: 1.3.4
+ * Version: 2.0.0
* Author: WPDomain.com
* Author URI: https://wpdomain.com/
* Network: true
@@ -14,1129 +14,1445 @@
* Requires at least: 6.7.2
*/
-if (!defined('ABSPATH')) {
+// Prevent direct access
+if ( ! defined( 'ABSPATH' ) ) {
exit;
}
-define('WP_DOMAIN_MAPPING_VERSION', '1.3.4');
-define('WP_DOMAIN_MAPPING_DIR_URL', plugin_dir_url(__FILE__));
-define('WP_DOMAIN_MAPPING_DIR_PATH', plugin_dir_path(__FILE__));
-define('WP_DOMAIN_MAPPING_BASENAME', plugin_basename(__FILE__));
+// Define plugin constants
+define( 'WP_DOMAIN_MAPPING_VERSION', '2.0.0' );
+define( 'WP_DOMAIN_MAPPING_DIR_URL', plugin_dir_url( __FILE__ ) );
+define( 'WP_DOMAIN_MAPPING_DIR_PATH', plugin_dir_path( __FILE__ ) );
+define( 'WP_DOMAIN_MAPPING_BASENAME', plugin_basename( __FILE__ ) );
+/**
+ * Main Domain Mapping Class
+ *
+ * Handles the core functionality of the plugin
+ */
+class WP_Domain_Mapping {
-// Load text domain
-function wp_domain_mapping_text_domain() {
- load_plugin_textdomain('wp-domain-mapping', false, dirname(plugin_basename(__FILE__)) . '/languages');
-}
-add_action('init', 'wp_domain_mapping_text_domain');
+ /**
+ * Plugin instance
+ *
+ * @var WP_Domain_Mapping
+ */
+ private static $instance = null;
+ /**
+ * Database table names
+ *
+ * @var array
+ */
+ private $tables = array();
-// Integrate UpdatePulse Server for updates using PUC v5.3
-require_once plugin_dir_path(__FILE__) . 'lib/plugin-update-checker/plugin-update-checker.php';
-use YahnisElsts\PluginUpdateChecker\v5p3\PucFactory;
-
-$WpDomainMappingUpdateChecker = PucFactory::buildUpdateChecker(
- 'https://updates.weixiaoduo.com/wp-domain-mapping.json',
- __FILE__,
- 'wp-domain-mapping'
-);
-
-// Warning: Network not configured
-function domain_mapping_warning() {
- echo '' . __('Domain Mapping Disabled.', 'wp-domain-mapping') . ' ' . sprintf(__('You must create a network for it to work.', 'wp-domain-mapping'), 'http://codex.wordpress.org/Create_A_Network') . '
';
-}
-
-// Add admin pages
-function dm_add_pages() {
- global $current_site, $wpdb, $wp_db_version;
-
- if (!isset($current_site) && $wp_db_version >= 15260) {
- add_action('admin_notices', 'domain_mapping_warning');
- return false;
- }
- if ($current_site->path != "/") {
- wp_die(__("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'));
+ /**
+ * Get plugin instance
+ *
+ * @return WP_Domain_Mapping
+ */
+ public static function get_instance() {
+ if ( null === self::$instance ) {
+ self::$instance = new self();
+ }
+ return self::$instance;
}
- if (get_site_option('dm_user_settings') && $current_site->blog_id != $wpdb->blogid && !dm_sunrise_warning(false)) {
- add_management_page(__('Domain Mapping', 'wp-domain-mapping'), __('Domain Mapping', 'wp-domain-mapping'), 'manage_options', 'domainmapping', 'dm_manage_page');
- }
-}
-add_action('admin_menu', 'dm_add_pages');
+ /**
+ * Constructor
+ */
+ private function __construct() {
+ global $wpdb;
-// Add network admin pages
-function dm_network_pages() {
- add_submenu_page('settings.php', __('Domain Mapping', 'wp-domain-mapping'), __('Domain Mapping', 'wp-domain-mapping'), 'manage_network', 'domain-mapping', 'dm_admin_page');
- add_submenu_page('sites.php', __('Domains', 'wp-domain-mapping'), __('Domains', 'wp-domain-mapping'), 'manage_network', 'domains', 'dm_domains_admin');
-}
-add_action('network_admin_menu', 'dm_network_pages');
-
-// Default update messages
-function dm_echo_default_updated_msg() {
- 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;
- }
- echo "";
-}
-add_action('dm_echo_updated_msg', 'dm_echo_default_updated_msg');
-
-// Create database tables
-function maybe_create_db() {
- global $wpdb;
-
- // Initialize remote login hash
- get_dm_hash();
-
- // Set global table names
- $wpdb->dmtable = $wpdb->base_prefix . 'domain_mapping';
- $wpdb->dmtablelogins = $wpdb->base_prefix . 'domain_mapping_logins';
- $wpdb->dmtablelogs = $wpdb->base_prefix . 'domain_mapping_logs';
-
- // Only network admins can create tables
- if (!dm_site_admin()) {
- return;
- }
-
- // Use static variable to prevent repeated creation
- static $tables_created = false;
- if ($tables_created) {
- return;
- }
-
- $created = 0;
-
- // Create domain_mapping table
- if ($wpdb->get_var("SHOW TABLES LIKE '{$wpdb->dmtable}'") != $wpdb->dmtable) {
- $wpdb->query("CREATE TABLE IF NOT EXISTS `{$wpdb->dmtable}` (
- `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`)
- );");
- $created = 1;
- }
-
- // Create domain_mapping_logins table
- if ($wpdb->get_var("SHOW TABLES LIKE '{$wpdb->dmtablelogins}'") != $wpdb->dmtablelogins) {
- $wpdb->query("CREATE TABLE IF NOT EXISTS `{$wpdb->dmtablelogins}` (
- `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`)
- );");
- $created = 1;
- }
-
- // Create domain_mapping_logs table
- if ($wpdb->get_var("SHOW TABLES LIKE '{$wpdb->dmtablelogs}'") != $wpdb->dmtablelogs) {
- $wpdb->query("CREATE TABLE IF NOT EXISTS `{$wpdb->dmtablelogs}` (
- `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`)
- );");
- $created = 1;
- }
-
- // If any table was created, show success message and mark as created
- if ($created) {
- echo '' . __('Domain mapping database table created.', 'wp-domain-mapping') . '
';
- $tables_created = true;
- }
-}
-
-// Ajax handler for domain actions
-function dm_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;
- $wpdb->dmtable = $wpdb->base_prefix . 'domain_mapping';
- $wpdb->dmtablelogs = $wpdb->base_prefix . 'domain_mapping_logs';
- $action = sanitize_text_field($_POST['action_type']);
- $domain = dm_clean_domain(sanitize_text_field(strtolower($_POST['domain'])));
- $blog_id = absint($_POST['blog_id']);
- $active = isset($_POST['active']) ? absint($_POST['active']) : 0;
- $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 && null == $wpdb->get_var($wpdb->prepare("SELECT domain FROM {$wpdb->dmtable} WHERE blog_id != %d AND domain = %s", $blog_id, $domain))) {
- if (empty($orig_domain)) {
- $wpdb->query($wpdb->prepare("INSERT INTO {$wpdb->dmtable} ( `blog_id`, `domain`, `active` ) VALUES ( %d, %s, %d )", $blog_id, $domain, $active));
- $wpdb->insert($wpdb->dmtablelogs, array('user_id' => $current_user_id, 'action' => 'add', 'domain' => $domain, 'blog_id' => $blog_id));
- wp_send_json_success(__('Domain added successfully.', 'wp-domain-mapping'));
- } else {
- $wpdb->query($wpdb->prepare("UPDATE {$wpdb->dmtable} SET blog_id = %d, domain = %s, active = %d WHERE domain = %s", $blog_id, $domain, $active, $orig_domain));
- $wpdb->insert($wpdb->dmtablelogs, array('user_id' => $current_user_id, 'action' => 'edit', 'domain' => $domain, 'blog_id' => $blog_id));
- wp_send_json_success(__('Domain updated successfully.', 'wp-domain-mapping'));
- }
- } else {
- wp_send_json_error(__('Invalid site ID or domain already exists.', 'wp-domain-mapping'));
- }
- break;
- case 'delete':
- $domains = isset($_POST['domains']) ? array_map('sanitize_text_field', (array)$_POST['domains']) : array($domain);
- foreach ($domains as $del_domain) {
- $affected_blog_id = $wpdb->get_var($wpdb->prepare("SELECT blog_id FROM {$wpdb->dmtable} WHERE domain = %s", $del_domain));
- $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->dmtable} WHERE domain = %s", $del_domain));
- $wpdb->insert($wpdb->dmtablelogs, array('user_id' => $current_user_id, 'action' => 'delete', 'domain' => $del_domain, 'blog_id' => $affected_blog_id));
- }
- wp_send_json_success(__('Selected domains deleted successfully.', 'wp-domain-mapping'));
- break;
- default:
- wp_send_json_error(__('Invalid action.', 'wp-domain-mapping'));
- }
-}
-add_action('wp_ajax_dm_handle_actions', 'dm_ajax_handle_actions');
-
-// Domains admin page
-function dm_domains_admin() {
- global $wpdb, $current_site;
- if (!dm_site_admin()) {
- return false;
- }
-
- dm_sunrise_warning();
- maybe_create_db();
-
- if ($current_site->path != "/") {
- wp_die(sprintf(__("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"), $current_site->path));
- }
-
- $wpdb->dmtable = $wpdb->base_prefix . 'domain_mapping';
- $total_domains = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->dmtable}");
- $primary_domains = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->dmtable} WHERE active = 1");
-
- $edit_row = false;
- if (isset($_GET['edit_domain'])) {
- $edit_domain = sanitize_text_field($_GET['edit_domain']);
- $edit_row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->dmtable} WHERE domain = %s", $edit_domain));
- }
-
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- blog_id = '';
- $row->domain = '';
- $row->active = 1;
- }
- ?>
-
- dmtable = $wpdb->base_prefix . 'domain_mapping';
- if (!$rows) {
- echo '' . __('No domains found.', 'wp-domain-mapping') . '
';
- return;
+ }
}
- $edit_url = network_admin_url(file_exists(ABSPATH . 'wp-admin/network/site-info.php') ? 'site-info.php' : (file_exists(ABSPATH . 'wp-admin/ms-sites.php') ? 'ms-sites.php' : 'wpmu-blogs.php'));
- ?>
-
-
-
- " . __('Warning! Primary domains are currently disabled.', 'wp-domain-mapping') . "";
+ /**
+ * 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' ) );
+ }
}
-}
-function dm_ensure_protocol($domain) {
- if (preg_match('#^https?://#', $domain)) {
- return $domain;
+ /**
+ * 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' ) );
+
+ // 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 );
+ }
+
+ // Handle remote login
+ if ( isset( $_GET['dm'] ) ) {
+ add_action( 'template_redirect', array( $this, 'remote_login_js' ) );
+ }
}
- return 'http://' . $domain;
-}
-function dm_clean_domain($domain) {
- $domain = preg_replace('#^https?://#', '', $domain);
- $domain = rtrim($domain, '/');
- return $domain;
-}
+ /**
+ * 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 );
-// Domain logs display
-function dm_domain_logs() {
- global $wpdb;
- $wpdb->dmtablelogs = $wpdb->base_prefix . 'domain_mapping_logs';
- $logs = $wpdb->get_results("SELECT * FROM {$wpdb->dmtablelogs} ORDER BY timestamp DESC LIMIT 20");
- if (!$logs) {
- echo '' . __('No logs available.', 'wp-domain-mapping') . '
';
- return;
+ 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' ) );
}
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
- user_id)->user_login); ?>
- action); ?>
- domain); ?>
- blog_id); ?>
- timestamp); ?>
-
-
-
-
- 'domains', 'blog_id' => $blog_id), admin_url('network/sites.php'));
- $actions['domains'] = '' . __('Domains', 'wp-domain-mapping') . ' ';
- return $actions;
-}
-add_filter('manage_sites_action_links', 'dm_add_domain_link_to_sites', 10, 2);
+ /**
+ * Actions to perform on plugin activation
+ */
+ public function plugin_activation() {
+ // Create database tables
+ $this->create_tables();
-// Configuration page
-function dm_admin_page() {
- global $wpdb, $current_site;
- if (!dm_site_admin()) {
+ // Initialize remote login hash
+ $this->get_hash();
+ }
+
+ /**
+ * 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' )
+ );
+ }
+
+ /**
+ * 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 ( $this->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 ( $this->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 ( $this->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;
}
- dm_sunrise_warning();
- maybe_create_db();
-
- if ($current_site->path != "/") {
- wp_die(sprintf(__("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"), $current_site->path));
+ /**
+ * 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'
+ ) . '
';
}
- 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);
+ /**
+ * Create required database tables
+ */
+ public function create_tables() {
+ global $wpdb;
- if (!empty($_POST['action']) && $_POST['action'] == 'update') {
- check_admin_referer('domain_mapping');
- $ipok = true;
- $ipaddresses = explode(',', sanitize_text_field($_POST['ipaddress']));
- foreach ($ipaddresses as $address) {
- if (($ip = trim($address)) && !preg_match('|^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$|', $ip)) {
- $ipok = false;
+ // Only network admins can create tables
+ if ( ! $this->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;
+ }
+ }
+
+ /**
+ * 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 = $this->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'] ) ? $this->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 ( ! $this->is_valid_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
+ $wpdb->insert(
+ $this->tables['logs'],
+ array(
+ 'user_id' => $current_user_id,
+ 'action' => 'add',
+ 'domain' => $domain,
+ 'blog_id' => $blog_id
+ ),
+ array( '%d', '%s', '%s', '%d' )
+ );
+
+ $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
+ $wpdb->insert(
+ $this->tables['logs'],
+ array(
+ 'user_id' => $current_user_id,
+ 'action' => 'edit',
+ 'domain' => $domain,
+ 'blog_id' => $blog_id
+ ),
+ array( '%d', '%s', '%s', '%d' )
+ );
+
+ $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;
- }
- }
- if ($ipok) update_site_option('dm_ipaddress', $_POST['ipaddress']);
- if (intval($_POST['always_redirect_admin']) == 0) $_POST['dm_remote_login'] = 0;
- update_site_option('dm_remote_login', intval($_POST['dm_remote_login']));
- 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', '');
- 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);
- }
- ?>
-
-
- query( 'START TRANSACTION' );
+ $deleted = 0;
- if (isset($_GET['updated'])) {
- do_action('dm_echo_updated_msg');
- }
+ try {
+ foreach ( $domains as $del_domain ) {
+ if ( empty( $del_domain ) ) continue;
- dm_sunrise_warning();
+ // 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 (!get_site_option('dm_ipaddress') && !get_site_option('dm_cname')) {
- if (dm_site_admin()) {
- _e("Please set the IP address or CNAME of your server in the site admin page .", 'wp-domain-mapping');
- } else {
- _e("This plugin has not been configured correctly yet.", 'wp-domain-mapping');
- }
- return false;
- }
+ if ( $affected_blog_id ) {
+ // Delete the domain
+ $result = $wpdb->delete(
+ $this->tables['domains'],
+ array( 'domain' => $del_domain ),
+ array( '%s' )
+ );
- $protocol = is_ssl() ? 'https://' : 'http://';
- $domains = $wpdb->get_results("SELECT * FROM {$wpdb->dmtable} WHERE blog_id = '{$wpdb->blogid}'", ARRAY_A);
- ?>
-
-
-
-
-
- $orig_url['host'], 'path' => $orig_url['path'], 'active' => 0); ?>
-
-
-
Warning! Primary domains are currently disabled.', 'wp-domain-mapping'); ?>
-
-
-
-
-
- " . sprintf(__('Add a DNS "CNAME" record pointing to:
%s ', 'wp-domain-mapping'), esc_html(get_site_option('dm_cname'))) . "";
- } else {
- $dm_ipaddress = get_site_option('dm_ipaddress', 'IP not set.');
- echo "
" . sprintf(__('Add a DNS "A" record pointing to: %s ', 'wp-domain-mapping'), esc_html($dm_ipaddress)) . "
";
- }
- ?>
-
Note: %s', 'wp-domain-mapping'), dm_idn_warning()); ?>
-
-
-
- 'domainmapping'), admin_url($parent_file));
- if (!empty($_POST['action'])) {
- $domain = sanitize_text_field($_POST['domain']);
- if (empty($domain)) wp_die(__("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);
- if (null == $wpdb->get_row("SELECT blog_id FROM {$wpdb->blogs} WHERE domain = '$domain'") && null == $wpdb->get_row("SELECT blog_id FROM {$wpdb->dmtable} WHERE domain = '$domain'")) {
- if ($_POST['primary']) {
- $wpdb->query($wpdb->prepare("UPDATE {$wpdb->dmtable} SET active = 0 WHERE blog_id = %d", $wpdb->blogid));
+ // Log the action
+ $wpdb->insert(
+ $this->tables['logs'],
+ array(
+ 'user_id' => $current_user_id,
+ 'action' => 'delete',
+ 'domain' => $del_domain,
+ 'blog_id' => $affected_blog_id
+ ),
+ array( '%d', '%s', '%s', '%d' )
+ );
+ }
+ }
}
- $wpdb->query($wpdb->prepare("INSERT INTO {$wpdb->dmtable} ( `id`, `blog_id`, `domain`, `active` ) VALUES ( NULL, %d, %s, %d )", $wpdb->blogid, $domain, $_POST['primary']));
- wp_redirect(add_query_arg(array('updated' => 'add'), $url));
+
+ 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 ( ! $this->is_valid_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
+ $orig_url = parse_url( $this->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() {
+ global $wpdb, $current_site;
+
+ if ( ! $this->is_site_admin() ) {
+ return false;
+ }
+
+ $this->sunrise_warning();
+ $this->create_tables();
+
+ 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 )
+ ));
+ }
+
+ $total_domains = $wpdb->get_var( "SELECT COUNT(*) FROM {$this->tables['domains']}" );
+ $primary_domains = $wpdb->get_var( "SELECT COUNT(*) FROM {$this->tables['domains']} WHERE active = 1" );
+
+ $edit_row = false;
+ if ( isset( $_GET['edit_domain'] ) ) {
+ $edit_domain = sanitize_text_field( $_GET['edit_domain'] );
+ $edit_row = $wpdb->get_row( $wpdb->prepare(
+ "SELECT * FROM {$this->tables['domains']} WHERE domain = %s",
+ $edit_domain
+ ));
+ }
+
+ // Load admin UI
+ require_once( WP_DOMAIN_MAPPING_DIR_PATH . 'admin/domains-page.php' );
+ }
+
+ /**
+ * Render domain logs
+ */
+ public function render_domain_logs() {
+ global $wpdb;
+
+ $logs = $wpdb->get_results( "SELECT * FROM {$this->tables['logs']} ORDER BY timestamp DESC LIMIT 50" );
+
+ if ( ! $logs ) {
+ echo '' . esc_html__( 'No logs available.', 'wp-domain-mapping' ) . '
';
+ return;
+ }
+
+ // Load logs UI
+ require_once( WP_DOMAIN_MAPPING_DIR_PATH . 'admin/logs-table.php' );
+ }
+
+ /**
+ * Render admin configuration page
+ */
+ public function render_admin_page() {
+ global $wpdb, $current_site;
+
+ if ( ! $this->is_site_admin() ) {
+ return false;
+ }
+
+ $this->sunrise_warning();
+ $this->create_tables();
+
+ 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'
+ );
+ }
+
+ // Load admin UI
+ require_once( WP_DOMAIN_MAPPING_DIR_PATH . 'admin/settings-page.php' );
+ }
+
+ /**
+ * 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 ( $this->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 = $wpdb->get_results( $wpdb->prepare(
+ "SELECT * FROM {$this->tables['domains']} WHERE blog_id = %d",
+ $wpdb->blogid
+ ), ARRAY_A );
+
+ // Load user UI
+ require_once( WP_DOMAIN_MAPPING_DIR_PATH . 'admin/user-page.php' );
+ }
+
+ /**
+ * 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 ) . ' ';
+ }
+ }
+ }
+ }
+
+ /**
+ * 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;
+ }
+ }
+ }
+
+ /**
+ * 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_redirect(add_query_arg(array('updated' => 'exists'), $url));
- exit;
+ wp_die( esc_html__( "Unknown logout key", 'wp-domain-mapping' ) );
}
- case "primary":
- do_action('dm_handle_actions_primary', $domain);
- $wpdb->query($wpdb->prepare("UPDATE {$wpdb->dmtable} SET active = 0 WHERE blog_id = %d", $wpdb->blogid));
- $orig_url = parse_url(get_original_url('siteurl'));
- if ($domain != $orig_url['host']) {
- $wpdb->query($wpdb->prepare("UPDATE {$wpdb->dmtable} SET active = 1 WHERE domain = %s", $domain));
- }
- wp_redirect(add_query_arg(array('updated' => 'primary'), $url));
- exit;
- }
- } elseif ($_GET['action'] == 'delete') {
- $domain = sanitize_text_field($_GET['domain']);
- if (empty($domain)) wp_die(__("You must enter a domain", 'wp-domain-mapping'));
- check_admin_referer("delete" . $_GET['domain']);
- do_action('dm_handle_actions_del', $domain);
- $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->dmtable} WHERE domain = %s", $domain));
- wp_redirect(add_query_arg(array('updated' => 'del'), $url));
- exit;
- }
-}
-if (isset($_GET['page']) && $_GET['page'] == 'domainmapping') {
- add_action('admin_init', 'dm_handle_actions');
-}
-
-// Sunrise check
-function dm_sunrise_warning($die = true) {
- if (!file_exists(WP_CONTENT_DIR . '/sunrise.php')) {
- if (!$die) return true;
- if (dm_site_admin()) {
- wp_die(sprintf(__("Please copy sunrise.php to %s/sunrise.php and ensure the SUNRISE definition is in %swp-config.php", 'wp-domain-mapping'), WP_CONTENT_DIR, ABSPATH));
- } else {
- wp_die(__("This plugin has not been configured correctly yet.", 'wp-domain-mapping'));
- }
- } elseif (!defined('SUNRISE')) {
- if (!$die) return true;
- if (dm_site_admin()) {
- wp_die(sprintf(__("Please uncomment the line define( 'SUNRISE', 'on' ); or add it to your %swp-config.php", 'wp-domain-mapping'), ABSPATH));
- } else {
- wp_die(__("This plugin has not been configured correctly yet.", 'wp-domain-mapping'));
- }
- } elseif (!defined('SUNRISE_LOADED')) {
- if (!$die) return true;
- if (dm_site_admin()) {
- wp_die(sprintf(__("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'), ABSPATH));
- } else {
- wp_die(__("This plugin has not been configured correctly yet.", 'wp-domain-mapping'));
- }
- }
- return false;
-}
-
-// Core domain mapping functions
-function domain_mapping_siteurl($setting) {
- global $wpdb, $current_blog;
- static $return_url = array();
-
- $wpdb->dmtable = $wpdb->base_prefix . 'domain_mapping';
-
- if (!isset($return_url[$wpdb->blogid])) {
- $s = $wpdb->suppress_errors();
- if (get_site_option('dm_no_primary_domain') == 1) {
- $domain = $wpdb->get_var($wpdb->prepare("SELECT domain FROM {$wpdb->dmtable} WHERE blog_id = %d AND domain = %s LIMIT 1", $wpdb->blogid, $_SERVER['HTTP_HOST']));
- if (null == $domain) {
- $return_url[$wpdb->blogid] = untrailingslashit(get_original_url("siteurl"));
- return $return_url[$wpdb->blogid];
- }
- } else {
- $domain = $wpdb->get_var($wpdb->prepare("SELECT domain FROM {$wpdb->dmtable} WHERE blog_id = %d AND active = 1 LIMIT 1", $wpdb->blogid));
- if (null == $domain) {
- $return_url[$wpdb->blogid] = untrailingslashit(get_original_url("siteurl"));
- return $return_url[$wpdb->blogid];
}
}
- $wpdb->suppress_errors($s);
+ }
+
+ /**
+ * 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://';
- if ($domain) {
- $return_url[$wpdb->blogid] = untrailingslashit($protocol . $domain);
+ $hash = $this->get_hash();
+
+ echo "";
+ }
+
+ /**
+ * 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;
}
- } elseif ($return_url[$wpdb->blogid] !== false) {
- $setting = $return_url[$wpdb->blogid];
+
+ return $setting;
}
- return $setting;
-}
-function get_original_url($url, $blog_id = 0) {
- global $wpdb;
- $id = $blog_id ?: $wpdb->blogid;
- static $orig_urls = array();
+ /**
+ * 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])) {
- if (defined('DOMAIN_MAPPING')) remove_filter('pre_option_' . $url, 'domain_mapping_' . $url);
- $orig_url = $blog_id == 0 ? get_option($url) : get_blog_option($blog_id, $url);
- $orig_url = is_ssl() ? str_replace("http://", "https://", $orig_url) : str_replace("https://", "http://", $orig_url);
- $orig_urls[$id] = $orig_url;
- if (defined('DOMAIN_MAPPING')) add_filter('pre_option_' . $url, 'domain_mapping_' . $url);
- }
- return $orig_urls[$id];
-}
-
-function domain_mapping_adminurl($url, $path, $blog_id = 0) {
- $index = strpos($url, '/wp-admin');
- if ($index !== false) {
- $url = 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;
-}
-
-function domain_mapping_post_content($post_content) {
- $orig_url = get_original_url('siteurl');
- $url = domain_mapping_siteurl('NA');
- if ($url == 'NA') return $post_content;
- return str_replace($orig_url, $url, $post_content);
-}
-
-function dm_redirect_admin() {
- if (strpos($_SERVER['REQUEST_URI'], 'wp-admin/admin-ajax.php') !== false) return;
- if (get_site_option('dm_redirect_admin')) {
- $url = get_original_url('siteurl');
- if (false === strpos($url, $_SERVER['HTTP_HOST'])) {
- wp_redirect(untrailingslashit($url) . $_SERVER['REQUEST_URI']);
- exit;
- }
- } else {
- global $current_blog;
- $url = 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;
- }
- }
-}
-
-function redirect_login_to_orig() {
- if (!get_site_option('dm_remote_login') || $_GET['action'] == 'logout' || isset($_GET['loggedout'])) return;
- $url = get_original_url('siteurl');
- if ($url != site_url()) {
- $url .= "/wp-login.php";
- echo "";
- }
-}
-
-function domain_mapping_plugins_uri($full_url, $path = null, $plugin = null) {
- return get_option('siteurl') . substr($full_url, stripos($full_url, PLUGINDIR) - 1);
-}
-
-function domain_mapping_themes_uri($full_url) {
- return str_replace(get_original_url('siteurl'), get_option('siteurl'), $full_url);
-}
-
-if (defined('DOMAIN_MAPPING')) {
- add_filter('plugins_url', 'domain_mapping_plugins_uri', 1);
- add_filter('theme_root_uri', 'domain_mapping_themes_uri', 1);
- add_filter('pre_option_siteurl', 'domain_mapping_siteurl');
- add_filter('pre_option_home', 'domain_mapping_siteurl');
- add_filter('the_content', 'domain_mapping_post_content');
- add_action('wp_head', 'remote_login_js_loader');
- add_action('login_head', 'redirect_login_to_orig');
- add_action('wp_logout', 'remote_logout_loader', 9999);
-
- add_filter('stylesheet_uri', 'domain_mapping_post_content');
- add_filter('stylesheet_directory', 'domain_mapping_post_content');
- add_filter('stylesheet_directory_uri', 'domain_mapping_post_content');
- add_filter('template_directory', 'domain_mapping_post_content');
- add_filter('template_directory_uri', 'domain_mapping_post_content');
- add_filter('plugins_url', 'domain_mapping_post_content');
-} else {
- add_filter('admin_url', 'domain_mapping_adminurl', 10, 3);
-}
-add_action('admin_init', 'dm_redirect_admin');
-if (isset($_GET['dm'])) add_action('template_redirect', 'remote_login_js');
-
-function remote_logout_loader() {
- global $current_site, $current_blog, $wpdb;
- $wpdb->dmtablelogins = $wpdb->base_prefix . 'domain_mapping_logins';
- $protocol = is_ssl() ? 'https://' : 'http://';
- $hash = get_dm_hash();
- $key = md5(time());
- $wpdb->query($wpdb->prepare("INSERT INTO {$wpdb->dmtablelogins} ( `id`, `user_id`, `blog_id`, `t` ) VALUES( %s, 0, %d, NOW() )", $key, $current_blog->blog_id));
- 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;
- }
-}
-
-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 = 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']);
- }
- header("Location: {$url}{$_SERVER['REQUEST_URI']}", true, $redirect);
- exit;
- }
-}
-add_action('template_redirect', 'redirect_to_mapped_domain');
-
-function get_dm_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;
-}
-
-function remote_login_js() {
- global $current_blog, $current_user, $wpdb;
- if (0 == get_site_option('dm_remote_login')) return;
-
- $wpdb->dmtablelogins = $wpdb->base_prefix . 'domain_mapping_logins';
- $hash = get_dm_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->query($wpdb->prepare("INSERT INTO {$wpdb->dmtablelogins} ( `id`, `user_id`, `blog_id`, `t` ) VALUES( %s, %d, %d, NOW() )", $key, $current_user->ID, $_GET['blogid']));
- $url = add_query_arg(array('action' => 'login', 'dm' => $hash, 'k' => $key, 't' => mt_rand()), $_GET['back']);
- echo "window.location = '$url'";
- exit;
- } elseif ($_GET['action'] == 'login') {
- if ($details = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->dmtablelogins} WHERE id = %s AND blog_id = %d", $_GET['k'], $wpdb->blogid))) {
- if ($details->blog_id == $wpdb->blogid) {
- $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->dmtablelogins} WHERE id = %s", $_GET['k']));
- $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->dmtablelogins} WHERE t < %d", (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(__("Incorrect or out of date login key", 'wp-domain-mapping'));
- }
- } else {
- wp_die(__("Unknown login key", 'wp-domain-mapping'));
+ 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' ) );
}
- } elseif ($_GET['action'] == 'logout') {
- if ($details = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->dmtablelogins} WHERE id = %s AND blog_id = %d", $_GET['k'], $_GET['blogid']))) {
- $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->dmtablelogins} WHERE id = %s", $_GET['k']));
- $blog = get_blog_details($_GET['blogid']);
- wp_clear_auth_cookie();
- wp_redirect(trailingslashit($blog->siteurl) . "wp-login.php?loggedout=true");
- exit;
- } else {
- wp_die(__("Unknown logout key", 'wp-domain-mapping'));
+
+ $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];
}
-}
-function remote_login_js_loader() {
- global $current_site, $current_blog;
- if (0 == get_site_option('dm_remote_login') || is_user_logged_in()) return;
+ /**
+ * 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' );
- $protocol = is_ssl() ? 'https://' : 'http://';
- $hash = get_dm_hash();
- echo "";
-}
+ if ( $index !== false ) {
+ $url = $this->get_original_url( 'siteurl', $blog_id ) . substr( $url, $index );
-function delete_blog_domain_mapping($blog_id, $drop) {
- global $wpdb;
- $wpdb->dmtable = $wpdb->base_prefix . 'domain_mapping';
- if ($blog_id && $drop) {
- $domains = $wpdb->get_col($wpdb->prepare("SELECT domain FROM {$wpdb->dmtable} WHERE blog_id = %d", $blog_id));
- do_action('dm_delete_blog_domain_mappings', $domains);
- $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->dmtable} WHERE blog_id = %d", $blog_id));
- }
-}
-add_action('delete_blog', 'delete_blog_domain_mapping', 1, 2);
-
-function ra_domain_mapping_columns($columns) {
- $columns['map'] = __('Mapping');
- return $columns;
-}
-add_filter('wpmu_blogs_columns', 'ra_domain_mapping_columns');
-
-function ra_domain_mapping_field($column, $blog_id) {
- global $wpdb;
- static $maps = false;
-
- if ($column == 'map') {
- if ($maps === false) {
- $wpdb->dmtable = $wpdb->base_prefix . 'domain_mapping';
- $work = $wpdb->get_results("SELECT blog_id, domain FROM {$wpdb->dmtable} ORDER BY blog_id");
- $maps = array();
- if ($work) {
- foreach ($work as $blog) {
- $maps[$blog->blog_id][] = $blog->domain;
- }
+ if ( ( is_ssl() || force_ssl_admin() ) && 0 === strpos( $url, 'http://' ) ) {
+ $url = 'https://' . substr( $url, 7 );
}
}
- if (!empty($maps[$blog_id]) && is_array($maps[$blog_id])) {
- foreach ($maps[$blog_id] as $blog) {
- echo esc_html($blog) . ' ';
- }
+
+ 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;
+ }
+
+ /**
+ * Check if user is a site admin
+ *
+ * @return bool True if user is a site admin
+ */
+ public function is_site_admin() {
+ return current_user_can( 'manage_network' );
+ }
+
+ /**
+ * Clean domain name
+ *
+ * @param string $domain The domain
+ * @return string The cleaned domain
+ */
+ public function clean_domain( $domain ) {
+ // Remove protocol
+ $domain = preg_replace( '#^https?://#', '', $domain );
+
+ // Remove trailing slash
+ $domain = rtrim( $domain, '/' );
+
+ // Convert to punycode for IDN
+ if ( function_exists( 'idn_to_ascii' ) && preg_match( '/[^a-z0-9\-\.]/i', $domain ) ) {
+ $domain = idn_to_ascii( $domain );
+ }
+
+ return $domain;
+ }
+
+ /**
+ * Validate a domain name
+ *
+ * @param string $domain The domain
+ * @return bool True if valid
+ */
+ public function is_valid_domain( $domain ) {
+ // Basic validation
+ return (bool) preg_match( '/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}$/i', $domain );
+ }
+
+ /**
+ * Get IDN warning message
+ *
+ * @return string The warning message
+ */
+ public function idn_warning() {
+ return sprintf(
+ /* translators: %s: URL to punycode converter */
+ __( 'International Domain Names should be in punycode format.', 'wp-domain-mapping' ),
+ "https://www.punycoder.com/"
+ );
+ }
+
+ /**
+ * Ensure URL has protocol
+ *
+ * @param string $domain The domain
+ * @return string The URL with protocol
+ */
+ public function ensure_protocol( $domain ) {
+ if ( preg_match( '#^https?://#', $domain ) ) {
+ return $domain;
+ }
+ return 'http://' . $domain;
+ }
+
+ /**
+ * 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_action('manage_blogs_custom_column', 'ra_domain_mapping_field', 1, 3);
-add_action('manage_sites_custom_column', 'ra_domain_mapping_field', 1, 3);
-function dm_site_admin() {
- return current_user_can('manage_network');
-}
+// Initialize the plugin
+function wp_domain_mapping_init() {
+ // Add default updated messages action
+ add_action( 'dm_echo_updated_msg', array( WP_Domain_Mapping::get_instance(), 'echo_default_updated_msg' ) );
-function dm_idn_warning() {
- return sprintf(__('International Domain Names should be in punycode format.', 'wp-domain-mapping'), "https://www.punycoder.com/");
+ // Initialize the plugin
+ return WP_Domain_Mapping::get_instance();
}
-?>
+wp_domain_mapping_init();
+
+// Include admin UI templates
+require_once( WP_DOMAIN_MAPPING_DIR_PATH . 'includes/admin-ui.php' );