Skip to content

Add Plesk hosting integration#337

Merged
superdav42 merged 3 commits intomainfrom
feature/plesk-integration
Mar 2, 2026
Merged

Add Plesk hosting integration#337
superdav42 merged 3 commits intomainfrom
feature/plesk-integration

Conversation

@superdav42
Copy link
Collaborator

@superdav42 superdav42 commented Feb 9, 2026

Summary

  • Adds new Plesk hosting provider integration for automatic domain mapping
  • Uses Plesk REST API v2 CLI gateway to manage site aliases (site_alias/call) and subdomains (subdomain/call)
  • Supports both API key (X-API-Key header) and HTTP Basic Auth
  • Handles www subdomain creation via Domain_Manager::should_create_www_subdomain()
  • Includes AutoSSL support flag for Plesk's SSL It! / Let's Encrypt extension

Files

  • inc/integrations/providers/plesk/class-plesk-integration.php — Provider with credentials, connection test, and API request method
  • inc/integrations/providers/plesk/class-plesk-domain-mapping.php — Domain mapping capability (add/remove domain aliases and subdomains)
  • assets/img/hosts/plesk.svg — Plesk logo for the integrations settings page
  • inc/integrations/class-integration-registry.php — Registration of Plesk provider and capability

Test plan

  • Enable Plesk integration in WP Admin > Settings > Integrations
  • Verify wizard form renders all credential fields (host, port, API key, username, password, base domain)
  • Test connection with valid Plesk API credentials
  • Map a domain and verify site alias is created in Plesk
  • Remove a mapped domain and verify alias is deleted
  • Verify www alias is created/removed when configured
  • On subdomain installs, verify subdomain creation via CLI gateway
  • Full PHPUnit suite passes (2175 tests, 0 failures)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added Plesk hosting provider support for managing domain aliases, subdomains, and automatic SSL.
    • Added Plesk domain-mapping capability to automate alias/subdomain lifecycle operations.
  • Bug Fixes

    • Ensure duplicated sites preserve the requested site title (blog name) after duplication.
  • Tests

    • Updated site duplicator tests to validate the duplicated site's title via stored option.

Adds a new Plesk provider that uses the REST API v2 CLI gateway
to manage site aliases and subdomains automatically when domains
are mapped or removed. Supports API key and Basic Auth, www alias
handling, and AutoSSL.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Feb 9, 2026

🔨 Build Complete - Ready for Testing!

📦 Download Build Artifact (Recommended)

Download the zip build, upload to WordPress and test:

🌐 Test in WordPress Playground (Very Experimental)

Click the link below to instantly test this PR in your browser - no installation needed!
Playground support for multisite is very limitied, hopefully it will get better in the future.

🚀 Launch in Playground

Login credentials: admin / password

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 2, 2026

Warning

Rate limit exceeded

@superdav42 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 19 minutes and 23 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 25c8655 and 226f9e3.

📒 Files selected for processing (1)
  • inc/helpers/class-site-duplicator.php
📝 Walkthrough

Walkthrough

Added a new Plesk integration provider and a Plesk domain-mapping capability, registered them in the integration registry, and implemented Plesk API request handling plus lifecycle hooks for domain and subdomain create/delete. Also apply requested title after site duplication and adjust corresponding tests.

Changes

Cohort / File(s) Summary
Integration Registry
inc/integrations/class-integration-registry.php
Register new Plesk_Integration provider and Plesk_Domain_Mapping capability in core integrations/capabilities.
Plesk Integration Provider
inc/integrations/providers/plesk/class-plesk-integration.php
New Plesk_Integration class: provider metadata, config fields (host, port, API key, user/pass, base domain), test_connection(), and send_plesk_api_request() implementing API key or basic auth, request/response handling, JSON parsing and WP_Error creation.
Plesk Domain Mapping
inc/integrations/providers/plesk/class-plesk-domain-mapping.php
New Plesk_Domain_Mapping capability: supported features, lifecycle hook registration, handlers for add/remove domain and subdomain that call Plesk CLI endpoints, response logging, and test_connection wrapper.
Site Duplicator
inc/helpers/class-site-duplicator.php
After duplication, update duplicated site blogname option with provided title via update_blog_option.
Tests
tests/WP_Ultimo/Helpers/Site_Duplicator_Test.php
Adjust test assertions to read duplicated site title via get_blog_option($result, 'blogname'); minor formatting changes in setUp.

Sequence Diagram(s)

sequenceDiagram
    participant Admin as WP Ultimo Admin
    participant Core as WP Ultimo Core
    participant Capability as Plesk Domain Mapping
    participant Integration as Plesk Integration
    participant API as Plesk API v2

    Admin->>Core: Add domain to site
    Core->>Capability: on_add_domain(domain, site_id)
    Capability->>Integration: send_plesk_api_request("/api/v2/cli/site_alias/call", POST, --create)
    Integration->>API: POST /api/v2/cli/site_alias/call with auth
    API-->>Integration: 2xx / error
    Integration-->>Capability: response/result
    Capability-->>Core: complete
Loading
sequenceDiagram
    participant Admin as WP Ultimo Admin
    participant Core as WP Ultimo Core
    participant Capability as Plesk Domain Mapping
    participant Integration as Plesk Integration
    participant API as Plesk API v2

    Admin->>Core: Add subdomain to site
    Core->>Capability: on_add_subdomain(subdomain, site_id)
    Capability->>Integration: send_plesk_api_request("/api/v2/cli/subdomain/call", POST, --create)
    Integration->>API: POST /api/v2/cli/subdomain/call with auth
    API-->>Integration: 2xx / error
    Integration-->>Capability: response/result
    Capability-->>Core: complete
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped in with Plesk in tow,
Aliases and subs begin to grow,
CLI whispers across the wire,
Domains align and servers sigh,
WP_Ultimo warms — a rabbit's joy!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add Plesk hosting integration' accurately summarizes the main change—introducing a new Plesk provider integration with domain mapping capabilities.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/plesk-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Mar 2, 2026

🔨 Build Complete - Ready for Testing!

📦 Download Build Artifact (Recommended)

Download the zip build, upload to WordPress and test:

🌐 Test in WordPress Playground (Very Experimental)

Click the link below to instantly test this PR in your browser - no installation needed!
Playground support for multisite is very limitied, hopefully it will get better in the future.

🚀 Launch in Playground

Login credentials: admin / password

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
inc/integrations/providers/plesk/class-plesk-domain-mapping.php (1)

106-106: Mark intentionally unused hook parameters explicitly.

$site_id is unused in these handlers. Renaming to $_site_id (or adding a local suppression) will clarify intent and quiet PHPMD noise.

Also applies to: 156-156, 198-198, 231-231

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@inc/integrations/providers/plesk/class-plesk-domain-mapping.php` at line 106,
The hook handlers currently accept a $site_id parameter that is not used; rename
the parameter to $_site_id in the affected methods (e.g., on_add_domain) so the
intent is explicit and PHPMD won't flag it, or alternatively add a local
suppression comment; update the function signatures where $site_id appears (the
handlers at the noted locations) to use $_site_id and leave the bodies
unchanged.
inc/integrations/providers/plesk/class-plesk-integration.php (1)

183-200: Reduce raw payload logging by default.

Line 186 and Line 200 log full request/response bodies. For hosting APIs, this can leak sensitive infrastructure details into persistent logs. Prefer status-level logs by default and gated/truncated payload logging only in debug mode.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@inc/integrations/providers/plesk/class-plesk-integration.php` around lines
183 - 200, The current logging in class-plesk-integration (around the
wp_remote_request call) writes full request and response bodies via wu_log_add
for $data and $response_body; change this to log only status-level info by
default (e.g., method and $api_url and $response_code) and gate any full payload
logging behind a debug flag (use WP_DEBUG or an internal $this->debug property)
or truncate payloads before logging (e.g., first N chars + ellipsis). Update the
wu_log_add calls that reference $data and $response_body so they only emit
truncated or gated logs (keep the existing error log for WP_Error intact), and
ensure you still use wp_json_encode for safe serialization when debug is
enabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@inc/integrations/providers/plesk/class-plesk-domain-mapping.php`:
- Around line 170-183: The deletion of the "www" alias is unconditional and can
remove aliases this integration didn't create; wrap the existing deletion block
so it only runs when the same policy used for creation applies by checking
should_create_www_subdomain() before attempting to delete (in addition to the
existing ! str_starts_with($domain, 'www.') check). Update the code around the
log_response call that calls
$this->get_plesk()->send_plesk_api_request('/api/v2/cli/site_alias/call',
'POST', ...) so it is executed only when $this->should_create_www_subdomain()
returns true, mirroring the creation logic.

In `@inc/integrations/providers/plesk/class-plesk-integration.php`:
- Around line 41-46: The required credential list in the class constructor is
inconsistent with the runtime Basic Auth check: update the
set_required_constants call to include 'WU_PLESK_USERNAME' (and remove it from
set_optional_constants) so the class-level requirements match the runtime check
that expects both username and password; alternatively, if API-key-only auth is
intended keep 'WU_PLESK_USERNAME' optional but change the runtime credential
validation to accept API-key auth, ensuring the change is applied in the same
class (class-plesk-integration.php) where set_required_constants,
set_optional_constants and the runtime username/password validation are
implemented.

---

Nitpick comments:
In `@inc/integrations/providers/plesk/class-plesk-domain-mapping.php`:
- Line 106: The hook handlers currently accept a $site_id parameter that is not
used; rename the parameter to $_site_id in the affected methods (e.g.,
on_add_domain) so the intent is explicit and PHPMD won't flag it, or
alternatively add a local suppression comment; update the function signatures
where $site_id appears (the handlers at the noted locations) to use $_site_id
and leave the bodies unchanged.

In `@inc/integrations/providers/plesk/class-plesk-integration.php`:
- Around line 183-200: The current logging in class-plesk-integration (around
the wp_remote_request call) writes full request and response bodies via
wu_log_add for $data and $response_body; change this to log only status-level
info by default (e.g., method and $api_url and $response_code) and gate any full
payload logging behind a debug flag (use WP_DEBUG or an internal $this->debug
property) or truncate payloads before logging (e.g., first N chars + ellipsis).
Update the wu_log_add calls that reference $data and $response_body so they only
emit truncated or gated logs (keep the existing error log for WP_Error intact),
and ensure you still use wp_json_encode for safe serialization when debug is
enabled.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb0cfe8 and 2bf709a.

⛔ Files ignored due to path filters (1)
  • assets/img/hosts/plesk.svg is excluded by !**/*.svg
📒 Files selected for processing (3)
  • inc/integrations/class-integration-registry.php
  • inc/integrations/providers/plesk/class-plesk-domain-mapping.php
  • inc/integrations/providers/plesk/class-plesk-integration.php

Comment on lines +170 to +183
// Also try to remove www alias
if (! str_starts_with($domain, 'www.')) {
$www = 'www.' . $domain;

$this->log_response(
sprintf('Delete alias %s', $www),
$this->get_plesk()->send_plesk_api_request(
'/api/v2/cli/site_alias/call',
'POST',
[
'params' => ['--delete', $www],
]
)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

www alias deletion should follow the same policy used for creation.

Line 129 gates www creation with should_create_www_subdomain(), but Line 171 deletes www.<domain> unconditionally. This can delete aliases not created by this integration.

🔧 Proposed fix
-		// Also try to remove www alias
-		if (! str_starts_with($domain, 'www.')) {
+		// Also remove www alias only when this integration policy would create it
+		if (
+			! str_starts_with($domain, 'www.')
+			&& \WP_Ultimo\Managers\Domain_Manager::get_instance()->should_create_www_subdomain($domain)
+		) {
 			$www = 'www.' . $domain;

 			$this->log_response(
 				sprintf('Delete alias %s', $www),
 				$this->get_plesk()->send_plesk_api_request(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@inc/integrations/providers/plesk/class-plesk-domain-mapping.php` around lines
170 - 183, The deletion of the "www" alias is unconditional and can remove
aliases this integration didn't create; wrap the existing deletion block so it
only runs when the same policy used for creation applies by checking
should_create_www_subdomain() before attempting to delete (in addition to the
existing ! str_starts_with($domain, 'www.') check). Update the code around the
log_response call that calls
$this->get_plesk()->send_plesk_api_request('/api/v2/cli/site_alias/call',
'POST', ...) so it is executed only when $this->should_create_www_subdomain()
returns true, mirroring the creation logic.

Comment on lines +41 to +46
['WU_PLESK_API_KEY', 'WU_PLESK_PASSWORD'],
'WU_PLESK_DOMAIN',
]
);
$this->set_optional_constants(['WU_PLESK_PORT', 'WU_PLESK_USERNAME']);
$this->set_supports(['autossl', 'no-instructions']);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Credential requirements are inconsistent with Basic Auth runtime checks.

Line 41 allows WU_PLESK_PASSWORD to satisfy setup without WU_PLESK_USERNAME, but Line 164 requires both username and password. That permits “configured” states that can only fail at runtime.

🔧 Proposed fix
 public function __construct() {
 	parent::__construct('plesk', 'Plesk');

 	$this->set_description(__('Integrates with Plesk to add and remove domain aliases automatically when domains are mapped or removed.', 'ultimate-multisite'));
 	$this->set_logo(function_exists('wu_get_asset') ? wu_get_asset('plesk.svg', 'img/hosts') : '');
 	$this->set_tutorial_link('https://ultimatemultisite.com/docs/user-guide/host-integrations/plesk');
-	$this->set_constants(
-		[
-			'WU_PLESK_HOST',
-			['WU_PLESK_API_KEY', 'WU_PLESK_PASSWORD'],
-			'WU_PLESK_DOMAIN',
-		]
-	);
-	$this->set_optional_constants(['WU_PLESK_PORT', 'WU_PLESK_USERNAME']);
+	$this->set_constants(['WU_PLESK_HOST', 'WU_PLESK_DOMAIN']);
+	$this->set_optional_constants(['WU_PLESK_PORT', 'WU_PLESK_API_KEY', 'WU_PLESK_USERNAME', 'WU_PLESK_PASSWORD']);
 	$this->set_supports(['autossl', 'no-instructions']);
 }
+
+public function is_setup(): bool {
+	if (! parent::is_setup()) {
+		return false;
+	}
+
+	$api_key  = $this->get_credential('WU_PLESK_API_KEY');
+	$username = $this->get_credential('WU_PLESK_USERNAME');
+	$password = $this->get_credential('WU_PLESK_PASSWORD');
+
+	return ! empty($api_key) || (! empty($username) && ! empty($password));
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@inc/integrations/providers/plesk/class-plesk-integration.php` around lines 41
- 46, The required credential list in the class constructor is inconsistent with
the runtime Basic Auth check: update the set_required_constants call to include
'WU_PLESK_USERNAME' (and remove it from set_optional_constants) so the
class-level requirements match the runtime check that expects both username and
password; alternatively, if API-key-only auth is intended keep
'WU_PLESK_USERNAME' optional but change the runtime credential validation to
accept API-key auth, ensuring the change is applied in the same class
(class-plesk-integration.php) where set_required_constants,
set_optional_constants and the runtime username/password validation are
implemented.

@github-actions
Copy link

github-actions bot commented Mar 2, 2026

🔨 Build Complete - Ready for Testing!

📦 Download Build Artifact (Recommended)

Download the zip build, upload to WordPress and test:

🌐 Test in WordPress Playground (Very Experimental)

Click the link below to instantly test this PR in your browser - no installation needed!
Playground support for multisite is very limitied, hopefully it will get better in the future.

🚀 Launch in Playground

Login credentials: admin / password

@superdav42 superdav42 force-pushed the feature/plesk-integration branch from 197be5a to 25c8655 Compare March 2, 2026 22:08
@github-actions
Copy link

github-actions bot commented Mar 2, 2026

🔨 Build Complete - Ready for Testing!

📦 Download Build Artifact (Recommended)

Download the zip build, upload to WordPress and test:

🌐 Test in WordPress Playground (Very Experimental)

Click the link below to instantly test this PR in your browser - no installation needed!
Playground support for multisite is very limitied, hopefully it will get better in the future.

🚀 Launch in Playground

Login credentials: admin / password

After MUCD_Data::copy_data copies the template's options table, the
blogname option gets overwritten with the template's value. The existing
save/restore mechanism can fail when the WordPress object cache returns
stale values (particularly in test environments with transaction rollback).

Explicitly set the blogname via update_blog_option after duplication
completes so the requested title is always applied. Also use
get_blog_option in the test assertion instead of the cached WP_Site
property.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@superdav42 superdav42 force-pushed the feature/plesk-integration branch from 25c8655 to 226f9e3 Compare March 2, 2026 22:18
@github-actions
Copy link

github-actions bot commented Mar 2, 2026

🔨 Build Complete - Ready for Testing!

📦 Download Build Artifact (Recommended)

Download the zip build, upload to WordPress and test:

🌐 Test in WordPress Playground (Very Experimental)

Click the link below to instantly test this PR in your browser - no installation needed!
Playground support for multisite is very limitied, hopefully it will get better in the future.

🚀 Launch in Playground

Login credentials: admin / password

@superdav42 superdav42 merged commit 61dc3ca into main Mar 2, 2026
9 checks passed
@superdav42 superdav42 deleted the feature/plesk-integration branch March 4, 2026 22:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant