-
-
Notifications
You must be signed in to change notification settings - Fork 67
WIP Create network wiz #218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds a new Multisite Setup admin wizard, wires it into initialization, and updates redirect logic to route non-multisite installs to this wizard. Introduces multisite creation/configuration routines, completion views, and manual instructions. Also includes a small docblock fix and a multisite guard in a default content installer. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Admin
participant WP as WP Admin
participant SW as Setup_Wizard_Admin_Page
participant MS as Multisite_Setup_Admin_Page
Admin->>WP: Access admin
WP->>SW: redirect_to_wizard()
alt Multisite disabled
SW-->>WP: wp_redirect(admin.php?page=wp-ultimo-multisite-setup)
WP->>MS: Load wizard (welcome)
else Multisite enabled AND setup not finished
SW-->>WP: wp_redirect(wu_network_admin_url('wp-ultimo-setup'))
else Multisite enabled AND setup finished
SW-->>WP: No redirect
end
sequenceDiagram
autonumber
actor Admin
participant MS as Multisite_Setup_Admin_Page
participant FS as Filesystem
participant WP as WordPress (Install/Network)
Admin->>MS: Configure (structure, sitename, email) + Submit
MS->>MS: Validate & store transients
MS->>FS: modify_wp_config() (initial multisite constants)
alt Write OK
MS->>WP: create_network(subdomain?, sitename, email)
alt Network created
MS->>FS: add_final_multisite_constants()
MS-->>Admin: Redirect to Complete
else Creation failed
MS-->>Admin: Show manual instructions
end
else Write failed
MS-->>Admin: Show manual instructions
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (3)
inc/class-wp-ultimo.php (1)
143-147: Gate Multisite Setup page instantiation to non‑multisite to avoid duplicate top‑level menusRight now both Setup Wizard and Multisite Setup pages register as top-level in single-site, causing duplicate “Multisite Ultimate” menus. Instantiate the Multisite Setup page only when multisite is disabled.
- /* - * Multisite Setup for non-multisite installations - */ - new WP_Ultimo\Admin_Pages\Multisite_Setup_Admin_Page(); + /* + * Multisite Setup for non-multisite installations + */ + if (! is_multisite()) { + new WP_Ultimo\Admin_Pages\Multisite_Setup_Admin_Page(); + }inc/admin-pages/class-multisite-setup-admin-page.php (2)
382-394: Subdomain vs subdirectory .htaccess rules differProvide rules based on selected structure; current rules are subdirectory-only.
- $htaccess_rules = 'RewriteEngine On + $htaccess_rules = $subdomain_install ? 'RewriteEngine On +RewriteBase / +RewriteRule ^index\.php$ - [L] + +RewriteRule ^wp-admin$ wp-admin/ [R=301,L] +RewriteCond %{REQUEST_FILENAME} -f [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteRule ^ - [L] +RewriteRule ^(wp-(content|admin|includes).*) $1 [L] +RewriteRule ^(.*\.php)$ $1 [L] +RewriteRule . index.php [L]' : 'RewriteEngine On RewriteRule ^index\.php$ - [L] # add a trailing slash to /wp-admin RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L] RewriteCond %{REQUEST_FILENAME} -f [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^ - [L] RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L] RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L] RewriteRule . index.php [L]';Please verify these match WordPress Network Setup output for your server environment.
Also applies to: 368-375
74-83: Optional: Auto-redirect away if multisite is already enabledPrevents showing this page after success.
public function __construct() { + if (is_multisite()) { + wp_safe_redirect(wu_network_admin_url('wp-ultimo-setup')); + exit; + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
inc/admin-pages/class-base-admin-page.php(1 hunks)inc/admin-pages/class-multisite-setup-admin-page.php(1 hunks)inc/admin-pages/class-setup-wizard-admin-page.php(1 hunks)inc/class-wp-ultimo.php(1 hunks)inc/installers/class-default-content-installer.php(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
inc/class-wp-ultimo.php (1)
inc/admin-pages/class-multisite-setup-admin-page.php (1)
Multisite_Setup_Admin_Page(21-626)
inc/admin-pages/class-setup-wizard-admin-page.php (2)
inc/class-requirements.php (1)
run_setup(101-120)inc/functions/helper.php (1)
wu_request(132-137)
inc/admin-pages/class-multisite-setup-admin-page.php (4)
inc/admin-pages/class-wizard-admin-page.php (3)
Wizard_Admin_Page(23-392)render_submit_box(373-383)get_next_section_link(266-275)inc/admin-pages/class-base-admin-page.php (4)
__construct(173-205)get_title(718-718)get_menu_title(726-726)register_scripts(694-694)inc/functions/helper.php (1)
wu_request(132-137)inc/functions/url.php (1)
wu_network_admin_url(53-60)
inc/installers/class-default-content-installer.php (1)
inc/class-requirements.php (1)
is_multisite(228-237)
🔇 Additional comments (3)
inc/admin-pages/class-base-admin-page.php (1)
219-224: Docblock fix looks goodAccurate and clarifies the method purpose.
inc/installers/class-default-content-installer.php (1)
55-57: Good guard to avoid fatal in single-siteEarly return prevents calling domain_exists() outside multisite. Nice.
inc/admin-pages/class-setup-wizard-admin-page.php (1)
224-236: Redirect split is correct and avoids loopsClear separation of single-site vs network flows, with exits. LGTM.
| return [ | ||
| 'welcome' => [ | ||
| 'title' => __('Multisite Required', 'multisite-ultimate'), | ||
| 'description' => implode( | ||
| '<br><br>', | ||
| [ | ||
| __('WordPress Multisite is required for Multisite Ultimate to function properly.', 'multisite-ultimate'), | ||
| __('This wizard will guide you through enabling WordPress Multisite and configuring your network.', 'multisite-ultimate'), | ||
| __('We recommend creating a backup of your files and database before proceeding.', 'multisite-ultimate'), | ||
| ] | ||
| ), | ||
| 'next_label' => __('Get Started →', 'multisite-ultimate'), | ||
| 'back' => false, | ||
| ], | ||
| 'configure' => [ | ||
| 'title' => __('Network Configuration', 'multisite-ultimate'), | ||
| 'description' => __('Configure your network settings. These settings determine how your sites will be structured.', 'multisite-ultimate'), | ||
| 'next_label' => __('Create Network', 'multisite-ultimate'), | ||
| 'handler' => [$this, 'handle_configure'], | ||
| 'fields' => [$this, 'get_network_configuration_fields'], | ||
| ], | ||
| 'complete' => [ | ||
| 'title' => __('Setup Complete', 'multisite-ultimate'), | ||
| 'description' => __('WordPress Multisite setup is now complete!', 'multisite-ultimate'), | ||
| 'view' => [$this, 'section_complete'], | ||
| 'back' => false, | ||
| 'next' => false, | ||
| ], | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick
Welcome view defined but not used
section_welcome() won’t run because the 'welcome' section lacks a 'view' entry. Wire it up.
- 'welcome' => [
+ 'welcome' => [
'title' => __('Multisite Required', 'multisite-ultimate'),
'description' => implode(
'<br><br>',
[
__('WordPress Multisite is required for Multisite Ultimate to function properly.', 'multisite-ultimate'),
__('This wizard will guide you through enabling WordPress Multisite and configuring your network.', 'multisite-ultimate'),
__('We recommend creating a backup of your files and database before proceeding.', 'multisite-ultimate'),
]
),
'next_label' => __('Get Started →', 'multisite-ultimate'),
'back' => false,
+ 'view' => [$this, 'section_welcome'],
],Also applies to: 160-208
🤖 Prompt for AI Agents
inc/admin-pages/class-multisite-setup-admin-page.php around lines 123-151 (and
similarly 160-208): the "welcome" section currently has no 'view' entry so the
section_welcome() callback is never invoked; add a 'view' => [$this,
'section_welcome'] key to the welcome array, and review other section arrays in
the 160-208 range to ensure each section that has a corresponding section_*
method is wired with a 'view' => [$this, 'section_<name>'] entry (or remove
unused section_* methods) so the defined view callbacks are actually used.
| 'subdomain_install' => [ | ||
| 'type' => 'radio', | ||
| 'title' => __('Site Structure', 'multisite-ultimate'), | ||
| 'desc' => __('Choose between subdirectories or subdomains for your network sites.', 'multisite-ultimate'), | ||
| 'options' => [ | ||
| 'sub0' => sprintf(__('Sites will use sub-directories like %s (Recommended)', 'multisite-ultimate'), '<code>' . esc_html($base_domain) . '/site1</code>'), | ||
|
|
||
| 'sub1' => sprintf(__('Sites will use sub-domains like %s (Requires wildcard DNS)', 'multisite-ultimate'), '<code>site1.' . esc_html($base_domain) . '</code>'), | ||
|
|
||
| ], | ||
| 'default' => '0', | ||
| ], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Radio values → boolean conversion bug (always true)
Options use 'sub0'/'sub1' but the code casts the string directly to bool, making both truthy. Use '0'/'1' (or map explicitly) and compare string values.
- 'subdomain_install' => [
+ 'subdomain_install' => [
'type' => 'radio',
'title' => __('Site Structure', 'multisite-ultimate'),
'desc' => __('Choose between subdirectories or subdomains for your network sites.', 'multisite-ultimate'),
'options' => [
- 'sub0' => sprintf(__('Sites will use sub-directories like %s (Recommended)', 'multisite-ultimate'), '<code>' . esc_html($base_domain) . '/site1</code>'),
-
- 'sub1' => sprintf(__('Sites will use sub-domains like %s (Requires wildcard DNS)', 'multisite-ultimate'), '<code>site1.' . esc_html($base_domain) . '</code>'),
+ '0' => sprintf(__('Sites will use sub-directories like %s (Recommended)', 'multisite-ultimate'), '<code>' . esc_html($base_domain) . '/site1</code>'),
+ '1' => sprintf(__('Sites will use sub-domains like %s (Requires wildcard DNS)', 'multisite-ultimate'), '<code>site1.' . esc_html($base_domain) . '</code>'),
],
- 'default' => '0',
+ 'default' => '0',
],- $subdomain_install = (bool) wu_request('subdomain_install', 0);
+ $subdomain_install = wu_request('subdomain_install', '0') === '1';Also applies to: 287-290
🤖 Prompt for AI Agents
inc/admin-pages/class-multisite-setup-admin-page.php lines ~228-239 and
~287-290: the radio options currently use keys 'sub0'/'sub1' but elsewhere the
code casts the selected value to boolean (making both keys truthy), so selection
is always treated as true; change the option keys to simple '0' and '1' (and
keep 'default' => '0'), or explicitly map 'sub0'/'sub1' to boolean values and
update any checks to compare the string value (use strict equality to '1' or
'0') instead of casting to bool; apply the same fix to the block at lines
~287-290 so selection logic works correctly.
| $wp_config_path = ABSPATH . 'wp-config.php'; | ||
|
|
||
| if (! file_exists($wp_config_path) || ! is_writable($wp_config_path)) { | ||
| return false; | ||
| } | ||
|
|
||
| $config_content = file_get_contents($wp_config_path); | ||
|
|
||
| if ($config_content === false) { | ||
| return false; | ||
| } | ||
|
|
||
| // Check if WP_ALLOW_MULTISITE is already defined | ||
| if (strpos($config_content, 'WP_ALLOW_MULTISITE') !== false) { | ||
| return true; // Already configured | ||
| } | ||
|
|
||
| // Find the location to insert the constant | ||
| $search = "/* That's all, stop editing! Happy publishing. */"; | ||
| $insert_position = strpos($config_content, $search); | ||
|
|
||
| if ($insert_position === false) { | ||
| // Fallback: look for the wp-settings.php include | ||
| $search = "require_once ABSPATH . 'wp-settings.php';"; | ||
| $insert_position = strpos($config_content, $search); | ||
| } | ||
|
|
||
| if ($insert_position === false) { | ||
| return false; // Can't find a safe place to insert | ||
| } | ||
|
|
||
| $constant_to_add = "\n// Multisite Ultimate: Enable WordPress Multisite\ndefine( 'WP_ALLOW_MULTISITE', true );\n\n"; | ||
|
|
||
| $new_content = substr_replace($config_content, $constant_to_add, $insert_position, 0); | ||
|
|
||
| return file_put_contents($wp_config_path, $new_content) !== false; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Support wp-config.php one level above ABSPATH
Many installs place wp-config.php in the parent directory. Add a fallback search before giving up.
- $wp_config_path = ABSPATH . 'wp-config.php';
-
- if (! file_exists($wp_config_path) || ! is_writable($wp_config_path)) {
+ $paths = [
+ ABSPATH . 'wp-config.php',
+ trailingslashit(dirname(ABSPATH)) . 'wp-config.php',
+ ];
+ $wp_config_path = null;
+ foreach ($paths as $candidate) {
+ if (file_exists($candidate) && is_writable($candidate)) {
+ $wp_config_path = $candidate;
+ break;
+ }
+ }
+ if ($wp_config_path === null) {
return false;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| $wp_config_path = ABSPATH . 'wp-config.php'; | |
| if (! file_exists($wp_config_path) || ! is_writable($wp_config_path)) { | |
| return false; | |
| } | |
| $config_content = file_get_contents($wp_config_path); | |
| if ($config_content === false) { | |
| return false; | |
| } | |
| // Check if WP_ALLOW_MULTISITE is already defined | |
| if (strpos($config_content, 'WP_ALLOW_MULTISITE') !== false) { | |
| return true; // Already configured | |
| } | |
| // Find the location to insert the constant | |
| $search = "/* That's all, stop editing! Happy publishing. */"; | |
| $insert_position = strpos($config_content, $search); | |
| if ($insert_position === false) { | |
| // Fallback: look for the wp-settings.php include | |
| $search = "require_once ABSPATH . 'wp-settings.php';"; | |
| $insert_position = strpos($config_content, $search); | |
| } | |
| if ($insert_position === false) { | |
| return false; // Can't find a safe place to insert | |
| } | |
| $constant_to_add = "\n// Multisite Ultimate: Enable WordPress Multisite\ndefine( 'WP_ALLOW_MULTISITE', true );\n\n"; | |
| $new_content = substr_replace($config_content, $constant_to_add, $insert_position, 0); | |
| return file_put_contents($wp_config_path, $new_content) !== false; | |
| } | |
| $paths = [ | |
| ABSPATH . 'wp-config.php', | |
| trailingslashit(dirname(ABSPATH)) . 'wp-config.php', | |
| ]; | |
| $wp_config_path = null; | |
| foreach ($paths as $candidate) { | |
| if (file_exists($candidate) && is_writable($candidate)) { | |
| $wp_config_path = $candidate; | |
| break; | |
| } | |
| } | |
| if ($wp_config_path === null) { | |
| return false; | |
| } | |
| $config_content = file_get_contents($wp_config_path); | |
| if ($config_content === false) { | |
| return false; | |
| } | |
| // Check if WP_ALLOW_MULTISITE is already defined | |
| if (strpos($config_content, 'WP_ALLOW_MULTISITE') !== false) { | |
| return true; // Already configured | |
| } | |
| // Find the location to insert the constant | |
| $search = "/* That's all, stop editing! Happy publishing. */"; | |
| $insert_position = strpos($config_content, $search); | |
| if ($insert_position === false) { | |
| // Fallback: look for the wp-settings.php include | |
| $search = "require_once ABSPATH . 'wp-settings.php';"; | |
| $insert_position = strpos($config_content, $search); | |
| } | |
| if ($insert_position === false) { | |
| return false; // Can't find a safe place to insert | |
| } | |
| $constant_to_add = "\n// Multisite Ultimate: Enable WordPress Multisite\ndefine( 'WP_ALLOW_MULTISITE', true );\n\n"; | |
| $new_content = substr_replace($config_content, $constant_to_add, $insert_position, 0); | |
| return file_put_contents($wp_config_path, $new_content) !== false; | |
| } |
| // Add final multisite constants to wp-config.php | ||
| $this->add_final_multisite_constants($subdomain_install, $domain); | ||
|
|
||
| return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t ignore failure to write MULTISITE constants; add insertion fallback
- add_final_multisite_constants() returns false if the exact WP_ALLOW_MULTISITE line isn’t found. Provide a fallback insertion point.
- In create_network(), treat a failure to add constants as a failure overall.
- // Add final multisite constants to wp-config.php
- $this->add_final_multisite_constants($subdomain_install, $domain);
-
- return true;
+ // Add final multisite constants to wp-config.php
+ $constants_ok = $this->add_final_multisite_constants($subdomain_install, $domain);
+ return (bool) $constants_ok;- // Find the location to insert the constants (after WP_ALLOW_MULTISITE)
- $search = "define( 'WP_ALLOW_MULTISITE', true );";
- $insert_position = strpos($config_content, $search);
-
- if ($insert_position !== false) {
- $insert_position += strlen($search);
- $new_content = substr_replace($config_content, $constants_to_add, $insert_position, 0);
- return file_put_contents($wp_config_path, $new_content) !== false;
- }
-
- return false;
+ // Prefer inserting after WP_ALLOW_MULTISITE, otherwise before "That's all" or wp-settings
+ $anchors = [
+ "define( 'WP_ALLOW_MULTISITE', true );",
+ "/* That's all, stop editing! Happy publishing. */",
+ "require_once ABSPATH . 'wp-settings.php';",
+ ];
+ foreach ($anchors as $anchor) {
+ $pos = strpos($config_content, $anchor);
+ if ($pos !== false) {
+ $pos += ($anchor === $anchors[0]) ? strlen($anchor) : 0;
+ $new_content = substr_replace($config_content, $constants_to_add, $pos, 0);
+ return file_put_contents($wp_config_path, $new_content) !== false;
+ }
+ }
+ return false;Also applies to: 579-590
🤖 Prompt for AI Agents
inc/admin-pages/class-multisite-setup-admin-page.php lines 535-538 and 579-590:
add_final_multisite_constants() can return false when the exact
WP_ALLOW_MULTISITE anchor isn’t found, so update create_network() to check its
return value and treat a false as an overall failure (return false and surface
an error). If add_final_multisite_constants() fails, implement a fallback
insertion strategy: attempt to insert the MULTISITE constants at a secondary
anchor (for example before "/* That's all, stop editing! */" or immediately
before the require_once for wp-settings.php), and only if both primary and
fallback insertions fail return false; ensure proper logging of the failure and
that create_network() propagates that failure instead of returning true.
| return true; | ||
| } catch (Exception $e) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Namespace issue: catching Exception will fatal
Use the global Exception class or import it. Otherwise PHP looks for WP_Ultimo\Admin_Pages\Exception.
- } catch (Exception $e) {
+ } catch (\Exception $e) {
return false;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return true; | |
| } catch (Exception $e) { | |
| return false; | |
| } | |
| return true; | |
| } catch (\Exception $e) { | |
| return false; | |
| } |
🤖 Prompt for AI Agents
In inc/admin-pages/class-multisite-setup-admin-page.php around lines 538 to 541,
the catch block currently references Exception without qualification which makes
PHP look for WP_Ultimo\Admin_Pages\Exception and will fatal; change the catch to
use the global Exception class (e.g. catch (\Exception $e)) or add a top-level
import (use Exception;) at the file namespace header so the global Exception is
correctly referenced; update the catch signature accordingly and keep existing
handling (return false).
| if (get_current_screen()->id !== 'toplevel_page_wp-ultimo-multisite-setup') { | ||
| return; | ||
| } | ||
|
|
||
| wp_add_inline_script( | ||
| 'wp-admin', | ||
| ' | ||
| // Copy to clipboard functionality | ||
| document.addEventListener("DOMContentLoaded", function() { | ||
| document.querySelectorAll("button[onclick*=\'navigator.clipboard.writeText\']").forEach(function(button) { | ||
| button.addEventListener("click", function() { | ||
| var textarea = this.nextElementSibling; | ||
| if (textarea && textarea.tagName === "TEXTAREA") { | ||
| navigator.clipboard.writeText(textarea.value).then(function() { | ||
| button.textContent = "Copied!"; | ||
| setTimeout(function() { | ||
| button.textContent = "Copy to clipboard"; | ||
| }, 2000); | ||
| }); | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| ' | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick
Inline script attached to a non‑existent handle
wp_add_inline_script('wp-admin', ...) won’t run. Attach to a registered/enqueued handle (e.g., 'jquery') or enqueue a small handle for this page and attach to it.
- wp_add_inline_script(
- 'wp-admin',
+ wp_add_inline_script(
+ 'jquery',
'
// Copy to clipboard functionality
document.addEventListener("DOMContentLoaded", function() {
document.querySelectorAll("button[onclick*=\'navigator.clipboard.writeText\']").forEach(function(button) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (get_current_screen()->id !== 'toplevel_page_wp-ultimo-multisite-setup') { | |
| return; | |
| } | |
| wp_add_inline_script( | |
| 'wp-admin', | |
| ' | |
| // Copy to clipboard functionality | |
| document.addEventListener("DOMContentLoaded", function() { | |
| document.querySelectorAll("button[onclick*=\'navigator.clipboard.writeText\']").forEach(function(button) { | |
| button.addEventListener("click", function() { | |
| var textarea = this.nextElementSibling; | |
| if (textarea && textarea.tagName === "TEXTAREA") { | |
| navigator.clipboard.writeText(textarea.value).then(function() { | |
| button.textContent = "Copied!"; | |
| setTimeout(function() { | |
| button.textContent = "Copy to clipboard"; | |
| }, 2000); | |
| }); | |
| } | |
| }); | |
| }); | |
| }); | |
| ' | |
| ); | |
| } | |
| if (get_current_screen()->id !== 'toplevel_page_wp-ultimo-multisite-setup') { | |
| return; | |
| } | |
| wp_add_inline_script( | |
| 'jquery', | |
| ' | |
| // Copy to clipboard functionality | |
| document.addEventListener("DOMContentLoaded", function() { | |
| document.querySelectorAll("button[onclick*=\'navigator.clipboard.writeText\']").forEach(function(button) { | |
| button.addEventListener("click", function() { | |
| var textarea = this.nextElementSibling; | |
| if (textarea && textarea.tagName === "TEXTAREA") { | |
| navigator.clipboard.writeText(textarea.value).then(function() { | |
| button.textContent = "Copied!"; | |
| setTimeout(function() { | |
| button.textContent = "Copy to clipboard"; | |
| }, 2000); | |
| }); | |
| } | |
| }); | |
| }); | |
| }); | |
| ' | |
| ); | |
| } |
Summary by CodeRabbit