The confirmed live layout change must name the WordPress revision screen (audit L3); an editor without the Theme Builder gets forbidden from tb-assign and tb-activate whatever the ID (review M-9). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
544 lines
38 KiB
Bash
Executable File
544 lines
38 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# End-to-end checks of Etappe 3b (Divi Theme Builder, Pods fields, find-similar, make-global, P10, P13) through the
|
||
# OAuth-protected endpoint. Runs INSIDE the DDEV web container (Divi 5 must be the active theme):
|
||
# cd /home/janniclas/Projekte/WordPress && timeout 900 ddev exec bash wp-content/plugins/WP-LIT-MCP/tests/Integration/tb-smoke.sh
|
||
# REQUIRE_PODS=1 makes the Pods section mandatory (else it is skipped with SKIP when Pods is inactive).
|
||
#
|
||
# Creates only temporary data of this run (titles "LIT Smoke <run> …"): draft pages, a post, Theme Builder templates
|
||
# and layouts, library entries, its OWN temporary Pods pod (ruling Q6 – lit_person and lit_fixture_person are never
|
||
# touched), a role cloned from "editor" (no edit_theme_options, so no Theme Builder) with one user, and it changes the
|
||
# options lit_mcp_allowed_roles/lit_mcp_exposure for a while. Visible site: the only template this script ever
|
||
# enables has exactly one condition – a temporary DRAFT page, which visitors cannot open (it is rendered through a
|
||
# logged-in preview). The EXIT trap removes, by exact ID: the created templates from the live Theme Builder list,
|
||
# all created posts (with revisions, meta and term relationships), a Theme Builder draft it may have created,
|
||
# change-log rows, the pod, the user with its app passwords and sessions and the role; it restores every option it
|
||
# touched (lit_mcp_*, Divi's Theme Builder backup option, rewrite_rules; absent stays absent) and clears Divi's
|
||
# caches. The last section checks that the Theme Builder list and every template's enabled state equal the state
|
||
# before the run and that nothing of the run is left. [Fixture]/[Test] content is only read.
|
||
set -euo pipefail
|
||
cd /var/www/html
|
||
# shellcheck source=lib.sh
|
||
source wp-content/plugins/WP-LIT-MCP/tests/Integration/lib.sh
|
||
|
||
ADMIN_LOGIN=$(wp user list --role=administrator --field=user_login | head -1)
|
||
STAGE3=$PLUGIN/tests/Fixtures/divi/stage3
|
||
RUN="$(date +%s)-$$"
|
||
PREFIX="LIT Smoke $RUN"
|
||
DIGITS=$(printf '%s' "$RUN" | tr -dc '0-9')
|
||
# Per-run names: cleanup never touches an account, role or pod that existed before this run.
|
||
EDITOR_LOGIN="lit-tb-editor-$RUN"
|
||
EDITOR_ROLE="lit_tb_editor_${RUN//-/_}"
|
||
POD="lit_smk_${DIGITS: -8}"
|
||
EDITOR_CREATED=0
|
||
ROLE_CREATED=0
|
||
POD_CREATED=0
|
||
POD_POSTS=()
|
||
CREATED_POSTS=()
|
||
CREATED_TEMPLATES=()
|
||
DRAFT_TB=""
|
||
SESSION_COOKIE=""
|
||
|
||
# State before the run (restored by cleanup, compared at the end).
|
||
option_json() { wp option get "$1" --format=json 2>/dev/null || echo '__none__'; }
|
||
ORIGINAL_ROLES=$(option_json lit_mcp_allowed_roles)
|
||
ORIGINAL_EXPOSURE=$(option_json lit_mcp_exposure)
|
||
ORIGINAL_REWRITE=$(option_json rewrite_rules)
|
||
TB_ID=$(wp eval 'echo (int) et_theme_builder_get_theme_builder_post_id(true, false);')
|
||
BACKUP_OPTION="et_tb_templates_backup_$TB_ID"
|
||
ORIGINAL_BACKUP=$(option_json "$BACKUP_OPTION")
|
||
# The live Theme Builder list with every template's _et_enabled, exactly as stored.
|
||
tb_state() {
|
||
LIT_TB="$TB_ID" wp eval '
|
||
$tb = (int) getenv("LIT_TB");
|
||
$rows = get_post_meta($tb, "_et_template");
|
||
$enabled = [];
|
||
foreach ($rows as $id) { $enabled[(string) $id] = get_post_meta((int) $id, "_et_enabled", true); }
|
||
echo wp_json_encode(["list" => $rows, "enabled" => $enabled]);'
|
||
}
|
||
TB_BEFORE=$(tb_state)
|
||
# Divi's front-end feature caches (created by any page view, e.g. this script's renders): absent stays absent.
|
||
divi_caches() {
|
||
wp eval 'global $wpdb; echo implode(" ", $wpdb->get_col("SELECT option_name FROM $wpdb->options WHERE option_name LIKE \"\\_et\\_builder\\_%\\_feature\\_cache\" ORDER BY option_name"));'
|
||
}
|
||
DIVI_CACHES_BEFORE=" $(divi_caches) "
|
||
|
||
restore_option() { # $1 = option, $2 = original JSON or __none__
|
||
if [ "$2" = "__none__" ]; then
|
||
wp option delete "$1" >/dev/null 2>&1 || true
|
||
else
|
||
wp option update "$1" "$2" --format=json >/dev/null 2>&1 || true
|
||
fi
|
||
}
|
||
|
||
# Every created post (and those a stopped run could not record: title prefix of this run) – idempotent.
|
||
cleanup() {
|
||
local found id name
|
||
found=$(LIT_PREFIX="$PREFIX " wp eval '
|
||
global $wpdb;
|
||
echo implode(" ", $wpdb->get_col($wpdb->prepare(
|
||
"SELECT ID FROM $wpdb->posts WHERE post_title LIKE %s AND post_type IN (%s, %s, %s, %s, %s, %s, %s)",
|
||
$wpdb->esc_like((string) getenv("LIT_PREFIX")) . "%",
|
||
"page", "post", "et_pb_layout", "et_template", "et_header_layout", "et_body_layout", "et_footer_layout"
|
||
)));' 2>/dev/null || true)
|
||
for id in $found; do CREATED_POSTS+=("$id"); done
|
||
# 1. Off the live list first (nothing of this run may stay visible or listed), then disabled.
|
||
for id in "${CREATED_TEMPLATES[@]}"; do
|
||
LIT_ID="$id" LIT_TB="$TB_ID" wp eval '
|
||
$id = (string) getenv("LIT_ID");
|
||
delete_post_meta((int) getenv("LIT_TB"), "_et_template", $id);
|
||
update_post_meta((int) $id, "_et_enabled", "0");' >/dev/null 2>&1 || true
|
||
done
|
||
if [ -n "$DRAFT_TB" ]; then wp post delete "$DRAFT_TB" --force >/dev/null 2>&1 || true; DRAFT_TB=""; fi
|
||
# 2. Change-log rows: every row of the temporary user and every row about a post of this run.
|
||
LIT_LOGIN="$EDITOR_LOGIN" LIT_IDS="${CREATED_POSTS[*]} ${CREATED_TEMPLATES[*]}" wp eval '
|
||
global $wpdb;
|
||
$table = $wpdb->prefix . "lit_mcp_log";
|
||
$u = get_user_by("login", getenv("LIT_LOGIN"));
|
||
if ($u) { $wpdb->delete($table, ["user_id" => $u->ID]); }
|
||
$types = ["page", "post", "et_pb_layout", "et_template", "et_header_layout", "et_body_layout", "et_footer_layout"];
|
||
foreach (array_unique(array_filter(explode(" ", (string) getenv("LIT_IDS")))) as $id) {
|
||
foreach ($types as $type) { $wpdb->delete($table, ["object_type" => $type, "object_id" => $id]); }
|
||
}' >/dev/null 2>&1 || true
|
||
# 3. Posts with revisions and meta; term relationships also for types a taxonomy is not registered for.
|
||
LIT_IDS="${CREATED_POSTS[*]} ${CREATED_TEMPLATES[*]}" wp eval '
|
||
global $wpdb;
|
||
foreach (array_unique(array_filter(array_map("intval", explode(" ", (string) getenv("LIT_IDS"))))) as $id) {
|
||
if (get_post($id)) { wp_delete_post($id, true); }
|
||
$wpdb->delete($wpdb->term_relationships, ["object_id" => $id]);
|
||
}' >/dev/null 2>&1 || true
|
||
CREATED_POSTS=()
|
||
CREATED_TEMPLATES=()
|
||
# 4. The temporary pod (definition, fields, content) and anything of it left by ID.
|
||
if [ "$POD_CREATED" = 1 ]; then
|
||
LIT_POD="$POD" wp eval '
|
||
if (function_exists("pods_api") && pods_api()->load_pod(["name" => getenv("LIT_POD")])) {
|
||
pods_api()->delete_pod(["name" => getenv("LIT_POD")], false, true);
|
||
}' >/dev/null 2>&1 || true
|
||
LIT_POD="$POD" LIT_IDS="${POD_POSTS[*]}" wp eval '
|
||
global $wpdb;
|
||
$ids = array_merge(array_map("intval", array_filter(explode(" ", (string) getenv("LIT_IDS")))),
|
||
array_map("intval", $wpdb->get_col($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_type = %s", getenv("LIT_POD")))));
|
||
foreach (array_unique($ids) as $id) { if ($id > 0 && get_post($id)) { wp_delete_post($id, true); } }' >/dev/null 2>&1 || true
|
||
POD_CREATED=0
|
||
fi
|
||
# 5. Accounts, options, caches.
|
||
cleanup_app_passwords_for "$ADMIN_LOGIN"
|
||
if [ "$EDITOR_CREATED" = 1 ]; then
|
||
cleanup_app_passwords_for "$EDITOR_LOGIN"
|
||
wp user delete "$EDITOR_LOGIN" --yes >/dev/null 2>&1 || true
|
||
EDITOR_CREATED=0
|
||
fi
|
||
if [ "$ROLE_CREATED" = 1 ]; then
|
||
wp role delete "$EDITOR_ROLE" >/dev/null 2>&1 || true
|
||
ROLE_CREATED=0
|
||
fi
|
||
restore_option lit_mcp_allowed_roles "$ORIGINAL_ROLES"
|
||
restore_option lit_mcp_exposure "$ORIGINAL_EXPOSURE"
|
||
restore_option "$BACKUP_OPTION" "$ORIGINAL_BACKUP"
|
||
restore_option rewrite_rules "$ORIGINAL_REWRITE"
|
||
for name in $(divi_caches 2>/dev/null || true); do
|
||
if [[ "$DIVI_CACHES_BEFORE" != *" $name "* ]]; then wp option delete "$name" >/dev/null 2>&1 || true; fi
|
||
done
|
||
wp eval 'if (function_exists("et_theme_builder_clear_wp_cache")) { et_theme_builder_clear_wp_cache("all"); }
|
||
if (class_exists("ET_Core_PageResource")) { ET_Core_PageResource::remove_static_resources("all", "all", true); }' \
|
||
>/dev/null 2>&1 || true
|
||
}
|
||
trap 'rc=$?; cleanup; guard_exit "$rc" || exit $?' EXIT
|
||
|
||
has() { grep -qF -- "$2" <<<"$1"; } # $1 = haystack, $2 = needle (no pipe: pipefail + grep -q would race)
|
||
|
||
create_post() { # $1 = post type, $2 = title suffix, $3 = status, $4 = markup (optional) -> ID
|
||
LIT_TYPE="$1" LIT_TITLE="$PREFIX $2" LIT_STATUS="$3" LIT_CONTENT="${4:-}" wp --user="$ADMIN_LOGIN" eval '
|
||
$id = wp_insert_post(["post_type" => getenv("LIT_TYPE"), "post_status" => getenv("LIT_STATUS"),
|
||
"post_title" => getenv("LIT_TITLE"), "post_content" => wp_slash((string) getenv("LIT_CONTENT"))]);
|
||
update_post_meta($id, "_et_pb_use_builder", "on");
|
||
update_post_meta($id, "_et_pb_use_divi_5", "on");
|
||
echo $id;'
|
||
}
|
||
|
||
content_of() { wp eval "echo (string) get_post($1)->post_content;"; }
|
||
content_hash() { wp eval "echo hash('sha256', (string) get_post($1)->post_content);"; }
|
||
rev_of() { tool lit-divi-outline "{\"post_id\":$1}" | jq -r '.tree_rev'; }
|
||
enabled_of() { wp post meta get "$1" _et_enabled; }
|
||
listed() { tb_state | jq -e --arg t "$1" '.list | index($t) != null' >/dev/null; } # on the live Theme Builder list
|
||
tb_list() { # $1 = arguments JSON (default {})
|
||
local args='{}'
|
||
if [ $# -gt 0 ]; then args=$1; fi
|
||
ability lit/tb-list "$args"
|
||
}
|
||
template_rev() { tb_list "{\"template_id\":$1}" | jq -r '.templates[0].template_rev // .detached[0].template_rev'; }
|
||
activate() { # $1 = template ID, $2 = enabled (true|false), $3 = confirm (true|false)
|
||
ability lit/tb-activate "$(jq -cn --argjson id "$1" --arg rev "$(template_rev "$1")" --argjson e "$2" --argjson c "$3" \
|
||
'{template_id: $id, template_rev: $rev, enabled: $e} + (if $c then {confirm: true} else {} end)')"
|
||
}
|
||
details() { echo "$1" | jq -r '.__error' | jq -c '.'; } # the details of a tool error (confirm_required etc.)
|
||
# Front-end HTML: $1 = post ID; $2 = "preview" renders it for the logged-in temporary editor (drafts), else anonymous.
|
||
render_of() {
|
||
local cookie=()
|
||
if [ "${2:-}" = preview ]; then cookie=(-H "Cookie: $SESSION_COOKIE"); fi
|
||
curl -s --max-time 30 -H "Host: $HOST" -H 'X-Forwarded-Proto: https' "${cookie[@]}" "http://localhost/?page_id=$1"
|
||
}
|
||
render_home() { curl -s --max-time 30 -H "Host: $HOST" -H 'X-Forwarded-Proto: https' "http://localhost/"; }
|
||
|
||
[ "$(wp eval 'echo get_template();')" = "Divi" ] || fail "Divi must be the active theme"
|
||
[ "$TB_ID" -gt 0 ] || fail "setup: no live Theme Builder post (save the Theme Builder once)"
|
||
[ "$(wp post list --post_type=et_theme_builder --post_status=auto-draft --format=count)" = "0" ] \
|
||
|| fail "setup: the Theme Builder has unsaved changes – save or discard them first"
|
||
echo "$TB_BEFORE" | jq -e '[.enabled[]] | all(. != "1")' >/dev/null \
|
||
|| fail "setup: a Theme Builder template is enabled – this local site must show no active template before the run"
|
||
if wp user get "$EDITOR_LOGIN" >/dev/null 2>&1 || wp role exists "$EDITOR_ROLE" >/dev/null 2>&1; then
|
||
fail "user $EDITOR_LOGIN or role $EDITOR_ROLE exists already"
|
||
fi
|
||
|
||
# Temporary editor (per-run role cloned from editor: no edit_theme_options, Divi's "Theme Builder" option off).
|
||
wp role create "$EDITOR_ROLE" "LIT Smoke Theme Builder" --clone=editor >/dev/null
|
||
ROLE_CREATED=1
|
||
wp option update lit_mcp_allowed_roles "[\"administrator\",\"$EDITOR_ROLE\"]" --format=json >/dev/null
|
||
wp user create "$EDITOR_LOGIN" "$EDITOR_LOGIN@example.invalid" --role="$EDITOR_ROLE" >/dev/null
|
||
EDITOR_CREATED=1
|
||
[ "$(LIT_LOGIN="$EDITOR_LOGIN" wp eval 'echo user_can(get_user_by("login", getenv("LIT_LOGIN")), "edit_theme_options") ? "yes" : "no";')" = "no" ] \
|
||
|| fail "setup: the temporary editor must lack edit_theme_options"
|
||
# Its logged-in cookie previews the temporary draft page (session stored on the temporary user only).
|
||
SESSION_COOKIE=$(LIT_LOGIN="$EDITOR_LOGIN" wp eval '
|
||
$u = get_user_by("login", getenv("LIT_LOGIN"));
|
||
$expires = time() + 1800;
|
||
$token = WP_Session_Tokens::get_instance($u->ID)->create($expires);
|
||
echo LOGGED_IN_COOKIE . "=" . wp_generate_auth_cookie($u->ID, $expires, "logged_in", $token);')
|
||
|
||
connect "$ADMIN_LOGIN"
|
||
|
||
# 1. Exposure: still 22 direct lit tools; the six new abilities are indirect and reachable; no TB compat problem
|
||
LIST=$(rpc tools/list '{}')
|
||
DIRECT='["lit-content-create","lit-content-get","lit-content-query","lit-content-types","lit-content-update","lit-divi-catalog",
|
||
"lit-divi-delete","lit-divi-design-system","lit-divi-insert","lit-divi-move","lit-divi-node-get","lit-divi-outline","lit-divi-update",
|
||
"lit-guide","lit-library-insert","lit-media-download","lit-media-preview","lit-media-search","lit-media-upload-from-url",
|
||
"lit-page-render-summary","lit-post-publish","lit-site-status"]'
|
||
echo "$LIST" | jq -e --argjson want "$DIRECT" '[.result.tools[].name | select(startswith("lit-"))] | sort == ($want | sort)' >/dev/null \
|
||
|| fail "direct lit tools: $(echo "$LIST" | jq -c '[.result.tools[].name]')"
|
||
for a in lit/tb-list lit/tb-create-template lit/tb-assign lit/tb-activate lit/library-find-similar lit/library-make-global; do
|
||
if echo "$LIST" | jq -e --arg t "lit-${a#lit/}" '.result.tools[] | select(.name == $t)' >/dev/null; then fail "$a must be indirect"; fi
|
||
tool mcp-adapter-get-ability-info "$(jq -cn --arg a "$a" '{ability_name: $a}')" | jq -e 'has("__error") | not' >/dev/null \
|
||
|| fail "$a is not reachable"
|
||
done
|
||
STATUS=$(tool lit-site-status '{}')
|
||
echo "$STATUS" | jq -e '.components[] | select(.id == "divi") | .active == true and .problems == []' >/dev/null \
|
||
|| fail "Divi compatibility findings: $STATUS"
|
||
pass "exposure: 22 direct tools, the six new abilities indirect and reachable; no Divi/Theme Builder compatibility problem"
|
||
|
||
# 2. Create a template: disabled, on the live list, body layout by Divi with the plugin's content, invisible
|
||
PAGE=$(create_post page "Entwurf" draft '')
|
||
CREATED_POSTS+=("$PAGE")
|
||
[ "$(wp post get "$PAGE" --field=post_status)" = "draft" ] || fail "setup: the target page must be a draft"
|
||
MARKER="LIT-Smoke-Marker-$RUN"
|
||
BASE_PREVIEW=$(render_of "$PAGE" preview)
|
||
has "$BASE_PREVIEW" "$PREFIX Entwurf" || fail "setup: the preview does not show the draft page: $(head -c 300 <<<"$BASE_PREVIEW")"
|
||
CREATED=$(ability lit/tb-create-template "$(jq -cn --arg t "$PREFIX Vorlage" --arg m "$MARKER" --argjson p "$PAGE" '{
|
||
title: $t,
|
||
header: {source: "global"},
|
||
body: {source: "new", nodes: [{type: "section", modules: [{type: "heading", text: "{{post:title}}"}, {type: "text", content: $m}]}]},
|
||
footer: {source: "hidden"},
|
||
use_on: ["singular:post_type:page:id:\($p)"],
|
||
exclude_from: ["search"]}')")
|
||
TPL=$(echo "$CREATED" | jq -r '.template_id // empty')
|
||
[ -n "$TPL" ] || fail "tb-create-template: $CREATED"
|
||
CREATED_TEMPLATES+=("$TPL")
|
||
BODY=$(echo "$CREATED" | jq -r '.layouts.body.id')
|
||
CREATED_POSTS+=("$BODY")
|
||
echo "$CREATED" | jq -e '.enabled == false and .layouts.footer.enabled == false and .layouts.header.source == "global"
|
||
and .layouts.body.source == "new"' >/dev/null || fail "tb-create-template result: $CREATED"
|
||
[ "$(enabled_of "$TPL")" = "0" ] || fail "the new template is enabled"
|
||
[ "$(wp post meta get "$TPL" _et_use_on)" = "singular:post_type:page:id:$PAGE" ] || fail "conditions: $(wp post meta get "$TPL" _et_use_on)"
|
||
listed "$TPL" || fail "template $TPL is not on the live Theme Builder list"
|
||
[ "$(wp post get "$BODY" --field=post_type)" = "et_body_layout" ] || fail "body layout type"
|
||
[ "$(wp post meta get "$BODY" _et_pb_use_divi_5)" = "on" ] || fail "body layout without the Divi 5 flag"
|
||
BODY_CONTENT=$(content_of "$BODY")
|
||
[[ "$BODY_CONTENT" == "<!-- wp:divi/placeholder -->"* ]] || fail "body layout content: $BODY_CONTENT"
|
||
has "$BODY_CONTENT" "$MARKER" || fail "body layout lacks the marker"
|
||
tb_list "{\"template_id\":$TPL}" | jq -e '.templates[0] | .enabled == false and .live == false and .attached == true' >/dev/null \
|
||
|| fail "tb-list does not show the new template disabled and attached"
|
||
if has "$(render_of "$PAGE" preview)" "$MARKER"; then fail "a disabled template is rendered"; fi
|
||
pass "tb-create-template: disabled, on the live list, body layout by Divi with the plugin's content, not rendered"
|
||
|
||
# 3. Divi tools on the body of a disabled template save at once; post-content is allowed in the body
|
||
UPD=$(tool lit-divi-insert "$(jq -cn --argjson id "$BODY" --arg rev "$(rev_of "$BODY")" \
|
||
'{post_id: $id, tree_rev: $rev, parent: "s0.r0.c0", nodes: [{type: "post-content"}]}')")
|
||
echo "$UPD" | jq -e '.ok == true and .templates == []' >/dev/null || fail "insert into the body of a disabled template: $UPD"
|
||
has "$(content_of "$BODY")" "wp:divi/post-content" || fail "post-content not stored in the body"
|
||
pass "Divi tools write the body of a disabled template without confirm; post-content allowed in the body"
|
||
|
||
# 4. tb-assign while disabled (Q3): an omitted exclude_from stays, conditions still only the draft page
|
||
ASSIGN=$(ability lit/tb-assign "$(jq -cn --argjson id "$TPL" --arg rev "$(template_rev "$TPL")" --argjson p "$PAGE" \
|
||
'{template_id: $id, template_rev: $rev, use_on: ["singular:post_type:page:id:\($p)"]}')")
|
||
echo "$ASSIGN" | jq -e '.ok == true and .live == false and .previous.exclude_from == ["search"]' >/dev/null || fail "tb-assign: $ASSIGN"
|
||
[ "$(wp post meta get "$TPL" _et_exclude_from)" = "search" ] || fail "an omitted exclude_from changed the exceptions"
|
||
[ "$(wp post meta get "$TPL" _et_use_on)" = "singular:post_type:page:id:$PAGE" ] || fail "use_on after tb-assign"
|
||
[ "$(error_code "$(ability lit/tb-assign "$(jq -cn --argjson id "$TPL" --argjson p "$PAGE" \
|
||
'{template_id: $id, template_rev: "abcdef012345", use_on: ["singular:post_type:page:id:\($p)"]}')")")" = "rev_mismatch" ] \
|
||
|| fail "tb-assign with a stale template_rev"
|
||
pass "tb-assign on a disabled template: saved at once, omitted exclude_from kept (Q3), stale template_rev refused"
|
||
|
||
# 5. Activation: dry run, then confirm; the draft page shows the template, visitors see nothing new
|
||
DRY=$(activate "$TPL" true false)
|
||
[ "$(error_code "$DRY")" = "confirm_required" ] || fail "activation without confirm: $DRY"
|
||
details "$DRY" | jq -e --arg p "$PAGE" '.use_on | length == 1 and (.[0] | contains($p))' >/dev/null || fail "activation dry run: $DRY"
|
||
[ "$(enabled_of "$TPL")" = "0" ] || fail "the dry run enabled the template"
|
||
ON=$(activate "$TPL" true true)
|
||
echo "$ON" | jq -e '.enabled == true and .live == true' >/dev/null || fail "activation: $ON"
|
||
[ "$(enabled_of "$TPL")" = "1" ] || fail "template not enabled"
|
||
PREVIEW=$(render_of "$PAGE" preview)
|
||
has "$PREVIEW" "$MARKER" || fail "the active template is not rendered for its draft page"
|
||
if has "$(render_of "$PAGE")" "$MARKER"; then fail "a visitor sees the draft page's template"; fi
|
||
if has "$(render_home)" "$MARKER"; then fail "the template shows on the home page"; fi
|
||
pass "tb-activate: dry run first, then enabled; only the draft page (preview) shows it, visitors see nothing"
|
||
|
||
# 6. Live template: layout changes and condition changes are a dry run first (Q9), Q3 on the live template
|
||
HASH=$(content_hash "$BODY")
|
||
CHANGE=$(jq -cn --arg m "$MARKER-geändert" '[{id: "s0.r0.c0.m1", set: {content: $m}}]')
|
||
LIVE=$(tool lit-divi-update "$(jq -cn --argjson id "$BODY" --arg rev "$(rev_of "$BODY")" --argjson c "$CHANGE" '{post_id: $id, tree_rev: $rev, changes: $c}')")
|
||
[ "$(error_code "$LIVE")" = "confirm_required" ] || fail "visible layout changed without confirm: $LIVE"
|
||
details "$LIVE" | jq -e --argjson t "$TPL" '.templates | map(.id) | index($t) != null' >/dev/null || fail "dry run lacks the template: $LIVE"
|
||
[ "$(content_hash "$BODY")" = "$HASH" ] || fail "the dry run wrote the layout"
|
||
DEL=$(tool lit-divi-delete "$(jq -cn --argjson id "$BODY" --arg rev "$(rev_of "$BODY")" '{post_id: $id, tree_rev: $rev, ids: ["s0.r0.c0.m0"]}')")
|
||
[ "$(error_code "$DEL")" = "confirm_required" ] || fail "visible layout: delete without confirm: $DEL"
|
||
[ "$(content_hash "$BODY")" = "$HASH" ] || fail "the delete dry run wrote the layout"
|
||
DONE=$(tool lit-divi-update "$(jq -cn --argjson id "$BODY" --arg rev "$(rev_of "$BODY")" --argjson c "$CHANGE" \
|
||
'{post_id: $id, tree_rev: $rev, confirm: true, changes: $c}')")
|
||
echo "$DONE" | jq -e --argjson t "$TPL" '.ok == true and (.templates | map(.id) | index($t) != null)
|
||
and (.restore_url | test("/wp-admin/revision\\.php\\?revision=[0-9]+$"))' >/dev/null \
|
||
|| fail "confirmed layout change (templates, restore_url): $DONE"
|
||
has "$(render_of "$PAGE" preview)" "$MARKER-geändert" || fail "the changed layout is not rendered"
|
||
REASSIGN=$(jq -cn --argjson id "$TPL" --arg rev "$(template_rev "$TPL")" --argjson p "$PAGE" \
|
||
'{template_id: $id, template_rev: $rev, use_on: ["singular:post_type:page:id:\($p)"], exclude_from: []}')
|
||
ADRY=$(ability lit/tb-assign "$REASSIGN")
|
||
[ "$(error_code "$ADRY")" = "confirm_required" ] || fail "conditions of an active template changed without confirm: $ADRY"
|
||
details "$ADRY" | jq -e '(.before.exclude_from | length) == 1 and .after.exclude_from == [] and (.after.use_on | length) == 1' >/dev/null \
|
||
|| fail "tb-assign dry run before/after: $ADRY"
|
||
[ "$(wp post meta get "$TPL" _et_exclude_from)" = "search" ] || fail "the dry run changed the exceptions"
|
||
ACONF=$(ability lit/tb-assign "$(echo "$REASSIGN" | jq -c '. + {confirm: true}')")
|
||
echo "$ACONF" | jq -e '.ok == true and .live == true and .previous.exclude_from == ["search"]' >/dev/null || fail "confirmed tb-assign: $ACONF"
|
||
[ -z "$(wp post meta get "$TPL" _et_exclude_from 2>/dev/null || true)" ] || fail "exclude_from [] did not clear the exceptions"
|
||
[ "$(wp post meta get "$TPL" _et_use_on)" = "singular:post_type:page:id:$PAGE" ] || fail "use_on of the live template changed"
|
||
pass "live template: layout update/delete and conditions only after a dry run; confirmed change rendered; [] clears exceptions"
|
||
|
||
# 7. Theme Builder draft: blocks create/assign/enable, the off-switch still works (Q20); default template refused
|
||
DEFAULT=$(tb_list | jq -r '[.templates[] | select(.default)][0].id // empty')
|
||
if [ -n "$DEFAULT" ]; then
|
||
DEF_ENABLED=$(tb_list "{\"template_id\":$DEFAULT}" | jq -r '.templates[0].enabled')
|
||
[ "$(error_code "$(activate "$DEFAULT" "$([ "$DEF_ENABLED" = true ] && echo false || echo true)" false)")" = "unsupported" ] \
|
||
|| fail "the default template is not refused"
|
||
fi
|
||
TPL_REV=$(template_rev "$TPL")
|
||
DRAFT_TB=$(wp eval 'echo (int) wp_insert_post(["post_type" => "et_theme_builder", "post_status" => "auto-draft", "post_title" => "Theme Builder"]);')
|
||
[ "$DRAFT_TB" -gt 0 ] || fail "setup: Theme Builder draft"
|
||
[ "$(error_code "$(ability lit/tb-create-template "$(jq -cn --arg t "$PREFIX Gesperrt" '{title: $t}')")")" = "unsupported" ] \
|
||
|| fail "tb-create-template despite a Theme Builder draft"
|
||
[ "$(error_code "$(ability lit/tb-assign "$(jq -cn --argjson id "$TPL" --arg rev "$TPL_REV" --argjson p "$PAGE" \
|
||
'{template_id: $id, template_rev: $rev, use_on: ["singular:post_type:page:id:\($p)"], confirm: true}')")")" = "unsupported" ] \
|
||
|| fail "tb-assign despite a Theme Builder draft"
|
||
OFF_DRY=$(ability lit/tb-activate "$(jq -cn --argjson id "$TPL" --arg rev "$TPL_REV" '{template_id: $id, template_rev: $rev, enabled: false}')")
|
||
[ "$(error_code "$OFF_DRY")" = "confirm_required" ] || fail "deactivation without confirm: $OFF_DRY"
|
||
[ "$(enabled_of "$TPL")" = "1" ] || fail "the deactivation dry run switched"
|
||
OFF=$(ability lit/tb-activate "$(jq -cn --argjson id "$TPL" --arg rev "$TPL_REV" '{template_id: $id, template_rev: $rev, enabled: false, confirm: true}')")
|
||
echo "$OFF" | jq -e '.enabled == false and .live == false and (.warnings | map(contains("Entwurf")) | any)' >/dev/null \
|
||
|| fail "off-switch with a Theme Builder draft: $OFF"
|
||
[ "$(enabled_of "$TPL")" = "0" ] || fail "template still enabled"
|
||
[ "$(error_code "$(activate "$TPL" true true)")" = "unsupported" ] || fail "tb-activate (on) despite a Theme Builder draft"
|
||
[ "$(enabled_of "$TPL")" = "0" ] || fail "enabled despite a Theme Builder draft"
|
||
wp post delete "$DRAFT_TB" --force >/dev/null
|
||
DRAFT_TB=""
|
||
if has "$(render_of "$PAGE" preview)" "$MARKER"; then fail "a deactivated template is still rendered"; fi
|
||
pass "Theme Builder draft blocks create, assign and enable; the off-switch works with a warning; default template refused"
|
||
|
||
# 8. Detached template (a stale Theme Builder save removed it): listed, refused, reattached disabled on request
|
||
LIT_TB="$TB_ID" LIT_ID="$TPL" wp eval '
|
||
delete_post_meta((int) getenv("LIT_TB"), "_et_template", (string) getenv("LIT_ID"));
|
||
update_post_meta((int) getenv("LIT_ID"), "_et_theme_builder_marked_as_unused", "2026-09-27 00:00:00");' >/dev/null
|
||
if listed "$TPL"; then fail "setup: template still listed"; fi
|
||
tb_list | jq -e --argjson t "$TPL" '(.detached | map(.id) | index($t) != null) and (.templates | map(.id) | index($t) == null)' >/dev/null \
|
||
|| fail "detached template not listed under detached"
|
||
[ "$(error_code "$(activate "$TPL" true true)")" = "validation_failed" ] || fail "a detached template was activated"
|
||
[ "$(error_code "$(ability lit/tb-assign "$(jq -cn --argjson id "$TPL" --arg rev "$(template_rev "$TPL")" --argjson p "$PAGE" \
|
||
'{template_id: $id, template_rev: $rev, use_on: ["singular:post_type:page:id:\($p)"]}')")")" = "validation_failed" ] \
|
||
|| fail "tb-assign on a detached template without reattach"
|
||
RE=$(ability lit/tb-assign "$(jq -cn --argjson id "$TPL" --arg rev "$(template_rev "$TPL")" --argjson p "$PAGE" \
|
||
'{template_id: $id, template_rev: $rev, use_on: ["singular:post_type:page:id:\($p)"], reattach: true}')")
|
||
echo "$RE" | jq -e '.ok == true and .live == false' >/dev/null || fail "reattach: $RE"
|
||
listed "$TPL" || fail "not reattached"
|
||
[ "$(enabled_of "$TPL")" = "0" ] || fail "reattached enabled"
|
||
[ -z "$(wp post meta get "$TPL" _et_theme_builder_marked_as_unused 2>/dev/null || true)" ] || fail "unused mark kept"
|
||
pass "stale-editor detach: listed as detached, activate/assign refused, reattach puts it back disabled and unmarked"
|
||
|
||
# 9. Rights: an editor without the Theme Builder; group "nur lesen" and "deaktiviert" gate the Divi tools
|
||
connect "$EDITOR_LOGIN"
|
||
[ "$(error_code "$(ability lit/tb-list '{}')")" = "forbidden" ] || fail "editor lists the Theme Builder"
|
||
[ "$(error_code "$(ability lit/tb-create-template "$(jq -cn --arg t "$PREFIX Redakteur" '{title: $t}')")")" = "forbidden" ] \
|
||
|| fail "editor creates a template"
|
||
# Review M-9: without the Theme Builder rights the refusal is forbidden whatever the ID (like tb-list).
|
||
[ "$(error_code "$(ability lit/tb-activate "$(jq -cn --argjson id "$TPL" '{template_id: $id, template_rev: "abcdef012345", enabled: true, confirm: true}')")")" = "forbidden" ] \
|
||
|| fail "editor reaches the template"
|
||
[ "$(error_code "$(ability lit/tb-assign "$(jq -cn --argjson id "$TPL" --argjson p "$PAGE" \
|
||
'{template_id: $id, template_rev: "abcdef012345", use_on: ["singular:post_type:page:id:\($p)"]}')")")" = "forbidden" ] \
|
||
|| fail "editor reaches the template's conditions"
|
||
[ "$(error_code "$(tool lit-divi-outline "{\"post_id\":$BODY}")")" = "not_found" ] || fail "editor reads a Theme Builder layout"
|
||
[ "$(enabled_of "$TPL")" = "0" ] || fail "editor refusal changed the template"
|
||
connect "$ADMIN_LOGIN"
|
||
wp option update lit_mcp_exposure '{"@readonly:divi-theme-builder":true}' --format=json >/dev/null
|
||
connect "$ADMIN_LOGIN"
|
||
[ "$(error_code "$(tool lit-divi-update "$(jq -cn --argjson id "$BODY" --arg rev "$(rev_of "$BODY")" \
|
||
'{post_id: $id, tree_rev: $rev, changes: [{id: "s0.r0.c0.m1", label: "Nur lesen"}]}')")")" = "forbidden" ] \
|
||
|| fail "group read-only: Theme Builder layout written"
|
||
[ -n "$(template_rev "$TPL")" ] || fail "group read-only: tb-list must stay readable"
|
||
RO=$(activate "$TPL" true true)
|
||
has "$RO" "Ability 'lit/tb-activate' not found" || fail "group read-only: tb-activate still executable: $RO"
|
||
[ "$(enabled_of "$TPL")" = "0" ] || fail "group read-only: template enabled"
|
||
wp option update lit_mcp_exposure '{"@group:divi-theme-builder":"disabled"}' --format=json >/dev/null
|
||
connect "$ADMIN_LOGIN"
|
||
[ "$(error_code "$(tool lit-divi-outline "{\"post_id\":$BODY}")")" = "not_found" ] || fail "group disabled: Theme Builder layout readable"
|
||
GONE=$(ability lit/tb-list '{}')
|
||
echo "$GONE" | jq -e 'has("__error")' >/dev/null && has "$GONE" 'not found' || fail "group disabled: tb-list still executable: $GONE"
|
||
restore_option lit_mcp_exposure "$ORIGINAL_EXPOSURE"
|
||
connect "$ADMIN_LOGIN"
|
||
pass "editor without the Theme Builder: forbidden/not_found everywhere; group read-only and disabled gate the Divi tools"
|
||
|
||
# 10. Pods fields in a Theme Builder body – with a temporary pod of this run (ruling Q6)
|
||
if wp plugin is-active pods >/dev/null 2>&1; then
|
||
if [ "$(LIT_POD="$POD" wp eval 'echo pods_api()->load_pod(["name" => getenv("LIT_POD")]) ? "yes" : "no";')" != "no" ] \
|
||
|| [ "$POD" = lit_person ] || [ "$POD" = lit_fixture_person ]; then
|
||
fail "pod $POD exists already"
|
||
fi
|
||
POD_CREATED=1
|
||
POD_IDS=$(LIT_POD="$POD" LIT_LABEL="$PREFIX Personen" wp eval '
|
||
$api = pods_api();
|
||
$pod = getenv("LIT_POD");
|
||
$ids = [(int) $api->save_pod(["name" => $pod, "label" => getenv("LIT_LABEL"), "label_singular" => getenv("LIT_LABEL"),
|
||
"type" => "post_type", "storage" => "meta", "public" => 1, "show_ui" => 1])];
|
||
foreach ([
|
||
["name" => "role", "label" => "Funktion", "type" => "text"],
|
||
["name" => "level", "label" => "Stufe", "type" => "pick", "pick_object" => "custom-simple", "pick_custom" => "junior|Junior\nsenior|Senior"],
|
||
["name" => "mentor", "label" => "Mentor", "type" => "pick", "pick_object" => "post_type", "pick_val" => $pod, "pick_format_type" => "single"],
|
||
] as $field) {
|
||
$ids[] = (int) $api->save_field(array_merge(["pod" => $pod], $field));
|
||
}
|
||
echo implode(" ", $ids);')
|
||
for id in $POD_IDS; do POD_POSTS+=("$id"); done
|
||
[ "$(echo "$POD_IDS" | wc -w)" = 4 ] || fail "setup: temporary pod $POD: $POD_IDS"
|
||
CONDS=$(tb_list '{"conditions":true}')
|
||
echo "$CONDS" | jq -e --arg c "singular:post_type:$POD:all" '.conditions | map(.id) | index($c) != null' >/dev/null \
|
||
|| fail "Divi offers no condition for the temporary pod"
|
||
PODS=$(ability lit/tb-create-template "$(jq -cn --arg t "$PREFIX Person-Vorlage" --arg pod "$POD" '{
|
||
title: $t, header: {source: "default"}, footer: {source: "default"},
|
||
body: {source: "new", nodes: [{type: "section", modules: [
|
||
{type: "heading", text: "{{post:title}}"}, {type: "text", content: "{{field:role}}"},
|
||
{type: "text", content: "{{field:level}}"}, {type: "text", content: "{{field:mentor}}"}]}]},
|
||
use_on: ["singular:post_type:\($pod):all"]}')")
|
||
PTPL=$(echo "$PODS" | jq -r '.template_id // empty')
|
||
[ -n "$PTPL" ] || fail "Pods template: $PODS"
|
||
CREATED_TEMPLATES+=("$PTPL")
|
||
CREATED_POSTS+=("$(echo "$PODS" | jq -r '.layouts.body.id')")
|
||
[ "$(enabled_of "$PTPL")" = "0" ] || fail "the Pods template is enabled"
|
||
echo "$PODS" | jq -e '[.warnings[] | select(contains("„mentor“"))] | length == 1' >/dev/null || fail "no warning for mentor: $PODS"
|
||
echo "$PODS" | jq -e '[.warnings[] | select(contains("„level“"))] | length == 1' >/dev/null || fail "no note for level: $PODS"
|
||
echo "$PODS" | jq -e '[.warnings[] | select(contains("„role“"))] | length == 0' >/dev/null || fail "role wrongly warned: $PODS"
|
||
PBODY=$(echo "$PODS" | jq -r '.layouts.body.id')
|
||
PUPD=$(tool lit-divi-insert "$(jq -cn --argjson id "$PBODY" --arg rev "$(rev_of "$PBODY")" \
|
||
'{post_id: $id, tree_rev: $rev, parent: "s0.r0.c0", nodes: [{type: "text", content: "{{field:mentor}}"}]}')")
|
||
echo "$PUPD" | jq -e '.ok == true and ([.warnings[] | select(contains("„mentor“"))] | length >= 1)' >/dev/null \
|
||
|| fail "Divi tool on the Pods body without the mentor warning: $PUPD"
|
||
pass "Pods (temporary pod $POD): condition offered; relation field warned, choice field noted, text field clean; template disabled"
|
||
elif [ "${REQUIRE_PODS:-0}" = 1 ]; then
|
||
fail "Pods is not active (REQUIRE_PODS=1)"
|
||
else
|
||
echo "SKIP - Pods section (Pods inactive)"
|
||
fi
|
||
|
||
# 11. find-similar and make-global (mode 1, mode 2: dry run, confirm, identical only)
|
||
SECTION=$(tr -d '\r' < "$STAGE3/library--library-abschnitt.html")
|
||
VARIANT=${SECTION/Library-Blurb\"/Library-Blurb anders\"}
|
||
[ "$VARIANT" != "$SECTION" ] || fail "setup: variant equals the section"
|
||
A=$(create_post page "Ähnlich A" draft "$SECTION")
|
||
B=$(create_post page "Ähnlich B" draft "$SECTION")
|
||
C=$(create_post page "Ähnlich C" draft "$VARIANT")
|
||
CREATED_POSTS+=("$A" "$B" "$C")
|
||
SIM=$(ability lit/library-find-similar "$(jq -cn --argjson a "$A" '{post_id: $a, id: "s0", post_type: "page", limit: 200}')")
|
||
echo "$SIM" | jq -e --argjson b "$B" --argjson c "$C" '
|
||
([.matches[] | select(.post_id == $b and .id == "s0" and .match == "identical")] | length == 1) and
|
||
([.matches[] | select(.post_id == $c and .id == "s0" and .match == "structure")] | length == 1)' >/dev/null \
|
||
|| fail "find-similar: $SIM"
|
||
MG=$(ability lit/library-make-global "$(jq -cn --argjson a "$A" --arg rev "$(rev_of "$A")" --arg t "$PREFIX Global" \
|
||
'{post_id: $a, tree_rev: $rev, id: "s0", title: $t}')")
|
||
LIB=$(echo "$MG" | jq -r '.library_id // empty')
|
||
[ -n "$LIB" ] || fail "make-global: $MG"
|
||
CREATED_POSTS+=("$LIB")
|
||
[ "$(content_of "$LIB")" = "$SECTION" ] || fail "global entry bytes differ from the section"
|
||
[ "$(wp eval "echo implode(',', wp_get_object_terms($LIB, 'scope', ['fields' => 'slugs']));")" = "global" ] || fail "entry is not global"
|
||
has "$(content_of "$A")" "\"globalModule\":\"$LIB\",\"blockName\":\"divi/section\"" || fail "page A holds no instance"
|
||
BHASH=$(content_hash "$B")
|
||
CHASH=$(content_hash "$C")
|
||
BREVS=$(wp eval "echo count(wp_get_post_revisions($B));")
|
||
TARGETS=$(jq -cn --argjson lib "$LIB" --argjson b "$B" --arg rev "$(rev_of "$B")" '{library_id: $lib, targets: [{post_id: $b, tree_rev: $rev, ids: ["s0"]}]}')
|
||
MDRY=$(ability lit/library-make-global "$TARGETS")
|
||
[ "$(error_code "$MDRY")" = "confirm_required" ] || fail "replace without dry run: $MDRY"
|
||
details "$MDRY" | jq -e --argjson b "$B" '.affected | map(.post_id) == [$b]' >/dev/null || fail "make-global dry run: $MDRY"
|
||
[ "$(content_hash "$B")" = "$BHASH" ] || fail "the dry run changed page B"
|
||
BAD=$(ability lit/library-make-global "$(jq -cn --argjson lib "$LIB" --argjson c "$C" --arg rev "$(rev_of "$C")" \
|
||
'{library_id: $lib, confirm: true, targets: [{post_id: $c, tree_rev: $rev, ids: ["s0"]}]}')")
|
||
[ "$(error_code "$BAD")" = "validation_failed" ] || fail "a section that is not identical was replaced: $BAD"
|
||
[ "$(content_hash "$C")" = "$CHASH" ] || fail "the refused target C changed"
|
||
MCONF=$(ability lit/library-make-global "$(echo "$TARGETS" | jq -c '. + {confirm: true}')")
|
||
echo "$MCONF" | jq -e --argjson b "$B" '.pages[0].post_id == $b and .pages[0].revision_id > 0' >/dev/null || fail "replace occurrences: $MCONF"
|
||
has "$(content_of "$B")" "\"globalModule\":\"$LIB\"" || fail "page B holds no instance"
|
||
[ "$(wp eval "echo count(wp_get_post_revisions($B));")" -gt "$BREVS" ] || fail "replacing made no revision of page B"
|
||
[ "$(content_hash "$C")" = "$CHASH" ] || fail "page C changed"
|
||
pass "find-similar: identical and structure; make-global mode 1 (entry + instance), mode 2 dry run, confirm, identical only"
|
||
|
||
# 12. P13: with the library group disabled no library ID is named; P10: post_link_url_page refuses a post
|
||
wp option update lit_mcp_exposure '{"@group:divi-library":"disabled"}' --format=json >/dev/null
|
||
connect "$ADMIN_LOGIN"
|
||
if has "$(tool lit-divi-outline "{\"post_id\":$A}")" "$LIB"; then fail "outline names library ID $LIB with the library hidden"; fi
|
||
if has "$(tool lit-divi-node-get "{\"post_id\":$A,\"id\":\"g0\"}")" "$LIB"; then fail "node-get names library ID $LIB"; fi
|
||
if has "$(tool lit-page-render-summary "{\"post_id\":$A}")" "$LIB"; then fail "render summary names library ID $LIB"; fi
|
||
restore_option lit_mcp_exposure "$ORIGINAL_EXPOSURE"
|
||
connect "$ADMIN_LOGIN"
|
||
has "$(tool lit-divi-outline "{\"post_id\":$A}")" "$LIB" || fail "control: outline does not name $LIB with the library visible"
|
||
POST=$(create_post post "Beitrag" draft '')
|
||
CREATED_POSTS+=("$POST")
|
||
LINK="\$variable({\"type\":\"content\",\"value\":{\"name\":\"post_link_url_page\",\"settings\":{\"post_id\":\"$POST\"}}})\$"
|
||
PHASH=$(content_hash "$PAGE")
|
||
P10=$(tool lit-divi-insert "$(jq -cn --argjson id "$PAGE" --arg rev "$(rev_of "$PAGE")" --arg l "$LINK" \
|
||
'{post_id: $id, tree_rev: $rev, nodes: [{type: "section", modules: [{type: "button", text: "Mehr", url: $l}]}]}')")
|
||
[ "$(error_code "$P10")" = "validation_failed" ] || fail "post_link_url_page accepted a post: $P10"
|
||
[ "$(content_hash "$PAGE")" = "$PHASH" ] || fail "the refused link changed the page"
|
||
pass "P13: no library ID without library access; P10: post_link_url_page refuses a post as target"
|
||
|
||
# 13. Cleanup now and check it: Theme Builder list and enabled states as before, nothing of the run left
|
||
if has "$(render_home)" "$MARKER"; then fail "the home page shows the marker"; fi
|
||
KEEP_IDS="${CREATED_POSTS[*]} ${CREATED_TEMPLATES[*]} ${POD_POSTS[*]}"
|
||
cleanup
|
||
[ " $(divi_caches) " = "$DIVI_CACHES_BEFORE" ] || fail "Divi feature cache options differ: before$DIVI_CACHES_BEFORE, after $(divi_caches)"
|
||
[ "$(tb_state)" = "$TB_BEFORE" ] || fail "Theme Builder list/enabled states differ: before $TB_BEFORE, after $(tb_state)"
|
||
tb_state | jq -e '[.enabled[]] | all(. != "1")' >/dev/null || fail "a Theme Builder template is enabled after the run: $(tb_state)"
|
||
[ "$(option_json "$BACKUP_OPTION")" = "$ORIGINAL_BACKUP" ] || fail "Theme Builder backup option not restored"
|
||
[ "$(option_json lit_mcp_exposure)" = "$ORIGINAL_EXPOSURE" ] || fail "lit_mcp_exposure not restored"
|
||
[ "$(option_json lit_mcp_allowed_roles)" = "$ORIGINAL_ROLES" ] || fail "lit_mcp_allowed_roles not restored"
|
||
[ "$(option_json rewrite_rules)" = "$ORIGINAL_REWRITE" ] || fail "rewrite_rules not restored"
|
||
LEFT=$(LIT_IDS="$KEEP_IDS" LIT_PREFIX="$PREFIX" LIT_POD="$POD" LIT_LOGIN="$EDITOR_LOGIN" LIT_ROLE="$EDITOR_ROLE" wp eval '
|
||
global $wpdb;
|
||
$ids = array_values(array_unique(array_filter(array_map("intval", explode(" ", (string) getenv("LIT_IDS"))))));
|
||
$in = $ids === [] ? "0" : implode(",", $ids);
|
||
$left = [];
|
||
$left["posts"] = (int) $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->posts WHERE ID IN ($in) OR post_parent IN ($in)");
|
||
$left["meta"] = (int) $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->postmeta WHERE post_id IN ($in)");
|
||
$left["terms"] = (int) $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->term_relationships WHERE object_id IN ($in)");
|
||
$left["log"] = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}lit_mcp_log WHERE object_id IN (\"" . implode("\",\"", $ids ?: [0]) . "\")");
|
||
$left["titles"] = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $wpdb->posts WHERE post_title LIKE %s",
|
||
$wpdb->esc_like((string) getenv("LIT_PREFIX")) . "%"));
|
||
$left["pod"] = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $wpdb->posts WHERE post_type = %s OR (post_type IN (\"_pods_pod\", \"_pods_field\") AND post_name = %s)",
|
||
getenv("LIT_POD"), getenv("LIT_POD")));
|
||
$left["user"] = get_user_by("login", getenv("LIT_LOGIN")) ? 1 : 0;
|
||
$left["role"] = wp_roles()->is_role(getenv("LIT_ROLE")) ? 1 : 0;
|
||
echo wp_json_encode(array_filter($left));')
|
||
[ "$LEFT" = "[]" ] || fail "left over after cleanup: $LEFT"
|
||
pass "cleanup: Theme Builder list as before, no template enabled, options restored, no post/meta/term/log/pod/user/role left"
|
||
|
||
finish
|