PluginProbe ʕ •ᴥ•ʔ
Superb Addons: Blocks, Patterns, Pre-built Pages, Sliders, Popups, Free Forms, Animations & More / 3.4.2
Superb Addons: Blocks, Patterns, Pre-built Pages, Sliders, Popups, Free Forms, Animations & More v3.4.2
4.0.6 4.0.5 4.0.4 4.0.3 4.0.2 4.0.1 4.0.0 trunk 1.0.0 2.0.0 2.0.1 2.0.2 2.0.3 3.0 3.0.1 3.0.2 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.2.0 3.2.1 3.2.2 3.2.4 3.2.5 3.2.7 3.2.8 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.4.5 3.4.6 3.5.0 3.5.1 3.5.2 3.5.3 3.5.4 3.5.6 3.5.7 3.5.8 3.5.9 3.6.0 3.6.1 3.6.2 3.7.0 3.7.1
superb-blocks / src / admin / controllers / wizard / class-wizard-controller.php
superb-blocks / src / admin / controllers / wizard Last commit date
class-wizard-controller.php 1 year ago class-wizard-restorationpoint-controller.php 1 year ago class-wizard-template-preview-controller.php 1 year ago
class-wizard-controller.php
435 lines
1 <?php
2
3 namespace SuperbAddons\Admin\Controllers\Wizard;
4
5 use Exception;
6 use SuperbAddons\Admin\Controllers\DashboardController;
7 use SuperbAddons\Config\Capabilities;
8 use SuperbAddons\Data\Controllers\LogController;
9 use SuperbAddons\Data\Controllers\RestController;
10 use SuperbAddons\Data\Utils\ThemeInstaller;
11 use SuperbAddons\Data\Utils\Wizard\AddonsPageTemplateUtil;
12 use SuperbAddons\Data\Utils\Wizard\WizardActionParameter;
13 use SuperbAddons\Data\Utils\Wizard\WizardException;
14 use SuperbAddons\Data\Utils\Wizard\WizardItemTypes;
15 use SuperbAddons\Data\Utils\Wizard\WizardMenuCreator;
16 use SuperbAddons\Data\Utils\Wizard\WizardPageCreator;
17 use SuperbAddons\Data\Utils\Wizard\WizardPartCreator;
18 use SuperbAddons\Data\Utils\Wizard\WizardStageUtil;
19 use SuperbAddons\Gutenberg\Controllers\GutenbergController;
20 use WP_Error;
21 use WP_REST_Server;
22
23 defined('ABSPATH') || exit();
24
25 class WizardController
26 {
27 const WIZARD_ROUTE = '/wizard';
28 const ACTION_QUERY_PARAM = 'superbaddons-wizard-action';
29 const COMPLETED_QUERY_PARAM = 'superbaddons-wizard-completed';
30 const RECOMMENDER_TRANSIENT = 'superbaddons_wizard_recommender_transient';
31 const WOOCOMMERCE_TRANSIENT = 'superbaddons_wizard_woocommerce_transient';
32
33
34 public static function Initialize()
35 {
36 self::InitializeWizardRecommenderSwitchAction();
37 self::InitializeTemplateWizardEndpoints();
38 if (!GutenbergController::is_block_theme()) {
39 return;
40 }
41
42 self::InitializeWizardPageTemplates();
43 WizardTemplatePreviewController::InitializeTemplatePreview();
44 }
45
46 private static function InitializeWizardPageTemplates()
47 {
48 add_filter('get_block_templates', function ($query_result, $query, $template_type) {
49 if ($template_type !== WizardItemTypes::WP_TEMPLATE) {
50 return $query_result;
51 }
52
53 if (
54 !empty($query) &&
55 (isset($query['slug__in']) && !in_array(AddonsPageTemplateUtil::TEMPLATE_ID, $query['slug__in'])) ||
56 (isset($query['slug__not_in']) && in_array(AddonsPageTemplateUtil::TEMPLATE_ID, $query['slug__not_in']))
57 ) {
58 return $query_result;
59 }
60
61 $template = AddonsPageTemplateUtil::GetAddonsPageBlockTemplateObject();
62
63 $query_result[] = $template;
64
65 return $query_result;
66 }, 10, 3);
67
68 add_filter('get_block_file_template', function ($block_template, $id, $template_type) {
69 if ($template_type !== WizardItemTypes::WP_TEMPLATE) {
70 return $block_template;
71 }
72
73 if ($id === get_stylesheet() . "//" . AddonsPageTemplateUtil::TEMPLATE_ID || $id === AddonsPageTemplateUtil::PLUGIN_SLUG . '//' . AddonsPageTemplateUtil::TEMPLATE_ID) {
74 return AddonsPageTemplateUtil::GetAddonsPageBlockTemplateObject();
75 }
76
77 return $block_template;
78 }, 10, 3);
79 }
80
81 private static function InitializeWizardRecommenderSwitchAction()
82 {
83 // Add action to the switch theme hook
84 add_action('switch_theme', function () {
85 self::MaybeSetWizardRecommenderTransient();
86 });
87
88 // Check if WooCommerce is active
89 add_action('activated_plugin', function ($plugin) {
90 if ($plugin !== 'woocommerce/woocommerce.php') {
91 return;
92 }
93 self::MaybeSetWizardWooCommerceTransient();
94 });
95
96 add_action('deactivated_plugin', function ($plugin) {
97 if ($plugin !== 'woocommerce/woocommerce.php') {
98 return;
99 }
100 self::RemoveWizardWooCommerceTransient();
101 });
102 }
103
104 public static function MaybeSetWizardRecommenderTransient()
105 {
106 if (!GutenbergController::is_block_theme()) {
107 self::RemoveWizardRecommenderTransient();
108 self::RemoveWizardWooCommerceTransient();
109 return;
110 }
111 $current_theme = get_stylesheet();
112 $completed_themes = get_option('superbaddons_wizard_completed_themes', []);
113 if (in_array($current_theme, $completed_themes)) {
114 self::RemoveWizardRecommenderTransient();
115 return;
116 }
117 set_transient(self::RECOMMENDER_TRANSIENT, true, MONTH_IN_SECONDS);
118 self::MaybeSetWizardWooCommerceTransient();
119 }
120
121 public static function MaybeSetWizardWooCommerceTransient()
122 {
123 if (is_plugin_active('woocommerce/woocommerce.php')) {
124 set_transient(self::WOOCOMMERCE_TRANSIENT, true, MONTH_IN_SECONDS);
125 } else {
126 self::RemoveWizardWooCommerceTransient();
127 }
128 }
129
130 public static function RemoveWizardRecommenderTransient()
131 {
132 return delete_transient(self::RECOMMENDER_TRANSIENT);
133 }
134
135 public static function GetWizardRecommenderTransient()
136 {
137 return get_transient(self::RECOMMENDER_TRANSIENT);
138 }
139
140 public static function GetWizardWoocommerceTransient()
141 {
142 return get_transient(self::WOOCOMMERCE_TRANSIENT);
143 }
144
145 public static function RemoveWizardWooCommerceTransient()
146 {
147 return delete_transient(self::WOOCOMMERCE_TRANSIENT);
148 }
149
150 private static function SetWizardPartPreviewTransient($preview_data)
151 {
152 $user_id = get_current_user_id();
153 $transient = get_transient(WizardTemplatePreviewController::TEMPLATE_PART_PREVIEW_TRANSIENT);
154 $transient[$user_id] = $preview_data;
155 return set_transient(WizardTemplatePreviewController::TEMPLATE_PART_PREVIEW_TRANSIENT, $transient, DAY_IN_SECONDS);
156 }
157
158 public static function GetPartPreviewTransient()
159 {
160 $user_id = get_current_user_id();
161 $transient = get_transient(WizardTemplatePreviewController::TEMPLATE_PART_PREVIEW_TRANSIENT);
162 return isset($transient[$user_id]) ? $transient[$user_id] : false;
163 }
164
165 public static function RemoveWizardPartPreviewTransient()
166 {
167 return delete_transient(self::RECOMMENDER_TRANSIENT);
168 }
169
170 public static function GetRecommendedBlockThemes()
171 {
172 if (!function_exists('themes_api')) {
173 require_once(ABSPATH . 'wp-admin/includes/theme.php');
174 }
175
176 return themes_api(
177 "query_themes",
178 array(
179 "author" => "superbaddons",
180 "per_page" => 24,
181 "browse" => "popular",
182 "fields" => array("name" => true, "slug" => true, "screenshot_url" => true)
183 )
184 );
185 }
186
187 public static function GetWizardURL($action)
188 {
189 return add_query_arg(
190 array(
191 'page' => DashboardController::PAGE_WIZARD,
192 self::ACTION_QUERY_PARAM => $action,
193 ),
194 admin_url("admin.php")
195 );
196 }
197
198 public static function GetWizardCompleteURL($wizardType)
199 {
200 return add_query_arg(
201 array(
202 'page' => DashboardController::PAGE_WIZARD,
203 self::ACTION_QUERY_PARAM => WizardActionParameter::COMPLETE,
204 self::COMPLETED_QUERY_PARAM => $wizardType
205 ),
206 admin_url("admin.php")
207 );
208 }
209
210 public static function GetCompletedWizardType()
211 {
212 return isset($_GET[self::COMPLETED_QUERY_PARAM]) ? $_GET[self::COMPLETED_QUERY_PARAM] : false;
213 }
214
215 private static function isAction($param, $action)
216 {
217 return isset($_GET[$param]) && $_GET[$param] === $action;
218 }
219
220 private static function isAllowedAction($action)
221 {
222 if (!isset($action)) {
223 return false;
224 }
225 $allowed_actions = [WizardActionParameter::ADD_NEW_PAGES, WizardActionParameter::HEADER_FOOTER, WizardActionParameter::WOOCOMMERCE_HEADER, WizardActionParameter::THEME_DESIGNER, WizardActionParameter::RESTORE];
226 return in_array($action, $allowed_actions);
227 }
228
229 public static function IsWizardStages()
230 {
231 if (!isset($_GET[self::ACTION_QUERY_PARAM]) || !self::isAllowedAction($_GET[self::ACTION_QUERY_PARAM])) {
232 return false;
233 }
234
235 return true;
236 }
237
238 public static function IsCompleteScreen()
239 {
240 return self::isAction(self::ACTION_QUERY_PARAM, WizardActionParameter::COMPLETE);
241 }
242
243 public static function ThemeHasCompletedWizard()
244 {
245 $current_theme = get_stylesheet();
246 $completed_themes = get_option('superbaddons_wizard_completed_themes', []);
247 return in_array($current_theme, $completed_themes);
248 }
249
250 public static function CompleteWizard()
251 {
252 $current_theme = get_stylesheet();
253 $completed_themes = get_option('superbaddons_wizard_completed_themes', []);
254 if (!in_array($current_theme, $completed_themes)) {
255 $completed_themes[] = $current_theme;
256 update_option('superbaddons_wizard_completed_themes', $completed_themes, false);
257 }
258 self::RemoveWizardRecommenderTransient();
259 }
260
261 public static function CompleteWooCommerceWizard()
262 {
263 self::RemoveWizardWooCommerceTransient();
264 }
265
266 private static function InitializeTemplateWizardEndpoints()
267 {
268 RestController::AddRoute(self::WIZARD_ROUTE, array(
269 'methods' => WP_REST_Server::EDITABLE,
270 'permission_callback' => array(self::class, 'TemplateWizardCallbackPermissionCheck'),
271 'callback' => array(self::class, 'TemplateWizardCallback'),
272 ));
273 }
274
275 public static function TemplateWizardCallbackPermissionCheck()
276 {
277 // Restrict endpoint to only users who have the proper capability.
278 if (!current_user_can(Capabilities::ADMIN)) {
279 return new WP_Error('rest_forbidden', esc_html__('Unauthorized. Please check user permissions.', "superb-blocks"), array('status' => 401));
280 }
281
282 return true;
283 }
284
285 public static function TemplateWizardCallback($request)
286 {
287 if (!isset($request['action'])) {
288 return new \WP_Error('bad_request_plugin', 'Bad Plugin Request', array('status' => 400));
289 }
290 switch ($request['action']) {
291 case 'switchtheme':
292 return self::SwitchThemeCallback($request);
293 case 'create':
294 if (!GutenbergController::is_block_theme()) {
295 return new \WP_Error('bad_request_plugin', 'Bad Plugin Request', array('status' => 400));
296 }
297 return self::TemplateWizardCreateCallback($request);
298 case 'headerfooterpreview':
299 if (!GutenbergController::is_block_theme()) {
300 return new \WP_Error('bad_request_plugin', 'Bad Plugin Request', array('status' => 400));
301 }
302 return self::HeaderFooterPreviewCallback($request);
303 default:
304 return new \WP_Error('bad_request_plugin', 'Bad Plugin Request', array('status' => 400));
305 }
306 }
307
308 private static function SwitchThemeCallback($request)
309 {
310 try {
311 $theme_slug = $request['theme'];
312 if (empty($theme_slug)) {
313 return rest_ensure_response(['success' => false, 'text' => esc_html__("Couldn't find selected theme.", "superb-blocks")]);
314 }
315
316 $themes_api = self::GetRecommendedBlockThemes();
317
318 $accepted_theme = false;
319 foreach ($themes_api->themes as $theme) {
320 if ($theme->slug === $theme_slug) {
321 $accepted_theme = true;
322 break;
323 }
324 }
325
326 if (!$accepted_theme) {
327 return rest_ensure_response(['success' => false, 'text' => esc_html__("Selected theme is invalid. Please contact support for assistance.", "superb-blocks")]);
328 }
329
330 $installed = ThemeInstaller::Install($theme_slug);
331
332 return rest_ensure_response(['success' => $installed]);
333 } catch (Exception $ex) {
334 LogController::HandleException($ex);
335 return new \WP_Error('internal_error_plugin', 'Internal Plugin Error', array('status' => 500));
336 }
337 }
338
339 private static function TemplateWizardCreateCallback($request)
340 {
341 try {
342 if (!isset($request['selection_data'])) {
343 return rest_ensure_response(['success' => false, 'text' => esc_html__("Something went wrong. The process could not start.", "superb-blocks")]);
344 }
345
346 $selection_data = json_decode($request['selection_data'], true);
347
348 $stageUtil = new WizardStageUtil($request['wizardType']);
349 if (!self::ValidateSelectionData($selection_data, $stageUtil)) {
350 return rest_ensure_response(['success' => false, 'text' => esc_html__("Something went wrong. Please double-check that all steps have been correctly completed.", "superb-blocks")]);
351 }
352
353 WizardPartCreator::CreateTemplateParts($selection_data, $stageUtil);
354
355 if ($stageUtil->HasPageStages()) {
356 $menu_items = WizardPageCreator::CreateTemplatePages($selection_data, $stageUtil);
357
358 if (empty($menu_items)) {
359 return rest_ensure_response(['success' => false, 'text' => esc_html__("Something went wrong. Templates and/or pages were not able to be properly processed.", "superb-blocks")]);
360 }
361
362 WizardMenuCreator::MaybeUpdateMenu($selection_data, $menu_items);
363 }
364
365 if ($stageUtil->GetType() === WizardActionParameter::THEME_DESIGNER) {
366 WizardController::CompleteWizard();
367 } elseif ($stageUtil->GetType() === WizardActionParameter::WOOCOMMERCE_HEADER) {
368 WizardController::CompleteWooCommerceWizard();
369 }
370
371 return rest_ensure_response(['success' => true]);
372 } catch (WizardException $wex) {
373 return rest_ensure_response(['success' => false, 'text' => $wex->getMessage()]);
374 } catch (Exception $ex) {
375 LogController::HandleException($ex);
376 return new WP_Error('internal_error_plugin', 'Internal Plugin Error', array('status' => 500));
377 }
378 }
379
380 private static function ValidateSelectionData($selection_data, $stageUtil)
381 {
382 if (!self::isAllowedAction($stageUtil->GetType())) {
383 return false;
384 }
385
386 foreach ($stageUtil->GetStages() as $stage_type) {
387 if (!isset($selection_data[$stage_type])) {
388 if ($stageUtil->GetType() === WizardActionParameter::RESTORE) {
389 continue;
390 }
391 return false;
392 }
393
394 $stage_selections = $selection_data[$stage_type];
395
396 if (!is_array($stage_selections)) {
397 return false;
398 }
399
400 if (empty($stage_selections)) {
401 continue;
402 }
403
404 if (isset($stage_selections[0]['isChanged']) && !boolval($stage_selections[0]['isChanged'])) {
405 continue;
406 }
407
408 foreach ($stage_selections as $selection) {
409 if (!isset($selection['slug']) || !isset($selection['title'])) {
410 return false;
411 }
412 }
413 }
414
415 return true;
416 }
417
418 public static function HeaderFooterPreviewCallback($request)
419 {
420 $preview_transient = [];
421 // Do not set if the header is the default header.
422 if (isset($request['header']) && $request['header'] !== 'header') {
423 $preview_transient['header'] = $request['header'];
424 }
425 // Do not set if the footer is the default footer.
426 if (isset($request['footer']) && $request['footer'] !== 'footer') {
427 $preview_transient['footer'] = $request['footer'];
428 }
429
430 self::SetWizardPartPreviewTransient($preview_transient);
431
432 return rest_ensure_response(['success' => true]);
433 }
434 }
435