diff --git a/inc/admin-pages/class-base-admin-page.php b/inc/admin-pages/class-base-admin-page.php
index 5cb9f17e..4109af08 100644
--- a/inc/admin-pages/class-base-admin-page.php
+++ b/inc/admin-pages/class-base-admin-page.php
@@ -216,7 +216,7 @@ public function get_id() {
}
/**
- * Returns the appropriate capability for a this page, depending on the context.
+ * Returns the appropriate capability for this page, depending on the context.
*
* @since 2.0.0
* @return string
diff --git a/inc/admin-pages/class-multisite-setup-admin-page.php b/inc/admin-pages/class-multisite-setup-admin-page.php
new file mode 100644
index 00000000..dd6ef9a6
--- /dev/null
+++ b/inc/admin-pages/class-multisite-setup-admin-page.php
@@ -0,0 +1,626 @@
+ 'capability_here'
+ * To add a page to the network admin (wp-admin/network), use: 'network_admin_menu' => 'capability_here'
+ * To add a page to the user (wp-admin/user) admin, use: 'user_admin_menu' => 'capability_here'
+ *
+ * @since 2.0.0
+ * @var array
+ */
+ protected $supported_panels = [
+ 'admin_menu' => 'manage_options',
+ ];
+
+ /**
+ * Constructor method.
+ *
+ * @since 2.0.0
+ * @return void
+ */
+ public function __construct() {
+
+ $this->type = 'menu';
+ $this->position = 10_101_010;
+ $this->menu_icon = 'dashicons-wu-wp-ultimo';
+
+ parent::__construct();
+
+ add_action('admin_enqueue_scripts', [$this, 'register_scripts']);
+ }
+
+ /**
+ * Returns the title of the page.
+ *
+ * @since 2.0.0
+ * @return string Title of the page.
+ */
+ public function get_title(): string {
+ return __('Enable WordPress Multisite', 'multisite-ultimate');
+ }
+
+ /**
+ * Returns the title of menu for this page.
+ *
+ * @since 2.0.0
+ * @return string Menu label of the page.
+ */
+ public function get_menu_title() {
+ return __('Multisite Ultimate', 'multisite-ultimate');
+ }
+
+ /**
+ * Returns the logo for the wizard.
+ *
+ * @since 2.0.0
+ * @return string
+ */
+ public function get_logo() {
+ return wu_get_asset('logo.webp', 'img');
+ }
+
+ /**
+ * Returns the sections for this Wizard.
+ *
+ * @since 2.0.0
+ * @return array
+ */
+ public function get_sections() {
+
+ return [
+ 'welcome' => [
+ 'title' => __('Multisite Required', 'multisite-ultimate'),
+ 'description' => implode(
+ '
',
+ [
+ __('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,
+ ],
+ ];
+ }
+
+ /**
+ * Welcome section view.
+ *
+ * @since 2.0.0
+ * @return void
+ */
+ public function section_welcome(): void {
+
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ render_submit_box();
+ }
+
+ /**
+ * Returns the network configuration fields.
+ *
+ * @since 2.0.0
+ * @return array
+ */
+ public function get_network_configuration_fields() {
+
+ $home_url = get_option('home');
+ $base_domain = parse_url($home_url, PHP_URL_HOST);
+ $user = wp_get_current_user();
+
+ return [
+ 'network_structure_header' => [
+ 'type' => 'header',
+ 'title' => __('Network Structure', 'multisite-ultimate'),
+ 'desc' => __('Choose how you want your network sites to be organized:', 'multisite-ultimate'),
+ ],
+ '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'), '' . esc_html($base_domain) . '/site1'),
+
+ 'sub1' => sprintf(__('Sites will use sub-domains like %s (Requires wildcard DNS)', 'multisite-ultimate'), 'site1.' . esc_html($base_domain) . ''),
+
+ ],
+ 'default' => '0',
+ ],
+ 'network_details_header' => [
+ 'type' => 'header',
+ 'title' => __('Network Details', 'multisite-ultimate'),
+ ],
+ 'sitename' => [
+ 'type' => 'text',
+ 'title' => __('Network Title', 'multisite-ultimate'),
+ 'desc' => __('This will be the title of your network.', 'multisite-ultimate'),
+ 'placeholder' => __('Enter network title', 'multisite-ultimate'),
+ 'default' => get_option('blogname'),
+ ],
+ 'email' => [
+ 'type' => 'email',
+ 'title' => __('Network Admin Email', 'multisite-ultimate'),
+ 'desc' => __('This email address will be used for network administration.', 'multisite-ultimate'),
+ 'placeholder' => __('Enter admin email', 'multisite-ultimate'),
+ 'default' => $user->user_email,
+ ],
+ 'backup_warning' => [
+ 'type' => 'note',
+ 'desc' => '
+
+
+
+
+
+
' . __('Before You Continue', 'multisite-ultimate') . '
+
' . __('Please ensure you have a recent backup of your website files and database. The multisite setup process will modify your wp-config.php file and create new database tables.', 'multisite-ultimate') . '
+
+
+
',
+ ],
+ ];
+ }
+
+ /**
+ * Handles the network configuration form submission.
+ *
+ * @since 2.0.0
+ * @return void
+ */
+ public function handle_configure(): void {
+
+ if (! current_user_can('manage_options')) {
+ wp_die(__('Permission denied.', 'multisite-ultimate'));
+ }
+
+ $subdomain_install = (bool) wu_request('subdomain_install', 0);
+ $sitename = sanitize_text_field(wu_request('sitename', ''));
+ $email = sanitize_email(wu_request('email', ''));
+
+ // Store values in transients for completion page
+ set_transient('wu_multisite_subdomain_install', $subdomain_install, 300);
+ set_transient('wu_multisite_sitename', $sitename, 300);
+ set_transient('wu_multisite_email', $email, 300);
+
+ // Try to enable multisite
+ $wp_config_modified = $this->modify_wp_config();
+ $network_created = false;
+
+ if ($wp_config_modified) {
+ // Create the network
+ $network_created = $this->create_network($subdomain_install, $sitename, $email);
+ }
+
+ // Store results
+ set_transient('wu_multisite_wp_config_modified', $wp_config_modified, 300);
+ set_transient('wu_multisite_network_created', $network_created, 300);
+
+ // Redirect to completion step
+ wp_safe_redirect($this->get_next_section_link());
+ exit;
+ }
+
+ /**
+ * Completion section view.
+ *
+ * @since 2.0.0
+ * @return void
+ */
+ public function section_complete(): void {
+
+ $wp_config_modified = get_transient('wu_multisite_wp_config_modified');
+ $network_created = get_transient('wu_multisite_network_created');
+
+ if ($network_created && $wp_config_modified) :
+ ?>
+
+
+
+ display_manual_instructions();
+ endif;
+
+ // Clean up transients
+ delete_transient('wu_multisite_wp_config_modified');
+ delete_transient('wu_multisite_network_created');
+ delete_transient('wu_multisite_subdomain_install');
+ delete_transient('wu_multisite_sitename');
+ delete_transient('wu_multisite_email');
+ }
+
+ /**
+ * Display manual configuration instructions.
+ *
+ * @since 2.0.0
+ * @return void
+ */
+ protected function display_manual_instructions(): void {
+
+ $home_url = get_option('home');
+ $base_domain = parse_url($home_url, PHP_URL_HOST);
+ $subdomain_install = get_transient('wu_multisite_subdomain_install');
+
+ $wp_config_constants = "define( 'WP_ALLOW_MULTISITE', true );
+define( 'MULTISITE', true );
+define( 'SUBDOMAIN_INSTALL', " . ($subdomain_install ? 'true' : 'false') . " );
+define( 'DOMAIN_CURRENT_SITE', '{$base_domain}' );
+define( 'PATH_CURRENT_SITE', '/' );
+define( 'SITE_ID_CURRENT_SITE', 1 );
+define( 'BLOG_ID_CURRENT_SITE', 1 );";
+
+ $htaccess_rules = '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]';
+
+ ?>
+
+
+
+
+
+
+
+ wp-config.php',
+ '/* That\'s all, stop editing! Happy publishing. */'
+ );
+ ?>
+
+
+
+
+
+
+
+
+
+ add_final_multisite_constants($subdomain_install, $domain);
+
+ return true;
+ } catch (Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Adds the final multisite constants to wp-config.php.
+ *
+ * @since 2.0.0
+ * @param bool $subdomain_install Whether subdomains are used.
+ * @param string $domain The main domain.
+ * @return bool Whether the modification was successful.
+ */
+ protected function add_final_multisite_constants(bool $subdomain_install, string $domain): bool {
+
+ $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 MULTISITE is already defined
+ if (strpos($config_content, 'MULTISITE') !== false) {
+ return true; // Already configured
+ }
+
+ $constants_to_add = "\n// Multisite Ultimate: Multisite Configuration\n";
+ $constants_to_add .= "define( 'MULTISITE', true );\n";
+ $constants_to_add .= "define( 'SUBDOMAIN_INSTALL', " . ($subdomain_install ? 'true' : 'false') . " );\n";
+ $constants_to_add .= "define( 'DOMAIN_CURRENT_SITE', '{$domain}' );\n";
+ $constants_to_add .= "define( 'PATH_CURRENT_SITE', '/' );\n";
+ $constants_to_add .= "define( 'SITE_ID_CURRENT_SITE', 1 );\n";
+ $constants_to_add .= "define( 'BLOG_ID_CURRENT_SITE', 1 );\n\n";
+
+ // 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;
+ }
+
+ /**
+ * Register page scripts and styles.
+ *
+ * @since 2.0.0
+ * @return void
+ */
+ public function register_scripts(): void {
+
+ 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);
+ });
+ }
+ });
+ });
+ });
+ '
+ );
+ }
+}
\ No newline at end of file
diff --git a/inc/admin-pages/class-setup-wizard-admin-page.php b/inc/admin-pages/class-setup-wizard-admin-page.php
index 8c9906e0..685f3ad3 100644
--- a/inc/admin-pages/class-setup-wizard-admin-page.php
+++ b/inc/admin-pages/class-setup-wizard-admin-page.php
@@ -221,7 +221,15 @@ public function set_settings(): void {
*/
public function redirect_to_wizard(): void {
- if ( ! Requirements::run_setup() && wu_request('page') !== 'wp-ultimo-setup') {
+ // If multisite is not enabled, redirect to multisite setup page
+ if ( ! is_multisite() && wu_request('page') !== 'wp-ultimo-multisite-setup') {
+ wp_safe_redirect(admin_url('admin.php?page=wp-ultimo-multisite-setup'));
+
+ exit;
+ }
+
+ // If multisite is enabled but setup is not finished, redirect to setup wizard
+ if ( is_multisite() && ! Requirements::run_setup() && wu_request('page') !== 'wp-ultimo-setup') {
wp_safe_redirect(wu_network_admin_url('wp-ultimo-setup'));
exit;
diff --git a/inc/class-wp-ultimo.php b/inc/class-wp-ultimo.php
index e9b68296..ac2ac33a 100644
--- a/inc/class-wp-ultimo.php
+++ b/inc/class-wp-ultimo.php
@@ -140,6 +140,11 @@ public function init(): void {
*/
new WP_Ultimo\Admin_Pages\Setup_Wizard_Admin_Page();
+ /*
+ * Multisite Setup for non-multisite installations
+ */
+ new WP_Ultimo\Admin_Pages\Multisite_Setup_Admin_Page();
+
/*
* Loads the Multisite Ultimate settings helper class.
*/
diff --git a/inc/installers/class-default-content-installer.php b/inc/installers/class-default-content-installer.php
index fc278113..cc0b1030 100644
--- a/inc/installers/class-default-content-installer.php
+++ b/inc/installers/class-default-content-installer.php
@@ -52,8 +52,9 @@ public function init(): void {
*/
protected function done_creating_template_site() {
- $current_site = get_current_site();
-
+ if (! is_multisite()) {
+ return false;
+ }
$d = wu_get_site_domain_and_path('template');
return domain_exists($d->domain, $d->path, get_current_network_id());