HEX
Server: Apache
System: Linux server1.panigaletech.com 5.4.0-1103-aws #111~18.04.1-Ubuntu SMP Tue May 23 20:04:10 UTC 2023 x86_64
User: ubuntu (1000)
PHP: 7.4.30
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /var/www/concertium.com/public_html/wp-content/plugins/category-posts/cat-posts.php
<?php
/**
 * Main file of the plugin
 *
 * @package categoryposts.
 *
 * @since 1.0
 */

/*
Plugin Name: Category Posts Widget
Plugin URI: https://wordpress.org/plugins/category-posts/
Description: Adds a widget that shows the most recent posts from a single category.
Author: TipTopPress
Version: 4.9.13
Author URI: https://tiptoppress.com
Text Domain: category-posts
Domain Path: /languages
*/

namespace categoryPosts;

// Don't call the file directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

const VERSION        = '4.9.13';
const DOC_URL        = 'https://tiptoppress.com/category-posts-widget/documentation-4-9/';
const PRO_URL        = 'https://tiptoppress.com/term-and-category-based-posts-widget/';
const SUPPORT_URL    = 'https://wordpress.org/support/plugin/category-posts/';
const SHORTCODE_NAME = 'catposts';
const SHORTCODE_META = 'categoryPosts-shorcode';
const WIDGET_BASE_ID = 'category-posts';

require_once __DIR__ . '/class-virtual-widget.php';
require_once __DIR__ . '/class-virtual-widgets-repository.php';
require_once __DIR__ . '/class-widget.php';
require_once __DIR__ . '/loadmore.php';
require_once __DIR__ . '/localizeddate.php';

/**
 *  Adds the "Customize" link to the Toolbar on edit mode.
 *
 *  @since 4.6
 */
function wp_admin_bar_customize_menu() {
	global $wp_admin_bar;

	if ( ! isset( $_GET['action'] ) || ( 'edit' !== $_GET['action'] ) ) {
		return;
	}

	if ( ! current_user_can( 'customize' ) || ! is_admin() || ! is_user_logged_in() || ! is_admin_bar_showing() ) {
		return;
	}

	$current_url = '';
	if ( isset( $_GET['post'] ) && ( '' !== $_GET['post'] ) ) {
		$current_url = get_permalink( absint( $_GET['post'] ) );
	}
	$customize_url = add_query_arg( 'url', rawurlencode( $current_url ), wp_customize_url() );

	$p = isset( $_GET['post'] ) && ! empty( $_GET['post'] ) ? get_post( absint( $_GET['post'] ) ) : false;
	$names = $p ? shortcode_names( SHORTCODE_NAME, $p->post_content ) : array();
	if ( empty( $names ) ) {
		return;
	}

	$wp_admin_bar->add_menu(
		array(
			'id'    => 'customize',
			'title' => __( 'Customize' ),
			'href'  => $customize_url,
			'meta'  => array(
				'class' => 'hide-if-no-customize',
			),
		)
	);
	add_action( 'wp_before_admin_bar_render', 'wp_customize_support_script' );
}

add_action( 'admin_bar_menu', __NAMESPACE__ . '\wp_admin_bar_customize_menu', 35 );

/**
 * Print out required CSS, hooked on the wp_head hook.
 */
function wp_head() {

	$widget_repository = new Virtual_Widgets_Repository();

	$styles = array();

	foreach ( $widget_repository->getShortcodes() as $widget ) {
		$widget->getCSSRules( true, $styles );
	}

	foreach ( $widget_repository->getWidgets() as $widget ) {
		$widget->getCSSRules( false, $styles );
	}

	if ( ! empty( $styles ) ) {
		?>
<style>
		<?php
		foreach ( $styles as $rules ) {
			foreach ( $rules as $rule ) {
				echo "$rule\n"; // Xss ok. raw css output, can not be html escaped.
			}
		}
		?>
</style>
		<?php
	}
}

add_action( 'wp_head', __NAMESPACE__ . '\register_virtual_widgets', 0 );

/**
 *  Register virtual widgets for all widgets and shortcodes that are going to be displayed on the page
 *
 *  @return void
 *
 *  @since 4.7
 */
function register_virtual_widgets() {
	global $post;
	global $wp_registered_widgets;

	$repository = new Virtual_Widgets_Repository();

	// check first for shortcode settings.
	if ( is_singular() ) {
		$names = shortcode_names( SHORTCODE_NAME, $post->post_content );

		foreach ( $names as $name ) {
			$meta = shortcode_settings( get_the_ID(), $name );
			if ( is_array( $meta ) ) {
				$id = WIDGET_BASE_ID . '-shortcode-' . get_the_ID(); // needed to make a unique id for the widget html element.
				if ( '' !== $name ) { // if not default name append to the id.
					$id .= '-' . sanitize_title( $name ); // sanitize to be on the safe side, not sure where when and how this will be used.
				}
				$repository->addShortcode( $name, new Virtual_Widget( $id, WIDGET_BASE_ID . '-shortcode', $meta ) );
			}
		}
	}

	$sidebars_widgets = wp_get_sidebars_widgets();

	if ( is_array( $sidebars_widgets ) ) {
		foreach ( $sidebars_widgets as $sidebar => $widgets ) {
			if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) {
				continue;
			}

			if ( is_array( $widgets ) ) {
				foreach ( $widgets as $widget ) {
					$widget_base = _get_widget_id_base( $widget );
					if ( WIDGET_BASE_ID === $widget_base ) {
						$class = __NAMESPACE__ . '\Widget';
						$widgetclass = new $class();
						$allsettings = $widgetclass->get_settings();
						$settings = isset( $allsettings[ str_replace( $widget_base . '-', '', $widget ) ] ) ? $allsettings[ str_replace( $widget_base . '-', '', $widget ) ] : false;
						$repository->addWidget( $widget, new Virtual_Widget( $widget, $widget, $settings ) );
					}
				}
			}
		}
	}
}

add_action( 'wp_head', __NAMESPACE__ . '\wp_head' );

/**
 * Enqueue widget related scripts for the widget front-end
 *
 * @since 4.8
 */
function frontend_script() {
	$suffix = 'min.js';
	if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
		$suffix = 'js';
	}
	wp_enqueue_script( 'cat-posts-frontend-js', plugins_url( 'js/frontend/category-posts-frontend.' . $suffix, __FILE__ ), array( 'jquery' ), VERSION, true );
}

/**
 * Embed the front end JS in the HTML footer.
 *
 * @since 4.9
 */
function embed_front_end_scripts() {
	$suffix = 'min.js';
	if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
		$suffix = 'js';
	}
	echo '<script>';
	include __DIR__ . '/js/frontend/category-posts-frontend.' . $suffix;
	echo '</script>';
}

/**
 * Enqueue widget related scripts for the widget admin page and customizer.
 *
 * @param string $hook the name of the admin hook for which the function was triggered.
 */
function admin_scripts( $hook ) {

	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).

		/*
		 * Add script to control admin UX.
		 */

		// Use unminified version of JS when debuging, and minified when not.
		$suffix = 'min.js';
		if ( defined( 'WP_DEBUG' ) && WP_DEBUG === true ) {
			$suffix = 'js';
		}
		wp_register_script( 'category-posts-widget-admin-js', plugins_url( 'js/admin/category-posts-widget.' . $suffix, __FILE__ ), array( 'jquery' ), VERSION, true );
		wp_enqueue_script( 'category-posts-widget-admin-js' );

		$js_data = array( 'accordion' => false );
		$meta = get_user_meta( get_current_user_id(), __NAMESPACE__, true );
		if ( is_array( $meta ) && isset( $meta['panels'] ) ) {
			$js_data['accordion'] = true;
		}
		$js_data['template_tags'] = get_template_tags();
		$js_data[ __NAMESPACE__ ] = $js_data; // To make accessing the data in JS easier to understand.

		wp_localize_script( 'category-posts-widget-admin-js', 'tiptoppress', $js_data );
		wp_enqueue_media();
		wp_localize_script(
			'category-posts-widget-admin-js',
			'cwp_default_thumb_selection',
			array(
				'frame_title'  => __( 'Select a default thumbnail', 'category-posts' ),
				'button_title' => __( 'Select', 'category-posts' ),
				'none'         => __( 'None', 'category-posts' ),
			)
		);
	}
}

add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\admin_scripts' ); // "called on widgets.php and costumizer since 3.9.

add_action( 'admin_init', __NAMESPACE__ . '\load_textdomain' );

/**
 * Load plugin textdomain.
 *
 * @return void
 *
 * @since 4.1
 **/
function load_textdomain() {
	load_plugin_textdomain( 'category-posts', false, plugin_basename( dirname( __FILE__ ) ) . '/languages' );
}

/**
 * Add styles for widget admin sections
 *
 * @since 4.1
 **/
function admin_styles() {
	wp_enqueue_style( 'cat-posts-admin-styles', plugins_url( 'styles/admin/category-posts-widget.css', __FILE__ ), array(), VERSION, false );
}

add_action( 'admin_print_styles-widgets.php', __NAMESPACE__ . '\admin_styles' );

// fix make widget SiteOrigin Page Builder plugin, GH issue #181.
add_action( 'siteorigin_panel_enqueue_admin_scripts', __NAMESPACE__ . '\admin_styles' );

/**
 *  Get the tags which might be used in the template.
 *
 *  @since 4.8
 *
 *  @return array Array of strings of the tags.
 */
function get_template_tags() {
	return array( 'author', 'title', 'date', 'thumb', 'excerpt', 'commentnum', 'post_tag', 'category', 'more-link' );
}

/**
 *  Get a regex to parse the template in order to find the tags used in it.
 *
 *  @since 4.8
 *
 *  @return string The template parsing regex.
 */
function get_template_regex() {
	$tags = get_template_tags();

	$regexp = '';
	foreach ( $tags as $t ) {
		if ( ! empty( $regexp ) ) {
			$regexp .= '|';
		}
		$regexp .= '%' . $t . '%';
	}

	$regexp = '@(' . $regexp . ')@i';

	return $regexp;
}

/**
 * The convert old data structure to current one.
 *
 * @param array $settings The settings to upgrade.
 *
 * @return array The settings matching current version standard.
 *
 * @since 4.8
 */
function upgrade_settings( $settings ) {

	if ( isset( $setting ) && is_countable( $setting ) && 0 === count( $settings ) ) {
		return default_settings();
	}

	if ( ! isset( $settings['ver'] ) ) {
		/*
		 * Pre 4.9 version.
		 */

		// Upgrade the hide if empty option.
		if ( isset( $settings['hide_if_empty'] ) && $settings['hide_if_empty'] ) {
			$settings['no_match_handling'] = 'hide';
		} else {
			$settings['no_match_handling'] = 'nothing';
		}
		if ( isset( $settings['hide_if_empty'] ) ) {
			unset( $settings['hide_if_empty'] );
		}
	} else {
		if ( version_compare( '4.9.8', $settings['ver']) ) {
			if ( isset( $settings['preset_date_format'] ) && 'sincepublished' === $settings['preset_date_format'] ) {
				$settings['date_past_time']     = 999;
				$settings['preset_date_format'] = 'sitedate';
			}
		}
	}

	$settings['ver'] = VERSION;

	// Make sure all "empty" settings have default value.
	$settings = wp_parse_args( $settings, default_settings() );

	return $settings;
}

/**
 * Convert pre 4.8 settings into template
 *
 * @param  array $instance Array which contains the various settings.
 *
 * @since 4.8
 */
function convert_settings_to_template( $instance ) {

	$template = '';

	if ( isset( $instance['thumb'] ) && $instance['thumb'] ) {
		if ( isset( $instance['thumbTop'] ) && $instance['thumbTop'] ) {
			$template .= "%thumb%\n\n";
			if ( ! ( isset( $instance['hide_post_titles'] ) && $instance['hide_post_titles'] ) ) {
				$template .= "%title%\n";
			}
		} elseif ( isset( $instance['date'] ) && $instance['date'] ) {
			if ( ! ( isset( $instance['hide_post_titles'] ) && $instance['hide_post_titles'] ) ) {
				$template .= "%title%\n\n";
			}
			$template .= "%date%\n\n";
			$template .= "%thumb%\n";
		} elseif ( ! ( isset( $instance['hide_post_titles'] ) && $instance['hide_post_titles'] ) ) {
			$template .= "%thumb%\n%title%\n";
		}
	} else {
		if ( ! ( isset( $instance['hide_post_titles'] ) && $instance['hide_post_titles'] ) ) {
			$template .= "%title%\n";
		}
		if ( isset( $instance['date'] ) && $instance['date'] ) {
			$template .= "%date%\n\n";
		}
	}
	if ( isset( $instance['excerpt'] ) && $instance['excerpt'] ) {
		$template .= '%excerpt%';
	}
	if ( isset( $instance['comment_num'] ) && $instance['comment_num'] ) {
		$template .= "%commentnum%\n\n";
	}
	if ( isset( $instance['author'] ) && $instance['author'] ) {
		$template .= "%author%\n\n";
	}

	return $template;
}

/*
 * Plugin action links section
 */

/**
 *  Applied to the list of links to display on the plugins page (beside the activate/deactivate links).
 *
 *  @return array of the widget links
 *
 *  @since 4.6.3
 */
add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), __NAMESPACE__ . '\add_action_links' );

/**
 * Handle the add links filter, add our links to the links displayed under the plugin_basename
 * in the plugin admin screen.
 *
 * @param array $links The current links about to be displayed.
 */
function add_action_links( $links ) {

	if ( ! class_exists( '\\termcategoryPostsPro\\Widget' ) ) {
		$pro_link = array(
			'<a target="_blank" href="' . esc_url( PRO_URL ) . '">' . esc_html__( 'Get the Pro version', 'category-posts' ) . '</a>',
		);

		$links = array_merge( $pro_link, $links );
	}

	return $links;
}

/**
 * Register the widget.
 */
function register_widget() {
	return \register_widget( __NAMESPACE__ . '\Widget' );
}

add_action( 'widgets_init', __NAMESPACE__ . '\register_widget' );

/**
 * Output js code to set the image height if float text around image.
 *
 * @param int   $number         The widget number used to identify the specific list.
 * @param array $widgetsettings The "instance" parameters of the widget.
 *
 * @since 4.9
 **/
function equal_cover_content_height( $number, $widgetsettings ) {

	if ( isset( $widgetsettings['template'] ) && preg_match( '/%thumb%|%excerpt%/', $widgetsettings['template'] ) ) :
		?>
		<script type="text/javascript">
			if (typeof jQuery !== 'undefined') {

				var cat_posts_namespace              = window.cat_posts_namespace || {};
				cat_posts_namespace.layout_wrap_text = cat_posts_namespace.layout_wrap_text || {};
				cat_posts_namespace.layout_img_size  = cat_posts_namespace.layout_img_size || {};

				cat_posts_namespace.layout_wrap_text = {
					<?php	/* Handle item */ echo "\r\n"; ?>
					preWrap : function (widget) {
						// var _widget = jQuery(widget);
						jQuery(widget).find('.cat-post-item').each(function(){
							var _that = jQuery(this);
							_that.find('p.cpwp-excerpt-text').addClass('cpwp-wrap-text');
							_that.find('p.cpwp-excerpt-text').closest('div').wrap('<div class="cpwp-wrap-text-stage"></div>');;
						});
						return;
					},
					<?php	/* Handle add class */ echo "\r\n"; ?>
					add : function(_this){
						var _that = jQuery(_this);
						if (_that.find('p.cpwp-excerpt-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"; ?>
							_that.find('p.cpwp-excerpt-text').closest('.cpwp-wrap-text-stage').removeClass( "cpwp-wrap-text" );
							_that.find('p.cpwp-excerpt-text').addClass( "cpwp-wrap-text" ); <?php /* don't do the CSS hack, just set the class */ echo "\r\n"; ?>
						}else{ <?php /* add the CSS hack, it's needed, text is wrapping, line's height is higher as thumb */ echo "\r\n"; ?>
							_that.find('p.cpwp-excerpt-text').removeClass( "cpwp-wrap-text" );
							_that.find('p.cpwp-excerpt-text').closest('.cpwp-wrap-text-stage').addClass( "cpwp-wrap-text" ); <?php /* text is wrapping, do the CSS hack */ echo "\r\n"; ?>
						}
						return;
					},
					<?php	/* Wait for image is loaded */ echo "\r\n"; ?>
					handleLazyLoading : function(_this) {
						var width = jQuery(_this).find('img').width();
						<?php	/* image is loaded */ echo "\r\n"; ?>
						if( 0 !== width ){
							cat_posts_namespace.layout_wrap_text.add(_this);
						} else {
							jQuery(_this).find('img').one("load", function(){
								cat_posts_namespace.layout_wrap_text.add(_this);
							});
						}
						return;
					},
					<?php /* Handle post items */ echo "\r\n"; ?>
					setClass : function (widget) {
						// var _widget = jQuery(widget);
						jQuery(widget).find('.cat-post-item').each(function(){
							cat_posts_namespace.layout_wrap_text.handleLazyLoading(this);
						});
						return;
					}
				}
				cat_posts_namespace.layout_img_size = {
					<?php	/* Handle replace */ echo "\r\n"; ?>
					replace : function(_this){
						var _that = jQuery(_this),
						resp_w = _that.width(),
						resp_h = _that.height(),
						orig_w = _that.data('cat-posts-width'),
						orig_h = _that.data('cat-posts-height');
						<?php	/* replace height */ echo "\r\n"; ?>
						if( resp_w < orig_w ){
							_that.height( resp_w * orig_h / orig_w );
						} else {
							_that.height( '' );
						}
						return;
					},
					<?php	/* Wait for image is loaded */ echo "\r\n"; ?>
					handleLazyLoading : function(_this) {
						var width = jQuery(_this).width();
						<?php	/* image is loaded */ echo "\r\n"; ?>
						if( 0 !== width ){
							cat_posts_namespace.layout_img_size.replace(_this);
						} else {
							jQuery(_this).one("load", function(){
								cat_posts_namespace.layout_img_size.replace(_this);
							});
						}
						return;
					},
					setHeight : function (widget) {
						jQuery(widget).find('.cat-post-item img').each(function(){
							cat_posts_namespace.layout_img_size.handleLazyLoading(this);
						});
						return;
					}
				}

				let widget = jQuery('#<?php echo esc_attr( $number ); ?>');

				jQuery( document ).ready(function () {
					cat_posts_namespace.layout_wrap_text.preWrap(widget);
					cat_posts_namespace.layout_wrap_text.setClass(widget);
					<?php	/* No ratio calculation if one or more dimensions is set to 0 */ echo "\r\n"; ?>
					<?php	if ( isset( $widgetsettings['thumb_w'] ) && 0 !== intval( $widgetsettings['thumb_w'] ) &&
								isset( $widgetsettings['thumb_h'] ) && 0 !== intval( $widgetsettings['thumb_h'] ) ) : echo "\r\n"; ?>
						cat_posts_namespace.layout_img_size.setHeight(widget);
					<?php	endif; echo "\r\n"; ?>
				});

				jQuery(window).on('load resize', function() {
					cat_posts_namespace.layout_wrap_text.setClass(widget);
					<?php	/* No ratio calculation if one or more dimensions is set to 0 */ echo "\r\n"; ?>
					<?php	if ( isset( $widgetsettings['thumb_w'] ) && 0 !== intval( $widgetsettings['thumb_w'] ) &&
								isset( $widgetsettings['thumb_h'] ) && 0 !== intval( $widgetsettings['thumb_h'] ) ) : echo "\r\n"; ?>
						cat_posts_namespace.layout_img_size.setHeight(widget);
					<?php	endif; echo "\r\n"; ?>
				});

			}
		</script>
		<?php
	endif;
}

/*
 * shortcode section.
 */

/**
 * Get shortcode settings taking into account if it is being customized
 *
 * When not customized returns the settings as stored in the meta, but when
 * it is customized returns the setting stored in the virtual option used by the customizer
 *
 * @param string $pid  The ID of the post in which the shortcode is.
 * @param string $name The name of the shortcode to retun, empty string indicates the nameless.
 *
 * @return array The shortcode settings if a short code name exists or is an empty string,
 *               empty array if name not found.
 *
 * @since 4.6
 */
function shortcode_settings( $pid, $name ) {
	$meta = get_post_meta( $pid, SHORTCODE_META, true );

	if ( ! empty( $meta ) && ! is_array( reset( $meta ) ) ) {
		$meta = array( '' => $meta );  // the conversion.
	}

	if ( ! isset( $meta[ $name ] ) ) { // name do not exists? return empty array.
		return array();
	}

	$instance = $meta[ $name ];
	if ( is_customize_preview() ) {
		$o = get_option( '_virtual-' . WIDGET_BASE_ID );
		if ( is_array( $o ) ) {
			$instance = $o[ $pid ][ $name ];
			$instance['ver'] = VERSION;
		}
	}

	if ( isset( $instance['template'] ) && $instance['template'] ) {
		;
	} else {
		$instance['template'] = convert_settings_to_template( $instance );
	}

	$instance = upgrade_settings( $instance );

	return $instance;
}

/**
 *  Handle the shortcode
 *
 *  @param array  $attr Array of the attributes to the short code, none is expected.
 *  @param string $content The content enclosed in the shortcode, none is expected.
 *
 *  @return string An HTML of the "widget" based on its settings, actual or customized
 */
function shortcode( $attr, $content = null ) {
	$repository = new Virtual_Widgets_Repository();

	$shortcodes = $repository->getShortcodes();

	$name = '';
	if ( isset( $attr['name'] ) ) {
		$name = $attr['name'];
	}

	if ( is_singular() ) {
		if ( isset( $shortcodes[ $name ] ) ) {
			return $shortcodes[ $name ]->getHTML();
		}
	}

	return '';
}

add_shortcode( SHORTCODE_NAME, __NAMESPACE__ . '\shortcode' );

/**
 *  Find if a specific shortcode is used in a content
 *
 *  @param string $shortcode_name The name of the shortcode.
 *  @param string $content The content to look at.
 *
 *  @return array An array containing the name attributes of the shortcodes. Empty array is
 *                an indication there were no shourcodes
 *
 *  @since 4.7
 */
function shortcode_names( $shortcode_name, $content ) {

	$names = array();

	$regex_pattern = get_shortcode_regex();
	if ( preg_match_all( '/' . $regex_pattern . '/s', $content, $matches ) ) {
		foreach ( $matches[2] as $k => $shortcode ) {
			if ( SHORTCODE_NAME === $shortcode ) {
				$name = '';
				$atts = shortcode_parse_atts( $matches[3][ $k ] );
				if ( ! empty( $atts['name'] ) ) {
					$name = $atts['name'];
				}
				$names[] = $name;
			}
		}
	}

	return $names;
}

/**
 *  Organized way to have the default widget settings accessible
 *
 *  @since 4.6
 */
function default_settings() {
	return array(
		'title'                  => __( 'Category Posts', 'category-posts' ),
		'title_link'             => false,
		'title_level'            => 'Initial',
		'title_link_url'         => '',
		'hide_title'             => false,
		'cat'                    => 0,
		'num'                    => get_option( 'posts_per_page' ),
		'offset'                 => 1,
		'sort_by'                => 'date',
		'status'                 => 'publish',
		'asc_sort_order'         => false,
		'exclude_current_post'   => false,
		'hideNoThumb'            => false,
		'sticky'                 => false,
		'footer_link_text'       => '',
		'footer_link'            => '',
		'item_title_level'       => 'Inline',
		'item_title_lines'       => 2,
		'thumb_w'                => get_option( 'thumbnail_size_w', 150 ),
		'thumb_fluid_width'      => 100,
		'thumb_h'                => get_option( 'thumbnail_size_h', 150 ),
		'thumb_hover'            => 'none',
		'hide_post_titles'       => false,
		'excerpt_lines'          => 4,
		'excerpt_length'         => 0,
		'excerpt_more_text'      => __( '', 'category-posts' ),
		'excerpt_filters'        => false,
		'comment_num'            => false,
		'disable_css'            => false,
		'disable_font_styles'    => false,
		'disable_theme_styles'   => false,
		'show_post_format'       => 'none',
		'no_cat_childs'          => false,
		'everything_is_link'     => false,
		'preset_date_format'     => 'sitedate',
		'date_format'            => '',
		'date_past_time'         => '0',
		'template'               => "%title%\n\n%thumb%",
		'text_do_not_wrap_thumb' => false,
		'enable_loadmore'        => false,
		'loadmore_scrollTo'      => false,
		'loadmore_text'          => sprintf( __( 'Load More (%s/%s)', 'category-posts' ), '%step%', '%all%'),
		'loading_text'           => __( 'Loading...', 'category-posts' ),
		'date_range'             => 'off',
		'start_date'             => '',
		'end_date'               => '',
		'days_ago'               => 30,
		'no_match_handling'      => 'nothing',
		'no_match_text'          => '',
		'default_thunmbnail'     => 0,
		'ver'                    => VERSION,
	);
}

/**
 *  Manipulate the relevant meta related to the short code when a post is save
 *
 *  If A post has a short code, a meta holder is created, If it does not the meta holder is deleted
 *
 *  @param integer $pid  The post ID of the post being saved.
 *  @param WP_Post $post The post being saved.
 *  @return void
 *
 *  @since 4.6
 */
function save_post( $pid, $post ) {

	// ignore revisions and auto saves.
	if ( wp_is_post_revision( $pid ) || wp_is_post_autosave( $pid ) ) {
		return;
	}

	$meta = get_post_meta( $pid, SHORTCODE_META, true );
	if ( empty( $meta ) ) {
		$meta = array();
	}

	// check if only one shortcode format - non array of arrays, and convert it.
	if ( ! empty( $meta ) && ! is_array( reset( $meta ) ) ) {
		$meta = array( '' => $meta );  // the conversion.
	}

	$old_names = array_keys( $meta ); // keep list of current shortcode names to delete lter whatever was deleted.
	$names = shortcode_names( SHORTCODE_NAME, $post->post_content );

	// remove setting for unused names.
	$to_delete = array_diff( $old_names, $names );
	foreach ( $to_delete as $k ) {
		unset( $meta[ $k ] );
	}

	foreach ( $names as $name ) {
		if ( ! isset( $meta[ $name ] ) ) {
			$meta[ $name ] = default_settings();
		}
	}

	delete_post_meta( $pid, SHORTCODE_META );
	if ( ! empty( $meta ) ) {
		add_post_meta( $pid, SHORTCODE_META, $meta, true );
	}
}

add_action( 'save_post', __NAMESPACE__ . '\save_post', 10, 2 );

/**
 * Called on Customizer init to do related registrations.
 *
 * @param mixed $wp_customize The customizer object.
 */
function customize_register( $wp_customize ) {

	require_once __DIR__ . '/class-shortcode-control.php';

	$args = array(
		'post_type'              => 'any',
		'post_status'            => 'any',
		'posts_per_page'         => -1,
		'update_post_term_cache' => false,
		'meta_query'             => array(
			array(
				'key'     => SHORTCODE_META,
				'compare' => 'EXISTS',
			),
		),

	);
	$posts = get_posts( $args );

	if ( count( $posts ) > 0 ) {
		$wp_customize->add_panel(
			__NAMESPACE__,
			array(
				'title'      => __( 'Category Posts Shortcode', 'category-posts' ),
				'priority'   => 300,
				'capability' => 'edit_theme_options',
			)
		);

		foreach ( $posts as $p ) {
			$widget = new Widget();
			$meta = get_post_meta( $p->ID, SHORTCODE_META, true );
			if ( ! is_array( $meta ) ) {
				continue;
			}

			if ( ! is_array( reset( $meta ) ) ) { // 4.6 format.
				$meta = array( '' => $meta );
			}

			foreach ( $meta as $k => $m ) {

				if ( isset( $m['template'] ) && $m['template'] ) {
					;
				} else {
					$m['template'] = convert_settings_to_template( $m );
				}

				if ( 0 === count( $meta ) ) { // new widget, use defaults.
					;
				} else { // updated widgets come from =< 4.6 excerpt filter is on.
					if ( ! isset( $m['excerpt_filters'] ) ) {
						$m['excerpt_filters'] = 'on';
					}
				}

				$m = upgrade_settings( $m );

				$section_title = $k;
				if ( '' === $section_title ) {
					$section_title = __( '[shortcode]', 'category-posts' );
				}

				$wp_customize->add_section(
					__NAMESPACE__ . '-' . $p->id . '-' . $k,
					array(
						'title'      => $section_title,
						'priority'   => 10,
						'capability' => 'edit_theme_options',
						'panel'      => __NAMESPACE__,
					)
				);

				ob_start();

				// 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.
				if ( '' !== $k ) {
					$widget->number = $p->ID . '_' . md5( $k );
				} else {
					$widget->number = $p->ID . '_';
				}

				$widget->form( $m );
				$form = ob_get_clean();
				$form = preg_replace_callback(
					'/<(input|select|textarea)\s+.*name=("|\').*\[\w*\]\[([^\]]*)\][^>]*>/',
					function ( $matches ) use ( $p, $wp_customize, $m, $k ) {
						$setting = '_virtual-' . WIDGET_BASE_ID . '[' . $p->ID . '][' . $k . '][' . $matches[3] . ']';
						if ( ! isset( $m[ $matches[3] ] ) ) {
							$m[ $matches[3] ] = null;
						}
						$wp_customize->add_setting(
							$setting,
							array(
								'default' => $m[ $matches[3] ], // set default to current value.
								'type'    => 'option',
							)
						);

						return str_replace( '<' . $matches[1], '<' . $matches[1] . ' data-customize-setting-link="' . $setting . '"', $matches[0] );
					},
					$form
				);

				$args = array(
					'label'           => __( 'Layout', 'twentyfourteen' ),
					'section'         => __NAMESPACE__ . '-' . $p->id . '-' . $k,
					'form'            => $form,
					'settings'        => '_virtual-' . WIDGET_BASE_ID . '[' . $p->ID . '][' . $k . '][title]',
					'active_callback' => function () use ( $p ) {
						return is_singular() && ( get_the_ID() === $p->ID );
					},
				);

				if ( get_option( 'page_on_front' ) === $p->ID ) {
					$args['active_callback'] = function () {
						return is_front_page();
					};
				}

				$sc = new ShortCode_Control(
					$wp_customize,
					'_virtual-' . WIDGET_BASE_ID . '[' . $p->ID . '][' . $k . '][title]',
					$args
				);

				if ( '' !== $k ) {
					$sc->title_postfix = ' ' . $k;
				}
				$wp_customize->add_control( $sc );
			}
		}
	}
}

add_action( 'customize_register', __NAMESPACE__ . '\customize_register' );

/**
 *  Save the virtual option used by the customizer into the proper meta values.
 *
 *  The customizer actually saves only the changed values, so a merge needs to be done.
 *  After everything is updated the virtual option is deleted to leave a clean slate
 *
 *  @return void
 *
 *  @since 4.6
 */
function customize_save_after() {
	$virtual = get_option( '_virtual-' . WIDGET_BASE_ID );

	if ( is_array( $virtual ) ) {
		foreach ( $virtual as $pid => $instance ) {
			$meta = get_post_meta( $pid, SHORTCODE_META, true );
			if ( ! empty( $meta ) && ! is_array( reset( $meta ) ) ) {
				$meta = array( '' => $meta );  // the conversion.
			}

			foreach ( $instance as $name => $new ) {
				if ( isset( $meta[ $name ] ) ) { // unlikely but maybe that short code was deleted by other session.
					$meta[ $name ] = array_merge( $meta[ $name ], $new );

					$meta[ $name ] = upgrade_settings( $meta[ $name ] );
				}
			}
		}
		update_post_meta( $pid, SHORTCODE_META, $meta );
	}

	delete_option( '_virtual-' . WIDGET_BASE_ID );
}

add_action( 'customize_save_after', __NAMESPACE__ . '\customize_save_after', 100 );

/*
 * tinymce related functions.
 */

/**
 *  Uninstall handler, cleanup DB from options and meta
 *
 *  @return void
 *
 *  @since 4.7
 */
function uninstall() {
	delete_option( 'widget-' . WIDGET_BASE_ID ); // delete the option storing the widget options.
	delete_post_meta_by_key( SHORTCODE_META ); // delete the meta storing the shortcode.
	delete_metadata( 'user', 0, __NAMESPACE__, '', true );  // delete all user metadata.
}

register_uninstall_hook( __FILE__, __NAMESPACE__ . 'uninstall' );

/**
 *  Register the tinymce shortcode plugin
 *
 *  @param array $plugin_array An array containing the current plugins to be used by tinymce.
 *
 *  @return array An array containing the plugins to be used by tinymce, our plugin added to the $plugin_array parameter
 *
 *  @since 4.7
 */
function mce_external_plugins( $plugin_array ) {
	if ( current_user_can( 'edit_theme_options' ) ) { // don't load the code if the user can not customize the shortcode.
		// enqueue TinyMCE plugin script with its ID.
		$meta = get_user_meta( get_current_user_id(), __NAMESPACE__, true );
		if ( is_array( $meta ) && isset( $meta['editor'] ) ) {
			;
		} else {
			$plugin_array[ __NAMESPACE__ ] = plugins_url( 'js/admin/tinymce.min.js?ver=' . VERSION, __FILE__ );
		}
	}

	return $plugin_array;
}

add_filter( 'mce_external_plugins', __NAMESPACE__ . '\mce_external_plugins' );

/**
 *  Register the tinymce buttons for the add shortcode
 *
 *  @param array $buttons An array containing the current buttons to be used by tinymce.
 *
 *  @return array An array containing the buttons to be used by tinymce, our button added to the $buttons parameter
 *
 *  @since 4.7
 */
function mce_buttons( $buttons ) {
	if ( current_user_can( 'edit_theme_options' ) ) { // don't load the code if the user can not customize the shortcode
		// register buttons with their id.
		$meta = get_user_meta( get_current_user_id(), __NAMESPACE__, true );
		if ( is_array( $meta ) && isset( $meta['editor'] ) ) {
			;
		} else {
			array_push( $buttons, __NAMESPACE__ );
		}
	}

	return $buttons;
}

add_filter( 'mce_buttons', __NAMESPACE__ . '\mce_buttons' );

/**
 *  Register the tinymcetranslation file
 *
 *  @param array $locales An array containing the current translations to be used by tinymce.
 *
 *  @return array An array containing the translations to be used by tinymce, our localization added to the $locale parameter
 *
 *  @since 4.7
 */
function mce_external_languages( $locales ) {
	if ( current_user_can( 'edit_theme_options' ) ) { // don't load the code if the user can not customize the shortcode.
		$meta = get_user_meta( get_current_user_id(), __NAMESPACE__, true );
		if ( is_array( $meta ) && isset( $meta['editor'] ) ) {
			;
		} else {
			$locales['cat-posts'] = plugin_dir_path( __FILE__ ) . 'tinymce-translations.php';
		}
	}

	return $locales;
}

add_filter( 'mce_external_languages', __NAMESPACE__ . '\mce_external_languages' );

/*
 * user profile related functions.
 */

add_action( 'show_user_profile', __NAMESPACE__ . '\show_user_profile' );
add_action( 'edit_user_profile', __NAMESPACE__ . '\show_user_profile' );

/**
 *  Display the user specific setting on its profile page
 *
 *  @param WP_user $user The user for which the profile page displays information.
 *
 *  @since 4.7
 */
function show_user_profile( $user ) {

	if ( ! current_user_can( 'edit_user', $user->ID ) ) {
		return;
	}

	if ( ! current_user_can( 'edit_theme_options', $user->ID ) ) {
		return;
	}

	$meta = get_the_author_meta( __NAMESPACE__, $user->ID );

	if ( empty( $meta ) ) {
		$meta = array();
	}

	$accordion = false;
	if ( isset( $meta['panels'] ) ) {
		$accordion = true;
	}

	$editor = false;
	if ( isset( $meta['editor'] ) ) {
		$editor = true;
	}
	?>
	<h3 id="<?php echo __NAMESPACE__; ?>"><?php esc_html_e( 'Category Posts Widget behaviour settings', 'category-posts' ); ?></h3>

	<table class="form-table">
		<tr>
			<th><label for="<?php echo __NAMESPACE__; ?>[panels]"><?php esc_html_e( 'Open panels behavior', 'category-posts' ); ?></label></th>
			<td>
				<input type="checkbox" name="<?php echo __NAMESPACE__; ?>[panels]" id="<?php echo __NAMESPACE__; ?>[panels]" <?php checked( $accordion ); ?>>
				<label for=<?php echo __NAMESPACE__; ?>[panels]><?php esc_html_e( 'Close the currently open panel when opening a new one', 'category-posts' ); ?></label>
			</td>
		</tr>
		<tr>
			<th><label for="<?php echo __NAMESPACE__; ?>[editor]"><?php esc_html_e( 'Visual editor button', 'category-posts' ); ?></label></th>
			<td>
				<input type="checkbox" name="<?php echo __NAMESPACE__; ?>[editor]" id="<?php echo __NAMESPACE__; ?>[editor]" <?php checked( $editor ); ?>>
				<label for="<?php echo __NAMESPACE__; ?>[editor]"><?php esc_html_e( 'Hide the "insert shortcode" button from the editor', 'category-posts' ); ?></label>
			</td>
		</tr>
	</table>
	<?php
}

add_action( 'personal_options_update', __NAMESPACE__ . '\personal_options_update' );
add_action( 'edit_user_profile_update', __NAMESPACE__ . '\personal_options_update' );

/**
 *  Handles saving user related settings as was set in the profile page.
 *
 *  @param int $user_id the ID of the user for which the data is saved..
 *
 *  @since 4.7
 */
function personal_options_update( $user_id ) {

	if ( ! current_user_can( 'edit_user', $user_id ) ) {
		return false;
	}

	if ( ! current_user_can( 'edit_theme_options', $user_id ) ) {
		return;
	}

	if ( isset( $_POST[ __NAMESPACE__ ] ) ) {
		update_user_meta( $user_id, __NAMESPACE__, wp_unslash( $_POST[ __NAMESPACE__ ] ) );
	} else {
		delete_user_meta( $user_id, __NAMESPACE__ );
	}
}

add_action( 'wp_loaded', __NAMESPACE__ . '\wp_loaded' );

/**
 *  Run after WordPress finished bootstrapping, do whatever is needed at this stage
 *  like registering the meta.
 */
function wp_loaded() {
	register_meta( 'post', SHORTCODE_META, null, '__return_false' ); // do not allow access to the shortcode meta
																	// use the pre 4.6 format for backward compatibility.
}