claude-plugin-wordpress-manager 1.8.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +7 -3
- package/CHANGELOG.md +41 -0
- package/agents/wp-cicd-engineer.md +194 -0
- package/agents/wp-site-manager.md +27 -0
- package/docs/plans/2026-02-28-cicd-v2.0.0.md +375 -0
- package/docs/plans/2026-02-28-multisite-v1.9.0-design.md +258 -0
- package/docs/plans/2026-02-28-multisite-v1.9.0.md +1604 -0
- package/package.json +8 -3
- package/servers/wp-rest-bridge/build/tools/index.d.ts +260 -0
- package/servers/wp-rest-bridge/build/tools/index.js +6 -0
- package/servers/wp-rest-bridge/build/tools/multisite-network.d.ts +132 -0
- package/servers/wp-rest-bridge/build/tools/multisite-network.js +157 -0
- package/servers/wp-rest-bridge/build/tools/multisite-sites.d.ts +150 -0
- package/servers/wp-rest-bridge/build/tools/multisite-sites.js +160 -0
- package/servers/wp-rest-bridge/build/types.d.ts +13 -0
- package/servers/wp-rest-bridge/build/wordpress.d.ts +19 -0
- package/servers/wp-rest-bridge/build/wordpress.js +10 -0
- package/servers/wp-rest-bridge/build/wpcli.d.ts +23 -0
- package/servers/wp-rest-bridge/build/wpcli.js +72 -0
- package/skills/wordpress-router/references/decision-tree.md +7 -3
- package/skills/wp-cicd/SKILL.md +119 -0
- package/skills/wp-cicd/references/bitbucket-pipelines-wordpress.md +142 -0
- package/skills/wp-cicd/references/deploy-strategies.md +164 -0
- package/skills/wp-cicd/references/github-actions-wordpress.md +183 -0
- package/skills/wp-cicd/references/gitlab-ci-wordpress.md +189 -0
- package/skills/wp-cicd/references/quality-gates.md +215 -0
- package/skills/wp-cicd/references/secrets-management.md +175 -0
- package/skills/wp-cicd/references/wp-env-ci.md +135 -0
- package/skills/wp-cicd/scripts/cicd_inspect.mjs +183 -0
- package/skills/wp-deploy/SKILL.md +4 -0
- package/skills/wp-e2e-testing/SKILL.md +4 -0
- package/skills/wp-multisite/SKILL.md +92 -0
- package/skills/wp-multisite/references/domain-mapping.md +70 -0
- package/skills/wp-multisite/references/migration-multisite.md +76 -0
- package/skills/wp-multisite/references/network-plugins.md +66 -0
- package/skills/wp-multisite/references/network-setup.md +69 -0
- package/skills/wp-multisite/references/site-management.md +67 -0
- package/skills/wp-multisite/references/user-roles.md +73 -0
- package/skills/wp-multisite/scripts/multisite_inspect.mjs +160 -0
- package/skills/wp-phpstan/SKILL.md +4 -0
- package/skills/wp-security/SKILL.md +4 -0
- package/skills/wp-wpcli-and-ops/SKILL.md +4 -0
|
@@ -184,3 +184,7 @@ After completing the setup, verify:
|
|
|
184
184
|
## Recommended Agent
|
|
185
185
|
|
|
186
186
|
For hands-on test execution, debugging, and CI setup, use the **`wp-test-engineer`** agent.
|
|
187
|
+
|
|
188
|
+
### CI/CD Integration
|
|
189
|
+
|
|
190
|
+
For integrating WordPress tests into CI/CD pipelines (GitHub Actions, GitLab CI, Bitbucket Pipelines), see the `wp-cicd` skill and `wp-cicd-engineer` agent.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wp-multisite
|
|
3
|
+
description: |
|
|
4
|
+
This skill should be used when the user asks about "multisite", "network admin",
|
|
5
|
+
"sub-sites", "domain mapping", "super admin", "network activate",
|
|
6
|
+
"WordPress Multisite network", or any multisite network management operations.
|
|
7
|
+
version: 1.0.0
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
WordPress Multisite network management via WP-CLI (10 MCP tools). Covers sub-site CRUD, network plugin management, Super Admin listing, network settings, and domain mapping guidance. Uses a hybrid approach: REST API where available, WP-CLI for network-only operations.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
- User mentions multisite, network, sub-sites, or domain mapping
|
|
17
|
+
- User needs to create, activate, deactivate, or delete sub-sites
|
|
18
|
+
- User wants to network-activate or network-deactivate plugins
|
|
19
|
+
- User needs Super Admin listing or network settings
|
|
20
|
+
- User asks about migrating single-site to multisite or vice versa
|
|
21
|
+
|
|
22
|
+
## Prerequisites
|
|
23
|
+
|
|
24
|
+
WP-CLI access and multisite flag must be configured in `WP_SITES_CONFIG`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"id": "mynetwork",
|
|
29
|
+
"url": "https://network.example.com",
|
|
30
|
+
"username": "superadmin",
|
|
31
|
+
"password": "xxxx xxxx xxxx xxxx",
|
|
32
|
+
"wp_path": "/var/www/wordpress",
|
|
33
|
+
"ssh_host": "network.example.com",
|
|
34
|
+
"ssh_user": "deploy",
|
|
35
|
+
"ssh_key": "~/.ssh/id_rsa",
|
|
36
|
+
"is_multisite": true
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- `wp_path` — required for all wp-cli operations
|
|
41
|
+
- `ssh_host` / `ssh_user` — required for remote sites (omit for local)
|
|
42
|
+
- `is_multisite: true` — required flag to enable ms_* tools
|
|
43
|
+
|
|
44
|
+
## Detection
|
|
45
|
+
|
|
46
|
+
Run the detection script to check multisite presence:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
node skills/wp-multisite/scripts/multisite_inspect.mjs
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Multisite Operations Decision Tree
|
|
53
|
+
|
|
54
|
+
1. **Sub-site management?**
|
|
55
|
+
- List all sub-sites → `ms_list_sites`
|
|
56
|
+
- Get sub-site details → `ms_get_site`
|
|
57
|
+
- Create new sub-site → `ms_create_site`
|
|
58
|
+
- Activate/deactivate → `ms_activate_site`
|
|
59
|
+
- Delete sub-site → `ms_delete_site`
|
|
60
|
+
|
|
61
|
+
2. **Network plugin management?**
|
|
62
|
+
- List all plugins (with network status) → `ms_list_network_plugins`
|
|
63
|
+
- Network-activate plugin → `ms_network_activate_plugin`
|
|
64
|
+
- Network-deactivate plugin → `ms_network_deactivate_plugin`
|
|
65
|
+
|
|
66
|
+
3. **Network administration?**
|
|
67
|
+
- List Super Admins → `ms_list_super_admins`
|
|
68
|
+
- Get network settings → `ms_get_network_settings`
|
|
69
|
+
|
|
70
|
+
4. **Domain mapping / network setup / migration?**
|
|
71
|
+
- See reference files below (no dedicated MCP tool — use wp-cli via Bash)
|
|
72
|
+
|
|
73
|
+
## Recommended Agent
|
|
74
|
+
|
|
75
|
+
For complex multi-step multisite operations, use the `wp-site-manager` agent (which has a dedicated Multisite Network Management section).
|
|
76
|
+
|
|
77
|
+
## Additional Resources
|
|
78
|
+
|
|
79
|
+
### Reference Files
|
|
80
|
+
|
|
81
|
+
- **`references/network-setup.md`** — Sub-directory vs sub-domain, wp-config constants, installation
|
|
82
|
+
- **`references/site-management.md`** — CRUD sub-sites, templates, bulk operations
|
|
83
|
+
- **`references/domain-mapping.md`** — Custom domains, SSL, DNS CNAME, sunrise.php
|
|
84
|
+
- **`references/network-plugins.md`** — Network-activated vs per-site plugins, must-use plugins
|
|
85
|
+
- **`references/user-roles.md`** — Super Admin capabilities, site-level roles
|
|
86
|
+
- **`references/migration-multisite.md`** — Single to multisite and back, database tables
|
|
87
|
+
|
|
88
|
+
### Related Skills
|
|
89
|
+
|
|
90
|
+
- `wp-wpcli-and-ops` — WP-CLI command reference and multisite flags
|
|
91
|
+
- `wp-security` — Super Admin capabilities and multisite security
|
|
92
|
+
- `wp-deploy` — Deploy to multisite network
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Domain Mapping
|
|
2
|
+
|
|
3
|
+
Domain mapping allows each sub-site in a WordPress Multisite network to use its own custom domain instead of the default sub-directory or sub-domain URL.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
| Default URL | Mapped Domain |
|
|
8
|
+
|-------------|---------------|
|
|
9
|
+
| `network.com/shopA/` | `shopA.com` |
|
|
10
|
+
| `shopB.network.com` | `shopB.com` |
|
|
11
|
+
|
|
12
|
+
Since WordPress 4.5+, domain mapping is built into core (no plugin required for basic mapping).
|
|
13
|
+
|
|
14
|
+
## Setup Procedure
|
|
15
|
+
|
|
16
|
+
### 1. DNS Configuration
|
|
17
|
+
|
|
18
|
+
For each custom domain, create a DNS record pointing to the network server:
|
|
19
|
+
|
|
20
|
+
| Record Type | Name | Value |
|
|
21
|
+
|-------------|------|-------|
|
|
22
|
+
| A | `shopA.com` | `<server-ip>` |
|
|
23
|
+
| CNAME | `www.shopA.com` | `shopA.com` |
|
|
24
|
+
|
|
25
|
+
### 2. WordPress Configuration
|
|
26
|
+
|
|
27
|
+
In Network Admin > Sites > Edit Site > Domain:
|
|
28
|
+
- Change the site URL to the custom domain
|
|
29
|
+
|
|
30
|
+
Or via WP-CLI:
|
|
31
|
+
```bash
|
|
32
|
+
wp site list # find the blog_id
|
|
33
|
+
wp option update home 'https://shopA.com' --url=network.com/shopA/
|
|
34
|
+
wp option update siteurl 'https://shopA.com' --url=network.com/shopA/
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 3. SSL Certificate
|
|
38
|
+
|
|
39
|
+
Each mapped domain needs its own SSL certificate:
|
|
40
|
+
- **Let's Encrypt**: Use Certbot with `--domains shopA.com,shopB.com`
|
|
41
|
+
- **Wildcard**: Only covers `*.network.com`, NOT custom domains
|
|
42
|
+
- **Multi-domain SAN cert**: Can cover all mapped domains in one cert
|
|
43
|
+
|
|
44
|
+
### 4. Web Server Configuration
|
|
45
|
+
|
|
46
|
+
The web server must accept requests for all mapped domains. In Nginx:
|
|
47
|
+
|
|
48
|
+
```nginx
|
|
49
|
+
server {
|
|
50
|
+
server_name shopA.com shopB.com network.com *.network.com;
|
|
51
|
+
# ... standard WordPress config
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## sunrise.php (Advanced)
|
|
56
|
+
|
|
57
|
+
For complex domain mapping logic, WordPress supports a `sunrise.php` drop-in:
|
|
58
|
+
|
|
59
|
+
- Location: `wp-content/sunrise.php`
|
|
60
|
+
- Loaded very early in the WordPress bootstrap (before plugins)
|
|
61
|
+
- Must be enabled: `define('SUNRISE', true);` in wp-config.php
|
|
62
|
+
- Used by plugins like "WordPress MU Domain Mapping" (legacy) or "Mercator"
|
|
63
|
+
|
|
64
|
+
## Tips and Gotchas
|
|
65
|
+
|
|
66
|
+
- **Cookie domain**: After mapping, update `COOKIE_DOMAIN` if login issues occur.
|
|
67
|
+
- **Mixed content**: Ensure all mapped domains use HTTPS to avoid mixed content warnings.
|
|
68
|
+
- **Caching**: Flush caches after domain mapping changes — both server-side and CDN.
|
|
69
|
+
- **Search Console**: Register each mapped domain separately in Google Search Console.
|
|
70
|
+
- **Reverse proxy**: If using Cloudflare or similar, configure the DNS to point to the origin server's IP, not the CDN.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Migration: Single-site to Multisite and Back
|
|
2
|
+
|
|
3
|
+
Migrating between single-site and multisite WordPress installations requires careful planning due to database structure differences.
|
|
4
|
+
|
|
5
|
+
## Single-site to Multisite
|
|
6
|
+
|
|
7
|
+
### Prerequisites
|
|
8
|
+
- WordPress installed at domain root (not a subdirectory)
|
|
9
|
+
- All plugins deactivated
|
|
10
|
+
- Permalink structure using "pretty permalinks" (not plain)
|
|
11
|
+
- Full database and file backup
|
|
12
|
+
|
|
13
|
+
### Procedure
|
|
14
|
+
|
|
15
|
+
1. **Backup**: Full database dump + wp-content directory
|
|
16
|
+
2. **Deactivate all plugins** via wp-admin or wp-cli
|
|
17
|
+
3. **Enable multisite**: Add `define('WP_ALLOW_MULTISITE', true);` to wp-config.php
|
|
18
|
+
4. **Network Setup**: Navigate to Tools > Network Setup, choose sub-directory or sub-domain
|
|
19
|
+
5. **Apply configuration**: Copy generated code to wp-config.php and .htaccess
|
|
20
|
+
6. **Re-login**: WordPress redirects to login — sign in as Super Admin
|
|
21
|
+
7. **Re-activate plugins**: One by one, test each plugin for multisite compatibility
|
|
22
|
+
8. **Verify**: Check permalink structure, media uploads, and user roles
|
|
23
|
+
|
|
24
|
+
### What Changes in the Database
|
|
25
|
+
|
|
26
|
+
| Component | Before | After |
|
|
27
|
+
|-----------|--------|-------|
|
|
28
|
+
| Tables | `wp_posts`, `wp_options`, ... | Same (become site 1) |
|
|
29
|
+
| New tables | — | `wp_blogs`, `wp_site`, `wp_sitemeta`, `wp_registration_log`, `wp_signups` |
|
|
30
|
+
| Options | `wp_options` | `wp_options` (site 1) + `wp_sitemeta` (network) |
|
|
31
|
+
|
|
32
|
+
## Multisite to Single-site
|
|
33
|
+
|
|
34
|
+
This migration is more complex because you need to extract one sub-site from the network.
|
|
35
|
+
|
|
36
|
+
### Procedure (extract sub-site)
|
|
37
|
+
|
|
38
|
+
1. **Backup**: Full database dump + wp-content directory
|
|
39
|
+
2. **Export content**: Use WordPress Export (Tools > Export) on the target sub-site
|
|
40
|
+
3. **Fresh WordPress install**: Install a clean single-site WordPress
|
|
41
|
+
4. **Import content**: Use WordPress Importer plugin
|
|
42
|
+
5. **Copy uploads**: Copy `wp-content/uploads/sites/{blog_id}/` to `wp-content/uploads/`
|
|
43
|
+
6. **Activate theme and plugins**: Install and activate the same theme and plugins
|
|
44
|
+
7. **Verify**: Check media URLs, internal links, shortcodes
|
|
45
|
+
|
|
46
|
+
### Alternative: Direct Database Extraction
|
|
47
|
+
|
|
48
|
+
For large sites where export/import is impractical:
|
|
49
|
+
|
|
50
|
+
1. Export tables with prefix `wp_{blog_id}_` (e.g., `wp_2_posts`, `wp_2_options`)
|
|
51
|
+
2. Rename tables to standard prefix (e.g., `wp_2_posts` → `wp_posts`)
|
|
52
|
+
3. Update `siteurl` and `home` in `wp_options`
|
|
53
|
+
4. Search-replace old URLs in content
|
|
54
|
+
5. Remove multisite constants from wp-config.php
|
|
55
|
+
6. Update .htaccess to standard WordPress rules
|
|
56
|
+
|
|
57
|
+
## WP-CLI Migration Commands
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Export single site from multisite
|
|
61
|
+
wp db export site-backup.sql --url=subsite.example.com
|
|
62
|
+
|
|
63
|
+
# Search-replace URLs after migration
|
|
64
|
+
wp search-replace 'subsite.example.com' 'newdomain.com' --all-tables
|
|
65
|
+
|
|
66
|
+
# Export content as WXR
|
|
67
|
+
wp export --url=subsite.example.com --dir=/tmp/exports/
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Tips and Gotchas
|
|
71
|
+
|
|
72
|
+
- **Media paths**: Multisite stores uploads in `uploads/sites/{blog_id}/`. After migration to single-site, media URLs need search-replace.
|
|
73
|
+
- **User roles**: Users may have different roles on different sub-sites. When extracting, only the target site's role assignments transfer.
|
|
74
|
+
- **Plugins**: Some plugins store network-wide options in `wp_sitemeta`. These are lost when extracting to single-site.
|
|
75
|
+
- **Test first**: Always perform migration on a staging environment before production.
|
|
76
|
+
- **Backup twice**: Keep backups of both the source (multisite) and target (single-site) before starting.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Network Plugins and Themes
|
|
2
|
+
|
|
3
|
+
In WordPress Multisite, plugins and themes can be managed at the network level (Super Admin) or at the individual site level (Site Admin). Understanding the activation modes prevents conflicts.
|
|
4
|
+
|
|
5
|
+
## MCP Tools
|
|
6
|
+
|
|
7
|
+
| Tool | Usage |
|
|
8
|
+
|------|-------|
|
|
9
|
+
| `ms_list_network_plugins` | List all plugins with network activation status |
|
|
10
|
+
| `ms_network_activate_plugin` | Activate a plugin across the entire network |
|
|
11
|
+
| `ms_network_deactivate_plugin` | Deactivate a plugin from the entire network |
|
|
12
|
+
|
|
13
|
+
## Plugin Activation Modes
|
|
14
|
+
|
|
15
|
+
| Mode | Who Controls | Scope | Use Case |
|
|
16
|
+
|------|-------------|-------|----------|
|
|
17
|
+
| Network-activated | Super Admin | All sites | Security plugins, caching, essential functionality |
|
|
18
|
+
| Per-site activated | Site Admin | One site | Site-specific features |
|
|
19
|
+
| Must-use (mu-plugins) | Developer | All sites, always on | Core business logic, cannot be deactivated |
|
|
20
|
+
|
|
21
|
+
## Procedures
|
|
22
|
+
|
|
23
|
+
### Network-Activate a Plugin
|
|
24
|
+
|
|
25
|
+
1. `ms_network_activate_plugin` with the plugin slug
|
|
26
|
+
2. The plugin immediately activates on ALL sub-sites
|
|
27
|
+
3. Site Admins cannot deactivate a network-activated plugin
|
|
28
|
+
|
|
29
|
+
### Network-Deactivate a Plugin
|
|
30
|
+
|
|
31
|
+
1. `ms_network_deactivate_plugin` with the plugin slug
|
|
32
|
+
2. The plugin deactivates on ALL sub-sites simultaneously
|
|
33
|
+
3. Per-site activation state is lost
|
|
34
|
+
|
|
35
|
+
### Check Plugin Status
|
|
36
|
+
|
|
37
|
+
1. `ms_list_network_plugins` — returns all plugins with their status
|
|
38
|
+
2. Look for `network_only: true` in the response for network-activated plugins
|
|
39
|
+
|
|
40
|
+
## Theme Management
|
|
41
|
+
|
|
42
|
+
Themes in multisite work differently from plugins:
|
|
43
|
+
|
|
44
|
+
| Action | Level | Effect |
|
|
45
|
+
|--------|-------|--------|
|
|
46
|
+
| Network Enable | Super Admin | Theme becomes available for site admins to activate |
|
|
47
|
+
| Network Disable | Super Admin | Theme removed from site admin's theme list |
|
|
48
|
+
| Activate | Site Admin | Theme becomes active for that specific site |
|
|
49
|
+
|
|
50
|
+
A theme must be **network-enabled** before any site admin can use it.
|
|
51
|
+
|
|
52
|
+
## Must-Use Plugins
|
|
53
|
+
|
|
54
|
+
- Location: `wp-content/mu-plugins/`
|
|
55
|
+
- Always active on ALL sites — cannot be deactivated via UI
|
|
56
|
+
- Loaded before regular plugins
|
|
57
|
+
- No activation hooks (code runs immediately)
|
|
58
|
+
- Useful for: custom login, security rules, performance optimizations
|
|
59
|
+
|
|
60
|
+
## Tips and Gotchas
|
|
61
|
+
|
|
62
|
+
- **Network activation is immediate**: No confirmation dialog. All sites are affected instantly.
|
|
63
|
+
- **Plugin conflicts**: A network-activated plugin may conflict with per-site plugins. Test thoroughly.
|
|
64
|
+
- **Updates**: Plugin updates on multisite affect all sites. Test in staging first.
|
|
65
|
+
- **Memory**: Each network-activated plugin increases memory usage across all sites.
|
|
66
|
+
- **Drop-in replacements**: `object-cache.php`, `advanced-cache.php`, `db.php` are shared across all sites.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Network Setup
|
|
2
|
+
|
|
3
|
+
WordPress Multisite allows a single WordPress installation to host multiple websites (sub-sites) sharing the same codebase and database. Understanding the setup options is critical for architecture decisions.
|
|
4
|
+
|
|
5
|
+
## MCP Tools
|
|
6
|
+
|
|
7
|
+
| Tool | Usage |
|
|
8
|
+
|------|-------|
|
|
9
|
+
| `ms_get_network_settings` | View current network configuration |
|
|
10
|
+
|
|
11
|
+
## Sub-directory vs Sub-domain
|
|
12
|
+
|
|
13
|
+
| Mode | URL Pattern | Example | Requirements |
|
|
14
|
+
|------|------------|---------|-------------|
|
|
15
|
+
| Sub-directory | `example.com/site1/` | `example.com/blog/` | Default, works everywhere |
|
|
16
|
+
| Sub-domain | `site1.example.com` | `blog.example.com` | Wildcard DNS (`*.example.com`), wildcard SSL |
|
|
17
|
+
|
|
18
|
+
Decision factors:
|
|
19
|
+
- **Sub-directory**: simpler DNS, single SSL cert, better for related sites
|
|
20
|
+
- **Sub-domain**: each site feels independent, better for unrelated brands
|
|
21
|
+
|
|
22
|
+
## wp-config.php Constants
|
|
23
|
+
|
|
24
|
+
Required constants for multisite (set during network installation):
|
|
25
|
+
|
|
26
|
+
```php
|
|
27
|
+
define('WP_ALLOW_MULTISITE', true); // Step 1: enables Network Setup menu
|
|
28
|
+
define('MULTISITE', true); // Step 2: after network creation
|
|
29
|
+
define('SUBDOMAIN_INSTALL', false); // true for sub-domain, false for sub-directory
|
|
30
|
+
define('DOMAIN_CURRENT_SITE', 'example.com');
|
|
31
|
+
define('PATH_CURRENT_SITE', '/');
|
|
32
|
+
define('SITE_ID_CURRENT_SITE', 1);
|
|
33
|
+
define('BLOG_ID_CURRENT_SITE', 1);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Installation Procedure
|
|
37
|
+
|
|
38
|
+
1. Start with a fresh single-site WordPress installation
|
|
39
|
+
2. Add `define('WP_ALLOW_MULTISITE', true);` to wp-config.php
|
|
40
|
+
3. Navigate to Tools > Network Setup in wp-admin
|
|
41
|
+
4. Choose sub-directory or sub-domain
|
|
42
|
+
5. WordPress generates the remaining constants and .htaccess rules
|
|
43
|
+
6. Add the generated code to wp-config.php and .htaccess
|
|
44
|
+
7. Log in again — Network Admin menu appears
|
|
45
|
+
|
|
46
|
+
## .htaccess Rules (sub-directory mode)
|
|
47
|
+
|
|
48
|
+
```apache
|
|
49
|
+
RewriteEngine On
|
|
50
|
+
RewriteBase /
|
|
51
|
+
RewriteRule ^index\.php$ - [L]
|
|
52
|
+
|
|
53
|
+
# add a trailing slash to /wp-admin
|
|
54
|
+
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
|
|
55
|
+
|
|
56
|
+
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
|
57
|
+
RewriteCond %{REQUEST_FILENAME} -d
|
|
58
|
+
RewriteRule ^ - [L]
|
|
59
|
+
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
|
60
|
+
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
|
|
61
|
+
RewriteRule . index.php [L]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Tips and Gotchas
|
|
65
|
+
|
|
66
|
+
- **Cannot switch modes**: You cannot change from sub-directory to sub-domain (or vice versa) after network creation without a fresh install or complex migration.
|
|
67
|
+
- **Existing content**: If the single site already has content, sub-directory mode may conflict with existing page slugs.
|
|
68
|
+
- **SSL**: Sub-domain mode requires wildcard SSL (`*.example.com`). Let's Encrypt supports wildcard via DNS-01 challenge.
|
|
69
|
+
- **WP_ALLOW_MULTISITE vs MULTISITE**: `WP_ALLOW_MULTISITE` enables the setup UI; `MULTISITE` activates the network. They are different constants.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Site Management
|
|
2
|
+
|
|
3
|
+
Sub-site lifecycle in a WordPress Multisite network: creating, configuring, activating/deactivating, and deleting sites.
|
|
4
|
+
|
|
5
|
+
## MCP Tools
|
|
6
|
+
|
|
7
|
+
| Tool | Usage |
|
|
8
|
+
|------|-------|
|
|
9
|
+
| `ms_list_sites` | List all sub-sites with status |
|
|
10
|
+
| `ms_get_site` | Get details of a specific sub-site |
|
|
11
|
+
| `ms_create_site` | Create a new sub-site |
|
|
12
|
+
| `ms_activate_site` | Activate or deactivate a sub-site |
|
|
13
|
+
| `ms_delete_site` | Permanently delete a sub-site |
|
|
14
|
+
|
|
15
|
+
## Sub-site Lifecycle
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Create → Active → [Deactivate → Archived/Spam/Deleted]
|
|
19
|
+
→ [Delete permanently]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Common Procedures
|
|
23
|
+
|
|
24
|
+
### List All Sub-sites
|
|
25
|
+
|
|
26
|
+
1. `ms_list_sites` — returns blog_id, url, registered date, status for all sites
|
|
27
|
+
2. Review the `archived`, `spam`, `deleted` flags for each
|
|
28
|
+
|
|
29
|
+
### Create a New Sub-site
|
|
30
|
+
|
|
31
|
+
1. `ms_create_site` with slug, title, admin email
|
|
32
|
+
2. WordPress creates the sub-site with default theme and plugins
|
|
33
|
+
3. The specified email becomes the sub-site admin
|
|
34
|
+
|
|
35
|
+
### Deactivate a Sub-site
|
|
36
|
+
|
|
37
|
+
1. `ms_activate_site` with `active: false` and the target blog_id
|
|
38
|
+
2. Deactivated sites return a "This site has been archived" message to visitors
|
|
39
|
+
3. Content and settings are preserved
|
|
40
|
+
|
|
41
|
+
### Delete a Sub-site
|
|
42
|
+
|
|
43
|
+
1. `ms_delete_site` with blog_id and `confirm: true`
|
|
44
|
+
2. **Permanent**: removes all content, settings, and uploads for that sub-site
|
|
45
|
+
3. Database tables for the sub-site are dropped
|
|
46
|
+
|
|
47
|
+
## Site Properties
|
|
48
|
+
|
|
49
|
+
| Property | Description |
|
|
50
|
+
|----------|-------------|
|
|
51
|
+
| `blog_id` | Unique numeric identifier |
|
|
52
|
+
| `domain` | Domain name of the sub-site |
|
|
53
|
+
| `path` | URL path (e.g., `/blog/` in sub-directory mode) |
|
|
54
|
+
| `registered` | Creation timestamp |
|
|
55
|
+
| `last_updated` | Last modification timestamp |
|
|
56
|
+
| `public` | Whether the site appears in search results |
|
|
57
|
+
| `archived` | Manually archived by network admin |
|
|
58
|
+
| `spam` | Marked as spam |
|
|
59
|
+
| `deleted` | Soft-deleted (not permanently removed) |
|
|
60
|
+
|
|
61
|
+
## Tips and Gotchas
|
|
62
|
+
|
|
63
|
+
- **Blog ID 1**: The main site always has `blog_id: 1`. Do not delete it.
|
|
64
|
+
- **Uploads**: Each sub-site has its own uploads directory under `wp-content/uploads/sites/{blog_id}/`.
|
|
65
|
+
- **Database tables**: Each sub-site gets its own set of tables with prefix `wp_{blog_id}_` (e.g., `wp_2_posts`, `wp_2_options`).
|
|
66
|
+
- **Default content**: New sub-sites get a "Hello World" post and sample page, similar to a fresh WordPress install.
|
|
67
|
+
- **Themes**: Sub-sites can only use themes that are network-enabled or network-activated. See `network-plugins.md`.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# User Roles in Multisite
|
|
2
|
+
|
|
3
|
+
WordPress Multisite adds a Super Admin role above the standard role hierarchy. Users can have different roles on different sub-sites within the same network.
|
|
4
|
+
|
|
5
|
+
## MCP Tools
|
|
6
|
+
|
|
7
|
+
| Tool | Usage |
|
|
8
|
+
|------|-------|
|
|
9
|
+
| `ms_list_super_admins` | List all Super Admin users in the network |
|
|
10
|
+
|
|
11
|
+
## Role Hierarchy
|
|
12
|
+
|
|
13
|
+
| Role | Scope | Key Capabilities |
|
|
14
|
+
|------|-------|-----------------|
|
|
15
|
+
| Super Admin | Entire network | All capabilities on all sites, network settings, site CRUD |
|
|
16
|
+
| Administrator | Single site | Full control of one sub-site (cannot install plugins/themes) |
|
|
17
|
+
| Editor | Single site | Manage and publish all posts on one site |
|
|
18
|
+
| Author | Single site | Publish own posts |
|
|
19
|
+
| Contributor | Single site | Write drafts, cannot publish |
|
|
20
|
+
| Subscriber | Single site | Read-only access |
|
|
21
|
+
|
|
22
|
+
## Super Admin vs Administrator (Multisite)
|
|
23
|
+
|
|
24
|
+
| Capability | Super Admin | Site Administrator |
|
|
25
|
+
|-----------|-------------|-------------------|
|
|
26
|
+
| Install plugins | Yes | No |
|
|
27
|
+
| Install themes | Yes | No |
|
|
28
|
+
| Create/delete sub-sites | Yes | No |
|
|
29
|
+
| Network activate plugins | Yes | No |
|
|
30
|
+
| Edit wp-config.php | Yes | No |
|
|
31
|
+
| Manage network settings | Yes | No |
|
|
32
|
+
| Edit files (theme/plugin editor) | Yes | No (disabled by default) |
|
|
33
|
+
| Manage site users | Yes | Yes (own site only) |
|
|
34
|
+
| Manage site options | Yes | Yes (own site only) |
|
|
35
|
+
|
|
36
|
+
## User Registration Modes
|
|
37
|
+
|
|
38
|
+
Network-wide setting (Network Admin > Settings):
|
|
39
|
+
|
|
40
|
+
| Mode | Description |
|
|
41
|
+
|------|-------------|
|
|
42
|
+
| Registration disabled | No one can register |
|
|
43
|
+
| User accounts may be registered | Users can register but not create sites |
|
|
44
|
+
| Logged-in users may register new sites | Existing users can create sub-sites |
|
|
45
|
+
| Both user accounts and sites can be registered | Open registration for users and sites |
|
|
46
|
+
|
|
47
|
+
## Common Operations
|
|
48
|
+
|
|
49
|
+
### List Super Admins
|
|
50
|
+
1. `ms_list_super_admins` — returns usernames with super admin status
|
|
51
|
+
|
|
52
|
+
### Add Super Admin (via wp-cli)
|
|
53
|
+
```bash
|
|
54
|
+
wp super-admin add username
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Remove Super Admin (via wp-cli)
|
|
58
|
+
```bash
|
|
59
|
+
wp super-admin remove username
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Add User to Sub-site (via wp-cli)
|
|
63
|
+
```bash
|
|
64
|
+
wp user set-role username editor --url=site1.example.com
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Tips and Gotchas
|
|
68
|
+
|
|
69
|
+
- **Super Admin bypass**: Super Admins bypass all capability checks. Use this role sparingly.
|
|
70
|
+
- **User exists once**: A user account exists once in the network but can have different roles on different sub-sites.
|
|
71
|
+
- **Cannot demote yourself**: The last Super Admin cannot remove their own super admin status.
|
|
72
|
+
- **wp-admin vs network-admin**: Super Admins see both site-level wp-admin and network-level wp-admin/network/.
|
|
73
|
+
- **Plugin capability checks**: Plugins using `current_user_can()` should work correctly with multisite, but some older plugins may not distinguish Super Admin from site Administrator.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* multisite_inspect.mjs — Detect WordPress Multisite configuration.
|
|
3
|
+
*
|
|
4
|
+
* Scans for multisite indicators: wp-config.php constants, WP_SITES_CONFIG flags,
|
|
5
|
+
* sunrise.php (domain mapping), .htaccess multisite rewrite rules.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node multisite_inspect.mjs [--cwd=/path/to/check]
|
|
9
|
+
*
|
|
10
|
+
* Exit codes:
|
|
11
|
+
* 0 — multisite indicators found
|
|
12
|
+
* 1 — no multisite indicators found
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
16
|
+
import { join, resolve } from 'node:path';
|
|
17
|
+
import { argv, env, stdout, exit } from 'node:process';
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Helpers
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
function readFileSafe(filePath) {
|
|
24
|
+
try { return readFileSync(filePath, 'utf-8'); } catch { return null; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function existsSafe(filePath) {
|
|
28
|
+
try { return existsSync(filePath); } catch { return false; }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Detectors
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
function detectWpConfig(cwd) {
|
|
36
|
+
const paths = [
|
|
37
|
+
join(cwd, 'wp-config.php'),
|
|
38
|
+
join(cwd, '../wp-config.php'), // wp-config one level up (common setup)
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
for (const p of paths) {
|
|
42
|
+
const content = readFileSafe(p);
|
|
43
|
+
if (!content) continue;
|
|
44
|
+
|
|
45
|
+
const multisite = /define\s*\(\s*['"]MULTISITE['"]\s*,\s*true\s*\)/i.test(content);
|
|
46
|
+
const subdomain = content.match(/define\s*\(\s*['"]SUBDOMAIN_INSTALL['"]\s*,\s*(true|false)\s*\)/i);
|
|
47
|
+
const domain = content.match(/define\s*\(\s*['"]DOMAIN_CURRENT_SITE['"]\s*,\s*['"]([^'"]+)['"]\s*\)/i);
|
|
48
|
+
const pathMatch = content.match(/define\s*\(\s*['"]PATH_CURRENT_SITE['"]\s*,\s*['"]([^'"]+)['"]\s*\)/i);
|
|
49
|
+
|
|
50
|
+
if (multisite) {
|
|
51
|
+
return {
|
|
52
|
+
found: true,
|
|
53
|
+
path: p,
|
|
54
|
+
subdomain_install: subdomain ? subdomain[1] === 'true' : null,
|
|
55
|
+
domain_current_site: domain ? domain[1] : null,
|
|
56
|
+
path_current_site: pathMatch ? pathMatch[1] : null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function detectSitesConfig() {
|
|
64
|
+
const sitesJson = env.WP_SITES_CONFIG;
|
|
65
|
+
if (!sitesJson) return null;
|
|
66
|
+
try {
|
|
67
|
+
const sites = JSON.parse(sitesJson);
|
|
68
|
+
const msSites = sites.filter(s => s.is_multisite === true);
|
|
69
|
+
const cliSites = sites.filter(s => s.wp_path);
|
|
70
|
+
return {
|
|
71
|
+
multisite_sites: msSites.map(s => ({
|
|
72
|
+
id: s.id,
|
|
73
|
+
wp_path: s.wp_path || null,
|
|
74
|
+
ssh_host: s.ssh_host || null,
|
|
75
|
+
has_wpcli: !!s.wp_path,
|
|
76
|
+
})),
|
|
77
|
+
cli_ready_sites: cliSites.map(s => s.id),
|
|
78
|
+
count: msSites.length,
|
|
79
|
+
};
|
|
80
|
+
} catch { return null; }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function detectSunrise(cwd) {
|
|
84
|
+
const paths = [
|
|
85
|
+
join(cwd, 'wp-content/sunrise.php'),
|
|
86
|
+
join(cwd, 'sunrise.php'),
|
|
87
|
+
];
|
|
88
|
+
for (const p of paths) {
|
|
89
|
+
if (existsSafe(p)) {
|
|
90
|
+
return { found: true, path: p };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function detectHtaccessMultisite(cwd) {
|
|
97
|
+
const content = readFileSafe(join(cwd, '.htaccess'));
|
|
98
|
+
if (!content) return null;
|
|
99
|
+
|
|
100
|
+
// WordPress multisite .htaccess has specific rewrite rules
|
|
101
|
+
const hasMultisiteRules = /RewriteRule\s+\.\s+index\.php/i.test(content) &&
|
|
102
|
+
(/upload/.test(content) || /files/.test(content) || /blogs\.dir/.test(content));
|
|
103
|
+
|
|
104
|
+
return hasMultisiteRules ? { found: true } : null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Main
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
function main() {
|
|
112
|
+
const cwdArg = argv.find(a => a.startsWith('--cwd='));
|
|
113
|
+
const cwd = cwdArg ? resolve(cwdArg.split('=')[1]) : process.cwd();
|
|
114
|
+
|
|
115
|
+
const wpConfig = detectWpConfig(cwd);
|
|
116
|
+
const sitesConfig = detectSitesConfig();
|
|
117
|
+
const sunrise = detectSunrise(cwd);
|
|
118
|
+
const htaccess = detectHtaccessMultisite(cwd);
|
|
119
|
+
|
|
120
|
+
const signals = [];
|
|
121
|
+
if (wpConfig) signals.push('wp_config_multisite');
|
|
122
|
+
if (sitesConfig?.count > 0) signals.push('sites_config_multisite');
|
|
123
|
+
if (sunrise) signals.push('sunrise_domain_mapping');
|
|
124
|
+
if (htaccess) signals.push('htaccess_multisite_rules');
|
|
125
|
+
|
|
126
|
+
const report = {
|
|
127
|
+
tool: 'multisite_inspect',
|
|
128
|
+
version: '1.0.0',
|
|
129
|
+
timestamp: new Date().toISOString(),
|
|
130
|
+
cwd,
|
|
131
|
+
found: signals.length > 0,
|
|
132
|
+
signals,
|
|
133
|
+
details: {
|
|
134
|
+
wp_config: wpConfig || undefined,
|
|
135
|
+
sites_config: sitesConfig || undefined,
|
|
136
|
+
sunrise: sunrise || undefined,
|
|
137
|
+
htaccess: htaccess || undefined,
|
|
138
|
+
},
|
|
139
|
+
recommendations: [],
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
if (wpConfig && !sitesConfig?.count) {
|
|
143
|
+
report.recommendations.push('Multisite detected in wp-config.php but no site in WP_SITES_CONFIG has is_multisite: true');
|
|
144
|
+
}
|
|
145
|
+
if (sitesConfig?.count > 0) {
|
|
146
|
+
const noCli = sitesConfig.multisite_sites.filter(s => !s.has_wpcli);
|
|
147
|
+
if (noCli.length > 0) {
|
|
148
|
+
report.recommendations.push(`Sites without wp_path (no wp-cli access): ${noCli.map(s => s.id).join(', ')}`);
|
|
149
|
+
}
|
|
150
|
+
report.recommendations.push(`${sitesConfig.count} multisite network(s) configured — 10 ms_* tools available`);
|
|
151
|
+
}
|
|
152
|
+
if (sunrise) {
|
|
153
|
+
report.recommendations.push('sunrise.php detected — domain mapping is active');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
157
|
+
exit(signals.length > 0 ? 0 : 1);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
main();
|