PluginProbe ʕ •ᴥ•ʔ
Superb Addons: Blocks, Patterns, Pre-built Pages, Sliders, Popups, Free Forms, Animations & More / 3.6.1
Superb Addons: Blocks, Patterns, Pre-built Pages, Sliders, Popups, Free Forms, Animations & More v3.6.1
4.0.7 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-template-preview-controller.php
superb-blocks / src / admin / controllers / wizard Last commit date
class-wizard-controller.php 11 months ago class-wizard-restorationpoint-controller.php 11 months ago class-wizard-template-preview-controller.php 11 months ago
class-wizard-template-preview-controller.php
348 lines
1 <?php
2
3 namespace SuperbAddons\Admin\Controllers\Wizard;
4
5 use SuperbAddons\Config\Capabilities;
6 use SuperbAddons\Data\Controllers\CacheController;
7 use SuperbAddons\Data\Utils\AllowedTemplateHTMLUtil;
8 use SuperbAddons\Data\Utils\CacheTypes;
9 use SuperbAddons\Data\Utils\GutenbergCache;
10 use SuperbAddons\Data\Utils\Wizard\AddonsPageTemplateUtil;
11 use SuperbAddons\Data\Utils\Wizard\WizardItemIdAffix;
12 use SuperbAddons\Data\Utils\Wizard\WizardItemTypes;
13 use WP_Query;
14
15 defined('ABSPATH') || exit();
16
17 class WizardTemplatePreviewController
18 {
19 const TEMPLATE_PREVIEW_KEY = 'superbaddons-template-preview';
20 const TEMPLATE_TYPE_KEY = 'superbaddons-template-type';
21 const USE_PAGE_TEMPLATE_KEY = 'superbaddons-template-custom';
22 const TEMPLATE_PART_PREVIEW_TRANSIENT = 'superbaddons_template_part_preview';
23
24 const TEMPLATE_PREVIEW_NONCE = 'superbaddons-template-preview-nonce';
25 const TEMPLATE_PREVIEW_NONCE_ACTION = 'superbaddons-template-preview-action';
26
27 public static function InitializeTemplatePreview()
28 {
29 if (is_admin()) {
30 return;
31 }
32
33 add_action('wp_loaded', function () {
34 if (!isset($_GET[self::TEMPLATE_PREVIEW_KEY]) || !isset($_GET[self::TEMPLATE_TYPE_KEY]) || !isset($_GET[self::USE_PAGE_TEMPLATE_KEY]) || !isset($_GET[self::TEMPLATE_PREVIEW_NONCE])) {
35 return;
36 }
37 if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_GET[self::TEMPLATE_PREVIEW_NONCE])), self::TEMPLATE_PREVIEW_NONCE_ACTION)) {
38 return;
39 }
40 if (!current_user_can(Capabilities::ADMIN)) {
41 return;
42 }
43 $template_id = sanitize_text_field(wp_unslash($_GET[self::TEMPLATE_PREVIEW_KEY]));
44 $template_type = sanitize_text_field(wp_unslash($_GET[self::TEMPLATE_TYPE_KEY]));
45 $template_custom = $_GET[self::USE_PAGE_TEMPLATE_KEY] == 1;
46 show_admin_bar(false);
47 switch ($template_type) {
48 case WizardItemTypes::WP_TEMPLATE:
49 case WizardItemTypes::WP_TEMPLATE_PART:
50 self::SetPlaceholderFilters($template_id, $template_type);
51 self::PreviewBlockTemplate($template_id, $template_type, $template_custom);
52 exit();
53 break;
54 case WizardItemTypes::PATTERN:
55 case WizardItemTypes::PAGE:
56 self::SetPlaceholderFilters($template_id, $template_type);
57 self::PreviewSuperbTemplate($template_id, $template_type, $template_custom);
58 exit();
59 break;
60 default:
61 // Static preview
62 break;
63 }
64 });
65 }
66
67 private static function SetPlaceholderFilters($template_id, $template_type)
68 {
69 if ($template_type == WizardItemTypes::WP_TEMPLATE_PART || $template_type == WizardItemTypes::PATTERN) {
70 // Enqueue part preview specific styles
71 wp_enqueue_style('superbaddons-wizard-part-preview-styles', SUPERBADDONS_ASSETS_PATH . '/css/page-wizard-preview.min.css', array(), SUPERBADDONS_VERSION);
72 }
73 // Filter the header and footer parts to show a placeholder image instead of the actual content if a preview is requested instead of default theme header/footer.
74 self::FilterHeaderFooterPreviewParts();
75
76 // Set the post ID to 0 to prevent the preview from showing the actual content of the page and to prevent Undefined index: postId in /wp-includes/blocks/comments.php on line 31
77 add_filter("render_block_context", function ($context) {
78 if (isset($context['postId'])) {
79 return $context;
80 }
81 $context['postId'] = 0;
82 return $context;
83 });
84
85 add_filter('the_title', function ($title) use ($template_type, $template_id) {
86 if (!empty($title)) {
87 return $title;
88 }
89 if ($template_type === WizardItemTypes::WP_TEMPLATE) {
90 $template = get_block_template($template_id, WizardItemTypes::WP_TEMPLATE);
91 if ($template) {
92 return $template->title;
93 }
94 }
95
96 return esc_html__("Placeholder Title", "superb-blocks");
97 });
98
99 // Replace the post content with a placeholder content for preview
100 add_filter("the_content", function ($content) {
101 return sprintf('<!-- wp:heading {"level":1,"style":{"spacing":{"padding":{"top":"50px","bottom":"50px","left":"50px","right":"50px"}}}} --> <h1 class="wp-block-heading" style="padding-top:50px;padding-right:50px;padding-bottom:50px;padding-left:50px">%s</h1> <!-- /wp:heading -->', esc_html__("This is a preview of the template. Any page content that you create will appear here.", "superb-blocks"));
102 }, 10);
103
104 // Replace the posts with a placeholder title and placeholder content for preview
105 add_filter('the_posts', function ($posts, $query) {
106 if (!isset($query->query)) {
107 return $posts;
108 }
109
110 if (isset($query->query['post_type']) && $query->query['post_type'] !== 'post') {
111 return $posts;
112 }
113
114 $amount = isset($query->query['posts_per_page']) ? $query->query['posts_per_page'] : 3;
115 $amount = ($amount === -1) ? 3 : min($amount, 10);
116
117 $placeholder_posts = array();
118 for ($i = 0; $i < $amount; $i++) {
119 // Create a placeholder post
120 $placeholder_posts[] = (object)array(
121 'post_title' => esc_html__("Placeholder Post", "superb-blocks") . " " . ($i + 1),
122 'post_content' => '',
123 'post_status' => 'publish',
124 'post_type' => 'post',
125 'post_author' => 1,
126 'post_date' => gmdate('Y-m-d H:i:s'),
127 );
128 }
129 return $placeholder_posts;
130 }, 10, 2);
131
132 // Replace the post thumbnail with a placeholder for preview
133 add_filter("post_thumbnail_html", function () {
134 return self::GetPlaceholderPreviewImage();
135 }, 0, 0);
136
137 // Replace featured image in "Featured Image" blocks with a placeholder for preview
138 add_filter("render_block", function ($block_content, $block) {
139 if (isset($block['blockName']) && $block['blockName'] === 'core/post-featured-image' && empty($block_content)) {
140 return '<figure class="alignwide wp-block-post-featured-image">' . self::GetPlaceholderPreviewImage() . '</figure>';
141 }
142 return $block_content;
143 }, 10, 2);
144
145 // Start global wp_query query to make sure placeholder posts are shown.
146 global $wp_query;
147 if (!$wp_query || !$wp_query instanceof WP_Query) {
148 return;
149 }
150 $wp_query = new WP_Query([
151 'post_type' => 'post',
152 ]);
153 }
154
155 private static function GetPlaceholderPreviewImage()
156 {
157 return '<img width="1900" height="1267" src="' . esc_url(SUPERBADDONS_ASSETS_PATH . "/img/placeholder-image.svg") . '" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" style="height:100%;object-fit:cover;">';
158 }
159
160 private static function PreviewBlockTemplate($template_id, $template_type = WizardItemTypes::WP_TEMPLATE)
161 {
162 if (strpos($template_id, WizardItemIdAffix::FILE_TEMPLATE) !== false) {
163 // If the template is a file template, get the file template preview.
164 $template_id = str_replace(WizardItemIdAffix::FILE_TEMPLATE, '', $template_id);
165 self::PreviewBlockFileTemplate($template_id, $template_type);
166 return;
167 }
168
169 if (strpos($template_id, WizardItemIdAffix::RESTORATION_POINT) !== false) {
170 // If the template is a restoration point, get the restoration point preview.
171 $template_id = str_replace(WizardItemIdAffix::RESTORATION_POINT, '', $template_id);
172 self::PreviewRestorationPoint($template_id);
173 return;
174 }
175
176 $template = get_block_template($template_id, $template_type);
177 if (!$template || $template->type !== $template_type || $template->status !== 'publish' || $template->theme !== get_stylesheet() || empty($template->content)) {
178 return;
179 }
180
181 $template_html = self::GetTheTemplateHTML($template->content);
182
183 self::RenderTemplateCanvas($template_html);
184 }
185
186 private static function PreviewBlockFileTemplate($template_id, $template_type)
187 {
188 $template = get_block_file_template($template_id, $template_type);
189 if ($template->theme !== get_stylesheet() || empty($template->content)) {
190 return;
191 }
192
193 $template_html = self::GetTheTemplateHTML($template->content);
194
195 self::RenderTemplateCanvas($template_html);
196 }
197
198 private static function PreviewRestorationPoint($template_id)
199 {
200 $restoration_point = WizardRestorationPointController::GetTemplateRestorationPoint($template_id);
201 if (!$restoration_point) return;
202
203 $template_html = self::GetTheTemplateHTML($restoration_point['content']);
204
205 self::RenderTemplateCanvas($template_html);
206 }
207
208 private static function PreviewSuperbTemplate($template_id, $template_type = WizardItemTypes::PAGE, $template_custom = false)
209 {
210 //// Get the preview URL from the cache by page id.
211 switch ($template_type) {
212 case WizardItemTypes::PAGE:
213 $cache_type = GutenbergCache::PAGES;
214 break;
215 case WizardItemTypes::PATTERN:
216 $cache_type = GutenbergCache::PATTERNS;
217 break;
218 default:
219 return;
220 }
221 // The cache has already been set, otherwise the preview will not work and we shouldn't be here.
222 $cache = CacheController::GetCache($cache_type, CacheTypes::GUTENBERG);
223 if (!$cache || !isset($cache->items)) return;
224 $preview_url = false;
225 foreach ($cache->items as $template) {
226 if ($template->id === $template_id) {
227 $preview_url = $template->preview;
228 break;
229 }
230 }
231
232 if (!$preview_url) return;
233
234 $preview_content = '<img src="' . esc_url($preview_url) . '" class="superbaddons-preview-image" width="100%" height="auto" style="user-select:none;">';
235
236 if ($template_type === WizardItemTypes::PAGE) {
237 // Override the content with a placeholder content for preview
238 if ($template_custom) {
239 add_filter("the_content", function () use ($preview_content) {
240 return $preview_content;
241 }, PHP_INT_MAX);
242 $template = get_block_template(get_stylesheet() . '//' . AddonsPageTemplateUtil::TEMPLATE_ID, WizardItemTypes::WP_TEMPLATE);
243 $content = $template->content;
244 } else {
245 $content = AddonsPageTemplateUtil::GetAddonsPageTemplateContent($preview_content);
246 }
247 } else {
248 // Preview pattern only
249 $content = $preview_content;
250 }
251
252 $template_html = self::GetTheTemplateHTML($content);
253
254 self::RenderTemplateCanvas($template_html);
255 }
256
257 private static function FilterHeaderFooterPreviewParts()
258 {
259 add_filter('render_block', function ($block_content, $block) {
260 if (isset($block['blockName']) && $block['blockName'] === 'core/template-part') {
261 if (isset($block['attrs']['slug']) && $block['attrs']['slug'] === 'header') {
262 $header_content = self::GetTemplatePartPreview('header');
263 if ($header_content) {
264 return $header_content;
265 }
266 }
267 if (isset($block['attrs']['slug']) && $block['attrs']['slug'] === 'footer') {
268 $footer_content = self::GetTemplatePartPreview('footer');
269 if ($footer_content) {
270 return $footer_content;
271 }
272 }
273 }
274 return $block_content;
275 }, 10, 2);
276 }
277
278 private static function GetTemplatePartPreview($slug)
279 {
280 $template_part_preview = WizardController::GetPartPreviewTransient();
281 if (!$template_part_preview || !isset($template_part_preview[$slug])) {
282 return false;
283 }
284
285 if (strpos($template_part_preview[$slug], WizardItemIdAffix::FILE_TEMPLATE) !== false) {
286 $template_id = get_stylesheet() . '//' . str_replace(WizardItemIdAffix::FILE_TEMPLATE, '', $template_part_preview[$slug]);
287 $template = get_block_file_template($template_id, WizardItemTypes::WP_TEMPLATE_PART);
288 return self::GetTheTemplateHTML($template->content);
289 }
290
291 if (strpos($template_part_preview[$slug], WizardItemIdAffix::RESTORATION_POINT) !== false) {
292 $restoration_id = str_replace(WizardItemIdAffix::RESTORATION_POINT, '', $template_part_preview[$slug]);
293 $restoration_point = WizardRestorationPointController::GetTemplateRestorationPoint($restoration_id);
294 if (!$restoration_point) return false;
295
296 return self::GetTheTemplateHTML($restoration_point['content']);
297 }
298
299
300 if ($template_part_preview[$slug] === $slug || strpos($slug, get_stylesheet()) !== false) return false;
301
302 $cache = CacheController::GetCache(GutenbergCache::PATTERNS, CacheTypes::GUTENBERG);
303 if (!$cache || !isset($cache->items)) return false;
304
305 $preview_url = false;
306 foreach ($cache->items as $template) {
307 if ($template->id === $template_part_preview[$slug]) {
308 $preview_url = $template->preview;
309 break;
310 }
311 }
312
313 if (!$preview_url) return false;
314
315 return '<img src="' . esc_url($preview_url) . '" class="superbaddons-preview-image" width="100%" height="auto" style="user-select:none;">';
316 }
317
318 private static function GetTheTemplateHTML($content)
319 {
320 global $_wp_current_template_content;
321 $_wp_current_template_content = $content;
322 return get_the_block_template_html();
323 }
324
325 private static function RenderTemplateCanvas($content)
326 {
327 ?>
328 <!DOCTYPE html>
329 <html <?php language_attributes(); ?>>
330
331 <head>
332 <meta charset="<?php bloginfo('charset'); ?>" />
333 <?php wp_head(); ?>
334 </head>
335
336 <body <?php body_class(); ?>>
337 <?php wp_body_open(); ?>
338 <?php AllowedTemplateHTMLUtil::enable_safe_styles(); ?>
339 <?php echo wp_kses($content, "post"); ?>
340 <?php AllowedTemplateHTMLUtil::disable_safe_styles(); ?>
341 <?php wp_footer(); ?>
342 </body>
343
344 </html>
345 <?php
346 }
347 }
348