Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 5 additions & 50 deletions packages/playground/blueprints/src/lib/steps/zip-wp-content.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
import { joinPaths, phpVars } from '@php-wasm/util';
import type { UniversalPHP } from '@php-wasm/universal';
import { wpContentFilesExcludedFromExport } from '../utils/wp-content-files-excluded-from-exports';

interface ZipWpContentOptions {
/**
* @private
* A temporary workaround to enable including the WordPress default theme
* in the exported zip file.
*/
selfContained?: boolean;
}

/**
* Replace the current wp-content directory with one from the provided zip file.
* Creates a complete Playground archive containing wp-content, wp-config.php,
* and export metadata.
*
* @param playground Playground client.
* @param wpContentZip Zipped WordPress site.
*/
export const zipWpContent = async (
playground: UniversalPHP,
{ selfContained = false }: ZipWpContentOptions = {}
) => {
export const zipWpContent = async (playground: UniversalPHP) => {
const zipPath = '/tmp/wordpress-playground.zip';
const manifestPath = '/tmp/playground-export.json';

Expand All @@ -33,50 +20,23 @@ export const zipWpContent = async (
const siteUrl = await playground.absoluteUrl;
await playground.writeFile(
manifestPath,
new TextEncoder().encode(JSON.stringify({ siteUrl }))
new TextEncoder().encode(JSON.stringify({ formatVersion: 2, siteUrl }))
);

let exceptPaths = wpContentFilesExcludedFromExport;
/*
* This is a temporary workaround to enable including the WordPress
* default theme and the SQLite plugin in the exported zip file. Let's
* transition from this workaround to iterator-based streams once the
* new API is merged in PR 851.
*/
if (selfContained) {
// This is a bit backwards, so hang on!
// We have a list of paths to exclude.
// We then *remove* the default theme and the SQLite plugin from that list.
// As a result, we *include* the default theme and the SQLite plugin in the
// final zip. It is hacky and will be removed soon.
exceptPaths = exceptPaths
.filter((path) => !path.startsWith('themes/twenty'))
.filter(
(path) => path !== 'mu-plugins/sqlite-database-integration'
);
}

const additionalPaths: Record<string, string> = {
[manifestPath]: 'playground-export.json',
[joinPaths(documentRoot, 'wp-config.php')]: 'wp-config.php',
};
if (selfContained) {
additionalPaths[joinPaths(documentRoot, 'wp-config.php')] =
'wp-config.php';
}

const js = phpVars({
zipPath,
wpContentPath,
documentRoot,
exceptPaths: exceptPaths.map((path) =>
joinPaths(documentRoot, 'wp-content', path)
),
additionalPaths,
});
await runPhpWithZipFunctions(
playground,
`zipDir(${js.wpContentPath}, ${js.zipPath}, array(
'exclude_paths' => ${js.exceptPaths},
'zip_root' => ${js.documentRoot},
'additional_paths' => ${js.additionalPaths}
));`
Expand All @@ -95,7 +55,6 @@ function zipDir($root, $output, $options = array())
{
$root = rtrim($root, '/');
$additionalPaths = array_key_exists('additional_paths', $options) ? $options['additional_paths'] : array();
$excludePaths = array_key_exists('exclude_paths', $options) ? $options['exclude_paths'] : array();
$zip_root = array_key_exists('zip_root', $options) ? $options['zip_root'] : $root;

$zip = new ZipArchive;
Expand All @@ -114,10 +73,6 @@ function zipDir($root, $output, $options = array())
}

$entry = join_paths($current_dir, $entry);
if (in_array($entry, $excludePaths)) {
continue;
}

if (is_dir($entry)) {
$directory_path = $entry . '/';
array_push($directories, $directory_path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from '@wp-playground/wordpress-builds';
import { bootWordPressAndRequestHandler } from '@wp-playground/wordpress';
import { loadNodeRuntime } from '@php-wasm/node';
import { joinPaths, phpVar, randomFilename } from '@php-wasm/util';
import { dirname, joinPaths, phpVar, randomFilename } from '@php-wasm/util';
import { setURLScope } from '@php-wasm/scopes';

describe('Blueprint step importWordPressFiles', () => {
Expand Down Expand Up @@ -76,9 +76,42 @@ describe('Blueprint step importWordPressFiles', () => {

expect(result.text).toBeTruthy();
const manifest = JSON.parse(result.text);
expect(manifest.formatVersion).toBe(2);
expect(manifest.siteUrl).toContain(`scope:${sourceScope}`);
});

it('should export all wp-content files and wp-config.php', async () => {
const documentRoot = await sourcePHP.documentRoot;
const customizedThemeFile = joinPaths(
documentRoot,
'wp-content/themes/twentytwentyfive/playground-export-test.txt'
);
await sourcePHP.mkdir(dirname(customizedThemeFile));
await sourcePHP.writeFile(
customizedThemeFile,
new TextEncoder().encode('customized default theme')
);

const zipBuffer = await zipWpContent(sourcePHP);
await targetPHP.writeFile('/tmp/check-complete.zip', zipBuffer);
const result = await targetPHP.run({
code: `<?php
$zip = new ZipArchive();
$zip->open('/tmp/check-complete.zip');
echo json_encode([
'themeFile' => $zip->getFromName('wp-content/themes/twentytwentyfive/playground-export-test.txt'),
'hasWpConfig' => $zip->locateName('wp-config.php') !== false,
]);
$zip->close();
`,
});

expect(JSON.parse(result.text)).toEqual({
themeFile: 'customized default theme',
hasWpConfig: true,
});
});

it('should replace old scope URLs with new scope URLs in post content during import', async () => {
// Create a post with an image URL containing the source scope
const sourceUrl = await sourcePHP.absoluteUrl;
Expand Down
4 changes: 1 addition & 3 deletions packages/playground/personal-wp/src/lib/hooks/use-backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,7 @@ export function useBackup() {
const siteName =
wpSiteName || activeSite.metadata.name || 'playground';

const bytes = await zipWpContent(playground, {
selfContained: true,
});
const bytes = await zipWpContent(playground);
const filename = formatBackupFilename(siteName);
const timestamp = Date.now();
saveAs(new File([bytes], filename));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -945,9 +945,7 @@ export class DirectTunnelHost {
const siteName =
(await getWordPressSiteName(this.playgroundClient)) ||
'playground';
const bytes = await zipWpContent(this.playgroundClient, {
selfContained: true,
});
const bytes = await zipWpContent(this.playgroundClient);
const totalChunks = Math.ceil(
bytes.length / DATA_CHANNEL_CHUNK_SIZE
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ export function DownloadAsZipMenuItem({ onClose, disabled }: Props) {
);
}

/** Downloads a self-contained archive of the supplied Playground. */
/** Downloads a complete archive of the supplied Playground. */
export async function downloadPlaygroundAsZip(playground: PlaygroundClient) {
const bytes = await zipWpContent(playground, {
selfContained: true,
});
const bytes = await zipWpContent(playground);
saveAs(new File([bytes], 'wordpress-playground.zip'));
}
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,13 @@ export default function GitHubExportForm({
)}
</div>
{allowZipExport ? (
/*
* Playground ZIPs are now complete snapshots, so do not offer to
* commit one alongside the GitHub file export. Retain this control
* and its export path until GitHub ZIP exports are revisited.
*/
<div
hidden
className={`${forms.formGroup} ${forms.formGroupLast}`}
>
<label>
Expand Down
Loading