PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.3
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.3
3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3.0 2.3.1 All 196 releases
convertkit / includes / blocks / class-convertkit-block-broadcasts.php

class-convertkit-block-broadcasts.php in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 3.4.3, at includes/blocks/class-convertkit-block-broadcasts.php

977 lines 28.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ConvertKit Broadcasts List Block class.
4 *
5 * @package ConvertKit
6 * @author ConvertKit
7 */
8
9 /**
10 * ConvertKit Broadcasts List Block for Gutenberg and Shortcode.
11 *
12 * @package ConvertKit
13 * @author ConvertKit
14 */
15 class ConvertKit_Block_Broadcasts extends ConvertKit_Block {
16
17 /**
18 * Constructor
19 *
20 * @since 1.9.7.4
21 */
22 public function __construct() {
23
24 // Register this as a shortcode in the ConvertKit Plugin.
25 add_filter( 'convertkit_shortcodes', array( $this, 'register' ) );
26
27 // Register this as a Gutenberg block in the ConvertKit Plugin.
28 add_filter( 'convertkit_blocks', array( $this, 'register' ) );
29
30 // Register this block's MCP abilities.
31 add_filter( 'convertkit_abilities', array( $this, 'register_abilities' ) );
32
33 // Enqueue scripts and styles for this Gutenberg Block in the editor and frontend views.
34 add_action( 'convertkit_gutenberg_enqueue_scripts_editor_and_frontend', array( $this, 'enqueue_scripts' ) );
35 add_action( 'convertkit_gutenberg_enqueue_styles_editor_and_frontend', array( $this, 'enqueue_styles' ) );
36
37 // Register REST API routes.
38 add_action( 'rest_api_init', array( $this, 'register_routes' ) );
39
40 }
41
42 /**
43 * Register REST API routes.
44 *
45 * @since 3.1.8
46 */
47 public function register_routes() {
48
49 register_rest_route(
50 'kit/v1',
51 '/broadcasts',
52 array(
53 'methods' => WP_REST_Server::READABLE,
54 'args' => array(
55 'date_format' => array(
56 'default' => $this->get_default_value( 'date_format' ),
57 'sanitize_callback' => 'sanitize_text_field',
58 ),
59 'display_image' => array(
60 'default' => $this->get_default_value( 'display_image' ),
61 'sanitize_callback' => 'absint',
62 ),
63 'display_description' => array(
64 'default' => $this->get_default_value( 'display_description' ),
65 'sanitize_callback' => 'absint',
66 ),
67 'display_read_more' => array(
68 'default' => $this->get_default_value( 'display_read_more' ),
69 'sanitize_callback' => 'absint',
70 ),
71 'read_more_label' => array(
72 'default' => $this->get_default_value( 'read_more_label' ),
73 'sanitize_callback' => 'sanitize_text_field',
74 ),
75 'limit' => array(
76 'default' => $this->get_default_value( 'limit' ),
77 'sanitize_callback' => 'absint',
78 ),
79 'page' => array(
80 'default' => $this->get_default_value( 'page' ),
81 'sanitize_callback' => 'absint',
82 ),
83 'paginate' => array(
84 'default' => $this->get_default_value( 'paginate' ),
85 'sanitize_callback' => 'absint',
86 ),
87 'paginate_label_next' => array(
88 'default' => $this->get_default_value( 'paginate_label_next' ),
89 'sanitize_callback' => 'sanitize_text_field',
90 ),
91 'paginate_label_prev' => array(
92 'default' => $this->get_default_value( 'paginate_label_prev' ),
93 'sanitize_callback' => 'sanitize_text_field',
94 ),
95 'link_color' => array(
96 'default' => $this->get_default_value( 'link_color' ),
97 'sanitize_callback' => 'sanitize_text_field',
98 ),
99 ),
100 'callback' => function ( $request ) {
101 return rest_ensure_response( $this->render_ajax( $request ) );
102 },
103
104 // No authentication required, as this is on the frontend site.
105 'permission_callback' => '__return_true',
106 )
107 );
108
109 }
110
111 /**
112 * Enqueues scripts for this Gutenberg Block in the editor and frontend views.
113 *
114 * @since 1.9.7.6
115 */
116 public function enqueue_scripts() {
117
118 // Get ConvertKit Settings.
119 $settings = new ConvertKit_Settings();
120
121 // Enqueue frontend JS.
122 convertkit_enqueue_frontend_js();
123
124 // Define variables.
125 wp_localize_script(
126 'convertkit-js',
127 'convertkit_broadcasts',
128 array(
129 // REST API URL endpoint.
130 'ajax_url' => rest_url( 'kit/v1/broadcasts' ),
131
132 // Whether debugging is enabled.
133 'debug' => $settings->debug_enabled(),
134 )
135 );
136
137 }
138
139 /**
140 * Enqueues styles for this Gutenberg Block in the editor and frontend views.
141 *
142 * @since 1.9.7.4
143 */
144 public function enqueue_styles() {
145
146 convertkit_enqueue_frontend_css();
147
148 }
149
150 /**
151 * Returns this block's programmatic name, excluding the convertkit- prefix.
152 *
153 * @since 1.9.7.4
154 */
155 public function get_name() {
156
157 /**
158 * This will register as:
159 * - a shortcode, with the name [convertkit_broadcasts].
160 * - a Gutenberg block, with the name convertkit/broadcasts.
161 */
162 return 'broadcasts';
163
164 }
165
166 /**
167 * Returns this block's title.
168 *
169 * @since 3.1.1
170 */
171 public function get_title() {
172
173 return __( 'Kit Broadcasts', 'convertkit' );
174
175 }
176
177 /**
178 * Returns this block's plural title.
179 *
180 * @since 3.4.0
181 *
182 * @return string
183 */
184 public function get_title_plural() {
185
186 return __( 'Kit Broadcasts', 'convertkit' );
187
188 }
189
190 /**
191 * Returns this block's icon.
192 *
193 * @since 3.1.1
194 */
195 public function get_icon() {
196
197 return 'resources/backend/images/block-icon-broadcasts.svg';
198
199 }
200
201 /**
202 * Returns this block's Title, Icon, Categories, Keywords and properties.
203 *
204 * @since 1.9.7.4
205 */
206 public function get_overview() {
207
208 // Fetch Posts.
209 $posts = new ConvertKit_Resource_Posts( 'output_broadcasts' );
210 $settings = new ConvertKit_Settings();
211
212 return array(
213 'title' => $this->get_title(),
214 'description' => __( 'Displays a list of your Kit broadcasts.', 'convertkit' ),
215 'icon' => $this->get_icon(),
216 'category' => 'convertkit',
217 'keywords' => array(
218 __( 'ConvertKit', 'convertkit' ),
219 __( 'Kit', 'convertkit' ),
220 __( 'Broadcasts', 'convertkit' ),
221 __( 'Posts', 'convertkit' ),
222 ),
223
224 // Function to call when rendering as a block or a shortcode on the frontend web site.
225 'render_callback' => array( $this, 'render' ),
226
227 // Shortcode: TinyMCE / QuickTags Modal Width and Height.
228 'modal' => array(
229 'width' => 650,
230 'height' => 455,
231 ),
232
233 // Shortcode: Include a closing [/shortcode] tag when using TinyMCE or QuickTag Modals.
234 'shortcode_include_closing_tag' => false,
235
236 // Gutenberg: Block Icon in Editor.
237 'gutenberg_icon' => convertkit_get_file_contents( CONVERTKIT_PLUGIN_PATH . '/resources/backend/images/block-icon-broadcasts.svg' ),
238
239 // Gutenberg: Example image showing how this block looks when choosing it in Gutenberg.
240 'gutenberg_example_image' => CONVERTKIT_PLUGIN_URL . 'resources/backend/images/block-example-broadcasts.png',
241
242 // Help descriptions, displayed when no Access Token / resources exist and this block/shortcode is added.
243 'no_access_token' => array(
244 'notice' => __( 'Not connected to Kit.', 'convertkit' ),
245 'link' => convertkit_get_setup_wizard_plugin_link(),
246 'link_text' => __( 'Click here to connect your Kit account.', 'convertkit' ),
247 'instruction_text' => __( 'Connect your Kit account at Settings > Kit, and then refresh this page to configure broadcasts to display.', 'convertkit' ),
248 ),
249 'no_resources' => array(
250 'notice' => __( 'No broadcasts exist in Kit.', 'convertkit' ),
251 'link' => convertkit_get_new_broadcast_url(),
252 'link_text' => __( 'Click here to send your first broadcast.', 'convertkit' ),
253 'instruction_text' => __( 'Add a broadcast to your Kit account, and then refresh this page to configure broadcasts to display.', 'convertkit' ),
254 ),
255
256 // Whether an API Key exists in the Plugin, and are the required resources (broadcasts) available.
257 // If no API Key is specified in the Plugin's settings, render the "No API Key" output.
258 'has_access_token' => $settings->has_access_and_refresh_token(),
259 'has_resources' => $posts->exist(),
260 );
261
262 }
263
264 /**
265 * Returns this block's Attributes
266 *
267 * @since 1.9.7.4
268 */
269 public function get_attributes() {
270
271 return array(
272 // Block attributes.
273 'display_grid' => array(
274 'type' => 'boolean',
275 'default' => $this->get_default_value( 'display_grid' ),
276 ),
277 'display_order' => array(
278 'type' => 'string',
279 'default' => $this->get_default_value( 'display_order' ),
280 ),
281 'date_format' => array(
282 'type' => 'string',
283 'default' => $this->get_default_value( 'date_format' ),
284 ),
285 'display_image' => array(
286 'type' => 'boolean',
287 'default' => $this->get_default_value( 'display_image' ),
288 ),
289 'display_description' => array(
290 'type' => 'boolean',
291 'default' => $this->get_default_value( 'display_description' ),
292 ),
293 'display_read_more' => array(
294 'type' => 'boolean',
295 'default' => $this->get_default_value( 'display_read_more' ),
296 ),
297 'read_more_label' => array(
298 'type' => 'string',
299 'default' => $this->get_default_value( 'read_more_label' ),
300 ),
301 'limit' => array(
302 'type' => 'number',
303 'default' => $this->get_default_value( 'limit' ),
304 ),
305 'page' => array(
306 'type' => 'number',
307 'default' => $this->get_default_value( 'page' ),
308 ),
309 'paginate' => array(
310 'type' => 'boolean',
311 'default' => false,
312 ),
313 'paginate_label_prev' => array(
314 'type' => 'string',
315 'default' => $this->get_default_value( 'paginate_label_prev' ),
316 ),
317 'paginate_label_next' => array(
318 'type' => 'string',
319 'default' => $this->get_default_value( 'paginate_label_next' ),
320 ),
321
322 // get_supports() style, color and typography attributes.
323 'style' => array(
324 'type' => 'object',
325 ),
326 'backgroundColor' => array(
327 'type' => 'string',
328 ),
329 'textColor' => array(
330 'type' => 'string',
331 ),
332 'fontSize' => array(
333 'type' => 'string',
334 ),
335
336 // Always required for Gutenberg.
337 'is_gutenberg_example' => array(
338 'type' => 'boolean',
339 'default' => false,
340 ),
341 );
342
343 }
344
345 /**
346 * Returns this block's supported built-in Attributes.
347 *
348 * @since 1.9.7.4
349 *
350 * @return array Supports
351 */
352 public function get_supports() {
353
354 return array(
355 'className' => true,
356 'color' => array(
357 'link' => true,
358 'background' => true,
359 'text' => true,
360 ),
361 'typography' => array(
362 'fontSize' => true,
363 'lineHeight' => true,
364 ),
365 'spacing' => array(
366 'margin' => true,
367 'padding' => true,
368 ),
369 );
370
371 }
372
373 /**
374 * Returns this block's Fields
375 *
376 * @since 1.9.7.4
377 *
378 * @return bool|array
379 */
380 public function get_fields() {
381
382 return array(
383 'display_grid' => array(
384 'label' => __( 'Display as grid', 'convertkit' ),
385 'type' => 'toggle',
386 'description' => __( 'If enabled, displays broadcasts in a grid, instead of a list.', 'convertkit' ),
387 ),
388 'display_order' => array(
389 'label' => __( 'Display order', 'convertkit' ),
390 'type' => 'select',
391 'values' => array(
392 'date-broadcast' => __( 'Date, Broadcast', 'convertkit' ),
393 'broadcast-date' => __( 'Broadcast, Date', 'convertkit' ),
394 ),
395 ),
396 'date_format' => array(
397 'label' => __( 'Date format', 'convertkit' ),
398 'type' => 'select',
399 'values' => array(
400 'F j, Y' => date_i18n( 'F j, Y', strtotime( 'now' ) ),
401 'Y-m-d' => date_i18n( 'Y-m-d', strtotime( 'now' ) ),
402 'm/d/Y' => date_i18n( 'm/d/Y', strtotime( 'now' ) ),
403 'd/m/Y' => date_i18n( 'd/m/Y', strtotime( 'now' ) ),
404 ),
405 ),
406 'display_image' => array(
407 'label' => __( 'Display images', 'convertkit' ),
408 'type' => 'toggle',
409 ),
410 'display_description' => array(
411 'label' => __( 'Display descriptions', 'convertkit' ),
412 'type' => 'toggle',
413 ),
414 'display_read_more' => array(
415 'label' => __( 'Display read more links', 'convertkit' ),
416 'type' => 'toggle',
417 ),
418 'read_more_label' => array(
419 'label' => __( 'Read more label', 'convertkit' ),
420 'type' => 'text',
421 'description' => __( 'The label to display for the "read more" link below each broadcast.', 'convertkit' ),
422 'display_if' => array(
423 'key' => 'display_read_more',
424 'value' => 1,
425 ),
426 ),
427 'limit' => array(
428 'label' => __( 'Number of posts', 'convertkit' ),
429 'type' => 'number',
430 'min' => 1,
431 'max' => 999,
432 'step' => 1,
433 ),
434 'paginate' => array(
435 'label' => __( 'Display pagination', 'convertkit' ),
436 'type' => 'toggle',
437 'description' => __( 'If the number of broadcasts exceeds the "Number of posts" settings above, previous/next pagination links will be displayed.', 'convertkit' ),
438 ),
439 'paginate_label_prev' => array(
440 'label' => __( 'Newer posts label', 'convertkit' ),
441 'type' => 'text',
442 'description' => __( 'The label to display for the link to newer broadcasts.', 'convertkit' ),
443 'display_if' => array(
444 'key' => 'paginate',
445 'value' => 1,
446 ),
447 ),
448 'paginate_label_next' => array(
449 'label' => __( 'Older posts label', 'convertkit' ),
450 'type' => 'text',
451 'description' => __( 'The label to display for the link to older broadcasts.', 'convertkit' ),
452 'display_if' => array(
453 'key' => 'paginate',
454 'value' => 1,
455 ),
456 ),
457
458 // These fields will only display on the shortcode, and are deliberately not registered in get_attributes(),
459 // because Gutenberg will register its own color pickers for link, background and text.
460 'link_color' => array(
461 'label' => __( 'Link color', 'convertkit' ),
462 'type' => 'color',
463 ),
464 'background_color' => array(
465 'label' => __( 'Background color', 'convertkit' ),
466 'type' => 'color',
467 ),
468 'text_color' => array(
469 'label' => __( 'Text color', 'convertkit' ),
470 'type' => 'color',
471 ),
472 );
473
474 }
475
476 /**
477 * Returns this block's UI panels / sections.
478 *
479 * @since 1.9.7.4
480 *
481 * @return bool|array
482 */
483 public function get_panels() {
484
485 return array(
486 'general' => array(
487 'label' => __( 'General', 'convertkit' ),
488 'fields' => array(
489 'display_grid',
490 'display_order',
491 'date_format',
492 'display_image',
493 'display_description',
494 'display_read_more',
495 'read_more_label',
496 ),
497 ),
498 'pagination' => array(
499 'label' => __( 'Pagination', 'convertkit' ),
500 'fields' => array(
501 'limit',
502 'paginate',
503 'paginate_label_prev',
504 'paginate_label_next',
505 ),
506 ),
507 'styles' => array(
508 'label' => __( 'Styles', 'convertkit' ),
509 'fields' => array(
510 'link_color',
511 'background_color',
512 'text_color',
513 ),
514 ),
515 );
516
517 }
518
519 /**
520 * Returns this block's Default Values
521 *
522 * @since 1.9.7.4
523 *
524 * @return array
525 */
526 public function get_default_values() {
527
528 return array(
529 'display_grid' => false,
530 'display_order' => 'date-broadcast',
531 'date_format' => 'F j, Y',
532 'display_image' => false,
533 'display_description' => false,
534 'display_read_more' => false,
535 'read_more_label' => __( 'Read more', 'convertkit' ),
536 'limit' => 10,
537 'paginate' => false,
538 'paginate_label_prev' => __( 'Previous', 'convertkit' ),
539 'paginate_label_next' => __( 'Next', 'convertkit' ),
540 'link_color' => '',
541 'background_color' => '',
542 'text_color' => '',
543
544 // Built-in Gutenberg block attributes.
545 'style' => '',
546 'backgroundColor' => '',
547 'textColor' => '',
548
549 // Not output as a block option, but stores the page requested by the user if using pagination without JS.
550 'page' => $this->get_page(),
551 );
552
553 }
554
555 /**
556 * Returns the block's output, based on the supplied configuration attributes.
557 *
558 * @since 1.9.7.4
559 *
560 * @param array $atts Block / Shortcode / Page Builder Module Attributes.
561 * @return string
562 */
563 public function render( $atts ) {
564
565 // Parse attributes, defining fallback defaults if required
566 // and moving some attributes (such as Gutenberg's styles), if defined.
567 $atts = $this->sanitize_and_declare_atts( $atts );
568
569 // Setup Settings class.
570 $settings = new ConvertKit_Settings();
571
572 // Fetch Posts.
573 $posts = new ConvertKit_Resource_Posts( 'output_broadcasts' );
574
575 // If no Posts exist, bail.
576 if ( ! $posts->exist() ) {
577 if ( $settings->debug_enabled() ) {
578 return '<!-- ' . __( 'No Broadcasts exist in Kit.', 'convertkit' ) . ' -->';
579 }
580
581 return '';
582 }
583
584 // Build HTML.
585 if ( $this->is_block_editor_request() ) {
586 // For the block editor, don't include compiled CSS classes and styles,
587 // as the block editor will add these to the parent container.
588 // Otherwise the block will render incorrectly with double padding, double margins etc.
589 $html = $this->build_html(
590 $posts,
591 $atts,
592 true,
593 array(
594 'convertkit-' . $this->get_name(),
595 )
596 );
597 } else {
598 $html = $this->build_html(
599 $posts,
600 $atts,
601 true,
602 $this->get_css_classes(),
603 $this->get_css_styles( $atts )
604 );
605 }
606
607 /**
608 * Filter the block's content immediately before it is output.
609 *
610 * @since 1.9.7.4
611 *
612 * @param string $html ConvertKit Broadcasts HTML.
613 * @param array $atts Block Attributes.
614 */
615 $html = apply_filters( 'convertkit_block_broadcasts_render', $html, $atts );
616
617 return $html;
618
619 }
620
621 /**
622 * Returns the block's output, based on the supplied configuration attributes,
623 * when requested via AJAX.
624 *
625 * @since 1.9.7.6
626 *
627 * @param WP_REST_Request $request The REST request.
628 * @return string
629 */
630 public function render_ajax( $request ) {
631
632 // Build attributes array.
633 $atts = array(
634 'date_format' => $request->get_param( 'date_format' ),
635 'display_image' => $request->get_param( 'display_image' ),
636 'display_description' => $request->get_param( 'display_description' ),
637 'display_read_more' => $request->get_param( 'display_read_more' ),
638 'read_more_label' => $request->get_param( 'read_more_label' ),
639 'limit' => $request->get_param( 'limit' ),
640 'page' => $request->get_param( 'page' ),
641 'paginate' => $request->get_param( 'paginate' ),
642 'paginate_label_next' => $request->get_param( 'paginate_label_next' ),
643 'paginate_label_prev' => $request->get_param( 'paginate_label_prev' ),
644 'link_color' => $request->get_param( 'link_color' ),
645 );
646
647 // Parse attributes, defining fallback defaults if required
648 // and moving some attributes (such as Gutenberg's styles), if defined.
649 $atts = $this->sanitize_and_declare_atts( $atts );
650
651 // Setup Settings class.
652 $settings = new ConvertKit_Settings();
653
654 // Fetch Posts.
655 $posts = new ConvertKit_Resource_Posts( 'output_broadcasts' );
656
657 // If no Posts exist, bail.
658 if ( ! $posts->exist() ) {
659 if ( $settings->debug_enabled() ) {
660 return '<!-- ' . __( 'No Broadcasts exist in Kit.', 'convertkit' ) . ' -->';
661 }
662
663 return '';
664 }
665
666 // Build HTML.
667 $html = $this->build_html( $posts, $atts, false );
668
669 /**
670 * Filter the block's inner content immediately before it is output by AJAX,
671 * which occurs when pagination was clicked.
672 *
673 * @since 1.9.7.6
674 *
675 * @param string $html ConvertKit Broadcasts HTML.
676 * @param array $atts Block Attributes.
677 */
678 $html = apply_filters( 'convertkit_block_broadcasts_render_ajax', $html, $atts );
679
680 return $html;
681
682 }
683
684 /**
685 * Returns a HTML list of ConvertKit broadcasts, honoring the supplied
686 * attribute's current requested page and limit.
687 *
688 * @since 1.9.7.4
689 *
690 * @param ConvertKit_Resource_Posts $posts ConvertKit Posts Resource class.
691 * @param array $atts Block attributes.
692 * @param bool $include_container Include container div in HTML.
693 * @param array $css_classes CSS classes to apply to block.
694 * @param array $css_styles CSS inline styles to apply to block.
695 * @return string
696 */
697 private function build_html( $posts, $atts, $include_container = true, $css_classes = array(), $css_styles = array() ) {
698
699 // Get paginated subset of Posts.
700 $broadcasts = $posts->get_paginated_subset( $atts['page'], $atts['limit'] );
701
702 // Define a nonce to ensure requests made for paginated broadcasts are protected against e.g. CSRF attacks.
703 $nonce = wp_create_nonce( 'convertkit-broadcasts' );
704
705 // Define HTML string.
706 $html = '';
707
708 // Include container, if required.
709 if ( $include_container ) {
710 $html .= '<div class="' . implode( ' ', map_deep( $css_classes, 'sanitize_html_class' ) ) . '" style="' . implode( ';', map_deep( $css_styles, 'esc_attr' ) ) . '" ' . $this->get_atts_as_html_data_attributes( $atts ) . '>';
711 }
712
713 // Start list.
714 $html .= '<ul class="convertkit-broadcasts-list">';
715
716 // Iterate through broadcasts, building HTML list items.
717 foreach ( $broadcasts['items'] as $count => $broadcast ) {
718 // Add broadcast as list item.
719 $html .= $this->build_html_list_item( $broadcast, $atts );
720 }
721
722 // End list.
723 $html .= '</ul>';
724
725 // If pagination is disabled, return the output now.
726 if ( ! $atts['paginate'] ) {
727 // Close container div, if required.
728 if ( $include_container ) {
729 $html .= '</div>';
730 }
731
732 return $html;
733 }
734
735 // If no next or previous page exists, just return the output.
736 if ( ! $broadcasts['has_next_page'] && ! $broadcasts['has_prev_page'] ) {
737 // Close container div, if required.
738 if ( $include_container ) {
739 $html .= '</div>';
740 }
741
742 return $html;
743 }
744
745 // Append pagination.
746 $html .= '<ul class="convertkit-broadcasts-pagination">
747 <li class="convertkit-broadcasts-pagination-prev">' . ( $broadcasts['has_prev_page'] ? $this->get_pagination_link_prev_html( $atts, $nonce ) : '' ) . '</li>
748 <li class="convertkit-broadcasts-pagination-next">' . ( $broadcasts['has_next_page'] ? $this->get_pagination_link_next_html( $atts, $nonce ) : '' ) . '</li>
749 </ul>';
750
751 // Close container div, if required.
752 if ( $include_container ) {
753 $html .= '</div>';
754 }
755
756 /**
757 * Filter the block's content immediately before it is output.
758 *
759 * @since 2.2.3
760 *
761 * @param string $html ConvertKit Broadcasts HTML.
762 * @param array $atts Block Attributes.
763 */
764 $html = apply_filters( 'convertkit_block_broadcasts_render', $html, $atts );
765
766 // Return.
767 return $html;
768
769 }
770
771 /**
772 * Defines the HTML for an individual broadcast item in the Broadcasts block.
773 *
774 * @since 2.2.3
775 *
776 * @param array $broadcast Broadcast.
777 * @param array $atts Block attributes.
778 * @return string HTML
779 */
780 private function build_html_list_item( $broadcast, $atts ) {
781
782 // Convert UTC date to timestamp.
783 $date_timestamp = strtotime( $broadcast['published_at'] );
784
785 // Build broadcast URL.
786 $url = add_query_arg(
787 array(
788 'utm_source' => 'wordpress',
789 'utm_term' => get_locale(),
790 'utm_content' => 'convertkit',
791 ),
792 $broadcast['url']
793 );
794
795 // Build HTML.
796 $html = '<li class="convertkit-broadcast">';
797
798 // Display date.
799 $html .= '<time datetime="' . esc_attr( date_i18n( 'Y-m-d', $date_timestamp ) ) . '">' . esc_html( date_i18n( $atts['date_format'], $date_timestamp ) ) . '</time>';
800
801 // Display linked title.
802 $html .= '<a href="' . esc_url( $url ) . '" target="_blank" rel="nofollow noopener"' . $this->get_link_style_tag( $atts ) . ' class="convertkit-broadcast-title">' . esc_html( $broadcast['title'] ) . '</a>';
803
804 // Display image.
805 // We check for thumbnail_url, as these were added to the API in https://github.com/ConvertKit/convertkit/pull/23938,
806 // and might not immediately be available until the resources are refreshed.
807 if ( $atts['display_image'] && array_key_exists( 'thumbnail_url', $broadcast ) && ! is_null( $broadcast['thumbnail_url'] ) ) {
808 $html .= '<a href="' . esc_url( $url ) . '" target="_blank" rel="nofollow noopener" class="convertkit-broadcast-image">
809 <img src="' . esc_url( $broadcast['thumbnail_url'] ) . '" alt="' . esc_attr( $broadcast['thumbnail_alt'] ) . '" />
810 </a>';
811 }
812
813 // Display description / read more.
814 if ( $atts['display_description'] || $atts['display_read_more'] ) {
815 $html .= '<span class="convertkit-broadcast-text">';
816
817 // Display description.
818 // We check for description, as these were added to the API in https://github.com/ConvertKit/convertkit/pull/23938,
819 // and might not immediately be available until the resources are refreshed.
820 if ( $atts['display_description'] && array_key_exists( 'description', $broadcast ) && ! is_null( $broadcast['description'] ) ) {
821 $html .= '<span class="convertkit-broadcast-description">' . esc_html( $broadcast['description'] ) . '</span>';
822 }
823
824 // Display read more link.
825 if ( $atts['display_read_more'] ) {
826 $html .= '<a href="' . esc_url( $url ) . '" target="_blank" rel="nofollow noopener" class="convertkit-broadcast-read-more">' . esc_html( $atts['read_more_label'] ) . '</a>';
827 }
828
829 $html .= '</span>';
830 }
831
832 // Close list item.
833 $html .= '</li>';
834
835 /**
836 * Defines the HTML for an individual broadcast item in the Broadcasts block.
837 *
838 * @since 2.2.3
839 *
840 * @param string $html HTML.
841 * @param array $broadcast Broadcast.
842 * @param array $atts Block attributes.
843 * @return string HTML
844 */
845 $html = apply_filters( 'convertkit_block_broadcasts_build_html_list_item', $html, $broadcast, $atts );
846
847 return $html;
848
849 }
850
851 /**
852 * Returns the HTML link to paginate to the previous page, to view
853 * newer broadcasts.
854 *
855 * @since 1.9.7.6
856 *
857 * @param array $atts Block attributes.
858 * @param string $nonce Nonce.
859 * @return string HTML Link
860 */
861 private function get_pagination_link_prev_html( $atts, $nonce ) {
862
863 return '<a href="' . esc_url( $this->get_pagination_link( $atts['page'] - 1, $nonce ) ) . '" title="' . esc_attr( $atts['paginate_label_prev'] ) . '" data-page="' . esc_attr( (string) ( $atts['page'] - 1 ) ) . '" data-nonce="' . esc_attr( $nonce ) . '"' . $this->get_link_style_tag( $atts ) . '>
864 ' . esc_html( $atts['paginate_label_prev'] ) . '
865 </a>';
866
867 }
868
869 /**
870 * Returns the HTML link to paginate to the next page, to view
871 * older broadcasts.
872 *
873 * @since 1.9.7.6
874 *
875 * @param array $atts Block attributes.
876 * @param string $nonce Nonce.
877 * @return string HTML Link
878 */
879 private function get_pagination_link_next_html( $atts, $nonce ) {
880
881 return '<a href="' . esc_url( $this->get_pagination_link( $atts['page'] + 1, $nonce ) ) . '" title="' . esc_attr( $atts['paginate_label_next'] ) . '" data-page="' . esc_attr( (string) ( $atts['page'] + 1 ) ) . '" data-nonce="' . esc_attr( $nonce ) . '"' . $this->get_link_style_tag( $atts ) . '>
882 ' . esc_html( $atts['paginate_label_next'] ) . '
883 </a>';
884
885 }
886
887 /**
888 * Returns the link to paginate to the specified page.
889 *
890 * @since 1.9.7.6
891 *
892 * @param int $page Page Number.
893 * @param string $nonce Nonce.
894 * @return string URL
895 */
896 private function get_pagination_link( $page, $nonce ) {
897
898 global $post, $wp;
899
900 // Determine the base Permalink, depending on whether we're viewing an individual Page/Post or not.
901 if ( ! is_null( $post ) ) {
902 $permalink = get_permalink( $post->ID );
903 } else {
904 // Fallback to WordPress' request object to identify the current slug, as we are not viewing
905 // an individual Page or Post e.g. we're on the Home Page and this block is in a footer widget.
906 $permalink = home_url( $wp->request );
907 }
908
909 return add_query_arg(
910 array(
911 'convertkit-broadcasts-page' => absint( $page ),
912 'convertkit-broadcasts-nonce' => $nonce,
913 ),
914 $permalink
915 );
916
917 }
918
919 /**
920 * Returns the current pagination page requested for broadcasts.
921 *
922 * @since 1.9.7.6
923 *
924 * @return int Page
925 */
926 private function get_page() {
927
928 // Assume we're requesting the first page.
929 $page = 1;
930
931 // Return first page number if no nonce exists.
932 if ( ! array_key_exists( 'convertkit-broadcasts-nonce', $_REQUEST ) ) {
933 return $page;
934 }
935
936 // Return first page number if nonce verification fails, as this means we can't reliably trust $_REQUEST['convertkit-broadcasts-page'].
937 if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['convertkit-broadcasts-nonce'] ), 'convertkit-broadcasts' ) ) {
938 return $page;
939 }
940
941 // Return first page number if no specific page was requested.
942 if ( ! isset( $_REQUEST['convertkit-broadcasts-page'] ) ) {
943 return $page;
944 }
945
946 // Return requested page number.
947 return absint( $_REQUEST['convertkit-broadcasts-page'] );
948
949 }
950
951 /**
952 * If a link_color attribute exists in the given array of attributes, we're rendering a shortcode, and therefore
953 * need to include inline styling for links.
954 *
955 * The Gutenberg block doesn't need this, because WordPress generates its own inline styles when a link color is selected.
956 *
957 * @since 1.9.8.5
958 *
959 * @param array $atts Block attributes.
960 * @return string style attribute (blank string if no styles need to be applied)
961 */
962 private function get_link_style_tag( $atts ) {
963
964 if ( ! isset( $atts['link_color'] ) ) {
965 return '';
966 }
967
968 if ( empty( $atts['link_color'] ) ) {
969 return '';
970 }
971
972 return ' style="color:' . esc_attr( $atts['link_color'] ) . '"';
973
974 }
975
976 }
977