tables = 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, ); // Initialize the plugin $this->init(); } /** * Initialize plugin */ private function init() { // Load text domain add_action( 'init', array( $this, 'load_textdomain' ) ); // Initialize plugin update checker $this->setup_updater(); // Setup admin-related hooks $this->setup_admin_hooks(); // Setup general hooks $this->setup_general_hooks(); // Check plugin requirements on activation register_activation_hook( __FILE__, array( $this, 'plugin_activation' ) ); } /** * Load plugin textdomain */ public function load_textdomain() { load_plugin_textdomain( 'wp-domain-mapping', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); } /** * Setup plugin updater */ private function setup_updater() { // Only include the updater if the PUC library exists if ( file_exists( plugin_dir_path( __FILE__ ) . 'lib/plugin-update-checker/plugin-update-checker.php' ) ) { require_once plugin_dir_path( __FILE__ ) . 'lib/plugin-update-checker/plugin-update-checker.php'; if ( class_exists( 'YahnisElsts\PluginUpdateChecker\v5p3\PucFactory' ) ) { \YahnisElsts\PluginUpdateChecker\v5p3\PucFactory::buildUpdateChecker( 'https://updates.weixiaoduo.com/wp-domain-mapping.json', __FILE__, 'wp-domain-mapping' ); } } } /** * Setup admin hooks */ private function setup_admin_hooks() { // Add menu pages add_action( 'admin_menu', array( $this, 'add_admin_pages' ) ); add_action( 'network_admin_menu', array( $this, 'add_network_admin_pages' ) ); // Add AJAX handlers add_action( 'wp_ajax_dm_handle_actions', array( $this, 'ajax_handle_actions' ) ); // Add domain link to sites list add_filter( 'manage_sites_action_links', array( $this, 'add_domain_link_to_sites' ), 10, 2 ); // Add custom columns add_filter( 'wpmu_blogs_columns', array( $this, 'add_domain_mapping_columns' ) ); add_action( 'manage_blogs_custom_column', array( $this, 'display_domain_mapping_column' ), 1, 3 ); add_action( 'manage_sites_custom_column', array( $this, 'display_domain_mapping_column' ), 1, 3 ); // Handle user domain mapping actions if ( isset( $_GET['page'] ) && 'domainmapping' === $_GET['page'] ) { add_action( 'admin_init', array( $this, 'handle_user_domain_actions' ) ); } } /** * Setup general hooks */ private function setup_general_hooks() { // Delete domain mappings when a blog is deleted add_action( 'delete_blog', array( $this, 'delete_blog_domain_mapping' ), 1, 2 ); // Handle domain redirection add_action( 'template_redirect', array( $this, 'redirect_to_mapped_domain' ) ); // Handle admin area redirection add_action( 'admin_init', array( $this, 'redirect_admin' ) ); // Setup domain mapping filters if DOMAIN_MAPPING is defined if ( defined( 'DOMAIN_MAPPING' ) ) { $this->setup_domain_mapping_filters(); } else { add_filter( 'admin_url', array( $this, 'domain_mapping_adminurl' ), 10, 3 ); } // Handle remote login if ( isset( $_GET['dm'] ) ) { add_action( 'template_redirect', array( $this, 'remote_login_js' ) ); } } /** * 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' ) ); } /** * Actions to perform on plugin activation */ public function plugin_activation() { // Create database tables $this->create_tables(); // Initialize remote login hash $this->get_hash(); } /** * Add admin menu pages */ public function add_admin_pages() { global $current_site, $wpdb, $wp_db_version; if ( ! isset( $current_site ) && $wp_db_version >= 15260 ) { add_action( 'admin_notices', array( $this, 'domain_mapping_warning' ) ); return false; } if ( isset($current_site->path) && $current_site->path != "/" ) { wp_die( esc_html__( "The domain mapping plugin only works if the site is installed in /. This is a limitation of how virtual servers work and is very difficult to work around.", 'wp-domain-mapping' ) ); } if ( get_site_option( 'dm_user_settings' ) && $current_site->blog_id != $wpdb->blogid && ! $this->sunrise_warning( false ) ) { add_management_page( __( 'Domain Mapping', 'wp-domain-mapping' ), __( 'Domain Mapping', 'wp-domain-mapping' ), 'manage_options', 'domainmapping', array( $this, 'render_manage_page' ) ); } } /** * Add network admin menu pages */ public function add_network_admin_pages() { add_submenu_page( 'settings.php', __( 'Domain Mapping', 'wp-domain-mapping' ), __( 'Domain Mapping', 'wp-domain-mapping' ), 'manage_network', 'domain-mapping', array( $this, 'render_admin_page' ) ); add_submenu_page( 'sites.php', __( 'Domains', 'wp-domain-mapping' ), __( 'Domains', 'wp-domain-mapping' ), 'manage_network', 'domains', array( $this, 'render_domains_admin' ) ); } /** * Check if sunrise.php is properly configured * * @param bool $die Whether to die with an error message * @return bool True if there's a problem, false if everything is okay */ public function sunrise_warning( $die = true ) { if ( ! file_exists( WP_CONTENT_DIR . '/sunrise.php' ) ) { if ( ! $die ) return true; if ( $this->is_site_admin() ) { wp_die( sprintf( /* translators: %1$s: Content directory, %2$s: WordPress install path */ esc_html__( 'Please copy sunrise.php to %1$s/sunrise.php and ensure the SUNRISE definition is in %2$swp-config.php', 'wp-domain-mapping' ), esc_html( WP_CONTENT_DIR ), esc_html( ABSPATH ) ) ); } else { wp_die( esc_html__( "This plugin has not been configured correctly yet.", 'wp-domain-mapping' ) ); } } elseif ( ! defined( 'SUNRISE' ) ) { if ( ! $die ) return true; if ( $this->is_site_admin() ) { wp_die( sprintf( /* translators: %s: WordPress install path */ esc_html__( 'Please uncomment the line define( \'SUNRISE\', \'on\' ); or add it to your %swp-config.php', 'wp-domain-mapping' ), esc_html( ABSPATH ) ) ); } else { wp_die( esc_html__( "This plugin has not been configured correctly yet.", 'wp-domain-mapping' ) ); } } elseif ( ! defined( 'SUNRISE_LOADED' ) ) { if ( ! $die ) return true; if ( $this->is_site_admin() ) { wp_die( sprintf( /* translators: %s: WordPress install path */ esc_html__( 'Please edit your %swp-config.php and move the line define( \'SUNRISE\', \'on\' ); above the last require_once() in that file or make sure you updated sunrise.php.', 'wp-domain-mapping' ), esc_html( ABSPATH ) ) ); } else { wp_die( esc_html__( "This plugin has not been configured correctly yet.", 'wp-domain-mapping' ) ); } } return false; } /** * Display warning message for network configuration */ public function domain_mapping_warning() { echo '

' . esc_html__( 'Domain Mapping Disabled.', 'wp-domain-mapping' ) . ' ' . sprintf( /* translators: %s: URL to WordPress network creation documentation */ wp_kses( __( 'You must create a network for it to work.', 'wp-domain-mapping' ), array( 'a' => array( 'href' => array() ) ) ), 'http://codex.wordpress.org/Create_A_Network' ) . '

'; } /** * Create required database tables */ public function create_tables() { global $wpdb; // Only network admins can create tables if ( ! $this->is_site_admin() ) { return; } // Initialize remote login hash $this->get_hash(); // Use static variable to prevent repeated execution static $tables_created = false; if ( $tables_created ) { return; } $created = 0; $charset_collate = $wpdb->get_charset_collate(); // Create domain_mapping table if ( $wpdb->get_var( "SHOW TABLES LIKE '{$this->tables['domains']}'" ) != $this->tables['domains'] ) { $wpdb->query( "CREATE TABLE IF NOT EXISTS `{$this->tables['domains']}` ( `id` bigint(20) NOT NULL auto_increment, `blog_id` bigint(20) NOT NULL, `domain` varchar(255) NOT NULL, `active` tinyint(4) default '1', PRIMARY KEY (`id`), KEY `blog_id` (`blog_id`,`domain`,`active`) ) $charset_collate;" ); $created = 1; } // Create domain_mapping_logins table if ( $wpdb->get_var( "SHOW TABLES LIKE '{$this->tables['logins']}'" ) != $this->tables['logins'] ) { $wpdb->query( "CREATE TABLE IF NOT EXISTS `{$this->tables['logins']}` ( `id` varchar(32) NOT NULL, `user_id` bigint(20) NOT NULL, `blog_id` bigint(20) NOT NULL, `t` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) $charset_collate;" ); $created = 1; } // Create domain_mapping_logs table if ( $wpdb->get_var( "SHOW TABLES LIKE '{$this->tables['logs']}'" ) != $this->tables['logs'] ) { $wpdb->query( "CREATE TABLE IF NOT EXISTS `{$this->tables['logs']}` ( `id` bigint(20) NOT NULL auto_increment, `user_id` bigint(20) NOT NULL, `action` varchar(50) NOT NULL, `domain` varchar(255) NOT NULL, `blog_id` bigint(20) NOT NULL, `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) $charset_collate;" ); $created = 1; } // If any table was created, show success message and mark as created if ( $created ) { add_action( 'admin_notices', function() { echo '

' . esc_html__( 'Domain mapping database tables created.', 'wp-domain-mapping' ) . '

'; }); $tables_created = true; } } /** * AJAX handler for domain actions */ public function ajax_handle_actions() { check_ajax_referer( 'domain_mapping', 'nonce' ); if ( ! current_user_can( 'manage_network' ) ) { wp_send_json_error( __( 'Permission denied.', 'wp-domain-mapping' ) ); } global $wpdb; $action = sanitize_text_field( $_POST['action_type'] ); $domain = $this->clean_domain( sanitize_text_field( isset( $_POST['domain'] ) ? strtolower( $_POST['domain'] ) : '' ) ); $blog_id = isset( $_POST['blog_id'] ) ? absint( $_POST['blog_id'] ) : 0; $active = isset( $_POST['active'] ) ? absint( $_POST['active'] ) : 0; $orig_domain = isset( $_POST['orig_domain'] ) ? $this->clean_domain( sanitize_text_field( $_POST['orig_domain'] ) ) : ''; $current_user_id = get_current_user_id(); switch ( $action ) { case 'save': if ( $blog_id != 0 && $blog_id != 1 ) { // Validate domain format if ( ! $this->is_valid_domain( $domain ) ) { wp_send_json_error( __( 'Invalid domain format.', 'wp-domain-mapping' ) ); } // Check if domain exists for another blog $exists = $wpdb->get_var( $wpdb->prepare( "SELECT domain FROM {$this->tables['domains']} WHERE blog_id != %d AND domain = %s", $blog_id, $domain )); if ( null == $exists ) { $wpdb->query( 'START TRANSACTION' ); try { if ( empty( $orig_domain ) ) { // Insert new domain $success = $wpdb->insert( $this->tables['domains'], array( 'blog_id' => $blog_id, 'domain' => $domain, 'active' => $active ), array( '%d', '%s', '%d' ) ); if ( $success ) { // Log the action $wpdb->insert( $this->tables['logs'], array( 'user_id' => $current_user_id, 'action' => 'add', 'domain' => $domain, 'blog_id' => $blog_id ), array( '%d', '%s', '%s', '%d' ) ); $wpdb->query( 'COMMIT' ); wp_send_json_success( __( 'Domain added successfully.', 'wp-domain-mapping' ) ); } else { $wpdb->query( 'ROLLBACK' ); wp_send_json_error( __( 'Failed to add domain.', 'wp-domain-mapping' ) ); } } else { // Update existing domain $success = $wpdb->update( $this->tables['domains'], array( 'blog_id' => $blog_id, 'domain' => $domain, 'active' => $active ), array( 'domain' => $orig_domain ), array( '%d', '%s', '%d' ), array( '%s' ) ); if ( $success !== false ) { // Log the action $wpdb->insert( $this->tables['logs'], array( 'user_id' => $current_user_id, 'action' => 'edit', 'domain' => $domain, 'blog_id' => $blog_id ), array( '%d', '%s', '%s', '%d' ) ); $wpdb->query( 'COMMIT' ); wp_send_json_success( __( 'Domain updated successfully.', 'wp-domain-mapping' ) ); } else { $wpdb->query( 'ROLLBACK' ); wp_send_json_error( __( 'No changes were made or update failed.', 'wp-domain-mapping' ) ); } } } catch ( Exception $e ) { $wpdb->query( 'ROLLBACK' ); wp_send_json_error( __( 'An error occurred while saving domain.', 'wp-domain-mapping' ) ); } } else { wp_send_json_error( __( 'Domain already exists for another site.', 'wp-domain-mapping' ) ); } } else { wp_send_json_error( __( 'Invalid site ID.', 'wp-domain-mapping' ) ); } break; 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 $wpdb->insert( $this->tables['logs'], array( 'user_id' => $current_user_id, 'action' => 'delete', 'domain' => $del_domain, 'blog_id' => $affected_blog_id ), array( '%d', '%s', '%s', '%d' ) ); } } } if ( $deleted > 0 ) { $wpdb->query( 'COMMIT' ); $message = sprintf( _n( 'Domain deleted successfully.', '%d domains deleted successfully.', $deleted, 'wp-domain-mapping' ), $deleted ); wp_send_json_success( $message ); } else { $wpdb->query( 'ROLLBACK' ); wp_send_json_error( __( 'No domains were deleted.', 'wp-domain-mapping' ) ); } } catch ( Exception $e ) { $wpdb->query( 'ROLLBACK' ); wp_send_json_error( __( 'An error occurred while deleting domains.', 'wp-domain-mapping' ) ); } break; default: wp_send_json_error( __( 'Invalid action.', 'wp-domain-mapping' ) ); } } /** * Handle user domain mapping actions */ public function handle_user_domain_actions() { global $wpdb, $parent_file; $url = add_query_arg( array( 'page' => 'domainmapping' ), admin_url( $parent_file ) ); if ( ! empty( $_POST['action'] ) ) { $domain = isset( $_POST['domain'] ) ? sanitize_text_field( $_POST['domain'] ) : ''; if ( empty( $domain ) ) { wp_die( esc_html__( "You must enter a domain", 'wp-domain-mapping' ) ); } check_admin_referer( 'domain_mapping' ); do_action( 'dm_handle_actions_init', $domain ); switch ( $_POST['action'] ) { case "add": do_action( 'dm_handle_actions_add', $domain ); // Validate domain format if ( ! $this->is_valid_domain( $domain ) ) { wp_die( esc_html__( "Invalid domain format", 'wp-domain-mapping' ) ); } // Check if domain already exists $domain_exists = $wpdb->get_row( $wpdb->prepare( "SELECT blog_id FROM {$wpdb->blogs} WHERE domain = %s OR (SELECT blog_id FROM {$this->tables['domains']} WHERE domain = %s)", $domain, $domain )); if ( null == $domain_exists ) { // If primary, reset other domains to not primary if ( isset( $_POST['primary'] ) && $_POST['primary'] ) { $wpdb->update( $this->tables['domains'], array( 'active' => 0 ), array( 'blog_id' => $wpdb->blogid ), array( '%d' ), array( '%d' ) ); } // Insert new domain $wpdb->insert( $this->tables['domains'], array( 'blog_id' => $wpdb->blogid, 'domain' => $domain, 'active' => isset( $_POST['primary'] ) ? 1 : 0 ), array( '%d', '%s', '%d' ) ); wp_redirect( add_query_arg( array( 'updated' => 'add' ), $url ) ); exit; } else { wp_redirect( add_query_arg( array( 'updated' => 'exists' ), $url ) ); exit; } break; case "primary": do_action( 'dm_handle_actions_primary', $domain ); // Reset all domains to not primary $wpdb->update( $this->tables['domains'], array( 'active' => 0 ), array( 'blog_id' => $wpdb->blogid ), array( '%d' ), array( '%d' ) ); // Check if domain is not the original domain $orig_url = parse_url( $this->get_original_url( 'siteurl' ) ); if ( $domain != $orig_url['host'] ) { // Set the selected domain as primary $wpdb->update( $this->tables['domains'], array( 'active' => 1 ), array( 'domain' => $domain, 'blog_id' => $wpdb->blogid ), array( '%d' ), array( '%s', '%d' ) ); } wp_redirect( add_query_arg( array( 'updated' => 'primary' ), $url ) ); exit; break; } } elseif ( isset( $_GET['action'] ) && $_GET['action'] == 'delete' ) { $domain = sanitize_text_field( $_GET['domain'] ); if ( empty( $domain ) ) { wp_die( esc_html__( "You must enter a domain", 'wp-domain-mapping' ) ); } check_admin_referer( "delete" . $_GET['domain'] ); do_action( 'dm_handle_actions_del', $domain ); // Delete the domain $wpdb->delete( $this->tables['domains'], array( 'domain' => $domain ), array( '%s' ) ); wp_redirect( add_query_arg( array( 'updated' => 'del' ), $url ) ); exit; } } /** * Render domains admin page */ public function render_domains_admin() { global $wpdb, $current_site; if ( ! $this->is_site_admin() ) { return false; } $this->sunrise_warning(); $this->create_tables(); if ( isset($current_site->path) && $current_site->path != "/" ) { wp_die( sprintf( esc_html__( "Warning! This plugin will only work if WordPress is installed in the root directory of your webserver. It is currently installed in '%s'.", "wp-domain-mapping" ), esc_html( $current_site->path ) )); } $total_domains = $wpdb->get_var( "SELECT COUNT(*) FROM {$this->tables['domains']}" ); $primary_domains = $wpdb->get_var( "SELECT COUNT(*) FROM {$this->tables['domains']} WHERE active = 1" ); $edit_row = false; if ( isset( $_GET['edit_domain'] ) ) { $edit_domain = sanitize_text_field( $_GET['edit_domain'] ); $edit_row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$this->tables['domains']} WHERE domain = %s", $edit_domain )); } // Load admin UI require_once( WP_DOMAIN_MAPPING_DIR_PATH . 'admin/domains-page.php' ); } /** * Render domain logs */ public function render_domain_logs() { global $wpdb; $logs = $wpdb->get_results( "SELECT * FROM {$this->tables['logs']} ORDER BY timestamp DESC LIMIT 50" ); if ( ! $logs ) { echo '

' . esc_html__( 'No logs available.', 'wp-domain-mapping' ) . '

'; return; } // Load logs UI require_once( WP_DOMAIN_MAPPING_DIR_PATH . 'admin/logs-table.php' ); } /** * Render admin configuration page */ public function render_admin_page() { global $wpdb, $current_site; if ( ! $this->is_site_admin() ) { return false; } $this->sunrise_warning(); $this->create_tables(); if ( isset($current_site->path) && $current_site->path != "/" ) { wp_die( sprintf( esc_html__( "Warning! This plugin will only work if WordPress is installed in the root directory of your webserver. It is currently installed in '%s'.", "wp-domain-mapping" ), esc_html( $current_site->path ) )); } // Initialize options if needed if ( get_site_option( 'dm_remote_login', 'NA' ) == 'NA' ) { add_site_option( 'dm_remote_login', 1 ); } if ( get_site_option( 'dm_redirect_admin', 'NA' ) == 'NA' ) { add_site_option( 'dm_redirect_admin', 1 ); } if ( get_site_option( 'dm_user_settings', 'NA' ) == 'NA' ) { add_site_option( 'dm_user_settings', 1 ); } // Handle form submission if ( ! empty( $_POST['action'] ) && $_POST['action'] == 'update' ) { check_admin_referer( 'domain_mapping' ); // Validate and save IP addresses $ipok = true; $ipaddresses = explode( ',', sanitize_text_field( $_POST['ipaddress'] ) ); foreach ( $ipaddresses as $address ) { if ( ( $ip = trim( $address ) ) && ! filter_var( $ip, FILTER_VALIDATE_IP ) ) { $ipok = false; break; } } if ( $ipok ) { update_site_option( 'dm_ipaddress', sanitize_text_field( $_POST['ipaddress'] ) ); } // Save remote login option if ( intval( $_POST['always_redirect_admin'] ) == 0 ) { $_POST['dm_remote_login'] = 0; } update_site_option( 'dm_remote_login', intval( $_POST['dm_remote_login'] ) ); // Validate and save CNAME if ( ! preg_match( '/(--|\.\.)/', $_POST['cname'] ) && preg_match( '|^([a-zA-Z0-9-\.])+$|', $_POST['cname'] ) ) { update_site_option( 'dm_cname', sanitize_text_field( $_POST['cname'] ) ); } else { update_site_option( 'dm_cname', '' ); } // Save other options update_site_option( 'dm_301_redirect', isset( $_POST['permanent_redirect'] ) ? intval( $_POST['permanent_redirect'] ) : 0 ); update_site_option( 'dm_redirect_admin', isset( $_POST['always_redirect_admin'] ) ? intval( $_POST['always_redirect_admin'] ) : 0 ); update_site_option( 'dm_user_settings', isset( $_POST['dm_user_settings'] ) ? intval( $_POST['dm_user_settings'] ) : 0 ); update_site_option( 'dm_no_primary_domain', isset( $_POST['dm_no_primary_domain'] ) ? intval( $_POST['dm_no_primary_domain'] ) : 0 ); // Add settings saved notice add_settings_error( 'dm_settings', 'settings_updated', __( 'Settings saved.', 'wp-domain-mapping' ), 'updated' ); } // Load admin UI require_once( WP_DOMAIN_MAPPING_DIR_PATH . 'admin/settings-page.php' ); } /** * Render user management page */ public function render_manage_page() { global $wpdb, $parent_file; if ( isset( $_GET['updated'] ) ) { do_action( 'dm_echo_updated_msg' ); } $this->sunrise_warning(); if ( ! get_site_option( 'dm_ipaddress' ) && ! get_site_option( 'dm_cname' ) ) { if ( $this->is_site_admin() ) { echo wp_kses( __( "Please set the IP address or CNAME of your server in the site admin page.", 'wp-domain-mapping' ), array( 'a' => array( 'href' => array() ) ) ); } else { esc_html_e( "This plugin has not been configured correctly yet.", 'wp-domain-mapping' ); } return false; } $protocol = is_ssl() ? 'https://' : 'http://'; $domains = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$this->tables['domains']} WHERE blog_id = %d", $wpdb->blogid ), ARRAY_A ); // Load user UI require_once( WP_DOMAIN_MAPPING_DIR_PATH . 'admin/user-page.php' ); } /** * Add "Domains" link to sites list * * @param array $actions The row actions * @param int $blog_id The blog ID * @return array Modified actions */ public function add_domain_link_to_sites( $actions, $blog_id ) { $domains_url = add_query_arg( array( 'page' => 'domains', 'blog_id' => $blog_id ), network_admin_url( 'sites.php' ) ); $actions['domains'] = '' . esc_html__( 'Domains', 'wp-domain-mapping' ) . ''; return $actions; } /** * Add domain mapping columns to sites list * * @param array $columns The columns * @return array Modified columns */ public function add_domain_mapping_columns( $columns ) { $columns['map'] = __( 'Mapping' ); return $columns; } /** * Display domain mapping column content * * @param string $column The column name * @param int $blog_id The blog ID */ public function display_domain_mapping_column( $column, $blog_id ) { global $wpdb; static $maps = false; if ( $column == 'map' ) { if ( $maps === false ) { $work = $wpdb->get_results( "SELECT blog_id, domain FROM {$this->tables['domains']} ORDER BY blog_id" ); $maps = array(); if ( $work ) { foreach ( $work as $blog ) { $maps[$blog->blog_id][] = $blog->domain; } } } if ( ! empty( $maps[$blog_id] ) && is_array( $maps[$blog_id] ) ) { foreach ( $maps[$blog_id] as $blog ) { echo esc_html( $blog ) . '
'; } } } } /** * Delete blog domain mappings when a blog is deleted * * @param int $blog_id The blog ID * @param bool $drop Whether to drop tables */ public function delete_blog_domain_mapping( $blog_id, $drop ) { global $wpdb; if ( $blog_id && $drop ) { $domains = $wpdb->get_col( $wpdb->prepare( "SELECT domain FROM {$this->tables['domains']} WHERE blog_id = %d", $blog_id )); do_action( 'dm_delete_blog_domain_mappings', $domains ); $wpdb->delete( $this->tables['domains'], array( 'blog_id' => $blog_id ), array( '%d' ) ); } } /** * Redirect to mapped domain */ public function redirect_to_mapped_domain() { global $current_blog, $wpdb; if ( is_main_site() || ( isset( $_GET['preview'] ) && $_GET['preview'] == 'true' ) || ( isset( $_POST['customize'] ) && isset( $_POST['theme'] ) && $_POST['customize'] == 'on' ) ) { return; } $protocol = is_ssl() ? 'https://' : 'http://'; $url = $this->domain_mapping_siteurl( false ); if ( $url && $url != untrailingslashit( $protocol . $current_blog->domain . $current_blog->path ) ) { $redirect = get_site_option( 'dm_301_redirect' ) ? '301' : '302'; if ( ( defined( 'VHOST' ) && constant("VHOST") != 'yes' ) || ( defined( 'SUBDOMAIN_INSTALL' ) && constant( 'SUBDOMAIN_INSTALL' ) == false ) ) { $_SERVER['REQUEST_URI'] = str_replace( $current_blog->path, '/', $_SERVER['REQUEST_URI'] ); } wp_redirect( $url . $_SERVER['REQUEST_URI'], $redirect ); exit; } } /** * Redirect admin area if needed */ public function redirect_admin() { if ( strpos( $_SERVER['REQUEST_URI'], 'wp-admin/admin-ajax.php' ) !== false ) { return; } if ( get_site_option( 'dm_redirect_admin' ) ) { $url = $this->get_original_url( 'siteurl' ); if ( false === strpos( $url, $_SERVER['HTTP_HOST'] ) ) { wp_redirect( untrailingslashit( $url ) . $_SERVER['REQUEST_URI'] ); exit; } } else { global $current_blog; $url = $this->domain_mapping_siteurl( false ); $request_uri = str_replace( $current_blog->path, '/', $_SERVER['REQUEST_URI'] ); if ( false === strpos( $url, $_SERVER['HTTP_HOST'] ) ) { wp_redirect( str_replace( '//wp-admin', '/wp-admin', trailingslashit( $url ) . $request_uri ) ); exit; } } } /** * Redirect login to original domain */ public function redirect_login_to_orig() { if ( ! get_site_option( 'dm_remote_login' ) || ( isset( $_GET['action'] ) && $_GET['action'] == 'logout' ) || isset( $_GET['loggedout'] ) ) { return; } $url = $this->get_original_url( 'siteurl' ); if ( $url != site_url() ) { $url .= "/wp-login.php"; echo ""; } } /** * Handle logout across domains */ public function remote_logout_loader() { global $current_site, $current_blog, $wpdb; $protocol = is_ssl() ? 'https://' : 'http://'; $hash = $this->get_hash(); $key = md5( time() ); $wpdb->insert( $this->tables['logins'], array( 'id' => $key, 'user_id' => 0, 'blog_id' => $current_blog->blog_id, 't' => current_time( 'mysql' ) ), array( '%s', '%d', '%d', '%s' ) ); if ( get_site_option( 'dm_redirect_admin' ) ) { wp_redirect( $protocol . $current_site->domain . $current_site->path . "?dm={$hash}&action=logout&blogid={$current_blog->blog_id}&k={$key}&t=" . mt_rand() ); exit; } } /** * Handle remote login JS */ public function remote_login_js() { global $current_blog, $current_user, $wpdb; if ( 0 == get_site_option( 'dm_remote_login' ) ) { return; } $hash = $this->get_hash(); $protocol = is_ssl() ? 'https://' : 'http://'; if ( $_GET['dm'] == $hash ) { if ( $_GET['action'] == 'load' ) { if ( ! is_user_logged_in() ) { exit; } $key = md5( time() . mt_rand() ); $wpdb->insert( $this->tables['logins'], array( 'id' => $key, 'user_id' => $current_user->ID, 'blog_id' => $_GET['blogid'], 't' => current_time( 'mysql' ) ), array( '%s', '%d', '%d', '%s' ) ); $url = add_query_arg( array( 'action' => 'login', 'dm' => $hash, 'k' => $key, 't' => mt_rand() ), $_GET['back'] ); echo "window.location = '" . esc_url( $url ) . "'"; exit; } elseif ( $_GET['action'] == 'login' ) { $details = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$this->tables['logins']} WHERE id = %s AND blog_id = %d", $_GET['k'], $wpdb->blogid )); if ( $details ) { if ( $details->blog_id == $wpdb->blogid ) { $wpdb->delete( $this->tables['logins'], array( 'id' => $_GET['k'] ), array( '%s' ) ); $wpdb->query( $wpdb->prepare( "DELETE FROM {$this->tables['logins']} WHERE t < %s", date( 'Y-m-d H:i:s', time() - 120 ) )); wp_set_auth_cookie( $details->user_id ); wp_redirect( remove_query_arg( array( 'dm', 'action', 'k', 't' ), $protocol . $current_blog->domain . $_SERVER['REQUEST_URI'] )); exit; } else { wp_die( esc_html__( "Incorrect or out of date login key", 'wp-domain-mapping' ) ); } } else { wp_die( esc_html__( "Unknown login key", 'wp-domain-mapping' ) ); } } elseif ( $_GET['action'] == 'logout' ) { $details = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$this->tables['logins']} WHERE id = %s AND blog_id = %d", $_GET['k'], $_GET['blogid'] )); if ( $details ) { $wpdb->delete( $this->tables['logins'], array( 'id' => $_GET['k'] ), array( '%s' ) ); $blog = get_blog_details( $_GET['blogid'] ); wp_clear_auth_cookie(); wp_redirect( trailingslashit( $blog->siteurl ) . "wp-login.php?loggedout=true" ); exit; } else { wp_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 ""; } /** * 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; } /** * Check if user is a site admin * * @return bool True if user is a site admin */ public function is_site_admin() { return current_user_can( 'manage_network' ); } /** * Clean domain name * * @param string $domain The domain * @return string The cleaned domain */ public function clean_domain( $domain ) { // Remove protocol $domain = preg_replace( '#^https?://#', '', $domain ); // Remove trailing slash $domain = rtrim( $domain, '/' ); // Convert to punycode for IDN if ( function_exists( 'idn_to_ascii' ) && preg_match( '/[^a-z0-9\-\.]/i', $domain ) ) { $domain = idn_to_ascii( $domain ); } return $domain; } /** * Validate a domain name * * @param string $domain The domain * @return bool True if valid */ public function is_valid_domain( $domain ) { // Basic validation return (bool) preg_match( '/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}$/i', $domain ); } /** * Get IDN warning message * * @return string The warning message */ public function idn_warning() { return sprintf( /* translators: %s: URL to punycode converter */ __( 'International Domain Names should be in punycode format.', 'wp-domain-mapping' ), "https://www.punycoder.com/" ); } /** * Ensure URL has protocol * * @param string $domain The domain * @return string The URL with protocol */ public function ensure_protocol( $domain ) { if ( preg_match( '#^https?://#', $domain ) ) { return $domain; } return 'http://' . $domain; } /** * Default updated messages */ public function echo_default_updated_msg() { if ( ! isset( $_GET['updated'] ) ) { return; } switch ( $_GET['updated'] ) { case "add": $msg = __( 'New domain added.', 'wp-domain-mapping' ); break; case "exists": $msg = __( 'New domain already exists.', 'wp-domain-mapping' ); break; case "primary": $msg = __( 'New primary domain.', 'wp-domain-mapping' ); break; case "del": $msg = __( 'Domain deleted.', 'wp-domain-mapping' ); break; default: return; } echo "

" . esc_html( $msg ) . "

"; } } // Initialize the plugin function wp_domain_mapping_init() { // Add default updated messages action add_action( 'dm_echo_updated_msg', array( WP_Domain_Mapping::get_instance(), 'echo_default_updated_msg' ) ); // Initialize the plugin return WP_Domain_Mapping::get_instance(); } wp_domain_mapping_init(); // Include admin UI templates require_once( WP_DOMAIN_MAPPING_DIR_PATH . 'includes/admin-ui.php' );