662 lines
14 KiB
PHP
662 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* The Domain model for the Domain Mappings.
|
|
*
|
|
* @package WP_Ultimo
|
|
* @subpackage Models
|
|
* @since 2.0.0
|
|
*/
|
|
|
|
namespace WP_Ultimo\Models;
|
|
|
|
use WP_Ultimo\Models\Base_Model;
|
|
use WP_Ultimo\Domain_Mapping\Helper;
|
|
use WP_Ultimo\Database\Domains\Domain_Stage;
|
|
use WP_Ultimo\Models\Site;
|
|
|
|
// Exit if accessed directly
|
|
defined('ABSPATH') || exit;
|
|
|
|
/**
|
|
* Domain model class. Implements the Base Model.
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
class Domain extends Base_Model {
|
|
|
|
/**
|
|
* Blog ID of the site associated with this domain.
|
|
*
|
|
* @since 2.0.0
|
|
* @var integer
|
|
*/
|
|
protected $blog_id;
|
|
|
|
/**
|
|
* The domain name mapped.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $domain = '';
|
|
|
|
/**
|
|
* Is this domain active?
|
|
*
|
|
* @since 2.0.0
|
|
* @var boolean
|
|
*/
|
|
protected $active = true;
|
|
|
|
/**
|
|
* Is this a primary_domain? Requests to other mapped domains will resolve to the primary.
|
|
*
|
|
* @since 2.0.0
|
|
* @var boolean
|
|
*/
|
|
protected $primary_domain = false;
|
|
|
|
/**
|
|
* Should this domain be forced to be used only on HTTPS?
|
|
*
|
|
* @since 2.0.0
|
|
* @var boolean
|
|
*/
|
|
protected $secure = false;
|
|
|
|
/**
|
|
* Stages of domain mapping
|
|
*
|
|
* - checking-dns
|
|
* - checking-ssl-cert
|
|
* - done
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $stage = 'checking-dns';
|
|
|
|
/**
|
|
* Date when this was created.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $date_created;
|
|
|
|
/**
|
|
* List of stages that should force the domain to an inactive status.
|
|
*
|
|
* @since 2.0.0
|
|
* @var array
|
|
*/
|
|
const INACTIVE_STAGES = [
|
|
'checking-dns',
|
|
'checking-ssl-cert',
|
|
'failed',
|
|
];
|
|
|
|
/**
|
|
* Query Class to the static query methods.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $query_class = \WP_Ultimo\Database\Domains\Domain_Query::class;
|
|
|
|
/**
|
|
* Set the validation rules for this particular model.
|
|
*
|
|
* To see how to setup rules, check the documentation of the
|
|
* validation library we are using: https://github.com/rakit/validation
|
|
*
|
|
* @since 2.0.0
|
|
* @link https://github.com/rakit/validation
|
|
* @return array
|
|
*/
|
|
public function validation_rules() {
|
|
|
|
$id = $this->get_id();
|
|
|
|
return [
|
|
'blog_id' => 'required|integer',
|
|
'domain' => "required|domain|unique:\WP_Ultimo\Models\Domain,domain,{$id}",
|
|
'stage' => 'required|in:checking-dns,checking-ssl-cert,done-without-ssl,done,failed|default:checking-dns',
|
|
'active' => 'default:1',
|
|
'secure' => 'default:0',
|
|
'primary_domain' => 'default:0',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Returns the domain address mapped.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_domain() {
|
|
|
|
return $this->domain;
|
|
}
|
|
|
|
/**
|
|
* Sets the domain of this model object;
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param string $domain Your Domain name. You don't need to put http or https in front of your domain in this field. e.g: example.com.
|
|
* @return void
|
|
*/
|
|
public function set_domain($domain): void {
|
|
|
|
$this->domain = strtolower($domain);
|
|
}
|
|
|
|
/**
|
|
* Gets the URL with schema and all.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param string $path The path to add to the end of the url.
|
|
*/
|
|
public function get_url($path = ''): string {
|
|
|
|
$schema = $this->is_secure() ? 'https://' : 'http://';
|
|
|
|
return sprintf('%s%s/%s', $schema, $this->get_domain(), $path);
|
|
}
|
|
|
|
/**
|
|
* Get the ID of the corresponding site.
|
|
*
|
|
* @access public
|
|
* @since 2.0
|
|
* @return int
|
|
*/
|
|
public function get_blog_id() {
|
|
|
|
return (int) $this->blog_id;
|
|
}
|
|
|
|
/**
|
|
* Sets the blog_id of this model object;
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param int $blog_id The blog ID attached to this domain.
|
|
* @return void
|
|
*/
|
|
public function set_blog_id($blog_id): void {
|
|
|
|
$this->blog_id = $blog_id;
|
|
}
|
|
|
|
/**
|
|
* Get the ID of the corresponding site.
|
|
*
|
|
* @since 2.0.0
|
|
* @return int
|
|
*/
|
|
public function get_site_id() {
|
|
|
|
return $this->get_blog_id();
|
|
}
|
|
|
|
/**
|
|
* Get the site object for this particular mapping.
|
|
*
|
|
* @since 2.0.0
|
|
* @return \WP_Site|\WP_Ultimo\Models\Site|false
|
|
*/
|
|
public function get_site() {
|
|
|
|
/**
|
|
* In a domain mapping environment, the user is not yet logged in.
|
|
* This means that we can't use BerlinDB, unfortunately, as it uses the user caps
|
|
* to decide which fields to make available.
|
|
*
|
|
* To bypass this limitation, we use the default WordPress function on those cases.
|
|
*/
|
|
if ( ! function_exists('current_user_can')) {
|
|
return \WP_Site::get_instance($this->get_blog_id());
|
|
}
|
|
|
|
return wu_get_site($this->get_blog_id());
|
|
}
|
|
|
|
/**
|
|
* Check if this particular mapping is active.
|
|
*
|
|
* @since 2.0.0
|
|
* @return boolean
|
|
*/
|
|
public function is_active() {
|
|
|
|
if ($this->has_inactive_stage()) {
|
|
return false;
|
|
}
|
|
|
|
return (bool) $this->active;
|
|
}
|
|
|
|
/**
|
|
* Sets the active state of this model object;
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param boolean $active Set this domain as active (true), which means available to be used, or inactive (false).
|
|
* @return void
|
|
*/
|
|
public function set_active($active): void {
|
|
|
|
$this->active = $active;
|
|
}
|
|
|
|
/**
|
|
* Check if this is a primary domain.
|
|
*
|
|
* @since 2.0.0
|
|
* @return boolean
|
|
*/
|
|
public function is_primary_domain() {
|
|
|
|
return (bool) $this->primary_domain;
|
|
}
|
|
|
|
/**
|
|
* Sets the primary_domain state of this model object;
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param boolean $primary_domain Define true to set this as primary domain of a site, meaning it's the main url, or set false.
|
|
* @return void
|
|
*/
|
|
public function set_primary_domain($primary_domain): void {
|
|
|
|
$this->primary_domain = $primary_domain;
|
|
}
|
|
|
|
/**
|
|
* Check if we should use this domain securely (via HTTPS).
|
|
*
|
|
* @since 2.0.0
|
|
* @return boolean
|
|
*/
|
|
public function is_secure() {
|
|
|
|
return (bool) $this->secure;
|
|
}
|
|
|
|
/**
|
|
* Sets the secure state of this model object;
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param boolean $secure If this domain has some SSL security or not.
|
|
* @return void
|
|
*/
|
|
public function set_secure($secure): void {
|
|
|
|
$this->secure = $secure;
|
|
}
|
|
|
|
/**
|
|
* Get the stage in which this domain is in at the moment.
|
|
*
|
|
* This is used to check the stage of the domain lifecycle.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_stage() {
|
|
|
|
return $this->stage;
|
|
}
|
|
|
|
/**
|
|
* Sets the stage of this model object;
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param string $stage The state of the domain model object. Can be one of this options: checking-dns, checking-ssl-cert, done-without-ssl, done and failed.
|
|
* @return void
|
|
*/
|
|
public function set_stage($stage): void {
|
|
|
|
$this->stage = $stage;
|
|
}
|
|
|
|
/**
|
|
* Check if this domain is on a inactive stage.
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
public function has_inactive_stage(): bool {
|
|
|
|
return in_array($this->get_stage(), self::INACTIVE_STAGES, true);
|
|
}
|
|
|
|
/**
|
|
* Returns the Label for a given stage level.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_stage_label() {
|
|
|
|
$type = new Domain_Stage($this->get_stage());
|
|
|
|
return $type->get_label();
|
|
}
|
|
|
|
/**
|
|
* Gets the classes for a given stage level.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_stage_class() {
|
|
|
|
$type = new Domain_Stage($this->get_stage());
|
|
|
|
return $type->get_classes();
|
|
}
|
|
|
|
/**
|
|
* Get date when this was created.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_date_created() {
|
|
|
|
return $this->date_created;
|
|
}
|
|
|
|
/**
|
|
* Set date when this was created.
|
|
*
|
|
* @since 2.0.0
|
|
* @param string $date_created Date when the domain was created. If no date is set, the current date and time will be used.
|
|
* @return void
|
|
*/
|
|
public function set_date_created($date_created): void {
|
|
|
|
$this->date_created = $date_created;
|
|
}
|
|
|
|
/**
|
|
* Check if the domain is correctly set-up in terms of DNS resolution.
|
|
*
|
|
* @since 2.0.0
|
|
* @return boolean
|
|
*/
|
|
public function has_correct_dns() {
|
|
|
|
global $current_site;
|
|
|
|
$domain_url = $this->get_domain();
|
|
|
|
$network_ip_address = Helper::get_network_public_ip();
|
|
|
|
$results = \WP_Ultimo\Managers\Domain_Manager::dns_get_record($domain_url);
|
|
|
|
$domains_and_ips = array_column($results, 'data');
|
|
|
|
if (in_array($current_site->domain, $domains_and_ips, true)) {
|
|
return true;
|
|
}
|
|
|
|
if (in_array($network_ip_address, $domains_and_ips, true)) {
|
|
return true;
|
|
}
|
|
|
|
$result = false;
|
|
|
|
/**
|
|
* Allow plugin developers to add new checks in order to define the results.
|
|
*
|
|
* @since 2.0.4
|
|
* @param bool $result the current result.
|
|
* @param self $this The current domain instance.
|
|
* @param array $domains_and_ips The list of domains and IPs found on the DNS lookup.
|
|
* @return bool If the DNS is correctly setup or not.
|
|
*/
|
|
$result = apply_filters('wu_domain_has_correct_dns', $result, $this, $domains_and_ips);
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Checks if the current domain has a valid SSL certificate that covers it.
|
|
*
|
|
* @since 2.0.0
|
|
* @return boolean
|
|
*/
|
|
public function has_valid_ssl_certificate() {
|
|
|
|
return Helper::has_valid_ssl_certificate($this->get_domain());
|
|
}
|
|
|
|
/**
|
|
* Save (create or update) the model on the database.
|
|
*
|
|
* Needs to override the parent implementation
|
|
* to clear the cache.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function save() {
|
|
|
|
$new_domain = $this->exists();
|
|
|
|
$before_changes = clone $this;
|
|
|
|
$results = parent::save();
|
|
|
|
if (is_wp_error($results) === false) {
|
|
if ($new_domain) {
|
|
if (has_action('mercator.mapping.created')) {
|
|
$deprecated_args = [
|
|
$this,
|
|
];
|
|
|
|
/**
|
|
* Deprecated: Mercator created domain.
|
|
*
|
|
* @since 2.0.0
|
|
* @param self The domain object after saving.
|
|
* @param self The domain object before the changes.
|
|
* @return void.
|
|
*/
|
|
do_action_deprecated('mercator.mapping.created', $deprecated_args, '2.0.0', 'wu_domain_post_save');
|
|
}
|
|
} elseif (has_action('mercator.mapping.updated')) {
|
|
$deprecated_args = [
|
|
$this,
|
|
$before_changes,
|
|
];
|
|
|
|
/**
|
|
* Deprecated: Mercator updated domain.
|
|
*
|
|
* @since 2.0.0
|
|
* @param self The domain object after saving.
|
|
* @param self The domain object before the changes.
|
|
* @return void.
|
|
*/
|
|
do_action_deprecated('mercator.mapping.updated', $deprecated_args, '2.0.0', 'wu_domain_post_save');
|
|
}
|
|
|
|
/*
|
|
* Resets cache.
|
|
*
|
|
* This will make sure the list of domains gets rebuild
|
|
* after a change is made.
|
|
*/
|
|
wp_cache_flush();
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Delete the model from the database.
|
|
*
|
|
* @since 2.0.0
|
|
* @return \WP_Error|bool
|
|
*/
|
|
public function delete() {
|
|
|
|
$results = parent::delete();
|
|
|
|
if (is_wp_error($results) === false && has_action('mercator.mapping.deleted')) {
|
|
$deprecated_args = [
|
|
$this,
|
|
];
|
|
|
|
/**
|
|
* Deprecated: Mercator Deleted domain.
|
|
*
|
|
* @since 2.0.0
|
|
* @param self The domain object just deleted.
|
|
* @return void.
|
|
*/
|
|
do_action_deprecated('mercator.mapping.deleted', $deprecated_args, '2.0.0', 'wu_domain_post_delete');
|
|
}
|
|
|
|
/*
|
|
* Delete log file.
|
|
*/
|
|
wu_log_clear("domain-{$this->get_domain()}");
|
|
|
|
wu_log_add("domain-{$this->get_domain()}", __('Domain deleted and logs cleared...', 'wp-multisite-waas'));
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Get mapping by site ID
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param int|stdClass $site Site ID, or site object from {@see get_blog_details}.
|
|
* @return Domain|Domain[]|\WP_Error|false Mapping on success, WP_Error if error occurred, or false if no mapping found.
|
|
*/
|
|
public static function get_by_site($site) {
|
|
|
|
global $wpdb;
|
|
|
|
// Allow passing a site object in
|
|
if (is_object($site) && isset($site->blog_id)) {
|
|
$site = $site->blog_id;
|
|
}
|
|
|
|
if ( ! is_numeric($site)) {
|
|
return new \WP_Error('wu_domain_mapping_invalid_id');
|
|
}
|
|
|
|
$site = absint($site);
|
|
|
|
// Check cache first
|
|
$mappings = wp_cache_get('id:' . $site, 'domain_mapping');
|
|
|
|
if ('none' === $mappings) {
|
|
return false;
|
|
}
|
|
|
|
if ( ! empty($mappings)) {
|
|
return static::to_instances($mappings);
|
|
}
|
|
|
|
// Cache missed, fetch from DB
|
|
// Suppress errors in case the table doesn't exist
|
|
$suppress = $wpdb->suppress_errors();
|
|
|
|
$domain_table = "{$wpdb->base_prefix}wu_domain_mappings";
|
|
|
|
$mappings = $wpdb->get_results($wpdb->prepare('SELECT * FROM ' . $domain_table . ' WHERE blog_id = %d ORDER BY primary_domain DESC, active DESC, secure DESC', $site)); //phpcs:ignore
|
|
|
|
$wpdb->suppress_errors($suppress);
|
|
|
|
if ( ! $mappings) {
|
|
wp_cache_set('id:' . $site, 'none', 'domain_mapping');
|
|
|
|
return false;
|
|
}
|
|
|
|
wp_cache_set('id:' . $site, $mappings, 'domain_mapping');
|
|
|
|
return static::to_instances($mappings);
|
|
}
|
|
|
|
/**
|
|
* Gets mappings by domain names
|
|
*
|
|
* Note: This is used in sunrise, so unfortunately, we can't use the Query model.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param array|string $domains Domain names to search for.
|
|
* @return object
|
|
*/
|
|
public static function get_by_domain($domains) {
|
|
|
|
global $wpdb;
|
|
|
|
$domains = (array) $domains;
|
|
|
|
// Check cache first
|
|
$not_exists = 0;
|
|
|
|
foreach ($domains as $domain) {
|
|
$data = wp_cache_get('domain:' . $domain, 'domain_mappings');
|
|
|
|
if ( ! empty($data) && 'notexists' !== $data) {
|
|
return new static($data);
|
|
} elseif ('notexists' === $data) {
|
|
++$not_exists;
|
|
}
|
|
}
|
|
|
|
if (count($domains) === $not_exists) {
|
|
|
|
// Every domain we checked was found in the cache, but doesn't exist
|
|
// so skip the query
|
|
return null;
|
|
}
|
|
|
|
$placeholders = array_fill(0, count($domains), '%s');
|
|
|
|
$placeholders_in = implode(',', $placeholders);
|
|
|
|
// Prepare the query
|
|
$query = "SELECT * FROM {$wpdb->wu_dmtable} WHERE domain IN ($placeholders_in) AND active = 1 ORDER BY primary_domain DESC, active DESC, secure DESC LIMIT 1";
|
|
|
|
$query = $wpdb->prepare($query, $domains); // phpcs:ignore
|
|
|
|
// Suppress errors in case the table doesn't exist
|
|
$suppress = $wpdb->suppress_errors();
|
|
|
|
$mapping = $wpdb->get_row($query); // phpcs:ignore
|
|
|
|
$wpdb->suppress_errors($suppress);
|
|
|
|
if (empty($mapping)) {
|
|
|
|
// Cache that it doesn't exist
|
|
foreach ($domains as $domain) {
|
|
wp_cache_set('domain:' . $domain, 'notexists', 'domain_mappings');
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
wp_cache_set('domain:' . $mapping->domain, $mapping, 'domain_mappings');
|
|
|
|
return new static($mapping);
|
|
}
|
|
}
|