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
← All changes | cat-posts.php +1250 -154 2.25.1.0 View file →
@@ -1,154 +1,1250 @@
1 -<?php
2 -/*
3 -Plugin Name: Category Posts Widget
4 -Plugin URI: http://jameslao.com/2009/07/29/category-posts-widget-2-0/
5 -Description: Adds a widget that can display a specified number of posts from a single category. Can also set how many widgets to show.
6 -Author: James Lao
7 -Version: 2.2
8 -Author URI: http://jameslao.com/
9 -*/
10 -
11 -class CategoryPosts extends WP_Widget {
12 -
13 -function CategoryPosts() {
14 - parent::WP_Widget(false, $name='Category Posts');
15 -}
16 -
17 -/**
18 - * Displays category posts widget on blog.
19 - */
20 -function widget($args, $instance) {
21 - global $post, $wp_query;
22 - extract( $args );
23 -
24 - // If not title, use the name of the category.
25 - if( !$instance["title"] ) {
26 - $category_info = get_category($instance["cat"]);
27 - $instance["title"] = $category_info->name;
28 - }
29 -
30 - // Save old query...
31 - $old_query = $wp_query;
32 -
33 - // Get array of post info.
34 - query_posts("showposts=" . $instance["num"] . "&cat=" . $instance["cat"]);
35 -
36 - echo $before_widget;
37 -
38 - // Widget title
39 -
40 - echo $before_title;
41 - if( (bool) $instance["title_link"] )
42 - echo '<a href="' . get_category_link($instance["cat"]) . '">' . $instance["title"] . '</a>';
43 - else
44 - echo $instance["title"];
45 - echo $after_title;
46 -
47 - // Post list
48 - echo "<ul>\n";
49 -
50 - while ( have_posts() ) : the_post();
51 - ?>
52 - <li class='cat-post-item'>
53 -
54 - <?php if ( (bool) $instance["thumbnail"] && function_exists('p75HasThumbnail') && p75HasThumbnail($post->ID) ) { ?>
55 -
56 - <a href="<?php the_permalink(); ?>" title="<?php echo the_title_attribute(); ?>"><img class='alignleft' src='<?php echo p75GetThumbnail($post->ID, $instance["thumbnail_width"], $instance["thumbnail_height"]); ?>' alt='<?php echo the_title_attribute(); ?>' /></a>
57 -
58 - <?php } // end thumbnail function check ?>
59 -
60 - <a class="post-title" href="<?php echo the_permalink(); ?>" title="<?php echo the_title_attribute(); ?>"><?php echo the_title(); ?></a>
61 -
62 - <?php if ( $instance['excerpt'] ) the_excerpt(); ?>
63 - </li>
64 -<?php
65 - endwhile;
66 -
67 - echo "</ul>\n";
68 -
69 - echo $after_widget;
70 -
71 - // Won't even know I was here...
72 - $wp_query = $old_query;
73 -}
74 -
75 -/**
76 - * Form processing... Dead simple.
77 - */
78 -function update($new_instance, $old_instance) {
79 - $new_instance["cat"] = absint( $new_instance["cat"] );
80 - $new_instance["exclude"] = (bool) $new_instance["exclude"];
81 -
82 - return $new_instance;
83 -}
84 -
85 -/**
86 - * The configuration form.
87 - */
88 -function form($instance) {
89 -?>
90 - <p>
91 - <label for="<?php echo $this->get_field_id("title"); ?>">
92 - <?php _e( 'Title' ); ?>:
93 - <input class="widefat" id="<?php echo $this->get_field_id("title"); ?>" name="<?php echo $this->get_field_name("title"); ?>" type="text" value="<?php echo esc_attr($instance["title"]); ?>" />
94 - </label>
95 - </p>
96 -
97 - <p>
98 - <label>
99 - <?php _e( 'Category' ); ?>:
100 - <?php wp_dropdown_categories( array( 'name' => $this->get_field_name("cat"), 'selected' => $instance["cat"] ) ); ?>
101 - </label>
102 - </p>
103 -
104 - <p>
105 - <label for="<?php echo $this->get_field_id("num"); ?>">
106 - <?php _e('Number of posts to show'); ?>:
107 - <input style="text-align: center;" id="<?php echo $this->get_field_id("num"); ?>" name="<?php echo $this->get_field_name("num"); ?>" type="text" value="<?php echo absint($instance["num"]); ?>" size='3' />
108 - </label>
109 - </p>
110 -
111 - <p>
112 - <label for="<?php echo $this->get_field_id("title_link"); ?>">
113 - <input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id("title_link"); ?>" name="<?php echo $this->get_field_name("title_link"); ?>"<?php checked( (bool) $instance["title_link"], true ); ?> />
114 - <?php _e( 'Make widget title link' ); ?>
115 - </label>
116 - </p>
117 -
118 - <p>
119 - <label for="<?php echo $this->get_field_id("excerpt"); ?>">
120 - <input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id("excerpt"); ?>" name="<?php echo $this->get_field_name("excerpt"); ?>"<?php checked( (bool) $instance["excerpt"], true ); ?> />
121 - <?php _e( 'Show post excerpt' ); ?>
122 - </label>
123 - </p>
124 -
125 - <?php if ( function_exists("p75GetThumbnail") ) : ?>
126 - <p>
127 - <label for="<?php echo $this->get_field_id("thumbnail"); ?>">
128 - <input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id("thumbnail"); ?>" name="<?php echo $this->get_field_name("thumbnail"); ?>"<?php checked( (bool) $instance["thumbnail"], true ); ?> />
129 - <?php _e( 'Show post thumbnail' ); ?>
130 - </label>
131 - </p>
132 - <p>
133 - <label>
134 - <?php _e('Thumbnail dimensions'); ?>:<br />
135 - <label for="<?php echo $this->get_field_id("thumbnail_width"); ?>">
136 - W: <input class="widefat" style="width:40%;" type="text" id="<?php echo $this->get_field_id("thumbnail_width"); ?>" name="<?php echo $this->get_field_name("thumbnail_width"); ?>" value="<?php echo $instance["thumbnail_width"]; ?>" />
137 - </label>
138 -
139 - <label for="<?php echo $this->get_field_id("thumbnail_height"); ?>">
140 - H: <input class="widefat" style="width:40%;" type="text" id="<?php echo $this->get_field_id("thumbnail_height"); ?>" name="<?php echo $this->get_field_name("thumbnail_height"); ?>" value="<?php echo $instance["thumbnail_height"]; ?>" />
141 - </label>
142 - </label>
143 - </p>
144 - <?php endif; ?>
145 -
146 -<?php
147 -
148 -}
149 -
150 -}
151 -
152 -add_action( 'widgets_init', create_function('', 'return register_widget("CategoryPosts");') );
153 -
154 -?>
1 +<?php
2 +/**
3 + * Main file of the plugin
4 + *
5 + * @package categoryposts.
6 + *
7 + * @since 1.0
8 + */
9 +
10 +/*
11 +Plugin Name: Category Posts Widget
12 +Plugin URI: https://wordpress.org/plugins/category-posts/
13 +Description: Adds a widget that shows the most recent posts from a single category.
14 +Author: TipTopPress
15 +Version: 5.1.0
16 +Author URI: https://tiptoppress.com
17 +Text Domain: category-posts
18 +Domain Path: /languages
19 +*/
20 +
21 +namespace categoryPosts;
22 +
23 +// Don't call the file directly.
24 +if ( ! defined( 'ABSPATH' ) ) {
25 + exit;
26 +}
27 +
28 +const VERSION = '5.1.0';
29 +const DOC_URL = 'https://tiptoppress.com/category-posts-widget/documentation-5-0/';
30 +const PRO_URL = 'https://tiptoppress.com/term-and-category-based-posts-widget/';
31 +const SUPPORT_URL = 'https://wordpress.org/support/plugin/category-posts/';
32 +const SHORTCODE_NAME = 'catposts';
33 +const SHORTCODE_META = 'categoryPosts-shorcode';
34 +const WIDGET_BASE_ID = 'category-posts';
35 +const CONTEXT_WIDGET = 'widget';
36 +const CONTEXT_SHORTCODE = 'shortcode';
37 +const CONTEXT_BLOCK = 'block';
38 +
39 +require_once __DIR__ . '/class-virtual-widget.php';
40 +require_once __DIR__ . '/class-virtual-widgets-repository.php';
41 +require_once __DIR__ . '/class-widget.php';
42 +require_once __DIR__ . '/loadmore.php';
43 +require_once __DIR__ . '/localizeddate.php';
44 +require_once __DIR__ . '/cat-posts-block.php';
45 +
46 +/**
47 + * Adds the "Customize" link to the Toolbar on edit mode.
48 + *
49 + * @since 4.6
50 + */
51 +function wp_admin_bar_customize_menu() {
52 + global $wp_admin_bar;
53 +
54 + if ( ! isset( $_GET['action'] ) || ( 'edit' !== $_GET['action'] ) ) {
55 + return;
56 + }
57 +
58 + if ( ! current_user_can( 'customize' ) || ! is_admin() || ! is_user_logged_in() || ! is_admin_bar_showing() ) {
59 + return;
60 + }
61 +
62 + $current_url = '';
63 + if ( isset( $_GET['post'] ) && ( '' !== $_GET['post'] ) ) {
64 + $current_url = get_permalink( absint( $_GET['post'] ) );
65 + }
66 + $customize_url = add_query_arg( 'url', rawurlencode( $current_url ), wp_customize_url() );
67 +
68 + $p = isset( $_GET['post'] ) && ! empty( $_GET['post'] ) ? get_post( absint( $_GET['post'] ) ) : false;
69 + $names = $p ? shortcode_names( SHORTCODE_NAME, $p->post_content ) : array();
70 + if ( empty( $names ) ) {
71 + return;
72 + }
73 +
74 + $wp_admin_bar->add_menu(
75 + array(
76 + 'id' => 'customize',
77 + 'title' => __( 'Customize' ),
78 + 'href' => $customize_url,
79 + 'meta' => array(
80 + 'class' => 'hide-if-no-customize',
81 + ),
82 + )
83 + );
84 + add_action( 'wp_before_admin_bar_render', 'wp_customize_support_script' );
85 +}
86 +
87 +add_action( 'admin_bar_menu', __NAMESPACE__ . '\wp_admin_bar_customize_menu', 35 );
88 +
89 +/**
90 + * Print out required CSS, hooked on the wp_head hook.
91 + */
92 +function wp_head() {
93 +
94 + $widget_repository = new Virtual_Widgets_Repository();
95 +
96 + $styles = array();
97 +
98 + foreach ( $widget_repository->getShortcodes() as $widget ) {
99 + $widget->getCSSRules( CONTEXT_SHORTCODE, $styles );
100 + }
101 +
102 + foreach ( $widget_repository->getWidgets() as $widget ) {
103 + $widget->getCSSRules( CONTEXT_WIDGET, $styles );
104 + }
105 +
106 + if ( ! empty( $styles ) ) {
107 + ?>
108 +<style>
109 + <?php
110 + foreach ( $styles as $rules ) {
111 + foreach ( $rules as $rule ) {
112 + echo "$rule\n"; // Xss ok. raw css output, can not be html escaped.
113 + }
114 + }
115 + ?>
116 +</style>
117 + <?php
118 + }
119 +}
120 +
121 +add_action( 'wp_head', __NAMESPACE__ . '\register_virtual_widgets', 0 );
122 +
123 +/**
124 + * Register virtual widgets for all widgets and shortcodes that are going to be displayed on the page
125 + *
126 + * @return void
127 + *
128 + * @since 4.7
129 + */
130 +function register_virtual_widgets() {
131 + global $post;
132 + global $wp_registered_widgets;
133 +
134 + $repository = new Virtual_Widgets_Repository();
135 +
136 + // check first for shortcode settings.
137 + if ( is_singular() ) {
138 + $names = shortcode_names( SHORTCODE_NAME, $post->post_content );
139 +
140 + foreach ( $names as $name ) {
141 + $meta = shortcode_settings( get_the_ID(), $name );
142 + if ( is_array( $meta ) ) {
143 + $id = WIDGET_BASE_ID . '-shortcode-' . get_the_ID(); // needed to make a unique id for the widget html element.
144 + if ( '' !== $name ) { // if not default name append to the id.
145 + $id .= '-' . sanitize_title( $name ); // sanitize to be on the safe side, not sure where when and how this will be used.
146 + }
147 + $repository->addShortcode( $name, new Virtual_Widget( $id, WIDGET_BASE_ID . '-shortcode', $meta ) );
148 + }
149 + }
150 + }
151 +
152 + $sidebars_widgets = wp_get_sidebars_widgets();
153 +
154 + if ( is_array( $sidebars_widgets ) ) {
155 + foreach ( $sidebars_widgets as $sidebar => $widgets ) {
156 + if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) {
157 + continue;
158 + }
159 +
160 + if ( is_array( $widgets ) ) {
161 + foreach ( $widgets as $widget ) {
162 + $widget_base = _get_widget_id_base( $widget );
163 + if ( WIDGET_BASE_ID === $widget_base ) {
164 + $class = __NAMESPACE__ . '\Widget';
165 + $widgetclass = new $class();
166 + $allsettings = $widgetclass->get_settings();
167 + $settings = isset( $allsettings[ str_replace( $widget_base . '-', '', $widget ) ] ) ? $allsettings[ str_replace( $widget_base . '-', '', $widget ) ] : false;
168 + $repository->addWidget( $widget, new Virtual_Widget( $widget, $widget, $settings ) );
169 + }
170 + }
171 + }
172 + }
173 + }
174 +}
175 +
176 +add_action( 'wp_head', __NAMESPACE__ . '\wp_head' );
177 +
178 +/**
179 + * Enqueue widget related scripts for the widget front-end
180 + *
181 + * @since 4.8
182 + */
183 +function frontend_script() {
184 + $suffix = 'min.js';
185 + if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
186 + $suffix = 'js';
187 + }
188 + wp_enqueue_script( 'cat-posts-frontend-js', plugins_url( 'js/frontend/category-posts-frontend.' . $suffix, __FILE__ ), array( 'jquery' ), VERSION, true );
189 +}
190 +
191 +/**
192 + * Embed the front end JS in the HTML footer.
193 + *
194 + * @since 4.9
195 + */
196 +function embed_front_end_scripts() {
197 + $suffix = 'min.js';
198 + if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
199 + $suffix = 'js';
200 + }
201 + echo '<script>';
202 + include __DIR__ . '/js/frontend/category-posts-frontend.' . $suffix;
203 + echo '</script>';
204 +}
205 +
206 +/**
207 + * Enqueue widget related scripts for the widget admin page and customizer.
208 + *
209 + * @param string $hook the name of the admin hook for which the function was triggered.
210 + */
211 +function admin_scripts( $hook ) {
212 +
213 + if ( 'widgets.php' === $hook || 'post.php' === $hook ) { // enqueue only for widget admin and customizer. (add if post.php: fix make widget SiteOrigin Page Builder plugin, GH issue #181).
214 +
215 + /*
216 + * Add script to control admin UX.
217 + */
218 +
219 + // Use unminified version of JS when debuging, and minified when not.
220 + $suffix = 'min.js';
221 + if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
222 + $suffix = 'js';
223 + }
224 + wp_register_script( 'category-posts-widget-admin-js', plugins_url( 'js/admin/category-posts-widget.' . $suffix, __FILE__ ), array( 'jquery' ), VERSION, true );
225 + wp_enqueue_script( 'category-posts-widget-admin-js' );
226 +
227 + $js_data = array( 'accordion' => false );
228 + $meta = get_user_meta( get_current_user_id(), __NAMESPACE__, true );
229 + if ( is_array( $meta ) && isset( $meta['panels'] ) ) {
230 + $js_data['accordion'] = true;
231 + }
232 + $js_data['template_tags'] = get_template_tags();
233 + $js_data[ __NAMESPACE__ ] = $js_data; // To make accessing the data in JS easier to understand.
234 +
235 + wp_localize_script( 'category-posts-widget-admin-js', 'tiptoppress', $js_data );
236 + wp_enqueue_media();
237 + wp_localize_script(
238 + 'category-posts-widget-admin-js',
239 + 'cwp_default_thumb_selection',
240 + array(
241 + 'frame_title' => __( 'Select a default thumbnail', 'category-posts' ),
242 + 'button_title' => __( 'Select', 'category-posts' ),
243 + 'none' => __( 'None', 'category-posts' ),
244 + )
245 + );
246 + }
247 +}
248 +
249 +add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\admin_scripts' ); // "called on widgets.php and costumizer since 3.9.
250 +
251 +add_action( 'admin_init', __NAMESPACE__ . '\load_textdomain' );
252 +
253 +/**
254 + * Load plugin textdomain.
255 + *
256 + * @return void
257 + *
258 + * @since 4.1
259 + **/
260 +function load_textdomain() {
261 + load_plugin_textdomain( 'category-posts', false, plugin_basename( dirname( __FILE__ ) ) . '/languages' );
262 +}
263 +
264 +/**
265 + * Add styles for widget admin sections
266 + *
267 + * @since 4.1
268 + **/
269 +function admin_styles() {
270 + wp_enqueue_style( 'cat-posts-admin-styles', plugins_url( 'styles/admin/category-posts-widget.css', __FILE__ ), array(), VERSION, false );
271 +}
272 +
273 +add_action( 'admin_print_styles-widgets.php', __NAMESPACE__ . '\admin_styles' );
274 +
275 +// fix make widget SiteOrigin Page Builder plugin, GH issue #181.
276 +add_action( 'siteorigin_panel_enqueue_admin_scripts', __NAMESPACE__ . '\admin_styles' );
277 +
278 +/**
279 + * Get the tags which might be used in the template.
280 + *
281 + * @since 4.8
282 + *
283 + * @return array Array of strings of the tags.
284 + */
285 +function get_template_tags() {
286 + return array( 'author', 'title', 'date', 'thumb', 'excerpt', 'commentnum', 'post_tag', 'category', 'more-link' );
287 +}
288 +
289 +/**
290 + * Get a regex to parse the template in order to find the tags used in it.
291 + *
292 + * @since 4.8
293 + *
294 + * @return string The template parsing regex.
295 + */
296 +function get_template_regex() {
297 + $tags = get_template_tags();
298 +
299 + $regexp = '';
300 + foreach ( $tags as $t ) {
301 + if ( ! empty( $regexp ) ) {
302 + $regexp .= '|';
303 + }
304 + $regexp .= '%' . $t . '%';
305 + }
306 +
307 + $regexp = '@(' . $regexp . ')@i';
308 +
309 + return $regexp;
310 +}
311 +
312 +/**
313 + * The convert old data structure to current one.
314 + *
315 + * @param array $settings The settings to upgrade.
316 + *
317 + * @return array The settings matching current version standard.
318 + *
319 + * @since 4.8
320 + */
321 +function upgrade_settings( $settings ) {
322 +
323 + if ( isset( $setting ) && is_countable( $setting ) && 0 === count( $settings ) ) {
324 + return default_settings();
325 + }
326 +
327 + // Whether $settings carries any of the pre-4.8 display flags that
328 + // convert_settings_to_template() knows how to translate. A settings array
329 + // that has none of these (e.g. a brand new instance, or a partial override
330 + // such as array( 'title' => 'bla' ) passed straight to Virtual_Widget) is not
331 + // a legacy instance just because it happens to be non-empty, and should keep
332 + // falling through to the default template below instead of being converted
333 + // into a bare title-only one.
334 + $has_legacy_display_flags = (bool) array_intersect(
335 + array( 'thumb', 'thumbTop', 'date', 'hide_post_titles', 'excerpt', 'comment_num', 'author' ),
336 + array_keys( $settings )
337 + );
338 +
339 + if ( ! isset( $settings['ver'] ) ) {
340 + /*
341 + * Pre 4.9 version.
342 + */
343 +
344 + // Upgrade the hide if empty option.
345 + if ( isset( $settings['hide_if_empty'] ) && $settings['hide_if_empty'] ) {
346 + $settings['no_match_handling'] = 'hide';
347 + } else {
348 + $settings['no_match_handling'] = 'nothing';
349 + }
350 + if ( isset( $settings['hide_if_empty'] ) ) {
351 + unset( $settings['hide_if_empty'] );
352 + }
353 +
354 + /*
355 + * Settings saved before the template system existed (pre 4.8) do not have a
356 + * 'template' key. shortcode_settings() and the customizer migration path both
357 + * build one from the old display flags (excerpt, thumb, thumbTop, date,
358 + * comment_num, author) via convert_settings_to_template() before calling this
359 + * function; the widget render path did not, so those choices were silently
360 + * lost the moment wp_parse_args() below filled 'template' in from
361 + * default_settings(). Do the same conversion here so every caller gets it,
362 + * but only when there is an actual legacy flag to convert - a brand new
363 + * instance (or a partial override with no display flags) has nothing to
364 + * convert and should keep falling through to the default template (which
365 + * includes %thumb%).
366 + */
367 + if ( $has_legacy_display_flags && ( ! isset( $settings['template'] ) || ! $settings['template'] ) ) {
368 + $settings['template'] = convert_settings_to_template( $settings );
369 + }
370 + } else {
371 + if ( version_compare( '4.9.8', $settings['ver']) ) {
372 + if ( isset( $settings['preset_date_format'] ) && 'sincepublished' === $settings['preset_date_format'] ) {
373 + $settings['date_past_time'] = 999;
374 + $settings['preset_date_format'] = 'sitedate';
375 + }
376 + }
377 + }
378 +
379 + $settings['ver'] = VERSION;
380 +
381 + // Make sure all "empty" settings have default value.
382 + $settings = wp_parse_args( $settings, default_settings() );
383 +
384 + return $settings;
385 +}
386 +
387 +/**
388 + * Convert pre 4.8 settings into template
389 + *
390 + * @param array $instance Array which contains the various settings.
391 + *
392 + * @since 4.8
393 + */
394 +function convert_settings_to_template( $instance ) {
395 +
396 + $template = '';
397 +
398 + if ( isset( $instance['thumb'] ) && $instance['thumb'] ) {
399 + if ( isset( $instance['thumbTop'] ) && $instance['thumbTop'] ) {
400 + $template .= "%thumb%\n\n";
401 + if ( ! ( isset( $instance['hide_post_titles'] ) && $instance['hide_post_titles'] ) ) {
402 + $template .= "%title%\n";
403 + }
404 + } elseif ( isset( $instance['date'] ) && $instance['date'] ) {
405 + if ( ! ( isset( $instance['hide_post_titles'] ) && $instance['hide_post_titles'] ) ) {
406 + $template .= "%title%\n\n";
407 + }
408 + $template .= "%date%\n\n";
409 + $template .= "%thumb%\n";
410 + } elseif ( ! ( isset( $instance['hide_post_titles'] ) && $instance['hide_post_titles'] ) ) {
411 + $template .= "%thumb%\n%title%\n";
412 + }
413 + } else {
414 + if ( ! ( isset( $instance['hide_post_titles'] ) && $instance['hide_post_titles'] ) ) {
415 + $template .= "%title%\n";
416 + }
417 + if ( isset( $instance['date'] ) && $instance['date'] ) {
418 + $template .= "%date%\n\n";
419 + }
420 + }
421 + if ( isset( $instance['excerpt'] ) && $instance['excerpt'] ) {
422 + $template .= '%excerpt%';
423 + }
424 + if ( isset( $instance['comment_num'] ) && $instance['comment_num'] ) {
425 + $template .= "%commentnum%\n\n";
426 + }
427 + if ( isset( $instance['author'] ) && $instance['author'] ) {
428 + $template .= "%author%\n\n";
429 + }
430 +
431 + return $template;
432 +}
433 +
434 +/*
435 + * Plugin action links section
436 + */
437 +
438 +/**
439 + * Applied to the list of links to display on the plugins page (beside the activate/deactivate links).
440 + *
441 + * @return array of the widget links
442 + *
443 + * @since 4.6.3
444 + */
445 +add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), __NAMESPACE__ . '\add_action_links' );
446 +
447 +/**
448 + * Handle the add links filter, add our links to the links displayed under the plugin_basename
449 + * in the plugin admin screen.
450 + *
451 + * @param array $links The current links about to be displayed.
452 + */
453 +function add_action_links( $links ) {
454 +
455 + if ( ! class_exists( '\\termcategoryPostsPro\\Widget' ) ) {
456 + $pro_link = array(
457 + '<a target="_blank" href="' . esc_url( PRO_URL ) . '">' . esc_html__( 'Get the Pro version', 'category-posts' ) . '</a>',
458 + );
459 +
460 + $links = array_merge( $pro_link, $links );
461 + }
462 +
463 + return $links;
464 +}
465 +
466 +/**
467 + * Register the widget.
468 + */
469 +function register_widget() {
470 + return \register_widget( __NAMESPACE__ . '\Widget' );
471 +}
472 +
473 +add_action( 'widgets_init', __NAMESPACE__ . '\register_widget' );
474 +
475 +/**
476 + * Output js code to set the image height if float text around image.
477 + *
478 + * @param int $number The widget number used to identify the specific list.
479 + * @param array $widgetsettings The "instance" parameters of the widget.
480 + *
481 + * @since 4.9
482 + **/
483 +function equal_cover_content_height( $number, $widgetsettings ) {
484 +
485 + // When the text must not wrap the thumbnail, the layout is fully described by the
486 + // markup and the CSS itemHTML()/getCSSRules() generate, so the script must not
487 + // move the 'cpwp-wrap-text' class around at all.
488 + $wrap_text = ! ( isset( $widgetsettings['text_do_not_wrap_thumb'] ) && $widgetsettings['text_do_not_wrap_thumb'] );
489 +
490 + if ( isset( $widgetsettings['template'] ) && preg_match( '/%thumb%|%excerpt%/', $widgetsettings['template'] ) ) :
491 + ?>
492 + <script type="text/javascript">
493 + if (typeof jQuery !== 'undefined') {
494 +
495 + var cat_posts_namespace = window.cat_posts_namespace || {};
496 + cat_posts_namespace.layout_wrap_text = cat_posts_namespace.layout_wrap_text || {};
497 + cat_posts_namespace.layout_img_size = cat_posts_namespace.layout_img_size || {};
498 +
499 + cat_posts_namespace.layout_wrap_text = {
500 + <?php /* Handle add class */ echo "\r\n"; ?>
501 + add : function(_this){
502 + var _that = jQuery(_this),
503 + _text = _that.find('p.cpwp-excerpt-text'),
504 + _stage = _text.closest('.cpwp-wrap-text-stage');
505 +
506 + if ( 0 === _text.length ) {
507 + return;
508 + }
509 + <?php /* Measure from a defined state: the class on the paragraph, so its
510 + height is the clamped text height no matter what the server side
511 + or a previous run left behind. */ echo "\r\n"; ?>
512 + _stage.removeClass( "cpwp-wrap-text" );
513 + _text.addClass( "cpwp-wrap-text" );
514 +
515 + if (_text.height() < _that.find('.cat-post-thumbnail').height()) { <?php /* don't move class to do the CSS hack, line's height is smaller as thumb */ echo "\r\n"; ?>
516 + return; <?php /* don't do the CSS hack, the class is already on the paragraph */ echo "\r\n"; ?>
517 + }
518 + <?php /* add the CSS hack, it's needed, text is wrapping, line's height is higher as thumb */ echo "\r\n"; ?>
519 + _text.removeClass( "cpwp-wrap-text" );
520 + _stage.addClass( "cpwp-wrap-text" );
521 + return;
522 + },
523 + <?php /* Wait for image is loaded */ echo "\r\n"; ?>
524 + handleLazyLoading : function(_this) {
525 + var width = jQuery(_this).find('img').width();
526 + <?php /* image is loaded */ echo "\r\n"; ?>
527 + if( 0 !== width ){
528 + cat_posts_namespace.layout_wrap_text.add(_this);
529 + } else {
530 + jQuery(_this).find('img').one("load", function(){
531 + cat_posts_namespace.layout_wrap_text.add(_this);
532 + });
533 + }
534 + return;
535 + },
536 + <?php /* Handle post items */ echo "\r\n"; ?>
537 + setClass : function (widget) {
538 + // var _widget = jQuery(widget);
539 + jQuery(widget).find('.cat-post-item').each(function(){
540 + cat_posts_namespace.layout_wrap_text.handleLazyLoading(this);
541 + });
542 + return;
543 + }
544 + }
545 + cat_posts_namespace.layout_img_size = {
546 + <?php /* Handle replace */ echo "\r\n"; ?>
547 + replace : function(_this){
548 + var _that = jQuery(_this),
549 + resp_w = _that.width(),
550 + resp_h = _that.height(),
551 + orig_w = _that.data('cat-posts-width'),
552 + orig_h = _that.data('cat-posts-height');
553 + <?php /* replace height */ echo "\r\n"; ?>
554 + if( resp_w < orig_w ){
555 + _that.height( resp_w * orig_h / orig_w );
556 + } else {
557 + _that.height( '' );
558 + }
559 + return;
560 + },
561 + <?php /* Wait for image is loaded */ echo "\r\n"; ?>
562 + handleLazyLoading : function(_this) {
563 + var width = jQuery(_this).width();
564 + <?php /* image is loaded */ echo "\r\n"; ?>
565 + if( 0 !== width ){
566 + cat_posts_namespace.layout_img_size.replace(_this);
567 + } else {
568 + jQuery(_this).one("load", function(){
569 + cat_posts_namespace.layout_img_size.replace(_this);
570 + });
571 + }
572 + return;
573 + },
574 + setHeight : function (widget) {
575 + jQuery(widget).find('.cat-post-item img').each(function(){
576 + cat_posts_namespace.layout_img_size.handleLazyLoading(this);
577 + });
578 + return;
579 + }
580 + }
581 +
582 + let widget = jQuery('#<?php echo esc_attr( $number ); ?>');
583 +
584 + <?php if ( $wrap_text ) : ?>
585 + <?php /* Gutenberg Editor load or change (DOM changes) */ echo "\r\n"; ?>
586 + const observer = new MutationObserver(function () {
587 + let widget = jQuery('#<?php echo esc_attr( $number ); ?>');
588 + if (widget.length) {
589 + cat_posts_namespace.layout_wrap_text.setClass(widget);
590 + }
591 + });
592 +
593 + observer.observe(document.body, {
594 + childList: true,
595 + subtree: true,
596 + characterData: true
597 + });
598 + <?php endif; echo "\r\n"; ?>
599 +
600 + <?php /* DOM ready */ echo "\r\n"; ?>
601 + jQuery( document ).ready(function () {
602 + <?php if ( $wrap_text ) : ?>
603 + cat_posts_namespace.layout_wrap_text.setClass(widget);
604 + <?php endif; echo "\r\n"; ?>
605 + <?php /* No ratio calculation if one or more dimensions is set to 0 */ echo "\r\n"; ?>
606 + <?php if ( isset( $widgetsettings['thumb_w'] ) && 0 !== intval( $widgetsettings['thumb_w'] ) &&
607 + isset( $widgetsettings['thumb_h'] ) && 0 !== intval( $widgetsettings['thumb_h'] ) ) : echo "\r\n"; ?>
608 + cat_posts_namespace.layout_img_size.setHeight(widget);
609 + <?php endif; echo "\r\n"; ?>
610 + });
611 +
612 + jQuery(window).on('load resize', function() {
613 + <?php if ( $wrap_text ) : ?>
614 + cat_posts_namespace.layout_wrap_text.setClass(widget);
615 + <?php endif; echo "\r\n"; ?>
616 + <?php /* No ratio calculation if one or more dimensions is set to 0 */ echo "\r\n"; ?>
617 + <?php if ( isset( $widgetsettings['thumb_w'] ) && 0 !== intval( $widgetsettings['thumb_w'] ) &&
618 + isset( $widgetsettings['thumb_h'] ) && 0 !== intval( $widgetsettings['thumb_h'] ) ) : echo "\r\n"; ?>
619 + cat_posts_namespace.layout_img_size.setHeight(widget);
620 + <?php endif; echo "\r\n"; ?>
621 + });
622 +
623 + // low-end mobile
624 + <?php if ( $wrap_text ) : ?>
625 + cat_posts_namespace.layout_wrap_text.setClass(widget);
626 + <?php endif; echo "\r\n"; ?>
627 + <?php /* No ratio calculation if one or more dimensions is set to 0 */ echo "\r\n"; ?>
628 + <?php if ( isset( $widgetsettings['thumb_w'] ) && 0 !== intval( $widgetsettings['thumb_w'] ) &&
629 + isset( $widgetsettings['thumb_h'] ) && 0 !== intval( $widgetsettings['thumb_h'] ) ) : echo "\r\n"; ?>
630 + cat_posts_namespace.layout_img_size.setHeight(widget);
631 + <?php endif; echo "\r\n"; ?>
632 +
633 + }
634 + </script>
635 + <?php
636 + endif;
637 +}
638 +
639 +/*
640 + * shortcode section.
641 + */
642 +
643 +/**
644 + * Get shortcode settings taking into account if it is being customized
645 + *
646 + * When not customized returns the settings as stored in the meta, but when
647 + * it is customized returns the setting stored in the virtual option used by the customizer
648 + *
649 + * @param string $pid The ID of the post in which the shortcode is.
650 + * @param string $name The name of the shortcode to retun, empty string indicates the nameless.
651 + *
652 + * @return array The shortcode settings if a short code name exists or is an empty string,
653 + * empty array if name not found.
654 + *
655 + * @since 4.6
656 + */
657 +function shortcode_settings( $pid, $name ) {
658 + $meta = get_post_meta( $pid, SHORTCODE_META, true );
659 +
660 + if ( ! empty( $meta ) && ! is_array( reset( $meta ) ) ) {
661 + $meta = array( '' => $meta ); // the conversion.
662 + }
663 +
664 + if ( ! isset( $meta[ $name ] ) ) { // name do not exists? return empty array.
665 + return array();
666 + }
667 +
668 + $instance = $meta[ $name ];
669 + if ( is_customize_preview() ) {
670 + $o = get_option( '_virtual-' . WIDGET_BASE_ID );
671 + if ( is_array( $o ) ) {
672 + $instance = $o[ $pid ][ $name ];
673 + $instance['ver'] = VERSION;
674 + }
675 + }
676 +
677 + if ( isset( $instance['template'] ) && $instance['template'] ) {
678 + ;
679 + } else {
680 + $instance['template'] = convert_settings_to_template( $instance );
681 + }
682 +
683 + $instance = upgrade_settings( $instance );
684 +
685 + return $instance;
686 +}
687 +
688 +/**
689 + * Handle the shortcode
690 + *
691 + * @param array $attr Array of the attributes to the short code, none is expected.
692 + * @param string $content The content enclosed in the shortcode, none is expected.
693 + *
694 + * @return string An HTML of the "widget" based on its settings, actual or customized
695 + */
696 +function shortcode( $attr, $content = null ) {
697 + $repository = new Virtual_Widgets_Repository();
698 +
699 + $shortcodes = $repository->getShortcodes();
700 +
701 + $name = '';
702 + if ( isset( $attr['name'] ) ) {
703 + $name = $attr['name'];
704 + }
705 +
706 + if ( is_singular() ) {
707 + if ( isset( $shortcodes[ $name ] ) ) {
708 + return $shortcodes[ $name ]->getHTML();
709 + }
710 + }
711 +
712 + return '';
713 +}
714 +
715 +add_shortcode( SHORTCODE_NAME, __NAMESPACE__ . '\shortcode' );
716 +
717 +/**
718 + * Find if a specific shortcode is used in a content
719 + *
720 + * @param string $shortcode_name The name of the shortcode.
721 + * @param string $content The content to look at.
722 + *
723 + * @return array An array containing the name attributes of the shortcodes. Empty array is
724 + * an indication there were no shourcodes
725 + *
726 + * @since 4.7
727 + */
728 +function shortcode_names( $shortcode_name, $content ) {
729 +
730 + $names = array();
731 +
732 + $regex_pattern = get_shortcode_regex();
733 + if ( preg_match_all( '/' . $regex_pattern . '/s', $content, $matches ) ) {
734 + foreach ( $matches[2] as $k => $shortcode ) {
735 + if ( SHORTCODE_NAME === $shortcode ) {
736 + $name = '';
737 + $atts = shortcode_parse_atts( $matches[3][ $k ] );
738 + if ( ! empty( $atts['name'] ) ) {
739 + $name = $atts['name'];
740 + }
741 + $names[] = $name;
742 + }
743 + }
744 + }
745 +
746 + return $names;
747 +}
748 +
749 +/**
750 + * Organized way to have the default widget settings accessible
751 + *
752 + * @since 4.6
753 + */
754 +function default_settings() {
755 + return array(
756 + 'title' => __( 'Category Posts', 'category-posts' ),
757 + 'title_link' => false,
758 + 'title_link_target' => false,
759 + 'title_level' => 'Initial',
760 + 'title_link_url' => '',
761 + 'hide_title' => false,
762 + 'cat' => 0,
763 + 'num' => get_option( 'posts_per_page' ),
764 + 'offset' => 1,
765 + 'sort_by' => 'date',
766 + 'status' => 'publish',
767 + 'asc_sort_order' => false,
768 + 'exclude_current_post' => false,
769 + 'hideNoThumb' => false,
770 + 'sticky' => false,
771 + 'footer_link_text' => '',
772 + 'footer_link' => '',
773 + 'footer_link_target' => false,
774 + 'item_title_level' => 'Inline',
775 + 'item_title_lines' => 2,
776 + 'thumb_w' => get_option( 'thumbnail_size_w', 150 ),
777 + 'thumb_fluid_width' => 100,
778 + 'thumb_h' => get_option( 'thumbnail_size_h', 150 ),
779 + 'thumb_hover' => 'none',
780 + 'thumb_symbols' => false,
781 + 'hide_post_titles' => false,
782 + 'excerpt_radio' => __( 'excerpt', 'category-posts' ),
783 + 'excerpt_lines' => 4,
784 + 'excerpt_length' => 0,
785 + 'excerpt_more_text' => __( '', 'category-posts' ),
786 + 'excerpt_filters' => false,
787 + 'comment_num' => false,
788 + 'disable_css' => false,
789 + 'disable_font_styles' => false,
790 + 'disable_theme_styles' => false,
791 + 'show_post_format' => 'none',
792 + 'no_cat_childs' => false,
793 + 'everything_is_link' => false,
794 + 'preset_date_format' => 'sitedate',
795 + 'date_format' => '',
796 + 'date_past_time' => '0',
797 + 'template' => "%title%\n\n%thumb%",
798 + 'text_do_not_wrap_thumb' => false,
799 + 'enable_loadmore' => false,
800 + 'loadmore_scrollTo' => false,
801 + 'loadmore_text' => sprintf( __( 'Load More (%s/%s)', 'category-posts' ), '%step%', '%all%'),
802 + 'loading_text' => __( 'Loading...', 'category-posts' ),
803 + 'date_range' => 'off',
804 + 'start_date' => '',
805 + 'end_date' => '',
806 + 'days_ago' => 30,
807 + 'no_match_handling' => 'nothing',
808 + 'no_match_text' => '',
809 + 'default_thunmbnail' => 0,
810 + 'ver' => VERSION,
811 + );
812 +}
813 +
814 +/**
815 + * Manipulate the relevant meta related to the short code when a post is save
816 + *
817 + * If A post has a short code, a meta holder is created, If it does not the meta holder is deleted
818 + *
819 + * @param integer $pid The post ID of the post being saved.
820 + * @param WP_Post $post The post being saved.
821 + * @return void
822 + *
823 + * @since 4.6
824 + */
825 +function save_post( $pid, $post ) {
826 +
827 + // ignore revisions and auto saves.
828 + if ( wp_is_post_revision( $pid ) || wp_is_post_autosave( $pid ) ) {
829 + return;
830 + }
831 +
832 + $meta = get_post_meta( $pid, SHORTCODE_META, true );
833 + if ( empty( $meta ) ) {
834 + $meta = array();
835 + }
836 +
837 + // check if only one shortcode format - non array of arrays, and convert it.
838 + if ( ! empty( $meta ) && ! is_array( reset( $meta ) ) ) {
839 + $meta = array( '' => $meta ); // the conversion.
840 + }
841 +
842 + $old_names = array_keys( $meta ); // keep list of current shortcode names to delete lter whatever was deleted.
843 + $names = shortcode_names( SHORTCODE_NAME, $post->post_content );
844 +
845 + // remove setting for unused names.
846 + $to_delete = array_diff( $old_names, $names );
847 + foreach ( $to_delete as $k ) {
848 + unset( $meta[ $k ] );
849 + }
850 +
851 + foreach ( $names as $name ) {
852 + if ( ! isset( $meta[ $name ] ) ) {
853 + $meta[ $name ] = default_settings();
854 + }
855 + }
856 +
857 + delete_post_meta( $pid, SHORTCODE_META );
858 + if ( ! empty( $meta ) ) {
859 + add_post_meta( $pid, SHORTCODE_META, $meta, true );
860 + }
861 +}
862 +
863 +add_action( 'save_post', __NAMESPACE__ . '\save_post', 10, 2 );
864 +
865 +/**
866 + * Called on Customizer init to do related registrations.
867 + *
868 + * @param mixed $wp_customize The customizer object.
869 + */
870 +function customize_register( $wp_customize ) {
871 +
872 + require_once __DIR__ . '/class-shortcode-control.php';
873 +
874 + $args = array(
875 + 'post_type' => 'any',
876 + 'post_status' => 'any',
877 + 'posts_per_page' => -1,
878 + 'update_post_term_cache' => false,
879 + 'meta_query' => array(
880 + array(
881 + 'key' => SHORTCODE_META,
882 + 'compare' => 'EXISTS',
883 + ),
884 + ),
885 +
886 + );
887 + $posts = get_posts( $args );
888 +
889 + if ( count( $posts ) > 0 ) {
890 + $wp_customize->add_panel(
891 + __NAMESPACE__,
892 + array(
893 + 'title' => __( 'Category Posts Shortcode', 'category-posts' ),
894 + 'priority' => 300,
895 + 'capability' => 'edit_theme_options',
896 + )
897 + );
898 +
899 + foreach ( $posts as $p ) {
900 + $widget = new Widget();
901 + $meta = get_post_meta( $p->ID, SHORTCODE_META, true );
902 + if ( ! is_array( $meta ) ) {
903 + continue;
904 + }
905 +
906 + if ( ! is_array( reset( $meta ) ) ) { // 4.6 format.
907 + $meta = array( '' => $meta );
908 + }
909 +
910 + foreach ( $meta as $k => $m ) {
911 +
912 + if ( isset( $m['template'] ) && $m['template'] ) {
913 + ;
914 + } else {
915 + $m['template'] = convert_settings_to_template( $m );
916 + }
917 +
918 + if ( 0 === count( $meta ) ) { // new widget, use defaults.
919 + ;
920 + } else { // updated widgets come from =< 4.6 excerpt filter is on.
921 + if ( ! isset( $m['excerpt_filters'] ) ) {
922 + $m['excerpt_filters'] = 'on';
923 + }
924 + }
925 +
926 + $m = upgrade_settings( $m );
927 +
928 + $section_title = $k;
929 + if ( '' === $section_title ) {
930 + $section_title = __( '[shortcode]', 'category-posts' );
931 + }
932 +
933 + $wp_customize->add_section(
934 + __NAMESPACE__ . '-' . $p->id . '-' . $k,
935 + array(
936 + 'title' => $section_title,
937 + 'priority' => 10,
938 + 'capability' => 'edit_theme_options',
939 + 'panel' => __NAMESPACE__,
940 + )
941 + );
942 +
943 + ob_start();
944 +
945 + // For the form method to handle generation gracefully, the number needs to be a simple string, and the name might include other chars as well, so for simplisity md5 it.
946 + if ( '' !== $k ) {
947 + $widget->number = $p->ID . '_' . md5( $k );
948 + } else {
949 + $widget->number = $p->ID . '_';
950 + }
951 +
952 + $widget->form( $m );
953 + $form = ob_get_clean();
954 + $form = preg_replace_callback(
955 + '/<(input|select|textarea)\s+.*name=("|\').*\[\w*\]\[([^\]]*)\][^>]*>/',
956 + function ( $matches ) use ( $p, $wp_customize, $m, $k ) {
957 + $setting = '_virtual-' . WIDGET_BASE_ID . '[' . $p->ID . '][' . $k . '][' . $matches[3] . ']';
958 + if ( ! isset( $m[ $matches[3] ] ) ) {
959 + $m[ $matches[3] ] = null;
960 + }
961 + $wp_customize->add_setting(
962 + $setting,
963 + array(
964 + 'default' => $m[ $matches[3] ], // set default to current value.
965 + 'type' => 'option',
966 + )
967 + );
968 +
969 + return str_replace( '<' . $matches[1], '<' . $matches[1] . ' data-customize-setting-link="' . $setting . '"', $matches[0] );
970 + },
971 + $form
972 + );
973 +
974 + $args = array(
975 + 'label' => __( 'Layout', 'twentyfourteen' ),
976 + 'section' => __NAMESPACE__ . '-' . $p->id . '-' . $k,
977 + 'form' => $form,
978 + 'settings' => '_virtual-' . WIDGET_BASE_ID . '[' . $p->ID . '][' . $k . '][title]',
979 + 'active_callback' => function () use ( $p ) {
980 + return is_singular() && ( get_the_ID() === $p->ID );
981 + },
982 + );
983 +
984 + if ( get_option( 'page_on_front' ) === $p->ID ) {
985 + $args['active_callback'] = function () {
986 + return is_front_page();
987 + };
988 + }
989 +
990 + $sc = new ShortCode_Control(
991 + $wp_customize,
992 + '_virtual-' . WIDGET_BASE_ID . '[' . $p->ID . '][' . $k . '][title]',
993 + $args
994 + );
995 +
996 + if ( '' !== $k ) {
997 + $sc->title_postfix = ' ' . $k;
998 + }
999 + $wp_customize->add_control( $sc );
1000 + }
1001 + }
1002 + }
1003 +}
1004 +
1005 +add_action( 'customize_register', __NAMESPACE__ . '\customize_register' );
1006 +
1007 +/**
1008 + * Save the virtual option used by the customizer into the proper meta values.
1009 + *
1010 + * The customizer actually saves only the changed values, so a merge needs to be done.
1011 + * After everything is updated the virtual option is deleted to leave a clean slate
1012 + *
1013 + * @return void
1014 + *
1015 + * @since 4.6
1016 + */
1017 +function customize_save_after() {
1018 + $virtual = get_option( '_virtual-' . WIDGET_BASE_ID );
1019 +
1020 + if ( is_array( $virtual ) ) {
1021 + foreach ( $virtual as $pid => $instance ) {
1022 + $meta = get_post_meta( $pid, SHORTCODE_META, true );
1023 + if ( ! empty( $meta ) && ! is_array( reset( $meta ) ) ) {
1024 + $meta = array( '' => $meta ); // the conversion.
1025 + }
1026 +
1027 + foreach ( $instance as $name => $new ) {
1028 + if ( isset( $meta[ $name ] ) ) { // unlikely but maybe that short code was deleted by other session.
1029 + $meta[ $name ] = array_merge( $meta[ $name ], $new );
1030 +
1031 + $meta[ $name ] = upgrade_settings( $meta[ $name ] );
1032 + }
1033 + }
1034 + }
1035 + update_post_meta( $pid, SHORTCODE_META, $meta );
1036 + }
1037 +
1038 + delete_option( '_virtual-' . WIDGET_BASE_ID );
1039 +}
1040 +
1041 +add_action( 'customize_save_after', __NAMESPACE__ . '\customize_save_after', 100 );
1042 +
1043 +/*
1044 + * tinymce related functions.
1045 + */
1046 +
1047 +/**
1048 + * Uninstall handler, cleanup DB from options and meta
1049 + *
1050 + * @return void
1051 + *
1052 + * @since 4.7
1053 + */
1054 +function uninstall() {
1055 + delete_option( 'widget-' . WIDGET_BASE_ID ); // delete the option storing the widget options.
1056 + delete_post_meta_by_key( SHORTCODE_META ); // delete the meta storing the shortcode.
1057 + // delete_metadata( 'user', 0, __NAMESPACE__, '', true ); // delete all user metadata. // df 2024-03-26 Deletion failed: https://wordpress.org/support/topic/deletion-failed-there-has-been-a-critical-error-on-this-website-5/
1058 +}
1059 +
1060 +register_uninstall_hook( __FILE__, __NAMESPACE__ . '\uninstall' );
1061 +
1062 +/**
1063 + * Register the tinymce shortcode plugin
1064 + *
1065 + * @param array $plugin_array An array containing the current plugins to be used by tinymce.
1066 + *
1067 + * @return array An array containing the plugins to be used by tinymce, our plugin added to the $plugin_array parameter
1068 + *
1069 + * @since 4.7
1070 + */
1071 +function mce_external_plugins( $plugin_array ) {
1072 + if ( current_user_can( 'edit_theme_options' ) ) { // don't load the code if the user can not customize the shortcode.
1073 + // enqueue TinyMCE plugin script with its ID.
1074 + $meta = get_user_meta( get_current_user_id(), __NAMESPACE__, true );
1075 + if ( is_array( $meta ) && isset( $meta['editor'] ) ) {
1076 + ;
1077 + } else {
1078 + $plugin_array[ __NAMESPACE__ ] = plugins_url( 'js/admin/tinymce.min.js?ver=' . VERSION, __FILE__ );
1079 + }
1080 + }
1081 +
1082 + return $plugin_array;
1083 +}
1084 +
1085 +add_filter( 'mce_external_plugins', __NAMESPACE__ . '\mce_external_plugins' );
1086 +
1087 +/**
1088 + * Register the tinymce buttons for the add shortcode
1089 + *
1090 + * @param array $buttons An array containing the current buttons to be used by tinymce.
1091 + *
1092 + * @return array An array containing the buttons to be used by tinymce, our button added to the $buttons parameter
1093 + *
1094 + * @since 4.7
1095 + */
1096 +function mce_buttons( $buttons ) {
1097 + if ( current_user_can( 'edit_theme_options' ) ) { // don't load the code if the user can not customize the shortcode
1098 + // register buttons with their id.
1099 + $meta = get_user_meta( get_current_user_id(), __NAMESPACE__, true );
1100 + if ( is_array( $meta ) && isset( $meta['editor'] ) ) {
1101 + ;
1102 + } else {
1103 + array_push( $buttons, __NAMESPACE__ );
1104 + }
1105 + }
1106 +
1107 + return $buttons;
1108 +}
1109 +
1110 +add_filter( 'mce_buttons', __NAMESPACE__ . '\mce_buttons' );
1111 +
1112 +/**
1113 + * Register the tinymcetranslation file
1114 + *
1115 + * @param array $locales An array containing the current translations to be used by tinymce.
1116 + *
1117 + * @return array An array containing the translations to be used by tinymce, our localization added to the $locale parameter
1118 + *
1119 + * @since 4.7
1120 + */
1121 +function mce_external_languages( $locales ) {
1122 + if ( current_user_can( 'edit_theme_options' ) ) { // don't load the code if the user can not customize the shortcode.
1123 + $meta = get_user_meta( get_current_user_id(), __NAMESPACE__, true );
1124 + if ( is_array( $meta ) && isset( $meta['editor'] ) ) {
1125 + ;
1126 + } else {
1127 + $locales['cat-posts'] = plugin_dir_path( __FILE__ ) . 'tinymce-translations.php';
1128 + }
1129 + }
1130 +
1131 + return $locales;
1132 +}
1133 +
1134 +add_filter( 'mce_external_languages', __NAMESPACE__ . '\mce_external_languages' );
1135 +
1136 +/**
1137 + * The Legacy Widget block widget-type filter
1138 + *
1139 + * @param array Excluded widget-types.
1140 + *
1141 + * @return array Widgets which are not available.
1142 + *
1143 + * @since 5.0
1144 + */
1145 +function hide_as_legacy_widget( $widget_types ) {
1146 + $widget_types[] = 'category-posts';
1147 +
1148 + return $widget_types;
1149 +}
1150 +
1151 +add_filter( 'widget_types_to_hide_from_legacy_widget_block', __NAMESPACE__ . '\hide_as_legacy_widget' );
1152 +
1153 +/*
1154 + * user profile related functions.
1155 + */
1156 +
1157 +add_action( 'show_user_profile', __NAMESPACE__ . '\show_user_profile' );
1158 +add_action( 'edit_user_profile', __NAMESPACE__ . '\show_user_profile' );
1159 +
1160 +/**
1161 + * Display the user specific setting on its profile page
1162 + *
1163 + * @param WP_user $user The user for which the profile page displays information.
1164 + *
1165 + * @since 4.7
1166 + */
1167 +function show_user_profile( $user ) {
1168 +
1169 + if ( ! current_user_can( 'edit_user', $user->ID ) ) {
1170 + return;
1171 + }
1172 +
1173 + if ( ! current_user_can( 'edit_theme_options', $user->ID ) ) {
1174 + return;
1175 + }
1176 +
1177 + $meta = get_the_author_meta( __NAMESPACE__, $user->ID );
1178 +
1179 + if ( empty( $meta ) ) {
1180 + $meta = array();
1181 + }
1182 +
1183 + $accordion = false;
1184 + if ( isset( $meta['panels'] ) ) {
1185 + $accordion = true;
1186 + }
1187 +
1188 + $editor = false;
1189 + if ( isset( $meta['editor'] ) ) {
1190 + $editor = true;
1191 + }
1192 + ?>
1193 + <h3 id="<?php echo __NAMESPACE__; ?>"><?php esc_html_e( 'Category Posts Widget behaviour settings', 'category-posts' ); ?></h3>
1194 +
1195 + <table class="form-table">
1196 + <tr>
1197 + <th><label for="<?php echo __NAMESPACE__; ?>[panels]"><?php esc_html_e( 'Open panels behavior', 'category-posts' ); ?></label></th>
1198 + <td>
1199 + <input type="checkbox" name="<?php echo __NAMESPACE__; ?>[panels]" id="<?php echo __NAMESPACE__; ?>[panels]" <?php checked( $accordion ); ?>>
1200 + <label for=<?php echo __NAMESPACE__; ?>[panels]><?php esc_html_e( 'Close the currently open panel when opening a new one', 'category-posts' ); ?></label>
1201 + </td>
1202 + </tr>
1203 + <tr>
1204 + <th><label for="<?php echo __NAMESPACE__; ?>[editor]"><?php esc_html_e( 'Visual editor button', 'category-posts' ); ?></label></th>
1205 + <td>
1206 + <input type="checkbox" name="<?php echo __NAMESPACE__; ?>[editor]" id="<?php echo __NAMESPACE__; ?>[editor]" <?php checked( $editor ); ?>>
1207 + <label for="<?php echo __NAMESPACE__; ?>[editor]"><?php esc_html_e( 'Hide the "insert shortcode" button from the editor', 'category-posts' ); ?></label>
1208 + </td>
1209 + </tr>
1210 + </table>
1211 + <?php
1212 +}
1213 +
1214 +add_action( 'personal_options_update', __NAMESPACE__ . '\personal_options_update' );
1215 +add_action( 'edit_user_profile_update', __NAMESPACE__ . '\personal_options_update' );
1216 +
1217 +/**
1218 + * Handles saving user related settings as was set in the profile page.
1219 + *
1220 + * @param int $user_id the ID of the user for which the data is saved..
1221 + *
1222 + * @since 4.7
1223 + */
1224 +function personal_options_update( $user_id ) {
1225 +
1226 + if ( ! current_user_can( 'edit_user', $user_id ) ) {
1227 + return false;
1228 + }
1229 +
1230 + if ( ! current_user_can( 'edit_theme_options', $user_id ) ) {
1231 + return;
1232 + }
1233 +
1234 + if ( isset( $_POST[ __NAMESPACE__ ] ) ) {
1235 + update_user_meta( $user_id, __NAMESPACE__, wp_unslash( $_POST[ __NAMESPACE__ ] ) );
1236 + } else {
1237 + delete_user_meta( $user_id, __NAMESPACE__ );
1238 + }
1239 +}
1240 +
1241 +add_action( 'wp_loaded', __NAMESPACE__ . '\wp_loaded' );
1242 +
1243 +/**
1244 + * Run after WordPress finished bootstrapping, do whatever is needed at this stage
1245 + * like registering the meta.
1246 + */
1247 +function wp_loaded() {
1248 + register_meta( 'post', SHORTCODE_META, null, '__return_false' ); // do not allow access to the shortcode meta
1249 + // use the pre 4.6 format for backward compatibility.
1250 +}