diff --git a/composer.json b/composer.json index 95b12fce..dc279835 100644 --- a/composer.json +++ b/composer.json @@ -31,5 +31,10 @@ "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } + }, + "autoload": { + "psr-4": { + "DarkMatter\\": "includes/classes/" + } } } diff --git a/dark-matter.php b/dark-matter.php index b148add8..1180b54d 100644 --- a/dark-matter.php +++ b/dark-matter.php @@ -44,10 +44,22 @@ */ wp_cache_add_global_groups( 'dark-matter' ); -require_once DM_PATH . '/dark-matter/class-dm-pluginupdate.php'; +/** + * Include the PSR-4 autoloader. + */ +if ( file_exists( DM_PATH . 'vendor/autoload.php' ) ) { + require_once DM_PATH . 'vendor/autoload.php'; +} + +require_once DM_PATH . 'dark-matter/class-dm-pluginupdate.php'; new DM_PluginUpdate(); /** - * Domain Mapping module. + * Non-autoloader files. + */ +require_once DM_PATH . '/includes/utility/functions.php'; + +/** + * Let the magic - and bugs ... probably bugs! - begin. */ -require DM_PATH . '/domain-mapping/domain-mapping.php'; +\DarkMatter\DarkMatter::instance(); diff --git a/domain-mapping/classes/class-dm-ui.php b/domain-mapping/classes/class-dm-ui.php deleted file mode 100644 index 105dd26b..00000000 --- a/domain-mapping/classes/class-dm-ui.php +++ /dev/null @@ -1,124 +0,0 @@ -get_permission(), - 'domains', - array( - $this, - 'page', - ) - ); - - add_action( 'load-' . $hook_suffix, array( $this, 'enqueue' ) ); - } - - /** - * Enqueue assets for the Admin Page. - * - * @since 2.0.0 - * - * @return void - */ - public function enqueue() { - $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min' ); - - wp_register_script( - 'dark-matter-domains', - DM_PLUGIN_URL . 'domain-mapping/build/domain-mapping' . $min . '.js', - [ 'wp-i18n' ], - DM_VERSION, - true - ); - - wp_localize_script( - 'dark-matter-domains', - 'dmSettings', - array( - 'rest_root' => get_rest_url(), - 'nonce' => wp_create_nonce( 'wp_rest' ), - ) - ); - - wp_enqueue_script( 'dark-matter-domains' ); - - wp_enqueue_style( 'dark-matter-domains', DM_PLUGIN_URL . 'domain-mapping/build/domain-mapping-style' . $min . '.css', [], DM_VERSION ); - } - - /** - * Retrieve the capability that is required for using the admin page. - * - * @since 2.1.2 - * - * @return string Capability that must be met to use the Admin page. - */ - public function get_permission() { - /** - * Allows the override of the default permission for per site domain management. - * - * @since 2.1.2 - * - * @param string $capability Capability required to manage domains (upgrade_network / Super Admin). - * @param string $context The context the permission is checked. - */ - return apply_filters( 'dark_matter_domain_permission', 'upgrade_network', 'admin' ); - } - - /** - * Very basic HTML output for the - * - * @since 2.0.0 - * - * @return void - */ - public function page() { - if ( ! current_user_can( $this->get_permission() ) ) { - wp_die( esc_html__( 'You do not have permission to manage domains.', 'dark-matter' ) ); - } - ?> -
- cookie_domain_dm_set() && ( ! defined( 'DARKMATTER_SSO_TYPE' ) || 'disable' !== DARKMATTER_SSO_TYPE ) ) { - require_once DM_PATH . '/domain-mapping/sso/class-dm-sso-cookie.php'; -} - -require_once DM_PATH . '/domain-mapping/rest/class-dm-rest-domains-controller.php'; -require_once DM_PATH . '/domain-mapping/rest/class-dm-rest-restricted-controller.php'; - -if ( defined( 'WP_CLI' ) && WP_CLI ) { - require_once DM_PATH . '/domain-mapping/cli/class-darkmatter-domain-cli.php'; - require_once DM_PATH . '/domain-mapping/cli/class-darkmatter-dropin-cli.php'; - require_once DM_PATH . '/domain-mapping/cli/class-darkmatter-restrict-cli.php'; -} diff --git a/domain-mapping/inc/compat.php b/domain-mapping/inc/compat.php deleted file mode 100644 index b1d9174d..00000000 --- a/domain-mapping/inc/compat.php +++ /dev/null @@ -1,33 +0,0 @@ - over the unmapped domain. - */ - ! empty( $_GET['customize_changeset_uuid'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended - || - /** - * Do not redirect Previews - */ - ( ! empty( $_GET['preview'] ) || ! empty( $_GET['page_id'] ) || ! empty( $_GET['p'] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended - ) { - return; -} - -/** - * Helper function which is used to determine if we need to perform a redirect - * to the primary domain whilst retaining the remaining URI structure. - * - * @since 2.0.0 - * - * @return void - */ -function darkmatter_maybe_redirect() { - /** - * Do not perform redirects if it is the main site. - */ - if ( is_main_site() ) { - return; - } - - $request_uri = ( empty( $_SERVER['REQUEST_URI'] ) ? '' : wp_strip_all_tags( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ); - - $request = ltrim( $request_uri, '/' ); - - /** - * Determine if the request is one we shouldn't handle for redirects. - */ - $filename = basename( $request ); - $filename = strtok( $filename, '?' ); - - $ajax_filenames = array( - 'admin-post.php' => true, - 'admin-ajax.php' => true, - ); - - /** - * Check to see if the current request is an Admin Post action or an AJAX action. These two requests in Dark Matter - * can be on either the admin domain or the primary domain. - */ - if ( ! empty( $filename ) && array_key_exists( $filename, $ajax_filenames ) ) { - return; - } - - $original_blog = get_site(); - - $http_host = ( empty( $_SERVER['HTTP_HOST'] ) ? '' : wp_strip_all_tags( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ); - - $host = trim( $http_host, '/' ); - $primary = DarkMatter_Primary::instance()->get(); - - $is_admin = false; - - $admin_filenames = array( - 'wp-login.php' => true, - 'wp-register.php' => true, - ); - - if ( is_admin() || ( ! empty( $filename ) && array_key_exists( $filename, $admin_filenames ) ) ) { - $is_admin = true; - } - - /** - * Dark Matter will disengage if the website is no longer public or is archived or deleted. - */ - if ( (int) $original_blog->public < 0 || '0' !== $original_blog->archived || '0' !== $original_blog->deleted ) { - return; - } - - /** - * If Allow Logins is enabled, then the `wp-login.php` request is to be made - * available on both the primary mapped domain and admin domain. - */ - if ( ! apply_filters( 'darkmatter_allow_logins', false ) && $is_admin && $host === $original_blog->domain ) { - return; - } - - /** - * If there is no primary domain, there is nothing to do. Also make sure the - * domain is active. - */ - if ( ! $primary || ! $original_blog || ! $primary->active || absint( $original_blog->public ) < 1 ) { - return; - } - - if ( $is_admin && $host !== $original_blog->domain ) { - $is_ssl_admin = ( defined( 'FORCE_SSL_ADMIN' ) && FORCE_SSL_ADMIN ); - - $url = 'http' . ( $is_ssl_admin ? 's' : '' ) . '://' . $original_blog->domain . $original_blog->path . $request; - } elseif ( $host !== $primary->domain || is_ssl() !== $primary->is_https ) { - $url = 'http' . ( $primary->is_https ? 's' : '' ) . '://' . $primary->domain . '/' . $request; - - /** - * Make sure the Path - if this is a sub-folder Network - is removed from - * the URL. For sub-domain Networks, the path will be a single forward slash - * (/). - */ - if ( '/' !== $original_blog->path ) { - $path = '/' . trim( $original_blog->path, '/' ) . '/'; - $url = str_ireplace( $path, '/', $url ); - } - } - - /** - * If the URL is empty, then there is no redirect to perform. - */ - if ( empty( $url ) ) { - return; - } - - header( 'X-Redirect-By: Dark-Matter' ); - header( 'Location:' . $url, true, 301 ); - - die; -} - -/** - * We use `muplugins_loaded` action (introduced in WordPress 2.8.0) rather than - * the "ms_loaded" (introduced in WordPress 4.6.0). - * - * A hook on `muplugins_loaded` is used to ensure that WordPress has loaded the - * Blog / Site globals. This is specifically useful when some one goes to the - * Admin domain URL - http://my.sites.com/two/ - which is to redirect to the - * primary domain - http://example.com. - */ -add_action( 'muplugins_loaded', 'darkmatter_maybe_redirect', 20 ); diff --git a/domain-mapping/inc/sunrise.php b/domain-mapping/inc/sunrise.php deleted file mode 100644 index c54f1beb..00000000 --- a/domain-mapping/inc/sunrise.php +++ /dev/null @@ -1,107 +0,0 @@ -get( $fqdn ); - -if ( $dm_domain && $dm_domain->active ) { - /** - * Prepare all the global variables. This is require irrespective of whether - * it is a primary or secondary domain. - */ - global $current_blog, $original_blog; - $current_blog = get_site( $dm_domain->blog_id ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited - - global $current_site; - $current_site = WP_Network::get_instance( $current_blog->site_id ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited - - // @codingStandardsIgnoreStart - global $blog_id; - $blog_id = $current_blog->blog_id; - global $site_id; - $site_id = $current_blog->site_id; - // @codingStandardsIgnoreEnd - - /** - * Dark Matter will disengage if the website is no longer public or is - * archived or deleted. - */ - if ( (int) $current_blog->public < 0 || '0' !== $current_blog->archived || '0' !== $current_blog->deleted ) { - return; - } - - /** - * If the primary domain, then update the WP_Site properties to match the - * mapped domain and not the admin domain. - */ - if ( $dm_domain->is_primary ) { - $original_blog = clone $current_blog; - - $current_blog->domain = $dm_domain->domain; - $current_blog->path = '/'; - - /** - * Load and prepare the WordPress Network. - */ - global $current_site; // phpcs:ignore WordPressVIPMinimum.Variables.VariableAnalysis.VariableRedeclaration - $current_site = WP_Network::get_instance( $current_blog->site_id ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited - - if ( ! defined( 'COOKIE_DOMAIN' ) ) { - define( 'COOKIE_DOMAIN', $dm_domain->domain ); - } - - define( 'DOMAIN_MAPPING', true ); - - if ( empty( $current_site->blog_id ) ) { - $current_site->blog_id = get_main_site_id( $current_site->id ); - } - - /** - * Set the other necessary globals to ensure WordPress functions correctly. - */ - // @codingStandardsIgnoreStart - global $blog_id; - $blog_id = $current_blog->blog_id; - global $site_id; - $site_id = $current_blog->site_id; - // @codingStandardsIgnoreEnd - } -} - -/** - * Determine if we should perform a redirect. - */ -require_once $dirname . '/inc/redirect.php'; diff --git a/domain-mapping/sso/class-dm-sso-cookie.php b/domain-mapping/sso/class-dm-sso-cookie.php deleted file mode 100644 index a23e76f3..00000000 --- a/domain-mapping/sso/class-dm-sso-cookie.php +++ /dev/null @@ -1,377 +0,0 @@ -is_admin_domain() ) { - add_action( 'wp_head', array( $this, 'head_script' ) ); - add_action( 'plugins_loaded', array( $this, 'validate_token' ) ); - } - } - - /** - * Creates a nonce that isn't linked to a user, like the APIs in WordPress Core, but functions in a similar fashion. - * - * @since 2.0.4 - * - * @param string $action Value which creates the unique nonce. - * @return string Nonce token for use. - */ - private function create_shared_nonce( $action = '' ) { - $i = wp_nonce_tick(); - return substr( wp_hash( $i . '|' . $action, 'nonce' ), -12, 10 ); - } - - /** - * Verify a shared nonce. - * - * @since 2.0.4 - * - * @see create_shared_nonce() - * - * @param string $nonce Nonce that was used and requires verification. - * @param string $action Value which provides the nonce uniqueness. - * @return bool|int An integer if the nonce check passed, 1 for 0-12 hours ago and 2 for 12-24 hours ago. False otherwise. - */ - private function verify_shared_nonce( $nonce = '', $action = '' ) { - if ( empty( $nonce ) ) { - return false; - } - - $i = wp_nonce_tick(); - - /** - * Nonce generated 0-12 hours ago - */ - $expected = substr( wp_hash( $i . '|' . $action, 'nonce' ), -12, 10 ); - if ( hash_equals( $expected, $nonce ) ) { - return 1; - } - - /** - * Nonce generated 12-24 hours ago - */ - $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action, 'nonce' ), -12, 10 ); - if ( hash_equals( $expected, $nonce ) ) { - return 2; - } - - /** - * Invalid. - */ - return false; - } - - /** - * Determines if the current request is on the Admin Domain. - * - * @since 2.0.0 - * - * @return bool True if current request is on the admin domain. False otherwise. - */ - private function is_admin_domain() { - $network = get_network(); - - $http_host = ( empty( $_SERVER['HTTP_HOST'] ) ? '' : wp_strip_all_tags( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ); - - if ( ! empty( $network ) && false === stripos( $network->domain, $http_host ) ) { - return false; - } - - return true; - } - - /** - * Create the JavaScript output include for logging a user in to the admin - * when on the Mapped domain. This is ultimately what makes the Admin Bar - * appear. - * - * @since 2.0.0 - * - * @return void - */ - public function login_token() { - header( 'Content-Type: text/javascript' ); - - $this->nocache_headers(); - - /** - * Ensure that the JavaScript is never empty. - */ - echo '// dm_sso' . PHP_EOL; - - if ( is_user_logged_in() ) { - $referer = ( empty( $_SERVER['HTTP_REFERER'] ) ? '' : wp_strip_all_tags( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) ); - - $action = sprintf( - 'darkmatter-sso|%1$s|%2$s', - $referer, - md5( empty( $_SERVER['HTTP_USER_AGENT'] ) ? '' : wp_strip_all_tags( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ) - ); - - /** - * Construct an authentication token which is passed back along with an - * action flag to tell the front end to - */ - $url = add_query_arg( - array( - '__dm_action' => 'authorise', - 'auth' => wp_generate_auth_cookie( get_current_user_id(), time() + ( 2 * MINUTE_IN_SECONDS ) ), - 'nonce' => $this->create_shared_nonce( $action ), - ), - $referer - ); - - printf( 'window.location.replace( "%1$s" );', esc_url_raw( $url ) ); - } - - die(); - } - - /** - * Create the JavaScript output for handling the logout functionality. - * Without this, users can end up in a state where they are logged out of - * the Admin domain but remain perpetually logged in to the Mapped domains. - * - * @since 2.0.0 - * - * @return void - */ - public function logout_token() { - header( 'Content-Type: text/javascript' ); - - $this->nocache_headers(); - - /** - * Ensure that the JavaScript is never empty. - */ - echo '// dm_sso' . PHP_EOL; - - if ( false === is_user_logged_in() ) { - $referer = ( empty( $_SERVER['HTTP_REFERER'] ) ? '' : wp_strip_all_tags( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) ); - - $url = add_query_arg( - array( - '__dm_action' => 'logout', - ), - $referer - ); - printf( 'window.location.replace( "%1$s" );', esc_url_raw( $url ) ); - } - - die(); - } - - /** - * Adds the - $header_value ) { - header( "{$header_name}: {$header_value}" ); - } - } - - /** - * Handle the validation of the login token and logging in of a user. Also - * handle the logout if that action is provided. - * - * @since 2.0.0 - * - * @return void - */ - public function validate_token() { - $dm_action = ( empty( $_GET['__dm_action'] ) ? '' : wp_strip_all_tags( wp_unslash( $_GET['__dm_action'] ) ) ); - - /** - * Ensure that URLs with the __dm_action query string are not cached by browsers. - */ - if ( ! empty( $dm_action ) ) { - $this->nocache_headers(); - } - - /** - * If the validation token is provided on the admin domain, rather than the primary / mapped domain, then just - * ignore it and end processing. - */ - if ( $this->is_admin_domain() ) { - return; - } - - /** - * First check to see if the authorise action is provided in the URL. - */ - if ( 'authorise' === $dm_action ) { - /** - * Validate the token provided in the URL. - */ - $user_id = wp_validate_auth_cookie( wp_strip_all_tags( wp_unslash( $_GET['auth'] ) ), 'auth' ); - $nonce = wp_strip_all_tags( wp_unslash( $_GET['nonce'] ) ); - - $action = sprintf( - 'darkmatter-sso|%1$s|%2$s', - ( empty( $_SERVER['HTTP_REFERER'] ) ? '' : wp_strip_all_tags( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) ), - md5( empty( $_SERVER['HTTP_USER_AGENT'] ) ? '' : wp_strip_all_tags( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ) - ); - - /** - * Check if the validate token worked and we have a User ID. It will - * display an error message or login the User if all works out well. - */ - if ( false === $user_id || ! $this->verify_shared_nonce( $nonce, $action ) ) { - wp_die( 'Oops! Something went wrong with logging in.' ); - } else { - /** - * Create the Login session cookie and redirect the user to the - * current page with the URL querystrings for Domain Mapping SSO - * removed. - */ - wp_set_auth_cookie( $user_id ); - - // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect - wp_redirect( esc_url( remove_query_arg( array( '__dm_action', 'auth', 'nonce' ) ) ), 302, 'Dark-Matter' ); - die(); - } - } elseif ( 'logout' === $dm_action ) { - wp_logout(); - - // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect - wp_redirect( esc_url( remove_query_arg( array( '__dm_action' ) ) ), 302, 'Dark-Matter' ); - - die(); - } - } - - /** - * Return the Singleton Instance of the class. - * - * @since 2.0.0 - * - * @return DM_SSO_Cookie - */ - public static function instance() { - static $instance = false; - - if ( ! $instance ) { - $instance = new self(); - } - - return $instance; - } -} - -DM_SSO_Cookie::instance(); diff --git a/includes/classes/DarkMatter.php b/includes/classes/DarkMatter.php new file mode 100644 index 00000000..813c7036 --- /dev/null +++ b/includes/classes/DarkMatter.php @@ -0,0 +1,190 @@ +register_domainmapping(); + + /** + * SSO is a web-based process and not needed in the CLI. + */ + if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) { + $this->register_sso(); + } + } + + /** + * Register a set of classes used by the Dark Matter plugin. + * + * @since 3.0.0 + * + * @param array $classes String of class names / namespaces to be instantiated. + * @return array Array of the resultant objects. + */ + private function class_register( $classes = [] ) { + if ( empty( $classes ) ) { + return []; + } + + + $objs = []; + foreach ( $classes as $class ) { + $obj = new $class(); + + if ( $obj instanceof Registerable ) { + $obj->register(); + } + + if ( $obj instanceof \WP_CLI_Command ) { + $obj::define(); + } + + if ( $obj instanceof \WP_REST_Controller ) { + $obj->register_routes(); + } + } + + return $objs; + } + + /** + * Register the classes for Domain Mapping. + * + * @since 3.0.0 + * + * @return void + */ + public function register_domainmapping() { + /** + * Domain type constants. + */ + define( 'DM_DOMAIN_TYPE_MAIN', 1 ); + define( 'DM_DOMAIN_TYPE_MEDIA', 2 ); + + /** + * Basic compatibility/configuration for bbPress and WooCommerce. + */ + if ( + /** + * Detect if WooCommerce is installed. + */ + class_exists( 'WooCommerce' ) + || + /** + * Detect if bbPress is installed. + */ + class_exists( 'bbPress' ) + ) { + add_filter( 'darkmatter_allow_logins', '__return_true' ); + } + + $domainmapping_classes = [ + DomainMapping\Installer::class, + DomainMapping\Processor\Mapping::class, + DomainMapping\Processor\Media::class, + ]; + + if ( + ( ! defined( 'DARKMATTER_HIDE_UI' ) || ! DARKMATTER_HIDE_UI ) + && ! is_main_site() + ) { + $domainmapping_classes[] = DomainMapping\Admin\DomainSettings::class; + } + + $domainmapping_classes[] = DomainMapping\Admin\HealthChecks::class; + + if ( defined( 'WPSEO_VERSION' ) ) { + $domainmapping_classes = DomainMapping\Support\Yoast::class; + } + + $this->class_register( $domainmapping_classes ); + + if ( defined( 'WP_CLI' ) && WP_CLI ) { + $this->class_register( + [ + DomainMapping\CLI\Domains::class, + DomainMapping\CLI\Dropin::class, + DomainMapping\CLI\Restricted::class, + ] + ); + } + } + + /** + * Register REST routes. This is separate due to the need to be fired on the `rest_api_init` hook. + * + * @since 3.0.0 + * + * @return void + */ + public function register_rest() { + $this->class_register( + [ + DomainMapping\REST\Domains::class, + DomainMapping\REST\Restricted::class, + ] + ); + } + + /** + * Single Sign-On functionality. + * + * @return void + */ + public function register_sso() { + $this->class_register( + [ + Admin::class, + Authorise::class, + Authenticate::class, + ] + ); + } + + /** + * Return the Singleton Instance of the class. + * + * @since 3.0.0 + * + * @return DarkMatter + */ + public static function instance() { + static $instance = false; + + if ( ! $instance ) { + $instance = new self(); + } + + return $instance; + } +} diff --git a/includes/classes/DomainMapping/Admin/DomainSettings.php b/includes/classes/DomainMapping/Admin/DomainSettings.php new file mode 100644 index 00000000..f6f91b14 --- /dev/null +++ b/includes/classes/DomainMapping/Admin/DomainSettings.php @@ -0,0 +1,87 @@ +slug = 'domains'; + $this->menu_title = __( 'Domains', 'dark-matter' ); + $this->page_title = __( 'Domain Mappings', 'dark-matter' ); + + /** + * Allows the override of the default permission for per site domain management. + * + * @since 2.1.2 + * + * @param string $capability Capability required to manage domains (upgrade_network / Super Admin). + * @param string $context The context the permission is checked. + */ + $this->permission = apply_filters( 'dark_matter_domain_permission', 'upgrade_network', 'admin' ); + } + + /** + * Enqueue assets for the Admin Page. + * + * @since 3.0.0 + * + * @return void + */ + public function enqueue() { + $min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min' ); + + wp_register_script( + 'dark-matter-domains', + DM_PLUGIN_URL . 'domain-mapping/build/domain-mapping' . $min . '.js', + [ 'wp-i18n' ], + DM_VERSION, + true + ); + + wp_localize_script( + 'dark-matter-domains', + 'dmSettings', + [ + 'rest_root' => get_rest_url(), + 'nonce' => wp_create_nonce( 'wp_rest' ), + ] + ); + + wp_enqueue_script( 'dark-matter-domains' ); + + wp_enqueue_style( 'dark-matter-domains', DM_PLUGIN_URL . 'domain-mapping/build/domain-mapping-style' . $min . '.css', [], DM_VERSION ); + } + + /** + * Output the HTML container for the React app. + * + * @since 3.0.0 + * + * @return void + */ + public function render() { + ?> + + __( 'Dark Matter single-sign on (bringing the admin bar to the public-facing side) is enabled.', 'dark-matter' ), 'status' => 'good', - 'badge' => array( + 'badge' => [ 'label' => __( 'Domain Mapping', 'dark-matter' ), 'color' => 'green', - ), + ], 'description' => sprintf( '%s
', __( 'Dark Matter single-sign on is enabled and can load the admin bar when WordPress users are visiting the public-facing side of your site.', 'dark-matter' ) @@ -155,10 +160,10 @@ public function test_dropin() { $result = [ 'label' => __( 'Sunrise dropin is enabled and up-to-date.', 'dark-matter' ), 'status' => 'good', - 'badge' => array( + 'badge' => [ 'label' => __( 'Domain Mapping', 'dark-matter' ), 'color' => 'green', - ), + ], 'description' => sprintf( '%s
', __( 'Sunrise is the name of the dropin file which maps custom domains to your WordPress sites.', 'dark-matter' ) @@ -219,16 +224,15 @@ public function test_primary_domain_set() { $result = [ 'label' => __( 'You have a primary domain.', 'dark-matter' ), 'status' => 'good', - 'badge' => array( + 'badge' => [ 'label' => __( 'Domain Mapping', 'dark-matter' ), 'color' => 'green', - ), - 'description' => '', + ], 'actions' => '', 'test' => 'darkmatter_domain_mapping_primary_domain_set', ]; - $primary = DarkMatter_Primary::instance()->get(); + $primary = Primary::instance()->get(); if ( empty( $primary ) ) { $result['label'] = __( 'You have not a set a primary domain.', 'dark-matter' ); @@ -273,10 +277,10 @@ public function test_ssl() { $result = [ 'label' => __( 'Your SSL configuration is compatible with Dark Matter.', 'dark-matter' ), 'status' => 'good', - 'badge' => array( + 'badge' => [ 'label' => __( 'Domain Mapping', 'dark-matter' ), 'color' => 'green', - ), + ], 'description' => sprintf( '%s
', __( 'Your admin area is secured by HTTPS and compatible with domain mapping.', 'dark-matter' ) @@ -308,22 +312,4 @@ public function test_ssl() { return $result; } - - /** - * Return the Singleton Instance of the class. - * - * @since 2.1.0 - * - * @return DM_HealthChecks - */ - public static function instance() { - static $instance = false; - - if ( ! $instance ) { - $instance = new self(); - } - - return $instance; - } } -DM_HealthChecks::instance(); diff --git a/domain-mapping/cli/class-darkmatter-domain-cli.php b/includes/classes/DomainMapping/CLI/Domains.php similarity index 92% rename from domain-mapping/cli/class-darkmatter-domain-cli.php rename to includes/classes/DomainMapping/CLI/Domains.php index 0fffc3bc..7ee4926e 100644 --- a/domain-mapping/cli/class-darkmatter-domain-cli.php +++ b/includes/classes/DomainMapping/CLI/Domains.php @@ -1,21 +1,28 @@ add( $fqdn, $opts['primary'], $opts['https'], $opts['force'], ! $opts['disable'], $type ); if ( is_wp_error( $result ) ) { @@ -121,6 +128,15 @@ private function check_type_opt( $type = '' ) { return DM_DOMAIN_TYPE_MAIN; } + /** + * Include this CLI amongst the others. + * + * @return void + */ + public static function define() { + WP_CLI::add_command( 'darkmatter domain', self::class ); + } + /** * List a domain for the current Site. If the the URL is omitted and the * command is run on the root Site, it will list all domains available for @@ -166,12 +182,12 @@ public function list( $args, $assoc_args ) { ] ); - if ( ! in_array( $opts['format'], array( 'table', 'json', 'csv', 'yaml', 'count' ) ) ) { + if ( ! in_array( $opts['format'], [ 'table', 'json', 'csv', 'yaml', 'count' ] ) ) { $opts['format'] = 'table'; } if ( $opts['primary'] ) { - $db = DarkMatter_Primary::instance(); + $db = Manager\Primary::instance(); $domains = $db->get_all(); } else { /** @@ -184,7 +200,7 @@ public function list( $args, $assoc_args ) { $site_id = null; } - $db = DarkMatter_Domains::instance(); + $db = Manager\Domain::instance(); $domains = $db->get_domains( $site_id ); } @@ -196,13 +212,13 @@ function ( $domain ) { $no_val = __( 'No', 'dark-matter' ); $yes_val = __( 'Yes', 'dark-matter' ); - $columns = array( + $columns = [ 'F.Q.D.N.' => $domain->domain, 'Primary' => ( $domain->is_primary ? $yes_val : $no_val ), 'Protocol' => ( $domain->is_https ? 'HTTPS' : 'HTTP' ), 'Active' => ( $domain->active ? $yes_val : $no_val ), 'Type' => ( DM_DOMAIN_TYPE_MEDIA === $domain->type ? 'Media' : 'Main' ), - ); + ]; /** * If the query is the root Site and we are displaying all domains, @@ -288,7 +304,7 @@ public function remove( $args, $assoc_args ) { ] ); - $db = DarkMatter_Domains::instance(); + $db = Manager\Domain::instance(); /** * Remove the domain. @@ -371,7 +387,7 @@ public function set( $args, $assoc_args ) { $fqdn = $args[0]; - $db = DarkMatter_Domains::instance(); + $db = Manager\Domain::instance(); $domain_before = $db->get( $fqdn ); $opts = wp_parse_args( @@ -462,4 +478,3 @@ public function set( $args, $assoc_args ) { WP_CLI::success( $fqdn . __( ': successfully updated.', 'dark-matter' ) ); } } -WP_CLI::add_command( 'darkmatter domain', 'DarkMatter_Domain_CLI' ); diff --git a/domain-mapping/cli/class-darkmatter-dropin-cli.php b/includes/classes/DomainMapping/CLI/Dropin.php similarity index 81% rename from domain-mapping/cli/class-darkmatter-dropin-cli.php rename to includes/classes/DomainMapping/CLI/Dropin.php index 546bedd9..beb629fb 100644 --- a/domain-mapping/cli/class-darkmatter-dropin-cli.php +++ b/includes/classes/DomainMapping/CLI/Dropin.php @@ -1,23 +1,26 @@ is_dropin_latest() ) { WP_CLI::success( __( 'Current Sunrise dropin matches the Sunrise within Dark Matter plugin.', 'dark-matter' ) ); return; @@ -40,6 +42,15 @@ public function check() { WP_CLI::error( __( 'Sunrise dropin does not match the Sunrise within Dark Matter plugin. Consider using the "update" command to correct this issue.', 'dark-matter' ) ); } + /** + * Include this CLI amongst the others. + * + * @return void + */ + public static function define() { + WP_CLI::add_command( 'darkmatter dropin', self::class ); + } + /** * Upgrade the Sunrise dropin plugin to the latest version within the Dark * Matter plugin. @@ -66,7 +77,7 @@ public function check() { */ public function update( $args, $assoc_args ) { $destination = WP_CONTENT_DIR . '/sunrise.php'; - $source = DM_PATH . '/domain-mapping/sunrise.php'; + $source = DM_PATH . '/includes/dropins/sunrise.php'; $opts = wp_parse_args( $assoc_args, @@ -100,4 +111,3 @@ public function update( $args, $assoc_args ) { } } } -WP_CLI::add_command( 'darkmatter dropin', 'DarkMatter_Dropin_CLI' ); diff --git a/domain-mapping/cli/class-darkmatter-restrict-cli.php b/includes/classes/DomainMapping/CLI/Restricted.php similarity index 81% rename from domain-mapping/cli/class-darkmatter-restrict-cli.php rename to includes/classes/DomainMapping/CLI/Restricted.php index ed2171fc..6d893409 100644 --- a/domain-mapping/cli/class-darkmatter-restrict-cli.php +++ b/includes/classes/DomainMapping/CLI/Restricted.php @@ -1,21 +1,26 @@ add( $fqdn ); if ( is_wp_error( $result ) ) { @@ -51,6 +56,15 @@ public function add( $args, $assoc_args ) { WP_CLI::success( $fqdn . __( ': is now restricted.', 'dark-matter' ) ); } + /** + * Include this CLI amongst the others. + * + * @return void + */ + public static function define() { + WP_CLI::add_command( 'darkmatter restrict', self::class ); + } + /** * Retrieve a list of all Restricted domains for the Network. * @@ -90,11 +104,11 @@ public function list( $args, $assoc_args ) { ] ); - if ( ! in_array( $opts['format'], array( 'ids', 'table', 'json', 'csv', 'yaml', 'count' ) ) ) { + if ( ! in_array( $opts['format'], [ 'ids', 'table', 'json', 'csv', 'yaml', 'count' ] ) ) { $opts['format'] = 'table'; } - $db = DarkMatter_Restrict::instance(); + $db = Manager\Restricted::instance(); $restricted = $db->get(); @@ -146,7 +160,7 @@ public function remove( $args, $assoc_args ) { $fqdn = $args[0]; - $restricted = DarkMatter_Restrict::instance(); + $restricted = Manager\Restricted::instance(); $result = $restricted->delete( $fqdn ); if ( is_wp_error( $result ) ) { @@ -156,4 +170,3 @@ public function remove( $args, $assoc_args ) { WP_CLI::success( $fqdn . __( ': is no longer restricted.', 'dark-matter' ) ); } } -WP_CLI::add_command( 'darkmatter restrict', 'DarkMatter_Restrict_CLI' ); diff --git a/domain-mapping/classes/class-dm-domain.php b/includes/classes/DomainMapping/Data/Domain.php similarity index 86% rename from domain-mapping/classes/class-dm-domain.php rename to includes/classes/DomainMapping/Data/Domain.php index 4986174b..d7109ea9 100644 --- a/domain-mapping/classes/class-dm-domain.php +++ b/includes/classes/DomainMapping/Data/Domain.php @@ -1,19 +1,22 @@ $value ) { diff --git a/includes/classes/DomainMapping/Helper.php b/includes/classes/DomainMapping/Helper.php new file mode 100644 index 00000000..4781260b --- /dev/null +++ b/includes/classes/DomainMapping/Helper.php @@ -0,0 +1,193 @@ +get_request_filename() : $filename; + + $admin_filenames = [ + 'wp-login.php' => true, + 'wp-register.php' => true, + ]; + + if ( is_admin() || ( ! empty( $filename ) && array_key_exists( $filename, $admin_filenames ) ) ) { + return true; + } + + return false; + } + + /** + * Checks the supply blog/site to ensure it is public. + * + * @since 3.0.0 + * + * @param \WP_Site $blog Blog to check. + * @return bool True if public. False otherwise. + */ + public function is_public( $blog ) { + /** + * Make sure we have the right kind of object. + */ + if ( ! $blog instanceof \WP_Site ) { + return false; + } + + return ( + /** + * Check the current blog is public and be compatible with plugins such as Restricted Site Access. + */ + 0 < (int) $blog->public + /** + * Check the current blog has not been archived. + */ + && 0 === (int) $blog->archived + /** + * Check the current blog has not been "soft" deleted. + */ + && 0 === (int) $blog->deleted + ); + } + + /** + * Map the primary domain on the passed in value if it contains the unmapped URL and the Site has a primary domain. + * + * @since 3.0.0 + * + * @param mixed $value Potentially a value containing the site's unmapped URL. + * @param integer $blog_id Site (Blog) ID for the URL which is being mapped. + * @return string If unmapped URL is found, then returns the primary URL. Untouched otherwise. + */ + public function map( $value = '', $blog_id = 0 ) { + /** + * Ensure that we are working with a string. + */ + if ( ! is_string( $value ) ) { + return $value; + } + + /** + * Retrieve the current blog. + */ + $blog = get_site( absint( $blog_id ) ); + $primary = Primary::instance()->get( $blog->blog_id ); + + $unmapped = untrailingslashit( $blog->domain . $blog->path ); + + /** + * If there is no primary domain or the unmapped version cannot be found + * then we return the value as-is. + */ + if ( empty( $primary ) || false === stripos( $value, $unmapped ) ) { + return $value; + } + + $domain = 'http' . ( $primary->is_https ? 's' : '' ) . '://' . $primary->domain; + + return preg_replace( "#https?://{$unmapped}#", $domain, $value ); + } + + /** + * Converts a URL from a mapped domain to the admin domain. This will only convert a URL which is the primary + * domain. + * + * @since 3.0.0 + * + * @param mixed $value Potentially a value containing the site's unmapped URL. + * @return mixed If unmapped URL is found, then returns the primary URL. Untouched otherwise. + */ + public function unmap( $value = '' ) { + /** + * Ensure that we are working with a string. + */ + if ( ! is_string( $value ) ) { + return $value; + } + + /** + * Retrieve the current blog. + */ + $blog = get_site(); + $primary = Primary::instance()->get(); + + /** + * If there is no primary domain or the primary domain cannot be found + * then we return the value as-is. + */ + if ( empty( $primary ) || false === stripos( $value, $primary->domain ) ) { + return $value; + } + + $unmapped = 'http' . ( $primary->is_https ? 's' : '' ) . '://' . untrailingslashit( $blog->domain . $blog->path ); + + return preg_replace( "#https?://{$primary->domain}#", $unmapped, $value ); + } + + /** + * Return the Singleton Instance of the class. + * + * @return Helper + */ + public static function instance() { + static $instance = false; + + if ( ! $instance ) { + $instance = new self(); + } + + return $instance; + } +} diff --git a/domain-mapping/classes/class-dm-database.php b/includes/classes/DomainMapping/Installer.php similarity index 79% rename from domain-mapping/classes/class-dm-database.php rename to includes/classes/DomainMapping/Installer.php index 752bbd60..944820e3 100644 --- a/domain-mapping/classes/class-dm-database.php +++ b/includes/classes/DomainMapping/Installer.php @@ -1,26 +1,29 @@ wpdb = $wpdb; - if ( defined( 'DM_NETWORK_MEDIA' ) && ! empty( DM_NETWORK_MEDIA ) ) { - $this->network_media = DM_NETWORK_MEDIA; + if ( defined( '\DM_NETWORK_MEDIA' ) && ! empty( \DM_NETWORK_MEDIA ) ) { + $this->network_media = \DM_NETWORK_MEDIA; } } @@ -70,11 +75,11 @@ public function __construct() { * @since 2.0.0 * * @param string $fqdn Fully qualified domain name. - * @return WP_Error|boolean True on pass. WP_Error on failure. + * @return \WP_Error|boolean True on pass. WP_Error on failure. */ private function _basic_check( $fqdn = '' ) { if ( empty( $fqdn ) ) { - return new WP_Error( 'empty', __( 'Please include a fully qualified domain name to be added.', 'dark-matter' ) ); + return new \WP_Error( 'empty', __( 'Please include a fully qualified domain name to be added.', 'dark-matter' ) ); } /** @@ -90,7 +95,7 @@ private function _basic_check( $fqdn = '' ) { } if ( ! empty( $domain_parts['path'] ) || ! empty( $domain_parts['port'] ) || ! empty( $domain_parts['query'] ) ) { - return new WP_Error( 'unsure', __( 'The domain provided contains path, port, or query string information. Please removed this before continuing.', 'dark-matter' ) ); + return new \WP_Error( 'unsure', __( 'The domain provided contains path, port, or query string information. Please removed this before continuing.', 'dark-matter' ) ); } $fqdn = $domain_parts['host']; @@ -99,20 +104,20 @@ private function _basic_check( $fqdn = '' ) { * Check to ensure we have a valid domain to work with. */ if ( ! filter_var( $fqdn, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME ) ) { - return new WP_Error( 'domain', __( 'The domain is not valid.', 'dark-matter' ) ); + return new \WP_Error( 'domain', __( 'The domain is not valid.', 'dark-matter' ) ); } if ( defined( 'DOMAIN_CURRENT_SITE' ) && DOMAIN_CURRENT_SITE === $fqdn ) { - return new WP_Error( 'wp-config', __( 'You cannot configure the WordPress Network primary domain.', 'dark-matter' ) ); + return new \WP_Error( 'wp-config', __( 'You cannot configure the WordPress Network primary domain.', 'dark-matter' ) ); } if ( is_main_site() ) { - return new WP_Error( 'root', __( 'Domains cannot be mapped to the main / root Site.', 'dark-matter' ) ); + return new \WP_Error( 'root', __( 'Domains cannot be mapped to the main / root Site.', 'dark-matter' ) ); } - $reserve = DarkMatter_Restrict::instance(); + $reserve = \DarkMatter\DomainMapping\Manager\Restricted::instance(); if ( $reserve->is_exist( $fqdn ) ) { - return new WP_Error( 'reserved', __( 'This domain has been reserved.', 'dark-matter' ) ); + return new \WP_Error( 'reserved', __( 'This domain has been reserved.', 'dark-matter' ) ); } /** @@ -153,7 +158,7 @@ private function _clear_caches( $fqdn = '' ) { * @param boolean $force Whether the update should be forced. * @param boolean $active Default is active. Set to false if you wish to add a domain but not make it active. * @param integer $type Domain type. Defaults to `1`, which is "main". - * @return DM_Domain|WP_Error DM_Domain on success. WP_Error on failure. + * @return Data\Domain|\WP_Error Domain on success. WP_Error on failure. */ public function add( $fqdn = '', $is_primary = false, $is_https = false, $force = true, $active = true, $type = DM_DOMAIN_TYPE_MAIN ) { $fqdn = $this->_basic_check( $fqdn ); @@ -166,10 +171,10 @@ public function add( $fqdn = '', $is_primary = false, $is_https = false, $force * Check that the FQDN is not already stored in the database. */ if ( $this->is_exist( $fqdn ) ) { - return new WP_Error( 'exists', __( 'This domain is already assigned to a Site.', 'dark-matter' ) ); + return new \WP_Error( 'exists', __( 'This domain is already assigned to a Site.', 'dark-matter' ) ); } - $dm_primary = DarkMatter_Primary::instance(); + $dm_primary = Primary::instance(); if ( $is_primary ) { $primary_domain = $dm_primary->get(); @@ -179,7 +184,7 @@ public function add( $fqdn = '', $is_primary = false, $is_https = false, $force */ if ( ! empty( $primary_domain ) ) { if ( ! $force ) { - return new WP_Error( 'primary', __( 'You cannot add this domain as the primary domain without using the force flag.', 'dark-matter' ) ); + return new \WP_Error( 'primary', __( 'You cannot add this domain as the primary domain without using the force flag.', 'dark-matter' ) ); } } } @@ -188,29 +193,29 @@ public function add( $fqdn = '', $is_primary = false, $is_https = false, $force * Check the type is valid. */ if ( DM_DOMAIN_TYPE_MAIN !== $type && DM_DOMAIN_TYPE_MEDIA !== $type ) { - return new WP_Error( 'type', __( 'The type for the new domain is not supported.', 'dark-matter' ) ); + return new \WP_Error( 'type', __( 'The type for the new domain is not supported.', 'dark-matter' ) ); } - $_domain = array( + $_domain = [ 'active' => ( ! $active ? false : true ), 'blog_id' => get_current_blog_id(), 'domain' => $fqdn, 'is_primary' => ( ! $is_primary ? false : true ), 'is_https' => ( ! $is_https ? false : true ), 'type' => ( ! empty( $type ) ? $type : DM_DOMAIN_TYPE_MAIN ), - ); + ]; $result = $this->wpdb->insert( $this->dm_table, $_domain, - array( + [ '%d', '%d', '%s', '%d', '%d', '%d', - ) + ] ); if ( $result ) { @@ -235,7 +240,7 @@ public function add( $fqdn = '', $is_primary = false, $is_https = false, $force $this->primary_set( $fqdn, get_current_blog_id() ); } - $dm_domain = new DM_Domain( (object) $_domain ); + $dm_domain = new Data\Domain( (object) $_domain ); $this->update_last_changed(); @@ -247,14 +252,14 @@ public function add( $fqdn = '', $is_primary = false, $is_https = false, $force * * @since 2.0.0 * - * @param DM_Domain $dm_domain Domain object of the newly added Domain. + * @param Data\Domain $dm_domain Domain object of the newly added Domain. */ do_action( 'darkmatter_domain_add', $dm_domain ); return $dm_domain; } - return new WP_Error( 'unknown', __( 'Sorry, the domain could not be added. An unknown error occurred.', 'dark-matter' ) ); + return new \WP_Error( 'unknown', __( 'Sorry, the domain could not be added. An unknown error occurred.', 'dark-matter' ) ); } /** @@ -322,7 +327,7 @@ public function clear_cache_for_domain_type( $type = DM_DOMAIN_TYPE_MAIN, $site_ * * @param string $fqdn FQDN to be deleted. * @param boolean $force Force the FQDN to be deleted, even if it is the primary domain. - * @return WP_Error|boolean True on success. False otherwise. + * @return \WP_Error|boolean True on success. False otherwise. */ public function delete( $fqdn = '', $force = true ) { $fqdn = $this->_basic_check( $fqdn ); @@ -335,7 +340,7 @@ public function delete( $fqdn = '', $force = true ) { * Cannot delete what does not exist. */ if ( ! $this->is_exist( $fqdn ) ) { - return new WP_Error( 'exists', __( 'The domain cannot be found.', 'dark-matter' ) ); + return new \WP_Error( 'exists', __( 'The domain cannot be found.', 'dark-matter' ) ); } /** @@ -344,7 +349,7 @@ public function delete( $fqdn = '', $force = true ) { $_domain = $this->get( $fqdn ); if ( ! $_domain || get_current_blog_id() !== $_domain->blog_id ) { - return new WP_Error( 'not found', __( 'The domain cannot be found.', 'dark-matter' ) ); + return new \WP_Error( 'not found', __( 'The domain cannot be found.', 'dark-matter' ) ); } /** @@ -355,7 +360,7 @@ public function delete( $fqdn = '', $force = true ) { if ( $force ) { $this->primary_unset( $_domain->domain, $_domain->blog_id ); } else { - return new WP_Error( 'primary', __( 'This domain is the primary domain for this Site. Please provide the force flag to delete.', 'dark-matter' ) ); + return new \WP_Error( 'primary', __( 'This domain is the primary domain for this Site. Please provide the force flag to delete.', 'dark-matter' ) ); } } @@ -381,14 +386,14 @@ public function delete( $fqdn = '', $force = true ) { * * @since 2.0.0 * - * @param DM_Domain $_domain Domain object that was deleted. + * @param Data\Domain $_domain Domain object that was deleted. */ do_action( 'darkmatter_domain_delete', $_domain ); return true; } - return new WP_Error( 'unknown', __( 'Sorry, the domain could not be deleted. An unknown error occurred.', 'dark-matter' ) ); + return new \WP_Error( 'unknown', __( 'Sorry, the domain could not be deleted. An unknown error occurred.', 'dark-matter' ) ); } @@ -398,7 +403,7 @@ public function delete( $fqdn = '', $force = true ) { * @since 2.0.0 * * @param string $fqdn FQDN to search for. - * @return DM_Domain|boolean Domain object. False on failure or not found. + * @return Data\Domain|boolean Domain object. False on failure or not found. */ public function find( $fqdn = '' ) { if ( empty( $fqdn ) ) { @@ -414,7 +419,7 @@ public function find( $fqdn = '' ) { * @since 2.0.0 * * @param string $fqdn FQDN to search for. - * @return DM_Domain|boolean Domain object. False otherwise. + * @return Data\Domain|boolean Domain object. False otherwise. */ public function get( $fqdn = '' ) { if ( empty( $fqdn ) ) { @@ -478,7 +483,7 @@ public function get( $fqdn = '' ) { /** * Return the DM_Domain object version. */ - return new DM_Domain( (object) $_domain ); + return new Data\Domain( (object) $_domain ); } /** @@ -515,7 +520,7 @@ public function get_domains_by_type( $type = DM_DOMAIN_TYPE_MEDIA, $site_id = 0, * Convert the domains into DM_Domain objects. */ foreach ( $this->network_media as $i => $media_domain ) { - $media_domains[ $i ] = new DM_Domain( + $media_domains[ $i ] = new Data\Domain( (object) [ 'active' => true, 'blog_id' => get_current_blog_id(), @@ -576,7 +581,7 @@ public function get_domains_by_type( $type = DM_DOMAIN_TYPE_MEDIA, $site_id = 0, /** * Retrieve the domain details, probably from cache, and get an array of `DM_Domain` objects. */ - $domains = array(); + $domains = []; foreach ( $_domains as $_domain ) { $domains[] = $this->get( $_domain ); @@ -615,7 +620,7 @@ public function get_domains( $site_id = 0 ) { /** * Retrieve the domain details from the cache. If the cache is */ - $domains = array(); + $domains = []; foreach ( $_domains as $_domain ) { $domains[] = $this->get( $_domain ); @@ -671,7 +676,7 @@ public function is_reserved( $fqdn = '' ) { * @return void */ private function primary_set( $domain = '', $blog_id = 0 ) { - $current_primary = DarkMatter_Primary::instance()->get( $blog_id ); + $current_primary = \DarkMatter\DomainMapping\Manager\Primary::instance()->get( $blog_id ); if ( ! empty( $current_primary ) && $domain !== $current_primary->domain ) { $this->update( $current_primary->domain, false, null, true, $current_primary->active ); @@ -748,7 +753,7 @@ public function reserve( $fqdn = '' ) { * @param boolean $force Whether the update should be forced. * @param boolean $active Default is active. Set to false if you wish to add a domain but not make it active. * @param integer $type Domain type. Defaults to `null`, do not change current value. Accepts `1` for main and `2` for Media. - * @return DM_Domain|WP_Error DM_Domain on success. WP_Error on failure. + * @return Data\Domain|\WP_Error DM_Domain on success. WP_Error on failure. */ public function update( $fqdn = '', $is_primary = null, $is_https = null, $force = true, $active = true, $type = null ) { $fqdn = $this->_basic_check( $fqdn ); @@ -760,16 +765,16 @@ public function update( $fqdn = '', $is_primary = null, $is_https = null, $force $domain_before = $this->get( $fqdn ); if ( ! $domain_before ) { - return new WP_Error( 'not found', __( 'Cannot find the domain to update.', 'dark-matter' ) ); + return new \WP_Error( 'not found', __( 'Cannot find the domain to update.', 'dark-matter' ) ); } - $dm_primary = DarkMatter_Primary::instance(); + $dm_primary = \DarkMatter\DomainMapping\Manager\Primary::instance(); - $_domain = array( + $_domain = [ 'active' => ( ! $active ? false : true ), 'blog_id' => $domain_before->blog_id, 'domain' => $fqdn, - ); + ]; /** * Determine if there is an attempt to update the "is primary" field. @@ -779,7 +784,7 @@ public function update( $fqdn = '', $is_primary = null, $is_https = null, $force * Any update to the "is primary" requires the force flag. */ if ( ! $force ) { - return new WP_Error( 'primary', __( 'You cannot update the primary flag without setting the force parameter to true', 'dark-matter' ) ); + return new \WP_Error( 'primary', __( 'You cannot update the primary flag without setting the force parameter to true', 'dark-matter' ) ); } $_domain['is_primary'] = $is_primary; @@ -796,16 +801,16 @@ public function update( $fqdn = '', $is_primary = null, $is_https = null, $force if ( DM_DOMAIN_TYPE_MAIN === $type || DM_DOMAIN_TYPE_MEDIA === $type ) { $_domain['type'] = $type; } else { - return new WP_Error( 'type', __( 'The type for the new domain is not supported.', 'dark-matter' ) ); + return new \WP_Error( 'type', __( 'The type for the new domain is not supported.', 'dark-matter' ) ); } } $result = $this->wpdb->update( $this->dm_table, $_domain, - array( + [ 'id' => $domain_before->id, - ) + ] ); if ( $result ) { @@ -841,7 +846,7 @@ public function update( $fqdn = '', $is_primary = null, $is_https = null, $force $this->primary_unset( $domain_before->domain, $domain_before->blog_id ); } - $domain_after = new DM_Domain( (object) $_domain ); + $domain_after = new Data\Domain( (object) $_domain ); $this->update_last_changed(); @@ -850,15 +855,15 @@ public function update( $fqdn = '', $is_primary = null, $is_https = null, $force * * @since 2.0.0 * - * @param DM_Domain $domain_after Domain object after the changes have been applied successfully. - * @param DM_Domain $domain_before Domain object before. + * @param Data\Domain $domain_after Domain object after the changes have been applied successfully. + * @param Data\Domain $domain_before Domain object before. */ do_action( 'darkmatter_domain_updated', $domain_after, $domain_before ); return $domain_after; } - return new WP_Error( 'unknown', __( 'Sorry, the domain could not be updated. An unknown error occurred.', 'dark-matter' ) ); + return new \WP_Error( 'unknown', __( 'Sorry, the domain could not be updated. An unknown error occurred.', 'dark-matter' ) ); } /** @@ -877,7 +882,7 @@ private function update_last_changed() { * * @since 2.0.0 * - * @return DarkMatter_Domains + * @return Domain */ public static function instance() { static $instance = false; diff --git a/domain-mapping/api/class-darkmatter-primary.php b/includes/classes/DomainMapping/Manager/Primary.php similarity index 85% rename from domain-mapping/api/class-darkmatter-primary.php rename to includes/classes/DomainMapping/Manager/Primary.php index 7379ba6d..90d75f1e 100644 --- a/domain-mapping/api/class-darkmatter-primary.php +++ b/includes/classes/DomainMapping/Manager/Primary.php @@ -1,21 +1,26 @@ get( $primary_domain ); return $_domain; @@ -118,7 +123,7 @@ public function get( $site_id = 0 ) { * * @since 2.0.0 * - * @return array Array of DM_Domain objects of the Primary domains for each Site in the Network. + * @return array Array of Domain objects of the Primary domains for each Site in the Network. */ public function get_all() { global $wpdb; @@ -130,7 +135,7 @@ public function get_all() { return array(); } - $db = DarkMatter_Domains::instance(); + $db = Domain::instance(); /** * Retrieve the DM_Domain objects for each of the primary domains. @@ -154,13 +159,13 @@ public function get_all() { * @return boolean True on success, false otherwise. */ public function set( $site_id = 0, $domain = '' ) { - $new_primary_domain = DarkMatter_Domains::instance()->get( $domain ); + $new_primary_domain = Domain::instance()->get( $domain ); if ( $new_primary_domain->blog_id !== $site_id ) { return false; } - $result = DarkMatter_Domains::instance()->update( + $result = Domain::instance()->update( $new_primary_domain->domain, true, $new_primary_domain->is_https, @@ -188,13 +193,13 @@ public function set( $site_id = 0, $domain = '' ) { * @return boolean True on success. False otherwise. */ public function unset( $site_id = 0, $domain = '', $db = false ) { - $new_primary_domain = DarkMatter_Domains::instance()->get( $domain ); + $new_primary_domain = Domain::instance()->get( $domain ); if ( $new_primary_domain->blog_id !== $site_id ) { return false; } - $result = DarkMatter_Domains::instance()->update( + $result = Domain::instance()->update( $new_primary_domain->domain, false, $new_primary_domain->is_https, @@ -226,7 +231,7 @@ private function update_last_changed() { * * @since 2.0.0 * - * @return DarkMatter_Primary + * @return Primary */ public static function instance() { static $instance = false; diff --git a/domain-mapping/api/class-darkmatter-restrict.php b/includes/classes/DomainMapping/Manager/Restricted.php similarity index 72% rename from domain-mapping/api/class-darkmatter-restrict.php rename to includes/classes/DomainMapping/Manager/Restricted.php index 5b8ee87e..9b0408a1 100644 --- a/domain-mapping/api/class-darkmatter-restrict.php +++ b/includes/classes/DomainMapping/Manager/Restricted.php @@ -1,19 +1,22 @@ is_exist( $fqdn ) ) { - return new WP_Error( 'used', __( 'This domain is in use.', 'dark-matter' ) ); + return new \WP_Error( 'used', __( 'This domain is in use.', 'dark-matter' ) ); } return $fqdn; @@ -82,7 +85,7 @@ private function _basic_checks( $fqdn ) { * @since 2.0.0 * * @param string $fqdn Domain to be added to the reserve list. - * @return WP_Error|boolean True on success, WP_Error otherwise. + * @return \WP_Error|boolean True on success, WP_Error otherwise. */ public function add( $fqdn = '' ) { $fqdn = $this->_basic_checks( $fqdn ); @@ -92,7 +95,7 @@ public function add( $fqdn = '' ) { } if ( $this->is_exist( $fqdn ) ) { - return new WP_Error( 'exists', __( 'The Domain is already Restricted.', 'dark-matter' ) ); + return new \WP_Error( 'exists', __( 'The Domain is already Restricted.', 'dark-matter' ) ); } /** @@ -100,8 +103,8 @@ public function add( $fqdn = '' ) { */ global $wpdb; - // phpcs:ignore - $result = $wpdb->insert( + // phpcs:ignore + $result = $wpdb->insert( $this->restrict_table, array( 'domain' => $fqdn, @@ -110,7 +113,7 @@ public function add( $fqdn = '' ) { ); if ( ! $result ) { - return new WP_Error( 'unknown', __( 'An unknown error has occurred. The domain has not been removed from the Restrict list.', 'dark-matter' ) ); + return new \WP_Error( 'unknown', __( 'An unknown error has occurred. The domain has not been removed from the Restrict list.', 'dark-matter' ) ); } $this->refresh_cache(); @@ -133,7 +136,7 @@ public function add( $fqdn = '' ) { * @since 2.0.0 * * @param string $fqdn Domain to be deleted to the restrict list. - * @return WP_Error|boolean True on success, WP_Error otherwise. + * @return \WP_Error|boolean True on success, WP_Error otherwise. */ public function delete( $fqdn = '' ) { $fqdn = $this->_basic_checks( $fqdn ); @@ -143,7 +146,7 @@ public function delete( $fqdn = '' ) { } if ( ! $this->is_exist( $fqdn ) ) { - return new WP_Error( 'missing', __( 'The Domain is not found in the Restrict list.', 'dark-matter' ) ); + return new \WP_Error( 'missing', __( 'The Domain is not found in the Restrict list.', 'dark-matter' ) ); } /** @@ -151,8 +154,8 @@ public function delete( $fqdn = '' ) { */ global $wpdb; - // phpcs:ignore - $result = $wpdb->delete( + // phpcs:ignore + $result = $wpdb->delete( $this->restrict_table, array( 'domain' => $fqdn, @@ -161,7 +164,7 @@ public function delete( $fqdn = '' ) { ); if ( ! $result ) { - return new WP_Error( 'unknown', __( 'An unknown error has occurred. The domain has not been removed from the Restrict list.', 'dark-matter' ) ); + return new \WP_Error( 'unknown', __( 'An unknown error has occurred. The domain has not been removed from the Restrict list.', 'dark-matter' ) ); } $this->refresh_cache(); @@ -211,8 +214,8 @@ public function get() { */ global $wpdb; - // phpcs:ignore - $restricted_domains = $wpdb->get_col( "SELECT domain FROM {$this->restrict_table} ORDER BY domain" ); + // phpcs:ignore + $restricted_domains = $wpdb->get_col( "SELECT domain FROM {$this->restrict_table} ORDER BY domain" ); if ( empty( $restricted_domains ) ) { $restricted_domains = array(); @@ -263,7 +266,7 @@ public function refresh_cache() { * * @since 2.0.0 * - * @return DarkMatter_Restrict + * @return Restricted */ public static function instance() { static $instance = false; diff --git a/domain-mapping/classes/class-dm-url.php b/includes/classes/DomainMapping/Processor/Mapping.php similarity index 77% rename from domain-mapping/classes/class-dm-url.php rename to includes/classes/DomainMapping/Processor/Mapping.php index 7d714fda..8f2c89b5 100644 --- a/domain-mapping/classes/class-dm-url.php +++ b/includes/classes/DomainMapping/Processor/Mapping.php @@ -1,19 +1,27 @@ is_request_mapped = ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ); + public function register() { + self::$is_request_mapped = ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING ); /** * In some circumstances, we always want to process the logic regardless of request type, circumstances, * conditions, etc. (like ensuring saved post data is unmapped properly). */ - add_filter( 'http_request_host_is_external', array( $this, 'is_external' ), 10, 2 ); - add_filter( 'wp_insert_post_data', array( $this, 'insert_post' ), -10, 1 ); + add_filter( 'http_request_host_is_external', [ $this, 'is_external' ], 10, 2 ); + add_filter( 'wp_insert_post_data', [ $this, 'insert_post' ], -10, 1 ); /** * Ensure we do not map on the login and register pages. @@ -45,7 +53,7 @@ public function __construct() { 'wp-login.php' => true, 'wp-register.php' => true, ]; - $filename = $this->get_request_filename(); + $filename = Helper::instance()->get_request_filename(); if ( ! empty( $filename ) && array_key_exists( $filename, $admin_filenames ) ) { /** * Ensure the "Go to [site name]" and Privacy Policy links still go to the mapped domain. @@ -85,8 +93,7 @@ public function __construct() { * archived or deleted. */ $blog = get_site(); - - if ( (int) $blog->public < 0 || '0' !== $blog->archived || '0' !== $blog->deleted ) { + if ( ! Helper::instance()->is_public( $blog ) ) { return; } @@ -97,13 +104,13 @@ public function __construct() { * the unmapped and mapped domain - like REST API and XMLRPC - will not * be properly detected for the rewrite rules. */ - add_action( 'muplugins_loaded', array( $this, 'prepare' ), -10 ); + add_action( 'muplugins_loaded', [ $this, 'prepare' ], -10 ); /** * Jetpack compatibility. This filter ensures that Jetpack gets the * correct domain for the home URL. */ - add_action( 'jetpack_sync_home_url', array( $this, 'map' ), 10, 1 ); + add_action( 'jetpack_sync_home_url', [ $this, 'map' ], 10, 1 ); } /** @@ -131,10 +138,10 @@ public function adminurl( $url = '', $path = '', $blog_id = 0 ) { return $url; } - $valid_paths = array( + $valid_paths = [ 'admin-ajax.php' => true, 'admin-post.php' => true, - ); + ]; if ( array_key_exists( $filename, $valid_paths ) ) { return $this->map( $url, $blog_id ); @@ -143,22 +150,6 @@ public function adminurl( $url = '', $path = '', $blog_id = 0 ) { return $url; } - /** - * Get the filename, if it has one, from the current request. - * - * @return string - */ - public function get_request_filename() { - $request_uri = ( empty( $_SERVER['REQUEST_URI'] ) ? '' : wp_unslash( wp_strip_all_tags( $_SERVER['REQUEST_URI'] ) ) ); - $request = ltrim( $request_uri, '/' ); - - /** - * Get the filename and remove any query strings. - */ - $filename = basename( $request ); - return strtok( $filename, '?' ); - } - /** * Checks to ensure that "mapped" domains are considered internal to WordPress and not external. * @@ -181,7 +172,7 @@ public function is_external( $external = false, $host = '' ) { * Attempt to find the domain in Dark Matter. If the domain is found, then tell WordPress it is an internal * domain. */ - $db = DarkMatter_Domains::instance(); + $db = Domain::instance(); $domain = $db->find( $host ); if ( is_a( $domain, 'DM_Domain' ) ) { @@ -226,8 +217,8 @@ private function is_mapped() { * the context can be mapped (i.e. it has an active primary domain) and if so, we say the request is mapped. */ global $switched; - if ( $switched && $this->is_request_mapped ) { - $primary = DarkMatter_Primary::instance()->get(); + if ( $switched && self::$is_request_mapped ) { + $primary = Primary::instance()->get(); /** * If there is no primary or if it is inactive, then the site is not mapped. @@ -239,7 +230,7 @@ private function is_mapped() { return true; } - return $this->is_request_mapped; + return self::$is_request_mapped; } /** @@ -253,32 +244,7 @@ private function is_mapped() { * @return string If unmapped URL is found, then returns the primary URL. Untouched otherwise. */ public function map( $value = '', $blog_id = 0 ) { - /** - * Ensure that we are working with a string. - */ - if ( ! is_string( $value ) ) { - return $value; - } - - /** - * Retrieve the current blog. - */ - $blog = get_site( absint( $blog_id ) ); - $primary = DarkMatter_Primary::instance()->get( $blog->blog_id ); - - $unmapped = untrailingslashit( $blog->domain . $blog->path ); - - /** - * If there is no primary domain or the unmapped version cannot be found - * then we return the value as-is. - */ - if ( empty( $primary ) || false === stripos( $value, $unmapped ) ) { - return $value; - } - - $domain = 'http' . ( $primary->is_https ? 's' : '' ) . '://' . $primary->domain; - - return preg_replace( "#https?://{$unmapped}#", $domain, $value ); + return Helper::instance()->map( $value, $blog_id ); } /** @@ -301,14 +267,14 @@ public function prepare() { * manipulating `post_content`, usually on the "normal" priority (10). For domain mapping, we want to ensure we * catch every thing after WordPress core and any plugins, so we run later in the process to achieve that. */ - add_filter( 'the_content', array( $this, 'map' ), 5, 1 ); + add_filter( 'the_content', [ $this, 'map' ], 5, 1 ); /** * Please note: the `$this->map()` method will check for unmapped URLs before committing to an regex replace. So * most of the time, this will go "nothing to do here". And the rest of time, catch any edge cases that produce * unmapped URLs. */ - add_filter( 'the_content', array( $this, 'map' ), 50, 1 ); + add_filter( 'the_content', [ $this, 'map' ], 50, 1 ); /** * We only wish to affect `the_content` for Previews and nothing else. @@ -334,7 +300,7 @@ public function prepare() { * action is always called here and not just when the request is called (as in previous versions of Dark * Matter). */ - add_action( 'rest_api_init', array( $this, 'prepare_rest' ) ); + add_action( 'rest_api_init', [ $this, 'prepare_rest' ] ); /** * We have to stop here for the REST API as the later filters and hooks can cause the REST API endpoints to 404 @@ -345,7 +311,7 @@ public function prepare() { } if ( is_admin() ) { - add_action( 'init', array( $this, 'prepare_admin' ) ); + add_action( 'init', [ $this, 'prepare_admin' ] ); return; } @@ -353,16 +319,16 @@ public function prepare() { * Every thing here is designed to ensure all URLs throughout WordPress * is consistent. This is the public serving / theme powered code. */ - add_filter( 'admin_url', array( $this, 'adminurl' ), -10, 3 ); - add_filter( 'home_url', array( $this, 'siteurl' ), -10, 4 ); - add_filter( 'site_url', array( $this, 'siteurl' ), -10, 4 ); - add_filter( 'content_url', array( $this, 'map' ), -10, 1 ); - add_filter( 'get_shortlink', array( $this, 'map' ), -10, 4 ); + add_filter( 'admin_url', [ $this, 'adminurl' ], -10, 3 ); + add_filter( 'home_url', [ $this, 'siteurl' ], -10, 4 ); + add_filter( 'site_url', [ $this, 'siteurl' ], -10, 4 ); + add_filter( 'content_url', [ $this, 'map' ], -10, 1 ); + add_filter( 'get_shortlink', [ $this, 'map' ], -10, 4 ); - add_filter( 'script_loader_tag', array( $this, 'map' ), -10, 4 ); - add_filter( 'style_loader_tag', array( $this, 'map' ), -10, 4 ); + add_filter( 'script_loader_tag', [ $this, 'map' ], -10, 4 ); + add_filter( 'style_loader_tag', [ $this, 'map' ], -10, 4 ); - add_filter( 'upload_dir', array( $this, 'upload' ), 10, 1 ); + add_filter( 'upload_dir', [ $this, 'upload' ], 10, 1 ); } /** @@ -375,7 +341,7 @@ public function prepare() { * @return void */ public function prepare_admin() { - add_filter( 'home_url', array( $this, 'siteurl' ), -10, 4 ); + add_filter( 'home_url', [ $this, 'siteurl' ], -10, 4 ); /** * The Preview link in the metabox of Post Publish cannot be handled by the home_url hook. This is because it @@ -384,13 +350,13 @@ public function prepare_admin() { * @link https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/meta-boxes.php#L57 Preview Metabox call to get Preview URL. * @link https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/link-template.php#L1311-L1312 Query string parameter "preview=true" being added to the URL. */ - add_filter( 'preview_post_link', array( $this, 'unmap' ), 10, 1 ); + add_filter( 'preview_post_link', [ $this, 'unmap' ], 10, 1 ); /** * To prepare the Classic Editor, we need to attach to a very late hook to ensure that `get_current_screen()` is * available and returns something useful. */ - add_action( 'edit_form_top', array( $this, 'prepare_classic_editor' ) ); + add_action( 'edit_form_top', [ $this, 'prepare_classic_editor' ] ); } /** @@ -401,7 +367,7 @@ public function prepare_classic_editor() { $screen = get_current_screen(); if ( is_a( $screen, 'WP_Screen' ) && 'post' === $screen->base && 'edit' === $screen->parent_base ) { - add_filter( 'the_editor_content', array( $this, 'map' ), 10, 1 ); + add_filter( 'the_editor_content', [ $this, 'map' ], 10, 1 ); } } @@ -413,9 +379,9 @@ public function prepare_classic_editor() { * @return void */ public function prepare_rest() { - add_filter( 'home_url', array( $this, 'siteurl' ), -10, 4 ); + add_filter( 'home_url', [ $this, 'siteurl' ], -10, 4 ); - add_filter( 'preview_post_link', array( $this, 'unmap' ), 10, 1 ); + add_filter( 'preview_post_link', [ $this, 'unmap' ], 10, 1 ); /** * Loop all post types with REST endpoints to fix the mapping for content.raw property. @@ -423,7 +389,7 @@ public function prepare_rest() { $rest_post_types = get_post_types( [ 'show_in_rest' => true ] ); foreach ( $rest_post_types as $post_type ) { - add_filter( "rest_prepare_{$post_type}", array( $this, 'prepare_rest_post_item' ), 10, 1 ); + add_filter( "rest_prepare_{$post_type}", [ $this, 'prepare_rest_post_item' ], 10, 1 ); } } @@ -431,8 +397,8 @@ public function prepare_rest() { * Ensures the "raw" version of the content, typically used by Gutenberg through it's middleware pre-load / JS * hydrate process, gets handled the same as content (which runs through the `the_content` hook). * - * @param WP_REST_Response $item Individual post / item in the response that is being processed. - * @return WP_REST_Response Post / item with the content.raw, if present, mapped. + * @param \WP_REST_Response $item Individual post / item in the response that is being processed. + * @return \WP_REST_Response Post / item with the content.raw, if present, mapped. */ public function prepare_rest_post_item( $item = null ) { if ( isset( $item->data['content']['raw'] ) ) { @@ -463,10 +429,10 @@ public function siteurl( $url = '', $path = '', $scheme = null, $blog_id = 0 ) { return $url; } - $valid_schemes = array( + $valid_schemes = [ 'http' => true, 'https' => true, - ); + ]; if ( ! is_admin() ) { $valid_schemes['json'] = true; @@ -527,30 +493,7 @@ public function siteurl( $url = '', $path = '', $scheme = null, $blog_id = 0 ) { * @return mixed If unmapped URL is found, then returns the primary URL. Untouched otherwise. */ public function unmap( $value = '' ) { - /** - * Ensure that we are working with a string. - */ - if ( ! is_string( $value ) ) { - return $value; - } - - /** - * Retrieve the current blog. - */ - $blog = get_site(); - $primary = DarkMatter_Primary::instance()->get(); - - /** - * If there is no primary domain or the primary domain cannot be found - * then we return the value as-is. - */ - if ( empty( $primary ) || false === stripos( $value, $primary->domain ) ) { - return $value; - } - - $unmapped = 'http' . ( $primary->is_https ? 's' : '' ) . '://' . untrailingslashit( $blog->domain . $blog->path ); - - return preg_replace( "#https?://{$primary->domain}#", $unmapped, $value ); + return Helper::instance()->unmap( $value ); } /** @@ -572,22 +515,4 @@ public function upload( $uploads ) { return $uploads; } - - /** - * Return the Singleton Instance of the class. - * - * @since 2.0.0 - * - * @return DM_URL - */ - public static function instance() { - static $instance = false; - - if ( ! $instance ) { - $instance = new self(); - } - - return $instance; - } } -DM_URL::instance(); diff --git a/domain-mapping/classes/class-dm-media.php b/includes/classes/DomainMapping/Processor/Media.php similarity index 88% rename from domain-mapping/classes/class-dm-media.php rename to includes/classes/DomainMapping/Processor/Media.php index 706b9bc5..2ae652d4 100644 --- a/domain-mapping/classes/class-dm-media.php +++ b/includes/classes/DomainMapping/Processor/Media.php @@ -1,20 +1,25 @@ get( $site_id ); + $primary = Primary::instance()->get( $site_id ); if ( ! empty( $primary ) ) { $main_domains[] = $primary->domain; } @@ -295,10 +300,10 @@ public function prepare_rest() { /** * Loop all post types with REST endpoints to fix the mapping for content.raw property. */ - $rest_post_types = get_post_types( array( 'show_in_rest' => true ) ); + $rest_post_types = get_post_types( [ 'show_in_rest' => true ] ); foreach ( $rest_post_types as $post_type ) { - add_filter( "rest_prepare_{$post_type}", array( $this, 'prepare_rest_post_item' ), 10, 1 ); + add_filter( "rest_prepare_{$post_type}", [ $this, 'prepare_rest_post_item' ], 10, 1 ); } } @@ -306,8 +311,8 @@ public function prepare_rest() { * Ensures the "raw" version of the content, typically used by Gutenberg through it's middleware pre-load / JS * hydrate process, gets handled the same as content (which runs through the `the_content` hook). * - * @param WP_REST_Response $item Individual post / item in the response that is being processed. - * @return WP_REST_Response Post / item with the content.raw, if present, mapped. + * @param \WP_REST_Response $item Individual post / item in the response that is being processed. + * @return \WP_REST_Response Post / item with the content.raw, if present, mapped. * * @since 2.2.0 */ @@ -344,7 +349,7 @@ private function prime_site( $site_id = 0 ) { /** * Ensure we have media domains to use. */ - $media_domains = DarkMatter_Domains::instance()->get_domains_by_type( DM_DOMAIN_TYPE_MEDIA, $site_id ); + $media_domains = Domain::instance()->get_domains_by_type( DM_DOMAIN_TYPE_MEDIA, $site_id ); if ( empty( $media_domains ) ) { $this->sites[ $site_id ] = false; return; @@ -421,22 +426,4 @@ public function unmap( $value = '' ) { $value ); } - - /** - * Return the Singleton Instance of the class. - * - * @return DM_Media - * - * @since 2.2.0 - */ - public static function instance() { - static $instance = false; - - if ( ! $instance ) { - $instance = new self(); - } - - return $instance; - } } -DM_Media::instance(); diff --git a/includes/classes/DomainMapping/Processor/Redirect.php b/includes/classes/DomainMapping/Processor/Redirect.php new file mode 100644 index 00000000..3e4bd85f --- /dev/null +++ b/includes/classes/DomainMapping/Processor/Redirect.php @@ -0,0 +1,204 @@ + over the unmapped domain. + */ + ! empty( $_GET['customize_changeset_uuid'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended + || + /** + * Do not redirect Previews + */ + ( ! empty( $_GET['preview'] ) || ! empty( $_GET['page_id'] ) || ! empty( $_GET['p'] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended + ); + } + + /** + * Check to see if the current request is an Admin Post action or an AJAX action. These two requests in Dark Matter + * can be on either the admin domain or the primary domain. + * + * @since 3.0.0 + * + * @param string $filename Filename. + * @return bool True if request is AJAX, false otherwise. + */ + private function is_ajax( $filename = '' ) { + $ajax_filenames = [ + 'admin-post.php' => true, + 'admin-ajax.php' => true, + ]; + + + if ( ! empty( $filename ) && array_key_exists( $filename, $ajax_filenames ) ) { + return true; + } + + return false; + } + + /** + * Performs various checks and, if required, performs the redirect. + * + * @since 3.0.0 + * + * @return void + */ + public function maybe_redirect() { + /** + * Do not perform redirects if it is the main site or the site is _not_ public. + * + * Note: this is here inside the caller on `muplugins_loaded` as earlier is before the function is available for + * use. + */ + $original_blog = get_site(); + if ( is_main_site() || ! Helper::instance()->is_public( $original_blog ) ) { + return; + } + + $filename = Helper::instance()->get_request_filename(); + if ( $this->is_ajax( $filename ) ) { + return; + } + + $is_admin = Helper::instance()->is_admin( $filename ); + $host = Helper::instance()->get_request_fqdn(); + + /** + * Check if logins are allowed on mapped domains, as we shouldn't redirect here if it is allowed. + */ + if ( ! apply_filters( 'darkmatter_allow_logins', false ) && $is_admin && $host === $original_blog->domain ) { + return; + } + + /** + * Check we have a primary domain that we can, maybe, redirect to. + */ + $primary = Primary::instance()->get(); + if ( ! $primary || ! $primary->active ) { + return; + } + + /** + * Get the request so we can use it to build up the redirect URL. + */ + $request_uri = ( empty( $_SERVER['REQUEST_URI'] ) ? '' : filter_var( $_SERVER['REQUEST_URI'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW ) ); + $request = ltrim( $request_uri, '/' ); + + /** + * Final set of checks. Make sure we redirect were appropriate here, both for the admin side/admin domain and + * the public side/primary domain. + */ + if ( $is_admin && $host !== $original_blog->domain ) { + $is_ssl_admin = ( defined( 'FORCE_SSL_ADMIN' ) && FORCE_SSL_ADMIN ); + + $url = 'http' . ( $is_ssl_admin ? 's' : '' ) . '://' . $original_blog->domain . $original_blog->path . $request; + } elseif ( $host !== $primary->domain || is_ssl() !== $primary->is_https ) { + $url = 'http' . ( $primary->is_https ? 's' : '' ) . '://' . $primary->domain . '/' . $request; + + /** + * Make sure the Path - if this is a sub-folder Network - is removed from the URL. For subdomain Networks, + * the path will be a single forward slash (/). + */ + if ( '/' !== $original_blog->path ) { + $path = '/' . trim( $original_blog->path, '/' ) . '/'; + $url = str_ireplace( $path, '/', $url ); + } + } + + /** + * If the URL is empty, then there is no redirect to perform. + */ + if ( empty( $url ) ) { + return; + } + + header( 'X-Redirect-By: Dark-Matter-Plugin' ); + header( 'Location:' . $url, true, 301 ); + + die; + } + + /** + * Register hooks for this class. + * + * @since 3.0.0 + * + * @return void + */ + public function register() { + if ( $this->can_redirect() ) { + /** + * We use `muplugins_loaded` action (introduced in WordPress 2.8.0) rather than the "ms_loaded" (introduced + * in WordPress 4.6.0). + * + * A hook on `muplugins_loaded` is used to ensure that WordPress has loaded the Blog/Site globals. This is + * specifically useful when someone goes to the Admin domain URL - http://my.sites.com/two/ - which is to + * redirect to the primary domain - http://example.com. + */ + add_action( 'muplugins_loaded', [ $this, 'maybe_redirect' ], 20 ); + } + } +} diff --git a/domain-mapping/rest/class-dm-rest-domains-controller.php b/includes/classes/DomainMapping/REST/Domains.php similarity index 70% rename from domain-mapping/rest/class-dm-rest-domains-controller.php rename to includes/classes/DomainMapping/REST/Domains.php index cdfcef5c..e3122385 100644 --- a/domain-mapping/rest/class-dm-rest-domains-controller.php +++ b/includes/classes/DomainMapping/REST/Domains.php @@ -1,17 +1,25 @@ prepare_item_for_database( $request ); @@ -62,7 +70,7 @@ public function create_item( $request ) { * * @since 2.0.0 * - * @param WP_REST_Request $request Current request. + * @param \WP_REST_Request $request Current request. * @return boolean True if the current user is a Super Admin. False otherwise. */ public function create_item_permissions_check( $request ) { @@ -75,11 +83,11 @@ public function create_item_permissions_check( $request ) { * * @since 2.0.0 * - * @param WP_REST_Request $request Current request. - * @return WP_REST_Response|mixed WP_REST_Response on success. WP_Error on failure. + * @param \WP_REST_Request $request Current request. + * @return \WP_REST_Response|mixed WP_REST_Response on success. WP_Error on failure. */ public function delete_item( $request ) { - $db = DarkMatter_Domains::instance(); + $db = Manager\Domain::instance(); $result = $db->delete( $request['domain'], $request['force'] ); @@ -109,7 +117,7 @@ public function delete_item( $request ) { * * @since 2.0.0 * - * @param WP_REST_Request $request Current request. + * @param \WP_REST_Request $request Current request. * @return boolean True if the current user is a Super Admin. False otherwise. */ public function delete_item_permissions_check( $request ) { @@ -122,11 +130,11 @@ public function delete_item_permissions_check( $request ) { * * @since 2.0.0 * - * @param WP_REST_Request $request Current request. - * @return WP_REST_Response|mixed WP_REST_Response on success. WP_Error on failure. + * @param \WP_REST_Request $request Current request. + * @return \WP_REST_Response|mixed WP_REST_Response on success. WP_Error on failure. */ public function get_item( $request ) { - $db = DarkMatter_Domains::instance(); + $db = Manager\Domain::instance(); $result = $db->get( $request['domain'] ); @@ -158,136 +166,136 @@ public function get_item_schema() { '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'Domain', 'type' => 'object', - 'properties' => array( - 'id' => array( - 'context' => array( 'view', 'edit' ), + 'properties' => [ + 'id' => [ + 'context' => [ 'view', 'edit' ], 'description' => __( 'Unique identifier for the object.', 'dark-matter' ), 'readonly' => true, 'type' => 'integer', - ), - 'domain' => array( - 'context' => array( 'view', 'edit' ), + ], + 'domain' => [ + 'context' => [ 'view', 'edit' ], 'default' => '', 'description' => __( 'Domain name.', 'dark-matter' ), 'required' => true, 'type' => 'string', - ), - 'is_primary' => array( - 'context' => array( 'view', 'edit' ), + ], + 'is_primary' => [ + 'context' => [ 'view', 'edit' ], 'default' => null, 'description' => __( 'Domain is the primary domain for the Site.', 'dark-matter' ), 'required' => false, 'type' => 'boolean', - ), - 'is_active' => array( - 'context' => array( 'view', 'edit' ), + ], + 'is_active' => [ + 'context' => [ 'view', 'edit' ], 'default' => null, 'description' => __( 'Domain is currently being used.', 'dark-matter' ), 'required' => false, 'type' => 'boolean', - ), - 'is_https' => array( - 'context' => array( 'view', 'edit' ), + ], + 'is_https' => [ + 'context' => [ 'view', 'edit' ], 'default' => null, 'description' => __( 'Domain is to be available on the HTTPS protocol.', 'dark-matter' ), 'required' => false, 'type' => 'boolean', - ), - 'type' => array( - 'context' => array( 'view', 'edit' ), + ], + 'type' => [ + 'context' => [ 'view', 'edit' ], 'default' => null, 'description' => __( 'Type of domain.', 'dark-matter' ), 'required' => false, 'type' => 'integer', - ), - 'site' => array( + ], + 'site' => [ 'description' => __( 'Site ID the domain is assigned against.', 'dark-matter' ), 'type' => 'object', - 'context' => array( 'view', 'edit' ), + 'context' => [ 'view', 'edit' ], 'readonly' => true, - 'properties' => array( - 'blog_id' => array( - 'context' => array( 'view', 'edit' ), + 'properties' => [ + 'blog_id' => [ + 'context' => [ 'view', 'edit' ], 'description' => __( 'Site ID.', 'dark-matter' ), 'readonly' => true, 'required' => false, 'type' => 'integer', - ), - 'site_id' => array( - 'context' => array( 'view', 'edit' ), + ], + 'site_id' => [ + 'context' => [ 'view', 'edit' ], 'description' => __( 'The ID of the site\'s parent network.', 'dark-matter' ), 'readonly' => true, 'required' => false, 'type' => 'integer', - ), - 'domain' => array( - 'context' => array( 'view', 'edit' ), + ], + 'domain' => [ + 'context' => [ 'view', 'edit' ], 'description' => __( 'Domain of the site.', 'dark-matter' ), 'readonly' => true, 'required' => false, 'type' => 'string', - ), - 'path' => array( - 'context' => array( 'view', 'edit' ), + ], + 'path' => [ + 'context' => [ 'view', 'edit' ], 'description' => __( 'Path of the site.', 'dark-matter' ), 'readonly' => true, 'required' => false, 'type' => 'string', - ), - 'registered' => array( - 'context' => array( 'view', 'edit' ), + ], + 'registered' => [ + 'context' => [ 'view', 'edit' ], 'description' => __( 'The date on which the site was created or registered.', 'dark-matter' ), 'format' => 'date-time', 'readonly' => true, 'required' => false, 'type' => 'string', - ), - 'last_updated' => array( - 'context' => array( 'view', 'edit' ), + ], + 'last_updated' => [ + 'context' => [ 'view', 'edit' ], 'description' => __( 'The date and time on which site settings were last updated.', 'dark-matter' ), 'format' => 'date-time', 'readonly' => true, 'required' => false, 'type' => 'string', - ), - 'public' => array( - 'context' => array( 'view', 'edit' ), + ], + 'public' => [ + 'context' => [ 'view', 'edit' ], 'description' => __( 'Whether the site should be treated as public.', 'dark-matter' ), 'readonly' => true, 'required' => false, 'type' => 'integer', - ), - 'archived' => array( - 'context' => array( 'view', 'edit' ), + ], + 'archived' => [ + 'context' => [ 'view', 'edit' ], 'description' => __( 'Whether the site should be treated as archived.', 'dark-matter' ), 'readonly' => true, 'required' => false, 'type' => 'boolean', - ), - 'mature' => array( - 'context' => array( 'view', 'edit' ), + ], + 'mature' => [ + 'context' => [ 'view', 'edit' ], 'description' => __( 'Whether the site should be treated as mature.', 'dark-matter' ), 'readonly' => true, 'required' => false, 'type' => 'boolean', - ), - 'spam' => array( - 'context' => array( 'view', 'edit' ), + ], + 'spam' => [ + 'context' => [ 'view', 'edit' ], 'description' => __( 'Whether the site should be treated as spam.', 'dark-matter' ), 'readonly' => true, 'required' => false, 'type' => 'boolean', - ), - 'deleted' => array( - 'context' => array( 'view', 'edit' ), + ], + 'deleted' => [ + 'context' => [ 'view', 'edit' ], 'description' => __( 'Whether the site should be treated as deleted.', 'dark-matter' ), 'readonly' => true, 'required' => false, 'type' => 'boolean', - ), - ), - ), - ), + ], + ], + ], + ], ); return $schema; @@ -298,8 +306,8 @@ public function get_item_schema() { * * @since 2.0.0 * - * @param WP_REST_Request $request Current request. - * @return WP_REST_Response|mixed WP_REST_Response on success. WP_Error on failure. + * @param \WP_REST_Request $request Current request. + * @return \WP_REST_Response|mixed WP_REST_Response on success. WP_Error on failure. */ public function get_items( $request ) { $site_id = null; @@ -316,9 +324,9 @@ public function get_items( $request ) { $site_id = get_current_blog_id(); } - $db = DarkMatter_Domains::instance(); + $db = Manager\Domain::instance(); - $response = array(); + $response = []; $result = $db->get_domains( $site_id ); @@ -345,7 +353,7 @@ public function get_items( $request ) { * * @since 2.0.0 * - * @param WP_REST_Request $request Current request. + * @param \WP_REST_Request $request Current request. * @return boolean True if the current user is a Super Admin. False otherwise. */ public function get_items_permissions_check( $request ) { @@ -358,17 +366,17 @@ public function get_items_permissions_check( $request ) { * * @since 2.0.0 * - * @param WP_REST_Request $request Current request. + * @param \WP_REST_Request $request Current request. * @return array Data provided by the call to the endpoint. */ protected function prepare_item_for_database( $request ) { - $item = array( + $item = [ 'domain' => '', 'is_primary' => null, 'is_https' => null, 'is_active' => null, 'type' => null, - ); + ]; $method = $request->get_method(); @@ -379,15 +387,15 @@ protected function prepare_item_for_database( $request ) { $value = $request[ $key ]; } - if ( WP_REST_Server::CREATABLE === $method && null === $value && 'is_primary' === $key ) { + if ( \WP_REST_Server::CREATABLE === $method && null === $value && 'is_primary' === $key ) { $value = false; } - if ( WP_REST_Server::CREATABLE === $method && null === $value && 'is_https' === $key ) { + if ( \WP_REST_Server::CREATABLE === $method && null === $value && 'is_https' === $key ) { $value = false; } - if ( WP_REST_Server::CREATABLE === $method && null === $value && 'is_active' === $key ) { + if ( \WP_REST_Server::CREATABLE === $method && null === $value && 'is_active' === $key ) { $value = true; } @@ -402,14 +410,14 @@ protected function prepare_item_for_database( $request ) { * * @since 2.0.0 * - * @param DM_Domain $item Domain object to be prepared for response. - * @param WP_REST_Request $request Current request. + * @param Data\Domain $item Domain object to be prepared for response. + * @param \WP_REST_Request $request Current request. * @return array Prepared item for REST response. */ public function prepare_item_for_response( $item, $request ) { $fields = $this->get_fields_for_response( $request ); - $data = array(); + $data = []; if ( in_array( 'id', $fields, true ) ) { $data['id'] = $item->id; @@ -479,81 +487,81 @@ public function register_routes() { register_rest_route( $this->namespace, $this->rest_base, - array( - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'create_item' ), - 'permission_callback' => array( $this, 'create_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), - ) + [ + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => [ $this, 'create_item' ], + 'permission_callback' => [ $this, 'create_item_permissions_check' ], + 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ), + ] ); register_rest_route( $this->namespace, $this->rest_base . '/(?P