Initial Commit

This commit is contained in:
David Stone
2024-11-30 18:24:12 -07:00
commit e8f7955c1c
5432 changed files with 1397750 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,296 @@
<?php
/**
* The Broadcast model for the Broadcasts.
*
* @package WP_Ultimo
* @subpackage Models
* @since 2.0.0
*/
namespace WP_Ultimo\Models;
use WP_Ultimo\Models\Post_Base_Model;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* Broadcast model class. Implements the Base Model.
*
* @since 2.0.0
*/
class Broadcast extends Post_Base_Model {
/**
* Post model.
*
* @since 2.0.0
* @var string
*/
public $model = 'broadcast';
/**
* Callback function for turning IDs into objects
*
* @since 2.0.0
* @access public
* @var mixed
*/
protected $query_class = '\\WP_Ultimo\\Database\\Broadcasts\\Broadcast_Query';
/**
* Post type.
*
* @since 2.0.0
* @var string
*/
protected $type = 'broadcast_notice';
/**
* Set the allowed types to prevent saving wrong types.
*
* @since 2.0.0
* @var array
*/
protected $allowed_types = array('broadcast_email', 'broadcast_notice');
/**
* Set the allowed status to prevent saving wrong status.
*
* @since 2.0.0
* @var array
*/
protected $allowed_status = array('publish', 'draft');
/**
* Broadcast status.
*
* @since 2.0.0
* @var string
*/
protected $status = 'publish';
/**
* Notice type
*
* @since 2.0.0
* @var string
*/
protected $notice_type;
/**
* Constructs the object via the constructor arguments
*
* @since 2.0.7
*
* @param mixed $object Std object with model parameters.
*/
public function __construct($object = null) {
$object = (array) $object;
if (!wu_get_isset($object, 'migrated_from_id')) {
unset($object['migrated_from_id']);
} // end if;
parent::__construct($object);
} // end __construct;
/**
* 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() {
return array(
'notice_type' => 'in:info,success,warning,error',
'status' => 'default:publish',
'name' => 'default:title',
'title' => 'required|min:2',
'content' => 'required|min:3',
'type' => 'required|in:broadcast_email,broadcast_notice|default:broadcast_notice',
);
} // end validation_rules;
/**
* Get the id of the original 1.X model that was used to generate this item on migration.
*
* @since 2.0.0
* @return int
*/
public function get_migrated_from_id() {
if ($this->migrated_from_id === null) {
$this->migrated_from_id = $this->get_meta('migrated_from_id', 0);
} // end if;
return $this->migrated_from_id;
} // end get_migrated_from_id;
/**
* Set the id of the original 1.X model that was used to generate this item on migration.
*
* @since 2.0.0
* @param int $migrated_from_id The ID of the original 1.X model that was used to generate this item on migration.
* @return void
*/
public function set_migrated_from_id($migrated_from_id) {
$this->meta['migrated_from_id'] = $migrated_from_id;
$this->migrated_from_id = $this->meta['migrated_from_id'];
} // end set_migrated_from_id;
/**
* Get name of the broadcast
*
* @since 2.0.0
* @return string
*/
public function get_name() {
return $this->get_title();
} // end get_name;
/**
* Get title of the broadcast
*
* @since 2.0.0
* @return string
*/
public function get_title() {
return $this->title;
} // end get_title;
/**
* Get notice type
*
* @since 2.0.0
* @return string
*/
public function get_notice_type() {
if ($this->notice_type === null) {
$this->notice_type = $this->get_meta('notice_type', 'success');
} // end if;
return $this->notice_type;
} // end get_notice_type;
/**
* Get the message targets.
*
* @since 2.0.0
* @return string
*/
public function get_message_targets() {
return $this->get_meta('message_targets');
} // end get_message_targets;
/**
* Set the message product and/or customer targets.
*
* @since 2.0.0
*
* @param string $message_targets The targets for this broadcast.
* @return void
*/
public function set_message_targets($message_targets) {
$this->meta['message_targets'] = $message_targets;
} // end set_message_targets;
/**
* Set the type of the notice.
*
* @since 2.0.0
*
* @param string $notice_type Can be info, success, warning or error.
* @options info,success,warning,error
* @return void
*/
public function set_notice_type($notice_type) {
$this->meta['notice_type'] = $notice_type;
$this->notice_type = $this->meta['notice_type'];
} // end set_notice_type;
/**
* Set title using the name parameter.
*
* @since 2.0.0
*
* @param string $name This broadcast name, which is used as broadcast title as well.
* @return void
*/
public function set_name($name) {
$this->set_title($name);
} // end set_name;
/**
* Adds checks to prevent saving the model with the wrong type.
*
* @since 2.0.0
*
* @param string $type The type being set.
* @return void
*/
public function set_type($type) {
if (!in_array($type, $this->allowed_types, true)) {
$type = 'broadcast_notice';
} // end if;
$this->type = $type;
} // end set_type;
/**
* * Adds checks to prevent saving the model with the wrong status.
*
* @since 2.0.0
*
* @param string $status The status being set.
* @return void
*/
public function set_status($status) {
if (!in_array($status, $this->allowed_status, true)) {
$status = 'publish';
} // end if;
$this->status = $status;
} // end set_status;
} // end class Broadcast;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,954 @@
<?php
/**
* The Customer model.
*
* @package WP_Ultimo
* @subpackage Models
* @since 2.0.0
*/
namespace WP_Ultimo\Models;
use WP_Ultimo\Models\Base_Model;
use WP_Ultimo\Models\Membership;
use WP_Ultimo\Models\Site;
use WP_Ultimo\Models\Payment;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* Customer model class. Implements the Base Model.
*
* @since 2.0.0
*/
class Customer extends Base_Model {
use Traits\Billable, Traits\Notable;
/**
* User ID of the associated user.
*
* @since 2.0.0
* @var int
*/
protected $user_id;
/**
* The type of customer.
*
* Almost a 100% of the time this will be 'customer'
* but since we use this table to store support-agents as well
* this can be 'support-agent'.
*
* @see \WP_Ultimo\Models\Support_Agent
*
* @since 2.0.0
* @var string
*/
protected $type;
/**
* Date when the customer was created.
*
* @since 2.0.0
* @var string
*/
protected $date_registered;
/**
* Email verification status - either `none`, `pending`, or `verified`.
*
* @since 2.0.0
* @var string
*/
protected $email_verification;
/**
* Date this customer last logged in.
*
* @since 2.0.0
* @var string
*/
protected $last_login;
/**
* Whether or not the customer has trialed before.
*
* @since 2.0.0
* @var null|bool
*/
protected $has_trialed;
/**
* If this customer is a VIP customer or not.
*
* @since 2.0.0
* @var bool
*/
protected $vip = false;
/**
* List of IP addresses used by this customer.
*
* @since 2.0.0
* @var string
*/
protected $ips;
/**
* The form used to signup.
*
* @since 2.0.0
* @var string
*/
protected $signup_form;
/**
* Extra information about this customer.
*
* @since 2.0.0
* @var array
*/
protected $extra_information;
/**
* Query Class to the static query methods.
*
* @since 2.0.0
* @var string
*/
protected $query_class = '\\WP_Ultimo\\Database\\Customers\\Customer_Query';
/**
* Allows injection, which is useful for mocking.
*
* @since 2.2.0
* @var string
*/
public $_user;
/**
* Save (create or update) the model on the database.
*
* @since 2.0.0
*/
public function save() {
return parent::save();
} // end save;
/**
* 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 array(
'user_id' => "required|integer|unique:\WP_Ultimo\Models\Customer,user_id,{$id}",
'email_verification' => 'required|in:none,pending,verified',
'type' => 'required|in:customer',
'last_login' => 'default:',
'has_trialed' => 'boolean|default:0',
'vip' => 'boolean|default:0',
'ips' => 'array',
'extra_information' => 'default:',
'signup_form' => 'default:',
);
} // end validation_rules;
/**
* Get user ID of the associated user.
*
* @since 2.0.0
* @return int
*/
public function get_user_id() {
return absint($this->user_id);
} // end get_user_id;
/**
* Set user ID of the associated user.
*
* @since 2.0.0
* @param int $user_id The WordPress user ID attached to this customer.
* @return void
*/
public function set_user_id($user_id) {
$this->user_id = $user_id;
} // end set_user_id;
/**
* Returns the user associated with this customer.
*
* @since 2.0.0
* @return WP_User
*/
public function get_user() {
return get_user_by('id', $this->get_user_id());
} // end get_user;
/**
* Returns the customer's display name.
*
* @since 2.0.0
* @return string
*/
public function get_display_name() {
$user = $this->get_user();
if (empty($user)) {
return __('User Deleted', 'wp-ultimo');
} // end if;
return $user->display_name;
} // end get_display_name;
/**
* Returns the default billing address.
*
* Classes that implement this trait need to implement
* this method.
*
* @since 2.0.0
* @return \WP_Ultimo\Objects\Billing_Address
*/
public function get_default_billing_address() {
return new \WP_Ultimo\Objects\Billing_Address(array(
'company_name' => $this->get_display_name(),
'billing_email' => $this->get_email_address(),
'billing_country' => $this->get_meta('ip_country'),
));
} // end get_default_billing_address;
/**
* Returns the customer country.
*
* @since 2.0.0
* @return string
*/
public function get_country() {
$billing_address = $this->get_billing_address();
$country = $billing_address->billing_country;
if (!$country) {
return $this->get_meta('ip_country');
} // end if;
return $country;
} // end get_country;
/**
* Returns the customer's username.
*
* @since 2.0.0
* @return string
*/
public function get_username() {
$user = $this->get_user();
if (empty($user)) {
return __('none', 'wp-ultimo');
} // end if;
return $user->user_login;
} // end get_username;
/**
* Returns the customer's email address.
*
* @since 2.0.0
* @return string
*/
public function get_email_address() {
$user = $this->get_user();
if (empty($user)) {
return __('none', 'wp-ultimo');
} // end if;
return $user->user_email;
} // end get_email_address;
/**
* Get date when the customer was created.
*
* @since 2.0.0
* @param bool $formatted To format or not.
* @return string
*/
public function get_date_registered($formatted = true) {
return $this->date_registered;
} // end get_date_registered;
/**
* Set date when the customer was created.
*
* @since 2.0.0
* @param string $date_registered Date when the customer was created.
* @return void
*/
public function set_date_registered($date_registered) {
$this->date_registered = $date_registered;
} // end set_date_registered;
/**
* Get email verification status - either `none`, `pending`, or `verified`.
*
* @since 2.0.0
* @return string
*/
public function get_email_verification() {
return $this->email_verification;
} // end get_email_verification;
/**
* Set email verification status - either `none`, `pending`, or `verified`.
*
* @since 2.0.0
* @param string $email_verification Email verification status - either `none`, `pending`, or `verified`.
* @return void
*/
public function set_email_verification($email_verification) {
$this->email_verification = $email_verification;
} // end set_email_verification;
/**
* Get date this customer last logged in.
*
* @since 2.0.0
* @param bool $formatted To format or not.
* @return string
*/
public function get_last_login($formatted = true) {
return $this->last_login;
} // end get_last_login;
/**
* Set date this customer last logged in.
*
* @since 2.0.0
* @param string $last_login Date this customer last logged in.
* @return void
*/
public function set_last_login($last_login) {
$this->last_login = $last_login;
} // end set_last_login;
/**
* Get whether or not the customer has trialed before.
*
* @since 2.0.0
* @return null|bool
*/
public function has_trialed() {
if ((bool) $this->has_trialed) {
return true;
} // end if;
$this->has_trialed = $this->get_meta('wu_has_trialed');
if (!$this->has_trialed) {
$trial = wu_get_memberships(array(
'customer_id' => $this->get_id(),
'date_trial_end__not_in' => array(null, '0000-00-00 00:00:00'),
'fields' => 'ids',
'number' => 1,
));
if (!empty($trial)) {
$this->update_meta('wu_has_trialed', true);
$this->has_trialed = true;
} // end if;
} // end if;
return $this->has_trialed;
} // end has_trialed;
/**
* Set whether or not the customer has trialed before.
*
* @since 2.0.0
* @param bool $has_trialed Whether or not the customer has trialed before.
* @return void
*/
public function set_has_trialed($has_trialed) {
$this->meta['wu_has_trialed'] = $has_trialed;
$this->has_trialed = $has_trialed;
} // end set_has_trialed;
/**
* Get if this customer is a VIP customer or not.
*
* @since 2.0.0
* @return bool
*/
public function is_vip() {
return (bool) $this->vip;
} // end is_vip;
/**
* Set if this customer is a VIP customer or not.
*
* @since 2.0.0
* @param bool $vip If this customer is a VIP customer or not.
* @return void
*/
public function set_vip($vip) {
$this->vip = $vip;
} // end set_vip;
/**
* Get list of IP addresses used by this customer.
*
* @since 2.0.0
* @return array
*/
public function get_ips() {
if (empty($this->ips)) {
return array();
} // end if;
if (is_string($this->ips)) {
$this->ips = maybe_unserialize($this->ips);
} // end if;
return $this->ips;
} // end get_ips;
/**
* Returns the last IP address recorded for the customer.
*
* @since 2.0.0
* @return string
*/
public function get_last_ip() {
$ips = $this->get_ips();
return array_pop($ips);
} // end get_last_ip;
/**
* Set list of IP addresses used by this customer.
*
* @since 2.0.0
* @param array $ips List of IP addresses used by this customer.
* @return void
*/
public function set_ips($ips) {
if (is_string($ips)) {
$ips = maybe_unserialize(wp_unslash($ips));
} // end if;
$this->ips = $ips;
} // end set_ips;
/**
* Adds a new IP to the IP list.
*
* @since 2.0.0
*
* @param string $ip New IP address to add.
* @return void
*/
public function add_ip($ip) {
$ips = $this->get_ips();
if (!is_array($ips)) {
$ips = array();
} // end if;
/*
* IP already exists.
*/
if (in_array($ip, $ips, true)) {
return;
} // end if;
$ips[] = sanitize_text_field($ip);
$this->set_ips($ips);
} // end add_ip;
/**
* Updates the last login, as well as the ip and country if necessary.
*
* @since 2.0.0
*
* @param boolean $update_ip If we want to update the IP address.
* @param boolean $update_country_and_state If we want to update country and state.
* @return boolean
*/
public function update_last_login($update_ip = true, $update_country_and_state = false) {
$this->attributes(array(
'last_login' => wu_get_current_time('mysql', true),
));
$geolocation = $update_ip || $update_country_and_state ? \WP_Ultimo\Geolocation::geolocate_ip('', true) : false;
if ($update_ip) {
$this->add_ip($geolocation['ip']);
} // end if;
if ($update_country_and_state) {
$this->update_meta('ip_country', $geolocation['country']);
$this->update_meta('ip_state', $geolocation['state']);
} // end if;
return $this->save();
} // end update_last_login;
/**
* Get extra information.
*
* @since 2.0.0
* @return array
*/
public function get_extra_information() {
if ($this->extra_information === null) {
$extra_information = (array) $this->get_meta('wu_customer_extra_information');
$this->extra_information = array_filter($extra_information);
} // end if;
return $this->extra_information;
} // end get_extra_information;
/**
* Set featured extra information.
*
* @since 2.0.0
* @param array $extra_information Any extra information related to this customer.
* @return void
*/
public function set_extra_information($extra_information) {
$extra_information = array_filter((array) $extra_information);
$this->extra_information = $extra_information;
$this->meta['wu_customer_extra_information'] = $extra_information;
} // end set_extra_information;
/**
* Returns the subscriptions attached to this customer.
*
* @since 2.0.0
* @return array
*/
public function get_memberships() {
return Membership::query(array(
'customer_id' => $this->get_id(),
));
} // end get_memberships;
/**
* Returns the sites attached to this customer.
*
* @since 2.0.0
* @param array $query_args Query arguments.
* @return array
*/
public function get_sites($query_args = array()) {
$query_args = array_merge(
$query_args,
array(
'meta_query' => array(
'customer_id' => array(
'key' => 'wu_customer_id',
'value' => $this->get_id(),
),
),
)
);
return Site::query($query_args);
} // end get_sites;
/**
* Returns all pending sites associated with a customer.
*
* @since 2.0.0
* @return array
*/
public function get_pending_sites() {
$pending_sites = array();
$memberships = $this->get_memberships();
foreach ($memberships as $membership) {
$pending_site = $membership->get_pending_site();
if ($pending_site) {
$pending_sites[] = $pending_site;
} // end if;
} // end foreach;
return $pending_sites;
} // end get_pending_sites;
/**
* The the primary site ID if available.
*
* In cases where none is set, we:
* - return the id of the first site on the list off sites
* belonging to this customer;
* - or return the main site id.
*
* @since 2.0.0
* @return int
*/
public function get_primary_site_id() {
$primary_site_id = get_user_option('primary_blog', $this->get_user_id());
if (!$primary_site_id) {
$sites = $this->get_sites();
$primary_site_id = $sites ? $sites[0]->get_id() : wu_get_main_site_id();
} // end if;
return $primary_site_id;
} // end get_primary_site_id;
/**
* Returns the payments attached to this customer.
*
* @since 2.0.0
* @return array
*/
public function get_payments() {
return Payment::query(array(
'customer_id' => $this->get_id(),
));
} // end get_payments;
/**
* By default, we just use the to_array method, but you can rewrite this.
*
* @since 2.0.0
* @return array
*/
public function to_search_results() {
$user = get_userdata($this->get_user_id());
if (isset($this->_user)) {
$user = $this->_user; // Allows for injection, which is useful for mocking.
unset($this->_user);
} // end if;
$search_result = $this->to_array();
if ($user) {
$user->data->avatar = get_avatar($user->data->user_email, 40, 'identicon', '', array(
'force_display' => true,
'class' => 'wu-rounded-full wu-mr-3',
));
$search_result = array_merge((array) $user->data, $search_result);
} // end if;
$search_result['billing_address_data'] = $this->get_billing_address()->to_array();
$search_result['billing_address'] = $this->get_billing_address()->to_string();
return $search_result;
} // end to_search_results;
/**
* Get the customer type.
*
* @since 2.0.0
* @return string
*/
public function get_type() {
return $this->type;
} // end get_type;
/**
* Get the customer type.
*
* @since 2.0.0
* @param string $type The customer type. Can be 'customer'.
* @options customer
* @return void
*/
public function set_type($type) {
$this->type = $type;
} // end set_type;
/**
* Gets the total grossed by the customer so far.
*
* @since 2.0.0
* @return float
*/
public function get_total_grossed() {
global $wpdb;
static $sum;
if ($sum === null) {
$sum = $wpdb->get_var(
$wpdb->prepare(
"SELECT SUM(total) FROM {$wpdb->base_prefix}wu_payments WHERE parent_id = 0 AND customer_id = %d",
$this->get_id()
)
);
} // end if;
return $sum;
} // end get_total_grossed;
/**
* Get if the customer is online or not.
*
* @since 2.0.0
* @return boolean
*/
public function is_online() {
if ($this->get_last_login() === '0000-00-00 00:00:00') {
return false;
} // end if;
$last_login_date = new \DateTime($this->get_last_login());
$now = new \DateTime('now');
$interval = $last_login_date->diff($now);
$minutes_interval = $interval->days * 24 * 60;
$minutes_interval += $interval->h * 60;
$minutes_interval += $interval->i;
return $minutes_interval <= apply_filters('wu_is_online_minutes_interval', 3) ? true : false;
} // end is_online;
/**
* Saves a verification key.
*
* @since 2.0.0
* @return bool
*/
public function generate_verification_key() {
$seed = time();
$hash = \WP_Ultimo\Helpers\Hash::encode($seed, 'verification-key');
return $this->update_meta('wu_verification_key', $hash);
} // end generate_verification_key;
/**
* Returns the saved verification key.
*
* @since 2.0.0
* @return string|bool
*/
public function get_verification_key() {
return $this->get_meta('wu_verification_key', false);
} // end get_verification_key;
/**
* Disabled the verification by setting the key to false.
*
* @since 2.0.0
* @return bool
*/
public function disable_verification_key() {
return $this->update_meta('wu_verification_key', false);
} // end disable_verification_key;
/**
* Returns the link of the email verification endpoint.
*
* @since 2.0.0
* @return string|bool
*/
public function get_verification_url() {
$key = $this->get_verification_key();
if (!$key) {
return get_site_url(wu_get_main_site_id());
} // end if;
return add_query_arg(array(
'email-verification-key' => $key,
'customer' => $this->get_hash(),
), get_site_url(wu_get_main_site_id()));
} // end get_verification_url;
/**
* Send verification email.
*
* @since 2.0.4
* @return void
*/
public function send_verification_email() {
$this->generate_verification_key();
$payload = array_merge(
array('verification_link' => $this->get_verification_url()),
wu_generate_event_payload('customer', $this)
);
wu_do_event('confirm_email_address', $payload);
} // end send_verification_email;
/**
* Get the form used to signup.
*
* @since 2.0.0
* @return string
*/
public function get_signup_form() {
return $this->signup_form;
} // end get_signup_form;
/**
* Set the form used to signup.
*
* @since 2.0.0
* @param string $signup_form The form used to signup.
* @return void
*/
public function set_signup_form($signup_form) {
$this->signup_form = $signup_form;
} // end set_signup_form;
} // end class Customer;

View File

@ -0,0 +1,841 @@
<?php
/**
* The Discount_Code model for the Discount Codes.
*
* @package WP_Ultimo
* @subpackage Models
* @since 2.0.0
*/
namespace WP_Ultimo\Models;
use WP_Ultimo\Models\Base_Model;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* Discount_Code model class. Implements the Base Model.
*
* @since 2.0.0
*/
class Discount_Code extends Base_Model {
use \WP_Ultimo\Traits\WP_Ultimo_Coupon_Deprecated;
/**
* Name of the discount code.
*
* @since 2.0.0
* @var string
*/
protected $name;
/**
* Code to redeem the discount code.
*
* @since 2.0.0
* @var string
*/
protected $code;
/**
* Text describing the coupon code. Useful for identifying it.
*
* @since 2.0.0
* @var string
*/
protected $description;
/**
* Number of times this discount was applied.
*
* @since 2.0.0
* @var int
*/
protected $uses = 0;
/**
* The number of times this discount can be used before becoming inactive.
*
* @since 2.0.0
* @var int
*/
protected $max_uses;
/**
* If we should apply the discount to renewals as well.
*
* @since 2.0.0
* @var bool
*/
protected $apply_to_renewals = false;
/**
* Type of the discount. Can be a percentage or absolute.
*
* @since 2.0.0
* @var string
*/
protected $type = 'percentage';
/**
* Amount discounted in cents.
*
* @since 2.0.0
* @var int
*/
protected $value = 0;
/**
* Type of the discount for the setup fee value. Can be a percentage or absolute.
*
* @since 2.0.0
* @var string
*/
protected $setup_fee_type = 'percentage';
/**
* Amount discounted fpr setup fees in cents.
*
* @since 2.0.0
* @var int
*/
protected $setup_fee_value = 0;
/**
* If this coupon code is active or not.
*
* @since 2.0.0
* @var bool
*/
protected $active = 1;
/**
* If we should check for products or not.
*
* @since 2.0.0
* @var bool
*/
protected $limit_products;
/**
* Holds the list of allowed products.
*
* @since 2.0.0
* @var array
*/
protected $allowed_products;
/**
* Start date for the coupon code to be considered valid.
*
* @since 2.0.0
* @var string
*/
protected $date_start;
/**
* Expiration date for the coupon code.
*
* @since 2.0.0
* @var string
*/
protected $date_expiration;
/**
* Date when this discount code was created.
*
* @since 2.0.0
* @var string
*/
protected $date_created;
/**
* Query Class to the static query methods.
*
* @since 2.0.0
* @var string
*/
protected $query_class = '\\WP_Ultimo\\Database\\Discount_Codes\\Discount_Code_Query';
/**
* 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() {
return array(
'name' => 'required|min:2',
'code' => 'required|min:2|max:20|alpha_dash',
'uses' => 'integer|default:0',
'max_uses' => 'integer|min:0|default:0',
'active' => 'default:1',
'apply_to_renewals' => 'default:0',
'type' => 'default:absolute|in:percentage,absolute',
'value' => 'required|numeric',
'setup_fee_type' => 'in:percentage,absolute',
'setup_fee_value' => 'numeric',
'allowed_products' => 'array',
'limit_products' => 'default:0',
);
} // end validation_rules;
/**
* Get name of the discount code.
*
* @since 2.0.0
* @return string
*/
public function get_name() {
return $this->name;
} // end get_name;
/**
* Set name of the discount code.
*
* @since 2.0.0
* @param string $name Your discount code name, which is used as discount code title as well.
* @return void
*/
public function set_name($name) {
$this->name = $name;
} // end set_name;
/**
* Get code to redeem the discount code.
*
* @since 2.0.0
* @return string
*/
public function get_code() {
return $this->code;
} // end get_code;
/**
* Set code to redeem the discount code.
*
* @since 2.0.0
* @param string $code A unique identification to redeem the discount code. E.g. PROMO10.
* @return void
*/
public function set_code($code) {
$this->code = $code;
} // end set_code;
/**
* Get text describing the coupon code. Useful for identifying it.
*
* @since 2.0.0
* @return string
*/
public function get_description() {
return $this->description;
} // end get_description;
/**
* Set text describing the coupon code. Useful for identifying it.
*
* @since 2.0.0
* @param string $description A description for the discount code, usually a short text.
* @return void
*/
public function set_description($description) {
$this->description = $description;
} // end set_description;
/**
* Get number of times this discount was applied.
*
* @since 2.0.0
* @return int
*/
public function get_uses() {
return (int) $this->uses;
} // end get_uses;
/**
* Set number of times this discount was applied.
*
* @since 2.0.0
* @param int $uses Number of times this discount was applied.
* @return void
*/
public function set_uses($uses) {
$this->uses = (int) $uses;
} // end set_uses;
/**
* Add uses to this discount code.
*
* @since 2.0.4
* @param integer $uses Number of uses to add.
* @return void
*/
public function add_use($uses = 1) {
$use_count = (int) $this->get_uses();
$this->set_uses($use_count + (int) $uses);
} // end add_use;
/**
* Get the number of times this discount can be used before becoming inactive.
*
* @since 2.0.0
* @return int
*/
public function get_max_uses() {
return (int) $this->max_uses;
} // end get_max_uses;
/**
* Set the number of times this discount can be used before becoming inactive.
*
* @since 2.0.0
* @param int $max_uses The number of times this discount can be used before becoming inactive.
* @return void
*/
public function set_max_uses($max_uses) {
$this->max_uses = (int) $max_uses;
} // end set_max_uses;
/**
* Checks if the given discount code has a number of max uses.
*
* @since 2.0.0
* @return boolean
*/
public function has_max_uses() {
return $this->get_max_uses() > 0;
} // end has_max_uses;
/**
* Get if we should apply this coupon to renewals as well.
*
* @since 2.0.0
* @return int
*/
public function should_apply_to_renewals() {
return (bool) $this->apply_to_renewals;
} // end should_apply_to_renewals;
/**
* Set if we should apply this coupon to renewals as well.
*
* @since 2.0.0
* @param bool $apply_to_renewals Wether or not we should apply the discount to membership renewals.
* @return void
*/
public function set_apply_to_renewals($apply_to_renewals) {
$this->apply_to_renewals = (bool) $apply_to_renewals;
} // end set_apply_to_renewals;
/**
* Get type of the discount. Can be a percentage or absolute.
*
* @since 2.0.0
* @return string
*/
public function get_type() {
return $this->type;
} // end get_type;
/**
* Set type of the discount. Can be a percentage or absolute.
*
* @since 2.0.0
* @param string $type The type of the discount code. Can be 'percentage' (e.g. 10% OFF), 'absolute' (e.g. $10 OFF).
* @options percentage,absolute
* @return void
*/
public function set_type($type) {
$this->type = $type;
} // end set_type;
/**
* Get amount discounted in cents.
*
* @since 2.0.0
* @return int
*/
public function get_value() {
return (float) $this->value;
} // end get_value;
/**
* Set amount discounted in cents.
*
* @since 2.0.0
* @param int $value Amount discounted in cents.
* @return void
*/
public function set_value($value) {
$this->value = $value;
} // end set_value;
/**
* Get type of the discount for the setup fee value. Can be a percentage or absolute.
*
* @since 2.0.0
* @return string
*/
public function get_setup_fee_type() {
return $this->setup_fee_type;
} // end get_setup_fee_type;
/**
* Set type of the discount for the setup fee value. Can be a percentage or absolute.
*
* @since 2.0.0
* @param string $setup_fee_type Type of the discount for the setup fee value. Can be a percentage or absolute.
* @options percentage,absolute
* @return void
*/
public function set_setup_fee_type($setup_fee_type) {
$this->setup_fee_type = $setup_fee_type;
} // end set_setup_fee_type;
/**
* Get amount discounted fpr setup fees in cents.
*
* @since 2.0.0
* @return int
*/
public function get_setup_fee_value() {
return (float) $this->setup_fee_value;
} // end get_setup_fee_value;
/**
* Set amount discounted for setup fees in cents.
*
* @since 2.0.0
* @param int $setup_fee_value Amount discounted for setup fees in cents.
* @return void
*/
public function set_setup_fee_value($setup_fee_value) {
$this->setup_fee_value = $setup_fee_value;
} // end set_setup_fee_value;
/**
* Get if this coupon code is active or not.
*
* @since 2.0.0
* @return bool
*/
public function is_active() {
return (bool) $this->active;
} // end is_active;
/**
* Checks if a given coupon code is valid and can be applied.
*
* @since 2.0.0
* @param int|\WP_Ultimo\Models\Product $product Product to check against.
* @return true|\WP_Error
*/
public function is_valid($product = false) {
if ($this->is_active() === false) {
return new \WP_Error('discount_code', __('This coupon code is not valid.', 'wp-ultimo'));
} // end if;
/*
* Check for uses
*/
if ($this->has_max_uses() && $this->get_uses() >= $this->get_max_uses()) {
return new \WP_Error('discount_code', __('This discount code was already redeemed the maximum amount of times allowed.', 'wp-ultimo'));
} // end if;
/*
* Fist, check date boundaries.
*/
$start_date = $this->get_date_start();
$expiration_date = $this->get_date_expiration();
$now = wu_date();
if ($start_date) {
$start_date_instance = wu_date($start_date);
if ($now < $start_date_instance) {
return new \WP_Error('discount_code', __('This coupon code is not valid.', 'wp-ultimo'));
} // end if;
} // end if;
if ($expiration_date) {
$expiration_date_instance = wu_date($expiration_date);
if ($now > $expiration_date) {
return new \WP_Error('discount_code', __('This coupon code is not valid.', 'wp-ultimo'));
} // end if;
} // end if;
if (!$this->get_limit_products()) {
return true;
} // end if;
if (!empty($product)) {
if (is_a($product, '\WP_Ultimo\Models\Product')) {
$product_id = $product->get_id();
} elseif (is_numeric($product)) {
$product_id = $product;
} // end if;
$allowed = $this->get_limit_products() && in_array($product_id, $this->get_allowed_products()); // phpcs:ignore
if ($allowed === false) {
return new \WP_Error('discount_code', __('This coupon code is not valid.', 'wp-ultimo'));
} // end if;
} // end if;
return true;
} // end is_valid;
/**
* Checks if this discount applies just for the first payment.
*
* @since 2.0.0
* @return boolean
*/
public function is_one_time() {
return (bool) $this->should_apply_to_renewals();
} // end is_one_time;
/**
* Set if this coupon code is active or not.
*
* @since 2.0.0
* @param bool $active Set this discount code as active (true), which means available to be used, or inactive (false).
* @return void
*/
public function set_active($active) {
$this->active = (bool) $active;
} // end set_active;
/**
* Get start date for the coupon code to be considered valid.
*
* @since 2.0.0
* @return string
*/
public function get_date_start() {
if (!wu_validate_date($this->date_start)) {
return '';
} // end if;
return $this->date_start;
} // end get_date_start;
/**
* Set start date for the coupon code to be considered valid.
*
* @since 2.0.0
* @param string $date_start Start date for the coupon code to be considered valid.
* @return void
*/
public function set_date_start($date_start) {
$this->date_start = $date_start;
} // end set_date_start;
/**
* Get expiration date for the coupon code.
*
* @since 2.0.0
* @return string
*/
public function get_date_expiration() {
if (!wu_validate_date($this->date_expiration)) {
return '';
} // end if;
return $this->date_expiration;
} // end get_date_expiration;
/**
* Set expiration date for the coupon code.
*
* @since 2.0.0
* @param string $date_expiration Expiration date for the coupon code.
* @return void
*/
public function set_date_expiration($date_expiration) {
$this->date_expiration = $date_expiration;
} // end set_date_expiration;
/**
* Get date when this discount code was created.
*
* @since 2.0.0
* @return string
*/
public function get_date_created() {
return $this->date_created;
} // end get_date_created;
/**
* Set date when this discount code was created.
*
* @since 2.0.0
* @param string $date_created Date when this discount code was created.
* @return void
*/
public function set_date_created($date_created) {
$this->date_created = $date_created;
} // end set_date_created;
/**
* Returns a text describing the discount code values.
*
* @since 2.0.0
*/
public function get_discount_description(): string {
$description = array();
if ($this->get_value() > 0) {
$value = wu_format_currency($this->get_value());
if ($this->get_type() === 'percentage') {
$value = $this->get_value() . '%';
} // end if;
$description[] = sprintf(
// translators: placeholder is the value off. Can be wither $X.XX or X%
__('%1$s OFF on Subscriptions', 'wp-ultimo'),
$value
);
} // end if;
if ($this->get_setup_fee_value() > 0) {
$setup_fee_value = wu_format_currency($this->get_setup_fee_value());
if ($this->get_setup_fee_type() === 'percentage') {
$setup_fee_value = $this->get_setup_fee_value() . '%';
} // end if;
$description[] = sprintf(
// translators: placeholder is the value off. Can be wither $X.XX or X%
__('%1$s OFF on Setup Fees', 'wp-ultimo'),
$setup_fee_value
);
} // end if;
return implode(' ' . __('and', 'wp-ultimo') . ' ', $description);
} // end get_discount_description;
/**
* Transform the object into an assoc array.
*
* @since 2.0.0
* @return array
*/
public function to_array() {
$array = parent::to_array();
$array['discount_description'] = $this->get_discount_description();
return $array;
} // end to_array;
/**
* Save (create or update) the model on the database.
*
* @since 2.0.0
*
* @return bool
*/
public function save() {
$results = parent::save();
if (!is_wp_error($results) && has_action('wp_ultimo_coupon_after_save')) {
if (did_action('wp_ultimo_coupon_after_save')) {
return $results;
} // end if;
$compat_coupon = $this;
do_action_deprecated('wp_ultimo_coupon_after_save', array($compat_coupon), '2.0.0', 'wu_discount_code_post_save');
} // end if;
return $results;
} // end save;
/**
* Get holds the list of allowed products.
*
* @since 2.0.0
* @return array
*/
public function get_allowed_products() {
if ($this->allowed_products === null) {
$this->allowed_products = $this->get_meta('wu_allowed_products', array());
} // end if;
return (array) $this->allowed_products;
} // end get_allowed_products;
/**
* Set holds the list of allowed products.
*
* @since 2.0.0
* @param array $allowed_products The list of products that allows this discount code to be used. If empty, all products will accept this code.
* @return void
*/
public function set_allowed_products($allowed_products) {
$this->meta['wu_allowed_products'] = (array) $allowed_products;
$this->allowed_products = $this->meta['wu_allowed_products'];
} // end set_allowed_products;
/**
* Get if we should check for products or not.
*
* @since 2.0.0
* @return bool
*/
public function get_limit_products() {
if ($this->limit_products === null) {
$this->limit_products = $this->get_meta('wu_limit_products', false);
} // end if;
return (bool) $this->limit_products;
} // end get_limit_products;
/**
* Set if we should check for products or not.
*
* @since 2.0.0
* @param bool $limit_products This discount code will be limited to be used in certain products? If set to true, you must define a list of allowed products.
* @return void
*/
public function set_limit_products($limit_products) {
$this->meta['wu_limit_products'] = (bool) $limit_products;
$this->limit_products = $this->meta['wu_limit_products'];
} // end set_limit_products;
} // end class Discount_Code;

727
inc/models/class-domain.php Normal file
View File

@ -0,0 +1,727 @@
<?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 = array(
'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';
/**
* 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 array(
'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',
);
} // end validation_rules;
/**
* Returns the domain address mapped.
*
* @since 2.0.0
* @return string
*/
public function get_domain() {
return $this->domain;
} // end get_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) {
$this->domain = strtolower($domain);
} // end set_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);
} // end get_url;
/**
* Get the ID of the corresponding site.
*
* @access public
* @since 2.0
* @return int
*/
public function get_blog_id() {
return (int) $this->blog_id;
} // end get_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) {
$this->blog_id = $blog_id;
} // end set_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();
} // end get_site_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());
} // end if;
return wu_get_site($this->get_blog_id());
} // end get_site;
/**
* Check if this particular mapping is active.
*
* @since 2.0.0
* @return boolean
*/
public function is_active() {
if ($this->has_inactive_stage()) {
return false;
} // end if;
return (bool) $this->active;
} // end is_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) {
$this->active = $active;
} // end set_active;
/**
* Check if this is a primary domain.
*
* @since 2.0.0
* @return boolean
*/
public function is_primary_domain() {
return (bool) $this->primary_domain;
} // end is_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) {
$this->primary_domain = $primary_domain;
} // end set_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;
} // end is_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) {
$this->secure = $secure;
} // end set_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;
} // end get_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) {
$this->stage = $stage;
} // end set_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);
} // end has_inactive_stage;
/**
* 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();
} // end get_stage_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();
} // end get_stage_class;
/**
* Get date when this was created.
*
* @since 2.0.0
* @return string
*/
public function get_date_created() {
return $this->date_created;
} // end get_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) {
$this->date_created = $date_created;
} // end set_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;
} // end if;
if (in_array($network_ip_address, $domains_and_ips, true)) {
return true;
} // end if;
$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;
} // end has_correct_dns;
/**
* 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());
} // end has_valid_ssl_certificate;
/**
* 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 = array(
$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');
} // end if;
} else {
if (has_action('mercator.mapping.updated')) {
$deprecated_args = array(
$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');
} // end if;
} // end if;
/*
* Resets cache.
*
* This will make sure the list of domains gets rebuild
* after a change is made.
*/
wp_cache_flush();
} // end if;
return $results;
} // end save;
/**
* 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 = array(
$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');
} // end if;
/*
* Delete log file.
*/
wu_log_clear("domain-{$this->get_domain()}");
wu_log_add("domain-{$this->get_domain()}", __('Domain deleted and logs cleared...', 'wp-ultimo'));
return $results;
} // end delete;
/**
* 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;
} // end if;
if (!is_numeric($site)) {
return new \WP_Error('wu_domain_mapping_invalid_id');
} // end if;
$site = absint($site);
// Check cache first
$mappings = wp_cache_get('id:' . $site, 'domain_mapping');
if ($mappings === 'none') {
return false;
} // end if;
if (!empty($mappings)) {
return static::to_instances($mappings);
} // end if;
// 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;
} // end if;
wp_cache_set('id:' . $site, $mappings, 'domain_mapping');
return static::to_instances($mappings);
} // end get_by_site;
/**
* 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) && $data !== 'notexists') {
return new static($data);
} elseif ($data === 'notexists') {
$not_exists++;
} // end if;
} // end foreach;
if ($not_exists === count($domains)) {
// Every domain we checked was found in the cache, but doesn't exist
// so skip the query
return null;
} // end if;
$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');
} // end foreach;
return null;
} // end if;
wp_cache_set('domain:' . $mapping->domain, $mapping, 'domain_mappings');
return new static($mapping);
} // end get_by_domain;
} // end class Domain;

789
inc/models/class-email.php Normal file
View File

@ -0,0 +1,789 @@
<?php
/**
* The Email model for the Emails.
*
* @package WP_Ultimo
* @subpackage Models
* @since 2.0.0
*/
namespace WP_Ultimo\Models;
use WP_Ultimo\Models\Post_Base_Model;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* Email model class. Implements the Base Model.
*
* @since 2.0.0
*/
class Email extends Post_Base_Model {
/**
* Post model.
*
* @since 2.0.0
* @var string
*/
public $model = 'email';
/**
* Callback function for turning IDs into objects
*
* @since 2.0.0
* @access public
* @var mixed
*/
protected $query_class = '\\WP_Ultimo\\Database\\Emails\\Email_Query';
/**
* Post type.
*
* @since 2.0.0
* @var string
*/
protected $type = 'system_email';
/**
* Set the allowed types to prevent saving wrong types.
*
* @since 2.0.0
* @var array
*/
protected $allowed_types = array('system_email');
/**
* Email slug.
*
* @since 2.0.0
* @var string
*/
protected $slug = '';
/**
* Post status.
*
* @since 2.0.0
* @var string
*/
protected $allowed_status = array('publish', 'draft');
/**
* If this email is going to be send later.
*
* @since 2.0.0
* @var bool
*/
protected $schedule;
/**
* If we should send this to a customer or to the network admin.
*
* Can be either 'customer' or 'admin'.
*
* @since 2.0.0
* @var string
*/
protected $target;
/**
* Checks if we should send a copy of the email to the admin.
*
* @since 2.0.0
* @var boolean
*/
protected $send_copy_to_admin;
/**
* The event of this email.
*
* This determines when this email is going to be triggered.
*
* @since 2.0.0
* @var string
*/
protected $event;
/**
* The active status of an email.
*
* @since 2.0.0
* @var bool
*/
protected $active;
/**
* Whether or not this is a legacy email.
*
* @since 2.0.0
* @var bool
*/
protected $legacy;
/**
* Plain or HTML.
*
* @since 2.0.0
* @var bool
*/
protected $style;
/**
* 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() {
return array(
'schedule' => 'boolean|default:0',
'type' => 'in:system_email|default:system_email',
'event' => 'required|default:',
'send_hours' => 'default:',
'send_days' => 'integer|default:',
'schedule_type' => 'in:days,hours',
'name' => 'default:title',
'title' => 'required',
'slug' => 'required',
'custom_sender' => 'boolean|default:0',
'custom_sender_name' => 'default:',
'custom_sender_email' => 'default:',
'target' => 'required|in:customer,admin',
'send_copy_to_admin' => 'boolean|default:0',
'active' => 'default:1',
'legacy' => 'boolean|default:0',
);
} // end validation_rules;
/**
* Get event of the email
*
* @since 2.0.0
* @return string
*/
public function get_event() {
if ($this->event === null) {
$this->event = $this->get_meta('wu_system_email_event');
} // end if;
return $this->event;
} // end get_event;
/**
* Get title of the email
*
* @since 2.0.0
* @return string
*/
public function get_title() {
return $this->title;
} // end get_title;
/**
* Get title of the email using get_name
*
* @since 2.0.0
* @return string
*/
public function get_name() {
return $this->title;
} // end get_name;
/**
* Get style of the email
*
* @since 2.0.0
* @return string
*/
public function get_style() {
$this->style = $this->get_meta('wu_style', 'html');
if ($this->style === 'use_default') {
$this->style = wu_get_setting('email_template_type', 'html');
} // end if;
/*
* Do an extra check for old installs
* where the default value was not being
* properly installed.
*/
if (empty($this->style)) {
$this->style = 'html';
} // end if;
return $this->style;
} // end get_style;
/**
* Set the style.
*
* @since 2.0.0
*
* @param string $style The email style. Can be 'html' or 'plain-text'.
* @options html,plain-text
* @return void
*/
public function set_style($style) {
$this->style = $style;
$this->meta['wu_style'] = $this->style;
} // end set_style;
/**
* Get if the email has a schedule.
*
* @since 2.0.0
* @return string
*/
public function has_schedule() {
if ($this->schedule === null) {
$this->schedule = $this->get_meta('wu_schedule', false);
} // end if;
return $this->schedule;
} // end has_schedule;
/**
* Set the email schedule.
*
* @since 2.0.0
* @param bool $schedule Whether or not this is a scheduled email.
* @return void
*/
public function set_schedule($schedule) {
$this->schedule = $schedule;
$this->meta['wu_schedule'] = $schedule;
} // end set_schedule;
/**
* Set the email schedule.
*
* @since 2.0.0
* @return string
*/
public function get_schedule_type() {
return $this->get_meta('system_email_schedule_type', 'days');
} // end get_schedule_type;
/**
* Get schedule send in days of the email
*
* @since 2.0.0
* @return string
*/
public function get_send_days() {
return $this->get_meta('system_email_send_days', 0);
} // end get_send_days;
/**
* Get schedule send in hours of the email.
*
* @since 2.0.0
* @return string
*/
public function get_send_hours() {
return $this->get_meta('system_email_send_hours', '12:00');
} // end get_send_hours;
/**
* Returns a timestamp in the future when this email should be sent.
*
* @since 2.0.0
* @return int
*/
public function get_when_to_send() {
$when_to_send = 0;
if (!$this->has_schedule()) {
return $when_to_send;
} // end if;
if ($this->get_schedule_type() === 'hours') {
$send_time = explode(':', $this->get_send_hours());
$when_to_send = strtotime('+' . $send_time[0] . ' hours ' . $send_time[1] . ' minutes');
} // end if;
if ($this->get_schedule_type() === 'days') {
$when_to_send = strtotime('+' . $this->get_send_days() . ' days');
} // end if;
return $when_to_send;
} // end get_when_to_send;
/**
* Get email slug.
*
* @since 2.0.0
* @return string
*/
public function get_slug() {
return $this->slug;
} // end get_slug;
/**
* Get the custom sender option.
*
* @since 2.0.0
* @return string
*/
public function get_custom_sender() {
return $this->get_meta('system_email_custom_sender');
} // end get_custom_sender;
/**
* Get the custom sender name.
*
* @since 2.0.0
* @return string
*/
public function get_custom_sender_name() {
return $this->get_meta('system_email_custom_sender_name');
} // end get_custom_sender_name;
/**
* Get the custom sender email.
*
* @since 2.0.0
* @return string
*/
public function get_custom_sender_email() {
return $this->get_meta('system_email_custom_sender_email');
} // end get_custom_sender_email;
/**
* Adds checks to prevent saving the model with the wrong type.
*
* @since 2.0.0
*
* @param string $type The type being set.
* @return void
*/
public function set_type($type) {
if (!in_array($type, $this->allowed_types, true)) {
$type = 'system_email';
} // end if;
$this->type = $type;
} // end set_type;
/**
* Set the email event.
*
* @since 2.0.0
*
* @param string $event The event that needs to be fired for this email to be sent.
* @return void
*/
public function set_event($event) {
$this->event = $event;
$this->meta['wu_system_email_event'] = $event;
} // end set_event;
/**
* Set if the email is schedule.
*
* @since 2.0.0
*
* @param string $email_schedule if the send will be schedule.
* @return void
*/
public function set_email_schedule($email_schedule) {
$this->meta['system_email_schedule'] = $email_schedule;
} // end set_email_schedule;
/**
* Set the schedule date in hours.
*
* @since 2.0.0
*
* @param string $send_hours The amount of hours that the email will wait before is sent.
* @return void
*/
public function set_send_hours($send_hours) {
$this->meta['system_email_send_hours'] = $send_hours;
} // end set_send_hours;
/**
* Set the schedule date in days.
*
* @since 2.0.0
*
* @param string $send_days The amount of days that the email will wait before is sent.
* @return void
*/
public function set_send_days($send_days) {
$this->meta['system_email_send_days'] = $send_days;
} // end set_send_days;
/**
* Set the schedule type.
*
* @since 2.0.0
*
* @param string $schedule_type The type of schedule. Can be 'days' or 'hours'.
* @options days,hours
* @return void
*/
public function set_schedule_type($schedule_type) {
$this->meta['system_email_schedule_type'] = $schedule_type;
} // end set_schedule_type;
/**
* Set title using the name parameter.
*
* @since 2.0.0
*
* @param string $name The name being set as title.
* @return void
*/
public function set_name($name) {
$this->set_title($name);
} // end set_name;
/**
* Set the slug.
*
* @since 2.0.0
*
* @param string $slug The slug being set.
* @return void
*/
public function set_slug($slug) {
$this->slug = $slug;
} // end set_slug;
/**
* Set the custom sender.
*
* @since 2.0.0
*
* @param boolean $custom_sender If has a custom sender.
* @return void
*/
public function set_custom_sender($custom_sender) {
$this->meta['system_email_custom_sender'] = $custom_sender;
} // end set_custom_sender;
/**
* Set the custom sender name.
*
* @since 2.0.0
*
* @param string $custom_sender_name The name of the custom sender. E.g. From: John Doe.
* @return void
*/
public function set_custom_sender_name($custom_sender_name) {
$this->meta['system_email_custom_sender_name'] = $custom_sender_name;
} // end set_custom_sender_name;
/**
* Set the custom sender email.
*
* @since 2.0.0
*
* @param string $custom_sender_email The email of the custom sender. E.g. From: johndoe@gmail.com.
* @return void
*/
public function set_custom_sender_email($custom_sender_email) {
$this->meta['system_email_custom_sender_email'] = $custom_sender_email;
} // end set_custom_sender_email;
/**
* Get if we should send this to a customer or to the network admin.
*
* @since 2.0.0
* @return string
*/
public function get_target() {
if ($this->target === null) {
$this->target = $this->get_meta('wu_target', 'admin');
} // end if;
return $this->target;
} // end get_target;
/**
* Set if we should send this to a customer or to the network admin.
*
* @since 2.0.0
* @param string $target If we should send this to a customer or to the network admin. Can be 'customer' or 'admin'.
* @options customer,admin
* @return void
*/
public function set_target($target) {
$this->target = $target;
$this->meta['wu_target'] = $target;
} // end set_target;
/**
* Gets the list of targets for an email.
*
* @since 2.0.0
*
* @param array $payload The payload of the email being sent. Used to get the customer id.
* @return array
*/
public function get_target_list($payload = array()) {
$target_list = array();
$target_type = $this->get_target();
if ($target_type === 'admin') {
$target_list = self::get_super_admin_targets();
} elseif ($target_type === 'customer') {
if (!wu_get_isset($payload, 'customer_id')) {
return array();
} // end if;
$customer = wu_get_customer($payload['customer_id']);
if (!$customer) {
return array();
} // end if;
$target_list[] = array(
'name' => $customer->get_display_name(),
'email' => $customer->get_email_address(),
);
/*
* Maybe ad super admins as well.
*/
if ($this->get_send_copy_to_admin()) {
$admin_targets = self::get_super_admin_targets();
$target_list = array_merge($target_list, $admin_targets);
} // end if;
} // end if;
return $target_list;
} // end get_target_list;
/**
* Returns the list of super admin targets.
*
* @since 2.0.0
* @return array
*/
public static function get_super_admin_targets() {
$target_list = array();
$super_admins = get_super_admins();
foreach ($super_admins as $super_admin) {
$user = get_user_by('login', $super_admin);
if ($user) {
$target_list[] = array(
'name' => $user->display_name,
'email' => $user->user_email,
);
} // end if;
} // end foreach;
return $target_list;
} // end get_super_admin_targets;
/**
* Get if we should send a copy of the email to the admin.
*
* @since 2.0.0
* @return boolean
*/
public function get_send_copy_to_admin() {
if ($this->send_copy_to_admin === null) {
$this->send_copy_to_admin = $this->get_meta('wu_send_copy_to_admin', false);
} // end if;
return $this->send_copy_to_admin;
} // end get_send_copy_to_admin;
/**
* Set if we should send a copy of the email to the admin.
*
* @since 2.0.0
* @param boolean $send_copy_to_admin Checks if we should send a copy of the email to the admin.
* @return void
*/
public function set_send_copy_to_admin($send_copy_to_admin) {
$this->send_copy_to_admin = $send_copy_to_admin;
$this->meta['wu_send_copy_to_admin'] = $send_copy_to_admin;
} // end set_send_copy_to_admin;
/**
* Get the active status of an email.
*
* @since 2.0.0
* @return bool
*/
public function is_active() {
if ($this->active === null) {
$this->active = $this->get_meta('wu_active', true);
} // end if;
return $this->active;
} // end is_active;
/**
* Set the active status of an email.
*
* @since 2.0.0
* @param bool $active Set this email as active (true), which means available will fire when the event occur, or inactive (false).
* @return void
*/
public function set_active($active) {
$this->active = $active;
$this->meta['wu_active'] = $active;
} // end set_active;
/**
* Get whether or not this is a legacy email.
*
* @since 2.0.0
* @return bool
*/
public function is_legacy() {
if ($this->legacy === null) {
$this->legacy = $this->get_meta('wu_legacy', false);
} // end if;
return $this->legacy;
} // end is_legacy;
/**
* Set whether or not this is a legacy email.
*
* @since 2.0.0
* @param bool $legacy Whether or not this is a legacy email.
* @return void
*/
public function set_legacy($legacy) {
$this->legacy = $legacy;
$this->meta['wu_legacy'] = $legacy;
} // end set_legacy;
} // end class Email;

690
inc/models/class-event.php Normal file
View File

@ -0,0 +1,690 @@
<?php
/**
* The Event model for the Event Mappings.
*
* @package WP_Ultimo
* @subpackage Models
* @since 2.0.0
*/
namespace WP_Ultimo\Models;
use WP_Ultimo\Models\Base_Model;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* Event model class. Implements the Base Model.
*
* @since 2.0.0
*/
class Event extends Base_Model {
const SEVERITY_SUCCESS = 1;
const SEVERITY_NEUTRAL = 2;
const SEVERITY_INFO = 3;
const SEVERITY_WARNING = 4;
const SEVERITY_FATAL = 5;
/**
* Severity of the problem.
*
* @since 2.0.0
* @var int
*/
protected $severity;
/**
* Date when the event was created.
*
* @since 2.0.0
* @var string
*/
protected $date_created;
/**
* Initiator of this event.
*
* Events can be run by WP Ultimo, saved as 'system', or
* by people (admins, customers), saved as 'manual'.
*
* @since 2.0.0
* @var string
*/
protected $initiator;
/**
* The author of the action, saved as the user_id.
*
* @since 2.0.0
* @var int
*/
protected $author_id = 0;
/**
* Object type associated with this event.
*
* Can be one one the following:
* - network, for events concerning the entire network;
* - site, for events that concern a specific site;
* - customer, for events that concern a specific customer;
* - domain, for events that concern a specific domain;
* - membership, for events that concern a specific membership;
*
* @since 2.0.0
* @var string
*/
protected $object_type;
/**
* ID of the related objects.
*
* @since 2.0.0
* @var int
*/
protected $object_id;
/**
* Slug of the event.
*
* @since 2.0.0
* @var string
*/
protected $slug = '';
/**
* Payload of the event.
*
* @since 2.0.0
* @var object
*/
protected $payload;
/**
* Query Class to the static query methods.
*
* @since 2.0.0
* @var string
*/
protected $query_class = '\\WP_Ultimo\\Database\\Events\\Event_Query';
/**
* 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() {
return array(
'severity' => 'required|numeric|between:1,5',
'payload' => 'required',
'object_type' => 'required|alpha_dash|lowercase',
'object_id' => 'integer|default:0',
'author_id' => 'integer|default:0',
'slug' => 'required|alpha_dash',
'initiator' => 'required|in:system,manual'
);
} // end validation_rules;
/**
* Get severity of the problem..
*
* @since 2.0.0
* @return int
*/
public function get_severity() {
return (int) $this->severity;
} // end get_severity;
/**
* Returns the Label for a given severity level.
*
* @since 2.0.0
* @return string
*/
public function get_severity_label() {
$labels = array(
Event::SEVERITY_SUCCESS => __('Success', 'wp-ultimo'),
Event::SEVERITY_NEUTRAL => __('Neutral', 'wp-ultimo'),
Event::SEVERITY_INFO => __('Info', 'wp-ultimo'),
Event::SEVERITY_WARNING => __('Warning', 'wp-ultimo'),
Event::SEVERITY_FATAL => __('Fatal', 'wp-ultimo'),
);
return isset($labels[$this->get_severity()]) ? $labels[$this->get_severity()] : __('Note', 'wp-ultimo');
} // end get_severity_label;
/**
* Gets the classes for a given severity level.
*
* @since 2.0.0
* @return string
*/
public function get_severity_class() {
$classes = array(
Event::SEVERITY_SUCCESS => 'wu-bg-green-200 wu-text-green-700',
Event::SEVERITY_NEUTRAL => 'wu-bg-gray-200 wu-text-gray-700',
Event::SEVERITY_INFO => 'wu-bg-blue-200 wu-text-blue-700',
Event::SEVERITY_WARNING => 'wu-bg-yellow-200 wu-text-yellow-700',
Event::SEVERITY_FATAL => 'wu-bg-red-200 wu-text-red-700',
);
return isset($classes[$this->get_severity()]) ? $classes[$this->get_severity()] : '';
} // end get_severity_class;
/**
* Set severity of the problem..
*
* @since 2.0.0
* @param int $severity Severity of the problem.
* @return void
*/
public function set_severity($severity) {
$this->severity = $severity;
} // end set_severity;
/**
* Get date when the event was created..
*
* @since 2.0.0
* @return string
*/
public function get_date_created() {
return $this->date_created;
} // end get_date_created;
/**
* Set date when the event was created..
*
* @since 2.0.0
* @param string $date_created Date when the event was created.
* @return void
*/
public function set_date_created($date_created) {
$this->date_created = $date_created;
} // end set_date_created;
/**
* Get payload of the event..
*
* @since 2.0.0
* @return mixed
*/
public function get_payload() {
$payload = (array) maybe_unserialize(wp_unslash($this->payload));
return $payload;
} // end get_payload;
/**
* Set payload of the event..
*
* @since 2.0.0
* @param object $payload Payload of the event.
* @return void
*/
public function set_payload($payload) {
$this->payload = $payload;
} // end set_payload;
/**
* Get message for the event.
*
* @since 2.0.0
* @return string
*/
public function get_message() {
$message = self::get_default_system_messages($this->slug);
return $this->interpolate_message($message, $this->get_payload());
} // end get_message;
/**
* Interpolates the value of a message and its placeholders with the contents of the payload.
*
* @since 2.0.0
*
* @param string $message The message with placeholders.
* @param array $payload Key => value based array.
*/
public function interpolate_message($message, $payload): string {
$payload = json_decode(json_encode($payload), true);
$interpolation_keys = array();
foreach ($payload as $key => &$value) {
$interpolation_keys[] = "{{{$key}}}";
if (is_array($value)) {
$value = implode(' &rarr; ', wu_array_flatten($value));
} // end if;
} // end foreach;
$interpolation = array_combine($interpolation_keys, $payload);
$interpolation['{{payload}}'] = implode(' - ', wu_array_flatten($payload, true));
$interpolation['{{model}}'] = wu_slug_to_name($this->object_type);
$interpolation['{{object_id}}'] = $this->object_id;
return strtr($message, $interpolation);
} // end interpolate_message;
/**
* Returns the default system messages for events.
*
* @since 2.0.0
*
* @param string $slug The slug of the event.
* @return string
*/
public static function get_default_system_messages($slug) {
$default_messages = array();
$default_messages['changed'] = __('The <strong>{{model}}</strong> #{{object_id}} was changed: {{payload}}', 'wp-ultimo');
$default_messages['created'] = __('The <strong>{{model}}</strong> #{{object_id}} was created.', 'wp-ultimo');
$default_messages = apply_filters('wu_get_default_system_messages', $default_messages);
return wu_get_isset($default_messages, $slug, __('No Message', 'wp-ultimo'));
} // end get_default_system_messages;
/**
* Get by people (admins, customers), saved as 'manual'.
*
* @since 2.0.0
* @return string
*/
public function get_initiator() {
return $this->initiator;
} // end get_initiator;
/**
* Set by people (admins, customers), saved as 'manual'.
*
* @since 2.0.0
*
* @param string $initiator The type of user responsible for initiating the event. There are two options: Manual and System. By default, the event is saved as manual.
* @return void
*/
public function set_initiator($initiator) {
$this->initiator = $initiator;
} // end set_initiator;
/**
* Get the author of the action, saved as the user_id.
*
* @since 2.0.0
* @return int
*/
public function get_author_id() {
return $this->author_id;
} // end get_author_id;
/**
* Returns the user associated with this author.
*
* @since 2.0.0
* @return WP_User
*/
public function get_author_user() {
if ($this->author_id) {
$user = get_user_by('id', $this->author_id);
if ($user) {
return $user;
} // end if;
} // end if;
} // end get_author_user;
/**
* Returns the authors' display name.
*
* @since 2.0.0
* @return string
*/
public function get_author_display_name() {
$user = $this->get_author_user();
if ($user) {
return $user->display_name;
} // end if;
} // end get_author_display_name;
/**
* Returns the authors' email address.
*
* @since 2.0.0
* @return string
*/
public function get_author_email_address() {
$user = $this->get_author_user();
if ($user) {
return $user->user_email;
} // end if;
} // end get_author_email_address;
/**
* Set the author of the action, saved as the user_id.
*
* @since 2.0.0
* @param int $author_id The user responsible for creating the event. By default, the event is saved with the current user_id.
* @return void
*/
public function set_author_id($author_id) {
$this->author_id = $author_id;
} // end set_author_id;
/**
* Get the object of this event.
*
* @since 2.0.0
* @return mixed
*/
public function get_object() {
$object_type = $this->get_object_type();
$function_name = "wu_get_{$object_type}";
if (function_exists($function_name)) {
return $function_name($this->get_object_id());
} // end if;
return false;
} // end get_object;
/**
* Polyfill for the get_object method.
*
* @since 2.0.0
* @return false|object
*/
public function get_membership() {
$object_type = $this->get_object_type();
if ($object_type !== 'membership') {
return false;
} // end if;
return $this->get_object();
} // end get_membership;
/**
* Polyfill for the get_object method.
*
* @since 2.0.0
* @return false|object
*/
public function get_product() {
$object_type = $this->get_object_type();
if ($object_type !== 'product') {
return false;
} // end if;
return $this->get_object();
} // end get_product;
/**
* Polyfill for the get_object method.
*
* @since 2.0.0
* @return false|object
*/
public function get_site() {
$object_type = $this->get_object_type();
if ($object_type !== 'site') {
return false;
} // end if;
return $this->get_object();
} // end get_site;
/**
* Polyfill for the get_object method.
*
* @since 2.0.0
* @return false|object
*/
public function get_customer() {
$object_type = $this->get_object_type();
if ($object_type !== 'customer') {
return false;
} // end if;
return $this->get_object();
} // end get_customer;
/**
* Polyfill for the get_object method.
*
* @since 2.0.0
* @return false|object
*/
public function get_payment() {
$object_type = $this->get_object_type();
if ($object_type !== 'payment') {
return false;
} // end if;
return $this->get_object();
} // end get_payment;
/**
* Get the object type associated with this event.
*
* @since 2.0.0
* @return string
*/
public function get_object_type() {
return $this->object_type;
} // end get_object_type;
/**
* Set the object type associated with this event.
*
* @since 2.0.0
* @param string $object_type The type of object related to this event. It's usually the model name.
* @return void
*/
public function set_object_type($object_type) {
$this->object_type = $object_type;
} // end set_object_type;
/**
* Get the object type associated with this event.
*
* @since 2.0.0
* @return string
*/
public function get_slug() {
return $this->slug;
} // end get_slug;
/**
* Set the object type associated with this event.
*
* @since 2.0.0
* @param string $slug The event slug. It needs to be unique and preferably make it clear what it is about. Example: account_created is about creating an account.
* @return void
*/
public function set_slug($slug) {
$this->slug = $slug;
} // end set_slug;
/**
* Get iD of the related objects..
*
* @since 2.0.0
* @return int
*/
public function get_object_id() {
return $this->object_id;
} // end get_object_id;
/**
* Set iD of the related objects.
*
* @since 2.0.0
* @param int $object_id The ID of the related objects.
* @return void
*/
public function set_object_id($object_id) {
$this->object_id = $object_id;
} // end set_object_id;
/**
* Transform the object into an assoc array.
*
* @since 2.0.0
* @return array
*/
public function to_array() {
$array = parent::to_array();
$array['payload'] = $this->get_payload();
$array['message'] = $this->get_message();
$array['severity_label'] = $this->get_severity_label();
$array['severity_classes'] = $this->get_severity_class();
$array['author'] = array();
if ($this->get_initiator() === 'manual') {
$user = get_user_by('ID', $this->get_author_id());
if ($user) {
$array['author'] = (array) $user->data;
unset($array['author']['user_pass']);
unset($array['author']['user_activation_key']);
$array['author']['avatar'] = get_avatar_url($this->get_author_id(), array(
'default' => 'identicon',
));
} // end if;
} // end if;
return $array;
} // end to_array;
/**
* Override to clear event count.
*
* @since 2.0.0
* @return int|false
*/
public function save() {
if (!$this->exists() && function_exists('get_current_user_id')) {
$user_id = get_current_user_id();
delete_site_transient("wu_{$user_id}_unseen_events_count");
} // end if;
return parent::save();
} // end save;
} // end class Event;

File diff suppressed because it is too large Load Diff

1221
inc/models/class-payment.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,334 @@
<?php
/**
* The Post Base Model.
*
* @package WP_Ultimo
* @subpackage Models
* @since 2.0.0
*/
namespace WP_Ultimo\Models;
use WP_Ultimo\Models\Base_Model;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* Post Base model class. Implements the Base Model.
*
* This class is the base class that is extended by all of our data types
* with a title/content structure.
*
* @since 2.0.0
*/
class Post_Base_Model extends Base_Model {
/**
* Author ID.
*
* @since 2.0.0
* @var int
*/
protected $author_id = '';
/**
* Post type.
*
* @since 2.0.0
* @var string
*/
protected $type = '';
/**
* Post title.
*
* @since 2.0.0
* @var string
*/
protected $title = '';
/**
* Post title.
*
* @since 2.0.0
* @var string
*/
protected $name = '';
/**
* Post content.
*
* @since 2.0.0
* @var string
*/
protected $content = '';
/**
* Post excerpt.
*
* @since 2.0.0
* @var string
*/
protected $excerpt = '';
/**
* The post list order. Useful when ordering posts in a list.
*
* @since 2.0.0
* @var int
*/
protected $list_order = 10;
/**
* The post status.
*
* @since 2.0.0
* @var string
*/
protected $status = '';
/**
* Query Class to the static query methods.
*
* @since 2.0.0
* @var string
*/
protected $query_class = '\\WP_Ultimo\\Database\\Posts\\Post_Query';
/**
* Get author ID.
*
* @return int
*/
public function get_author_id() {
return $this->author_id;
} // end get_author_id;
/**
* Set author ID.
*
* @param int $author_id The author ID.
*/
public function set_author_id($author_id) {
$this->author_id = $author_id;
} // end set_author_id;
/**
* Get post type.
*
* @return string
*/
public function get_type() {
return $this->type;
} // end get_type;
/**
* Set post type.
*
* @param string $type Post type.
*/
public function set_type($type) {
$this->type = $type;
} // end set_type;
/**
* Get post title.
*
* @return string
*/
public function get_title() {
return $this->title;
} // end get_title;
/**
* Set post title.
*
* @param string $title Post title.
*/
public function set_title($title) {
$this->title = $title;
} // end set_title;
/**
* Get post content.
*
* @return string
*/
public function get_content() {
/**
* WordPress add slashes to the content, so we need to remove them when retrieve the data.
* Also we need to add the paragraphs and line breaks back.
*/
return wpautop(stripslashes($this->content));
} // end get_content;
/**
* Set post content.
*
* @param string $content Post content.
*/
public function set_content($content) {
$this->content = $content;
} // end set_content;
/**
* Get post excerpt.
*
* @return string
*/
public function get_excerpt() {
return $this->excerpt;
} // end get_excerpt;
/**
* Set post excerpt.
*
* @param string $excerpt Post excerpt.
*/
public function set_excerpt($excerpt) {
$this->excerpt = $excerpt;
} // end set_excerpt;
/**
* Get post creation date.
*
* @return string
*/
public function get_date_created() {
return $this->date_created;
} // end get_date_created;
/**
* Set post creation date.
*
* @param string $date_created Post creation date.
*/
public function set_date_created($date_created) {
$this->date_created = $date_created;
} // end set_date_created;
/**
* Get post last modification date.
*
* @return string
*/
public function get_date_modified() {
return $this->date_modified;
} // end get_date_modified;
/**
* Set post last modification date.
*
* @param string $date_modified Post last modification date.
*/
public function set_date_modified($date_modified) {
$this->date_modified = $date_modified;
} // end set_date_modified;
/**
* Get the post list order.
*
* @return int
*/
public function get_list_order() {
return $this->list_order;
} // end get_list_order;
/**
* Set the post list order.
*
* @param int $list_order The post list order.
*/
public function set_list_order($list_order) {
$this->list_order = $list_order;
} // end set_list_order;
/**
* Get the post status.
*
* @return string
*/
public function get_status() {
return $this->status;
} // end get_status;
/**
* Set the post status.
*
* @param string $status The post status.
*/
public function set_status($status) {
$this->status = $status;
} // end set_status;
/**
* Save (create or update) the model on the database,
* setting creation and modification dates first.
*
* @since 2.0.0
* @return bool
*/
public function save() {
if (!$this->author_id) {
$this->author_id = get_current_user_id();
} // end if;
if (!$this->status) {
/**
* Filters the object data before it is stored into the database.
*
* @since 2.0.0
*
* @param string $status The default status.
* @param string $post_type The post type.
* @param Base_Model $this The object instance.
*/
$this->status = apply_filters('wu_post_default_status', 'draft', $this->type, $this);
} // end if;
return parent::save();
} // end save;
} // end class Post_Base_Model;

1628
inc/models/class-product.php Normal file

File diff suppressed because it is too large Load Diff

2110
inc/models/class-site.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,324 @@
<?php
/**
* The Webhook model.
*
* @package WP_Ultimo
* @subpackage Models
* @since 2.0.0
*/
namespace WP_Ultimo\Models;
use WP_Ultimo\Models\Base_Model;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* Webhook model class. Implements the Base Model.
*
* @since 2.0.0
*/
class Webhook extends Base_Model {
/**
* The name of the webhook.
*
* @since 2.0.0
* @var string
*/
protected $name = '';
/**
* URL to be called when this webhook is triggered.
*
* @since 2.0.0
* @var string
*/
protected $webhook_url = '';
/**
* Event that should trigger this webhook.
*
* @since 2.0.0
* @var string
*/
protected $event = '';
/**
* The number of times this webhook was triggered.
*
* @since 2.0.0
* @var integer
*/
protected $event_count = 0;
/**
* Is this webhook active?
*
* @since 2.0.0
* @var boolean
*/
protected $active = true;
/**
* Is this webhook hidden?
*
* @since 2.0.0
* @var boolean
*/
protected $hidden = false;
/**
* Integration name.
*
* @since 2.0.0
* @var string
*/
protected $integration = 'manual';
/**
* Date when this was created.
*
* @since 2.0.0
* @var string
*/
protected $date_created;
/**
* Date when this webhook last failed.
*
* @since 2.0.0
* @var string
*/
protected $date_last_failed = '0000-00-00 00:00:00';
/**
* Query Class to the static query methods.
*
* @since 2.0.0
* @var string
*/
protected $query_class = '\\WP_Ultimo\\Database\\Webhooks\\Webhook_Query';
/**
* 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() {
return array(
'name' => 'required|min:2',
'webhook_url' => 'required|url:http,https',
'event' => 'required',
'event_count' => 'default:0',
'active' => 'default:1',
'hidden' => 'default:0',
'integration' => 'required|min:2',
'date_last_failed' => 'default:'
);
} // end validation_rules;
/**
* Get the value of name.
*
* @return string
*/
public function get_name() {
return $this->name;
} // end get_name;
/**
* Set the value of name.
*
* @param string $name Webhook name, which is used as product title as well.
*/
public function set_name($name) {
$this->name = $name;
} // end set_name;
/**
* Get the value of webhook_url.
*
* @return string
*/
public function get_webhook_url() {
return $this->webhook_url;
} // end get_webhook_url;
/**
* Set the value of webhook_url.
*
* @param string $webhook_url The URL used for the webhook call.
*/
public function set_webhook_url($webhook_url) {
$this->webhook_url = $webhook_url;
} // end set_webhook_url;
/**
* Get the value of event.
*
* @return string
*/
public function get_event() {
return $this->event;
} // end get_event;
/**
* Set the value of event.
*
* @param string $event The event that needs to be fired for this webhook to be sent.
*/
public function set_event($event) {
$this->event = $event;
} // end set_event;
/**
* Get the value of event_count.
*
* @return int The number of times this webhook was triggered and sent.
*/
public function get_event_count() {
return (int) $this->event_count;
} // end get_event_count;
/**
* Set the value of event_count.
*
* @param int $event_count How many times this webhook was sent.
*/
public function set_event_count($event_count) {
$this->event_count = $event_count;
} // end set_event_count;
/**
* Check if this particular mapping is active.
*
* @since 2.0.0
* @return boolean
*/
public function is_active() {
return (bool) $this->active;
} // end is_active;
/**
* Sets the active state of this model object;
*
* @since 2.0.0
*
* @param boolean $active Set this webhook as active (true), which means available will fire when the event occur, or inactive (false).
* @return void
*/
public function set_active($active) {
$this->active = (bool) wu_string_to_bool($active);
} // end set_active;
/**
* Get is this webhook hidden?
*
* @return boolean.
*/
public function is_hidden() {
return (bool) $this->hidden;
} // end is_hidden;
/**
* Set is this webhook hidden?
*
* @param boolean $hidden Is this webhook hidden.
*/
public function set_hidden($hidden) {
$this->hidden = $hidden;
} // end set_hidden;
/**
* Get integration name.
*
* @return string
*/
public function get_integration() {
return $this->integration;
} // end get_integration;
/**
* Get date when this was created..
*
* @since 2.0.0
* @return string
*/
public function get_date_created() {
return $this->date_created;
} // end get_date_created;
/**
* Get date when this was created..
*
* @since 2.0.0
* @return string
*/
public function get_date_last_failed() {
return $this->date_last_failed;
} // end get_date_last_failed;
/**
* Set date when this was created..
*
* @since 2.0.0
* @param string $date_created Date when this was created.
* @return void
*/
public function set_date_created($date_created) {
$this->date_created = $date_created;
} // end set_date_created;
/**
* Set integration name.
*
* @param string $integration The integration that created this webhook.
*/
public function set_integration($integration) {
$this->integration = $integration;
} // end set_integration;
} // end class Webhook;

View File

@ -0,0 +1,80 @@
<?php
/**
* A trait to handle billable models.
*
* @package WP_Ultimo
* @subpackage Models\Traits
* @since 2.0.0
*/
namespace WP_Ultimo\Models\Traits;
use \WP_Ultimo\Objects\Billing_Address;
/**
* Singleton trait.
*/
trait Billable {
/**
* The billing address.
*
* @since 2.0.0
* @var \WP_Ultimo\Objects\Billing_Address
*/
protected $billing_address;
/**
* Returns the default billing address.
*
* Classes that implement this trait need to implement
* this method.
*
* @since 2.0.0
* @return \WP_Ultimo\Objects\Billing_Address
*/
abstract public function get_default_billing_address();
/**
* Gets the billing address for this object.
*
* @since 2.0.0
* @return \WP_Ultimo\Objects\Billing_Address
*/
public function get_billing_address() {
if ($this->billing_address === null) {
$billing_address = $this->get_meta('wu_billing_address');
$this->billing_address = is_a($billing_address, '\WP_Ultimo\Objects\Billing_Address') ? $billing_address : $this->get_default_billing_address();
} // end if;
return $this->billing_address;
} // end get_billing_address;
/**
* Sets the billing address.
*
* @since 2.0.0
*
* @param array|\WP_Ultimo\Objects\Billing_Address $billing_address The billing address.
* @return void
*/
public function set_billing_address($billing_address) {
if (is_array($billing_address)) {
$billing_address = new Billing_Address($billing_address);
} // end if;
$this->meta['wu_billing_address'] = $billing_address;
$this->billing_address = $billing_address;
} // end set_billing_address;
} // end trait Billable;

View File

@ -0,0 +1,348 @@
<?php
/**
* A trait to handle limitable models.
*
* @package WP_Ultimo
* @subpackage Models\Traits
* @since 2.0.0
*/
namespace WP_Ultimo\Models\Traits;
use \WP_Ultimo\Database\Sites\Site_Type;
use \WP_Ultimo\Objects\Limitations;
/**
* Singleton trait.
*/
trait Limitable {
/**
* Internal limitations cache.
*
* @since 2.0.0
* @var array
*/
protected $_limitations = array();
/**
* List of limitations that need to be merged.
*
* Every model that is limitable (imports this trait)
* needs to declare explicitly the limitations that need to be
* merged. This allows us to chain the merges, and gives us
* a final list of limitations at the end of the process.
*
* @since 2.0.0
* @return array
*/
abstract public function limitations_to_merge();
/**
* Returns the limitations of this particular blog.
*
* @since 2.0.0
*
* @param bool $waterfall If we should construct the limitations object recursively.
* @param bool $skip_self If we should skip the current limitations.
* @return \WP_Ultimo\Objects\Limitations
*/
public function get_limitations($waterfall = true, $skip_self = false) {
/**
* If this is a site, and it's not a customer owned site, we don't have limitations.
* This is because we don't want to limit sites other than the customer owned ones.
*/
if ($this->model === 'site' && $this->get_type() !== Site_Type::CUSTOMER_OWNED) {
return new Limitations(array());
} // end if;
$cache_key = $waterfall ? '_composite_limitations_' : '_limitations_';
$cache_key = $skip_self ? $cache_key . '_no_self_' : $cache_key;
$cache_key = $this->get_id() . $cache_key . $this->model;
$cached_version = wu_get_isset($this->_limitations, $cache_key);
if (!empty($cached_version)) {
return $cached_version;
} // end if;
if (!is_array($this->meta)) {
$this->meta = array();
} // end if;
if (did_action('muplugins_loaded') === false) {
$modules_data = $this->get_meta('wu_limitations', array());
} else {
$modules_data = Limitations::early_get_limitations($this->model, $this->get_id());
} // end if;
$limitations = new Limitations(array());
if ($waterfall) {
$limitations = $limitations->merge(...$this->limitations_to_merge());
/**
* If we don't want to take into consideration our own permissions
* we set this flag to true.
*
* This will return only the parents permissions and is super useful for
* comparisons.
*/
if (!$skip_self) {
$limitations = $limitations->merge(true, $modules_data);
} // end if;
} else {
$limitations = $limitations->merge($modules_data);
} // end if;
$this->_limitations[$cache_key] = $limitations;
return $limitations;
} // end get_limitations;
/**
* Checks if this site has limitations or not.
*
* @since 2.0.0
* @return boolean
*/
public function has_limitations() {
return $this->get_limitations()->has_limitations();
} // end has_limitations;
/**
* Checks if a particular module is being limited.
*
* @since 2.0.0
*
* @param string $module Module to check.
* @return boolean
*/
public function has_module_limitation($module) {
return $this->get_limitations()->is_module_enabled($module);
} // end has_module_limitation;
/**
* Returns all user role quotas.
*
* @since 2.0.0
* @return array
*/
public function get_user_role_quotas() {
return $this->get_limitations()->get_user_role_quotas();
} // end get_user_role_quotas;
/**
* Proxy method to retrieve the allowed user roles.
*
* @since 2.0.0
* @return array
*/
public function get_allowed_user_roles() {
return $this->get_limitations()->get_allowed_user_roles();
} // end get_allowed_user_roles;
/**
* Schedules plugins to be activated or deactivated based on the current limitations;
*
* @since 2.0.5
* @return void
*/
public function sync_plugins() {
$sites = array();
if ($this->model === 'site') {
$sites[] = $this;
} elseif ($this->model === 'membership') {
$sites = $this->get_sites();
} // end if;
foreach ($sites as $site_object) {
if (!$site_object->get_id() || $site_object->get_type() !== Site_Type::CUSTOMER_OWNED) {
continue;
} // end if;
$site_id = $site_object->get_id();
$limitations = $site_object->get_limitations();
if (!$limitations->plugins->is_enabled()) {
continue;
} // end if;
$plugins_to_deactivate = $limitations->plugins->get_by_type('force_inactive');
$plugins_to_activate = $limitations->plugins->get_by_type('force_active');
if ($plugins_to_deactivate) {
wu_async_deactivate_plugins($site_id, array_keys($plugins_to_deactivate));
} // end if;
if ($plugins_to_activate) {
wu_async_activate_plugins($site_id, array_keys($plugins_to_activate));
} // end if;
} // end foreach;
} // end sync_plugins;
/**
* Makes sure we save limitations when we are supposed to.
*
* This is called on the handle_save method of the inc/admin-pages/class-edit-admin-page.php
* for all models that have the trait Limitable.
*
* @see inc/admin-pages/class-edit-admin-page.php
*
* @since 2.0.0
* @return void
*/
public function handle_limitations() {
/*
* Only handle limitations if there are to handle in the first place.
*/
if (!wu_request('modules')) {
return;
} // end if;
$object_limitations = $this->get_limitations(false);
$saved_limitations = $object_limitations->to_array();
$modules_to_save = array();
$limitations = Limitations::repository();
$current_limitations = $this->get_limitations(true, true);
foreach ($limitations as $limitation_id => $class_name) {
$module = wu_get_isset($saved_limitations, $limitation_id, array());
try {
if (is_string($module)) {
$module = json_decode($module, true);
} // end if;
} catch (\Throwable $exception) {
// Silence is golden.
} // end try;
$module['enabled'] = $object_limitations->{$limitation_id}->handle_enabled();
$module['limit'] = $object_limitations->{$limitation_id}->handle_limit();
$module = $object_limitations->{$limitation_id}->handle_others($module);
if ($module) {
$modules_to_save[$limitation_id] = $module;
} // end if;
} // end foreach;
if ($this->model !== 'product') {
/*
* Set the new permissions, based on the diff.
*/
$limitations = wu_array_recursive_diff($modules_to_save, $current_limitations->to_array());
} elseif ($this->model === 'product' && $this->get_type() !== 'plan') {
$limitations = wu_array_recursive_diff($modules_to_save, Limitations::get_empty()->to_array());
} else {
$limitations = $modules_to_save;
} // end if;
$this->meta['wu_limitations'] = $limitations;
} // end handle_limitations;
/**
* Returns the list of product slugs associated with this model.
*
* @since 2.0.0
* @return array
*/
public function get_applicable_product_slugs() {
if ($this->model === 'product') {
return array($this->get_slug());
} // end if;
$slugs = array();
if ($this->model === 'membership') {
$membership = $this;
} elseif ($this->model === 'site') {
$membership = $this->get_membership();
} // end if;
if (!empty($membership)) {
$slugs = array_column(array_map('wu_cast_model_to_array', array_column($membership->get_all_products(), 'product')), 'slug'); // WOW
} // end if;
return $slugs;
} // end get_applicable_product_slugs;
} // end trait Limitable;

View File

@ -0,0 +1,171 @@
<?php
/**
* A trait to handle notable models.
*
* @package WP_Ultimo
* @subpackage Models\Traits
* @since 2.0.0
*/
namespace WP_Ultimo\Models\Traits;
use \WP_Ultimo\Objects\Note;
/**
* Singleton trait.
*/
trait Notable {
/**
* The notes saved.
*
* @since 2.0.0
* @var \WP_Ultimo\Objects\Note[]
*/
protected $notes;
/**
* Get all the notes saved for this model.
*
* @since 2.0.0
* @return \WP_Ultimo\Objects\Note[]
*/
public function get_notes() {
if ($this->notes === null) {
$this->notes = get_metadata($this->get_meta_data_table_name(), $this->get_id(), 'wu_note', false);
} // end if;
return $this->notes;
} // end get_notes;
/**
* Adds a new note to this model.
*
* @since 2.0.0
*
* @param array|\WP_Ultimo|Objects\Note $note The note to add.
* @return bool
*/
public function add_note($note) {
if (!is_a($note, 'Note')) {
$note = new Note($note);
} // end if;
$status = $note->validate();
if (is_wp_error($status)) {
return $status;
} // end if;
$status = add_metadata($this->get_meta_data_table_name(), $this->get_id(), 'wu_note', $note, false);
return $status;
} // end add_note;
/**
* Remove all notes related to this model.
*
* @since 2.0.0
* @return bool
*/
public function clear_notes() {
$status = delete_metadata($this->get_meta_data_table_name(), $this->get_id(), 'wu_note', '', true);
return $status;
} // end clear_notes;
/**
* Remove one note related to this model.
*
* @since 2.0.0
*
* @param string $note_id The Note ID.
*
* @return bool
*/
public function delete_note($note_id) {
$model = $this->model;
$notes = $this->get_notes();
$mid = false;
foreach ($notes as $note) {
if ($note->note_id && $note->note_id === $note_id) {
global $wpdb;
$prefix = $wpdb->base_prefix;
$table_name = "{$prefix}wu_{$model}meta";
$column_name = "wu_{$model}_id";
if ($model === 'site') {
$table_name = "{$wpdb->base_prefix}blogmeta";
$column_name = 'blog_id';
} // end if;
$mid = $wpdb->get_row($wpdb->prepare("SELECT meta_id FROM $table_name WHERE $column_name = %d AND meta_key = %s AND meta_value = %s", $this->get_id(), 'wu_note', maybe_serialize($note)), ARRAY_A); // phpcs:ignore
} // end if;
} // end foreach;
if (!$mid) {
return false;
} // end if;
$status = delete_metadata_by_mid("wu_{$model}", $mid['meta_id']);
if ($model === 'site') {
$status = delete_metadata_by_mid('blog', $mid['meta_id']);
} // end if;
return $status;
} // end delete_note;
/**
* Returns the meta data meta table.
*
* This is redundant, but it is better than changing the access of the original method.
*
* @since 2.0.0
* @return string
*/
protected function get_meta_data_table_name() {
$query_class = new $this->query_class();
// Maybe apply table prefix
$table = !empty($query_class->prefix)
? "{$query_class->prefix}_{$query_class->item_name}"
: $query_class->item_name;
return $table;
} // end get_meta_data_table_name;
} // end trait Notable;