mirror of
https://github.com/WenPai-org/wp-domain-mapping.git
synced 2025-08-03 05:33:23 +08:00
v2.0 版本重写
新增多项改进
This commit is contained in:
parent
31bf7145ea
commit
fbebd6ea2f
12 changed files with 4241 additions and 2180 deletions
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* Domains administration page
|
||||
* Domains administration page template
|
||||
*
|
||||
* @package WP Domain Mapping
|
||||
*/
|
||||
|
@ -9,6 +9,8 @@
|
|||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// This file is included from the render_domains_admin method
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html( get_admin_page_title() ); ?>
|
||||
|
@ -16,7 +18,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
<?php printf( esc_html__( 'Version: %s', 'wp-domain-mapping' ), esc_html( WP_DOMAIN_MAPPING_VERSION ) ); ?>
|
||||
</span>
|
||||
<a href="https://wpmultisite.com/document/wp-domain-mapping" target="_blank" class="button button-secondary" style="margin-left: 10px;">
|
||||
<?php esc_html_e( 'Documentation', 'wp-domain-mapping' ); ?>
|
||||
<?php esc_html_e( 'Document', 'wp-domain-mapping' ); ?>
|
||||
</a>
|
||||
<a href="https://wpmultisite.com/forums/" target="_blank" class="button button-secondary">
|
||||
<?php esc_html_e( 'Support', 'wp-domain-mapping' ); ?>
|
||||
|
@ -26,7 +28,70 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
<div class="card domain-mapping-card">
|
||||
<h2><?php echo $edit_row ? esc_html__( 'Edit Domain', 'wp-domain-mapping' ) : esc_html__( 'Add New Domain', 'wp-domain-mapping' ); ?></h2>
|
||||
<div id="edit-domain-status" class="notice" style="display:none;"></div>
|
||||
<?php dm_edit_domain( $edit_row ); ?>
|
||||
|
||||
<form id="edit-domain-form" method="POST">
|
||||
<input type="hidden" name="orig_domain" value="<?php echo esc_attr( $edit_row ? $edit_row->domain : '' ); ?>" />
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th><label for="blog_id"><?php esc_html_e( 'Site ID', 'wp-domain-mapping' ); ?></label></th>
|
||||
<td>
|
||||
<input type="number" id="blog_id" name="blog_id" value="<?php echo esc_attr( $edit_row ? $edit_row->blog_id : '' ); ?>" class="regular-text" required />
|
||||
<?php if ( ! $edit_row ) : ?>
|
||||
<p class="description">
|
||||
<?php
|
||||
$site_list_url = network_admin_url( 'sites.php' );
|
||||
printf(
|
||||
/* translators: %s: URL to sites list */
|
||||
wp_kses(
|
||||
__( 'Not sure about Site ID? <a href="%s" target="_blank">View all sites</a> to find the ID.', 'wp-domain-mapping' ),
|
||||
array( 'a' => array( 'href' => array(), 'target' => array() ) )
|
||||
),
|
||||
esc_url( $site_list_url )
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><label for="domain"><?php esc_html_e( 'Domain', 'wp-domain-mapping' ); ?></label></th>
|
||||
<td>
|
||||
<input type="text" id="domain" name="domain" value="<?php echo esc_attr( $edit_row ? $edit_row->domain : '' ); ?>" class="regular-text" required
|
||||
placeholder="example.com" pattern="^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\.[a-zA-Z]{2,}$" />
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Enter the domain without http:// or https:// (e.g., example.com)', 'wp-domain-mapping' ); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><label for="active"><?php esc_html_e( 'Primary', 'wp-domain-mapping' ); ?></label></th>
|
||||
<td>
|
||||
<input type="checkbox" id="active" name="active" value="1" <?php checked( $edit_row ? $edit_row->active : 0, 1 ); ?> />
|
||||
<span class="description">
|
||||
<?php esc_html_e( 'Set as the primary domain for this site', 'wp-domain-mapping' ); ?>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<?php if ( get_site_option( 'dm_no_primary_domain' ) == 1 ) : ?>
|
||||
<tr>
|
||||
<td colspan="2" class="notice notice-warning">
|
||||
<p><?php esc_html_e( 'Warning! Primary domains are currently disabled in network settings.', 'wp-domain-mapping' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
<p>
|
||||
<input type="submit" class="button button-primary" value="<?php echo $edit_row
|
||||
? esc_attr__( 'Update Domain', 'wp-domain-mapping' )
|
||||
: esc_attr__( 'Add Domain', 'wp-domain-mapping' ); ?>" />
|
||||
|
||||
<?php if ( $edit_row ) : ?>
|
||||
<a href="<?php echo esc_url( admin_url( 'network/sites.php?page=domains' ) ); ?>" class="button button-secondary">
|
||||
<?php esc_html_e( 'Cancel', 'wp-domain-mapping' ); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card domain-mapping-card">
|
||||
|
@ -112,14 +177,15 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
}
|
||||
|
||||
$where_sql = $where ? ' WHERE ' . implode( ' AND ', $where ) : '';
|
||||
$tables = dm_get_table_names();
|
||||
|
||||
// Count total items for pagination
|
||||
$total_items = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->base_prefix}" . WP_DOMAIN_MAPPING_TABLE_DOMAINS . $where_sql );
|
||||
$total_items = $wpdb->get_var( "SELECT COUNT(*) FROM {$tables['domains']}" . $where_sql );
|
||||
$total_pages = ceil( $total_items / $per_page );
|
||||
|
||||
// Get the domains with pagination
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->base_prefix}" . WP_DOMAIN_MAPPING_TABLE_DOMAINS . $where_sql . " ORDER BY id DESC LIMIT %d, %d",
|
||||
"SELECT * FROM {$tables['domains']}" . $where_sql . " ORDER BY id DESC LIMIT %d, %d",
|
||||
$offset,
|
||||
$per_page
|
||||
));
|
||||
|
@ -191,7 +257,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
<th><?php esc_html_e( 'Sites with Mapped Domains', 'wp-domain-mapping' ); ?></th>
|
||||
<td>
|
||||
<?php
|
||||
$sites_with_domains = $wpdb->get_var( "SELECT COUNT(DISTINCT blog_id) FROM {$wpdb->base_prefix}" . WP_DOMAIN_MAPPING_TABLE_DOMAINS );
|
||||
$sites_with_domains = $wpdb->get_var( "SELECT COUNT(DISTINCT blog_id) FROM {$tables['domains']}" );
|
||||
echo esc_html( $sites_with_domains );
|
||||
?>
|
||||
</td>
|
||||
|
@ -201,314 +267,162 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
jQuery(document).ready(function($) {
|
||||
// Tab switching functionality
|
||||
$('.domain-mapping-tab').on('click', function() {
|
||||
$('.domain-mapping-tab').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
<?php
|
||||
/**
|
||||
* Render domain listing table
|
||||
*
|
||||
* @param array $rows Domain data rows
|
||||
*/
|
||||
function dm_domain_listing( $rows ) {
|
||||
global $wpdb;
|
||||
|
||||
var tab = $(this).data('tab');
|
||||
$('.domain-mapping-section').hide();
|
||||
$('.domain-mapping-section[data-section="' + tab + '"]').show();
|
||||
});
|
||||
|
||||
// Helper function for showing notices
|
||||
function showNotice(selector, message, type) {
|
||||
$(selector)
|
||||
.removeClass('notice-success notice-error notice-warning notice-info')
|
||||
.addClass('notice-' + type)
|
||||
.html('<p>' + message + '</p>')
|
||||
.show()
|
||||
.delay(3000)
|
||||
.fadeOut();
|
||||
if ( ! $rows ) {
|
||||
echo '<div class="notice notice-info"><p>' . esc_html__( 'No domains found.', 'wp-domain-mapping' ) . '</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle domain edit/add form submission
|
||||
$('#edit-domain-form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
$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' )
|
||||
);
|
||||
?>
|
||||
<div class="tablenav top">
|
||||
<div class="alignleft actions bulkactions">
|
||||
<label for="bulk-action-selector-top" class="screen-reader-text"><?php esc_html_e( 'Select bulk action', 'wp-domain-mapping' ); ?></label>
|
||||
<select id="bulk-action-selector-top" name="action">
|
||||
<option value="-1"><?php esc_html_e( 'Bulk Actions', 'wp-domain-mapping' ); ?></option>
|
||||
<option value="delete"><?php esc_html_e( 'Delete', 'wp-domain-mapping' ); ?></option>
|
||||
</select>
|
||||
<input type="submit" class="button action" value="<?php esc_attr_e( 'Apply', 'wp-domain-mapping' ); ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
// Validate form
|
||||
var blogId = $('#blog_id').val();
|
||||
var domain = $('#domain').val();
|
||||
<table class="wp-list-table widefat striped domains-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="manage-column column-cb check-column">
|
||||
<input id="select-all" type="checkbox" />
|
||||
</td>
|
||||
<th scope="col" class="column-site-id"><?php esc_html_e( 'Site ID', 'wp-domain-mapping' ); ?></th>
|
||||
<th scope="col" class="column-site-name"><?php esc_html_e( 'Site Name', 'wp-domain-mapping' ); ?></th>
|
||||
<th scope="col" class="column-domain"><?php esc_html_e( 'Domain', 'wp-domain-mapping' ); ?></th>
|
||||
<th scope="col" class="column-primary"><?php esc_html_e( 'Primary', 'wp-domain-mapping' ); ?></th>
|
||||
<th scope="col" class="column-actions"><?php esc_html_e( 'Actions', 'wp-domain-mapping' ); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ( $rows as $row ) :
|
||||
$site_name = get_blog_option( $row->blog_id, 'blogname', esc_html__( 'Unknown', 'wp-domain-mapping' ) );
|
||||
?>
|
||||
<tr>
|
||||
<th scope="row" class="check-column">
|
||||
<input type="checkbox" class="domain-checkbox" value="<?php echo esc_attr( $row->domain ); ?>" />
|
||||
</th>
|
||||
<td class="column-site-id">
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'action' => 'editblog', 'id' => $row->blog_id ), $edit_url ) ); ?>">
|
||||
<?php echo esc_html( $row->blog_id ); ?>
|
||||
</a>
|
||||
</td>
|
||||
<td class="column-site-name">
|
||||
<?php echo esc_html( $site_name ); ?>
|
||||
</td>
|
||||
<td class="column-domain">
|
||||
<a href="<?php echo esc_url( dm_ensure_protocol( $row->domain ) ); ?>" target="_blank">
|
||||
<?php echo esc_html( $row->domain ); ?>
|
||||
<span class="dashicons dashicons-external" style="font-size: 14px; line-height: 1.3; opacity: 0.7;"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td class="column-primary">
|
||||
<?php if ( $row->active == 1 ) : ?>
|
||||
<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>
|
||||
<span class="screen-reader-text"><?php esc_html_e( 'Yes', 'wp-domain-mapping' ); ?></span>
|
||||
<?php else : ?>
|
||||
<span class="dashicons dashicons-no-alt" style="color: #dc3232;"></span>
|
||||
<span class="screen-reader-text"><?php esc_html_e( 'No', 'wp-domain-mapping' ); ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="column-actions">
|
||||
<div class="row-actions">
|
||||
<span class="edit">
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'edit_domain' => $row->domain ), admin_url( 'network/sites.php?page=domains' ) ) ); ?>" class="button button-small">
|
||||
<?php esc_html_e( 'Edit', 'wp-domain-mapping' ); ?>
|
||||
</a>
|
||||
</span>
|
||||
<?php if ( $row->active != 1 ) : ?>
|
||||
<span class="delete">
|
||||
<a href="#" class="button button-small domain-delete-button" data-domain="<?php echo esc_attr( $row->domain ); ?>">
|
||||
<?php esc_html_e( 'Delete', 'wp-domain-mapping' ); ?>
|
||||
</a>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
if (!blogId || !domain) {
|
||||
showNotice('#edit-domain-status', '<?php esc_html_e( 'Please fill in all required fields.', 'wp-domain-mapping' ); ?>', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
var formData = $(this).serializeArray();
|
||||
formData.push({name: 'action', value: 'dm_handle_actions'});
|
||||
formData.push({name: 'action_type', value: 'save'});
|
||||
formData.push({name: 'nonce', value: '<?php echo wp_create_nonce( 'domain_mapping' ); ?>'});
|
||||
|
||||
$('#edit-domain-status').html('<p><?php esc_html_e( 'Saving...', 'wp-domain-mapping' ); ?></p>').show();
|
||||
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showNotice('#edit-domain-status', response.data, 'success');
|
||||
setTimeout(function() {
|
||||
location.href = '<?php echo esc_js( admin_url( 'network/sites.php?page=domains' ) ); ?>';
|
||||
}, 1000);
|
||||
} else {
|
||||
showNotice('#edit-domain-status', response.data || '<?php esc_html_e( 'Failed to save domain.', 'wp-domain-mapping' ); ?>', 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showNotice('#edit-domain-status', '<?php esc_html_e( 'Server error occurred.', 'wp-domain-mapping' ); ?>', 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Handle domain list 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', '<?php esc_html_e( 'Please select at least one domain.', 'wp-domain-mapping' ); ?>', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
var action = $('#bulk-action-selector-top').val();
|
||||
if (action === '-1') return;
|
||||
|
||||
if (!confirm('<?php esc_html_e( 'Are you sure you want to delete the selected domains?', 'wp-domain-mapping' ); ?>')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$('#domain-status').html('<p><?php esc_html_e( 'Processing...', 'wp-domain-mapping' ); ?></p>').show();
|
||||
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'dm_handle_actions',
|
||||
action_type: 'delete',
|
||||
domains: selectedDomains,
|
||||
nonce: '<?php echo wp_create_nonce( 'domain_mapping' ); ?>'
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showNotice('#domain-status', response.data, 'success');
|
||||
setTimeout(function() {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
showNotice('#domain-status', response.data || '<?php esc_html_e( 'Failed to delete domains.', 'wp-domain-mapping' ); ?>', 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showNotice('#domain-status', '<?php esc_html_e( 'Server error occurred.', 'wp-domain-mapping' ); ?>', 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Main cards */
|
||||
.domain-mapping-card {
|
||||
background: #fff;
|
||||
border: 1px solid #ccd0d4;
|
||||
border-radius: 4px;
|
||||
max-width: unset;
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,.04);
|
||||
<?php if ( get_site_option( 'dm_no_primary_domain' ) == 1 ) : ?>
|
||||
<div class="notice notice-warning inline">
|
||||
<p><?php esc_html_e( 'Warning! Primary domains are currently disabled in network settings.', 'wp-domain-mapping' ); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.domain-mapping-tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
border-bottom: 1px solid #c3c4c7;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
/**
|
||||
* Render domain logs table
|
||||
*/
|
||||
function dm_domain_logs() {
|
||||
global $wpdb;
|
||||
|
||||
.domain-mapping-tab {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
$tables = dm_get_table_names();
|
||||
|
||||
.domain-mapping-tab.active {
|
||||
border-bottom: 2px solid #007cba;
|
||||
font-weight: 600;
|
||||
background: #f0f0f1;
|
||||
}
|
||||
|
||||
.domain-mapping-tab:hover:not(.active) {
|
||||
background: #f0f0f1;
|
||||
border-bottom-color: #dcdcde;
|
||||
}
|
||||
|
||||
.domain-mapping-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Search form */
|
||||
.search-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-end;
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.search-form-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.search-form-field label {
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.search-form-submit {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.tablenav {
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tablenav-pages {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.tablenav-pages a,
|
||||
.tablenav-pages span.current {
|
||||
display: inline-block;
|
||||
min-width: 17px;
|
||||
padding: 3px 5px 7px;
|
||||
background: #f0f0f1;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tablenav-pages span.current {
|
||||
background: #007cba;
|
||||
color: #fff;
|
||||
border-color: #007cba;
|
||||
}
|
||||
|
||||
.displaying-num {
|
||||
margin-left: 10px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.domains-table th,
|
||||
.logs-table th {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.column-site-id {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.column-site-name {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.column-primary {
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.column-actions {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
/* Form elements */
|
||||
.form-table th {
|
||||
width: 200px;
|
||||
padding: 15px 10px 15px 0;
|
||||
}
|
||||
|
||||
.form-table td {
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* Notices */
|
||||
.notice {
|
||||
padding: 8px 12px;
|
||||
border-radius: 3px;
|
||||
margin: 5px 0 15px;
|
||||
}
|
||||
|
||||
.notice p {
|
||||
margin: 0.5em 0;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.notice-success {
|
||||
background-color: #f0f9eb;
|
||||
border-left: 4px solid #46b450;
|
||||
}
|
||||
|
||||
.notice-error {
|
||||
background-color: #fef0f0;
|
||||
border-left: 4px solid #dc3232;
|
||||
}
|
||||
|
||||
.notice-warning {
|
||||
background-color: #fff8e5;
|
||||
border-left: 4px solid #ffb900;
|
||||
}
|
||||
|
||||
.notice-info {
|
||||
background-color: #f0f6fa;
|
||||
border-left: 4px solid #00a0d2;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media screen and (max-width: 782px) {
|
||||
.search-form {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
// Make sure the table exists
|
||||
if ( $wpdb->get_var( "SHOW TABLES LIKE '{$tables['logs']}'" ) != $tables['logs'] ) {
|
||||
echo '<div class="notice notice-error"><p>' .
|
||||
esc_html__('Domain mapping logs table is missing. Please deactivate and reactivate the plugin.', 'wp-domain-mapping') .
|
||||
'</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
.search-form-field {
|
||||
min-width: 100%;
|
||||
// Get pagination parameters
|
||||
$per_page = isset( $_GET['logs_per_page'] ) ? absint( $_GET['logs_per_page'] ) : 50;
|
||||
$paged = isset( $_GET['logs_paged'] ) ? absint( $_GET['logs_paged'] ) : 1;
|
||||
$offset = ( $paged - 1 ) * $per_page;
|
||||
|
||||
// Get action filter
|
||||
$action_filter = isset( $_GET['log_action'] ) ? sanitize_text_field( $_GET['log_action'] ) : '';
|
||||
|
||||
// Build WHERE clause for filtering
|
||||
$where = array();
|
||||
if ( ! empty( $action_filter ) ) {
|
||||
$where[] = $wpdb->prepare( "action = %s", $action_filter );
|
||||
}
|
||||
|
||||
.form-table th {
|
||||
width: 100%;
|
||||
display: block;
|
||||
$where_sql = $where ? ' WHERE ' . implode( ' AND ', $where ) : '';
|
||||
|
||||
// Count total logs for pagination
|
||||
$total_logs = $wpdb->get_var( "SELECT COUNT(*) FROM {$tables['logs']}" . $where_sql );
|
||||
$total_pages = ceil( $total_logs / $per_page );
|
||||
|
||||
// Get logs with pagination
|
||||
$logs = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT * FROM {$tables['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 {$tables['logs']}" );
|
||||
|
||||
if ( ! $logs ) {
|
||||
echo '<div class="notice notice-info"><p>' . esc_html__( 'No domain mapping logs available.', 'wp-domain-mapping' ) . '</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
.form-table td {
|
||||
display: block;
|
||||
padding: 0 0 15px;
|
||||
}
|
||||
// Include the logs table template
|
||||
include WP_DOMAIN_MAPPING_DIR_PATH . 'admin/logs-table.php';
|
||||
}
|
||||
</style>
|
||||
?>
|
||||
|
|
|
@ -10,46 +10,12 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
exit;
|
||||
}
|
||||
|
||||
// Get pagination parameters
|
||||
$per_page = isset( $_GET['logs_per_page'] ) ? absint( $_GET['logs_per_page'] ) : 50;
|
||||
$paged = isset( $_GET['logs_paged'] ) ? absint( $_GET['logs_paged'] ) : 1;
|
||||
$offset = ( $paged - 1 ) * $per_page;
|
||||
|
||||
// Get action filter
|
||||
$action_filter = isset( $_GET['log_action'] ) ? sanitize_text_field( $_GET['log_action'] ) : '';
|
||||
|
||||
// Build WHERE clause for filtering
|
||||
$where = array();
|
||||
if ( ! empty( $action_filter ) ) {
|
||||
$where[] = $wpdb->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 '<div class="notice notice-info"><p>' . esc_html__( 'No domain mapping logs available.', 'wp-domain-mapping' ) . '</p></div>';
|
||||
return;
|
||||
}
|
||||
// This file is included from dm_domain_logs() function
|
||||
?>
|
||||
|
||||
<div class="tablenav top">
|
||||
<form method="GET" id="logs-filter-form" class="logs-filter">
|
||||
<input type="hidden" name="page" value="domains" />
|
||||
<input type="hidden" name="tab" value="domain-logs" />
|
||||
|
||||
<div class="alignleft actions">
|
||||
<label for="log_action" class="screen-reader-text"><?php esc_html_e( 'Filter by action', 'wp-domain-mapping' ); ?></label>
|
||||
|
@ -57,21 +23,7 @@ if ( ! $logs ) {
|
|||
<option value=""><?php esc_html_e( 'All actions', 'wp-domain-mapping' ); ?></option>
|
||||
<?php foreach ( $actions as $action ) : ?>
|
||||
<option value="<?php echo esc_attr( $action ); ?>" <?php selected( $action_filter, $action ); ?>>
|
||||
<?php
|
||||
switch ( $action ) {
|
||||
case 'add':
|
||||
esc_html_e( 'Added', 'wp-domain-mapping' );
|
||||
break;
|
||||
case 'edit':
|
||||
esc_html_e( 'Updated', 'wp-domain-mapping' );
|
||||
break;
|
||||
case 'delete':
|
||||
esc_html_e( 'Deleted', 'wp-domain-mapping' );
|
||||
break;
|
||||
default:
|
||||
echo esc_html( ucfirst( $action ) );
|
||||
}
|
||||
?>
|
||||
<?php echo esc_html( dm_format_action_name( $action ) ); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
@ -79,7 +31,7 @@ if ( ! $logs ) {
|
|||
<input type="submit" class="button" value="<?php esc_attr_e( 'Filter', 'wp-domain-mapping' ); ?>" />
|
||||
|
||||
<?php if ( ! empty( $action_filter ) ) : ?>
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'page' => 'domains', 'tab' => 'domain-logs' ), admin_url( 'network/sites.php' ) ) ); ?>" class="button">
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'page' => 'domains' ), admin_url( 'network/sites.php' ) ) ); ?>" class="button">
|
||||
<?php esc_html_e( 'Clear', 'wp-domain-mapping' ); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
@ -136,19 +88,8 @@ if ( ! $logs ) {
|
|||
: sprintf( esc_html__( 'User #%d', 'wp-domain-mapping' ), $log->user_id );
|
||||
|
||||
// Format action for display
|
||||
switch ( $log->action ) {
|
||||
case 'add':
|
||||
$action_display = '<span class="log-action log-action-add">' . esc_html__( 'Added', 'wp-domain-mapping' ) . '</span>';
|
||||
break;
|
||||
case 'edit':
|
||||
$action_display = '<span class="log-action log-action-edit">' . esc_html__( 'Updated', 'wp-domain-mapping' ) . '</span>';
|
||||
break;
|
||||
case 'delete':
|
||||
$action_display = '<span class="log-action log-action-delete">' . esc_html__( 'Deleted', 'wp-domain-mapping' ) . '</span>';
|
||||
break;
|
||||
default:
|
||||
$action_display = '<span class="log-action">' . esc_html( ucfirst( $log->action ) ) . '</span>';
|
||||
}
|
||||
$action_display = dm_format_action_name( $log->action );
|
||||
$action_class = 'log-action-' . $log->action;
|
||||
|
||||
// Get site name
|
||||
$site_name = get_blog_option( $log->blog_id, 'blogname', '' );
|
||||
|
@ -166,7 +107,11 @@ if ( ! $logs ) {
|
|||
?>
|
||||
<tr>
|
||||
<td class="column-user"><?php echo $username; ?></td>
|
||||
<td class="column-action"><?php echo $action_display; ?></td>
|
||||
<td class="column-action">
|
||||
<span class="log-action <?php echo esc_attr( $action_class ); ?>">
|
||||
<?php echo esc_html( $action_display ); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="column-domain">
|
||||
<a href="<?php echo esc_url( dm_ensure_protocol( $log->domain ) ); ?>" target="_blank">
|
||||
<?php echo esc_html( $log->domain ); ?>
|
||||
|
@ -216,73 +161,3 @@ if ( ! $logs ) {
|
|||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<style>
|
||||
/* Logs table styles */
|
||||
.logs-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.logs-table .column-user,
|
||||
.logs-table .column-site {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.logs-table .column-action {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.logs-table .column-date {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.log-action {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log-action-add {
|
||||
background-color: #dff0d8;
|
||||
color: #3c763d;
|
||||
}
|
||||
|
||||
.log-action-edit {
|
||||
background-color: #d9edf7;
|
||||
color: #31708f;
|
||||
}
|
||||
|
||||
.log-action-delete {
|
||||
background-color: #f2dede;
|
||||
color: #a94442;
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.tablenav-pages a,
|
||||
.tablenav-pages span.current {
|
||||
display: inline-block;
|
||||
min-width: 17px;
|
||||
padding: 3px 5px 7px;
|
||||
background: #f0f0f1;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tablenav-pages span.current {
|
||||
background: #007cba;
|
||||
color: #fff;
|
||||
border-color: #007cba;
|
||||
}
|
||||
|
||||
.displaying-num {
|
||||
margin-left: 10px;
|
||||
color: #555;
|
||||
}
|
||||
</style>
|
||||
|
|
1033
admin/pages.php
Normal file
1033
admin/pages.php
Normal file
File diff suppressed because it is too large
Load diff
|
@ -39,7 +39,7 @@ settings_errors( 'dm_settings' );
|
|||
<?php printf( esc_html__( 'Version: %s', 'wp-domain-mapping' ), esc_html( WP_DOMAIN_MAPPING_VERSION ) ); ?>
|
||||
</span>
|
||||
<a href="https://wpmultisite.com/document/wp-domain-mapping" target="_blank" class="button button-secondary" style="margin-left: 10px;">
|
||||
<?php esc_html_e( 'Documentation', 'wp-domain-mapping' ); ?>
|
||||
<?php esc_html_e( 'Document', 'wp-domain-mapping' ); ?>
|
||||
</a>
|
||||
<a href="https://wpmultisite.com/forums/" target="_blank" class="button button-secondary">
|
||||
<?php esc_html_e( 'Support', 'wp-domain-mapping' ); ?>
|
||||
|
|
|
@ -9,11 +9,6 @@ defined('ABSPATH') || exit;
|
|||
|
||||
$protocol = isset($protocol) ? $protocol : (is_ssl() ? 'https://' : 'http://');
|
||||
$domains = isset($domains) ? $domains : array();
|
||||
|
||||
// Initialize logs array if not set to prevent PHP warnings
|
||||
if (!isset($logs) || !is_array($logs)) {
|
||||
$logs = array();
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="wrap">
|
||||
|
@ -97,18 +92,18 @@ if (!isset($logs) || !is_array($logs)) {
|
|||
<?php foreach ($domains as $domain): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="<?php echo esc_url($protocol . $domain['domain']); ?>" target="_blank">
|
||||
<?php echo esc_html($domain['domain']); ?>
|
||||
<a href="<?php echo esc_url($protocol . $domain->domain); ?>" target="_blank">
|
||||
<?php echo esc_html($domain->domain); ?>
|
||||
<span class="dashicons dashicons-external" style="font-size: 14px; line-height: 1.3; opacity: 0.7;"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($domain['active'] == 1): ?>
|
||||
<?php if ($domain->active == 1): ?>
|
||||
<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>
|
||||
<span class="screen-reader-text"><?php _e('Yes', 'wp-domain-mapping'); ?></span>
|
||||
<?php else: ?>
|
||||
<a href="<?php echo wp_nonce_url(add_query_arg(
|
||||
array('page' => 'domainmapping', 'action' => 'primary', 'domain' => $domain['domain']),
|
||||
array('page' => 'domainmapping', 'action' => 'primary', 'domain' => $domain->domain),
|
||||
admin_url('tools.php')
|
||||
), 'domain_mapping'); ?>" class="button button-small">
|
||||
<?php _e('Make Primary', 'wp-domain-mapping'); ?>
|
||||
|
@ -116,11 +111,11 @@ if (!isset($logs) || !is_array($logs)) {
|
|||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($domain['active'] != 1): ?>
|
||||
<?php if ($domain->active != 1): ?>
|
||||
<a href="<?php echo wp_nonce_url(add_query_arg(
|
||||
array('page' => 'domainmapping', 'action' => 'delete', 'domain' => $domain['domain']),
|
||||
array('page' => 'domainmapping', 'action' => 'delete', 'domain' => $domain->domain),
|
||||
admin_url('tools.php')
|
||||
), 'delete' . $domain['domain']); ?>" class="button button-small button-link-delete" onclick="return confirm('<?php _e('Are you sure you want to delete this domain?', 'wp-domain-mapping'); ?>');">
|
||||
), 'delete' . $domain->domain); ?>" class="button button-small button-link-delete" onclick="return confirm('<?php _e('Are you sure you want to delete this domain?', 'wp-domain-mapping'); ?>');">
|
||||
<?php _e('Delete', 'wp-domain-mapping'); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
@ -160,24 +155,3 @@ if (!isset($logs) || !is_array($logs)) {
|
|||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* Main cards */
|
||||
.domain-mapping-card {
|
||||
background: #fff;
|
||||
border: 1px solid #ccd0d4;
|
||||
border-radius: 4px;
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,.04);
|
||||
}
|
||||
|
||||
.domains-table .column-primary {
|
||||
width: 150px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.domains-table .column-actions {
|
||||
width: 120px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
.card {
|
||||
/* WP Domain Mapping Admin Styles */
|
||||
|
||||
/* Main cards */
|
||||
.domain-mapping-card {
|
||||
background: #fff;
|
||||
border: 1px solid #ccd0d4;
|
||||
border-radius: 4px;
|
||||
max-width: unset;
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,.04);
|
||||
}
|
||||
.styles-sync-tabs {
|
||||
|
||||
/* Tabs */
|
||||
.domain-mapping-tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
border-bottom: 1px solid #c3c4c7;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.styles-tab {
|
||||
|
||||
.domain-mapping-tab {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
background: none;
|
||||
|
@ -21,44 +28,362 @@
|
|||
font-size: 14px;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
.styles-tab.active {
|
||||
|
||||
.domain-mapping-tab.active {
|
||||
border-bottom: 2px solid #007cba;
|
||||
font-weight: 600;
|
||||
background: #f0f0f1;
|
||||
}
|
||||
.styles-tab:hover:not(.active) {
|
||||
|
||||
.domain-mapping-tab:hover:not(.active) {
|
||||
background: #f0f0f1;
|
||||
border-bottom-color: #dcdcde;
|
||||
}
|
||||
.styles-sync-content { flex: 1; }
|
||||
.tablenav { margin: 10px 0; }
|
||||
.tablenav-pages { float: right; }
|
||||
.tablenav-pages a, .tablenav-pages span { padding: 5px 10px; }
|
||||
.form-table th { width: 200px; }
|
||||
.form-table td { padding: 10px
|
||||
|
||||
System: 0; }
|
||||
.description { color: #666; font-size: 12px; }
|
||||
.notice { padding: 8px 12px; border-radius: 3px; }
|
||||
.notice-success { background-color: #dff0d8; border-left: 4px solid #46b450; }
|
||||
.notice-error { background-color: #f2dede; border-left: 4px solid #dc3232; }
|
||||
.domain-mapping-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Responsive Styles */
|
||||
/* Search form */
|
||||
.search-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-end;
|
||||
gap: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.search-form-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.search-form-field label {
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.search-form-submit {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
.tablenav {
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tablenav-pages {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.tablenav-pages a,
|
||||
.tablenav-pages span.current {
|
||||
display: inline-block;
|
||||
min-width: 17px;
|
||||
padding: 3px 5px 7px;
|
||||
background: #f0f0f1;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tablenav-pages span.current {
|
||||
background: #007cba;
|
||||
color: #fff;
|
||||
border-color: #007cba;
|
||||
}
|
||||
|
||||
.displaying-num {
|
||||
margin-left: 10px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.domains-table th,
|
||||
.logs-table th,
|
||||
.domains-health-table th {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.column-site-id {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.column-site-name {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.column-primary {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.column-actions {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
/* Health table specific */
|
||||
.column-domain {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.column-site {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.column-dns,
|
||||
.column-ssl,
|
||||
.column-status {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.column-last-check {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
/* Site ID Badge */
|
||||
.dm-site-id-badge {
|
||||
display: inline-block;
|
||||
background: #2271b1;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Form elements */
|
||||
.form-table th {
|
||||
width: 200px;
|
||||
padding: 15px 10px 15px 0;
|
||||
}
|
||||
|
||||
.form-table td {
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* Notices */
|
||||
.notice {
|
||||
padding: 8px 12px;
|
||||
border-radius: 3px;
|
||||
margin: 5px 0 15px;
|
||||
}
|
||||
|
||||
.notice p {
|
||||
margin: 0.5em 0;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.notice-success {
|
||||
background-color: #f0f9eb;
|
||||
border-left: 4px solid #46b450;
|
||||
}
|
||||
|
||||
.notice-error {
|
||||
background-color: #fef0f0;
|
||||
border-left: 4px solid #dc3232;
|
||||
}
|
||||
|
||||
.notice-warning {
|
||||
background-color: #fff8e5;
|
||||
border-left: 4px solid #ffb900;
|
||||
}
|
||||
|
||||
.notice-info {
|
||||
background-color: #f0f6fa;
|
||||
border-left: 4px solid #00a0d2;
|
||||
}
|
||||
|
||||
/* DNS instructions */
|
||||
.dns-instructions {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.dns-example {
|
||||
background: #f9f9f9;
|
||||
border: 1px solid #e5e5e5;
|
||||
border-radius: 3px;
|
||||
padding: 15px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.dns-example h4 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.dns-tips {
|
||||
list-style: disc;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.dns-tips li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Status indicators */
|
||||
.dashicons-yes-alt,
|
||||
.dashicons-no-alt {
|
||||
font-size: 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
/* Logs table styles */
|
||||
.logs-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.logs-table .column-user,
|
||||
.logs-table .column-site {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.logs-table .column-action {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.logs-table .column-date {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.log-action {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log-action-add {
|
||||
background-color: #dff0d8;
|
||||
color: #3c763d;
|
||||
}
|
||||
|
||||
.log-action-edit {
|
||||
background-color: #d9edf7;
|
||||
color: #31708f;
|
||||
}
|
||||
|
||||
.log-action-delete {
|
||||
background-color: #f2dede;
|
||||
color: #a94442;
|
||||
}
|
||||
|
||||
/* Health details */
|
||||
.health-details-content {
|
||||
padding: 15px;
|
||||
background: #f9f9f9;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.health-details-content h4 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Progress bar */
|
||||
.progress-bar-outer {
|
||||
background-color: #f0f0f1;
|
||||
border-radius: 4px;
|
||||
height: 20px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.progress-bar-inner {
|
||||
background-color: #2271b1;
|
||||
height: 100%;
|
||||
width: 0%;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Import/Export specific */
|
||||
.import-summary,
|
||||
.import-details {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.import-details table {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.import-details .notice-success {
|
||||
background-color: #f0f9eb;
|
||||
}
|
||||
|
||||
.import-details .notice-error {
|
||||
background-color: #fef0f0;
|
||||
}
|
||||
|
||||
.import-details .notice-warning {
|
||||
background-color: #fff8e5;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media screen and (max-width: 782px) {
|
||||
.search-form {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.search-form-field {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.form-table th {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.form-table td {
|
||||
display: block;
|
||||
padding: 5px 0 15px;
|
||||
padding: 0 0 15px;
|
||||
}
|
||||
.wp-list-table th.column-primary ~ th,
|
||||
.wp-list-table td.column-primary ~ td {
|
||||
|
||||
.domain-mapping-tabs {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.domain-mapping-tab {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.domain-mapping-tab.active {
|
||||
border-bottom: 1px solid #007cba;
|
||||
border-left: 4px solid #007cba;
|
||||
}
|
||||
|
||||
/* Hide less important columns on mobile */
|
||||
.wp-list-table th.column-primary ~ th:not(.column-actions),
|
||||
.wp-list-table td.column-primary ~ td:not(.column-actions) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wp-list-table th.column-primary,
|
||||
.wp-list-table td.column-primary {
|
||||
.wp-list-table td.column-primary,
|
||||
.wp-list-table th.column-actions,
|
||||
.wp-list-table td.column-actions {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,60 +2,72 @@
|
|||
* WP Domain Mapping admin JavaScript
|
||||
*/
|
||||
jQuery(document).ready(function($) {
|
||||
// Tab switching
|
||||
$('.styles-tab').on('click', function() {
|
||||
$('.styles-tab').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
var tab = $(this).data('tab');
|
||||
$('.styles-section').hide();
|
||||
$('.styles-section[data-section="' + tab + '"]').show();
|
||||
});
|
||||
|
||||
// Helper function for showing notices
|
||||
function showNotice(selector, message, type) {
|
||||
$(selector)
|
||||
.removeClass('notice-success notice-error notice-warning notice-info')
|
||||
.addClass('notice-' + type)
|
||||
.html('<p>' + message + '</p>')
|
||||
.show()
|
||||
.delay(5000)
|
||||
.fadeOut();
|
||||
}
|
||||
|
||||
// Domain form validation
|
||||
$('#edit-domain-form').on('submit', function(e) {
|
||||
var domain = $('#domain').val();
|
||||
var blogId = $('#blog_id').val();
|
||||
|
||||
var domain = $('#domain').val().trim();
|
||||
var blogId = $('#blog_id').val().trim();
|
||||
|
||||
if (!domain) {
|
||||
e.preventDefault();
|
||||
alert(wpDomainMapping.messages.domainRequired);
|
||||
showNotice('#edit-domain-status', wpDomainMapping.messages.domainRequired, 'error');
|
||||
$('#domain').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!blogId) {
|
||||
e.preventDefault();
|
||||
alert(wpDomainMapping.messages.siteRequired);
|
||||
showNotice('#edit-domain-status', wpDomainMapping.messages.siteRequired, 'error');
|
||||
$('#blog_id').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Basic domain format validation
|
||||
var domainPattern = /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\.[a-zA-Z]{2,}$/;
|
||||
if (!domainPattern.test(domain)) {
|
||||
e.preventDefault();
|
||||
showNotice('#edit-domain-status', 'Please enter a valid domain format (e.g., example.com)', 'error');
|
||||
$('#domain').focus();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Check all domains
|
||||
// Check all domains functionality
|
||||
$('#select-all').on('change', function() {
|
||||
$('.domain-checkbox').prop('checked', this.checked);
|
||||
});
|
||||
|
||||
// AJAX domain operations
|
||||
function showNotice(selector, message, type) {
|
||||
$(selector).removeClass('notice-success notice-error')
|
||||
.addClass('notice-' + type)
|
||||
.html('<p>' + message + '</p>')
|
||||
.show()
|
||||
.delay(3000)
|
||||
.fadeOut();
|
||||
}
|
||||
// Update select all when individual checkboxes change
|
||||
$('.domain-checkbox').on('change', function() {
|
||||
if (!this.checked) {
|
||||
$('#select-all').prop('checked', false);
|
||||
} else if ($('.domain-checkbox:checked').length === $('.domain-checkbox').length) {
|
||||
$('#select-all').prop('checked', true);
|
||||
}
|
||||
});
|
||||
|
||||
// Save domain
|
||||
// Handle domain edit/add form submission via AJAX
|
||||
$('#edit-domain-form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var formData = $(this).serializeArray();
|
||||
formData.push({name: 'action', value: 'dm_handle_actions'});
|
||||
formData.push({name: 'action_type', value: 'save'});
|
||||
formData.push({name: 'nonce', value: wpDomainMapping.nonce});
|
||||
|
||||
$('#edit-domain-status').text(wpDomainMapping.messages.saving).show();
|
||||
|
||||
$('#edit-domain-status').html('<p>' + wpDomainMapping.messages.saving + '</p>').show();
|
||||
|
||||
$.ajax({
|
||||
url: wpDomainMapping.ajaxUrl,
|
||||
type: 'POST',
|
||||
|
@ -63,7 +75,12 @@ jQuery(document).ready(function($) {
|
|||
success: function(response) {
|
||||
if (response.success) {
|
||||
showNotice('#edit-domain-status', response.data, 'success');
|
||||
setTimeout(function() { location.reload(); }, 1000);
|
||||
setTimeout(function() {
|
||||
// Redirect to domains page without edit parameter
|
||||
var url = new URL(window.location);
|
||||
url.searchParams.delete('edit_domain');
|
||||
window.location.href = url.toString();
|
||||
}, 1500);
|
||||
} else {
|
||||
showNotice('#edit-domain-status', response.data || wpDomainMapping.messages.error, 'error');
|
||||
}
|
||||
|
@ -74,61 +91,498 @@ jQuery(document).ready(function($) {
|
|||
});
|
||||
});
|
||||
|
||||
// Bulk actions
|
||||
// Handle bulk domain actions
|
||||
$('#domain-list-form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var selectedDomains = [];
|
||||
$('.domain-checkbox:checked').each(function() {
|
||||
selectedDomains.push($(this).val());
|
||||
});
|
||||
|
||||
|
||||
if (selectedDomains.length === 0) {
|
||||
showNotice('#domain-status', wpDomainMapping.messages.noSelection, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
var action = $('#bulk-action-selector-top').val();
|
||||
if (action === '-1') return;
|
||||
if (action === '-1') {
|
||||
showNotice('#domain-status', 'Please select an action.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirm('Are you sure you want to delete the selected domains?')) {
|
||||
$('#domain-status').text(wpDomainMapping.messages.processing).show();
|
||||
|
||||
$.ajax({
|
||||
url: wpDomainMapping.ajaxUrl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'dm_handle_actions',
|
||||
action_type: 'delete',
|
||||
domains: selectedDomains,
|
||||
nonce: wpDomainMapping.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showNotice('#domain-status', response.data, 'success');
|
||||
setTimeout(function() { location.reload(); }, 1000);
|
||||
} else {
|
||||
showNotice('#domain-status', response.data || wpDomainMapping.messages.error, 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showNotice('#domain-status', wpDomainMapping.messages.error, 'error');
|
||||
if (action === 'delete' && !confirm('Are you sure you want to delete the selected domains? This action cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$('#domain-status').html('<p>' + wpDomainMapping.messages.processing + '</p>').show();
|
||||
|
||||
$.ajax({
|
||||
url: wpDomainMapping.ajaxUrl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'dm_handle_actions',
|
||||
action_type: action,
|
||||
domains: selectedDomains,
|
||||
nonce: wpDomainMapping.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showNotice('#domain-status', response.data, 'success');
|
||||
setTimeout(function() {
|
||||
location.reload();
|
||||
}, 1500);
|
||||
} else {
|
||||
showNotice('#domain-status', response.data || wpDomainMapping.messages.error, 'error');
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function() {
|
||||
showNotice('#domain-status', wpDomainMapping.messages.error, 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Single domain delete functionality
|
||||
$('.domain-delete-button').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!confirm('Are you sure you want to delete this domain? This action cannot be undone.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var domain = $(this).data('domain');
|
||||
var $row = $(this).closest('tr');
|
||||
|
||||
$.ajax({
|
||||
url: wpDomainMapping.ajaxUrl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'dm_handle_actions',
|
||||
action_type: 'delete',
|
||||
domain: domain,
|
||||
nonce: wpDomainMapping.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
$row.fadeOut(function() {
|
||||
$(this).remove();
|
||||
});
|
||||
showNotice('#domain-status', response.data, 'success');
|
||||
} else {
|
||||
showNotice('#domain-status', response.data || wpDomainMapping.messages.error, 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showNotice('#domain-status', wpDomainMapping.messages.error, 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Tab switching functionality (for admin settings page)
|
||||
$('.domain-mapping-tab').on('click', function() {
|
||||
var tab = $(this).data('tab');
|
||||
|
||||
// Update active tab
|
||||
$('.domain-mapping-tab').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
|
||||
// Show corresponding content
|
||||
$('.domain-mapping-section').hide();
|
||||
$('.domain-mapping-section[data-section="' + tab + '"]').show();
|
||||
|
||||
// Update URL without reloading
|
||||
if (history.pushState) {
|
||||
var url = new URL(window.location);
|
||||
url.searchParams.set('tab', tab);
|
||||
window.history.pushState({}, '', url);
|
||||
}
|
||||
});
|
||||
|
||||
// Copy to clipboard functionality
|
||||
$('.copy-to-clipboard').on('click', function() {
|
||||
var text = $(this).data('text');
|
||||
// Auto-fill server IP if detected
|
||||
if ($('#ipaddress').length && !$('#ipaddress').val()) {
|
||||
// This would be populated server-side, but we can enhance it client-side if needed
|
||||
}
|
||||
|
||||
// Domain input formatting - remove protocols and trailing slashes
|
||||
$('#domain').on('blur', function() {
|
||||
var domain = $(this).val().trim();
|
||||
// Remove http:// or https://
|
||||
domain = domain.replace(/^https?:\/\//, '');
|
||||
// Remove trailing slash
|
||||
domain = domain.replace(/\/$/, '');
|
||||
// Convert to lowercase
|
||||
domain = domain.toLowerCase();
|
||||
$(this).val(domain);
|
||||
});
|
||||
|
||||
// Copy to clipboard functionality for DNS instructions
|
||||
$('.copy-to-clipboard').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var text = $(this).data('text') || $(this).prev('code').text();
|
||||
|
||||
// Create temporary input
|
||||
var tempInput = $('<input>');
|
||||
$('body').append(tempInput);
|
||||
tempInput.val(text).select();
|
||||
document.execCommand('copy');
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
var $btn = $(this);
|
||||
var originalText = $btn.text();
|
||||
$btn.text('Copied!').addClass('copied');
|
||||
|
||||
setTimeout(function() {
|
||||
$btn.text(originalText).removeClass('copied');
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.log('Copy failed');
|
||||
}
|
||||
|
||||
tempInput.remove();
|
||||
|
||||
var $btn = $(this);
|
||||
var originalText = $btn.text();
|
||||
$btn.text('Copied!');
|
||||
setTimeout(function() { $btn.text(originalText); }, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
// Form validation enhancement
|
||||
$('form').on('submit', function() {
|
||||
var $submitBtn = $(this).find('input[type="submit"], button[type="submit"]');
|
||||
var originalText = $submitBtn.val() || $submitBtn.text();
|
||||
|
||||
// Disable submit button to prevent double submission
|
||||
$submitBtn.prop('disabled', true);
|
||||
|
||||
// Re-enable after a delay
|
||||
setTimeout(function() {
|
||||
$submitBtn.prop('disabled', false);
|
||||
}, 3000);
|
||||
});
|
||||
|
||||
// Enhanced table row highlighting
|
||||
$('.wp-list-table tbody tr').hover(
|
||||
function() {
|
||||
$(this).addClass('hover');
|
||||
},
|
||||
function() {
|
||||
$(this).removeClass('hover');
|
||||
}
|
||||
);
|
||||
|
||||
// Auto-hide notices after delay
|
||||
$('.notice.is-dismissible').delay(8000).fadeOut();
|
||||
|
||||
// Confirmation for destructive actions
|
||||
$('a[href*="action=delete"], .delete a').on('click', function(e) {
|
||||
if (!confirm('Are you sure you want to delete this item? This action cannot be undone.')) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Enhanced search functionality
|
||||
$('#domain-filter-form input[type="text"]').on('keypress', function(e) {
|
||||
if (e.which === 13) { // Enter key
|
||||
$(this).closest('form').submit();
|
||||
}
|
||||
});
|
||||
|
||||
// Tooltips for status indicators
|
||||
if ($.fn.tooltip) {
|
||||
$('.dashicons[title]').tooltip();
|
||||
}
|
||||
|
||||
// Progress indicator for long-running operations
|
||||
function showProgress(message) {
|
||||
var $progress = $('<div class="progress-indicator">' +
|
||||
'<p>' + message + '</p>' +
|
||||
'<div class="progress-bar">' +
|
||||
'<div class="progress-fill"></div>' +
|
||||
'</div>' +
|
||||
'</div>');
|
||||
|
||||
$('body').append($progress);
|
||||
return $progress;
|
||||
}
|
||||
|
||||
function hideProgress($progress) {
|
||||
if ($progress) {
|
||||
$progress.fadeOut(function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced error handling
|
||||
$(document).ajaxError(function(event, xhr, settings, thrownError) {
|
||||
if (xhr.status === 403) {
|
||||
showNotice('.wrap', 'Permission denied. Please refresh the page and try again.', 'error');
|
||||
} else if (xhr.status === 500) {
|
||||
showNotice('.wrap', 'Server error occurred. Please try again later.', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize any existing tab from URL
|
||||
if (typeof URLSearchParams !== 'undefined') {
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
var currentTab = urlParams.get('tab');
|
||||
if (currentTab && $('.domain-mapping-tab[data-tab="' + currentTab + '"]').length) {
|
||||
$('.domain-mapping-tab[data-tab="' + currentTab + '"]').click();
|
||||
}
|
||||
}
|
||||
|
||||
// Smooth scrolling for anchor links
|
||||
$('a[href^="#"]').on('click', function(e) {
|
||||
var target = $(this.getAttribute('href'));
|
||||
if (target.length) {
|
||||
e.preventDefault();
|
||||
$('html, body').stop().animate({
|
||||
scrollTop: target.offset().top - 100
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-refresh functionality for health checks
|
||||
var autoRefreshInterval;
|
||||
|
||||
function startAutoRefresh() {
|
||||
if (autoRefreshInterval) {
|
||||
clearInterval(autoRefreshInterval);
|
||||
}
|
||||
|
||||
// Only auto-refresh on health tab
|
||||
if ($('.domain-mapping-section[data-section="health"]').is(':visible')) {
|
||||
autoRefreshInterval = setInterval(function() {
|
||||
// Check if any health checks are running
|
||||
if ($('.check-domain-health:disabled').length === 0) {
|
||||
// Optionally auto-refresh health status every 5 minutes
|
||||
// location.reload();
|
||||
}
|
||||
}, 300000); // 5 minutes
|
||||
}
|
||||
}
|
||||
|
||||
function stopAutoRefresh() {
|
||||
if (autoRefreshInterval) {
|
||||
clearInterval(autoRefreshInterval);
|
||||
}
|
||||
}
|
||||
|
||||
// Start auto-refresh when health tab is active
|
||||
$('.domain-mapping-tab[data-tab="health"]').on('click', startAutoRefresh);
|
||||
|
||||
// Stop auto-refresh when leaving health tab
|
||||
$('.domain-mapping-tab:not([data-tab="health"])').on('click', stopAutoRefresh);
|
||||
|
||||
// Initialize auto-refresh if health tab is already active
|
||||
if ($('.domain-mapping-tab[data-tab="health"]').hasClass('active')) {
|
||||
startAutoRefresh();
|
||||
}
|
||||
|
||||
// Clean up on page unload
|
||||
$(window).on('beforeunload', function() {
|
||||
stopAutoRefresh();
|
||||
});
|
||||
|
||||
// Domain tabs switching
|
||||
$('.domain-mapping-tab').on('click', function() {
|
||||
$('.domain-mapping-tab').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
|
||||
var tab = $(this).data('tab');
|
||||
$('.domain-mapping-section').hide();
|
||||
$('.domain-mapping-section[data-section="' + tab + '"]').show();
|
||||
});
|
||||
|
||||
// Health check single domain
|
||||
$(document).on('click', '.check-domain-health', function() {
|
||||
var $button = $(this);
|
||||
var domain = $button.data('domain');
|
||||
var originalText = $button.text();
|
||||
|
||||
$button.prop('disabled', true).text('Checking...');
|
||||
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'dm_check_domain_health',
|
||||
domain: domain,
|
||||
nonce: $('input[name="dm_manual_health_check_nonce"]').val() || wpDomainMapping.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
// Refresh page to show updated results
|
||||
location.reload();
|
||||
} else {
|
||||
alert(response.data || 'An error occurred during the health check.');
|
||||
$button.prop('disabled', false).text(originalText);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert('An error occurred during the health check.');
|
||||
$button.prop('disabled', false).text(originalText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Import form handling
|
||||
$('#domain-mapping-import-form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var formData = new FormData(this);
|
||||
formData.append('action', 'dm_import_csv');
|
||||
|
||||
// Show progress bar
|
||||
$('#import-progress').show();
|
||||
$('#import-results').hide();
|
||||
|
||||
$.ajax({
|
||||
url: ajaxurl,
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
dataType: 'json',
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: function(response) {
|
||||
$('#import-progress').hide();
|
||||
$('#import-results').show();
|
||||
|
||||
if (response.success) {
|
||||
$('.import-summary').html(
|
||||
'<div class="notice notice-success"><p>' +
|
||||
response.data.message +
|
||||
'</p></div>'
|
||||
);
|
||||
|
||||
if (response.data.details && response.data.details.length > 0) {
|
||||
var details = '<table class="widefat striped">' +
|
||||
'<thead><tr>' +
|
||||
'<th>Status</th>' +
|
||||
'<th>Details</th>' +
|
||||
'</tr></thead><tbody>';
|
||||
|
||||
$.each(response.data.details, function(i, item) {
|
||||
var statusClass = '';
|
||||
if (item.status === 'error') {
|
||||
statusClass = 'style="background-color: #fef0f0;"';
|
||||
} else if (item.status === 'warning') {
|
||||
statusClass = 'style="background-color: #fff8e5;"';
|
||||
} else if (item.status === 'success') {
|
||||
statusClass = 'style="background-color: #f0f9eb;"';
|
||||
}
|
||||
|
||||
details += '<tr ' + statusClass + '>' +
|
||||
'<td><strong>' + item.status.toUpperCase() + '</strong></td>' +
|
||||
'<td>' + item.message + '</td>' +
|
||||
'</tr>';
|
||||
});
|
||||
|
||||
details += '</tbody></table>';
|
||||
$('.import-details').html(details);
|
||||
}
|
||||
} else {
|
||||
$('.import-summary').html(
|
||||
'<div class="notice notice-error"><p>' +
|
||||
(response.data || 'Import failed.') +
|
||||
'</p></div>'
|
||||
);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$('#import-progress').hide();
|
||||
$('#import-results').show();
|
||||
$('.import-summary').html(
|
||||
'<div class="notice notice-error"><p>' +
|
||||
'An error occurred during import.' +
|
||||
'</p></div>'
|
||||
);
|
||||
},
|
||||
xhr: function() {
|
||||
var xhr = new window.XMLHttpRequest();
|
||||
|
||||
xhr.upload.addEventListener('progress', function(evt) {
|
||||
if (evt.lengthComputable) {
|
||||
var percentComplete = (evt.loaded / evt.total) * 100;
|
||||
$('.progress-bar-inner').css('width', percentComplete + '%');
|
||||
$('.progress-text').text(Math.round(percentComplete) + '%');
|
||||
}
|
||||
}, false);
|
||||
|
||||
return xhr;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Enhanced domain validation
|
||||
$('#domain').on('input', function() {
|
||||
var domain = $(this).val();
|
||||
var $feedback = $('#domain-feedback');
|
||||
|
||||
if (!$feedback.length) {
|
||||
$(this).after('<div id="domain-feedback" class="description"></div>');
|
||||
$feedback = $('#domain-feedback');
|
||||
}
|
||||
|
||||
if (domain.length > 0) {
|
||||
// Check for common issues
|
||||
if (domain.indexOf('http') === 0) {
|
||||
$feedback.html('<span style="color: #dc3232;">Please remove http:// or https://</span>').show();
|
||||
} else if (domain.indexOf('/') !== -1) {
|
||||
$feedback.html('<span style="color: #dc3232;">Please remove any paths or slashes</span>').show();
|
||||
} else if (!/^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\.[a-zA-Z]{2,}$/.test(domain)) {
|
||||
$feedback.html('<span style="color: #dc3232;">Please enter a valid domain format</span>').show();
|
||||
} else {
|
||||
$feedback.html('<span style="color: #46b450;">Domain format looks good</span>').show();
|
||||
}
|
||||
} else {
|
||||
$feedback.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard shortcuts
|
||||
$(document).on('keydown', function(e) {
|
||||
// Ctrl/Cmd + S to save forms
|
||||
if ((e.ctrlKey || e.metaKey) && e.which === 83) {
|
||||
var $activeForm = $('form:visible').first();
|
||||
if ($activeForm.length) {
|
||||
e.preventDefault();
|
||||
$activeForm.submit();
|
||||
}
|
||||
}
|
||||
|
||||
// Escape key to close modals or cancel actions
|
||||
if (e.which === 27) {
|
||||
$('.notice.is-dismissible').fadeOut();
|
||||
}
|
||||
});
|
||||
|
||||
// Real-time search for domain filter
|
||||
var searchTimeout;
|
||||
$('#domain-filter-form input[name="s"]').on('input', function() {
|
||||
clearTimeout(searchTimeout);
|
||||
var searchTerm = $(this).val();
|
||||
|
||||
searchTimeout = setTimeout(function() {
|
||||
if (searchTerm.length > 2 || searchTerm.length === 0) {
|
||||
// Auto-submit form after delay
|
||||
$('#domain-filter-form').submit();
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// Initialize page based on current state
|
||||
function initializePage() {
|
||||
// Check if we're on the domains page and have messages to show
|
||||
if (window.location.href.indexOf('page=domains') > -1) {
|
||||
if (window.location.href.indexOf('updated=add') > -1) {
|
||||
showNotice('#domain-status', 'Domain added successfully.', 'success');
|
||||
} else if (window.location.href.indexOf('updated=del') > -1) {
|
||||
showNotice('#domain-status', 'Domain deleted successfully.', 'success');
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-focus first input field
|
||||
$('input[type="text"]:visible:first').focus();
|
||||
}
|
||||
|
||||
// Initialize when document is ready
|
||||
initializePage();
|
||||
});
|
||||
|
|
740
includes/class-admin.php
Normal file
740
includes/class-admin.php
Normal file
|
@ -0,0 +1,740 @@
|
|||
<?php
|
||||
/**
|
||||
* Admin functionality for WP Domain Mapping plugin
|
||||
*
|
||||
* @package WP Domain Mapping
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP Domain Mapping Admin Class
|
||||
*/
|
||||
class WP_Domain_Mapping_Admin {
|
||||
|
||||
/**
|
||||
* Class instance
|
||||
*
|
||||
* @var WP_Domain_Mapping_Admin
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Database table names
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $tables = array();
|
||||
|
||||
/**
|
||||
* Get class instance
|
||||
*
|
||||
* @return WP_Domain_Mapping_Admin
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
$this->tables = dm_get_table_names();
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize admin functionality
|
||||
*/
|
||||
private function init() {
|
||||
// Setup admin-related hooks
|
||||
$this->setup_admin_hooks();
|
||||
|
||||
// Load admin pages
|
||||
require_once WP_DOMAIN_MAPPING_DIR_PATH . 'admin/pages.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup admin hooks
|
||||
*/
|
||||
private function setup_admin_hooks() {
|
||||
// Add menu pages
|
||||
add_action( 'admin_menu', array( $this, 'add_admin_pages' ) );
|
||||
add_action( 'network_admin_menu', array( $this, 'add_network_admin_pages' ) );
|
||||
|
||||
// Add AJAX handlers
|
||||
add_action( 'wp_ajax_dm_handle_actions', array( $this, 'ajax_handle_actions' ) );
|
||||
|
||||
// Add domain link to sites list
|
||||
add_filter( 'manage_sites_action_links', array( $this, 'add_domain_link_to_sites' ), 10, 2 );
|
||||
|
||||
// Add custom columns
|
||||
add_filter( 'wpmu_blogs_columns', array( $this, 'add_domain_mapping_columns' ) );
|
||||
add_action( 'manage_blogs_custom_column', array( $this, 'display_domain_mapping_column' ), 1, 3 );
|
||||
add_action( 'manage_sites_custom_column', array( $this, 'display_domain_mapping_column' ), 1, 3 );
|
||||
|
||||
// Handle user domain mapping actions
|
||||
if ( isset( $_GET['page'] ) && 'domainmapping' === $_GET['page'] ) {
|
||||
add_action( 'admin_init', array( $this, 'handle_user_domain_actions' ) );
|
||||
}
|
||||
|
||||
// Enqueue admin scripts and styles
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add admin menu pages
|
||||
*/
|
||||
public function add_admin_pages() {
|
||||
global $current_site, $wpdb, $wp_db_version;
|
||||
|
||||
if ( ! isset( $current_site ) && $wp_db_version >= 15260 ) {
|
||||
add_action( 'admin_notices', array( $this, 'domain_mapping_warning' ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( isset($current_site->path) && $current_site->path != "/" ) {
|
||||
wp_die( esc_html__( "The domain mapping plugin only works if the site is installed in /. This is a limitation of how virtual servers work and is very difficult to work around.", 'wp-domain-mapping' ) );
|
||||
}
|
||||
|
||||
if ( get_site_option( 'dm_user_settings' ) && $current_site->blog_id != $wpdb->blogid && ! $this->sunrise_warning( false ) ) {
|
||||
add_management_page(
|
||||
__( 'Domain Mapping', 'wp-domain-mapping' ),
|
||||
__( 'Domain Mapping', 'wp-domain-mapping' ),
|
||||
'manage_options',
|
||||
'domainmapping',
|
||||
array( $this, 'render_manage_page' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add network admin menu pages
|
||||
*/
|
||||
public function add_network_admin_pages() {
|
||||
add_submenu_page(
|
||||
'settings.php',
|
||||
__( 'Domain Mapping', 'wp-domain-mapping' ),
|
||||
__( 'Domain Mapping', 'wp-domain-mapping' ),
|
||||
'manage_network',
|
||||
'domain-mapping',
|
||||
array( $this, 'render_admin_page' )
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'sites.php',
|
||||
__( 'Domains', 'wp-domain-mapping' ),
|
||||
__( 'Domains', 'wp-domain-mapping' ),
|
||||
'manage_network',
|
||||
'domains',
|
||||
array( $this, 'render_domains_admin' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin scripts and styles
|
||||
*/
|
||||
public function enqueue_admin_assets( $hook ) {
|
||||
// Only load on our admin pages
|
||||
if ( ! in_array( $hook, array( 'sites_page_domains', 'settings_page_domain-mapping', 'tools_page_domainmapping' ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style(
|
||||
'wp-domain-mapping-admin',
|
||||
WP_DOMAIN_MAPPING_DIR_URL . 'assets/css/admin.css',
|
||||
array(),
|
||||
WP_DOMAIN_MAPPING_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'wp-domain-mapping-admin',
|
||||
WP_DOMAIN_MAPPING_DIR_URL . 'assets/js/admin.js',
|
||||
array( 'jquery' ),
|
||||
WP_DOMAIN_MAPPING_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script( 'wp-domain-mapping-admin', 'wpDomainMapping', array(
|
||||
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'domain_mapping' ),
|
||||
'messages' => array(
|
||||
'domainRequired' => __( 'Domain is required.', 'wp-domain-mapping' ),
|
||||
'siteRequired' => __( 'Site ID is required.', 'wp-domain-mapping' ),
|
||||
'saving' => __( 'Saving...', 'wp-domain-mapping' ),
|
||||
'processing' => __( 'Processing...', 'wp-domain-mapping' ),
|
||||
'error' => __( 'An error occurred.', 'wp-domain-mapping' ),
|
||||
'noSelection' => __( 'Please select at least one domain.', 'wp-domain-mapping' ),
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if sunrise.php is properly configured
|
||||
*
|
||||
* @param bool $die Whether to die with an error message
|
||||
* @return bool True if there's a problem, false if everything is okay
|
||||
*/
|
||||
public function sunrise_warning( $die = true ) {
|
||||
if ( ! file_exists( WP_CONTENT_DIR . '/sunrise.php' ) ) {
|
||||
if ( ! $die ) return true;
|
||||
if ( dm_is_site_admin() ) {
|
||||
wp_die(
|
||||
sprintf(
|
||||
/* translators: %1$s: Content directory, %2$s: WordPress install path */
|
||||
esc_html__( 'Please copy sunrise.php to %1$s/sunrise.php and ensure the SUNRISE definition is in %2$swp-config.php', 'wp-domain-mapping' ),
|
||||
esc_html( WP_CONTENT_DIR ),
|
||||
esc_html( ABSPATH )
|
||||
)
|
||||
);
|
||||
} else {
|
||||
wp_die( esc_html__( "This plugin has not been configured correctly yet.", 'wp-domain-mapping' ) );
|
||||
}
|
||||
} elseif ( ! defined( 'SUNRISE' ) ) {
|
||||
if ( ! $die ) return true;
|
||||
if ( dm_is_site_admin() ) {
|
||||
wp_die(
|
||||
sprintf(
|
||||
/* translators: %s: WordPress install path */
|
||||
esc_html__( 'Please uncomment the line <em>define( \'SUNRISE\', \'on\' );</em> or add it to your %swp-config.php', 'wp-domain-mapping' ),
|
||||
esc_html( ABSPATH )
|
||||
)
|
||||
);
|
||||
} else {
|
||||
wp_die( esc_html__( "This plugin has not been configured correctly yet.", 'wp-domain-mapping' ) );
|
||||
}
|
||||
} elseif ( ! defined( 'SUNRISE_LOADED' ) ) {
|
||||
if ( ! $die ) return true;
|
||||
if ( dm_is_site_admin() ) {
|
||||
wp_die(
|
||||
sprintf(
|
||||
/* translators: %s: WordPress install path */
|
||||
esc_html__( 'Please edit your %swp-config.php and move the line <em>define( \'SUNRISE\', \'on\' );</em> above the last require_once() in that file or make sure you updated sunrise.php.', 'wp-domain-mapping' ),
|
||||
esc_html( ABSPATH )
|
||||
)
|
||||
);
|
||||
} else {
|
||||
wp_die( esc_html__( "This plugin has not been configured correctly yet.", 'wp-domain-mapping' ) );
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display warning message for network configuration
|
||||
*/
|
||||
public function domain_mapping_warning() {
|
||||
echo '<div class="notice notice-error"><p><strong>' . esc_html__( 'Domain Mapping Disabled.', 'wp-domain-mapping' ) . '</strong> ' .
|
||||
sprintf(
|
||||
/* translators: %s: URL to WordPress network creation documentation */
|
||||
wp_kses(
|
||||
__( 'You must <a href="%1$s">create a network</a> for it to work.', 'wp-domain-mapping' ),
|
||||
array( 'a' => array( 'href' => array() ) )
|
||||
),
|
||||
'http://codex.wordpress.org/Create_A_Network'
|
||||
) . '</p></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for domain actions
|
||||
*/
|
||||
public function ajax_handle_actions() {
|
||||
check_ajax_referer( 'domain_mapping', 'nonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_network' ) ) {
|
||||
wp_send_json_error( __( 'Permission denied.', 'wp-domain-mapping' ) );
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$action = sanitize_text_field( $_POST['action_type'] );
|
||||
$domain = dm_clean_domain( sanitize_text_field( isset( $_POST['domain'] ) ? strtolower( $_POST['domain'] ) : '' ) );
|
||||
$blog_id = isset( $_POST['blog_id'] ) ? absint( $_POST['blog_id'] ) : 0;
|
||||
$active = isset( $_POST['active'] ) ? absint( $_POST['active'] ) : 0;
|
||||
$orig_domain = isset( $_POST['orig_domain'] ) ? dm_clean_domain( sanitize_text_field( $_POST['orig_domain'] ) ) : '';
|
||||
$current_user_id = get_current_user_id();
|
||||
|
||||
switch ( $action ) {
|
||||
case 'save':
|
||||
if ( $blog_id != 0 && $blog_id != 1 ) {
|
||||
// Validate domain format
|
||||
if ( ! dm_validate_domain( $domain ) ) {
|
||||
wp_send_json_error( __( 'Invalid domain format.', 'wp-domain-mapping' ) );
|
||||
}
|
||||
|
||||
// Check if domain exists for another blog
|
||||
$exists = $wpdb->get_var( $wpdb->prepare(
|
||||
"SELECT domain FROM {$this->tables['domains']} WHERE blog_id != %d AND domain = %s",
|
||||
$blog_id, $domain
|
||||
));
|
||||
|
||||
if ( null == $exists ) {
|
||||
$wpdb->query( 'START TRANSACTION' );
|
||||
|
||||
try {
|
||||
if ( empty( $orig_domain ) ) {
|
||||
// Insert new domain
|
||||
$success = $wpdb->insert(
|
||||
$this->tables['domains'],
|
||||
array(
|
||||
'blog_id' => $blog_id,
|
||||
'domain' => $domain,
|
||||
'active' => $active
|
||||
),
|
||||
array( '%d', '%s', '%d' )
|
||||
);
|
||||
|
||||
if ( $success ) {
|
||||
// Log the action
|
||||
dm_log_action( 'add', $domain, $blog_id, $current_user_id );
|
||||
|
||||
$wpdb->query( 'COMMIT' );
|
||||
wp_send_json_success( __( 'Domain added successfully.', 'wp-domain-mapping' ) );
|
||||
} else {
|
||||
$wpdb->query( 'ROLLBACK' );
|
||||
wp_send_json_error( __( 'Failed to add domain.', 'wp-domain-mapping' ) );
|
||||
}
|
||||
} else {
|
||||
// Update existing domain
|
||||
$success = $wpdb->update(
|
||||
$this->tables['domains'],
|
||||
array(
|
||||
'blog_id' => $blog_id,
|
||||
'domain' => $domain,
|
||||
'active' => $active
|
||||
),
|
||||
array( 'domain' => $orig_domain ),
|
||||
array( '%d', '%s', '%d' ),
|
||||
array( '%s' )
|
||||
);
|
||||
|
||||
if ( $success !== false ) {
|
||||
// Log the action
|
||||
dm_log_action( 'edit', $domain, $blog_id, $current_user_id );
|
||||
|
||||
$wpdb->query( 'COMMIT' );
|
||||
wp_send_json_success( __( 'Domain updated successfully.', 'wp-domain-mapping' ) );
|
||||
} else {
|
||||
$wpdb->query( 'ROLLBACK' );
|
||||
wp_send_json_error( __( 'No changes were made or update failed.', 'wp-domain-mapping' ) );
|
||||
}
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$wpdb->query( 'ROLLBACK' );
|
||||
wp_send_json_error( __( 'An error occurred while saving domain.', 'wp-domain-mapping' ) );
|
||||
}
|
||||
} else {
|
||||
wp_send_json_error( __( 'Domain already exists for another site.', 'wp-domain-mapping' ) );
|
||||
}
|
||||
} else {
|
||||
wp_send_json_error( __( 'Invalid site ID.', 'wp-domain-mapping' ) );
|
||||
}
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$domains = isset( $_POST['domains'] ) ? array_map( 'sanitize_text_field', (array) $_POST['domains'] ) : array( $domain );
|
||||
|
||||
$wpdb->query( 'START TRANSACTION' );
|
||||
$deleted = 0;
|
||||
|
||||
try {
|
||||
foreach ( $domains as $del_domain ) {
|
||||
if ( empty( $del_domain ) ) continue;
|
||||
|
||||
// Get blog_id before deletion for logging
|
||||
$affected_blog_id = $wpdb->get_var( $wpdb->prepare(
|
||||
"SELECT blog_id FROM {$this->tables['domains']} WHERE domain = %s",
|
||||
$del_domain
|
||||
));
|
||||
|
||||
if ( $affected_blog_id ) {
|
||||
// Delete the domain
|
||||
$result = $wpdb->delete(
|
||||
$this->tables['domains'],
|
||||
array( 'domain' => $del_domain ),
|
||||
array( '%s' )
|
||||
);
|
||||
|
||||
if ( $result ) {
|
||||
$deleted++;
|
||||
|
||||
// Log the action
|
||||
dm_log_action( 'delete', $del_domain, $affected_blog_id, $current_user_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $deleted > 0 ) {
|
||||
$wpdb->query( 'COMMIT' );
|
||||
$message = sprintf(
|
||||
_n(
|
||||
'Domain deleted successfully.',
|
||||
'%d domains deleted successfully.',
|
||||
$deleted,
|
||||
'wp-domain-mapping'
|
||||
),
|
||||
$deleted
|
||||
);
|
||||
wp_send_json_success( $message );
|
||||
} else {
|
||||
$wpdb->query( 'ROLLBACK' );
|
||||
wp_send_json_error( __( 'No domains were deleted.', 'wp-domain-mapping' ) );
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$wpdb->query( 'ROLLBACK' );
|
||||
wp_send_json_error( __( 'An error occurred while deleting domains.', 'wp-domain-mapping' ) );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
wp_send_json_error( __( 'Invalid action.', 'wp-domain-mapping' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle user domain mapping actions
|
||||
*/
|
||||
public function handle_user_domain_actions() {
|
||||
global $wpdb, $parent_file;
|
||||
|
||||
$url = add_query_arg( array( 'page' => 'domainmapping' ), admin_url( $parent_file ) );
|
||||
|
||||
if ( ! empty( $_POST['action'] ) ) {
|
||||
$domain = isset( $_POST['domain'] ) ? sanitize_text_field( $_POST['domain'] ) : '';
|
||||
|
||||
if ( empty( $domain ) ) {
|
||||
wp_die( esc_html__( "You must enter a domain", 'wp-domain-mapping' ) );
|
||||
}
|
||||
|
||||
check_admin_referer( 'domain_mapping' );
|
||||
|
||||
do_action( 'dm_handle_actions_init', $domain );
|
||||
|
||||
switch ( $_POST['action'] ) {
|
||||
case "add":
|
||||
do_action( 'dm_handle_actions_add', $domain );
|
||||
|
||||
// Validate domain format
|
||||
if ( ! dm_validate_domain( $domain ) ) {
|
||||
wp_die( esc_html__( "Invalid domain format", 'wp-domain-mapping' ) );
|
||||
}
|
||||
|
||||
// Check if domain already exists
|
||||
$domain_exists = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT blog_id FROM {$wpdb->blogs} WHERE domain = %s OR (SELECT blog_id FROM {$this->tables['domains']} WHERE domain = %s)",
|
||||
$domain, $domain
|
||||
));
|
||||
|
||||
if ( null == $domain_exists ) {
|
||||
// If primary, reset other domains to not primary
|
||||
if ( isset( $_POST['primary'] ) && $_POST['primary'] ) {
|
||||
$wpdb->update(
|
||||
$this->tables['domains'],
|
||||
array( 'active' => 0 ),
|
||||
array( 'blog_id' => $wpdb->blogid ),
|
||||
array( '%d' ),
|
||||
array( '%d' )
|
||||
);
|
||||
}
|
||||
|
||||
// Insert new domain
|
||||
$wpdb->insert(
|
||||
$this->tables['domains'],
|
||||
array(
|
||||
'blog_id' => $wpdb->blogid,
|
||||
'domain' => $domain,
|
||||
'active' => isset( $_POST['primary'] ) ? 1 : 0
|
||||
),
|
||||
array( '%d', '%s', '%d' )
|
||||
);
|
||||
|
||||
wp_redirect( add_query_arg( array( 'updated' => 'add' ), $url ) );
|
||||
exit;
|
||||
} else {
|
||||
wp_redirect( add_query_arg( array( 'updated' => 'exists' ), $url ) );
|
||||
exit;
|
||||
}
|
||||
break;
|
||||
|
||||
case "primary":
|
||||
do_action( 'dm_handle_actions_primary', $domain );
|
||||
|
||||
// Reset all domains to not primary
|
||||
$wpdb->update(
|
||||
$this->tables['domains'],
|
||||
array( 'active' => 0 ),
|
||||
array( 'blog_id' => $wpdb->blogid ),
|
||||
array( '%d' ),
|
||||
array( '%d' )
|
||||
);
|
||||
|
||||
// Check if domain is not the original domain
|
||||
$core = WP_Domain_Mapping_Core::get_instance();
|
||||
$orig_url = parse_url( $core->get_original_url( 'siteurl' ) );
|
||||
|
||||
if ( $domain != $orig_url['host'] ) {
|
||||
// Set the selected domain as primary
|
||||
$wpdb->update(
|
||||
$this->tables['domains'],
|
||||
array( 'active' => 1 ),
|
||||
array(
|
||||
'domain' => $domain,
|
||||
'blog_id' => $wpdb->blogid
|
||||
),
|
||||
array( '%d' ),
|
||||
array( '%s', '%d' )
|
||||
);
|
||||
}
|
||||
|
||||
wp_redirect( add_query_arg( array( 'updated' => 'primary' ), $url ) );
|
||||
exit;
|
||||
break;
|
||||
}
|
||||
} elseif ( isset( $_GET['action'] ) && $_GET['action'] == 'delete' ) {
|
||||
$domain = sanitize_text_field( $_GET['domain'] );
|
||||
|
||||
if ( empty( $domain ) ) {
|
||||
wp_die( esc_html__( "You must enter a domain", 'wp-domain-mapping' ) );
|
||||
}
|
||||
|
||||
check_admin_referer( "delete" . $_GET['domain'] );
|
||||
|
||||
do_action( 'dm_handle_actions_del', $domain );
|
||||
|
||||
// Delete the domain
|
||||
$wpdb->delete(
|
||||
$this->tables['domains'],
|
||||
array( 'domain' => $domain ),
|
||||
array( '%s' )
|
||||
);
|
||||
|
||||
wp_redirect( add_query_arg( array( 'updated' => 'del' ), $url ) );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render domains admin page
|
||||
*/
|
||||
public function render_domains_admin() {
|
||||
if ( ! dm_is_site_admin() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->sunrise_warning();
|
||||
|
||||
// Include the admin page template
|
||||
dm_render_domains_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render admin configuration page
|
||||
*/
|
||||
public function render_admin_page() {
|
||||
if ( ! dm_is_site_admin() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->sunrise_warning();
|
||||
|
||||
global $current_site;
|
||||
if ( isset($current_site->path) && $current_site->path != "/" ) {
|
||||
wp_die( sprintf(
|
||||
esc_html__( "<strong>Warning!</strong> This plugin will only work if WordPress is installed in the root directory of your webserver. It is currently installed in '%s'.", "wp-domain-mapping" ),
|
||||
esc_html( $current_site->path )
|
||||
));
|
||||
}
|
||||
|
||||
// Initialize options if needed
|
||||
if ( get_site_option( 'dm_remote_login', 'NA' ) == 'NA' ) {
|
||||
add_site_option( 'dm_remote_login', 1 );
|
||||
}
|
||||
|
||||
if ( get_site_option( 'dm_redirect_admin', 'NA' ) == 'NA' ) {
|
||||
add_site_option( 'dm_redirect_admin', 1 );
|
||||
}
|
||||
|
||||
if ( get_site_option( 'dm_user_settings', 'NA' ) == 'NA' ) {
|
||||
add_site_option( 'dm_user_settings', 1 );
|
||||
}
|
||||
|
||||
// Handle form submission
|
||||
if ( ! empty( $_POST['action'] ) && $_POST['action'] == 'update' ) {
|
||||
check_admin_referer( 'domain_mapping' );
|
||||
|
||||
// Validate and save IP addresses
|
||||
$ipok = true;
|
||||
$ipaddresses = explode( ',', sanitize_text_field( $_POST['ipaddress'] ) );
|
||||
|
||||
foreach ( $ipaddresses as $address ) {
|
||||
if ( ( $ip = trim( $address ) ) && ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
|
||||
$ipok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $ipok ) {
|
||||
update_site_option( 'dm_ipaddress', sanitize_text_field( $_POST['ipaddress'] ) );
|
||||
}
|
||||
|
||||
// Save remote login option
|
||||
if ( intval( $_POST['always_redirect_admin'] ) == 0 ) {
|
||||
$_POST['dm_remote_login'] = 0;
|
||||
}
|
||||
|
||||
update_site_option( 'dm_remote_login', intval( $_POST['dm_remote_login'] ) );
|
||||
|
||||
// Validate and save CNAME
|
||||
if ( ! preg_match( '/(--|\.\.)/', $_POST['cname'] ) && preg_match( '|^([a-zA-Z0-9-\.])+$|', $_POST['cname'] ) ) {
|
||||
update_site_option( 'dm_cname', sanitize_text_field( $_POST['cname'] ) );
|
||||
} else {
|
||||
update_site_option( 'dm_cname', '' );
|
||||
}
|
||||
|
||||
// Save other options
|
||||
update_site_option( 'dm_301_redirect', isset( $_POST['permanent_redirect'] ) ? intval( $_POST['permanent_redirect'] ) : 0 );
|
||||
update_site_option( 'dm_redirect_admin', isset( $_POST['always_redirect_admin'] ) ? intval( $_POST['always_redirect_admin'] ) : 0 );
|
||||
update_site_option( 'dm_user_settings', isset( $_POST['dm_user_settings'] ) ? intval( $_POST['dm_user_settings'] ) : 0 );
|
||||
update_site_option( 'dm_no_primary_domain', isset( $_POST['dm_no_primary_domain'] ) ? intval( $_POST['dm_no_primary_domain'] ) : 0 );
|
||||
|
||||
// Add settings saved notice
|
||||
add_settings_error(
|
||||
'dm_settings',
|
||||
'settings_updated',
|
||||
__( 'Settings saved.', 'wp-domain-mapping' ),
|
||||
'updated'
|
||||
);
|
||||
}
|
||||
|
||||
// Include the admin page template
|
||||
dm_render_admin_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render user management page
|
||||
*/
|
||||
public function render_manage_page() {
|
||||
global $wpdb, $parent_file;
|
||||
|
||||
if ( isset( $_GET['updated'] ) ) {
|
||||
do_action( 'dm_echo_updated_msg' );
|
||||
}
|
||||
|
||||
$this->sunrise_warning();
|
||||
|
||||
if ( ! get_site_option( 'dm_ipaddress' ) && ! get_site_option( 'dm_cname' ) ) {
|
||||
if ( dm_is_site_admin() ) {
|
||||
echo wp_kses(
|
||||
__( "Please set the IP address or CNAME of your server in the <a href='wpmu-admin.php?page=domain-mapping'>site admin page</a>.", 'wp-domain-mapping' ),
|
||||
array( 'a' => array( 'href' => array() ) )
|
||||
);
|
||||
} else {
|
||||
esc_html_e( "This plugin has not been configured correctly yet.", 'wp-domain-mapping' );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$protocol = is_ssl() ? 'https://' : 'http://';
|
||||
$domains = dm_get_domains_by_blog_id( $wpdb->blogid );
|
||||
|
||||
// Include the user page template
|
||||
dm_render_user_page( $protocol, $domains );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "Domains" link to sites list
|
||||
*
|
||||
* @param array $actions The row actions
|
||||
* @param int $blog_id The blog ID
|
||||
* @return array Modified actions
|
||||
*/
|
||||
public function add_domain_link_to_sites( $actions, $blog_id ) {
|
||||
$domains_url = add_query_arg(
|
||||
array( 'page' => 'domains', 'blog_id' => $blog_id ),
|
||||
network_admin_url( 'sites.php' )
|
||||
);
|
||||
|
||||
$actions['domains'] = '<a href="' . esc_url( $domains_url ) . '">' .
|
||||
esc_html__( 'Domains', 'wp-domain-mapping' ) . '</a>';
|
||||
|
||||
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 ) . '<br />';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 "<div class='notice notice-success'><p>" . esc_html( $msg ) . "</p></div>";
|
||||
}
|
||||
}
|
||||
|
||||
// Add default updated messages action
|
||||
add_action( 'dm_echo_updated_msg', array( WP_Domain_Mapping_Admin::class, 'echo_default_updated_msg' ) );
|
593
includes/class-core.php
Normal file
593
includes/class-core.php
Normal file
|
@ -0,0 +1,593 @@
|
|||
<?php
|
||||
/**
|
||||
* Core functionality for WP Domain Mapping plugin
|
||||
*
|
||||
* @package WP Domain Mapping
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WP Domain Mapping Core Class
|
||||
*/
|
||||
class WP_Domain_Mapping_Core {
|
||||
|
||||
/**
|
||||
* Class instance
|
||||
*
|
||||
* @var WP_Domain_Mapping_Core
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Database table names
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $tables = array();
|
||||
|
||||
/**
|
||||
* Get class instance
|
||||
*
|
||||
* @return WP_Domain_Mapping_Core
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
$this->tables = dm_get_table_names();
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize core functionality
|
||||
*/
|
||||
private function init() {
|
||||
// Setup general hooks
|
||||
$this->setup_general_hooks();
|
||||
|
||||
// Setup domain mapping filters if DOMAIN_MAPPING is defined
|
||||
if ( defined( 'DOMAIN_MAPPING' ) ) {
|
||||
$this->setup_domain_mapping_filters();
|
||||
} else {
|
||||
add_filter( 'admin_url', array( $this, 'domain_mapping_adminurl' ), 10, 3 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup general hooks
|
||||
*/
|
||||
private function setup_general_hooks() {
|
||||
// Delete domain mappings when a blog is deleted
|
||||
add_action( 'delete_blog', array( $this, 'delete_blog_domain_mapping' ), 1, 2 );
|
||||
|
||||
// Handle domain redirection
|
||||
add_action( 'template_redirect', array( $this, 'redirect_to_mapped_domain' ) );
|
||||
|
||||
// Handle admin area redirection
|
||||
add_action( 'admin_init', array( $this, 'redirect_admin' ) );
|
||||
|
||||
// Handle remote login
|
||||
if ( isset( $_GET['dm'] ) ) {
|
||||
add_action( 'template_redirect', array( $this, 'remote_login_js' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup domain mapping filters when DOMAIN_MAPPING is defined
|
||||
*/
|
||||
private function setup_domain_mapping_filters() {
|
||||
add_filter( 'plugins_url', array( $this, 'domain_mapping_plugins_uri' ), 1 );
|
||||
add_filter( 'theme_root_uri', array( $this, 'domain_mapping_themes_uri' ), 1 );
|
||||
add_filter( 'pre_option_siteurl', array( $this, 'domain_mapping_siteurl' ) );
|
||||
add_filter( 'pre_option_home', array( $this, 'domain_mapping_siteurl' ) );
|
||||
add_filter( 'the_content', array( $this, 'domain_mapping_post_content' ) );
|
||||
add_action( 'wp_head', array( $this, 'remote_login_js_loader' ) );
|
||||
add_action( 'login_head', array( $this, 'redirect_login_to_orig' ) );
|
||||
add_action( 'wp_logout', array( $this, 'remote_logout_loader' ), 9999 );
|
||||
|
||||
add_filter( 'stylesheet_uri', array( $this, 'domain_mapping_post_content' ) );
|
||||
add_filter( 'stylesheet_directory', array( $this, 'domain_mapping_post_content' ) );
|
||||
add_filter( 'stylesheet_directory_uri', array( $this, 'domain_mapping_post_content' ) );
|
||||
add_filter( 'template_directory', array( $this, 'domain_mapping_post_content' ) );
|
||||
add_filter( 'template_directory_uri', array( $this, 'domain_mapping_post_content' ) );
|
||||
add_filter( 'plugins_url', array( $this, 'domain_mapping_post_content' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create required database tables
|
||||
*/
|
||||
public function create_tables() {
|
||||
global $wpdb;
|
||||
|
||||
// Only network admins can create tables
|
||||
if ( ! dm_is_site_admin() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize remote login hash
|
||||
$this->get_hash();
|
||||
|
||||
// Use static variable to prevent repeated execution
|
||||
static $tables_created = false;
|
||||
if ( $tables_created ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$created = 0;
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
// Create domain_mapping table
|
||||
if ( $wpdb->get_var( "SHOW TABLES LIKE '{$this->tables['domains']}'" ) != $this->tables['domains'] ) {
|
||||
$wpdb->query( "CREATE TABLE IF NOT EXISTS `{$this->tables['domains']}` (
|
||||
`id` bigint(20) NOT NULL auto_increment,
|
||||
`blog_id` bigint(20) NOT NULL,
|
||||
`domain` varchar(255) NOT NULL,
|
||||
`active` tinyint(4) default '1',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `blog_id` (`blog_id`,`domain`,`active`)
|
||||
) $charset_collate;" );
|
||||
$created = 1;
|
||||
}
|
||||
|
||||
// Create domain_mapping_logins table
|
||||
if ( $wpdb->get_var( "SHOW TABLES LIKE '{$this->tables['logins']}'" ) != $this->tables['logins'] ) {
|
||||
$wpdb->query( "CREATE TABLE IF NOT EXISTS `{$this->tables['logins']}` (
|
||||
`id` varchar(32) NOT NULL,
|
||||
`user_id` bigint(20) NOT NULL,
|
||||
`blog_id` bigint(20) NOT NULL,
|
||||
`t` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) $charset_collate;" );
|
||||
$created = 1;
|
||||
}
|
||||
|
||||
// Create domain_mapping_logs table
|
||||
if ( $wpdb->get_var( "SHOW TABLES LIKE '{$this->tables['logs']}'" ) != $this->tables['logs'] ) {
|
||||
$wpdb->query( "CREATE TABLE IF NOT EXISTS `{$this->tables['logs']}` (
|
||||
`id` bigint(20) NOT NULL auto_increment,
|
||||
`user_id` bigint(20) NOT NULL,
|
||||
`action` varchar(50) NOT NULL,
|
||||
`domain` varchar(255) NOT NULL,
|
||||
`blog_id` bigint(20) NOT NULL,
|
||||
`timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) $charset_collate;" );
|
||||
$created = 1;
|
||||
}
|
||||
|
||||
// If any table was created, show success message and mark as created
|
||||
if ( $created ) {
|
||||
add_action( 'admin_notices', function() {
|
||||
echo '<div class="notice notice-success"><p><strong>' .
|
||||
esc_html__( 'Domain mapping database tables created.', 'wp-domain-mapping' ) .
|
||||
'</strong></p></div>';
|
||||
});
|
||||
$tables_created = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete blog domain mappings when a blog is deleted
|
||||
*
|
||||
* @param int $blog_id The blog ID
|
||||
* @param bool $drop Whether to drop tables
|
||||
*/
|
||||
public function delete_blog_domain_mapping( $blog_id, $drop ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( $blog_id && $drop ) {
|
||||
$domains = $wpdb->get_col( $wpdb->prepare(
|
||||
"SELECT domain FROM {$this->tables['domains']} WHERE blog_id = %d",
|
||||
$blog_id
|
||||
));
|
||||
|
||||
do_action( 'dm_delete_blog_domain_mappings', $domains );
|
||||
|
||||
$wpdb->delete(
|
||||
$this->tables['domains'],
|
||||
array( 'blog_id' => $blog_id ),
|
||||
array( '%d' )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to mapped domain
|
||||
*/
|
||||
public function redirect_to_mapped_domain() {
|
||||
global $current_blog, $wpdb;
|
||||
|
||||
if ( is_main_site() || ( isset( $_GET['preview'] ) && $_GET['preview'] == 'true' ) ||
|
||||
( isset( $_POST['customize'] ) && isset( $_POST['theme'] ) && $_POST['customize'] == 'on' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$protocol = is_ssl() ? 'https://' : 'http://';
|
||||
$url = $this->domain_mapping_siteurl( false );
|
||||
|
||||
if ( $url && $url != untrailingslashit( $protocol . $current_blog->domain . $current_blog->path ) ) {
|
||||
$redirect = get_site_option( 'dm_301_redirect' ) ? '301' : '302';
|
||||
|
||||
if ( ( defined( 'VHOST' ) && constant("VHOST") != 'yes' ) ||
|
||||
( defined( 'SUBDOMAIN_INSTALL' ) && constant( 'SUBDOMAIN_INSTALL' ) == false ) ) {
|
||||
$_SERVER['REQUEST_URI'] = str_replace( $current_blog->path, '/', $_SERVER['REQUEST_URI'] );
|
||||
}
|
||||
|
||||
wp_redirect( $url . $_SERVER['REQUEST_URI'], $redirect );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect admin area if needed
|
||||
*/
|
||||
public function redirect_admin() {
|
||||
if ( strpos( $_SERVER['REQUEST_URI'], 'wp-admin/admin-ajax.php' ) !== false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( get_site_option( 'dm_redirect_admin' ) ) {
|
||||
$url = $this->get_original_url( 'siteurl' );
|
||||
|
||||
if ( false === strpos( $url, $_SERVER['HTTP_HOST'] ) ) {
|
||||
wp_redirect( untrailingslashit( $url ) . $_SERVER['REQUEST_URI'] );
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
global $current_blog;
|
||||
$url = $this->domain_mapping_siteurl( false );
|
||||
$request_uri = str_replace( $current_blog->path, '/', $_SERVER['REQUEST_URI'] );
|
||||
|
||||
if ( false === strpos( $url, $_SERVER['HTTP_HOST'] ) ) {
|
||||
wp_redirect( str_replace( '//wp-admin', '/wp-admin', trailingslashit( $url ) . $request_uri ) );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map domain for site URL
|
||||
*
|
||||
* @param string $setting The setting value
|
||||
* @return string The mapped URL
|
||||
*/
|
||||
public function domain_mapping_siteurl( $setting ) {
|
||||
global $wpdb, $current_blog;
|
||||
static $return_url = array();
|
||||
|
||||
// Check if already cached
|
||||
if ( isset( $return_url[$wpdb->blogid] ) ) {
|
||||
if ( $return_url[$wpdb->blogid] !== false ) {
|
||||
return $return_url[$wpdb->blogid];
|
||||
}
|
||||
return $setting;
|
||||
}
|
||||
|
||||
$s = $wpdb->suppress_errors();
|
||||
|
||||
// Handle primary domain checking based on settings
|
||||
if ( get_site_option( 'dm_no_primary_domain' ) == 1 ) {
|
||||
if ( isset( $_SERVER['HTTP_HOST'] ) ) {
|
||||
$domain = $wpdb->get_var( $wpdb->prepare(
|
||||
"SELECT domain FROM {$this->tables['domains']} WHERE blog_id = %d AND domain = %s LIMIT 1",
|
||||
$wpdb->blogid, $_SERVER['HTTP_HOST']
|
||||
));
|
||||
|
||||
if ( null == $domain ) {
|
||||
$return_url[$wpdb->blogid] = untrailingslashit( $this->get_original_url( "siteurl" ) );
|
||||
return $return_url[$wpdb->blogid];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$domain = $wpdb->get_var( $wpdb->prepare(
|
||||
"SELECT domain FROM {$this->tables['domains']} WHERE blog_id = %d AND active = 1 LIMIT 1",
|
||||
$wpdb->blogid
|
||||
));
|
||||
|
||||
if ( null == $domain ) {
|
||||
$return_url[$wpdb->blogid] = untrailingslashit( $this->get_original_url( "siteurl" ) );
|
||||
return $return_url[$wpdb->blogid];
|
||||
}
|
||||
}
|
||||
|
||||
$wpdb->suppress_errors( $s );
|
||||
|
||||
$protocol = is_ssl() ? 'https://' : 'http://';
|
||||
|
||||
if ( $domain ) {
|
||||
$return_url[$wpdb->blogid] = untrailingslashit( $protocol . $domain );
|
||||
$setting = $return_url[$wpdb->blogid];
|
||||
} else {
|
||||
$return_url[$wpdb->blogid] = false;
|
||||
}
|
||||
|
||||
return $setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original URL for a site
|
||||
*
|
||||
* @param string $url_type The URL type (siteurl or home)
|
||||
* @param int $blog_id The blog ID
|
||||
* @return string The original URL
|
||||
*/
|
||||
public function get_original_url( $url_type, $blog_id = 0 ) {
|
||||
global $wpdb;
|
||||
$id = $blog_id ?: $wpdb->blogid;
|
||||
static $orig_urls = array();
|
||||
|
||||
if ( ! isset( $orig_urls[$id] ) ) {
|
||||
// Remove filter to avoid infinite loop
|
||||
if ( defined( 'DOMAIN_MAPPING' ) ) {
|
||||
remove_filter( 'pre_option_' . $url_type, array( $this, 'domain_mapping_siteurl' ) );
|
||||
}
|
||||
|
||||
$orig_url = $blog_id == 0 ? get_option( $url_type ) : get_blog_option( $blog_id, $url_type );
|
||||
$orig_url = is_ssl() ? str_replace( "http://", "https://", $orig_url ) : str_replace( "https://", "http://", $orig_url );
|
||||
$orig_urls[$id] = $orig_url;
|
||||
|
||||
// Restore filter
|
||||
if ( defined( 'DOMAIN_MAPPING' ) ) {
|
||||
add_filter( 'pre_option_' . $url_type, array( $this, 'domain_mapping_siteurl' ) );
|
||||
}
|
||||
}
|
||||
|
||||
return $orig_urls[$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Map domain for admin URL
|
||||
*
|
||||
* @param string $url The URL
|
||||
* @param string $path The path
|
||||
* @param int $blog_id The blog ID
|
||||
* @return string The mapped URL
|
||||
*/
|
||||
public function domain_mapping_adminurl( $url, $path, $blog_id = 0 ) {
|
||||
$index = strpos( $url, '/wp-admin' );
|
||||
|
||||
if ( $index !== false ) {
|
||||
$url = $this->get_original_url( 'siteurl', $blog_id ) . substr( $url, $index );
|
||||
|
||||
if ( ( is_ssl() || force_ssl_admin() ) && 0 === strpos( $url, 'http://' ) ) {
|
||||
$url = 'https://' . substr( $url, 7 );
|
||||
}
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map domains in post content
|
||||
*
|
||||
* @param string $post_content The post content
|
||||
* @return string The mapped content
|
||||
*/
|
||||
public function domain_mapping_post_content( $post_content ) {
|
||||
$orig_url = $this->get_original_url( 'siteurl' );
|
||||
$url = $this->domain_mapping_siteurl( 'NA' );
|
||||
|
||||
if ( $url == 'NA' ) {
|
||||
return $post_content;
|
||||
}
|
||||
|
||||
return str_replace( $orig_url, $url, $post_content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Map domains in plugin URLs
|
||||
*
|
||||
* @param string $full_url The full URL
|
||||
* @param string $path The path
|
||||
* @param string $plugin The plugin
|
||||
* @return string The mapped URL
|
||||
*/
|
||||
public function domain_mapping_plugins_uri( $full_url, $path = null, $plugin = null ) {
|
||||
return get_option( 'siteurl' ) . substr( $full_url, stripos( $full_url, PLUGINDIR ) - 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Map domains in theme URLs
|
||||
*
|
||||
* @param string $full_url The full URL
|
||||
* @return string The mapped URL
|
||||
*/
|
||||
public function domain_mapping_themes_uri( $full_url ) {
|
||||
return str_replace( $this->get_original_url( 'siteurl' ), get_option( 'siteurl' ), $full_url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the domain mapping hash
|
||||
*
|
||||
* @return string The hash
|
||||
*/
|
||||
public function get_hash() {
|
||||
$remote_login_hash = get_site_option( 'dm_hash' );
|
||||
|
||||
if ( null == $remote_login_hash ) {
|
||||
$remote_login_hash = md5( time() );
|
||||
update_site_option( 'dm_hash', $remote_login_hash );
|
||||
}
|
||||
|
||||
return $remote_login_hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect login to original domain
|
||||
*/
|
||||
public function redirect_login_to_orig() {
|
||||
if ( ! get_site_option( 'dm_remote_login' ) ||
|
||||
( isset( $_GET['action'] ) && $_GET['action'] == 'logout' ) ||
|
||||
isset( $_GET['loggedout'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = $this->get_original_url( 'siteurl' );
|
||||
|
||||
if ( $url != site_url() ) {
|
||||
$url .= "/wp-login.php";
|
||||
echo "<script type='text/javascript'>\nwindow.location = '" . esc_url( $url ) . "'</script>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle logout across domains
|
||||
*/
|
||||
public function remote_logout_loader() {
|
||||
global $current_site, $current_blog, $wpdb;
|
||||
|
||||
$protocol = is_ssl() ? 'https://' : 'http://';
|
||||
$hash = $this->get_hash();
|
||||
$key = md5( time() );
|
||||
|
||||
$wpdb->insert(
|
||||
$this->tables['logins'],
|
||||
array(
|
||||
'id' => $key,
|
||||
'user_id' => 0,
|
||||
'blog_id' => $current_blog->blog_id,
|
||||
't' => current_time( 'mysql' )
|
||||
),
|
||||
array( '%s', '%d', '%d', '%s' )
|
||||
);
|
||||
|
||||
if ( get_site_option( 'dm_redirect_admin' ) ) {
|
||||
wp_redirect( $protocol . $current_site->domain . $current_site->path .
|
||||
"?dm={$hash}&action=logout&blogid={$current_blog->blog_id}&k={$key}&t=" . mt_rand() );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle remote login JS
|
||||
*/
|
||||
public function remote_login_js() {
|
||||
global $current_blog, $current_user, $wpdb;
|
||||
|
||||
if ( 0 == get_site_option( 'dm_remote_login' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hash = $this->get_hash();
|
||||
$protocol = is_ssl() ? 'https://' : 'http://';
|
||||
|
||||
if ( $_GET['dm'] == $hash ) {
|
||||
if ( $_GET['action'] == 'load' ) {
|
||||
if ( ! is_user_logged_in() ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$key = md5( time() . mt_rand() );
|
||||
|
||||
$wpdb->insert(
|
||||
$this->tables['logins'],
|
||||
array(
|
||||
'id' => $key,
|
||||
'user_id' => $current_user->ID,
|
||||
'blog_id' => $_GET['blogid'],
|
||||
't' => current_time( 'mysql' )
|
||||
),
|
||||
array( '%s', '%d', '%d', '%s' )
|
||||
);
|
||||
|
||||
$url = add_query_arg(
|
||||
array(
|
||||
'action' => 'login',
|
||||
'dm' => $hash,
|
||||
'k' => $key,
|
||||
't' => mt_rand()
|
||||
),
|
||||
$_GET['back']
|
||||
);
|
||||
|
||||
echo "window.location = '" . esc_url( $url ) . "'";
|
||||
exit;
|
||||
|
||||
} elseif ( $_GET['action'] == 'login' ) {
|
||||
$details = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM {$this->tables['logins']} WHERE id = %s AND blog_id = %d",
|
||||
$_GET['k'], $wpdb->blogid
|
||||
));
|
||||
|
||||
if ( $details ) {
|
||||
if ( $details->blog_id == $wpdb->blogid ) {
|
||||
$wpdb->delete(
|
||||
$this->tables['logins'],
|
||||
array( 'id' => $_GET['k'] ),
|
||||
array( '%s' )
|
||||
);
|
||||
|
||||
$wpdb->query( $wpdb->prepare(
|
||||
"DELETE FROM {$this->tables['logins']} WHERE t < %s",
|
||||
date( 'Y-m-d H:i:s', time() - 120 )
|
||||
));
|
||||
|
||||
wp_set_auth_cookie( $details->user_id );
|
||||
|
||||
wp_redirect( remove_query_arg(
|
||||
array( 'dm', 'action', 'k', 't' ),
|
||||
$protocol . $current_blog->domain . $_SERVER['REQUEST_URI']
|
||||
));
|
||||
exit;
|
||||
|
||||
} else {
|
||||
wp_die( esc_html__( "Incorrect or out of date login key", 'wp-domain-mapping' ) );
|
||||
}
|
||||
} else {
|
||||
wp_die( esc_html__( "Unknown login key", 'wp-domain-mapping' ) );
|
||||
}
|
||||
|
||||
} elseif ( $_GET['action'] == 'logout' ) {
|
||||
$details = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM {$this->tables['logins']} WHERE id = %s AND blog_id = %d",
|
||||
$_GET['k'], $_GET['blogid']
|
||||
));
|
||||
|
||||
if ( $details ) {
|
||||
$wpdb->delete(
|
||||
$this->tables['logins'],
|
||||
array( 'id' => $_GET['k'] ),
|
||||
array( '%s' )
|
||||
);
|
||||
|
||||
$blog = get_blog_details( $_GET['blogid'] );
|
||||
wp_clear_auth_cookie();
|
||||
wp_redirect( trailingslashit( $blog->siteurl ) . "wp-login.php?loggedout=true" );
|
||||
exit;
|
||||
} else {
|
||||
wp_die( esc_html__( "Unknown logout key", 'wp-domain-mapping' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add JS loader for remote login
|
||||
*/
|
||||
public function remote_login_js_loader() {
|
||||
global $current_site, $current_blog;
|
||||
|
||||
if ( 0 == get_site_option( 'dm_remote_login' ) || is_user_logged_in() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$protocol = is_ssl() ? 'https://' : 'http://';
|
||||
$hash = $this->get_hash();
|
||||
|
||||
echo "<script src='" . esc_url( $protocol . $current_site->domain . $current_site->path .
|
||||
"?dm={$hash}&action=load&blogid={$current_blog->blog_id}&siteid={$current_blog->site_id}&t=" .
|
||||
mt_rand() . "&back=" . urlencode( $protocol . $current_blog->domain . $_SERVER['REQUEST_URI'] ) ) .
|
||||
"' type='text/javascript'></script>";
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
341
includes/functions.php
Normal file
341
includes/functions.php
Normal file
|
@ -0,0 +1,341 @@
|
|||
<?php
|
||||
/**
|
||||
* Common functions for WP Domain Mapping plugin
|
||||
*
|
||||
* @package WP Domain Mapping
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a domain name
|
||||
*
|
||||
* @param string $domain The domain
|
||||
* @return bool True if valid
|
||||
*/
|
||||
function dm_validate_domain( $domain ) {
|
||||
// Basic validation
|
||||
return (bool) preg_match( '/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}$/i', $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 <a href="%s" target="_blank">punycode</a> format.', 'wp-domain-mapping' ),
|
||||
array( 'a' => array( 'href' => array(), 'target' => array() ) )
|
||||
),
|
||||
'https://www.punycoder.com/'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is a site admin
|
||||
*
|
||||
* @return bool True if user is a site admin
|
||||
*/
|
||||
function dm_is_site_admin() {
|
||||
return current_user_can( 'manage_network' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get domain mapping table names
|
||||
*
|
||||
* @return array Array of table names
|
||||
*/
|
||||
function dm_get_table_names() {
|
||||
global $wpdb;
|
||||
|
||||
return array(
|
||||
'domains' => $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_DOMAINS,
|
||||
'logins' => $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_LOGINS,
|
||||
'logs' => $wpdb->base_prefix . WP_DOMAIN_MAPPING_TABLE_LOGS,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log domain mapping action
|
||||
*
|
||||
* @param string $action Action type
|
||||
* @param string $domain Domain name
|
||||
* @param int $blog_id Blog ID
|
||||
* @param int $user_id User ID (optional)
|
||||
*/
|
||||
function dm_log_action( $action, $domain, $blog_id, $user_id = null ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( null === $user_id ) {
|
||||
$user_id = get_current_user_id();
|
||||
}
|
||||
|
||||
$tables = dm_get_table_names();
|
||||
|
||||
$wpdb->insert(
|
||||
$tables['logs'],
|
||||
array(
|
||||
'user_id' => $user_id,
|
||||
'action' => $action,
|
||||
'domain' => $domain,
|
||||
'blog_id' => $blog_id
|
||||
),
|
||||
array( '%d', '%s', '%s', '%d' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get domain by name
|
||||
*
|
||||
* @param string $domain Domain name
|
||||
* @return object|null Domain object or null
|
||||
*/
|
||||
function dm_get_domain_by_name( $domain ) {
|
||||
global $wpdb;
|
||||
|
||||
$tables = dm_get_table_names();
|
||||
|
||||
return $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM {$tables['domains']} WHERE domain = %s",
|
||||
$domain
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get domains by blog ID
|
||||
*
|
||||
* @param int $blog_id Blog ID
|
||||
* @return array Array of domain objects
|
||||
*/
|
||||
function dm_get_domains_by_blog_id( $blog_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$tables = dm_get_table_names();
|
||||
|
||||
return $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT * FROM {$tables['domains']} WHERE blog_id = %d ORDER BY active DESC, domain ASC",
|
||||
$blog_id
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new domain mapping
|
||||
*
|
||||
* @param int $blog_id Blog ID
|
||||
* @param string $domain Domain name
|
||||
* @param int $active Whether domain is primary (1) or not (0)
|
||||
* @return bool|int False on failure, insert ID on success
|
||||
*/
|
||||
function dm_add_domain( $blog_id, $domain, $active = 0 ) {
|
||||
global $wpdb;
|
||||
|
||||
$tables = dm_get_table_names();
|
||||
$domain = dm_clean_domain( $domain );
|
||||
|
||||
// Validate domain
|
||||
if ( ! dm_validate_domain( $domain ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if domain already exists
|
||||
if ( dm_get_domain_by_name( $domain ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If setting as primary, reset other domains
|
||||
if ( $active ) {
|
||||
$wpdb->update(
|
||||
$tables['domains'],
|
||||
array( 'active' => 0 ),
|
||||
array( 'blog_id' => $blog_id ),
|
||||
array( '%d' ),
|
||||
array( '%d' )
|
||||
);
|
||||
}
|
||||
|
||||
// Insert new domain
|
||||
$result = $wpdb->insert(
|
||||
$tables['domains'],
|
||||
array(
|
||||
'blog_id' => $blog_id,
|
||||
'domain' => $domain,
|
||||
'active' => $active
|
||||
),
|
||||
array( '%d', '%s', '%d' )
|
||||
);
|
||||
|
||||
if ( $result ) {
|
||||
dm_log_action( 'add', $domain, $blog_id );
|
||||
return $wpdb->insert_id;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update domain mapping
|
||||
*
|
||||
* @param string $domain Domain name
|
||||
* @param int $blog_id Blog ID
|
||||
* @param int $active Whether domain is primary (1) or not (0)
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
function dm_update_domain( $domain, $blog_id, $active ) {
|
||||
global $wpdb;
|
||||
|
||||
$tables = dm_get_table_names();
|
||||
|
||||
// If setting as primary, reset other domains
|
||||
if ( $active ) {
|
||||
$wpdb->update(
|
||||
$tables['domains'],
|
||||
array( 'active' => 0 ),
|
||||
array( 'blog_id' => $blog_id ),
|
||||
array( '%d' ),
|
||||
array( '%d' )
|
||||
);
|
||||
}
|
||||
|
||||
// Update domain
|
||||
$result = $wpdb->update(
|
||||
$tables['domains'],
|
||||
array( 'active' => $active ),
|
||||
array( 'domain' => $domain ),
|
||||
array( '%d' ),
|
||||
array( '%s' )
|
||||
);
|
||||
|
||||
if ( $result !== false ) {
|
||||
dm_log_action( 'edit', $domain, $blog_id );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete domain mapping
|
||||
*
|
||||
* @param string $domain Domain name
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
function dm_delete_domain( $domain ) {
|
||||
global $wpdb;
|
||||
|
||||
$tables = dm_get_table_names();
|
||||
|
||||
// Get domain info for logging
|
||||
$domain_info = dm_get_domain_by_name( $domain );
|
||||
|
||||
if ( ! $domain_info ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete domain
|
||||
$result = $wpdb->delete(
|
||||
$tables['domains'],
|
||||
array( 'domain' => $domain ),
|
||||
array( '%s' )
|
||||
);
|
||||
|
||||
if ( $result ) {
|
||||
dm_log_action( 'delete', $domain, $domain_info->blog_id );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save health check result
|
||||
*
|
||||
* @param string $domain Domain name
|
||||
* @param array $result Health check result
|
||||
*/
|
||||
function dm_save_health_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 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get health check result
|
||||
*
|
||||
* @param string $domain Domain name
|
||||
* @return array|null Health check result or null
|
||||
*/
|
||||
function dm_get_health_result( $domain ) {
|
||||
$health_results = get_site_option( 'dm_domain_health_results', array() );
|
||||
$domain_key = md5( $domain );
|
||||
|
||||
return isset( $health_results[$domain_key] ) ? $health_results[$domain_key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format action name for display
|
||||
*
|
||||
* @param string $action Action name
|
||||
* @return string Formatted action name
|
||||
*/
|
||||
function dm_format_action_name( $action ) {
|
||||
switch ( $action ) {
|
||||
case 'add':
|
||||
return __( 'Added', 'wp-domain-mapping' );
|
||||
case 'edit':
|
||||
return __( 'Updated', 'wp-domain-mapping' );
|
||||
case 'delete':
|
||||
return __( 'Deleted', 'wp-domain-mapping' );
|
||||
case 'import':
|
||||
return __( 'Imported', 'wp-domain-mapping' );
|
||||
default:
|
||||
return ucfirst( $action );
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue