PluginProbe
WDesignKit – AI Templates, Widget Builder & MCP Workflow / 2.7.0
WDesignKit – AI Templates, Widget Builder & MCP Workflow v2.7.0
2.7.0 2.6.6 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.4.0 2.3.3 2.3.2 2.3.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 All 129 releases
wdesignkit / includes / admin / import / class-wdkit-import-reset.php

class-wdkit-import-reset.php in WDesignKit – AI Templates, Widget Builder & MCP Workflow 2.7.0, at includes/admin/import/class-wdkit-import-reset.php

195 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Site reset — draft existing pages and disable existing builder templates.
4 *
5 * This is the one step the runner deliberately did not have, and the reason is worth keeping
6 * in front of whoever reads this next: it is destructive to content the importer did not
7 * create. It drafts EVERY published page on the site and disables EVERY Nexter builder
8 * template. There is no ownership test possible, because the whole point is to clear the
9 * site's existing content out of the way.
10 *
11 * ── Why it exists here now ─────────────────────────────────────────────────
12 *
13 * The browser wizard runs this unconditionally on every import: site_setting() calls the
14 * `reset_site` action with no flag and no guard (import_loader.js -> wkit_reset_site). The
15 * PHP-side full-site entry point, wdkit_handle_create_full_site(), also runs it as its Step 1
16 * and lets a caller opt OUT via `skip => ['reset_site']`.
17 *
18 * So for those two callers this is not new behaviour — it is existing behaviour that the
19 * runner has to be able to reproduce, or the runner cannot back that entry point.
20 *
21 * ── What is different from the wizard ──────────────────────────────────────
22 *
23 * It stays off unless a caller explicitly asks. `Wdkit_Import_Context::normalize()` defaults
24 * `reset_site` to false and nothing flips it implicitly, so a runner invocation that says
25 * nothing about resetting does not reset. The wizard's own default is unchanged.
26 *
27 * The two operations mirror wkit_reset_site() exactly, in the same order:
28 * 1. do_action( 'nxt_update_builder_status', 'all' ) — Nexter sets nxt_build_status = 0
29 * on every nxt_builder post. This is what makes an imported header/footer the active
30 * one afterwards; see the trace in the Phase 5 header/footer answer.
31 * 2. draft every published page.
32 *
33 * @package Wdesignkit
34 * @since 2.6.5
35 */
36
37 if ( ! defined( 'ABSPATH' ) ) {
38 exit;
39 }
40
41 if ( ! class_exists( 'Wdkit_Import_Reset' ) ) {
42
43 /**
44 * Site reset.
45 */
46 class Wdkit_Import_Reset {
47
48 /**
49 * Draft published pages and disable existing builder templates.
50 *
51 * @param array $context Validated import context.
52 * @return array{status:string,drafted:int,builders_disabled:bool,reason?:string}
53 */
54 public static function run( $context ) {
55 $result = array(
56 'status' => 'skipped',
57 'drafted' => 0,
58 'builders_disabled' => false,
59 );
60
61 /* Two independently-gated operations sharing one entry point:
62 *
63 * reset_site drafts EVERY published page — destructive to content this
64 * import did not create, so it stays opt-in per caller.
65 * reset_builders disables the site's existing header/footer/etc. templates so
66 * the ones this import just created become the active ones.
67 * Not destructive to page content, so it defaults to whatever
68 * reset_site says UNLESS a caller names it explicitly — which is
69 * what a remote website-triggered import does: it turns
70 * reset_site off (protecting the site's own pages, see the
71 * class doc) but still wants old header/footer templates
72 * disabled, or the imported ones just stack on top of them.
73 */
74 $want_pages = ! empty( $context['reset_site'] );
75 $want_builders = isset( $context['reset_builders'] )
76 ? ! empty( $context['reset_builders'] )
77 : $want_pages;
78
79 if ( ! $want_pages && ! $want_builders ) {
80 $result['reason'] = 'reset_site_disabled';
81
82 return $result;
83 }
84
85 /* The wizard's handler gates the page drafting on manage_options. Same gate here,
86 * and it covers the builder disable too, which the wizard leaves ungated. */
87 if ( ! current_user_can( 'manage_options' ) ) {
88 $result['reason'] = 'missing_manage_options_capability';
89
90 return $result;
91 }
92
93 /* Order matters and matches wkit_reset_site(): builders first, then pages. */
94 if ( $want_builders ) {
95 $result['builders_disabled'] = self::disable_builder_templates();
96 }
97
98 if ( $want_pages ) {
99 $pages = get_posts(
100 array(
101 'post_type' => 'page',
102 'post_status' => 'publish',
103 'numberposts' => -1,
104 'fields' => 'ids',
105 )
106 );
107
108 foreach ( (array) $pages as $page_id ) {
109 $page_id = (int) $page_id;
110 $updated = wp_update_post(
111 array(
112 'ID' => $page_id,
113 'post_status' => 'draft',
114 )
115 );
116
117 if ( ! is_wp_error( $updated ) && $updated ) {
118 ++$result['drafted'];
119
120 /* Stamp only pages this importer itself created. Drafting here is
121 * unconditional and untargeted (see the class docblock) - most pages it
122 * demotes on a real site are not ours, so a page we did not create must never
123 * carry this meta. This just records that our own reset (not a site owner
124 * manually drafting a page) is why it is in draft status - see
125 * Wdkit_Import_Session::POST_SUPERSEDED_META - so a later, human-reviewed
126 * cleanup can tell the two apart. There is deliberately no automatic sweep
127 * consuming this yet (see ClickUp 14ynqxywnaa). */
128 if ( get_post_meta( $page_id, Wdkit_Import_Session::POST_OWNER_META, true ) ) {
129 update_post_meta( $page_id, Wdkit_Import_Session::POST_SUPERSEDED_META, time() );
130 }
131 }
132 }
133 }
134
135 $result['status'] = 'reset';
136
137 return $result;
138 }
139
140 /**
141 * Disable every Nexter builder template, the way the wizard does.
142 *
143 * ── Why this is not just do_action() ───────────────────────────────
144 *
145 * Nexter registers its `nxt_update_builder_status` listener inside
146 *
147 * if ( is_admin() ) { if ( current_user_can( 'manage_options' ) ) { ... } }
148 *
149 * (nexter-extension/include/custom-options/nexter-builder-condition.php). The browser
150 * wizard always satisfies both — admin-ajax.php is an admin request and the wizard
151 * requires manage_options — so `wkit_reset_site` disables the templates there.
152 *
153 * A runner invoked from anywhere else does not. Measured on a disposable site via
154 * WP-CLI: the pages were drafted but every builder template stayed enabled, and this
155 * method used to report `builders_disabled => true` anyway. A reset that half-completes
156 * while claiming success is worse than one that refuses, because the caller cannot tell
157 * that an existing header will still render alongside the imported one.
158 *
159 * So: fire the action when a listener exists — Nexter stays the owner of the behaviour
160 * in the case that matters — and otherwise write the same meta it would have written,
161 * which is a four-line operation and the entire content of Nexter's handler. Either way
162 * the return value is what actually happened, not what was attempted.
163 *
164 * @return bool True when the templates are now disabled.
165 */
166 private static function disable_builder_templates() {
167 if ( has_action( 'nxt_update_builder_status' ) ) {
168 do_action( 'nxt_update_builder_status', 'all' );
169
170 return true;
171 }
172
173 if ( ! post_type_exists( 'nxt_builder' ) ) {
174 /* Nexter is not active at all — nothing to disable, which is a success. */
175 return true;
176 }
177
178 $templates = get_posts(
179 array(
180 'post_type' => 'nxt_builder',
181 'post_status' => 'any',
182 'posts_per_page' => -1,
183 'fields' => 'ids',
184 )
185 );
186
187 foreach ( (array) $templates as $template_id ) {
188 update_post_meta( (int) $template_id, 'nxt_build_status', 0 );
189 }
190
191 return true;
192 }
193 }
194 }
195