Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 193
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
Newspack_Blocks_API
0.00% covered (danger)
0.00%
0 / 192
0.00% covered (danger)
0.00%
0 / 11
1190
0.00% covered (danger)
0.00%
0 / 1
 newspack_blocks_get_image_src
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
6
 newspack_blocks_get_primary_category
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
42
 newspack_blocks_get_cat_tag_classes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 newspack_blocks_sponsor_info
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
20
 newspack_blocks_get_tag_labels
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 newspack_blocks_has_custom_excerpt
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 register_video_playlist_endpoint
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 video_playlist_endpoint
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 posts_endpoint
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 1
110
 specific_posts_endpoint
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
20
 add_post_title_wildcard_search
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Register Newspack Blocks rest fields
4 *
5 * @package Newspack_Blocks
6 */
7
8/**
9 * `Newspack_Blocks_API` is a wrapper for `register_rest_fields()`
10 */
11class Newspack_Blocks_API {
12    /**
13     * Get thumbnail featured image source for the rest field.
14     *
15     * @param array $object_info The object info.
16     * @return array | bool Featured image if available, false if not.
17     */
18    public static function newspack_blocks_get_image_src( $object_info ) {
19        $featured_image_set = [];
20
21        if ( 0 === $object_info['featured_media'] ) {
22            return false;
23        }
24
25        // Large image.
26        $feat_img_array_large        = wp_get_attachment_image_src(
27            $object_info['featured_media'],
28            'large',
29            false
30        );
31        $featured_image_set['large'] = $feat_img_array_large[0] ?? null;
32
33        // Landscape image.
34        $landscape_size = Newspack_Blocks::image_size_for_orientation( 'landscape' );
35
36        $feat_img_array_landscape        = wp_get_attachment_image_src(
37            $object_info['featured_media'],
38            $landscape_size,
39            false
40        );
41        $featured_image_set['landscape'] = $feat_img_array_landscape[0] ?? null;
42
43        // Portrait image.
44        $portrait_size = Newspack_Blocks::image_size_for_orientation( 'portrait' );
45
46        $feat_img_array_portrait        = wp_get_attachment_image_src(
47            $object_info['featured_media'],
48            $portrait_size,
49            false
50        );
51        $featured_image_set['portrait'] = $feat_img_array_portrait[0] ?? null;
52
53        // Square image.
54        $square_size = Newspack_Blocks::image_size_for_orientation( 'square' );
55
56        $feat_img_array_square        = wp_get_attachment_image_src(
57            $object_info['featured_media'],
58            $square_size,
59            false
60        );
61        $featured_image_set['square'] = $feat_img_array_square[0] ?? null;
62
63        // Uncropped image.
64        $uncropped_size = 'newspack-article-block-uncropped';
65
66        $feat_img_array_uncropped        = wp_get_attachment_image_src(
67            $object_info['featured_media'],
68            $uncropped_size,
69            false
70        );
71        $featured_image_set['uncropped'] = $feat_img_array_uncropped[0] ?? null;
72
73        return $featured_image_set;
74    }
75
76    /**
77     * Get primary category for the rest field.
78     *
79     * @param array $object_info The object info.
80     * @return string Category name.
81     */
82    public static function newspack_blocks_get_primary_category( $object_info ) {
83        $category = false;
84
85        // Use Yoast primary category if set.
86        if ( class_exists( 'WPSEO_Primary_Term' ) ) {
87            $primary_term = new WPSEO_Primary_Term( 'category', $object_info['id'] );
88            $category_id  = $primary_term->get_primary_term();
89            if ( $category_id ) {
90                $category = get_term( $category_id );
91            }
92        }
93
94        if ( ! $category ) {
95            $categories_list = get_the_category( $object_info['id'] );
96            if ( ! empty( $categories_list ) ) {
97                $category = $categories_list[0];
98            }
99        }
100
101        if ( ! $category ) {
102            return '';
103        }
104
105        $linked_category = '<a href="#">' . $category->name . '</a>';
106
107        return apply_filters( 'newspack_blocks_categories', $linked_category );
108    }
109
110    /**
111     * Get a list of category, tag classes for the rest field.
112     *
113     * @param array $object_info The object info.
114     * @return string classes from assigned categories and tags.
115     */
116    public static function newspack_blocks_get_cat_tag_classes( $object_info ) {
117        return Newspack_Blocks::get_term_classes( $object_info['id'] );
118    }
119
120    /**
121     * Get all sponsor information for the rest field.
122     *
123     * @param array $object_info The object info.
124     * @return array sponsor information.
125     */
126    public static function newspack_blocks_sponsor_info( $object_info ) {
127        $sponsors = Newspack_Blocks::get_all_sponsors(
128            $object_info['id'],
129            'native',
130            'post',
131            array(
132                'maxwidth'  => 80,
133                'maxheight' => 40,
134            )
135        );
136        if ( ! empty( $sponsors ) ) {
137            $sponsor_info = [];
138            foreach ( $sponsors as $sponsor ) {
139                $sponsor_info_item = [
140                    'flag'          => $sponsor['sponsor_flag'],
141                    'sponsor_name'  => $sponsor['sponsor_name'],
142                    'sponsor_url'   => $sponsor['sponsor_url'],
143                    'byline_prefix' => $sponsor['sponsor_byline'],
144                    'id'            => $sponsor['sponsor_id'],
145                    'scope'         => $sponsor['sponsor_scope'],
146                ];
147                if ( ! empty( $sponsor['sponsor_logo'] ) ) {
148                    $sponsor_info_item['src']        = $sponsor['sponsor_logo']['src'];
149                    $sponsor_info_item['img_width']  = $sponsor['sponsor_logo']['img_width'];
150                    $sponsor_info_item['img_height'] = $sponsor['sponsor_logo']['img_height'];
151                }
152                $sponsor_info[] = $sponsor_info_item;
153            }
154            return $sponsor_info;
155        }
156
157        return false;
158    }
159
160    /**
161     * Get tag labels for the REST API.
162     *
163     * @param  array $object_info The object info.
164     * @return array|bool Tag labels, or false if none.
165     */
166    public static function newspack_blocks_get_tag_labels( $object_info ) {
167        $tag_labels = Newspack_Blocks::get_tag_labels( $object_info['id'] );
168        return ! empty( $tag_labels ) ? array_values( $tag_labels ) : false;
169    }
170
171    /**
172     * Pass whether there is a custom excerpt to the editor.
173     *
174     * @param array $object_info The object info.
175     * @return boolean custom excerpt status.
176     */
177    public static function newspack_blocks_has_custom_excerpt( $object_info ) {
178        $post_has_custom_excerpt = has_excerpt( $object_info['id'] );
179        return $post_has_custom_excerpt;
180    }
181
182    /**
183     * Register the video-playlist endpoint.
184     */
185    public static function register_video_playlist_endpoint() {
186        register_rest_route(
187            'newspack-blocks/v1',
188            '/video-playlist',
189            [
190                'methods'             => 'GET',
191                'callback'            => [ 'Newspack_Blocks_API', 'video_playlist_endpoint' ],
192                'permission_callback' => function() {
193                    return current_user_can( 'edit_posts' );
194                },
195            ]
196        );
197    }
198
199    /**
200     * Process requests to the video-playlist endpoint.
201     *
202     * @param WP_REST_Request $request Request object.
203     * @return WP_REST_Response.
204     */
205    public static function video_playlist_endpoint( $request ) {
206        $args = $request->get_params();
207        return new \WP_REST_Response( newspack_blocks_get_video_playlist( $args ), 200 );
208    }
209
210    /**
211     * Posts endpoint
212     *
213     * @param WP_REST_Request $request Request object.
214     * @return WP_REST_Response.
215     */
216    public static function posts_endpoint( $request ) {
217        $attributes = $request->get_params();
218        $args       = Newspack_Blocks::build_articles_query( $attributes, apply_filters( 'newspack_blocks_block_name', 'newspack-blocks/homepage-articles' ) );
219
220        if ( $attributes['exclude'] && count( $attributes['exclude'] ) ) {
221            $args['post__not_in'] = $attributes['exclude']; // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in
222        }
223
224        if ( $attributes['include'] && count( $attributes['include'] ) ) {
225            $args['post__in'] = $attributes['include'];
226            $args['orderby']  = 'post__in';
227            $args['order']    = 'ASC';
228        }
229
230        if ( isset( $attributes['showExcerpt'], $attributes['excerptLength'] ) ) {
231            $block_attributes = [
232                'showExcerpt'   => $attributes['showExcerpt'],
233                'excerptLength' => $attributes['excerptLength'],
234            ];
235            Newspack_Blocks::filter_excerpt( $block_attributes );
236        }
237
238        $query = new WP_Query( $args );
239        $posts = [];
240
241        foreach ( $query->posts as $post ) {
242            $GLOBALS['post'] = $post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
243            setup_postdata( $post );
244
245            // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
246            $excerpt = apply_filters( 'get_the_excerpt', $post->post_excerpt, $post );
247            $excerpt = apply_filters( 'the_excerpt', $excerpt );
248            $content = apply_filters( 'the_content', $post->post_content );
249            // phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
250
251            $meta = new WP_REST_Post_Meta_Fields( 'post' );
252            $data = [
253                'author'              => (int) $post->post_author,
254                'content'             => [
255                    'rendered' => post_password_required( $post ) ? '' : $content,
256                ],
257                'date'                => Newspack_Blocks::get_displayed_post_date( $post ),
258                'date_formatted'      => Newspack_Blocks::get_formatted_displayed_post_date( $post ),
259                'article_meta_footer' => Newspack_Blocks::get_article_meta_footer( $post ),
260                'excerpt'             => [
261                    'rendered' => post_password_required( $post ) ? '' : $excerpt,
262                ],
263                'full_content'        => get_the_content( $post->ID ),
264                'featured_media'      => (int) get_post_thumbnail_id( $post->ID ),
265                'id'                  => $post->ID,
266                'meta'                => $meta->get_value( $post->ID, $request ),
267                'title'               => [
268                    'rendered' => get_the_title( $post->ID ),
269                ],
270            ];
271
272            $sponsors = Newspack_Blocks::get_all_sponsors( $post->ID );
273            $author_info = Newspack_Blocks::prepare_authors();
274            $add_ons  = [
275                'newspack_article_classes'          => Newspack_Blocks::get_term_classes( $data['id'] ),
276                'newspack_category_info'            => self::newspack_blocks_get_primary_category( $data ),
277                'newspack_featured_image_caption'   => Newspack_Blocks::get_image_caption( $data['featured_media'], $attributes['showCaption'], $attributes['showCredit'] ),
278                'newspack_featured_image_src'       => self::newspack_blocks_get_image_src( $data ),
279                'newspack_has_custom_excerpt'       => self::newspack_blocks_has_custom_excerpt( $data ),
280                'newspack_post_sponsors'            => self::newspack_blocks_sponsor_info( $data ),
281                'newspack_sponsors_show_author'     => Newspack_Blocks::newspack_display_sponsors_and_authors( $sponsors ),
282                'newspack_sponsors_show_categories' => Newspack_Blocks::newspack_display_sponsors_and_categories( $sponsors ),
283                'newspack_tag_labels'               => self::newspack_blocks_get_tag_labels( $data ),
284                'newspack_post_avatars'             => \newspack_blocks_format_avatars( $author_info ),
285                'newspack_post_byline'              => \newspack_blocks_format_byline( $author_info ),
286                'post_status'                       => $post->post_status,
287                'post_type'                         => $post->post_type,
288                'post_link'                         => Newspack_Blocks::get_post_link( $post->ID ),
289            ];
290
291            // Support Newspack Listings hide author/publish date options.
292            if ( class_exists( 'Newspack_Listings\Core' ) ) {
293                $add_ons['newspack_listings_hide_author']       = apply_filters( 'newspack_listings_hide_author', false ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
294                $add_ons['newspack_listings_hide_publish_date'] = apply_filters( 'newspack_listings_hide_publish_date', false ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
295            }
296
297            $posts[] = array_merge( $data, $add_ons );
298        }
299
300        Newspack_Blocks::remove_excerpt_filter();
301
302        return new \WP_REST_Response( $posts );
303    }
304
305    /**
306     * Lookup individual posts by title only.
307     *
308     * @param WP_REST_Request $request Request object.
309     * @return WP_REST_Response.
310     */
311    public static function specific_posts_endpoint( $request ) {
312        $params = $request->get_params();
313        if ( empty( $params['search'] ) ) {
314            return new \WP_REST_Response( [] );
315        }
316        add_filter( 'posts_where', [ 'Newspack_Blocks_API', 'add_post_title_wildcard_search' ], 10, 2 );
317
318        $args = [
319            'post_status'           => 'publish',
320            'title_wildcard_search' => esc_sql( $params['search'] ),
321            'posts_per_page'        => $params['postsToShow'],
322        ];
323
324        if ( $params['postType'] && count( $params['postType'] ) ) {
325            $args['post_type'] = $params['postType'];
326        } else {
327            $args['post_type'] = 'post';
328        }
329
330        $query = new WP_Query( $args );
331        remove_filter( 'posts_where', [ 'Newspack_Blocks_API', 'add_post_title_wildcard_search' ], 10, 2 );
332        return new \WP_REST_Response(
333            array_map(
334                function( $post ) {
335                    return [
336                        'id'    => $post->ID,
337                        'title' => $post->post_title,
338                    ];
339                },
340                $query->posts
341            ),
342            200
343        );
344    }
345
346    /**
347     * Add title wildcard search to post lookup query.
348     *
349     * @param String   $where Where clause.
350     * @param WP_Query $query The query.
351     */
352    public static function add_post_title_wildcard_search( $where, $query ) {
353        $search = ! empty( $query->query['title_wildcard_search'] ) ? $query->query['title_wildcard_search'] : null;
354        $where .= ' AND post_title LIKE "%' . $search . '%" ';
355        return $where;
356    }
357}
358
359add_action( 'rest_api_init', array( 'Newspack_Blocks_API', 'register_video_playlist_endpoint' ) );