PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.4
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.4
3.4.4 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 All 197 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.4, at includes/blocks/class-convertkit-block-broadcasts.php

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