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

View File

@ -0,0 +1,700 @@
<?php
/**
* Base class that new host providers integrations must extend.
*
* @package WP_Ultimo
* @subpackage Integrations/Host_Providers
* @since 2.0.0
*/
namespace WP_Ultimo\Integrations\Host_Providers;
use WP_Ultimo\Helpers\WP_Config;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* This base class should be extended to implement new host integrations for SSL and domains.
*/
abstract class Base_Host_Provider {
/**
* Holds the id of the integration.
*
* @var string
* @since 2.0.0
*/
protected $id;
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $title;
/**
* Link to the tutorial teaching how to make this integration work.
*
* @var string
* @since 2.0.0
*/
protected $tutorial_link = '';
/**
* Array containing the features this integration supports.
*
* @var array
* @since 2.0.0
*/
protected $supports = array();
/**
* Constants that need to be present on wp-config.php for this integration to work.
* The values can be strings or array with constants to check for a match.
*
* @since 2.0.0
* @var array
*/
protected $constants = array();
/**
* Constants that are optional on wp-config.php.
*
* @since 2.0.0
* @var array
*/
protected $optional_constants = array();
/**
* Runs on singleton instantiation.
*
* @since 2.0.0
* @return void
*/
public function init() {
add_filter('wu_domain_manager_get_integrations', array($this, 'self_register'));
add_action('init', array($this, 'add_to_integration_list'));
} // end init;
/**
* Loads the hooks and dependencies, but only if the hosting is enabled via is_enabled().
*
* @since 2.0.0
*/
final public function __construct() {
if ($this->detect() && !$this->is_enabled()) {
/*
* Adds an admin notice telling the admin that they should probably enable this integration.
*/
return $this->alert_provider_detected();
} // end if;
/*
* Only add hooks if the integration is enabled and correctly setup.
*/
if ($this->is_enabled()) {
/*
* Checks if everything was correctly setup.
*/
if (!$this->is_setup()) {
/*
* Adds an admin notice telling the admin that the provider is not correctly setup.
*/
return $this->alert_provider_not_setup();
} // end if;
/*
* Load the dependencies.
*/
$this->load_dependencies();
/*
* Initialize the hooks.
*/
$this->register_hooks();
} // end if;
} // end __construct;
/**
* Let the class register itself on the manager, allowing us to access the integrations later via the slug.
*
* @since 2.0.0
*
* @param array $integrations List of integrations added so far.
* @return array
*/
final public function self_register($integrations) {
$integrations[$this->get_id()] = get_called_class();
return $integrations;
} // end self_register;
/**
* Get the list of enabled host integrations.
*
* @since 2.0.0
* @return array
*/
protected function get_enabled_list() {
return get_network_option(null, 'wu_host_integrations_enabled', array());
} // end get_enabled_list;
/**
* Check if this integration is enabled.
*
* @since 2.0.0
* @return boolean
*/
final public function is_enabled() {
$list = $this->get_enabled_list();
return wu_get_isset($list, $this->get_id(), false);
} // end is_enabled;
/**
* Enables this integration.
*
* @since 2.0.0
* @return boolean
*/
public function enable() {
$list = $this->get_enabled_list();
$list[$this->get_id()] = true;
return update_network_option(null, 'wu_host_integrations_enabled', $list);
} // end enable;
/**
* Disables this integration.
*
* @since 2.0.0
* @return boolean
*/
public function disable() {
$list = $this->get_enabled_list();
$list[$this->get_id()] = false;
return update_network_option(null, 'wu_host_integrations_enabled', $list);
} // end disable;
/**
* Adds the host to the list of integrations.
*
* @since 2.0.0
* @return void
*/
public function add_to_integration_list() {
$slug = $this->get_id();
$html = $this->is_enabled() ? sprintf('<span class="wu-self-center wu-text-green-800 wu-mr-4"><span class="dashicons-wu-check"></span> %s</span>', __('Activated', 'wp-ultimo')) : '';
$url = wu_network_admin_url('wp-ultimo-hosting-integration-wizard', array(
'integration' => $slug,
));
$html .= sprintf('<a href="%s" class="button-primary">%s</a>', $url, __('Configuration', 'wp-ultimo'));
// translators: %s is the name of a host provider (e.g. Cloudways, WPMUDev, Closte...).
$title = sprintf(__('%s Integration', 'wp-ultimo'), $this->get_title());
$title .= sprintf(
"<span class='wu-normal-case wu-block wu-text-xs wu-font-normal wu-mt-1'>%s</span>",
__('Go to the setup wizard to setup this integration.', 'wp-ultimo')
);
wu_register_settings_field('integrations', "integration_{$slug}", array(
'type' => 'note',
'title' => $title,
'desc' => $html,
));
} // end add_to_integration_list;
/**
* Adds an admin notice telling the admin that they should probably enable this integration.
*
* @since 2.0.0
* @return void
*/
public function alert_provider_detected() {
if (WP_Ultimo()->is_loaded() === false) {
return;
} // end if;
// translators: %1$s will be replaced with the integration title. E.g. RunCloud
$message = sprintf(__('It looks like you are using %1$s as your hosting provider, yet the %1$s integration module is not active. In order for the domain mapping integration to work with %1$s, you might want to activate that module.', 'wp-ultimo'), $this->get_title());
$slug = $this->get_id();
$actions = array(
'activate' => array(
// translators: %s is the integration name.
'title' => sprintf(__('Activate %s', 'wp-ultimo'), $this->get_title()),
'url' => wu_network_admin_url('wp-ultimo-hosting-integration-wizard', array(
'integration' => $slug,
)),
)
);
WP_Ultimo()->notices->add($message, 'info', 'network-admin', "should-enable-{$slug}-integration", $actions);
} // end alert_provider_detected;
/**
* Adds an admin notice telling the admin that the provider is not correctly setup.
*
* @since 2.0.0
* @return void
*/
public function alert_provider_not_setup() {
if (WP_Ultimo()->is_loaded() === false) {
return;
} // end if;
// translators: %1$s will be replaced with the integration title. E.g. RunCloud.
$message = sprintf(__('It looks like you are using %1$s as your hosting provider, yet the %1$s integration module was not properly setup. In order for the domain mapping integration to work with %1$s, you need to configure that module.', 'wp-ultimo'), $this->get_title());
$slug = $this->get_id();
$actions = array(
'activate' => array(
// translators: %s is the integration name
'title' => sprintf(__('Setup %s', 'wp-ultimo'), $this->get_title()),
'url' => wu_network_admin_url('wp-ultimo-hosting-integration-wizard', array(
'integration' => $slug,
'tab' => 'config',
)),
)
);
WP_Ultimo()->notices->add($message, 'warning', 'network-admin', "should-setup-{$slug}-integration", $actions);
} // end alert_provider_not_setup;
/**
* Get Fields for the integration.
*
* @since 2.0.0
* @return string
*/
public function get_fields() {
return array();
} // end get_fields;
/**
* Returns the integration id.
*
* @since 2.0.0
* @return string
*/
public function get_id() {
return $this->id;
} // end get_id;
/**
* Returns the integration title.
*
* @since 2.0.0
* @return string
*/
public function get_title() {
return $this->title;
} // end get_title;
/**
* Checks if a feature is supported, like auto-ssl for example.
*
* @since 2.0.0
* @param string $feature Feature to check.
* @return boolean
*/
public function supports($feature) {
return apply_filters('wu_hosting_support_supports', in_array($feature, $this->supports, true), $this);
} // end supports;
/**
* Initializes the hooks.
*
* @since 2.0.0
* @return void
*/
public function register_hooks() {
/*
* Hooks the event that is triggered when a new domain is added.
*/
add_action('wu_add_domain', array($this, 'on_add_domain'), 10, 2);
/*
* Hooks the event that is triggered when a domain is deleted.
*/
add_action('wu_remove_domain', array($this, 'on_remove_domain'), 10, 2);
/*
* Hooks the event that is triggered when a sub-domain is added.
*/
add_action('wu_add_subdomain', array($this, 'on_add_subdomain'), 10, 2);
/*
* Hooks the event that is triggered when a sub-domain is added.
*/
add_action('wu_remove_subdomain', array($this, 'on_remove_subdomain'), 10, 2);
/*
* Add additional hooks.
*/
$this->additional_hooks();
} // end register_hooks;
/**
* Lets integrations add additional hooks.
*
* @since 2.0.7
* @return void
*/
public function additional_hooks() {} // end additional_hooks;
/**
* Can be used to load dependencies.
*
* @since 2.0.0
* @return void
*/
public function load_dependencies() {} // end load_dependencies;
/**
* Picks up on tips that a given host provider is being used.
*
* We use this to suggest that the user should activate an integration module.
*
* @since 2.0.0
* @return boolean
*/
abstract public function detect();
/**
* Checks if the integration is correctly setup after enabled.
*
* @since 2.0.0
* @return boolean
*/
public function is_setup() {
$all_set = true;
foreach ($this->constants as $constant) {
$constants = is_array($constant) ? $constant : array($constant);
$current = false;
foreach ($constants as $constant) {
if (defined($constant) && constant($constant)) {
$current = true;
break;
} // end if;
} // end foreach;
$all_set = $all_set && $current;
/*
* If any constant fail, bail.
*/
if ($all_set === false) {
return false;
} // end if;
} // end foreach;
return $all_set;
} // end is_setup;
/**
* Returns a list of missing constants configured on wp-config.php
*
* @since 2.0.14
* @return array
*/
public function get_missing_constants() {
$missing_constants = array();
foreach ($this->constants as $constant) {
$constants = is_array($constant) ? $constant : array($constant);
$current = false;
foreach ($constants as $constant) {
if (defined($constant) && constant($constant)) {
$current = true;
break;
} // end if;
} // end foreach;
$missing_constants = $current ? $missing_constants : array_merge($missing_constants, $constants);
} // end foreach;
return $missing_constants;
} // end get_missing_constants;
/**
* Returns a list of all constants, optional or not.
*
* @since 2.0.0
* @return array
*/
public function get_all_constants() {
$constants = array();
foreach ($this->constants as $constant) {
$current = is_array($constant) ? $constant : array($constant);
$constants = array_merge($constants, $current);
} // end foreach;
return array_merge($constants, $this->optional_constants);
} // end get_all_constants;
/**
* Adds the constants with their respective values into the wp-config.php.
*
* @since 2.0.0
*
* @param array $constant_values Key => Value of the necessary constants.
* @return void
*/
public function setup_constants($constant_values) {
/*
* Important: This step is crucial, as it makes sure we clean up undesired constants.
* Removing this can allow insertion of arbitrary constants onto the wp-config.pp file
* and that should NEVER happen.
*
* Note that this is also performed on the get_constants_string.
*/
$values = shortcode_atts(array_flip($this->get_all_constants()), $constant_values);
foreach ($values as $constant => $value) {
WP_Config::get_instance()->inject_wp_config_constant($constant, $value);
} // end foreach;
} // end setup_constants;
/**
* Generates a define string for manual insertion on-to wp-config.php.
*
* This is useful when the user is not willing to let WP Ultimo inject the code,
* Or when the wp-config.php is not writable.
*
* @since 2.0.0
*
* @param array $constant_values Key => Value of the necessary constants.
* @return string
*/
public function get_constants_string($constant_values) {
/*
* Initializes the array with an opening comment.
*/
$content = array(
sprintf('// WP Ultimo - Domain Mapping - %s', $this->get_title()),
);
/*
* Important: This step is crucial, as it makes sure we clean up undesired constants.
* Removing this can allow insertion of arbitrary constants onto the wp-config.php file
* and that should NEVER happen.
*/
$constant_values = shortcode_atts(array_flip($this->get_all_constants()), $constant_values);
/*
* Adds the constants, one by one.
*/
foreach ($constant_values as $constant => $value) {
$content[] = sprintf("define( '%s', '%s' );", $constant, $value);
} // end foreach;
/*
* Adds the final line.
*/
$content[] = sprintf('// WP Ultimo - Domain Mapping - %s - End', $this->get_title());
return implode(PHP_EOL, $content);
} // end get_constants_string;
/**
* Returns the explainer lines for the integration.
*
* @since 2.0.0
* @return array
*/
public function get_explainer_lines() {
$explainer_lines = array(
'will' => array(
// translators: %s is the name of the integration e.g. RunCloud
'send_domains' => sprintf(__('Send API calls to %s servers with domain names added to this network', 'wp-ultimo'), $this->get_title()),
),
'will_not' => array(),
);
if ($this->supports('autossl')) {
// translators: %s is the name of the integration e.g. RunCloud
$explainer_lines['will'][] = sprintf(__('Fetch and install a SSL certificate on %s platform after the domain is added.', 'wp-ultimo'), $this->get_title());
} else {
// translators: %s is the name of the integration e.g. RunCloud
$explainer_lines['will_not'][] = sprintf(__('Fetch and install a SSL certificate on %s platform after the domain is added. This needs to be done manually.', 'wp-ultimo'), $this->get_title());
} // end if;
return $explainer_lines;
} // end get_explainer_lines;
/**
* This method gets called when a new domain is mapped.
*
* @since 2.0.0
* @param string $domain The domain name being mapped.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
abstract public function on_add_domain($domain, $site_id);
/**
* This method gets called when a mapped domain is removed.
*
* @since 2.0.0
* @param string $domain The domain name being removed.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
abstract public function on_remove_domain($domain, $site_id);
/**
* This method gets called when a new subdomain is being added.
*
* This happens every time a new site is added to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being added to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
abstract public function on_add_subdomain($subdomain, $site_id);
/**
* This method gets called when a new subdomain is being removed.
*
* This happens every time a new site is removed to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being removed to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
abstract public function on_remove_subdomain($subdomain, $site_id);
/**
* Tests the connection with the API.
*
* Needs to be implemented by integrations.
*
* @since 2.0.0
* @return void
*/
public function test_connection() {
wp_send_json_success(array());
} // end test_connection;
/**
* Returns the description of this integration.
*
* @since 2.0.0
* @return string
*/
public function get_description() {
return __('No description provided.', 'wp-ultimo');
} // end get_description;
/**
* Returns the logo for the integration.
*
* @since 2.0.0
* @return string
*/
public function get_logo() {
return '';
} // end get_logo;
} // end class Base_Host_Provider;

View File

@ -0,0 +1,245 @@
<?php
/**
* Adds domain mapping and auto SSL support to customer hosting networks on Closte.
*
* @package WP_Ultimo
* @subpackage Integrations/Host_Providers/Closte
* @since 2.0.0
*/
namespace WP_Ultimo\Integrations\Host_Providers;
use WP_Ultimo\Integrations\Host_Providers\Base_Host_Provider;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* This base class should be extended to implement new host integrations for SSL and domains.
*/
class Closte_Host_Provider extends Base_Host_Provider {
use \WP_Ultimo\Traits\Singleton;
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $id = 'closte';
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $title = 'Closte';
/**
* Link to the tutorial teaching how to make this integration work.
*
* @var string
* @since 2.0.0
*/
protected $tutorial_link = '';
/**
* Array containing the features this integration supports.
*
* @var array
* @since 2.0.0
*/
protected $supports = array(
'autossl',
'no-instructions',
'no-config',
);
/**
* Constants that need to be present on wp-config.php for this integration to work.
*
* @since 2.0.0
* @var array
*/
protected $constants = array(
'CLOSTE_CLIENT_API_KEY',
);
/**
* Picks up on tips that a given host provider is being used.
*
* We use this to suggest that the user should activate an integration module.
*
* @since 2.0.0
* @return boolean
*/
public function detect() {
return defined('CLOSTE_CLIENT_API_KEY') && CLOSTE_CLIENT_API_KEY;
} // end detect;
/**
* This method gets called when a new domain is mapped.
*
* @since 2.0.0
* @param string $domain The domain name being mapped.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_domain($domain, $site_id) {
$this->send_closte_api_request('/adddomainalias', array(
'domain' => $domain,
'wildcard' => strncmp($domain, '*.', strlen('*.')) === 0
));
} // end on_add_domain;
/**
* This method gets called when a mapped domain is removed.
*
* @since 2.0.0
* @param string $domain The domain name being removed.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_domain($domain, $site_id) {
$this->send_closte_api_request('/deletedomainalias', array(
'domain' => $domain,
'wildcard' => strncmp($domain, '*.', strlen('*.')) === 0
));
} // end on_remove_domain;
/**
* This method gets called when a new subdomain is being added.
*
* This happens every time a new site is added to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being added to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_subdomain($subdomain, $site_id) {} // end on_add_subdomain;
/**
* This method gets called when a new subdomain is being removed.
*
* This happens every time a new site is removed to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being removed to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_subdomain($subdomain, $site_id) {} // end on_remove_subdomain;
/**
* Tests the connection with the API.
*
* Needs to be implemented by integrations.
*
* @since 2.0.0
* @return void
*/
public function test_connection() {
$response = $this->send_closte_api_request('/adddomainalias', array());
if (wu_get_isset($response, 'error') === 'Invalid or empty domain: ') {
wp_send_json_success(array(
'message' => __('Access Authorized'),
));
} // end if;
$error = new \WP_Error('not-auth', __('Something went wrong', 'wp-ultimo'));
wp_send_json_error($error);
} // end test_connection;
/**
* Sends a request to Closte, with the right API key.
*
* @since 1.7.3
* @param string $endpoint Endpoint to send the call to.
* @param array $data Array containing the params to the call.
* @return object
*/
public function send_closte_api_request($endpoint, $data) {
if (defined('CLOSTE_CLIENT_API_KEY') === false) {
return (object) array(
'success' => false,
'error' => 'Closte API Key not found.',
);
} // end if;
$post_fields = array(
'blocking' => true,
'timeout' => 45,
'method' => 'POST',
'body' => array_merge(array(
'apikey' => CLOSTE_CLIENT_API_KEY,
), $data)
);
$response = wp_remote_post('https://app.closte.com/api/client' . $endpoint, $post_fields);
wu_log_add('integration-closte', wp_remote_retrieve_body($response));
if (!is_wp_error($response)) {
$body = json_decode(wp_remote_retrieve_body($response), true);
if (json_last_error() === JSON_ERROR_NONE) {
return $body;
} // end if;
return (object) array(
'success' => false,
'error' => 'unknown'
);
} // end if;
return $response;
} // end send_closte_api_request;
/**
* Returns the description of this integration.
*
* @since 2.0.0
* @return string
*/
public function get_description() {
return __('Closte is not just another web hosting who advertise their services as a cloud hosting while still provides fixed plans like in 1995.', 'wp-ultimo');
} // end get_description;
/**
* Returns the logo for the integration.
*
* @since 2.0.0
* @return string
*/
public function get_logo() {
return wu_get_asset('closte.svg', 'img/hosts');
} // end get_logo;
} // end class Closte_Host_Provider;

View File

@ -0,0 +1,497 @@
<?php
/**
* Adds domain mapping and auto SSL support to customer using Cloudflare.
*
* @package WP_Ultimo
* @subpackage Integrations/Host_Providers/Cloudflare_Host_Provider
* @since 2.0.0
*/
namespace WP_Ultimo\Integrations\Host_Providers;
use WP_Ultimo\Dependencies\Psr\Log\LogLevel;
use WP_Ultimo\Integrations\Host_Providers\Base_Host_Provider;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* This base class should be extended to implement new host integrations for SSL and domains.
*/
class Cloudflare_Host_Provider extends Base_Host_Provider {
use \WP_Ultimo\Traits\Singleton;
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $id = 'cloudflare';
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $title = 'Cloudflare';
/**
* Link to the tutorial teaching how to make this integration work.
*
* @var string
* @since 2.0.0
*/
protected $tutorial_link = '#';
/**
* Array containing the features this integration supports.
*
* @var array
* @since 2.0.0
*/
protected $supports = array(
'autossl',
);
/**
* Constants that need to be present on wp-config.php for this integration to work.
*
* @since 2.0.0
* @var array
*/
protected $constants = array(
'WU_CLOUDFLARE_API_KEY',
'WU_CLOUDFLARE_ZONE_ID',
);
/**
* Add Cloudflare own DNS entries to the comparison table.
*
* @since 2.0.4
*
* @param array $dns_records List of current dns records.
* @param string $domain The domain name.
* @return array
*/
public function add_cloudflare_dns_entries($dns_records, $domain) {
$zone_ids = array();
$default_zone_id = defined('WU_CLOUDFLARE_ZONE_ID') && WU_CLOUDFLARE_ZONE_ID ? WU_CLOUDFLARE_ZONE_ID : false;
if ($default_zone_id) {
$zone_ids[] = $default_zone_id;
} // end if;
$cloudflare_zones = $this->cloudflare_api_call('client/v4/zones', 'GET', array(
'name' => $domain,
'status' => 'active',
));
foreach ($cloudflare_zones->result as $zone) {
$zone_ids[] = $zone->id;
} // end foreach;
foreach ($zone_ids as $zone_id) {
/**
* First, try to detect the domain as a proxied on the current zone,
* if applicable
*/
$dns_entries = $this->cloudflare_api_call("client/v4/zones/$zone_id/dns_records/", 'GET', array(
'name' => $domain,
'match' => 'any',
'type' => 'A,AAAA,CNAME',
));
if (!empty($dns_entries->result)) {
$proxied_tag = sprintf('<span class="wu-bg-orange-500 wu-text-white wu-p-1 wu-rounded wu-text-3xs wu-uppercase wu-ml-2 wu-font-bold" %s>%s</span>', wu_tooltip_text(__('Proxied', 'wp-ultimo')), __('Cloudflare', 'wp-ultimo'));
$not_proxied_tag = sprintf('<span class="wu-bg-gray-700 wu-text-white wu-p-1 wu-rounded wu-text-3xs wu-uppercase wu-ml-2 wu-font-bold" %s>%s</span>', wu_tooltip_text(__('Not Proxied', 'wp-ultimo')), __('Cloudflare', 'wp-ultimo'));
foreach ($dns_entries->result as $entry) {
$dns_records[] = array(
'ttl' => $entry->ttl,
'data' => $entry->content,
'type' => $entry->type,
'host' => $entry->name,
'tag' => $entry->proxied ? $proxied_tag : $not_proxied_tag,
);
} // end foreach;
} // end if;
} // end foreach;
return $dns_records;
} // end add_cloudflare_dns_entries;
/**
* Picks up on tips that a given host provider is being used.
*
* We use this to suggest that the user should activate an integration module.
* Unfortunately, we don't have a good method of detecting if someone is running from cPanel.
*
* @since 2.0.0
*/
public function detect(): bool {
/**
* As Cloudflare recently enabled wildcards for all customers, this integration is no longer required.
* https://blog.cloudflare.com/wildcard-proxy-for-everyone/
*
* @since 2.1
*/
return false;
} // end detect;
/**
* Returns the list of installation fields.
*
* @since 2.0.0
* @return array
*/
public function get_fields() {
return array(
'WU_CLOUDFLARE_ZONE_ID' => array(
'title' => __('Zone ID', 'wp-ultimo'),
'placeholder' => __('e.g. 644c7705723d62e31f700bb798219c75', 'wp-ultimo'),
),
'WU_CLOUDFLARE_API_KEY' => array(
'title' => __('API Key', 'wp-ultimo'),
'placeholder' => __('e.g. xKGbxxVDpdcUv9dUzRf4i4ngv0QNf1wCtbehiec_o', 'wp-ultimo'),
),
);
} // end get_fields;
/**
* Tests the connection with the Cloudflare API.
*
* @since 2.0.0
* @return void
*/
public function test_connection() {
$results = $this->cloudflare_api_call('client/v4/user/tokens/verify');
if (is_wp_error($results)) {
wp_send_json_error($results);
} // end if;
wp_send_json_success($results);
} // end test_connection;
/**
* Lets integrations add additional hooks.
*
* @since 2.0.7
* @return void
*/
public function additional_hooks() {
add_filter('wu_domain_dns_get_record', array($this, 'add_cloudflare_dns_entries'), 10, 2);
} // end additional_hooks;
/**
* This method gets called when a new domain is mapped.
*
* @since 2.0.0
* @param string $domain The domain name being mapped.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_domain($domain, $site_id) {} // end on_add_domain;
/**
* This method gets called when a mapped domain is removed.
*
* @since 2.0.0
* @param string $domain The domain name being removed.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_domain($domain, $site_id) {} // end on_remove_domain;
/**
* This method gets called when a new subdomain is being added.
*
* This happens every time a new site is added to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being added to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_subdomain($subdomain, $site_id) {
global $current_site;
$zone_id = defined('WU_CLOUDFLARE_ZONE_ID') && WU_CLOUDFLARE_ZONE_ID ? WU_CLOUDFLARE_ZONE_ID : '';
if (!$zone_id) {
return;
} // end if;
if (strpos($subdomain, (string) $current_site->domain) === false) {
return; // Not a sub-domain of the main domain.
} // end if;
$subdomain = rtrim(str_replace($current_site->domain, '', $subdomain), '.');
if (!$subdomain) {
return;
} // end if;
$should_add_www = apply_filters('wu_cloudflare_should_add_www', true, $subdomain, $site_id);
$domains_to_send = array($subdomain);
/**
* Adds the www version, if necessary.
*/
if (strncmp($subdomain, 'www.', strlen('www.')) !== 0 && $should_add_www) {
$domains_to_send[] = 'www.' . $subdomain;
} // end if;
foreach ($domains_to_send as $subdomain) {
$should_proxy = apply_filters('wu_cloudflare_should_proxy', true, $subdomain, $site_id);
$data = apply_filters('wu_cloudflare_on_add_domain_data', array(
'type' => 'CNAME',
'name' => $subdomain,
'content' => '@',
'proxied' => $should_proxy,
'ttl' => 1,
), $subdomain, $site_id);
$results = $this->cloudflare_api_call("client/v4/zones/$zone_id/dns_records/", 'POST', $data);
if (is_wp_error($results)) {
wu_log_add('integration-cloudflare', sprintf('Failed to add subdomain "%s" to Cloudflare. Reason: %s', $subdomain, $results->get_error_message()), LogLevel::ERROR);
return;
} // end if;
wu_log_add('integration-cloudflare', sprintf('Added sub-domain "%s" to Cloudflare.', $subdomain));
} // end foreach;
} // end on_add_subdomain;
/**
* This method gets called when a new subdomain is being removed.
*
* This happens every time a new site is removed to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being removed to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_subdomain($subdomain, $site_id) {
global $current_site;
$zone_id = defined('WU_CLOUDFLARE_ZONE_ID') && WU_CLOUDFLARE_ZONE_ID ? WU_CLOUDFLARE_ZONE_ID : '';
if (!$zone_id) {
return;
} // end if;
if (strpos($subdomain, (string) $current_site->domain) === false) {
return; // Not a sub-domain of the main domain.
} // end if;
$original_subdomain = $subdomain;
$subdomain = rtrim(str_replace($current_site->domain, '', $subdomain), '.');
if (!$subdomain) {
return;
} // end if;
/**
* Created the list that we should remove.
*/
$domains_to_remove = array(
$original_subdomain,
'www.' . $original_subdomain,
);
foreach ($domains_to_remove as $original_subdomain) {
$dns_entries = $this->cloudflare_api_call("client/v4/zones/$zone_id/dns_records/", 'GET', array(
'name' => $original_subdomain,
'type' => 'CNAME',
));
if (!$dns_entries->result) {
return;
} // end if;
$dns_entry_to_remove = $dns_entries->result[0];
$results = $this->cloudflare_api_call("client/v4/zones/$zone_id/dns_records/$dns_entry_to_remove->id", 'DELETE');
if (is_wp_error($results)) {
wu_log_add('integration-cloudflare', sprintf('Failed to remove subdomain "%s" to Cloudflare. Reason: %s', $subdomain, $results->get_error_message()), LogLevel::ERROR);
return;
} // end if;
wu_log_add('integration-cloudflare', sprintf('Removed sub-domain "%s" to Cloudflare.', $subdomain));
} // end foreach;
} // end on_remove_subdomain;
/**
* Sends an API call to Cloudflare.
*
* @since 2.0.0
*
* @param string $endpoint The endpoint to call.
* @param string $method The HTTP verb. Defaults to GET.
* @param array $data The date to send.
* @return object|\WP_Error
*/
protected function cloudflare_api_call($endpoint = 'client/v4/user/tokens/verify', $method = 'GET', $data = array()): object {
$api_url = 'https://api.cloudflare.com/';
$endpoint_url = $api_url . $endpoint;
$response = wp_remote_request($endpoint_url, array(
'method' => $method,
'body' => $method === 'GET' ? $data : wp_json_encode($data),
'data_format' => 'body',
'headers' => array(
'Authorization' => sprintf('Bearer %s', defined('WU_CLOUDFLARE_API_KEY') ? WU_CLOUDFLARE_API_KEY : ''),
'Content-Type' => 'application/json',
),
));
if (!is_wp_error($response)) {
$body = wp_remote_retrieve_body($response);
if (wp_remote_retrieve_response_code($response) === 200) {
return json_decode($body);
} else {
$error_message = wp_remote_retrieve_response_message($response);
$response = new \WP_Error('cloudflare-error', sprintf('%s: %s', $error_message, $body));
} // end if;
} // end if;
return $response;
} // end cloudflare_api_call;
/**
* Renders the instructions content.
*
* @since 2.0.0
* @return void
*/
public function get_instructions() {
wu_get_template('wizards/host-integrations/cloudflare-instructions');
} // end get_instructions;
/**
* Returns the description of this integration.
*
* @since 2.0.0
* @return string
*/
public function get_description() {
return __('Cloudflare secures and ensures the reliability of your external-facing resources such as websites, APIs, and applications. It protects your internal resources such as behind-the-firewall applications, teams, and devices. And it is your platform for developing globally-scalable applications.', 'wp-ultimo');
} // end get_description;
/**
* Returns the logo for the integration.
*
* @since 2.0.0
* @return string
*/
public function get_logo() {
return wu_get_asset('cloudflare.svg', 'img/hosts');
} // end get_logo;
/**
* Returns the explainer lines for the integration.
*
* @since 2.0.0
* @return array
*/
public function get_explainer_lines() {
$explainer_lines = array(
'will' => array(),
'will_not' => array(),
);
if (is_subdomain_install()) {
$explainer_lines['will']['send_sub_domains'] = __('Add a new proxied subdomain to the configured CloudFlare zone whenever a new site gets created', 'wp-ultimo');
} else {
$explainer_lines['will']['subdirectory'] = __('Do nothing! The CloudFlare integration has no effect in subdirectory multisite installs such as this one', 'wp-ultimo');
} // end if;
$explainer_lines['will_not']['send_domain'] = __('Add domain mappings as new CloudFlare zones', 'wp-ultimo');
return $explainer_lines;
} // end get_explainer_lines;
} // end class Cloudflare_Host_Provider;

View File

@ -0,0 +1,590 @@
<?php
/**
* Adds domain mapping and auto SSL support to customer hosting networks on Cloudways.
*
* @package WP_Ultimo
* @subpackage Integrations/Host_Providers/Cloudways_Host_Provider
* @since 2.0.0
*/
namespace WP_Ultimo\Integrations\Host_Providers;
use WP_Ultimo\Dependencies\Psr\Log\LogLevel;
use WP_Ultimo\Domain_Mapping\Helper;
use WP_Ultimo\Integrations\Host_Providers\Base_Host_Provider;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* This base class should be extended to implement new host integrations for SSL and domains.
*/
class Cloudways_Host_Provider extends Base_Host_Provider {
use \WP_Ultimo\Traits\Singleton;
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $id = 'cloudways';
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $title = 'Cloudways';
/**
* Link to the tutorial teaching how to make this integration work.
*
* @var string
* @since 2.0.0
*/
protected $tutorial_link = 'https://help.wpultimo.com/article/294-configuring-automatic-domain-syncing-with-cloudways';
/**
* Array containing the features this integration supports.
*
* @var array
* @since 2.0.0
*/
protected $supports = array(
'autossl',
);
/**
* Constants that need to be present on wp-config.php for this integration to work.
*
* @since 2.0.0
* @var array
*/
protected $constants = array(
'WU_CLOUDWAYS_EMAIL',
'WU_CLOUDWAYS_API_KEY',
'WU_CLOUDWAYS_SERVER_ID',
'WU_CLOUDWAYS_APP_ID',
);
/**
* Constants that maybe present on wp-config.php for this integration to work.
*
* @since 2.0.0
* @var array
*/
protected $optional_constants = array(
'WU_CLOUDWAYS_EXTRA_DOMAINS',
);
/**
* Runs on singleton instantiation.
*
* @since 2.1.1
* @return void
*/
public function init() {
parent::init();
// Add the action to sync domains SSL.
add_action('wu_domain_manager_dns_propagation_finished', array($this, 'request_ssl'), 10, 0);
} // end init;
/**
* Runs a request to Cloudways API to install SSL, after ensuring that the domain is already mapped.
*
* @since 2.1.1
* @return void
*/
public function request_ssl() {
/**
* If the integration is not active, bail.
*/
if (!$this->is_enabled()) {
return;
} // end if;
$all_domains = $this->get_domains();
$ssl_response = $this->send_cloudways_request('/security/lets_encrypt_install', array(
'ssl_domains' => $this->get_valid_ssl_domains($all_domains),
));
if (is_wp_error($ssl_response)) {
wu_log_add('integration-cloudways', '[SSL]' . $ssl_response->get_error_message(), LogLevel::ERROR);
} else {
wu_log_add('integration-cloudways', '[SSL]' . print_r($ssl_response, true));
} // end if;
} // end request_ssl;
/**
* Picks up on tips that a given host provider is being used.
*
* We use this to suggest that the user should activate an integration module.
*
* @since 2.0.0
*/
public function detect(): bool {
return strpos(ABSPATH, 'cloudways') !== false;
} // end detect;
/**
* Returns the list of installation fields.
*
* @since 2.0.0
* @return array
*/
public function get_fields() {
return array(
'WU_CLOUDWAYS_EMAIL' => array(
'title' => __('Cloudways Account Email', 'wp-ultimo'),
'desc' => __('Your Cloudways account email address.', 'wp-ultimo'),
'placeholder' => __('e.g. me@gmail.com', 'wp-ultimo'),
),
'WU_CLOUDWAYS_API_KEY' => array(
'title' => __('Cloudways API Key', 'wp-ultimo'),
'desc' => __('The API Key retrieved in the previous step.', 'wp-ultimo'),
'placeholder' => __('e.g. eYP0Jo3Fzzm5SOZCi5nLR0Mki2lbYZ', 'wp-ultimo'),
),
'WU_CLOUDWAYS_SERVER_ID' => array(
'title' => __('Cloudways Server ID', 'wp-ultimo'),
'desc' => __('The Server ID retrieved in the previous step.', 'wp-ultimo'),
'placeholder' => __('e.g. 11667', 'wp-ultimo'),
),
'WU_CLOUDWAYS_APP_ID' => array(
'title' => __('Cloudways App ID', 'wp-ultimo'),
'desc' => __('The App ID retrieved in the previous step.', 'wp-ultimo'),
'placeholder' => __('e.g. 940288', 'wp-ultimo'),
),
'WU_CLOUDWAYS_EXTRA_DOMAINS' => array(
'title' => __('Cloudways Extra Domains', 'wp-ultimo'),
'tooltip' => __('The Cloudways API is a bit strange in that it doesnt offer a way to add or remove just one domain, only a way to update the whole domain list. That means that WP Ultimo will replace all domains you might have there with the list of mapped domains of the network every time a new domain is added.', 'wp-ultimo'),
'desc' => __('Comma-separated list of additional domains to add to Cloudways.', 'wp-ultimo'),
'placeholder' => __('e.g. *.test.com, test.com', 'wp-ultimo'),
),
);
} // end get_fields;
/**
* This method gets called when a new domain is mapped.
*
* @since 2.0.0
* @param string $domain The domain name being mapped.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_domain($domain, $site_id) {
$this->sync_domains();
} // end on_add_domain;
/**
* This method gets called when a mapped domain is removed.
*
* @since 2.0.0
* @param string $domain The domain name being removed.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_domain($domain, $site_id) {
$this->sync_domains();
} // end on_remove_domain;
/**
* This method gets called when a new subdomain is being added.
*
* This happens every time a new site is added to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being added to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_subdomain($subdomain, $site_id) {
} // end on_add_subdomain;
/**
* This method gets called when a new subdomain is being removed.
*
* This happens every time a new site is removed to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being removed to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_subdomain($subdomain, $site_id) {
} // end on_remove_subdomain;
/**
* Syncs the domains with the Cloudways API.
*
* @since 2.0.0
* @return void
*/
private function sync_domains() {
$all_domains = $this->get_domains();
$alias_response = $this->send_cloudways_request('/app/manage/aliases', array(
'aliases' => $all_domains,
));
if (is_wp_error($alias_response)) {
wu_log_add('integration-cloudways', '[Alias]' . $alias_response->get_error_message(), LogLevel::ERROR);
} else {
wu_log_add('integration-cloudways', '[Alias]' . print_r($alias_response, true));
} // end if;
} // end sync_domains;
/**
* Returns an array of valid SSL domains to be used with the Cloudways API based on a list of domains.
*
* @since 2.1.1
* @param array $domains List of domains to be used.
*/
private function get_valid_ssl_domains($domains): array {
$ssl_domains = array_unique(array_map(function($domain) {
if (strncmp($domain, '*.', strlen('*.')) === 0) {
$domain = str_replace('*.', '', $domain);
}
return $domain;
}, $domains));
$ssl_valid_domains = $this->check_domain_dns($ssl_domains, Helper::get_network_public_ip());
$main_domain = get_current_site()->domain;
// Adds the main domain to the list of valid domains.
$ssl_valid_domains[] = $main_domain;
return array_values(array_unique(array_filter($ssl_valid_domains)));
} // end get_valid_ssl_domains;
/**
* Returns an array of all domains that should be added to Cloudways.
*
* @modified 2.1.2 removed support to subdomains due to Cloudways SSL generation limitations.
* @since 2.1.1
*/
private function get_domains(): array {
$domain_list = $this->get_domain_list();
foreach ($domain_list as $naked_domain) {
if (strncmp((string) $naked_domain, 'www.', strlen('www.')) !== 0 && strncmp((string) $naked_domain, '*.', strlen('*.')) !== 0) {
$domain_list[] = 'www.' . $naked_domain;
} // end if;
} // end foreach;
sort($domain_list);
return array_values(array_unique(array_filter($domain_list)));
} // end get_domains;
/**
* Validates the DNS records for the domains.
* This is used to check if the domains are already pointing to the server.
*
* @since 2.1.1
* @param array $domain_names Array of domain names to be checked.
* @param string $network_ip The IP address of the server.
* @return array
*/
private function check_domain_dns($domain_names, $network_ip) {
$valid_domains = array();
foreach ($domain_names as $domain_name) {
$response = wp_remote_get('https://dns.google/resolve?name=' . $domain_name);
if (is_wp_error($response)) {
wu_log_add('integration-cloudways', $response->get_error_message(), LogLevel::ERROR);
continue;
} // end if;
$data = json_decode(wp_remote_retrieve_body($response), true);
if (isset($data['Answer'])) {
foreach ($data['Answer'] as $answer) {
if ($answer['data'] === $network_ip) {
$valid_domains[] = $domain_name;
break;
} // end if;
} // end foreach;
} // end if;
} // end foreach;
return $valid_domains;
} // end check_domain_dns;
/**
* Tests the connection with the Cloudways API.
*
* @since 2.0.0
* @return void
*/
public function test_connection() {
$response = $this->send_cloudways_request('/app/manage/fpm_setting', array(), 'GET');
if (is_wp_error($response) || wu_get_isset($response, 'error')) {
wp_send_json_error($response);
} // end if;
wp_send_json_success($response);
} // end test_connection;
/**
* Returns an array of all the mapped domains currently on the network
*
* @since 1.6.0
* @return array
*/
public function get_all_mapped_domains() {
global $wpdb;
$final_domain_list = array();
// Prepare the query
$query = "SELECT domain FROM {$wpdb->base_prefix}wu_domain_mappings";
// Suppress errors in case the table doesn't exist.
$suppress = $wpdb->suppress_errors();
$mappings = $wpdb->get_col($query, 0); // phpcs:ignore
foreach ($mappings as $domain) {
$final_domain_list[] = $domain;
if (strncmp((string) $domain, 'www.', strlen('www.')) !== 0) {
$final_domain_list[] = "www.$domain";
} // end if;
} // end foreach;
$wpdb->suppress_errors($suppress);
return $final_domain_list;
} // end get_all_mapped_domains;
/**
* Get extra domains for Cloudways
*
* @since 1.6.1
* @return array
*/
protected function get_domain_list() {
$domain_list = $this->get_all_mapped_domains();
$extra_domains = defined('WU_CLOUDWAYS_EXTRA_DOMAINS') && WU_CLOUDWAYS_EXTRA_DOMAINS;
if ($extra_domains) {
$extra_domains_list = array_filter(array_map('trim', explode(',', (string) WU_CLOUDWAYS_EXTRA_DOMAINS)));
$domain_list = array_merge($domain_list, $extra_domains_list);
} // end if;
return $domain_list;
} // end get_domain_list;
/**
* Fetches and saves a Cloudways access token.
*
* @since 2.0.0
* @return string
*/
protected function get_cloudways_access_token() {
$token = get_site_transient('wu_cloudways_token');
if (!$token) {
$response = wp_remote_post('https://api.cloudways.com/api/v1/oauth/access_token', array(
'blocking' => true,
'method' => 'POST',
'headers' => array(
'cache-control' => 'no-cache',
'content-type' => 'application/x-www-form-urlencoded',
),
'body' => array(
'email' => defined('WU_CLOUDWAYS_EMAIL') ? WU_CLOUDWAYS_EMAIL : '',
'api_key' => defined('WU_CLOUDWAYS_API_KEY') ? WU_CLOUDWAYS_API_KEY : '',
),
));
if (!is_wp_error($response)) {
$body = json_decode(wp_remote_retrieve_body($response), true);
if (isset($body['access_token'])) {
$expires_in = isset($body['expires_in']) ? $body['expires_in'] : 50 * MINUTE_IN_SECONDS;
set_site_transient('wu_cloudways_token', $body['access_token'], $expires_in);
$token = $body['access_token'];
} // end if;
} // end if;
} // end if;
return $token;
} // end get_cloudways_access_token;
/**
* Sends a request to the Cloudways API.
*
* @since 2.0.0
*
* @param string $endpoint The API endpoint.
* @param array $data The data to send.
* @param string $method The HTTP verb.
* @return object|\WP_Error
*/
protected function send_cloudways_request($endpoint, $data = array(), $method = 'POST'): object {
$token = $this->get_cloudways_access_token();
$endpoint = '/' . ltrim($endpoint, '/');
$endpoint_url = "https://api.cloudways.com/api/v1$endpoint";
if ($method === 'GET') {
$endpoint_url = add_query_arg(array(
'server_id' => defined('WU_CLOUDWAYS_SERVER_ID') ? WU_CLOUDWAYS_SERVER_ID : '',
'app_id' => defined('WU_CLOUDWAYS_APP_ID') ? WU_CLOUDWAYS_APP_ID : '',
), $endpoint_url);
} else {
$data['server_id'] = defined('WU_CLOUDWAYS_SERVER_ID') ? WU_CLOUDWAYS_SERVER_ID : '';
$data['app_id'] = defined('WU_CLOUDWAYS_APP_ID') ? WU_CLOUDWAYS_APP_ID : '';
$data['ssl_email'] = defined('WU_CLOUDWAYS_EMAIL') ? WU_CLOUDWAYS_EMAIL : '';
$data['wild_card'] = false;
} // end if;
$response = wp_remote_post($endpoint_url, array(
'blocking' => true,
'method' => $method,
'timeout' => 45,
'body' => $data,
'headers' => array(
'cache-control' => 'no-cache',
'content-type' => 'application/x-www-form-urlencoded',
'authorization' => "Bearer $token",
),
));
if (is_wp_error($response)) {
return $response;
} // end if;
$response_data = wp_remote_retrieve_body($response);
return json_decode($response_data);
} // end send_cloudways_request;
/**
* Renders the instructions content.
*
* @since 2.0.0
* @return void
*/
public function get_instructions() {
wu_get_template('wizards/host-integrations/cloudways-instructions');
} // end get_instructions;
/**
* Returns the description of this integration.
*
* @since 2.0.0
* @return string
*/
public function get_description() {
return __('Focus on your business and avoid all the web hosting hassles. Our managed hosting guarantees unmatched performance, reliability and choice with 24/7 support that acts as your extended team, making Cloudways an ultimate choice for growing agencies and e-commerce businesses.', 'wp-ultimo');
} // end get_description;
/**
* Returns the logo for the integration.
*
* @since 2.0.0
* @return string
*/
public function get_logo() {
return wu_get_asset('cloudways.png', 'img/hosts');
} // end get_logo;
} // end class Cloudways_Host_Provider;

View File

@ -0,0 +1,386 @@
<?php
/**
* Adds domain mapping and auto SSL support to customer hosting networks on cPanel.
*
* @package WP_Ultimo
* @subpackage Integrations/Host_Providers/CPanel_Host_Provider
* @since 2.0.0
*/
namespace WP_Ultimo\Integrations\Host_Providers;
use WP_Ultimo\Dependencies\Psr\Log\LogLevel;
use WP_Ultimo\Integrations\Host_Providers\Base_Host_Provider;
use WP_Ultimo\Integrations\Host_Providers\CPanel_API\CPanel_API;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* This base class should be extended to implement new host integrations for SSL and domains.
*/
class CPanel_Host_Provider extends Base_Host_Provider {
use \WP_Ultimo\Traits\Singleton;
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $id = 'cpanel';
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $title = 'cPanel';
/**
* Link to the tutorial teaching how to make this integration work.
*
* @var string
* @since 2.0.0
*/
protected $tutorial_link = 'https://help.wpultimo.com/article/295-configuring-automatic-domain-syncing-with-cpanel';
/**
* Array containing the features this integration supports.
*
* @var array
* @since 2.0.0
*/
protected $supports = array(
'autossl',
'no-instructions',
);
/**
* Constants that need to be present on wp-config.php for this integration to work.
*
* @since 2.0.0
* @var array
*/
protected $constants = array(
'WU_CPANEL_USERNAME',
'WU_CPANEL_PASSWORD',
'WU_CPANEL_HOST',
);
/**
* Constants that are optional on wp-config.php.
*
* @since 2.0.0
* @var array
*/
protected $optional_constants = array(
'WU_CPANEL_PORT',
'WU_CPANEL_ROOT_DIR'
);
/**
* Holds the API object.
*
* @since 2.0.0
* @var WP_Ultimo\Integrations\Host_Providers\CPanel_API\CPanel_API
*/
protected $api = null;
/**
* Picks up on tips that a given host provider is being used.
*
* We use this to suggest that the user should activate an integration module.
* Unfortunately, we don't have a good method of detecting if someone is running from cPanel.
*
* @since 2.0.0
*/
public function detect(): bool {
return false;
} // end detect;
/**
* Returns the list of installation fields.
*
* @since 2.0.0
* @return array
*/
public function get_fields() {
return array(
'WU_CPANEL_USERNAME' => array(
'title' => __('cPanel Username', 'wp-ultimo'),
'placeholder' => __('e.g. username', 'wp-ultimo'),
),
'WU_CPANEL_PASSWORD' => array(
'type' => 'password',
'title' => __('cPanel Password', 'wp-ultimo'),
'placeholder' => __('password', 'wp-ultimo'),
),
'WU_CPANEL_HOST' => array(
'title' => __('cPanel Host', 'wp-ultimo'),
'placeholder' => __('e.g. yourdomain.com', 'wp-ultimo'),
),
'WU_CPANEL_PORT' => array(
'title' => __('cPanel Port', 'wp-ultimo'),
'placeholder' => __('Defaults to 2083', 'wp-ultimo'),
'value' => 2083,
),
'WU_CPANEL_ROOT_DIR' => array(
'title' => __('Root Directory', 'wp-ultimo'),
'placeholder' => __('Defaults to /public_html', 'wp-ultimo'),
'value' => '/public_html',
),
);
} // end get_fields;
/**
* This method gets called when a new domain is mapped.
*
* @since 2.0.0
* @param string $domain The domain name being mapped.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_domain($domain, $site_id) {
// Root Directory
$root_dir = defined('WU_CPANEL_ROOT_DIR') && WU_CPANEL_ROOT_DIR ? WU_CPANEL_ROOT_DIR : '/public_html';
// Send Request
$results = $this->load_api()->api2('AddonDomain', 'addaddondomain', array(
'dir' => $root_dir,
'newdomain' => $domain,
'subdomain' => $this->get_subdomain($domain),
));
$this->log_calls($results);
} // end on_add_domain;
/**
* This method gets called when a mapped domain is removed.
*
* @since 2.0.0
* @param string $domain The domain name being removed.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_domain($domain, $site_id) {
// Send Request
$results = $this->load_api()->api2('AddonDomain', 'deladdondomain', array(
'domain' => $domain,
'subdomain' => $this->get_subdomain($domain) . '_' . $this->get_site_url(),
));
$this->log_calls($results);
} // end on_remove_domain;
/**
* This method gets called when a new subdomain is being added.
*
* This happens every time a new site is added to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being added to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_subdomain($subdomain, $site_id) {
// Root Directory
$root_dir = defined('WU_CPANEL_ROOT_DIR') && WU_CPANEL_ROOT_DIR ? WU_CPANEL_ROOT_DIR : '/public_html';
$subdomain = $this->get_subdomain($subdomain, false);
$rootdomain = str_replace($subdomain . '.', '', $this->get_site_url($site_id));
// Send Request
$results = $this->load_api()->api2('SubDomain', 'addsubdomain', array(
'dir' => $root_dir,
'domain' => $subdomain,
'rootdomain' => $rootdomain,
));
// Check the results
$this->log_calls($results);
} // end on_add_subdomain;
/**
* This method gets called when a new subdomain is being removed.
*
* This happens every time a new site is removed to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being removed to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_subdomain($subdomain, $site_id) {} // end on_remove_subdomain;
/**
* Load the CPanel API.
*
* @since 2.0.0
* @return WU_CPanel
*/
public function load_api() {
if ($this->api === null) {
$username = defined('WU_CPANEL_USERNAME') ? WU_CPANEL_USERNAME : '';
$password = defined('WU_CPANEL_PASSWORD') ? WU_CPANEL_PASSWORD : '';
$host = defined('WU_CPANEL_HOST') ? WU_CPANEL_HOST : '';
$port = defined('WU_CPANEL_PORT') && WU_CPANEL_PORT ? WU_CPANEL_PORT : 2083;
/*
* Set up the API.
*/
$this->api = new CPanel_API($username, $password, preg_replace('#^https?://#', '', (string) $host), $port);
} // end if;
return $this->api;
} // end load_api;
/**
* Returns the Site URL.
*
* @since 1.6.2
* @param null|int $site_id The site id.
*/
public function get_site_url($site_id = null): string {
return trim(preg_replace('#^https?://#', '', get_site_url($site_id)), '/');
} // end get_site_url;
/**
* Returns the sub-domain version of the domain.
*
* @since 1.6.2
* @param string $domain The domain to be used.
* @param string $mapped_domain If this is a mapped domain.
* @return string
*/
public function get_subdomain($domain, $mapped_domain = true) {
if ($mapped_domain === false) {
$domain_parts = explode('.', $domain);
return array_shift($domain_parts);
} // end if;
$subdomain = str_replace(array('.', '/'), '', $domain);
return $subdomain;
} // end get_subdomain;
/**
* Logs the results of the calls for debugging purposes
*
* @since 1.6.2
* @param object $results Results of the cPanel call.
* @return bool
*/
public function log_calls($results) {
if (is_object($results->cpanelresult->data)) {
return wu_log_add('integration-cpanel', $results->cpanelresult->data->reason);
} elseif (!isset($results->cpanelresult->data[0])) {
return wu_log_add('integration-cpanel', __('Unexpected error ocurred trying to sync domains with CPanel', 'wp-ultimo'), LogLevel::ERROR);
} // end if;
return wu_log_add('integration-cpanel', $results->cpanelresult->data[0]->reason);
} // end log_calls;
/**
* Returns the description of this integration.
*
* @since 2.0.0
* @return string
*/
public function get_description() {
return __('cPanel is the management panel being used on a large number of shared and dedicated hosts across the globe.', 'wp-ultimo');
} // end get_description;
/**
* Returns the logo for the integration.
*
* @since 2.0.0
* @return string
*/
public function get_logo() {
return wu_get_asset('cpanel.svg', 'img/hosts');
} // end get_logo;
/**
* Tests the connection with the Cloudflare API.
*
* @since 2.0.0
* @return void
*/
public function test_connection() {
$results = $this->load_api()->api2('Cron', 'fetchcron', array());
$this->log_calls($results);
if (isset($results->cpanelresult->data) && !isset($results->cpanelresult->error)) {
wp_send_json_success($results);
exit;
} // end if;
wp_send_json_error($results);
} // end test_connection;
/**
* Returns the explainer lines for the integration.
*
* @since 2.0.0
* @return array
*/
public function get_explainer_lines() {
$explainer_lines = array(
'will' => array(
'send_domains' => __('Add a new Addon Domain on cPanel whenever a new domain mapping gets created on your network', 'wp-ultimo'),
),
'will_not' => array(),
);
if (is_subdomain_install()) {
$explainer_lines['will']['send_sub_domains'] = __('Add a new SubDomain on cPanel whenever a new site gets created on your network', 'wp-ultimo');
} // end if;
return $explainer_lines;
} // end get_explainer_lines;
} // end class CPanel_Host_Provider;

View File

@ -0,0 +1,266 @@
<?php
/**
* Adds domain mapping and auto SSL support to customer hosting networks on WPMU DEV.
*
* @package WP_Ultimo
* @subpackage Integrations/Host_Providers/Gridpane_Host_Provider
* @since 2.0.0
*/
namespace WP_Ultimo\Integrations\Host_Providers;
use WP_Ultimo\Integrations\Host_Providers\Base_Host_Provider;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* This base class should be extended to implement new host integrations for SSL and domains.
*/
class Gridpane_Host_Provider extends Base_Host_Provider {
use \WP_Ultimo\Traits\Singleton;
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $id = 'gridpane';
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $title = 'Gridpane';
/**
* Link to the tutorial teaching how to make this integration work.
*
* @var string
* @since 2.0.0
*/
protected $tutorial_link = '';
/**
* Array containing the features this integration supports.
*
* @var array
* @since 2.0.0
*/
protected $supports = array(
'autossl',
'no-config',
);
/**
* Constants that need to be present on wp-config.php for this integration to work.
*
* @since 2.0.0
* @var array
*/
protected $constants = array(
'WU_GRIDPANE',
);
/**
* Picks up on tips that a given host provider is being used.
*
* We use this to suggest that the user should activate an integration module.
*
* @since 2.0.0
* @return boolean
*/
public function detect() {
return defined('GRIDPANE') && GRIDPANE;
} // end detect;
/**
* Enables this integration.
*
* @since 2.0.0
* @return void
*/
public function enable() {
/*
* Prevent issues with Gridpane
*/
$success = \WP_Ultimo\Helpers\WP_Config::get_instance()->revert('SUNRISE');
parent::enable();
} // end enable;
/**
* Sends a request to the GridPane API.
*
* @since 2.0.0
*
* @param string $endpoint The endpoint to hit.
* @param array $data The post body to send to the API.
* @param string $method The HTTP method.
* @return mixed
*/
public function send_gridpane_api_request($endpoint, $data = array(), $method = 'POST') {
$post_fields = array(
'timeout' => 45,
'blocking' => true,
'method' => $method,
'body' => array_merge(array(
'api_token' => WU_GRIDPANE_API_KEY,
), $data)
);
$response = wp_remote_request("https://my.gridpane.com/api/{$endpoint}", $post_fields);
if (!is_wp_error($response)) {
$body = json_decode(wp_remote_retrieve_body($response), true);
if (json_last_error() === JSON_ERROR_NONE) {
return $body;
} // end if;
} // end if;
return $response;
} // end send_gridpane_api_request;
/**
* This method gets called when a new domain is mapped.
*
* @since 2.0.0
* @param string $domain The domain name being mapped.
* @param int $site_id ID of the site that is receiving that mapping.
* @return object\WP_Error
*/
public function on_add_domain($domain, $site_id) {
return $this->send_gridpane_api_request('application/add-domain', array(
'server_ip' => WU_GRIDPANE_SERVER_ID,
'site_url' => WU_GRIDPANE_APP_ID,
'domain_url' => $domain
));
} // end on_add_domain;
/**
* This method gets called when a mapped domain is removed.
*
* @since 2.0.0
* @param string $domain The domain name being removed.
* @param int $site_id ID of the site that is receiving that mapping.
* @return object\WP_Error
*/
public function on_remove_domain($domain, $site_id) {
return $this->send_gridpane_api_request('application/delete-domain', array(
'server_ip' => WU_GRIDPANE_SERVER_ID,
'site_url' => WU_GRIDPANE_APP_ID,
'domain_url' => $domain
));
} // end on_remove_domain;
/**
* This method gets called when a new subdomain is being added.
*
* This happens every time a new site is added to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being added to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_subdomain($subdomain, $site_id) {} // end on_add_subdomain;
/**
* This method gets called when a new subdomain is being removed.
*
* This happens every time a new site is removed to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being removed to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_subdomain($subdomain, $site_id) {} // end on_remove_subdomain;
/**
* Tests the connection with the Gridpane API.
*
* @since 2.0.0
* @return void
*/
public function test_connection() {
$results = $this->on_remove_domain('test.com', false);
if (wu_get_isset($results, 'message') === 'This action is unauthorized.') {
wp_send_json_error(array(
'error' => __('We were not able to successfully establish a connection.', 'wp-ultimo'),
));
} // end if;
if (is_wp_error($results)) {
wp_send_json_error(array(
'error' => __('We were not able to successfully establish a connection.', 'wp-ultimo'),
));
} // end if;
wp_send_json_success(array(
'success' => __('Connection successfully established.', 'wp-ultimo'),
));
} // end test_connection;
/**
* Renders the instructions content.
*
* @since 2.0.0
* @return void
*/
public function get_instructions() {
wu_get_template('wizards/host-integrations/gridpane-instructions');
} // end get_instructions;
/**
* Returns the description of this integration.
*
* @since 2.0.0
* @return string
*/
public function get_description() {
return __("GridPane is the world's first hosting control panel built exclusively for serious WordPress professionals.", 'wp-ultimo');
} // end get_description;
/**
* Returns the logo for the integration.
*
* @since 2.0.0
* @return string
*/
public function get_logo() {
return wu_get_asset('gridpane.png', 'img/hosts');
} // end get_logo;
} // end class Gridpane_Host_Provider;

View File

@ -0,0 +1,433 @@
<?php
/**
* Adds domain mapping and auto SSL support to customer hosting networks on RunCloud.
*
* @package WP_Ultimo
* @subpackage Integrations/Host_Providers/Runcloud_Host_Provider
* @since 2.0.0
*/
namespace WP_Ultimo\Integrations\Host_Providers;
use WP_Ultimo\Dependencies\Psr\Log\LogLevel;
use WP_Ultimo\Integrations\Host_Providers\Base_Host_Provider;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* This base class should be extended to implement new host integrations for SSL and domains.
*/
class Runcloud_Host_Provider extends Base_Host_Provider {
use \WP_Ultimo\Traits\Singleton;
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $id = 'runcloud';
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $title = 'RunCloud';
/**
* Link to the tutorial teaching how to make this integration work.
*
* @var string
* @since 2.0.0
*/
protected $tutorial_link = 'https://help.wpultimo.com/en/articles/2636845-configuring-automatic-domain-syncing-with-runcloud-io';
/**
* Array containing the features this integration supports.
*
* @var array
* @since 2.0.0
*/
protected $supports = array(
'autossl',
);
/**
* Constants that need to be present on wp-config.php for this integration to work.
*
* @since 2.0.0
* @var array
*/
protected $constants = array(
'WU_RUNCLOUD_API_KEY',
'WU_RUNCLOUD_API_SECRET',
'WU_RUNCLOUD_SERVER_ID',
'WU_RUNCLOUD_APP_ID',
);
/**
* Picks up on tips that a given host provider is being used.
*
* We use this to suggest that the user should activate an integration module.
*
* @since 2.0.0
*/
public function detect(): bool {
return strpos(ABSPATH, 'runcloud') !== false;
} // end detect;
/**
* Returns the list of installation fields.
*
* @since 2.0.0
* @return array
*/
public function get_fields() {
return array(
'WU_RUNCLOUD_API_KEY' => array(
'title' => __('RunCloud API Key', 'wp-ultimo'),
'desc' => __('The API Key retrieved in the previous step.', 'wp-ultimo'),
'placeholder' => __('e.g. Sx9tHAn5XMrkeyZKS1a7uj8dGTLgKnlEOaJEFRt1m95L', 'wp-ultimo'),
),
'WU_RUNCLOUD_API_SECRET' => array(
'title' => __('RunCloud API Secret', 'wp-ultimo'),
'desc' => __('The API secret retrieved in the previous step.', 'wp-ultimo'),
'placeholder' => __('e.g. ZlAebXp2sa6J5xsrPoiPcMXZRIVsHJ2rEkNCNGknZnF0UK5cSNSePS8GBW9FXIQd', 'wp-ultimo'),
),
'WU_RUNCLOUD_SERVER_ID' => array(
'title' => __('RunCloud Server ID', 'wp-ultimo'),
'desc' => __('The Server ID retrieved in the previous step.', 'wp-ultimo'),
'placeholder' => __('e.g. 11667', 'wp-ultimo'),
),
'WU_RUNCLOUD_APP_ID' => array(
'title' => __('RunCloud App ID', 'wp-ultimo'),
'desc' => __('The App ID retrieved in the previous step.', 'wp-ultimo'),
'placeholder' => __('e.g. 940288', 'wp-ultimo'),
),
);
} // end get_fields;
/**
* This method gets called when a new domain is mapped.
*
* @since 2.0.0
* @param string $domain The domain name being mapped.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_domain($domain, $site_id) {
$success = false;
$response = $this->send_runcloud_request($this->get_runcloud_base_url('domains'), array(
'name' => $domain,
'www' => true,
'redirection' => 'non-www'
), 'POST');
if (is_wp_error($response)) {
wu_log_add('integration-runcloud', $response->get_error_message(), LogLevel::ERROR);
} else {
$success = true; // At least one of the calls was successful;
wu_log_add('integration-runcloud', wp_remote_retrieve_body($response));
} // end if;
/**
* Only redeploy SSL if at least one of the domains were successfully added
*/
if ($success) {
$ssl_id = $this->get_runcloud_ssl_id();
if ($ssl_id) {
$this->redeploy_runcloud_ssl($ssl_id);
} // end if;
} // end if;
} // end on_add_domain;
/**
* This method gets called when a mapped domain is removed.
*
* @since 2.0.0
* @param string $domain The domain name being removed.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_domain($domain, $site_id) {
$domain_id = $this->get_runcloud_domain_id($domain);
if (!$domain_id) {
wu_log_add('integration-runcloud', __('Domain name not found on runcloud', 'wp-ultimo'));
} // end if;
$response = $this->send_runcloud_request($this->get_runcloud_base_url("domains/$domain_id"), array(), 'DELETE');
if (is_wp_error($response)) {
wu_log_add('integration-runcloud', $response->get_error_message(), LogLevel::ERROR);
} else {
wu_log_add('integration-runcloud', wp_remote_retrieve_body($response));
} // end if;
} // end on_remove_domain;
/**
* This method gets called when a new subdomain is being added.
*
* This happens every time a new site is added to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being added to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_subdomain($subdomain, $site_id) {} // end on_add_subdomain;
/**
* This method gets called when a new subdomain is being removed.
*
* This happens every time a new site is removed to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being removed to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_subdomain($subdomain, $site_id) {} // end on_remove_subdomain;
/**
* Tests the connection with the RunCloud API.
*
* @since 2.0.0
* @return void
*/
public function test_connection() {
$response = $this->send_runcloud_request($this->get_runcloud_base_url('domains'), array(), 'GET');
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
wp_send_json_error($response);
} else {
wp_send_json_success($this->maybe_return_runcloud_body($response));
} // end if;
} // end test_connection;
/**
* Returns the base domain API url to our calls.
*
* @since 1.7.0
* @param string $path Path relative to the main endpoint.
* @return string
*/
public function get_runcloud_base_url($path = '') {
$serverid = defined('WU_RUNCLOUD_SERVER_ID') ? WU_RUNCLOUD_SERVER_ID : '';
$appid = defined('WU_RUNCLOUD_APP_ID') ? WU_RUNCLOUD_APP_ID : '';
return "https://manage.runcloud.io/api/v2/servers/{$serverid}/webapps/{$appid}/{$path}";
} // end get_runcloud_base_url;
/**
* Sends the request to a given runcloud URL with a given body.
*
* @since 1.7.0
* @param string $url Endpoinbt to send the request to.
* @param array $data Data to be sent.
* @param string $method HTTP Method to send. Defaults to POST.
* @return array
*/
public function send_runcloud_request($url, $data = array(), $method = 'POST') {
$username = defined('WU_RUNCLOUD_API_KEY') ? WU_RUNCLOUD_API_KEY : '';
$password = defined('WU_RUNCLOUD_API_SECRET') ? WU_RUNCLOUD_API_SECRET : '';
$response = wp_remote_request($url, array(
'timeout' => 100,
'redirection' => 5,
'body' => $data,
'method' => $method,
'headers' => array(
'Authorization' => 'Basic ' . base64_encode($username . ':' . $password),
),
));
return $response;
} // end send_runcloud_request;
/**
* Treats the response, maybe returning the json decoded version
*
* @since 1.7.0
* @param array $response The response.
* @return mixed
*/
public function maybe_return_runcloud_body($response) {
if (is_wp_error($response)) {
return $response->get_error_message();
} else {
return json_decode(wp_remote_retrieve_body($response));
} // end if;
} // end maybe_return_runcloud_body;
/**
* Returns the RunCloud.io domain id to remove.
*
* @since 1.7.0
* @param string $domain The domain name being removed.
* @return string
*/
public function get_runcloud_domain_id($domain) {
$domains_list = $this->send_runcloud_request($this->get_runcloud_base_url('domains'), array(), 'GET');
$list = $this->maybe_return_runcloud_body($domains_list);
if (is_object($list) && !empty($list->data)) {
foreach ($list->data as $remote_domain) {
if ($remote_domain->name === $domain) {
return $remote_domain->id;
} // end if;
} // end foreach;
} // end if;
return false;
} // end get_runcloud_domain_id;
/**
* Checks if RunCloud has a SSL cert installed or not, and returns the ID.
*
* @since 1.10.4
* @return bool|int
*/
public function get_runcloud_ssl_id() {
$ssl_id = false;
$response = $this->send_runcloud_request($this->get_runcloud_base_url('ssl'), array(), 'GET');
if (is_wp_error($response)) {
wu_log_add('integration-runcloud', $response->get_error_message(), LogLevel::ERROR);
} else {
$data = $this->maybe_return_runcloud_body($response);
wu_log_add('integration-runcloud', json_encode($data));
if (property_exists($data, 'id')) {
$ssl_id = $data->id;
} // end if;
} // end if;
return $ssl_id;
} // end get_runcloud_ssl_id;
/**
* Redeploys the SSL cert when a new domain is added.
*
* @since 1.10.4
* @param int $ssl_id The SSL id on RunCloud.
* @return void
*/
public function redeploy_runcloud_ssl($ssl_id) {
$response = $this->send_runcloud_request($this->get_runcloud_base_url("ssl/$ssl_id"), array(), 'PUT');
if (is_wp_error($response)) {
wu_log_add('integration-runcloud', $response->get_error_message(), LogLevel::ERROR);
} else {
wu_log_add('integration-runcloud', wp_remote_retrieve_body($response));
} // end if;
} // end redeploy_runcloud_ssl;
/**
* Renders the instructions content.
*
* @since 2.0.0
* @return void
*/
public function get_instructions() {
wu_get_template('wizards/host-integrations/runcloud-instructions');
} // end get_instructions;
/**
* Returns the description of this integration.
*
* @since 2.0.0
* @return string
*/
public function get_description() {
return __('With RunCloud, you dont need to be a Linux expert to build a website powered by DigitalOcean, AWS, or Google Cloud. Use our graphical interface and build a business on the cloud affordably.', 'wp-ultimo');
} // end get_description;
/**
* Returns the logo for the integration.
*
* @since 2.0.0
* @return string
*/
public function get_logo() {
return wu_get_asset('runcloud.svg', 'img/hosts');
} // end get_logo;
} // end class Runcloud_Host_Provider;

View File

@ -0,0 +1,343 @@
<?php
/**
* Adds domain mapping and auto SSL support to customer hosting networks on ServerPilot.
*
* @package WP_Ultimo
* @subpackage Integrations/Host_Providers/ServerPilot_Host_Provider
* @since 2.0.0
*/
namespace WP_Ultimo\Integrations\Host_Providers;
use WP_Ultimo\Dependencies\Psr\Log\LogLevel;
use WP_Ultimo\Integrations\Host_Providers\Base_Host_Provider;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* This base class should be extended to implement new host integrations for SSL and domains.
*/
class ServerPilot_Host_Provider extends Base_Host_Provider {
use \WP_Ultimo\Traits\Singleton;
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $id = 'serverpilot';
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $title = 'ServerPilot';
/**
* Link to the tutorial teaching how to make this integration work.
*
* @var string
* @since 2.0.0
*/
protected $tutorial_link = 'https://help.wpultimo.com/en/articles/2632349-configuring-automatic-domain-syncing-with-serverpilot-io-with-autossl-support';
/**
* Array containing the features this integration supports.
*
* @var array
* @since 2.0.0
*/
protected $supports = array(
'autossl',
);
/**
* Constants that need to be present on wp-config.php for this integration to work.
*
* @since 2.0.0
* @var array
*/
protected $constants = array(
'WU_SERVER_PILOT_CLIENT_ID',
'WU_SERVER_PILOT_API_KEY',
'WU_SERVER_PILOT_APP_ID',
);
/**
* Picks up on tips that a given host provider is being used.
*
* We use this to suggest that the user should activate an integration module.
*
* @since 2.0.0
* @return boolean
*/
public function detect() {
return defined('WP_PLUGIN_DIR') && preg_match('/\/srv\/users\/(.+)\/apps\/(.+)/', (string) WP_PLUGIN_DIR);
} // end detect;
/**
* Returns the list of installation fields.
*
* @since 2.1.2
* @return array
*/
public function get_fields() {
return array(
'WU_SERVER_PILOT_CLIENT_ID' => array(
'title' => __('ServerPilot Client ID', 'wp-ultimo'),
'desc' => __('Your ServerPilot Client ID.', 'wp-ultimo'),
'placeholder' => __('e.g. cid_lSmjevkdoSOpasYVqm', 'wp-ultimo'),
),
'WU_SERVER_PILOT_API_KEY' => array(
'title' => __('ServerPilot API Key', 'wp-ultimo'),
'desc' => __('The API Key retrieved in the previous step.', 'wp-ultimo'),
'placeholder' => __('e.g. eYP0Jo3Fzzm5SOZCi5nLR0Mki2lbYZ', 'wp-ultimo'),
),
'WU_SERVER_PILOT_APP_ID' => array(
'title' => __('ServerPilot App ID', 'wp-ultimo'),
'desc' => __('The App ID retrieved in the previous step.', 'wp-ultimo'),
'placeholder' => __('e.g. 940288', 'wp-ultimo'),
),
);
} // end get_fields;
/**
* This method gets called when a new domain is mapped.
*
* @since 2.0.0
* @param string $domain The domain name being mapped.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_domain($domain, $site_id) {
$current_domain_list = $this->get_server_pilot_domains();
if ($current_domain_list && is_array($current_domain_list)) {
$this->send_server_pilot_api_request('', array(
'domains' => array_merge($current_domain_list, array($domain, 'www.' . $domain)),
));
/**
* Makes sure autoSSL is always on
*/
$this->turn_server_pilot_auto_ssl_on();
} // end if;
} // end on_add_domain;
/**
* This method gets called when a mapped domain is removed.
*
* @since 2.0.0
* @param string $domain The domain name being removed.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_domain($domain, $site_id) {
$current_domain_list = $this->get_server_pilot_domains();
if ($current_domain_list && is_array($current_domain_list)) {
/**
* Removes the current domain fromt he domain list
*/
$current_domain_list = array_filter($current_domain_list, fn($remote_domain) => $remote_domain !== $domain && $remote_domain !== 'www.' . $domain);
$this->send_server_pilot_api_request('', array(
'domains' => $current_domain_list
));
} // end if;
} // end on_remove_domain;
/**
* This method gets called when a new subdomain is being added.
*
* This happens every time a new site is added to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being added to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_subdomain($subdomain, $site_id) {
$current_domain_list = $this->get_server_pilot_domains();
if ($current_domain_list && is_array($current_domain_list)) {
$this->send_server_pilot_api_request('', array(
'domains' => array_merge($current_domain_list, array($subdomain)),
));
/**
* Makes sure autoSSL is always on
*/
$this->turn_server_pilot_auto_ssl_on();
} // end if;
} // end on_add_subdomain;
/**
* This method gets called when a new subdomain is being removed.
*
* This happens every time a new site is removed to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being removed to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_subdomain($subdomain, $site_id) {} // end on_remove_subdomain;
/**
* Sends a request to ServerPilot, with the right API key.
*
* @since 1.7.3
* @param string $endpoint Endpoint to send the call to.
* @param array $data Array containing the params to the call.
* @param string $method HTTP Method: POST, GET, PUT, etc.
* @return object
*/
public function send_server_pilot_api_request($endpoint, $data = array(), $method = 'POST') {
$post_fields = array(
'timeout' => 45,
'blocking' => true,
'method' => $method,
'body' => $data ? json_encode($data) : array(),
'headers' => array(
'Authorization' => 'Basic ' . base64_encode(WU_SERVER_PILOT_CLIENT_ID . ':' . WU_SERVER_PILOT_API_KEY),
'Content-Type' => 'application/json',
),
);
$response = wp_remote_request('https://api.serverpilot.io/v1/apps/' . WU_SERVER_PILOT_APP_ID . $endpoint, $post_fields);
if (!is_wp_error($response)) {
$body = json_decode(wp_remote_retrieve_body($response), true);
if (json_last_error() === JSON_ERROR_NONE) {
return $body;
} // end if;
} // end if;
return $response;
} // end send_server_pilot_api_request;
/**
* Makes sure ServerPilot autoSSL is always on, when possible.
*
* @since 1.7.4
* @return bool
*/
public function turn_server_pilot_auto_ssl_on() {
return $this->send_server_pilot_api_request('/ssl', array(
'auto' => true,
));
} // end turn_server_pilot_auto_ssl_on;
/**
* Get the current list of domains added on Server Pilot.
*
* @since 1.7.4
* @return mixed
*/
public function get_server_pilot_domains() {
$app_info = $this->send_server_pilot_api_request('', array(), 'GET');
if (isset($app_info['data']['domains'])) {
return $app_info['data']['domains'];
} // end if;
/*
* Log response so we can see what went wrong
*/
// translators: %s is the json_encode of the error.
wu_log_add('integration-serverpilot', sprintf(__('An error occurred while trying to get the current list of domains: %s', 'wp-ultimo'), json_encode($app_info)), LogLevel::ERROR);
return false;
} // end get_server_pilot_domains;
/**
* Tests the connection with the ServerPilot API.
*
* @since 2.0.0
* @return void
*/
public function test_connection() {
$response = $this->send_server_pilot_api_request('', array(), 'GET');
if (is_wp_error($response) || wu_get_isset($response, 'error')) {
wp_send_json_error($response);
} // end if;
wp_send_json_success($response);
} // end test_connection;
/**
* Renders the instructions content.
*
* @since 2.1.2
* @return void
*/
public function get_instructions() {
wu_get_template('wizards/host-integrations/serverpilot-instructions');
} // end get_instructions;
/**
* Returns the description of this integration.
*
* @since 2.0.0
* @return string
*/
public function get_description() {
return __('ServerPilot is a cloud service for hosting WordPress and other PHP websites on servers at DigitalOcean, Amazon, Google, or any other server provider. You can think of ServerPilot as a modern, centralized hosting control panel.', 'wp-ultimo');
} // end get_description;
/**
* Returns the logo for the integration.
*
* @since 2.1.2
* @return string
*/
public function get_logo() {
return wu_get_asset('serverpilot.svg', 'img/hosts');
} // end get_logo;
} // end class ServerPilot_Host_Provider;

View File

@ -0,0 +1,229 @@
<?php
/**
* Adds domain mapping and auto SSL support to customer hosting networks on WPEngine.
*
* @package WP_Ultimo
* @subpackage Integrations/Host_Providers/WPEngine
* @since 2.0.0
*/
namespace WP_Ultimo\Integrations\Host_Providers;
use WP_Ultimo\Integrations\Host_Providers\Base_Host_Provider;
use WP_Ultimo\Logger;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* This base class should be extended to implement new host integrations for SSL and domains.
*/
class WPEngine_Host_Provider extends Base_Host_Provider {
use \WP_Ultimo\Traits\Singleton;
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $id = 'wpengine';
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $title = 'WP Engine';
/**
* Link to the tutorial teaching how to make this integration work.
*
* @var string
* @since 2.0.0
*/
protected $tutorial_link = '';
/**
* Array containing the features this integration supports.
*
* @var array
* @since 2.0.0
*/
protected $supports = array(
'no-instructions',
'no-config',
);
/**
* Constants that need to be present on wp-config.php for this integration to work.
*
* @since 2.0.0
* @var array
*/
protected $constants = array(
array('WPE_API', 'WPE_APIKEY'),
);
/**
* Picks up on tips that a given host provider is being used.
*
* We use this to suggest that the user should activate an integration module.
*
* @since 2.0.0
* @return boolean
*/
public function detect() {
return (defined('WPE_APIKEY') && WPE_APIKEY) || (defined('WPE_API') && WPE_API);
} // end detect;
/**
* Can be used to load dependencies.
*
* @since 2.0.0
* @return void
*/
public function load_dependencies() {
// if WP Engine is not defined, then return
if (!defined('WPE_PLUGIN_DIR') || !is_readable(WPE_PLUGIN_DIR . '/class-wpeapi.php')) {
return;
} // end if;
include_once WPE_PLUGIN_DIR . '/class-wpeapi.php';
} // end load_dependencies;
/**
* This method gets called when a new domain is mapped.
*
* @since 2.0.0
* @param string $domain The domain name being mapped.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_domain($domain, $site_id) {
$api = new \WPE_API();
$api->set_arg('method', 'domain');
$api->set_arg('domain', $domain);
$api->get();
} // end on_add_domain;
/**
* This method gets called when a mapped domain is removed.
*
* @since 2.0.0
* @param string $domain The domain name being removed.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_domain($domain, $site_id) {
$api = new \WPE_API();
$api->set_arg('method', 'domain-remove');
$api->set_arg('domain', $domain);
$api->get();
} // end on_remove_domain;
/**
* This method gets called when a new subdomain is being added.
*
* This happens every time a new site is added to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being added to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_subdomain($subdomain, $site_id) {
$this->on_add_domain($subdomain, $site_id);
} // end on_add_subdomain;
/**
* This method gets called when a new subdomain is being removed.
*
* This happens every time a new site is removed to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being removed to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_subdomain($subdomain, $site_id) {
$this->on_remove_domain($subdomain, $site_id);
} // end on_remove_subdomain;
/**
* Returns the description of this integration.
*
* @since 2.0.0
* @return string
*/
public function get_description() {
$description = __('WP Engine drives your business forward faster with the first and only WordPress Digital Experience Platform. We offer the best WordPress hosting and developer experience on a proven, reliable architecture that delivers unparalleled speed, scalability, and security for your sites.', 'wp-ultimo');
$description .= '<br><br><b>' . __('We recommend to enter in contact with WP Engine support to ask for a Wildcard domain if you are using a subdomain install.', 'wp-ultimo') . '</b>';
return $description;
} // end get_description;
/**
* Returns the logo for the integration.
*
* @since 2.0.0
* @return string
*/
public function get_logo() {
return wu_get_asset('wpengine.svg', 'img/hosts');
} // end get_logo;
/**
* Tests the connection with the WP Engine API.
*
* @since 2.0.0
* @return void
*/
public function test_connection() {
$api = new \WPE_API();
$api->set_arg('method', 'site');
$results = $api->get();
if (is_wp_error($results)) {
wp_send_json_error($results->get_error_message());
} else {
wp_send_json_success($results);
} // end if;
} // end test_connection;
} // end class WPEngine_Host_Provider;

View File

@ -0,0 +1,287 @@
<?php
/**
* Adds domain mapping and auto SSL support to customer hosting networks on WPMU DEV.
*
* @package WP_Ultimo
* @subpackage Integrations/Host_Providers/WPMUDEV_Host_Provider
* @since 2.0.0
*/
namespace WP_Ultimo\Integrations\Host_Providers;
use WP_Ultimo\Dependencies\Psr\Log\LogLevel;
use WP_Ultimo\Integrations\Host_Providers\Base_Host_Provider;
// Exit if accessed directly
defined('ABSPATH') || exit;
/**
* This base class should be extended to implement new host integrations for SSL and domains.
*/
class WPMUDEV_Host_Provider extends Base_Host_Provider {
use \WP_Ultimo\Traits\Singleton;
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $id = 'wpmudev';
/**
* Keeps the title of the integration.
*
* @var string
* @since 2.0.0
*/
protected $title = 'WPMU DEV Hosting';
/**
* Link to the tutorial teaching how to make this integration work.
*
* @var string
* @since 2.0.0
*/
protected $tutorial_link = '';
/**
* Array containing the features this integration supports.
*
* @var array
* @since 2.0.0
*/
protected $supports = array(
'autossl',
'no-instructions',
'no-config',
);
/**
* Constants that need to be present on wp-config.php for this integration to work.
*
* @since 2.0.0
* @var array
*/
protected $constants = array(
'WPMUDEV_HOSTING_SITE_ID',
);
/**
* Runs on singleton instantiation.
*
* @since 2.1.1
* @return void
*/
public function init() {
parent::init();
/**
* Add filter to increase the number of tries to get the SSL certificate.
* This is needed because, from our tests, WPMU DEV hosting takes a while to get the SSL certificate.
*/
add_filter('wu_async_process_domain_stage_max_tries', array($this, 'ssl_tries'), 10, 2);
} // end init;
/**
* Increases the number of tries to get the SSL certificate.
*
* @since 2.1.1
* @param int $max_tries The number of tries to get the SSL certificate.
* @param \WP_Ultimo\Models\Domain $domain The domain object.
* @return int
*/
public function ssl_tries($max_tries, $domain) {
if (!$this->is_enabled()) {
return $max_tries;
} // end if;
if ('checking-ssl-cert' === $domain->get_stage()) {
$max_tries = 10;
} // end if;
return $max_tries;
} // end ssl_tries;
/**
* Picks up on tips that a given host provider is being used.
*
* We use this to suggest that the user should activate an integration module.
*
* @since 2.0.0
* @return boolean
*/
public function detect() {
return defined('WPMUDEV_HOSTING_SITE_ID') && WPMUDEV_HOSTING_SITE_ID;
} // end detect;
/**
* This method gets called when a new domain is mapped.
*
* @since 2.0.0
* @param string $domain The domain name being mapped.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_domain($domain, $site_id) {
$site_id = WPMUDEV_HOSTING_SITE_ID;
$api_key = get_site_option('wpmudev_apikey');
$domains = array($domain);
if (strncmp($domain, 'www.', strlen('www.')) !== 0) {
$domains[] = "www.$domain";
} // end if;
foreach ($domains as $_domain) {
$response = wp_remote_post("https://premium.wpmudev.org/api/hosting/v1/$site_id/domains", array(
'timeout' => 50,
'body' => array(
'domain' => $_domain,
'site_id' => $site_id,
),
'headers' => array(
'Authorization' => $api_key,
),
));
if (is_wp_error($response)) {
// translators: The %s placeholder will be replaced with the domain name.
wu_log_add('integration-wpmudev', sprintf(__('An error occurred while trying to add the custom domain %s to WPMU Dev hosting.', 'wp-ultimo'), $_domain), LogLevel::ERROR);
} // end if;
$body = json_decode(wp_remote_retrieve_body($response));
if ($body->message) {
// translators: The %1$s will be replaced with the domain name and %2$s is the error message.
wu_log_add('integration-wpmudev', sprintf(__('An error occurred while trying to add the custom domain %1$s to WPMU Dev hosting: %2$s', 'wp-ultimo'), $_domain, $body->message->message), LogLevel::ERROR);
} else {
// translators: The %s placeholder will be replaced with the domain name.
wu_log_add('integration-wpmudev', sprintf(__('Domain %s added to WPMU Dev hosting successfully.', 'wp-ultimo'), $_domain));
} // end if;
} // end foreach;
} // end on_add_domain;
/**
* This method gets called when a mapped domain is removed.
*
* @since 2.0.0
* @param string $domain The domain name being removed.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_domain($domain, $site_id) {
/**
* The WPMU DEV Hosting REST API does not offer an endpoint to remove domains yet.
* As soon as that's the case, we'll implement it here.
*
* @todo Implement support to removing domains when a mapping is removed.
*/
} // end on_remove_domain;
/**
* This method gets called when a new subdomain is being added.
*
* This happens every time a new site is added to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being added to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_add_subdomain($subdomain, $site_id) {} // end on_add_subdomain;
/**
* This method gets called when a new subdomain is being removed.
*
* This happens every time a new site is removed to a network running on subdomain mode.
*
* @since 2.0.0
* @param string $subdomain The subdomain being removed to the network.
* @param int $site_id ID of the site that is receiving that mapping.
* @return void
*/
public function on_remove_subdomain($subdomain, $site_id) {} // end on_remove_subdomain;
/**
* Tests the connection with the WPMUDEV API.
*
* @since 2.0.0
* @return void
*/
public function test_connection() {
$site_id = WPMUDEV_HOSTING_SITE_ID;
$api_key = get_site_option('wpmudev_apikey');
$response = wp_remote_get("https://premium.wpmudev.org/api/hosting/v1/{$site_id}/domains", array(
'timeout' => 50,
'headers' => array(
'Authorization' => $api_key,
),
));
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
wp_send_json_error($response);
} else {
wp_send_json_success(wp_remote_retrieve_body($response));
} // end if;
} // end test_connection;
/**
* Returns the description of this integration.
*
* @since 2.0.0
* @return string
*/
public function get_description() {
return __('WPMU DEV is one of the largest companies in the WordPress space. Founded in 2004, it was one of the first companies to scale the Website as a Service model with products such as Edublogs and CampusPress.', 'wp-ultimo');
} // end get_description;
/**
* Returns the logo for the integration.
*
* @since 2.0.0
* @return string
*/
public function get_logo() {
return wu_get_asset('wpmudev.jpg', 'img/hosts');
} // end get_logo;
} // end class WPMUDEV_Host_Provider;

View File

@ -0,0 +1,357 @@
<?php
/**
* CPanel API wrapper to send the calls.
*
* @package WP_Ultimo
* @subpackage Integrations/Host_Providers/CPanel_API
* @since 2.0.0
*/
namespace WP_Ultimo\Integrations\Host_Providers\CPanel_API;
use WP_Ultimo\Logger;
/**
* CPanel API wrapper to send the calls.
*/
class CPanel_API {
/**
* Holds the name of the cookis file.
*
* @since 2.0.0
* @var string
*/
private $cookie_file;
/**
* Holds the curl file.
*
* @since 2.0.0
* @var string
*/
private $curlfile;
/**
* Holds the cookie tokens
*
* @since 2.0.0
* @var string
*/
private $cpsess;
/**
* Holds the cPanel url.
*
* @since 2.0.0
* @var string|null
*/
private ?string $homepage = null;
/**
* Holds the execution url.
*
* @since 2.0.0
* @var string|null
*/
private ?string $ex_page = null;
/**
* @var string
*/
private $username;
/**
* @var string
*/
private $password;
/**
* @var string
*/
private $host;
/**
* @var integer
*/
private $port = 2083;
/**
* @var boolean
*/
private $log = false;
/**
* Creates the CPanel_API Object.
*
* @since 1.6.2
* @param string $username cPanel username.
* @param string $password cPanel password. Yep =(.
* @param string $host cPanel URL.
* @param integer $port cPanel port.
* @param boolean $log Log.
*/
public function __construct(
$username,
$password,
$host,
$port = 2083,
$log = false
) {
$this->username = $username;
$this->password = $password;
$this->host = $host;
$this->port = $port;
$this->log = $log;
// Generates the cookie file
$this->generate_cookie();
$this->cookie_file = Logger::get_logs_folder() . 'integration-cpanel-cookie.log';
// Signs up
$this->sign_in();
} // end __construct;
/**
* Generate the Cookie File, that is used to make API requests to CPanel.
*
* @since 1.6.2
* @return void
*/
public function generate_cookie() {
wu_log_add('integration-cpanel-cookie', '');
} // end generate_cookie;
/**
* Logs error or success messages.
*
* @since 1.6.2
* @param string $message Message to be logged.
* @return boolean
*/
public function log($message) {
return wu_log_add('integration-cpanel', $message);
} // end log;
/**
* Sends the request to the CPanel API.
*
* @since 1.6.2
* @param string $url URL endpoint.
* @param array $params Request parameters to send.
* @return mixed
*/
private function request($url, $params = array()) {
if ($this->log) {
$curl_log = fopen($this->curlfile, 'a+');
} // end if;
if (!file_exists($this->cookie_file)) {
try {
fopen($this->cookie_file, 'w');
} catch (Exception $ex) {
if (!file_exists($this->cookie_file)) {
$this->log($ex . __('Cookie file missing.', 'wp-ultimo'));
return false;
} // end if;
} // end try;
} elseif (!is_writable($this->cookie_file)) {
$this->log(__('Cookie file not writable', 'wp-ultimo'));
return false;
} // end if;
$ch = curl_init();
$curl_opts = array(
CURLOPT_URL => $url,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_COOKIEJAR => realpath($this->cookie_file),
CURLOPT_COOKIEFILE => realpath($this->cookie_file),
CURLOPT_HTTPHEADER => array(
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0',
'Host: ' . $this->host,
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language: en-US,en;q=0.5',
'Accept-Encoding: gzip, deflate',
'Connection: keep-alive',
'Content-Type: application/x-www-form-urlencoded'
),
);
if (!empty($params)) {
$curl_opts[CURLOPT_POST] = true;
$curl_opts[CURLOPT_POSTFIELDS] = $params;
} // end if;
if ($this->log) {
$curl_opts[CURLOPT_STDERR] = $curl_log;
$curl_opts[CURLOPT_FAILONERROR] = false;
$curl_opts[CURLOPT_VERBOSE] = true;
} // end if;
curl_setopt_array($ch, $curl_opts);
$answer = curl_exec($ch);
if (curl_error($ch)) {
// translators: %s is the cURL error.
$this->log(sprintf(__('cPanel API Error: %s', 'wp-ultimo'), curl_error($ch)));
return false;
} // end if;
curl_close($ch);
if ($this->log) {
fclose($curl_log);
} // end if;
return (@gzdecode($answer)) ? gzdecode($answer) : $answer; // phpcs:ignore
} // end request;
/**
* Get the base URL to make the calls.
*
* @since 2.0.0
*/
private function get_base_url(): string {
return sprintf('https://%s:%s', $this->host, $this->port);
} // end get_base_url;
/**
* Signs in on the cPanel.
*
* @since 1.6.2
* @return boolean
*/
private function sign_in() {
$url = $this->get_base_url() . '/login/?login_only=1';
$url .= '&user=' . $this->username . '&pass=' . urlencode($this->password);
$reply = $this->request($url);
$reply = json_decode((string) $reply, true);
if (isset($reply['status']) && $reply['status'] == 1) { // phpcs:ignore
$this->cpsess = $reply['security_token'];
$this->homepage = $this->get_base_url() . $reply['redirect'];
$this->ex_page = $this->get_base_url() . "/{$this->cpsess}/execute/";
} else {
return $this->log(__('Cannot connect to your cPanel server : Invalid Credentials', 'wp-ultimo'));
} // end if;
} // end sign_in;
/**
* Executes API calls, taking the request to the right API version
*
* @since 1.6.2
* @throws Exception Throwns exception when the api is invalid.
* @param string $api API version.
* @param string $module Module name, to build the endpoint.
* @param string $function Endpoint function to call.
* @param array $parameters Parameters to the API endpoint.
* @return boolean
*/
public function execute($api, $module, $function, array $parameters = array()) {
switch ($api) {
case 'api2':
return $this->api2($module, $function, $parameters);
case 'uapi':
return $this->uapi($module, $function, $parameters);
default:
throw new Exception('Invalid API type : api2 and uapi are accepted', 1);
} // end switch;
} // end execute;
/**
* Send the request if the API being used is the UAPI (newer version)
*
* @since 1.6.2
* @param string $module Module name, to build the endpoint.
* @param string $function Endpoint function to call.
* @param array $parameters Parameters to the API endpoint.
* @return mixed
*/
public function uapi($module, $function, array $parameters = array()) {
if (count($parameters) < 1) {
$parameters = '';
} else {
$parameters = (http_build_query($parameters));
} // end if;
return json_decode((string) $this->request($this->ex_page . $module . '/' . $function . '?' . $parameters));
} // end uapi;
/**
* Send the request if the API being used is the API2 (older version)
*
* @since 1.6.2
* @param string $module Module name, to build the endpoint.
* @param string $function Endpoint function to call.
* @param array $parameters Parameters to the API endpoint.
* @return mixed
*/
public function api2($module, $function, array $parameters = array()) {
if (count($parameters) < 1) {
$parameters = '';
} else {
$parameters = (http_build_query($parameters));
} // end if;
$url = $this->get_base_url() . $this->cpsess . '/json-api/cpanel' .
'?cpanel_jsonapi_version=2' .
"&cpanel_jsonapi_func={$function}" .
"&cpanel_jsonapi_module={$module}&" . $parameters;
return json_decode((string) $this->request($url, $parameters));
} // end api2;
} // end class CPanel_API;