Skip to content
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

Blueprints: Add enableMultisite step #888

Merged
merged 44 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
d93e5ed
Noodling on multisites
adamziel Dec 20, 2023
386914e
Try with a secure TLD
adamziel Jan 23, 2024
d5c2b19
Blueprints: Add enableMultisite step
adamziel Jan 23, 2024
f0ab21c
Merge branch 'trunk' into experiment/multisite-support
adamziel Jan 23, 2024
a6cbe9f
Ensure rewriteWordPressUrl returns in all code paths
adamziel Jan 23, 2024
87883cd
Improve rewrite rules for multisite/single site compat
adamziel Jan 24, 2024
23fd530
Remove the SQLite Translator patch to fix the unit tests
adamziel Jan 24, 2024
b62988d
Add an E2E test to confirm the permalink structure works
adamziel Jan 24, 2024
8dd5001
Merge branch 'trunk' into experiment/multisite-support
adamziel Jan 24, 2024
f96c614
Tarter mu-plugins/sqlite-database-integration instead of plugins/sqli…
adamziel Jan 24, 2024
7a78cf4
Preserve WP Nonce when cloning the request object
adamziel Jan 26, 2024
e3be213
Ensure the sqlite integration mu-plugin gets loaded
adamziel Jan 26, 2024
fc1344d
Setup the required sunrise.php file after setting up a multisite.
adamziel Jan 26, 2024
4f80766
Merge branch 'trunk' into experiment/multisite-support
adamziel Jan 26, 2024
3cc45ee
Clean up dev artifacts
adamziel Jan 26, 2024
676eeff
Run Cypress on https://playground.test instead of localhost:4400
adamziel Jan 26, 2024
37c7062
Use port 80 on CI
adamziel Jan 26, 2024
fc59427
Sudo run npx to bind to port 80
adamziel Jan 26, 2024
f1ef206
Run e2e as root to enable binding to port 80
adamziel Jan 26, 2024
072d199
Document the rationale for --user root
adamziel Jan 26, 2024
b23c783
Use a GitHub CI - compatible images
adamziel Jan 26, 2024
04b3c06
Install apt-get install libasound2:i386 for Cypress
adamziel Jan 26, 2024
fa7549b
Add apt-get update
adamziel Jan 26, 2024
b233c64
Install libasound2, not libasound2:i386
adamziel Jan 26, 2024
16616d8
Use sudo after all
adamziel Jan 26, 2024
9eef4a6
Sudo cypress install
adamziel Jan 26, 2024
b3d3b0a
Fix e2e tests
adamziel Jan 26, 2024
d3ba9c5
More relaxed e2e check
adamziel Jan 26, 2024
36ff321
Fix multisite unit tests
adamziel Jan 26, 2024
2788dd2
Set cypress timeout to 90s
adamziel Jan 27, 2024
2fbbc0c
Disable the less unit test on PHP 8.3
adamziel Jan 27, 2024
6b8ac82
Merge branch 'trunk' into experiment/multisite-support
adamziel Jan 27, 2024
dbced3f
Rely on a global 90s timeout (instead of localized 60s timeouts)
adamziel Jan 27, 2024
b424ee5
Bump the timeout to 6 minutes
adamziel Jan 27, 2024
c457c4a
Split E2E tests into three files
adamziel Jan 27, 2024
195c30d
Adjust the CYPRESS_CI env variable
adamziel Jan 27, 2024
d5587a6
Source the Gutenberg zip from a URL without /website-server in it
adamziel Jan 27, 2024
282c0ff
Use "find" instead of "get" in the multisite e2e test – otherwise it …
adamziel Jan 28, 2024
7b227da
Merge branch 'trunk' into experiment/multisite-support
adamziel Jan 29, 2024
54f53d1
Do not hardcode Playground domain
adamziel Jan 29, 2024
d8e4316
Improve the comment documenting the rewrite process.
adamziel Jan 29, 2024
30761da
Rebuild WordPress
adamziel Jan 29, 2024
b959461
Nice error message when setting up a multisite on a URL with a custom…
adamziel Jan 29, 2024
1ef8b87
Cache 'package-lock.json' in actions.yml
adamziel Jan 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/docs/site/docs/13-contributing/02-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,17 @@ Be sure to also review the following resources:
- [Packages and projects](./04-packages-and-projects.md)
- [Vision and Philosophy](https://github.com/WordPress/wordpress-playground/issues/472)
- [Roadmap](https://github.com/WordPress/wordpress-playground/issues/525)

## HTTPS setup

Some Playground features, like multisite, require a local test domain running via HTTPS.

One way to get it set up is with [Laravel Valet](https://laravel.com/docs/10.x/valet).

Once your Valet is installed, run:

```bash
valet proxy playground.test http://localhost:5400 --secure
```

Your dev server is now available via https://playground.test.
2 changes: 1 addition & 1 deletion packages/php-wasm/universal/src/lib/base-php.ts
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ export abstract class BasePHP implements IsomorphicLocalPHP {
}
}

defineConstant(key: string, value: string | number | null) {
defineConstant(key: string, value: string | boolean | number | null) {
let consts = {};
try {
consts = JSON.parse(
Expand Down
19 changes: 14 additions & 5 deletions packages/php-wasm/universal/src/lib/php-request-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export class PHPRequestHandler implements RequestHandler {
*/
const release = await this.#semaphore.acquire();
try {
this.php.addServerGlobalEntry('REMOTE_ADDR', '127.0.0.1');
this.php.addServerGlobalEntry('DOCUMENT_ROOT', this.#DOCROOT);
this.php.addServerGlobalEntry(
'HTTPS',
Expand Down Expand Up @@ -236,7 +237,18 @@ export class PHPRequestHandler implements RequestHandler {

let scriptPath;
try {
scriptPath = this.#resolvePHPFilePath(requestedUrl.pathname);
// Support URL rewriting
let requestedPath = requestedUrl.pathname;
if (request.headers?.['x-rewrite-url']) {
try {
requestedPath = new URL(
request.headers['x-rewrite-url']
).pathname;
} catch (error) {
// Ignore
}
}
scriptPath = this.#resolvePHPFilePath(requestedPath);
} catch (error) {
return new PHPResponse(
404,
Expand Down Expand Up @@ -291,10 +303,7 @@ export class PHPRequestHandler implements RequestHandler {
if (this.php.fileExists(resolvedFsPath)) {
return resolvedFsPath;
}
if (!this.php.fileExists(`${this.#DOCROOT}/index.php`)) {
throw new Error(`File not found: ${resolvedFsPath}`);
}
return `${this.#DOCROOT}/index.php`;
throw new Error(`File not found: ${resolvedFsPath}`);
}
}

Expand Down
11 changes: 10 additions & 1 deletion packages/php-wasm/web/src/lib/register-service-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,16 @@ export async function registerServiceWorker<
>(phpApi: Client, scope: string, scriptUrl: string) {
const sw = navigator.serviceWorker;
if (!sw) {
throw new Error('Service workers are not supported in this browser.');
if (location.protocol === 'https:') {
throw new Error(
'Service workers are not supported in this browser.'
);
} else {
throw new Error(
'WordPress Playground requires service workers which are only supported ' +
'on HTTPS sites. This site does not use HTTPS, please retry on one that does. '
);
}
}

console.debug(`[window][sw] Registering a Service Worker`);
Expand Down
2 changes: 1 addition & 1 deletion packages/php-wasm/web/src/lib/web-php-endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export class WebPHPEndpoint implements IsomorphicLocalPHP {
}

/** @inheritDoc @php-wasm/web!WebPHP.defineConstant */
defineConstant(key: string, value: string | number | null): void {
defineConstant(key: string, value: string | boolean | number | null): void {
_private.get(this)!.php.defineConstant(key, value);
}

Expand Down
23 changes: 23 additions & 0 deletions packages/playground/blueprints/public/blueprint-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,29 @@
},
"required": ["siteUrl", "step"]
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"progress": {
"type": "object",
"properties": {
"weight": {
"type": "number"
},
"caption": {
"type": "string"
}
},
"additionalProperties": false
},
"step": {
"type": "string",
"const": "enableMultisite"
}
},
"required": ["step"]
},
{
"type": "object",
"additionalProperties": false,
Expand Down
141 changes: 141 additions & 0 deletions packages/playground/blueprints/src/lib/steps/enable-multisite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { phpVar } from '@php-wasm/util';
import { StepHandler } from '.';
import { defineWpConfigConsts } from './define-wp-config-consts';
import { login } from './login';
import { request } from './request';
import { setSiteOptions } from './site-data';

/**
* @inheritDoc enableMultisite
* @hasRunnableExample
* @example
*
* <code>
* {
* "step": "enableMultisite",
* }
* </code>
*/
export interface EnableMultisiteStep {
step: 'enableMultisite';
}

/**
* Defines constants in a wp-config.php file.
*
* This step can be called multiple times, and the constants will be merged.
*
* @param playground The playground client.
* @param enableMultisite
*/
export const enableMultisite: StepHandler<EnableMultisiteStep> = async (
playground
) => {
// Ensure we're logged in
await login(playground, {});

await defineWpConfigConsts(playground, {
consts: {
WP_ALLOW_MULTISITE: 1,
},
});

const url = new URL(await playground.absoluteUrl);
const sitePath = url.pathname.replace(/\/$/, '') + '/';
const siteUrl = `${url.protocol}//${url.host}${sitePath}`;
await setSiteOptions(playground, {
options: {
siteurl: siteUrl,
home: siteUrl,
},
});

const docroot = await playground.documentRoot;

// Deactivate all the plugins as required by the multisite installation.
await playground.run({
code: `<?php
define( 'WP_ADMIN', true );
require_once(${phpVar(docroot)} . "/wp-load.php");
require_once(${phpVar(docroot)} . "/wp-admin/includes/plugin.php");
$plugins = glob(${phpVar(docroot)} . "/wp-content/plugins/*");
foreach($plugins as $plugin_path) {
if (!is_dir($plugin_path)) {
deactivate_plugins($plugin_path);
continue;
}
// Find plugin entry file
foreach ( ( glob( $plugin_path . '/*.php' ) ?: array() ) as $file ) {
$info = get_plugin_data( $file, false, false );
var_dump($file);
if ( ! empty( $info['Name'] ) ) {
deactivate_plugins( $file );
break;
}
}
}
`,
});

// Extract nonce for multisite form submission
const networkForm = await request(playground, {
request: {
url: '/wp-admin/network.php',
},
});
const nonce = networkForm.text.match(
/name="_wpnonce"\s+value="([^"]+)"/
)?.[1];

// @TODO: Extract nonce using wp_create_nonce() instead
// of an HTTP request.
// Unfortunately, the code snippet below does not
// yield a nonce that WordPress would accept:
/*
const nonce = (await playground.run({
code: `<?php
require '/wordpress/wp-load.php';
wp_set_current_user(1);
echo wp_create_nonce('install-network-1');
`,
})).text;
*/

await request(playground, {
request: {
url: '/wp-admin/network.php',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: jsonToUrlEncoded({
_wpnonce: nonce,
_wp_http_referer: sitePath + 'wp-admin/network.php',
sitename: 'My WordPress Website Sites',
email: '[email protected]',
submit: 'Install',
}),
},
});

await defineWpConfigConsts(playground, {
consts: {
MULTISITE: true,
SUBDOMAIN_INSTALL: false,
SITE_ID_CURRENT_SITE: 1,
BLOG_ID_CURRENT_SITE: 1,
DOMAIN_CURRENT_SITE: url.hostname,
PATH_CURRENT_SITE: sitePath,
},
});
await login(playground, {});
};

function jsonToUrlEncoded(json: Record<string, string>) {
return Object.keys(json)
.map(
(key) =>
encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
)
.join('&');
}
1 change: 1 addition & 0 deletions packages/playground/blueprints/src/lib/steps/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export { runPHPWithOptions } from './run-php-with-options';
export { runSql } from './run-sql';
export { setPhpIniEntry } from './set-php-ini-entry';
export { request } from './request';
export { enableMultisite } from './enable-multisite';
export { cp } from './cp';
export { mv } from './mv';
export { rm } from './rm';
Expand Down
3 changes: 3 additions & 0 deletions packages/playground/blueprints/src/lib/steps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { ActivateThemeStep } from './activate-theme';
import { UnzipStep } from './unzip';
import { ImportWordPressFilesStep } from './import-wordpress-files';
import { ImportFileStep } from './import-file';
import { EnableMultisiteStep } from './enable-multisite';

export type Step = GenericStep<FileReference>;
export type StepDefinition = Step & {
Expand All @@ -50,6 +51,7 @@ export type GenericStep<Resource> =
| CpStep
| DefineWpConfigConstsStep
| DefineSiteUrlStep
| EnableMultisiteStep
| ImportFileStep<Resource>
| ImportWordPressFilesStep<Resource>
| InstallPluginStep<Resource>
Expand Down Expand Up @@ -77,6 +79,7 @@ export type {
CpStep,
DefineWpConfigConstsStep,
DefineSiteUrlStep,
EnableMultisiteStep,
ImportFileStep,
ImportWordPressFilesStep,
InstallPluginStep,
Expand Down
2 changes: 2 additions & 0 deletions packages/playground/blueprints/src/lib/steps/run-sql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export const runSql: StepHandler<RunSqlStep<File>> = async (
}
`,
});
console.log(runPhp.text);
console.log(runPhp.errors);

await rm(playground, { path: sqlFilename });

Expand Down
1 change: 0 additions & 1 deletion packages/playground/client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ export async function startPlaygroundWeb({
progressTracker
);
await runBlueprintSteps(compiled, playground);
progressTracker.finish();

return playground;
}
Expand Down
Loading