Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
--exclude 'build' \
--exclude '*.zip' \
--exclude 'tests' \
--exclude 'bin' \
--exclude 'phpunit.xml' \
--exclude '.phpunit.cache'
(cd build && zip -rq datalumo.zip datalumo)
Expand Down
49 changes: 49 additions & 0 deletions bin/ensure-index-php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env php
<?php

/**
* Drop a Silence-is-golden index.php in every plugin directory so
* directory listing cannot expose files.
*/

$root = dirname(__DIR__);
$skip = ['.git', '.github', '.slimm', '.phpunit.cache'];
$contents = "<?php\n// Silence is golden.\n";

$iterator = new RecursiveIteratorIterator(
new RecursiveCallbackFilterIterator(
new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS),
static function (SplFileInfo $current) use ($skip): bool {
if (! $current->isDir()) {
return true;
}

$name = $current->getFilename();

if (in_array($name, $skip, true)) {
return false;
}

return ! str_contains($current->getPathname(), DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'bin');
},
),
RecursiveIteratorIterator::SELF_FIRST,
);

$dirs = [$root];

foreach ($iterator as $file) {
if ($file->isDir()) {
$dirs[] = $file->getPathname();
}
}

foreach ($dirs as $dir) {
$index = $dir.DIRECTORY_SEPARATOR.'index.php';

if (is_file($index)) {
continue;
}

file_put_contents($index, $contents);
}
139 changes: 139 additions & 0 deletions bin/fresh-site
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#!/usr/bin/env bash
# Spin up a clean WordPress + WooCommerce site on SQLite, with this plugin
# linked in, dummy posts/products, and admin / password.
#
# bin/fresh-site
# bin/fresh-site --force
# SITE_DIR=~/Sites/datalumo-fresh bin/fresh-site
set -euo pipefail

PLUGIN_DIR="$(cd "$(dirname "$0")/.." && pwd)"
SITE_DIR="${SITE_DIR:-$HOME/Sites/datalumo-fresh}"
SITE_SLUG="$(basename "$SITE_DIR")"
SITE_URL="${SITE_URL:-http://${SITE_SLUG}.test}"
FORCE=0

while [[ $# -gt 0 ]]; do
case "$1" in
--force) FORCE=1; shift ;;
--url) SITE_URL="$2"; shift 2 ;;
--dir) SITE_DIR="$2"; SITE_SLUG="$(basename "$SITE_DIR")"; shift 2 ;;
-h|--help)
echo "Usage: bin/fresh-site [--force] [--dir PATH] [--url URL]"
exit 0
;;
*)
echo "Unknown option: $1" >&2
exit 1
;;
esac
done

WP=(wp --path="$SITE_DIR" --allow-root)
export WP_CLI_PHP_ARGS="${WP_CLI_PHP_ARGS:--d memory_limit=512M}"

need() {
command -v "$1" >/dev/null 2>&1 || {
echo "Missing $1." >&2
exit 1
}
}

need wp
need php
need curl
need unzip

if ! php -r 'exit(extension_loaded("pdo_sqlite") ? 0 : 1);'; then
echo "PHP is missing pdo_sqlite." >&2
exit 1
fi

case "$SITE_DIR" in
/|"$HOME"|"$HOME/"|/)
echo "Refusing to use $SITE_DIR as the site path." >&2
exit 1
;;
esac

if [[ -e "$SITE_DIR" ]]; then
if [[ "$FORCE" -ne 1 ]]; then
echo "$SITE_DIR already exists. Re-run with --force to wipe it." >&2
exit 1
fi
echo "Wiping $SITE_DIR"
rm -rf "$SITE_DIR"
fi

echo "Installing WordPress in $SITE_DIR"
mkdir -p "$SITE_DIR"
wp core download --path="$SITE_DIR"

wp config create \
--path="$SITE_DIR" \
--dbname=wordpress \
--dbuser=wordpress \
--dbpass=wordpress \
--dbhost=127.0.0.1 \
--skip-check \
--extra-php <<'PHP'
define( 'DB_ENGINE', 'sqlite' );
define( 'WP_ENVIRONMENT_TYPE', 'local' );
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', false );
define( 'WP_DEBUG_LOG', true );
define( 'DATALUMO_SSL_VERIFY', false );
define( 'DATALUMO_API_URL', 'https://dl.test' );
PHP

echo "Adding SQLite drop-in"
SQLITE_ZIP="$(mktemp -t sqlite-wp).zip"
curl -fsSL "https://downloads.wordpress.org/plugin/sqlite-database-integration.latest-stable.zip" -o "$SQLITE_ZIP"
unzip -qo "$SQLITE_ZIP" -d "$SITE_DIR/wp-content/plugins"
rm -f "$SQLITE_ZIP"
cp "$SITE_DIR/wp-content/plugins/sqlite-database-integration/db.copy" "$SITE_DIR/wp-content/db.php"

"${WP[@]}" core install \
--url="$SITE_URL" \
--title="Datalumo Fresh" \
--admin_user=admin \
--admin_password=password \
--admin_email=admin@example.com \
--skip-email

"${WP[@]}" plugin activate sqlite-database-integration
"${WP[@]}" theme install twentytwentyfive --activate

echo "Linking Datalumo plugin"
if [[ ! -d "$PLUGIN_DIR/vendor" ]]; then
composer install --working-dir="$PLUGIN_DIR" --no-interaction
fi
ln -sfn "$PLUGIN_DIR" "$SITE_DIR/wp-content/plugins/datalumo"
"${WP[@]}" plugin activate datalumo

echo "Installing WooCommerce"
"${WP[@]}" plugin install woocommerce --activate
"${WP[@]}" eval 'if (class_exists("WC_Install")) { WC_Install::create_pages(); }'
"${WP[@]}" option update woocommerce_currency EUR
"${WP[@]}" option update woocommerce_default_country NL
"${WP[@]}" option update woocommerce_coming_soon no || true
"${WP[@]}" option update woocommerce_store_pages_only no || true
"${WP[@]}" option update woocommerce_onboarding_profile '{"skipped":true,"completed":true}' --format=json || true
"${WP[@]}" option update woocommerce_task_list_hidden yes || true
"${WP[@]}" option update woocommerce_task_list_complete yes || true

"${WP[@]}" rewrite structure '/%postname%/' --hard
"${WP[@]}" rewrite flush --hard

echo "Seeding posts and products"
"${WP[@]}" eval-file "$PLUGIN_DIR/bin/fresh-site-seed.php"

echo
echo "Ready."
echo " Site $SITE_URL"
echo " Admin $SITE_URL/wp-admin/"
echo " User admin"
echo " Password password"
echo " Settings $SITE_URL/wp-admin/options-general.php?page=datalumo"
echo " Datalumo https://dl.test (DATALUMO_API_URL)"
echo " Database $SITE_DIR/wp-content/database/.ht.sqlite"
185 changes: 185 additions & 0 deletions bin/fresh-site-seed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
<?php

/**
* Dummy content for bin/fresh-site. Run with: wp eval-file bin/fresh-site-seed.php
*/

if (! defined('ABSPATH')) {
fwrite(STDERR, "Run this with WP-CLI: wp eval-file bin/fresh-site-seed.php\n");
exit(1);
}

wp_delete_post(1, true);
wp_delete_post((int) get_option('page_on_front'), true);

$guide = wp_insert_post([
'post_title' => 'Visitor guide',
'post_name' => 'visitor-guide',
'post_status' => 'publish',
'post_type' => 'page',
'post_content' => <<<'HTML'
<p>This is a clean demo shop for trying the Datalumo WordPress plugin.</p>
<p>Sign in at <code>/wp-admin/</code> with <strong>admin</strong> / <strong>password</strong>, then open Settings → Datalumo and press Connect with Datalumo.</p>
HTML,
], true);

wp_insert_post([
'post_title' => 'About',
'post_name' => 'about',
'post_status' => 'publish',
'post_type' => 'page',
'post_content' => '<p>Datalumo Fresh is a disposable WordPress site. Wipe it with <code>bin/fresh-site --force</code>.</p>',
], true);

$posts = [
[
'Opening hours and pickup',
'We are open Tuesday to Saturday, 10:00 to 18:00. Weekend pickup is at the side door on Kerkstraat. Closed on Mondays.',
],
[
'Shipping and returns',
'Orders placed before 15:00 ship the same day inside the Netherlands. You have 30 days to return unused items. Start a return from your account or reply to the order email.',
],
[
'Care for wool',
'Hand wash wool in cold water and lay it flat to dry. A wool beanie can go in a mesh bag on a gentle cycle if you skip the dryer.',
],
[
'How we roast coffee',
'Beans are roasted in small batches every Thursday. Light roasts land on Friday. Dark roasts are bagged the same afternoon.',
],
[
'Gift wrapping',
'Add a note at checkout and we wrap the order in recycled paper. Gift receipts hide prices. We do not print prices on the packing slip when you ask.',
],
];

foreach ($posts as [$title, $content]) {
wp_insert_post([
'post_title' => $title,
'post_status' => 'publish',
'post_type' => 'post',
'post_content' => '<p>'.$content.'</p>',
], true);
}

if ($guide && ! is_wp_error($guide)) {
update_option('show_on_front', 'page');
update_option('page_on_front', $guide);
}

if (! class_exists('WooCommerce')) {
WP_CLI::warning('WooCommerce is not active; skipped products.');

return;
}

$simple = [
['Canvas Tote', '29.00', 'A heavy canvas tote that stands on its own. Natural colour, one size.', 'tote'],
['Ceramic Mug', '14.00', 'A 300ml stoneware mug. Dishwasher safe. Speckled cream glaze.', 'mug'],
['Wool Beanie', '22.00', 'Merino beanie, unisex. Hand wash or a gentle cycle in a mesh bag.', 'beanie'],
['Drip Coffee 250g', '11.50', 'Thursday roast. Light and chocolatey. Ground for filter on request.', 'coffee'],
];

foreach ($simple as [$name, $price, $description, $sku]) {
$product = new WC_Product_Simple();
$product->set_name($name);
$product->set_regular_price($price);
$product->set_short_description($description);
$product->set_description($description);
$product->set_sku($sku);
$product->set_manage_stock(true);
$product->set_stock_quantity(25);
$product->set_catalog_visibility('visible');
$product->set_status('publish');
$product->save();
}

foreach (['color' => 'Color', 'size' => 'Size'] as $slug => $label) {
if (wc_attribute_taxonomy_id_by_name($slug)) {
continue;
}

wc_create_attribute([
'name' => $label,
'slug' => $slug,
'type' => 'select',
'order_by' => 'menu_order',
'has_archives' => false,
]);
}

delete_transient('wc_attribute_taxonomies');
WC_Cache_Helper::invalidate_cache_group('woocommerce-attributes');
WC()->attributes = null;

if (! taxonomy_exists('pa_color') || ! taxonomy_exists('pa_size')) {
foreach (wc_get_attribute_taxonomies() as $taxonomy) {
$name = wc_attribute_taxonomy_name($taxonomy->attribute_name);
register_taxonomy($name, ['product'], []);
}
}

foreach (['Blue', 'Red'] as $color) {
wp_insert_term($color, 'pa_color');
}

foreach (['Small', 'Large'] as $size) {
wp_insert_term($size, 'pa_size');
}

$colorAttribute = new WC_Product_Attribute();
$colorAttribute->set_id(wc_attribute_taxonomy_id_by_name('color'));
$colorAttribute->set_name('pa_color');
$colorAttribute->set_options(array_values(array_filter([
get_term_by('name', 'Blue', 'pa_color')->term_id ?? null,
get_term_by('name', 'Red', 'pa_color')->term_id ?? null,
])));
$colorAttribute->set_visible(true);
$colorAttribute->set_variation(true);

$sizeAttribute = new WC_Product_Attribute();
$sizeAttribute->set_id(wc_attribute_taxonomy_id_by_name('size'));
$sizeAttribute->set_name('pa_size');
$sizeAttribute->set_options(array_values(array_filter([
get_term_by('name', 'Small', 'pa_size')->term_id ?? null,
get_term_by('name', 'Large', 'pa_size')->term_id ?? null,
])));
$sizeAttribute->set_visible(true);
$sizeAttribute->set_variation(true);

$jacket = new WC_Product_Variable();
$jacket->set_name('Trail Jacket');
$jacket->set_sku('jacket');
$jacket->set_short_description('A packable shell. Pick a colour, then a size.');
$jacket->set_description('Two-way zip, stuffs into its own pocket. Colour and size are variations so chat add-to-cart can walk those steps.');
$jacket->set_attributes([$colorAttribute, $sizeAttribute]);
$jacket->set_catalog_visibility('visible');
$jacket->set_status('publish');
$jacketId = $jacket->save();

$prices = [
'Blue' => ['Small' => '89.00', 'Large' => '89.00'],
'Red' => ['Small' => '92.00', 'Large' => '92.00'],
];

foreach ($prices as $color => $sizes) {
foreach ($sizes as $size => $price) {
$variation = new WC_Product_Variation();
$variation->set_parent_id($jacketId);
$variation->set_attributes([
'pa_color' => sanitize_title($color),
'pa_size' => sanitize_title($size),
]);
$variation->set_regular_price($price);
$variation->set_sku('jacket-'.sanitize_title($color).'-'.sanitize_title($size));
$variation->set_manage_stock(true);
$variation->set_stock_quantity(8);
$variation->set_status('publish');
$variation->save();
}
}

WC_Product_Variable::sync($jacketId);

WP_CLI::success('Seeded pages, posts, simple products, and a variable Trail Jacket.');
2 changes: 2 additions & 0 deletions bin/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?php
// Silence is golden.
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
"brain/monkey": "^2.7"
},
"scripts": {
"test": "pest"
"test": "pest",
"fresh-site": "bin/fresh-site",
"post-autoload-dump": "@php bin/ensure-index-php"
}
}
Loading
Loading