PluginProbe
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News / 4.0.4
Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News v4.0.4
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 4.0.2 4.0.1 2.3.5 2.3.6 2.4.0 2.4.1 2.4.10 2.4.11 2.4.12 2.4.13 2.4.14 2.4.15 2.4.16 2.4.17 2.4.18 2.4.19 2.4.2 2.4.20 2.4.21 All 88 releases
post-carousel / includes / Admin / Admin_Notices.php

Admin_Notices.php in Smart Post – Post Grid, Post Carousel, Post Slider Gutenberg Blocks for Blog & News 4.0.4, at includes/Admin/Admin_Notices.php

574 lines 18.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The admin notices file.
4 *
5 * @package Smart_Post_Show
6 * @subpackage Smart_Post_Show/Admin
7 * @author ShapedPlugin<support@shapedplugin.com>
8 */
9
10 namespace SmartPostShow\Admin;
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit; // Exit if accessed directly.
14 }
15
16 /**
17 * This class is responsible for all kinds of admin facing notices.
18 */
19 class Admin_Notices {
20
21 /**
22 * Initialize the class and set its properties.
23 */
24 public function __construct() {
25 add_action( 'admin_notices', array( $this, 'render_all_admin_notices' ) );
26 add_action( 'in_admin_header', array( $this, 'render_blocks_promo_modal' ) );
27 add_action( 'wp_ajax_spsp_dismiss_blocks_promo_notice', array( $this, 'dismiss_blocks_promo_notice' ) );
28 add_action( 'wp_ajax_spsp_dismiss_blocks_promo_modal', array( $this, 'dismiss_blocks_promo_modal' ) );
29 add_action( 'wp_ajax_check_sp_post_carousel_posts', array( $this, 'check_sp_post_carousel_posts' ) );
30 }
31
32 /**
33 * Display all admin notice for backend.
34 *
35 * @return void
36 */
37 public function render_all_admin_notices() {
38 $this->render_blocks_promo_notice();
39 }
40
41 /**
42 * Render the "Meet the new Smart Post" promo banner that surfaces
43 * the block editor + ready patterns on the classic sp_post_carousel edit
44 * screens. Dismissed per-user via user_meta.
45 *
46 * @return void
47 */
48 public function render_blocks_promo_notice() {
49 if ( ! current_user_can( 'edit_posts' ) ) {
50 return;
51 }
52
53 $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
54 if ( ! $screen || 'sp_post_carousel' !== $screen->post_type ) {
55 return;
56 }
57 if ( 'post' !== $screen->base ) {
58 return;
59 }
60
61 if ( get_option( 'spsp_blocks_promo_notice_dismissed' ) ) {
62 return;
63 }
64
65 $block_editor_url = admin_url( 'post-new.php?post_type=sp_post_template&spblock_inserter=true' );
66 $nonce = wp_create_nonce( 'spsp_blocks_promo_notice' );
67 ?>
68 <div class="notice spsp-blocks-promo-notice" data-spsp-nonce="<?php echo esc_attr( $nonce ); ?>">
69 <div class="spsp-blocks-promo-notice__text">
70 <?php
71 $highlight_open = '<a class="spsp-blocks-promo-notice__highlight" href="https://wpsmartpost.com/patterns/" target="_blank" rel="noopener">';
72 $highlight_blocks = '<a class="spsp-blocks-promo-notice__highlight" href="https://wpsmartpost.com/blocks/" target="_blank" rel="noopener">';
73 echo wp_kses(
74 sprintf(
75 /* translators: 1: opening strong, 2: closing strong, 3: opening anchor to block editor, 4: closing anchor. */
76 __( '%1$sMeet the new Smart Post%2$s — gives you %3$s60+ Gutenberg Blocks%4$s and %5$s250+ Ready Patterns%6$s to design visually — see every change live.', 'post-carousel' ),
77 '<strong>',
78 '</strong>',
79 $highlight_blocks,
80 '</a>',
81 $highlight_open,
82 '</a>'
83 ),
84 array(
85 'strong' => array(),
86 'a' => array(
87 'class' => array(),
88 'href' => array(),
89 'target' => array(),
90 'rel' => array(),
91 ),
92 )
93 );
94 ?>
95 </div>
96 <div class="spsp-blocks-promo-notice__actions">
97 <a class="spsp-blocks-promo-notice__cta" href="<?php echo esc_url( $block_editor_url ); ?>">
98 <?php esc_html_e( 'Try Block Editor', 'post-carousel' ); ?>
99 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
100 <path d="M2.25 6h7.5M6.75 2.25 10.5 6 6.75 9.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
101 </svg>
102 </a>
103 <button type="button" class="spsp-blocks-promo-notice__dismiss" aria-label="<?php esc_attr_e( 'Dismiss this notice', 'post-carousel' ); ?>">
104 <span class="dashicons dashicons-no-alt"></span>
105 </button>
106 </div>
107 </div>
108 <style>
109 .spsp-blocks-promo-notice {
110 display: flex;
111 align-items: center;
112 gap: 24px;
113 margin: 16px 20px 16px 0;
114 padding: 12px 20px 12px 24px;
115 background: #fff3cd;
116 border: 1px solid #f0c040;
117 border-left-width: 4px;
118 border-radius: 4px;
119 box-shadow: none;
120 color: #473400;
121 }
122 .spsp-blocks-promo-notice__text {
123 flex: 1 1 auto;
124 font: 400 15px/20px Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
125 color: #473400;
126 }
127 .spsp-blocks-promo-notice__text strong {
128 font-weight: 600;
129 color: #473400;
130 }
131 .spsp-blocks-promo-notice .spsp-blocks-promo-notice__highlight{
132 color: #473400;
133 font-weight: 600;
134 text-decoration: underline;
135 }
136 .spsp-blocks-promo-notice .spsp-blocks-promo-notice__highlight:hover,
137 .spsp-blocks-promo-notice .spsp-blocks-promo-notice__highlight:focus {
138 color: #1a74e4;
139 box-shadow: none;
140 outline: none;
141 }
142 .spsp-blocks-promo-notice__actions {
143 display: flex;
144 align-items: center;
145 gap: 12px;
146 flex-shrink: 0;
147 }
148 .spsp-blocks-promo-notice .spsp-blocks-promo-notice__cta {
149 display: inline-flex;
150 align-items: center;
151 gap: 5px;
152 padding: 8px 12px;
153 background: #1a74e4;
154 border: 1px solid #1a74e4;
155 border-radius: 2px;
156 color: #fff;
157 font: 600 13px/18px Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
158 text-decoration: none;
159 box-shadow: none;
160 }
161 .spsp-blocks-promo-notice .spsp-blocks-promo-notice__cta:hover,
162 .spsp-blocks-promo-notice .spsp-blocks-promo-notice__cta:focus {
163 background: #1565c0;
164 border-color: #1565c0;
165 color: #fff;
166 outline: none;
167 }
168 .spsp-blocks-promo-notice__cta svg {
169 flex-shrink: 0;
170 }
171 .spsp-blocks-promo-notice__dismiss {
172 display: inline-flex;
173 align-items: center;
174 justify-content: center;
175 width: 24px;
176 height: 24px;
177 padding: 0;
178 background: transparent;
179 border: 0;
180 color: #473400;
181 cursor: pointer;
182 }
183 .spsp-blocks-promo-notice__dismiss:hover,
184 .spsp-blocks-promo-notice__dismiss:focus {
185 color: #000;
186 outline: none;
187 }
188 .spsp-blocks-promo-notice__dismiss .dashicons {
189 font-size: 20px;
190 width: 20px;
191 height: 20px;
192 }
193 </style>
194 <script>
195 ( function() {
196 document.addEventListener( 'click', function( event ) {
197 var dismiss = event.target.closest( '.spsp-blocks-promo-notice__dismiss' );
198 if ( ! dismiss ) {
199 return;
200 }
201 var notice = dismiss.closest( '.spsp-blocks-promo-notice' );
202 if ( ! notice ) {
203 return;
204 }
205 notice.style.display = 'none';
206 var data = new FormData();
207 data.append( 'action', 'spsp_dismiss_blocks_promo_notice' );
208 data.append( 'nonce', notice.getAttribute( 'data-spsp-nonce' ) || '' );
209 if ( window.fetch && window.ajaxurl ) {
210 fetch( window.ajaxurl, { method: 'POST', credentials: 'same-origin', body: data } );
211 }
212 } );
213 } )();
214 </script>
215 <?php
216 }
217
218 /**
219 * Render a one-time welcome modal on the Add New Post Carousel screen pointing
220 * users at the block editor. Stored dismissal lives in wp_options so the
221 * popup never appears twice for the site.
222 *
223 * @return void
224 */
225 public function render_blocks_promo_modal() {
226 if ( ! current_user_can( 'edit_posts' ) ) {
227 return;
228 }
229
230 $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
231 if ( ! $screen || 'sp_post_carousel' !== $screen->post_type || 'add' !== $screen->action ) {
232 return;
233 }
234
235 if ( get_option( 'spsp_blocks_promo_modal_choice' ) ) {
236 return;
237 }
238
239 $block_editor_url = admin_url( 'post-new.php?post_type=sp_post_template&spblock_inserter=true' );
240 $nonce = wp_create_nonce( 'spsp_blocks_promo_modal' );
241 $modal_video_url = 'https://wpsmartpost.com/wp-content/uploads/2026/06/SP-Popup-V01_1.mp4';
242 ?>
243 <div class="spsp-blocks-promo-modal" data-spsp-nonce="<?php echo esc_attr( $nonce ); ?>" role="dialog" aria-modal="true" aria-labelledby="spsp-blocks-promo-modal__title">
244 <div class="spsp-blocks-promo-modal__backdrop" data-spsp-modal-close="1"></div>
245 <div class="spsp-blocks-promo-modal__dialog">
246 <div class="spsp-blocks-promo-modal__header">
247 <div class="spsp-blocks-promo-modal__heading">
248 <h2 id="spsp-blocks-promo-modal__title" class="spsp-blocks-promo-modal__title">
249 <?php esc_html_e( 'How would you like to create your post showcase?', 'post-carousel' ); ?>
250 </h2>
251 <p class="spsp-blocks-promo-modal__subtitle">
252 <?php esc_html_e( 'Choose the workflow that suits you best — you can always switch later.', 'post-carousel' ); ?>
253 </p>
254 </div>
255 <button type="button" class="spsp-blocks-promo-modal__close" data-spsp-modal-close="1" aria-label="<?php esc_attr_e( 'Close', 'post-carousel' ); ?>">
256 <svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
257 <path fill="currentColor" d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"/>
258 </svg>
259 </button>
260 </div>
261 <div class="spsp-blocks-promo-modal__body">
262 <div class="spsp-blocks-promo-modal__preview" aria-hidden="true">
263 <video class="spsp-blocks-promo-modal__video" src="<?php echo esc_url( $modal_video_url ); ?>" autoplay loop muted playsinline preload="metadata"></video>
264 </div>
265 <div class="spsp-blocks-promo-modal__actions">
266 <a class="spsp-blocks-promo-modal__cta" href="<?php echo esc_url( $block_editor_url ); ?>">
267 <?php esc_html_e( 'Start with Block Editor', 'post-carousel' ); ?>
268 <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
269 <path d="M2.625 7h8.75M7.875 2.625 12.25 7l-4.375 4.375" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
270 </svg>
271 </a>
272 <button type="button" class="spsp-blocks-promo-modal__skip" data-spsp-modal-close="1">
273 <?php esc_html_e( 'Continue with Classic Shortcode', 'post-carousel' ); ?>
274 </button>
275 </div>
276 </div>
277 <div class="spsp-blocks-promo-modal__footer">
278 <label class="spsp-blocks-promo-modal__checkbox">
279 <input type="checkbox" class="spsp-blocks-promo-modal__checkbox-input" />
280 <span class="spsp-blocks-promo-modal__checkbox-label">
281 <?php esc_html_e( 'Remember this choice and skip this step next time.', 'post-carousel' ); ?>
282 </span>
283 </label>
284 </div>
285 </div>
286 </div>
287 <style>
288 #wpcontent:has(.spsp-blocks-promo-modal) {
289 position: relative;
290 }
291 .spsp-blocks-promo-modal {
292 position: absolute;
293 inset: 0;
294 min-height: calc(100vh - 32px);
295 z-index: 99998;
296 }
297 @media screen and (max-width: 782px) {
298 .spsp-blocks-promo-modal {
299 min-height: calc(100vh - 46px);
300 }
301 }
302 .spsp-blocks-promo-modal__backdrop {
303 position: absolute;
304 inset: 0;
305 background: rgba(0, 0, 0, 0.85);
306 }
307 .spsp-blocks-promo-modal__dialog {
308 position: fixed;
309 top: 50%;
310 left: 50%;
311 transform: translate(-50%, -50%);
312 display: flex;
313 flex-direction: column;
314 width: calc(100% - 48px);
315 max-width: 660px;
316 background: #fff;
317 border-radius: 12px;
318 overflow: hidden;
319 box-shadow: 0 24px 48px rgba(0, 0, 0, 0.18);
320 font: 400 13px/20px Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
321 color: #3d434b;
322 }
323 .spsp-blocks-promo-modal__header {
324 display: flex;
325 align-items: center;
326 gap: 5px;
327 padding: 18px 32px;
328 background: #f6f7f7;
329 border-bottom: 1px solid #ddd;
330 }
331 .spsp-blocks-promo-modal__heading {
332 flex: 1 1 auto;
333 display: flex;
334 flex-direction: column;
335 gap: 5px;
336 }
337 .spsp-blocks-promo-modal__title {
338 margin: 0;
339 font: 600 20px/24px Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
340 color: #1d2327;
341 }
342 .spsp-blocks-promo-modal__subtitle {
343 margin: 0;
344 font: 400 12px/16px Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
345 color: #3d434b;
346 }
347 .spsp-blocks-promo-modal__close {
348 display: inline-flex;
349 align-items: center;
350 justify-content: center;
351 width: 32px;
352 height: 32px;
353 padding: 0;
354 background: #fff;
355 border: 1px solid #c3c4c7;
356 border-radius: 999px;
357 color: #1d2327;
358 cursor: pointer;
359 flex-shrink: 0;
360 }
361 .spsp-blocks-promo-modal__close:hover,
362 .spsp-blocks-promo-modal__close:focus {
363 background: #f6f7f7;
364 outline: none;
365 }
366 .spsp-blocks-promo-modal__body {
367 display: flex;
368 flex-direction: column;
369 gap: 23px;
370 padding: 28px 32px;
371 }
372 .spsp-blocks-promo-modal__preview {
373 width: 100%;
374 height: 356px;
375 background: #d9d9d9;
376 border-radius: 6px;
377 border: 1px solid #DFDFDF;
378 overflow: hidden;
379 }
380 .spsp-blocks-promo-modal__video {
381 display: block;
382 width: 100%;
383 height: 100%;
384 object-fit: cover;
385 border-radius: inherit;
386 }
387 .spsp-blocks-promo-modal__actions {
388 display: flex;
389 gap: 10px;
390 align-items: center;
391 }
392 .spsp-blocks-promo-modal .spsp-blocks-promo-modal__cta {
393 display: inline-flex;
394 align-items: center;
395 gap: 5px;
396 padding: 13px 18px;
397 background: #1a74e4;
398 border: 1px solid #1a74e4;
399 border-radius: 4px;
400 color: #fff;
401 font: 600 13px/18px Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
402 text-decoration: none;
403 box-shadow: none;
404 }
405 .spsp-blocks-promo-modal .spsp-blocks-promo-modal__cta:hover,
406 .spsp-blocks-promo-modal .spsp-blocks-promo-modal__cta:focus {
407 background: #1565c0;
408 border-color: #1565c0;
409 color: #fff;
410 outline: none;
411 }
412 .spsp-blocks-promo-modal__skip {
413 display: inline-flex;
414 align-items: center;
415 gap: 5px;
416 padding: 13px 18px;
417 background: #fff;
418 border: 1px solid #c3c4c7;
419 border-radius: 4px;
420 color: #1d2327;
421 font: 600 13px/18px Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
422 cursor: pointer;
423 }
424 .spsp-blocks-promo-modal__skip:hover,
425 .spsp-blocks-promo-modal__skip:focus {
426 background: #f6f7f7;
427 outline: none;
428 }
429 .spsp-blocks-promo-modal__footer {
430 padding: 8px 32px;
431 background: #f6f7f7;
432 }
433 .spsp-blocks-promo-modal__checkbox {
434 display: inline-flex;
435 align-items: center;
436 gap: 10px;
437 cursor: pointer;
438 }
439 .spsp-blocks-promo-modal__checkbox .spsp-blocks-promo-modal__checkbox-input {
440 width: 16px;
441 height: 16px;
442 margin: 0;
443 background: #fff;
444 border: 1px solid #949494;
445 border-radius: 2px;
446 }
447 .spsp-blocks-promo-modal__checkbox-label {
448 font: 400 13px/20px Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
449 color: #3d434b;
450 }
451 </style>
452 <script>
453 ( function() {
454 var modal = document.querySelector( '.spsp-blocks-promo-modal' );
455 if ( ! modal ) {
456 return;
457 }
458 var checkbox = modal.querySelector( '.spsp-blocks-promo-modal__checkbox-input' );
459 function persistChoice( choice ) {
460 if ( ! checkbox || ! checkbox.checked || ! window.ajaxurl ) {
461 return;
462 }
463 var data = new FormData();
464 data.append( 'action', 'spsp_dismiss_blocks_promo_modal' );
465 data.append( 'nonce', modal.getAttribute( 'data-spsp-nonce' ) || '' );
466 data.append( 'choice', choice );
467 if ( navigator.sendBeacon ) {
468 navigator.sendBeacon( window.ajaxurl, data );
469 } else if ( window.fetch ) {
470 fetch( window.ajaxurl, {
471 method: 'POST',
472 credentials: 'same-origin',
473 body: data,
474 keepalive: true,
475 } );
476 }
477 }
478 function close() {
479 modal.style.display = 'none';
480 }
481 var skip = modal.querySelector( '.spsp-blocks-promo-modal__skip' );
482 if ( skip ) {
483 skip.addEventListener( 'click', function() {
484 persistChoice( 'classic_shortcode' );
485 } );
486 }
487 var cta = modal.querySelector( '.spsp-blocks-promo-modal__cta' );
488 if ( cta ) {
489 cta.addEventListener( 'click', function() {
490 persistChoice( 'block_editor' );
491 } );
492 }
493 modal.addEventListener( 'click', function( event ) {
494 var target = event.target.closest( '[data-spsp-modal-close]' );
495 if ( target ) {
496 event.preventDefault();
497 close();
498 }
499 } );
500 document.addEventListener( 'keydown', function( event ) {
501 if ( event.key === 'Escape' && modal.style.display !== 'none' ) {
502 close();
503 }
504 } );
505 } )();
506 </script>
507 <?php
508 }
509
510 /**
511 * Persist dismissal of the blocks promo notice site-wide.
512 *
513 * @return void
514 */
515 public function dismiss_blocks_promo_notice() {
516 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
517 if ( ! wp_verify_nonce( $nonce, 'spsp_blocks_promo_notice' ) ) {
518 wp_send_json_error();
519 }
520 update_option( 'spsp_blocks_promo_notice_dismissed', 1, false );
521 wp_send_json_success();
522 }
523
524 /**
525 * Persist the user's workflow choice from the Add New Post Carousel welcome modal.
526 *
527 * Only fires when the "Remember this choice" checkbox is ticked. The stored
528 * value records which button the user picked so the modal can be skipped
529 * on subsequent visits.
530 *
531 * @return void
532 */
533 public function dismiss_blocks_promo_modal() {
534 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
535 if ( ! wp_verify_nonce( $nonce, 'spsp_blocks_promo_modal' ) ) {
536 wp_send_json_error();
537 }
538 $choice = isset( $_POST['choice'] ) ? sanitize_text_field( wp_unslash( $_POST['choice'] ) ) : '';
539 $allowed = array( 'block_editor', 'classic_shortcode' );
540 if ( ! in_array( $choice, $allowed, true ) ) {
541 wp_send_json_error();
542 }
543 update_option( 'spsp_blocks_promo_modal_choice', $choice, false );
544 wp_send_json_success();
545 }
546
547 /**
548 * Check if there are any sp_post_carousel posts.
549 *
550 * AJAX handler for checking if the sp_post_carousel post type has any posts.
551 *
552 * @return void
553 */
554 public function check_sp_post_carousel_posts() {
555 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
556 if ( ! wp_verify_nonce( $nonce, 'sp-update-block-settings-nonce' ) ) {
557 wp_send_json_error();
558 }
559
560 $args = array(
561 'post_type' => 'sp_post_carousel',
562 'posts_per_page' => 1,
563 'fields' => 'ids',
564 'post_status' => array( 'publish', 'draft', 'pending', 'private' ),
565 );
566
567 $posts = get_posts( $args );
568 $has_posts = count( $posts ) > 0;
569 $editor_preference = get_option( 'spsp_blocks_promo_modal_choice', '' );
570 $is_block_editor = 'block-editor' === $editor_preference;
571 wp_send_json_success( array( 'has_posts' => $has_posts, 'is_block_editor' => $is_block_editor ) );
572 }
573 }
574