PluginProbe
Category Posts Block / 5.1.0
Category Posts Block v5.1.0
5.1.0 5.0.0 trunk 1.0 1.1 1.2 1.2.1 1.3 1.3.1 1.3.2 1.3.3 2.0 2.1 2.2 2.3 3.1 3.2 3.3 4.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.1.5 All 63 releases
category-posts / cat-posts-block.php

cat-posts-block.php in Category Posts Block 5.1.0, at cat-posts-block.php

328 lines 15.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Gutenberg Block implementation.
4 *
5 * @package categoryposts.
6 *
7 * @since 4.9
8 */
9
10 namespace categoryPosts;
11
12 /**
13 * Build the widget instance data from block attributes.
14 *
15 * @param array $attributes The block attributes.
16 *
17 * @return array The widget instance data.
18 */
19 function build_block_instance( $attributes ) {
20 $instance = array();
21
22 if ( ! is_array( $attributes ) ) {
23 return $instance;
24 }
25
26 $instance['title'] = isset( $attributes['title'] ) ? $attributes['title'] : '';
27 $instance['title_link'] = isset( $attributes['titleLink'] ) ? $attributes['titleLink'] : false;
28 $instance['title_link_target'] = isset( $attributes['titleLinkTarget'] ) ? $attributes['titleLinkTarget'] : false;
29 $instance['title_level'] = isset( $attributes['titleLevel'] ) ? $attributes['titleLevel'] : 'Initial';
30 $instance['title_link_url'] = isset( $attributes['titleLinkUrl'] ) ? $attributes['titleLinkUrl'] : '';
31 $instance['hide_title'] = isset( $attributes['hideTitle'] ) ? $attributes['hideTitle'] : false;
32 $instance['category_suggestions'] = isset( $attributes['categorySuggestions'] ) ? $attributes['categorySuggestions'] : array();
33 $instance['select_categories'] = isset( $attributes['selectCategories'] ) ? $attributes['selectCategories'] : array();
34 $instance['cat'] = isset( $attributes['categories'] ) ? $attributes['categories'] : '';
35 $instance['num'] = isset( $attributes['num'] ) ? $attributes['num'] : 10;
36 $instance['offset'] = isset( $attributes['offset'] ) ? $attributes['offset'] : 1;
37 $instance['sort_by'] = isset( $attributes['orderBy'] ) ? $attributes['orderBy'] : 'date';
38 $instance['status'] = isset( $attributes['status'] ) ? $attributes['status'] : 'publish';
39 $instance['asc_sort_order'] = isset( $attributes['order'] ) ? $attributes['order'] : true;
40 $instance['exclude_current_post'] = isset( $attributes['excludeCurrentPost'] ) ? $attributes['excludeCurrentPost'] : false;
41 $instance['hide_no_thumb'] = isset( $attributes['hideNoThumb'] ) ? $attributes['hideNoThumb'] : false;
42 $instance['sticky'] = isset( $attributes['sticky'] ) ? $attributes['sticky'] : false;
43 $instance['footer_link_text'] = isset( $attributes['footerLinkText'] ) ? $attributes['footerLinkText'] : '';
44 $instance['footer_link'] = isset( $attributes['footerLink'] ) ? $attributes['footerLink'] : '';
45 $instance['footer_link_target'] = isset( $attributes['footerLinkTarget'] ) ? $attributes['footerLinkTarget'] : false;
46 $instance['item_title_level'] = isset( $attributes['itemTitleLevel'] ) ? $attributes['itemTitleLevel'] : 'Inline';
47 $instance['item_title_lines'] = isset( $attributes['itemTitleLines'] ) ? $attributes['itemTitleLines'] : 2;
48 $instance['thumb_w'] = isset( $attributes['thumbW'] ) ? $attributes['thumbW'] : 150;
49 $instance['thumb_fluid_width'] = isset( $attributes['thumbFluidWidth'] ) ? $attributes['thumbFluidWidth'] : 100;
50 $instance['thumb_h'] = isset( $attributes['thumbH'] ) ? $attributes['thumbH'] : 150;
51 $instance['thumb_hover'] = isset( $attributes['thumbHover'] ) ? $attributes['thumbHover'] : 'none';
52 $instance['thumb_symbols'] = isset( $attributes['thumbSymbols'] ) ? $attributes['thumbSymbols'] : false;
53 $instance['hide_post_titles'] = isset( $attributes['hidePostTitles'] ) ? $attributes['hidePostTitles'] : false;
54 $instance['excerpt_radio'] = isset( $attributes['excerptRadio'] ) ? $attributes['excerptRadio'] : 'excerpt';
55 $instance['excerpt_lines'] = isset( $attributes['excerptLines'] ) ? $attributes['excerptLines'] : 4;
56 $instance['excerpt_length'] = isset( $attributes['excerptLength'] ) ? $attributes['excerptLength'] : 0;
57 $instance['excerpt_more_text'] = isset( $attributes['excerptMoreText'] ) ? $attributes['excerptMoreText'] : '';
58 $instance['excerpt_filters'] = isset( $attributes['excerptFilters'] ) ? $attributes['excerptFilters'] : false;
59 $instance['comment_num'] = isset( $attributes['commentNum'] ) ? $attributes['commentNum'] : false;
60 $instance['disable_css'] = isset( $attributes['disableCss'] ) ? $attributes['disableCss'] : false;
61 $instance['disable_font_styles'] = isset( $attributes['disableFontStyles'] ) ? $attributes['disableFontStyles'] : false;
62 $instance['disable_theme_styles'] = isset( $attributes['disableThemeStyles'] ) ? $attributes['disableThemeStyles'] : false;
63 $instance['show_post_format'] = isset( $attributes['showPostFormat'] ) ? $attributes['showPostFormat'] : 'none';
64 $instance['no_cat_childs'] = isset( $attributes['noCatChilds'] ) ? $attributes['noCatChilds'] : false;
65 $instance['everything_is_link'] = isset( $attributes['everythingIsLink'] ) ? $attributes['everythingIsLink'] : false;
66 $instance['preset_date_format'] = isset( $attributes['presetDateFormat'] ) ? $attributes['presetDateFormat'] : 'sitedate';
67 $instance['date_format'] = isset( $attributes['dateFormat'] ) ? $attributes['dateFormat'] : '';
68 $instance['date_past_time'] = isset( $attributes['datePastTime'] ) ? $attributes['datePastTime'] : '0';
69 $instance['template'] = isset( $attributes['template'] ) ? $attributes['template'] : "%title%\n\n%thumb%\n%excerpt%";
70 $instance['text_do_not_wrap_thumb'] = isset( $attributes['textDoNotWrapThumb'] ) ? $attributes['textDoNotWrapThumb'] : false;
71 $instance['enable_loadmore'] = isset( $attributes['enableLoadmore'] ) ? $attributes['enableLoadmore'] : false;
72 $instance['loadmore_scrollTo'] = isset( $attributes['loadmoreScrollTo'] ) ? $attributes['loadmoreScrollTo'] : false;
73 $instance['loadmore_text'] = isset( $attributes['loadmoreText'] ) ? $attributes['loadmoreText'] : 'Load More (%step%/%all%)';
74 $instance['loading_text'] = isset( $attributes['loadingText'] ) ? $attributes['loadingText'] : 'Loading...';
75 $instance['date_range'] = isset( $attributes['dateRange'] ) ? $attributes['dateRange'] : 'off';
76 $instance['start_date'] = isset( $attributes['startDate'] ) ? $attributes['startDate'] : '';
77 $instance['end_date'] = isset( $attributes['endDate'] ) ? $attributes['endDate'] : '';
78 $instance['days_ago'] = isset( $attributes['daysAgo'] ) ? $attributes['daysAgo'] : 30;
79 $instance['no_match_handling'] = isset( $attributes['noMatchHandling'] ) ? $attributes['noMatchHandling'] : 'nothing';
80 $instance['no_match_text'] = isset( $attributes['noMatchText'] ) ? $attributes['noMatchText'] : '';
81 $instance['default_thunmbnail'] = isset( $attributes['defaultThunmbnail'] ) ? $attributes['defaultThunmbnail'] : 0;
82 $instance['ver'] = isset( $attributes['ver'] ) ? $attributes['ver'] : '5.1.0';
83 $instance['context'] = CONTEXT_BLOCK;
84
85 return $instance;
86 }
87
88 /**
89 * Get a stable ID for a block instance's load-more requests.
90 *
91 * Uses the `instanceId` block attribute (frozen in by the editor the first
92 * time the block is inserted, see src/edit.js) instead of wp_unique_id().
93 * wp_unique_id() only counts up within a single PHP request, so it produces
94 * colliding or diverging IDs when a block is rendered more than once per
95 * request or when several instances are each rendered via their own request
96 * (e.g. ServerSideRender previews in the block editor, excerpt generation,
97 * REST API output, full-page caching). Falling back to wp_unique_id() keeps
98 * older content saved before this attribute existed working.
99 *
100 * @param array $attributes The block attributes.
101 *
102 * @return string The block load-more ID.
103 */
104 function get_block_loadmore_id( $attributes ) {
105 if ( is_array( $attributes ) && isset( $attributes['instanceId'] ) && '' !== $attributes['instanceId'] ) {
106 return 'block-' . $attributes['instanceId'];
107 }
108
109 return wp_unique_id( 'block-' );
110 }
111
112 /**
113 * Store block settings for load-more requests.
114 *
115 * @param string $id The block load-more ID.
116 * @param array $attributes The block attributes.
117 *
118 * @return void
119 */
120 function store_block_loadmore_settings( $id, $attributes ) {
121 if ( empty( $id ) || ! is_array( $attributes ) ) {
122 return;
123 }
124 set_transient( 'cat_posts_block_' . $id, $attributes, HOUR_IN_SECONDS );
125 }
126
127 /**
128 * Retrieve stored block settings for load-more requests.
129 *
130 * @param string $id The block load-more ID.
131 *
132 * @return array|false Stored attributes or false.
133 */
134 function get_block_loadmore_settings( $id ) {
135 if ( empty( $id ) ) {
136 return false;
137 }
138 $settings = get_transient( 'cat_posts_block_' . $id );
139 return is_array( $settings ) ? $settings : false;
140 }
141
142 /**
143 * Collect everything the editor preview has to apply on its own.
144 *
145 * On the front end equal_cover_content_height() prints a script on wp_footer/
146 * admin_footer that refines the server-side "wrap the excerpt around the thumbnail"
147 * guess once the actual rendered heights are known. Neither hook runs for the REST
148 * request the ServerSideRender preview is built from, so the same flags are handed
149 * to src/edit.js as data instead, which applies the refinement itself.
150 *
151 * The conditions have to stay in sync with equal_cover_content_height().
152 *
153 * @param array $instance The widget instance data.
154 *
155 * @return array The wrap-text/image-ratio handling flags for the preview.
156 */
157 function block_preview_config( $instance ) {
158 $wrap_text = ! ( isset( $instance['text_do_not_wrap_thumb'] ) && $instance['text_do_not_wrap_thumb'] );
159
160 // No ratio calculation if one or more dimensions is set to 0.
161 $img_size = isset( $instance['thumb_w'] ) && 0 !== intval( $instance['thumb_w'] ) &&
162 isset( $instance['thumb_h'] ) && 0 !== intval( $instance['thumb_h'] );
163
164 return array(
165 'wrapText' => $wrap_text,
166 'imgSize' => $img_size,
167 );
168 }
169
170 /**
171 * Renders the `tiptip/category-posts-block` on server.
172 *
173 * @see WP_Widget_Archives
174 *
175 * @param array $attributes The block attributes.
176 *
177 * @return string Returns the post content with archives added.
178 */
179 function render_category_posts_block( $attributes ) {
180 global $attr, $before_title, $after_title;
181
182 $attr = $attributes;
183
184 // Get HTML
185 $widget = new Widget();
186 $instance = build_block_instance( $attributes );
187 $instance = upgrade_settings( $instance );
188
189 $block_id = get_block_loadmore_id( $attributes );
190 store_block_loadmore_settings( $block_id, $attributes );
191 $widget->number = $block_id;
192
193
194 $current_post_id = '';
195 if ( is_singular() ) {
196 $current_post_id = get_the_ID();
197 }
198
199 $items = $widget->get_elements_HTML( $instance, $current_post_id, 0, 0 );
200 $ret = '';
201
202 // Id of the wrapper element. $widget->number has to keep its 'block-' prefix
203 // because loadMoreHTML() relies on it, so use a separate variable for the DOM id.
204 $dom_id = WIDGET_BASE_ID . '-' . $block_id;
205
206 if ( ( 'nothing' === $instance['no_match_handling'] ) || ! empty( $items ) ) {
207 $ret = $widget->titleHTML( $before_title, $after_title, $instance );
208
209 $ret .= '<ul>' . implode( $items ) . '</ul>';
210
211 // Load more only if we think we have more items.
212 if ( count( $items ) === (int) $instance['num'] ) {
213 $ret .= $widget->loadMoreHTML( $instance );
214 }
215
216 $ret .= $widget->footerHTML( $instance );
217
218 // The 'cpwp-wrap-text' class the excerpt-lines CSS relies on, and the
219 // 'cpwp-wrap-text-stage' wrapper, are added by this script at runtime.
220 // Without it the excerpt lines and image ratio settings have no effect.
221 if ( isset( $instance['template'] ) && preg_match( '/%thumb%|%excerpt%/', $instance['template'] ) ) {
222 wp_enqueue_script( 'jquery' ); // Just in case the theme or other plugins did not enqueue it.
223 add_action(
224 'wp_footer',
225 function () use ( $dom_id, $instance ) {
226 equal_cover_content_height( $dom_id, $instance );
227 },
228 100
229 );
230 // Gutenberg Editor does not run wp_footer, so we also need to enqueue the script in the admin footer.
231 add_action(
232 'admin_footer',
233 function () use ( $dom_id, $instance ) {
234 equal_cover_content_height( $dom_id, $instance );
235 },
236 100
237 );
238 }
239
240 // Everything above hooks into wp_footer/admin_footer, which never run for the
241 // REST request the editor preview is built from - and a <script> element
242 // inside the returned markup would not be executed by React either
243 // (dangerouslySetInnerHTML does not run embedded scripts). So hand the editor
244 // the same wrap-text/image-ratio flags as inert JSON, src/edit.js applies
245 // them to the preview itself.
246 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
247 $ret .= '<script type="application/json" class="cpwp-block-preview-config">' .
248 wp_json_encode( block_preview_config( $instance ) ) .
249 '</script>';
250 }
251 } elseif ( 'text' === $instance['no_match_handling'] ) {
252 $ret = $widget->titleHTML( $before_title, $after_title, $instance );
253 $ret .= '<span class="cat-post-no-match">' . wp_kses_post( $instance['no_match_text'] ) . '</span>';
254 $ret .= $widget->footerHTML( $instance );
255 }
256
257 if ( '' === $ret ) {
258 return ''; // Nothing to show, so no style island either.
259 }
260
261 // The CSS rules depend on the settings of this specific block, so they can not be
262 // part of the static stylesheet. wp_head has long been sent by the time a block is
263 // rendered, so the rules are emitted as a style island right here.
264 $css = '';
265
266 static $styled = array(); // Identical attributes produce an identical id, emit once.
267 if ( ! isset( $styled[ $dom_id ] ) ) {
268 $styled[ $dom_id ] = true;
269
270 $virtual = new Virtual_Widget( $dom_id, WIDGET_BASE_ID . '-block', $instance );
271 $rules = array();
272 $virtual->getCSSRules( CONTEXT_BLOCK, $rules );
273
274 foreach ( $rules as $group ) {
275 $css .= implode( "\n", $group ) . "\n";
276 }
277 }
278
279 // get_block_wrapper_attributes() is what actually applies the block-supports
280 // classes (e.g. "aligncenter" from the toolbar's alignment option) to a
281 // dynamic block's markup - core never adds them on its own, they only ever
282 // end up in $attributes and are otherwise silently dropped by a hardcoded
283 // wrapper like the one this used to build.
284 $wrapper_attributes = get_block_wrapper_attributes(
285 array(
286 'id' => $dom_id,
287 'class' => WIDGET_BASE_ID . '-block',
288 )
289 );
290
291 return ( '' !== $css ? '<style>' . $css . '</style>' : '' ) .
292 '<div ' . $wrapper_attributes . '>' . $ret . '</div>';
293 }
294
295 /**
296 * Registers all block assets so that they can be enqueued through the block editor
297 * in the corresponding context.
298 *
299 * @see https://developer.wordpress.org/block-editor/tutorials/block-tutorial/applying-styles-with-stylesheets/
300 */
301 function category_posts_block_init() {
302 $dir = __DIR__;
303
304 $script_asset_path = "$dir/build/index.asset.php";
305 if ( ! file_exists( $script_asset_path ) ) {
306 throw new \Error(
307 'You need to run `npm start` or `npm run build` for the "tiptip/category-posts-block" block first.'
308 );
309 }
310 $index_js = 'build/index.js';
311 $script_asset = require( $script_asset_path );
312 wp_register_script(
313 'tiptip-category-posts-block-editor',
314 plugins_url( $index_js, __FILE__ ),
315 $script_asset['dependencies'],
316 $script_asset['version']
317 );
318 wp_set_script_translations( 'tiptip-category-posts-block-editor', 'category-posts' );
319
320 register_block_type_from_metadata(
321 __DIR__,
322 array(
323 'render_callback' => __NAMESPACE__ . '\render_category_posts_block',
324 )
325 );
326 }
327 add_action( 'init', __NAMESPACE__ . '\category_posts_block_init' );
328