2829 lines
		
	
	
		
			86 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			2829 lines
		
	
	
		
			86 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * Get portfolio list
 | |
|  *
 | |
|  * @package visual-portfolio/get
 | |
|  */
 | |
| 
 | |
| if ( ! defined( 'ABSPATH' ) ) {
 | |
| 	exit;
 | |
| }
 | |
| 
 | |
| // Slugify.
 | |
| if ( ! class_exists( 'Cocur\Slugify\Slugify' ) ) {
 | |
| 	require_once visual_portfolio()->plugin_path . 'vendors/slugify/RuleProvider/RuleProviderInterface.php';
 | |
| 	require_once visual_portfolio()->plugin_path . 'vendors/slugify/RuleProvider/DefaultRuleProvider.php';
 | |
| 	require_once visual_portfolio()->plugin_path . 'vendors/slugify/SlugifyInterface.php';
 | |
| 	require_once visual_portfolio()->plugin_path . 'vendors/slugify/Slugify.php';
 | |
| }
 | |
| /**
 | |
|  * Class Visual_Portfolio_Get
 | |
|  */
 | |
| class Visual_Portfolio_Get {
 | |
| 	/**
 | |
| 	 * ID of the current printed portfolio
 | |
| 	 *
 | |
| 	 * @var int
 | |
| 	 */
 | |
| 	private static $id = 0;
 | |
| 
 | |
| 	/**
 | |
| 	 * ID of the current printed single filter
 | |
| 	 *
 | |
| 	 * @var int
 | |
| 	 */
 | |
| 	private static $filter_id = 0;
 | |
| 
 | |
| 	/**
 | |
| 	 * ID of the current printed single sort
 | |
| 	 *
 | |
| 	 * @var int
 | |
| 	 */
 | |
| 	private static $sort_id = 0;
 | |
| 
 | |
| 	/**
 | |
| 	 * Random number for random order.
 | |
| 	 *
 | |
| 	 * @var int
 | |
| 	 */
 | |
| 	private static $rand_seed_session = false;
 | |
| 
 | |
| 	/**
 | |
| 	 * Array with already used IDs on the page. Used for option 'Avoid Duplicates'
 | |
| 	 *
 | |
| 	 * @var array
 | |
| 	 */
 | |
| 	private static $used_posts = array();
 | |
| 
 | |
| 	/**
 | |
| 	 * Check for main query to avoid duplication.
 | |
| 	 *
 | |
| 	 * @var array
 | |
| 	 */
 | |
| 	private static $check_main_query = true;
 | |
| 
 | |
| 	/**
 | |
| 	 * Array with all used layout IDs
 | |
| 	 *
 | |
| 	 * @var array
 | |
| 	 */
 | |
| 	private static $used_layouts = array();
 | |
| 
 | |
| 	/**
 | |
| 	 * Get all available layouts.
 | |
| 	 *
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public static function get_all_layouts() {
 | |
|         // phpcs:ignore Squiz.PHP.CommentedOutCode.Found, Squiz.Commenting.BlockComment.NoEmptyLineBefore
 | |
| 		/*
 | |
| 		 * Example:
 | |
| 			array(
 | |
| 				'new_layout' => array(
 | |
| 					'title'    => esc_html__( 'New Layout', 'text_domain' ),
 | |
| 					'controls' => array(
 | |
| 						... controls ...
 | |
| 					),
 | |
| 				),
 | |
| 			)
 | |
| 		 */
 | |
| 		$layouts = apply_filters( 'vpf_extend_layouts', array() );
 | |
| 
 | |
| 		// Extend specific layout controls.
 | |
| 		foreach ( $layouts as $name => $layout ) {
 | |
| 			if ( isset( $layout['controls'] ) ) {
 | |
|                 // phpcs:ignore Squiz.PHP.CommentedOutCode.Found, Squiz.Commenting.BlockComment.NoEmptyLineBefore
 | |
| 				/*
 | |
| 				 * Example:
 | |
| 					array(
 | |
| 						... controls ...
 | |
| 					)
 | |
| 				 */
 | |
| 				$layouts[ $name ]['controls'] = apply_filters( 'vpf_extend_layout_' . $name . '_controls', $layout['controls'] );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $layouts;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get all available items styles.
 | |
| 	 *
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public static function get_all_items_styles() {
 | |
|         // phpcs:ignore Squiz.PHP.CommentedOutCode.Found, Squiz.Commenting.BlockComment.NoEmptyLineBefore
 | |
| 		/*
 | |
| 		 * Example:
 | |
| 			array(
 | |
| 				'new_items_style' => array(
 | |
| 					'title'            => esc_html__( 'New Items Style', 'visual-portfolio' ),
 | |
| 					'builtin_controls' => array(
 | |
| 						'images_rounded_corners' => true,
 | |
| 						'show_title'             => true,
 | |
| 						'show_categories'        => true,
 | |
| 						'show_date'              => true,
 | |
| 						'show_author'            => true,
 | |
| 						'show_comments_count'    => true,
 | |
| 						'show_views_count'       => true,
 | |
| 						'show_reading_time'      => true,
 | |
| 						'show_excerpt'           => true,
 | |
| 						'show_icons'             => false,
 | |
| 						'align'                  => true,
 | |
| 					),
 | |
| 					'controls'         => array(
 | |
| 						... controls ...
 | |
| 					),
 | |
| 				),
 | |
| 			)
 | |
| 		 */
 | |
| 		$items_styles = apply_filters( 'vpf_extend_items_styles', array() );
 | |
| 
 | |
| 		// Extend specific item style controls.
 | |
| 		foreach ( $items_styles as $name => $style ) {
 | |
| 			if ( isset( $style['controls'] ) ) {
 | |
|                 // phpcs:ignore Squiz.PHP.CommentedOutCode.Found, Squiz.Commenting.BlockComment.NoEmptyLineBefore
 | |
| 				/*
 | |
| 				 * Example:
 | |
| 					array(
 | |
| 						... controls ...
 | |
| 					)
 | |
| 				 */
 | |
| 				$items_styles[ $name ]['controls'] = apply_filters( 'vpf_extend_item_style_' . $name . '_controls', $style['controls'] );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $items_styles;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get all available options of post.
 | |
| 	 *
 | |
| 	 * @param array $atts options for portfolio list to print.
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public static function get_options( $atts = array() ) {
 | |
| 		$id       = isset( $atts['id'] ) ? $atts['id'] : false;
 | |
| 		$block_id = isset( $atts['block_id'] ) ? $atts['block_id'] : false;
 | |
| 
 | |
| 		if ( ! $id && ! $block_id ) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		$result = array();
 | |
| 
 | |
| 		$registered = Visual_Portfolio_Controls::get_registered_array();
 | |
| 
 | |
| 		// Get default or saved layout options.
 | |
| 		foreach ( $registered as $item ) {
 | |
| 			if ( isset( $atts[ $item['name'] ] ) ) {
 | |
| 				$result[ $item['name'] ] = $atts[ $item['name'] ];
 | |
| 			} else {
 | |
| 				$result[ $item['name'] ] = Visual_Portfolio_Controls::get_registered_value( $item['name'], $block_id ? false : $id );
 | |
| 			}
 | |
| 
 | |
| 			// fix bool values.
 | |
| 			if ( 'false' === $result[ $item['name'] ] ) {
 | |
| 				$result[ $item['name'] ] = false;
 | |
| 			}
 | |
| 			if ( 'true' === $result[ $item['name'] ] ) {
 | |
| 				$result[ $item['name'] ] = true;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if ( ! isset( $result['id'] ) ) {
 | |
| 			$result['id'] = $block_id ? $block_id : $id;
 | |
| 		}
 | |
| 
 | |
| 		// filter.
 | |
| 		$result = apply_filters( 'vpf_get_options', $result, $atts );
 | |
| 
 | |
| 		return $result;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Check if portfolio showed in preview mode.
 | |
| 	 */
 | |
| 	public static function is_preview() {
 | |
| 		$frame = false;
 | |
| 		if ( isset( $_POST['vp_preview_nonce'] ) && wp_verify_nonce( sanitize_key( $_POST['vp_preview_nonce'] ), 'vp-ajax-nonce' ) ) {
 | |
|             // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
 | |
| 			$frame = isset( $_POST['vp_preview_frame'] ) ? Visual_Portfolio_Security::sanitize_boolean( $_POST['vp_preview_frame'] ) : false;
 | |
| 			$id    = isset( $_POST['vp_preview_frame_id'] ) ? sanitize_text_field( wp_unslash( $_POST['vp_preview_frame_id'] ) ) : false;
 | |
| 
 | |
| 			// Elementor preview.
 | |
| 			if ( ! $frame && ! $id && isset( $_REQUEST['vp_preview_type'] ) && 'elementor' === $_REQUEST['vp_preview_type'] ) {
 | |
|                 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
 | |
| 				$frame = isset( $_REQUEST['vp_preview_frame'] ) ? Visual_Portfolio_Security::sanitize_boolean( $_REQUEST['vp_preview_frame'] ) : false;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $frame;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Allow taxonomies to show in Filter
 | |
| 	 *
 | |
| 	 * @param string $taxonomy taxonomy name.
 | |
| 	 *
 | |
| 	 * @return bool
 | |
| 	 */
 | |
| 	public static function allow_taxonomies_for_filter( $taxonomy ) {
 | |
| 		// check taxonomies from settings.
 | |
| 		$custom_taxonomies        = Visual_Portfolio_Settings::get_option( 'filter_taxonomies', 'vp_general' );
 | |
| 		$custom_taxonomies        = explode( ',', $custom_taxonomies );
 | |
| 		$custom_taxonomies_result = false;
 | |
| 		if ( $custom_taxonomies && ! empty( $custom_taxonomies ) ) {
 | |
| 			foreach ( $custom_taxonomies as $tax ) {
 | |
| 				$custom_taxonomies_result = $custom_taxonomies_result || $taxonomy === $tax;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return apply_filters(
 | |
| 			'vpf_allow_taxonomy_for_filter',
 | |
| 			$custom_taxonomies_result
 | |
| 				|| strpos( $taxonomy, 'category' ) !== false
 | |
| 				|| strpos( $taxonomy, 'jetpack-portfolio-type' ) !== false
 | |
| 				|| 'product_cat' === $taxonomy,
 | |
| 			$taxonomy
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Prepare config, that will be used for output.
 | |
| 	 *
 | |
| 	 * @param array $atts options for portfolio list to print.
 | |
| 	 *
 | |
| 	 * @return array|bool
 | |
| 	 */
 | |
| 	public static function get_output_config( $atts = array() ) {
 | |
| 		if ( ! is_array( $atts ) ) {
 | |
| 			return '';
 | |
| 		}
 | |
| 
 | |
| 		$options = self::get_options( $atts );
 | |
| 
 | |
| 		if ( ! $options ) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		do_action( 'vpf_before_get_output', $options );
 | |
| 
 | |
| 		self::$used_layouts[] = $options['id'];
 | |
| 
 | |
| 		// generate unique ID.
 | |
| 		$uid   = ++self::$id;
 | |
| 		$uid   = hash( 'crc32b', $uid . $options['id'] );
 | |
| 		$class = 'vp-portfolio vp-uid-' . $uid;
 | |
| 
 | |
| 		// Add ID to class.
 | |
| 		$class .= ' vp-id-' . $options['id'];
 | |
| 
 | |
| 		// Add custom class.
 | |
| 		if ( isset( $atts['class'] ) ) {
 | |
| 			$class .= ' ' . $atts['class'];
 | |
| 		}
 | |
| 
 | |
| 		// Add custom CSS from VC.
 | |
| 		if ( function_exists( 'vc_shortcode_custom_css_class' ) && isset( $atts['vc_css'] ) ) {
 | |
| 			$class .= ' ' . vc_shortcode_custom_css_class( $atts['vc_css'] );
 | |
| 		}
 | |
| 
 | |
| 		// stretch class.
 | |
| 		if ( $options['stretch'] ) {
 | |
| 			$class .= ' vp-portfolio__stretch';
 | |
| 		}
 | |
| 
 | |
| 		// Filter to replace the main layout with custom. Particularly needed for password protection or age verification.
 | |
| 		$custom_output = apply_filters( 'vpf_custom_output', false, $uid, $class, $options );
 | |
| 
 | |
| 		if ( $custom_output ) {
 | |
| 			return array(
 | |
| 				'custom_output' => $custom_output,
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		$no_image = Visual_Portfolio_Settings::get_option( 'no_image', 'vp_general' );
 | |
| 
 | |
| 		// prepare image sizes.
 | |
| 		$img_size_popup    = 'vp_xl_popup';
 | |
| 		$img_size_md_popup = 'vp_md_popup';
 | |
| 		$img_size_sm_popup = 'vp_sm_popup';
 | |
| 		$img_size          = 'vp_xl';
 | |
| 		$columns_count     = false;
 | |
| 
 | |
| 		switch ( $options['layout'] ) {
 | |
| 			case 'masonry':
 | |
| 				$columns_count = (int) $options['masonry_columns'];
 | |
| 				break;
 | |
| 			case 'grid':
 | |
| 				$columns_count = (int) $options['grid_columns'];
 | |
| 				break;
 | |
| 			case 'tiles':
 | |
| 				$columns_count = explode( '|', $options['tiles_type'], 1 );
 | |
| 				$columns_count = (int) $columns_count[0];
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		switch ( $columns_count ) {
 | |
| 			case 1:
 | |
| 				$img_size = 'vp_xl';
 | |
| 				break;
 | |
| 			case 2:
 | |
| 				$img_size = 'vp_xl';
 | |
| 				break;
 | |
| 			case 3:
 | |
| 				$img_size = 'vp_xl';
 | |
| 				break;
 | |
| 			case 4:
 | |
| 				$img_size = 'vp_lg';
 | |
| 				break;
 | |
| 			case 5:
 | |
| 				$img_size = 'vp_lg';
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		$is_preview = self::is_preview();
 | |
| 		$start_page = self::get_current_page_number();
 | |
| 		$is_images  = 'images' === $options['content_source'];
 | |
| 		$is_social  = 'social-stream' === $options['content_source'];
 | |
| 
 | |
| 		if ( $is_images || $is_social ) {
 | |
| 			$query_opts = self::get_query_params( $options, false, $options['id'] );
 | |
| 
 | |
| 			if ( isset( $query_opts['max_num_pages'] ) ) {
 | |
| 				$max_pages = (int) ( $query_opts['max_num_pages'] < $start_page ? $start_page : $query_opts['max_num_pages'] );
 | |
| 			} else {
 | |
| 				$max_pages = $start_page;
 | |
| 			}
 | |
| 		} else {
 | |
| 			// Get query params.
 | |
| 			$query_opts = self::get_query_params( $options, false, $options['id'] );
 | |
| 
 | |
| 			// stupid hack as wp_reset_postdata() function is not working for some reason...
 | |
| 			$old_post = $GLOBALS['post'];
 | |
| 
 | |
| 			// get Post List.
 | |
| 			$portfolio_query = new WP_Query( $query_opts );
 | |
| 
 | |
| 			$max_pages = (int) ( $portfolio_query->max_num_pages < $start_page ? $start_page : $portfolio_query->max_num_pages );
 | |
| 		}
 | |
| 
 | |
| 		$next_page_url = ( ! $max_pages || $max_pages >= $start_page + 1 ) ? self::get_pagenum_link(
 | |
| 			array(
 | |
| 				'vp_page' => $start_page + 1,
 | |
| 			)
 | |
| 		) : false;
 | |
| 
 | |
| 		$options['start_page']    = $start_page;
 | |
| 		$options['max_pages']     = $max_pages;
 | |
| 		$options['next_page_url'] = $next_page_url;
 | |
| 
 | |
| 		/**
 | |
| 		 * Prepare data-attributes.
 | |
| 		 */
 | |
| 		$data_attrs = array(
 | |
| 			'data-vp-layout'             => $options['layout'],
 | |
| 			'data-vp-content-source'     => $options['content_source'],
 | |
| 			'data-vp-items-style'        => $options['items_style'],
 | |
| 			'data-vp-items-click-action' => $options['items_click_action'],
 | |
| 			'data-vp-items-gap'          => $options['items_gap'],
 | |
| 			'data-vp-items-gap-vertical' => $options['items_gap_vertical'],
 | |
| 			'data-vp-pagination'         => $options['pagination'],
 | |
| 			'data-vp-next-page-url'      => $next_page_url,
 | |
| 		);
 | |
| 
 | |
| 		if (
 | |
| 			(
 | |
| 				'post-based' === $options['content_source'] &&
 | |
| 				'rand' === $options['posts_order_by']
 | |
| 			) ||
 | |
| 			(
 | |
| 				isset( $options['images'] ) &&
 | |
| 				! empty( $options['images'] ) &&
 | |
| 				is_array( $options['images'] ) &&
 | |
| 				'rand' === $options['images_order_by']
 | |
| 			)
 | |
| 		) {
 | |
| 			$data_attrs['data-vp-random-seed'] = self::get_rand_seed_session();
 | |
| 		}
 | |
| 
 | |
| 		if ( 'tiles' === $options['layout'] || $is_preview ) {
 | |
| 			$data_attrs['data-vp-tiles-type'] = $options['tiles_type'];
 | |
| 		}
 | |
| 		if ( 'masonry' === $options['layout'] || $is_preview ) {
 | |
| 			$data_attrs['data-vp-masonry-columns'] = $options['masonry_columns'];
 | |
| 
 | |
| 			if ( $options['masonry_images_aspect_ratio'] ) {
 | |
| 				$data_attrs['data-vp-masonry-images-aspect-ratio'] = $options['masonry_images_aspect_ratio'];
 | |
| 			}
 | |
| 		}
 | |
| 		if ( 'grid' === $options['layout'] || $is_preview ) {
 | |
| 			$data_attrs['data-vp-grid-columns'] = $options['grid_columns'];
 | |
| 
 | |
| 			if ( $options['grid_images_aspect_ratio'] ) {
 | |
| 				$data_attrs['data-vp-grid-images-aspect-ratio'] = $options['grid_images_aspect_ratio'];
 | |
| 			}
 | |
| 		}
 | |
| 		if ( 'justified' === $options['layout'] || $is_preview ) {
 | |
| 			$data_attrs['data-vp-justified-row-height']           = $options['justified_row_height'];
 | |
| 			$data_attrs['data-vp-justified-row-height-tolerance'] = $options['justified_row_height_tolerance'];
 | |
| 			$data_attrs['data-vp-justified-max-rows-count']       = $options['justified_max_rows_count'];
 | |
| 			$data_attrs['data-vp-justified-last-row']             = $options['justified_last_row'];
 | |
| 		}
 | |
| 
 | |
| 		if ( 'slider' === $options['layout'] || $is_preview ) {
 | |
| 			$data_attrs['data-vp-slider-effect'] = $options['slider_effect'];
 | |
| 
 | |
| 			switch ( $options['slider_items_height_type'] ) {
 | |
| 				case 'auto':
 | |
| 					$data_attrs['data-vp-slider-items-height'] = 'auto';
 | |
| 					break;
 | |
| 				case 'static':
 | |
| 					$data_attrs['data-vp-slider-items-height']     = ( $options['slider_items_height_static'] ? $options['slider_items_height_static'] : '200' ) . 'px';
 | |
| 					$data_attrs['data-vp-slider-items-min-height'] = $options['slider_items_min_height'];
 | |
| 					break;
 | |
| 				case 'dynamic':
 | |
| 					$data_attrs['data-vp-slider-items-height']     = ( $options['slider_items_height_dynamic'] ? $options['slider_items_height_dynamic'] : '80' ) . '%';
 | |
| 					$data_attrs['data-vp-slider-items-min-height'] = $options['slider_items_min_height'];
 | |
| 					break;
 | |
| 				// no default.
 | |
| 			}
 | |
| 
 | |
| 			switch ( $options['slider_slides_per_view_type'] ) {
 | |
| 				case 'auto':
 | |
| 					$data_attrs['data-vp-slider-slides-per-view'] = 'auto';
 | |
| 					break;
 | |
| 				case 'custom':
 | |
| 					$data_attrs['data-vp-slider-slides-per-view'] = $options['slider_slides_per_view_custom'] ? $options['slider_slides_per_view_custom'] : '3';
 | |
| 					break;
 | |
| 				// no default.
 | |
| 			}
 | |
| 
 | |
| 			$data_attrs['data-vp-slider-speed']                = $options['slider_speed'];
 | |
| 			$data_attrs['data-vp-slider-autoplay']             = $options['slider_autoplay'];
 | |
| 			$data_attrs['data-vp-slider-autoplay-hover-pause'] = $options['slider_autoplay_hover_pause'] ? 'true' : 'false';
 | |
| 			$data_attrs['data-vp-slider-centered-slides']      = $options['slider_centered_slides'] ? 'true' : 'false';
 | |
| 			$data_attrs['data-vp-slider-loop']                 = $options['slider_loop'] ? 'true' : 'false';
 | |
| 			$data_attrs['data-vp-slider-free-mode']            = $options['slider_free_mode'] ? 'true' : 'false';
 | |
| 			$data_attrs['data-vp-slider-free-mode-sticky']     = $options['slider_free_mode_sticky'] ? 'true' : 'false';
 | |
| 			$data_attrs['data-vp-slider-arrows']               = $options['slider_arrows'] ? 'true' : 'false';
 | |
| 			$data_attrs['data-vp-slider-bullets']              = $options['slider_bullets'] ? 'true' : 'false';
 | |
| 			$data_attrs['data-vp-slider-bullets-dynamic']      = $options['slider_bullets_dynamic'] ? 'true' : 'false';
 | |
| 			$data_attrs['data-vp-slider-mousewheel']           = $options['slider_mousewheel'] ? 'true' : 'false';
 | |
| 
 | |
| 			$data_attrs['data-vp-slider-thumbnails'] = $options['slider_thumbnails'] ? 'true' : 'false';
 | |
| 
 | |
| 			if ( $options['slider_thumbnails'] ) {
 | |
| 				$data_attrs['data-vp-slider-thumbnails-height'] = 'auto';
 | |
| 				$data_attrs['data-vp-slider-thumbnails-gap']    = $options['slider_thumbnails_gap'] ? $options['slider_thumbnails_gap'] : '0';
 | |
| 
 | |
| 				switch ( $options['slider_thumbnails_height_type'] ) {
 | |
| 					case 'auto':
 | |
| 						$data_attrs['data-vp-slider-thumbnails-height'] = 'auto';
 | |
| 						break;
 | |
| 					case 'static':
 | |
| 						$data_attrs['data-vp-slider-thumbnails-height'] = ( $options['slider_thumbnails_height_static'] ? $options['slider_thumbnails_height_static'] : '100' ) . 'px';
 | |
| 						break;
 | |
| 					case 'dynamic':
 | |
| 						$data_attrs['data-vp-slider-thumbnails-height'] = ( $options['slider_thumbnails_height_dynamic'] ? $options['slider_thumbnails_height_dynamic'] : '30' ) . '%';
 | |
| 						break;
 | |
| 					// no default.
 | |
| 				}
 | |
| 
 | |
| 				switch ( $options['slider_thumbnails_per_view_type'] ) {
 | |
| 					case 'auto':
 | |
| 						$data_attrs['data-vp-slider-thumbnails-per-view'] = 'auto';
 | |
| 						break;
 | |
| 					case 'custom':
 | |
| 						$data_attrs['data-vp-slider-thumbnails-per-view'] = $options['slider_thumbnails_per_view_custom'] ? $options['slider_thumbnails_per_view_custom'] : '6';
 | |
| 						break;
 | |
| 					// no default.
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// get options for the current style.
 | |
| 		$style_options      = array();
 | |
| 		$style_options_slug = 'items_style_' . $options['items_style'] . '__';
 | |
| 		foreach ( $options as $k => $opt ) {
 | |
| 			// add option to array.
 | |
| 			if ( substr( $k, 0, strlen( $style_options_slug ) ) === $style_options_slug ) {
 | |
| 				$opt_name = str_replace( $style_options_slug, '', $k );
 | |
| 
 | |
| 				$style_options[ $opt_name ] = $opt;
 | |
| 			}
 | |
| 
 | |
| 			// remove style options from the options list.
 | |
| 			if ( substr( $k, 0, strlen( 'items_style_' ) ) === 'items_style_' ) {
 | |
| 				unset( $options[ $k ] );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
|         // phpcs:ignore Squiz.PHP.CommentedOutCode.Found, Squiz.Commenting.BlockComment.NoEmptyLineBefore
 | |
| 		/*
 | |
| 		 * Example:
 | |
| 			array(
 | |
| 				'data-vp-my-attribute' => 'data',
 | |
| 			)
 | |
| 		 */
 | |
| 		$data_attrs = apply_filters( 'vpf_extend_portfolio_data_attributes', $data_attrs, $options, $style_options );
 | |
| 		$class      = apply_filters( 'vpf_extend_portfolio_class', $class, $options, $style_options );
 | |
| 
 | |
| 		$items_class = 'vp-portfolio__items vp-portfolio__items-style-' . $options['items_style'];
 | |
| 
 | |
| 		if ( isset( $style_options['show_overlay'] ) && $style_options['show_overlay'] ) {
 | |
| 			$items_class .= ' vp-portfolio__items-show-overlay-' . $style_options['show_overlay'];
 | |
| 		}
 | |
| 		if ( isset( $style_options['show_caption'] ) && $style_options['show_caption'] ) {
 | |
| 			$items_class .= ' vp-portfolio__items-show-caption-' . $style_options['show_caption'];
 | |
| 		}
 | |
| 
 | |
| 		if ( isset( $style_options['show_img_overlay'] ) && $style_options['show_img_overlay'] ) {
 | |
| 			$items_class .= ' vp-portfolio__items-show-img-overlay-' . $style_options['show_img_overlay'];
 | |
| 		}
 | |
| 
 | |
| 		$items_class = apply_filters( 'vpf_extend_portfolio_items_class', $items_class, $options, $style_options );
 | |
| 
 | |
| 		/**
 | |
| 		 * Prepare each item args.
 | |
| 		 */
 | |
| 		$each_item_args = array(
 | |
| 			'uid'                => '',
 | |
| 			'post_id'            => '',
 | |
| 			'url'                => '',
 | |
| 			'title'              => '',
 | |
| 			'excerpt'            => '',
 | |
| 			'content'            => '',
 | |
| 			'comments_count'     => '',
 | |
| 			'comments_url'       => '',
 | |
| 			'author'             => '',
 | |
| 			'author_url'         => '',
 | |
| 			'author_avatar'      => '',
 | |
| 			'views_count'        => '',
 | |
| 			'reading_time'       => '',
 | |
| 			'format'             => '',
 | |
| 			'published'          => '',
 | |
| 			'published_time'     => '',
 | |
| 			'categories'         => array(),
 | |
| 			'filter'             => '',
 | |
| 			'video'              => '',
 | |
| 			'image_id'           => '',
 | |
| 			'img_size_popup'     => $img_size_popup,
 | |
| 			'img_size_md_popup'  => $img_size_md_popup,
 | |
| 			'img_size_sm_popup'  => $img_size_sm_popup,
 | |
| 			'img_size'           => $img_size,
 | |
| 			'no_image'           => $no_image,
 | |
| 			'opts'               => $style_options,
 | |
| 			'vp_opts'            => $options,
 | |
| 		);
 | |
| 
 | |
| 		$items = array();
 | |
| 
 | |
| 		if ( ( $is_images || $is_social ) &&
 | |
| 			isset( $query_opts['images'] ) &&
 | |
| 			is_array( $query_opts['images'] ) &&
 | |
| 			! empty( $query_opts['images'] ) ) {
 | |
| 
 | |
| 			foreach ( $query_opts['images'] as $img ) {
 | |
| 				// Get category taxonomies for data filter.
 | |
| 				$filter_values = array();
 | |
| 				$categories    = array();
 | |
| 
 | |
| 				if ( isset( $img['categories'] ) && is_array( $img['categories'] ) ) {
 | |
| 					foreach ( $img['categories'] as $cat ) {
 | |
| 						$slug = self::create_slug( $cat );
 | |
| 
 | |
| 						if ( ! in_array( $slug, $filter_values, true ) ) {
 | |
| 							// add in filter.
 | |
| 							$filter_values[] = $slug;
 | |
| 
 | |
| 							// add in categories array.
 | |
| 							$url = self::get_pagenum_link(
 | |
| 								array(
 | |
| 									'vp_filter' => rawurlencode( $slug ),
 | |
| 									'vp_page'   => 1,
 | |
| 								)
 | |
| 							);
 | |
| 
 | |
| 							$categories[] = array(
 | |
| 								'slug'        => $slug,
 | |
| 								'label'       => $cat,
 | |
| 								'description' => '',
 | |
| 								'count'       => '',
 | |
| 								'taxonomy'    => '',
 | |
| 								'id'          => 0,
 | |
| 								'parent'      => 0,
 | |
| 								'url'         => $url,
 | |
| 							);
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				$args = array_merge(
 | |
| 					$each_item_args,
 | |
| 					array(
 | |
| 						'uid'            => isset( $img['uid'] ) && $img['uid'] ? $img['uid'] : '',
 | |
| 						'url'            => isset( $img['url'] ) && $img['url'] ? $img['url'] : Visual_Portfolio_Images::wp_get_attachment_image_url( $img['id'], $img_size_popup ),
 | |
| 						'title'          => isset( $img['title'] ) && $img['title'] ? $img['title'] : '',
 | |
| 						'content'        => isset( $img['description'] ) && $img['description'] ? $img['description'] : '',
 | |
| 						'format'         => isset( $img['format'] ) && $img['format'] ? $img['format'] : 'standard',
 | |
| 						'published_time' => isset( $img['published_time'] ) && $img['published_time'] ? $img['published_time'] : '',
 | |
| 						'filter'         => implode( ',', $filter_values ),
 | |
| 						'image_id'       => intval( $img['id'] ),
 | |
| 						'focal_point'    => isset( $img['focalPoint'] ) && $img['focalPoint'] ? $img['focalPoint'] : '',
 | |
| 						'allow_popup'    => ! isset( $img['url'] ) || ! $img['url'],
 | |
| 						'categories'     => $categories,
 | |
| 						'author'         => isset( $img['author'] ) && $img['author'] ? $img['author'] : '',
 | |
| 						'author_url'     => isset( $img['author'] ) && isset( $img['author_url'] ) && $img['author'] && $img['author_url'] ? $img['author_url'] : '',
 | |
| 					)
 | |
| 				);
 | |
| 
 | |
| 				// Excerpt.
 | |
| 				if ( isset( $args['opts']['show_excerpt'] ) && $args['content'] ) {
 | |
| 					$args['excerpt'] = wp_trim_words( $args['content'], $args['opts']['excerpt_words_count'], '...' );
 | |
| 				}
 | |
| 
 | |
| 				if ( 'video' === $args['format'] && isset( $img['video_url'] ) && $img['video_url'] ) {
 | |
| 					$args['video'] = $img['video_url'];
 | |
| 				}
 | |
| 
 | |
| 				$args = apply_filters( 'vpf_image_item_args', $args, $img );
 | |
| 
 | |
| 				$items[] = $args;
 | |
| 			}
 | |
| 		} elseif ( isset( $portfolio_query ) ) {
 | |
| 			while ( $portfolio_query->have_posts() ) {
 | |
| 				$portfolio_query->the_post();
 | |
| 
 | |
| 				$the_post = get_post();
 | |
| 
 | |
| 				self::$used_posts[] = get_the_ID();
 | |
| 
 | |
| 				// Get category taxonomies for data filter.
 | |
| 				$filter_values  = array();
 | |
| 				$categories     = array();
 | |
| 				$all_taxonomies = get_object_taxonomies( $the_post );
 | |
| 
 | |
| 				foreach ( $all_taxonomies as $cat ) {
 | |
| 					// allow only specific taxonomies for filter.
 | |
| 					if ( ! self::allow_taxonomies_for_filter( $cat ) ) {
 | |
| 						continue;
 | |
| 					}
 | |
| 
 | |
| 					$category = get_the_terms( $the_post, $cat );
 | |
| 
 | |
| 					if ( $category && ! in_array( $category, $filter_values, true ) ) {
 | |
| 						foreach ( $category as $cat_item ) {
 | |
| 							// add in filter.
 | |
| 							$filter_values[] = $cat_item->slug;
 | |
| 
 | |
| 							// add in categories array.
 | |
| 							$unique_name  = rawurlencode( $cat_item->taxonomy . ':' ) . $cat_item->slug;
 | |
| 							$url          = self::get_pagenum_link(
 | |
| 								array(
 | |
| 									'vp_filter' => $unique_name,
 | |
| 									'vp_page'   => 1,
 | |
| 								)
 | |
| 							);
 | |
| 							$categories[] = array(
 | |
| 								'slug'        => $cat_item->slug,
 | |
| 								'label'       => $cat_item->name,
 | |
| 								'description' => $cat_item->description,
 | |
| 								'count'       => $cat_item->count,
 | |
| 								'taxonomy'    => $cat_item->taxonomy,
 | |
| 								'id'          => $cat_item->term_id,
 | |
| 								'parent'      => $cat_item->parent,
 | |
| 								'url'         => $url,
 | |
| 							);
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				$args = array_merge(
 | |
| 					$each_item_args,
 | |
| 					array(
 | |
| 						'uid'            => hash( 'crc32b', 'post-' . get_the_ID() ),
 | |
| 						'post_id'        => get_the_ID(),
 | |
| 						'url'            => get_permalink(),
 | |
| 						'title'          => get_the_title(),
 | |
| 						'content'        => get_the_content(),
 | |
| 						'format'         => get_post_format() ? get_post_format() : 'standard',
 | |
| 						'published_time' => get_the_date( 'Y-m-d H:i:s', $the_post ),
 | |
| 						'filter'         => implode( ',', $filter_values ),
 | |
| 						'image_id'       => 'attachment' === get_post_type() ? get_the_ID() : get_post_thumbnail_id( get_the_ID() ),
 | |
| 						'focal_point'    => Visual_Portfolio_Custom_Post_Meta::get_featured_image_focal_point( get_the_ID() ),
 | |
| 						'categories'     => $categories,
 | |
| 						'comments_count' => get_comments_number( get_the_ID() ),
 | |
| 						'comments_url'   => get_comments_link( get_the_ID() ),
 | |
| 						'views_count'    => Visual_Portfolio_Custom_Post_Meta::get_views_count( get_the_ID() ),
 | |
| 						'reading_time'   => Visual_Portfolio_Custom_Post_Meta::get_reading_time( get_the_ID() ),
 | |
| 					)
 | |
| 				);
 | |
| 
 | |
| 				// Author.
 | |
| 				$author_id = get_the_author_meta( 'ID' );
 | |
| 				if ( $author_id ) {
 | |
| 					$args['author']        = get_the_author();
 | |
| 					$args['author_url']    = get_author_posts_url( $author_id );
 | |
| 					$args['author_avatar'] = get_avatar_url( $author_id, array( 'size' => 50 ) );
 | |
| 				}
 | |
| 
 | |
| 				// Excerpt.
 | |
| 				if ( isset( $args['opts']['show_excerpt'] ) && $args['opts']['show_excerpt'] ) {
 | |
| 					$args['excerpt'] = wp_trim_words( do_shortcode( has_excerpt() ? get_the_excerpt() : $args['content'] ), $args['opts']['excerpt_words_count'], '...' );
 | |
| 				}
 | |
| 
 | |
| 				$args['allow_popup'] = isset( $args['image_id'] ) && $args['image_id'];
 | |
| 
 | |
| 				if ( 'video' === $args['format'] ) {
 | |
| 					$video_url = Visual_Portfolio_Custom_Post_Meta::get_video_format_url( get_the_ID() );
 | |
| 					if ( $video_url ) {
 | |
| 						$args['video']       = $video_url;
 | |
| 						$args['allow_popup'] = true;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				$args = apply_filters( 'vpf_post_item_args', $args, $args['post_id'] );
 | |
| 
 | |
| 				$items[] = $args;
 | |
| 			}
 | |
| 
 | |
| 			$portfolio_query->reset_postdata();
 | |
| 
 | |
| 			// Sometimes, when we use WPBakery Page Builder, without this reset output is wrong.
 | |
| 			wp_reset_postdata();
 | |
| 
 | |
| 			// stupid hack as wp_reset_postdata() function is not working in some situations...
 | |
|             // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
 | |
| 			$GLOBALS['post'] = $old_post;
 | |
| 		}
 | |
| 
 | |
| 		$notices = array();
 | |
| 
 | |
| 		// No items found notice.
 | |
| 		if ( empty( $items ) ) {
 | |
| 			$class .= ' vp-portfolio-not-found';
 | |
| 
 | |
| 			// Don't display any output if no items found (works on frontend only).
 | |
| 			if ( $options['no_items_notice'] && ( $is_preview || 'notice' === $options['no_items_action'] ) ) {
 | |
| 				$notices[] = $options['no_items_notice'];
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		$errors = array();
 | |
| 
 | |
| 		if ( isset( $query_opts['error'] ) && ! empty( $query_opts['error'] ) && ( $is_preview || is_admin_bar_showing() ) ) {
 | |
| 			$class   .= ' vp-portfolio-errors';
 | |
| 			$errors[] = $query_opts['error'];
 | |
| 		}
 | |
| 
 | |
| 		$result = array(
 | |
| 			'options'           => $options,
 | |
| 			'style_options'     => $style_options,
 | |
| 			'class'             => $class,
 | |
| 			'data_attrs'        => $data_attrs,
 | |
| 			'items_class'       => $items_class,
 | |
| 			'items'             => $items,
 | |
| 			'notices'           => $notices,
 | |
| 			'errors'            => $errors,
 | |
| 			'img_size_popup'    => $img_size_popup,
 | |
| 			'img_size_md_popup' => $img_size_md_popup,
 | |
| 			'img_size_sm_popup' => $img_size_sm_popup,
 | |
| 			'img_size'          => $img_size,
 | |
| 		);
 | |
| 
 | |
| 		return $result;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print portfolio by post ID or options
 | |
| 	 *
 | |
| 	 * @param array $atts options for portfolio list to print.
 | |
| 	 *
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	public static function get( $atts = array() ) {
 | |
| 		$config = self::get_output_config( $atts );
 | |
| 
 | |
| 		if ( ! $config ) {
 | |
| 			return '';
 | |
| 		}
 | |
| 
 | |
| 		if ( isset( $config['custom_output'] ) ) {
 | |
| 			return $config['custom_output'];
 | |
| 		}
 | |
| 
 | |
| 		$options       = $config['options'];
 | |
| 		$style_options = $config['style_options'];
 | |
| 		$data_attrs    = $config['data_attrs'];
 | |
| 		$class         = $config['class'];
 | |
| 		$items         = $config['items'];
 | |
| 		$items_class   = $config['items_class'];
 | |
| 		$notices       = $config['notices'];
 | |
| 		$errors        = $config['errors'];
 | |
| 
 | |
| 		// Insert styles and scripts.
 | |
| 		Visual_Portfolio_Assets::enqueue( $atts );
 | |
| 
 | |
| 		// No items found.
 | |
| 		if ( empty( $items ) ) {
 | |
| 			if ( empty( $notices ) ) {
 | |
| 				return '';
 | |
| 			}
 | |
| 
 | |
| 			ob_start();
 | |
| 
 | |
| 			?>
 | |
| 			<div class="<?php echo esc_attr( $class ); ?>">
 | |
| 				<?php
 | |
| 				foreach ( $notices as $notice ) {
 | |
| 					self::notice( $notice );
 | |
| 				}
 | |
| 				?>
 | |
| 			</div>
 | |
| 			<?php
 | |
| 
 | |
| 			return ob_get_clean();
 | |
| 		}
 | |
| 
 | |
| 		ob_start();
 | |
| 
 | |
| 		/**
 | |
| 		 * Wrapper start.
 | |
| 		 */
 | |
| 		do_action( 'vpf_before_wrapper_start', $options, $style_options );
 | |
| 
 | |
| 		visual_portfolio()->include_template(
 | |
| 			'items-list/wrapper-start',
 | |
| 			array(
 | |
| 				'options'       => $options,
 | |
| 				'style_options' => $style_options,
 | |
| 				'data_attrs'    => $data_attrs,
 | |
| 				'class'         => $class,
 | |
| 			)
 | |
| 		);
 | |
| 
 | |
| 		if ( ! empty( $errors ) ) {
 | |
| 			?>
 | |
| 				<?php
 | |
| 				foreach ( $errors as $error ) {
 | |
| 					self::error( $error );
 | |
| 				}
 | |
| 				?>
 | |
| 			<?php
 | |
| 		}
 | |
| 
 | |
| 		do_action( 'vpf_after_wrapper_start', $options, $style_options );
 | |
| 
 | |
| 		/**
 | |
| 		 * Top layout elements.
 | |
| 		 */
 | |
| 		self::print_layout_elements( 'top', $options );
 | |
| 
 | |
| 		/**
 | |
| 		 * Items wrap.
 | |
| 		 */
 | |
| 		?>
 | |
| 		<div class="vp-portfolio__items-wrap">
 | |
| 			<?php
 | |
| 
 | |
| 			/**
 | |
| 			 * Items wrapper start.
 | |
| 			 */
 | |
| 			do_action( 'vpf_before_items_wrapper_start', $options, $style_options );
 | |
| 
 | |
| 			visual_portfolio()->include_template(
 | |
| 				'items-list/items-wrapper-start',
 | |
| 				array(
 | |
| 					'options'       => $options,
 | |
| 					'style_options' => $style_options,
 | |
| 					'class'         => $items_class,
 | |
| 				)
 | |
| 			);
 | |
| 
 | |
| 			do_action( 'vpf_after_items_wrapper_start', $options, $style_options );
 | |
| 
 | |
| 			/**
 | |
| 			 * Each item.
 | |
| 			 */
 | |
| 			if ( is_array( $items ) && ! empty( $items ) ) {
 | |
| 				foreach ( $items as $item_args ) {
 | |
| 					self::each_item( $item_args );
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			/**
 | |
| 			 * Items wrapper end.
 | |
| 			 */
 | |
| 			do_action( 'vpf_before_items_wrapper_end', $options, $style_options );
 | |
| 
 | |
| 			visual_portfolio()->include_template(
 | |
| 				'items-list/items-wrapper-end',
 | |
| 				array(
 | |
| 					'options'       => $options,
 | |
| 					'style_options' => $style_options,
 | |
| 				)
 | |
| 			);
 | |
| 
 | |
| 			do_action( 'vpf_after_items_wrapper_end', $options, $style_options );
 | |
| 
 | |
| 			// Slider arrows and bullets.
 | |
| 			if ( 'slider' === $options['layout'] ) {
 | |
| 				if ( $options['slider_arrows'] ) {
 | |
| 					visual_portfolio()->include_template(
 | |
| 						'items-list/layouts/slider/arrows',
 | |
| 						array(
 | |
| 							'options'       => $options,
 | |
| 							'style_options' => $style_options,
 | |
| 						)
 | |
| 					);
 | |
| 				}
 | |
| 				if ( $options['slider_bullets'] ) {
 | |
| 					visual_portfolio()->include_template(
 | |
| 						'items-list/layouts/slider/bullets',
 | |
| 						array(
 | |
| 							'options'       => $options,
 | |
| 							'style_options' => $style_options,
 | |
| 						)
 | |
| 					);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			?>
 | |
| 		</div>
 | |
| 		<?php
 | |
| 
 | |
| 		/**
 | |
| 		 * Slider thumbnails.
 | |
| 		 */
 | |
| 		if ( 'slider' === $options['layout'] && $options['slider_thumbnails'] ) {
 | |
| 			$slider_thumbnails = array();
 | |
| 
 | |
| 			if ( is_array( $items ) && ! empty( $items ) ) {
 | |
| 				foreach ( $items as $item_args ) {
 | |
| 					$slider_thumbnails[] = $item_args['image_id'];
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			visual_portfolio()->include_template(
 | |
| 				'items-list/layouts/slider/thumbnails',
 | |
| 				array(
 | |
| 					'options'       => $options,
 | |
| 					'style_options' => $style_options,
 | |
| 					'thumbnails'    => $slider_thumbnails,
 | |
| 					'img_size'      => $config['img_size'],
 | |
| 				)
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		/**
 | |
| 		 * Bottom layout elements.
 | |
| 		 */
 | |
| 		self::print_layout_elements( 'bottom', $options );
 | |
| 
 | |
| 		/**
 | |
| 		 * Wrapper end.
 | |
| 		 */
 | |
| 		do_action( 'vpf_before_wrapper_end', $options, $style_options );
 | |
| 
 | |
| 		visual_portfolio()->include_template(
 | |
| 			'items-list/wrapper-end',
 | |
| 			array(
 | |
| 				'options'       => $options,
 | |
| 				'style_options' => $style_options,
 | |
| 			)
 | |
| 		);
 | |
| 
 | |
| 		do_action( 'vpf_after_wrapper_end', $options, $style_options );
 | |
| 
 | |
| 		do_action( 'vpf_after_get_output', $options, $style_options );
 | |
| 
 | |
| 		return ob_get_clean();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print layout elements like Filter, Sort, Search and Pagination.
 | |
| 	 *
 | |
| 	 * @param string $position elements position.
 | |
| 	 * @param array  $options options for portfolio list to print.
 | |
| 	 */
 | |
| 	public static function print_layout_elements( $position, $options ) {
 | |
| 		$options         = apply_filters( 'vpf_layout_element_options', $options );
 | |
| 		$layout_elements = $options['layout_elements'];
 | |
| 
 | |
| 		if ( ! isset( $layout_elements[ $position ] ) || ! isset( $layout_elements[ $position ]['elements'] ) ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$registered   = Visual_Portfolio_Controls::get_registered_array();
 | |
| 		$control_data = $registered['layout_elements'];
 | |
| 		$class_name   = 'vp-portfolio__layout-elements';
 | |
| 
 | |
| 		if ( $position ) {
 | |
| 			$class_name .= ' vp-portfolio__layout-elements-' . $position;
 | |
| 		}
 | |
| 
 | |
| 		if ( isset( $layout_elements[ $position ]['align'] ) ) {
 | |
| 			$class_name .= ' vp-portfolio__layout-elements-align-' . $layout_elements[ $position ]['align'];
 | |
| 		}
 | |
| 
 | |
| 		ob_start();
 | |
| 		foreach ( $layout_elements[ $position ]['elements'] as $element ) {
 | |
| 			if ( isset( $control_data['options'][ $element ]['render_callback'] ) && is_callable( $control_data['options'][ $element ]['render_callback'] ) ) {
 | |
| 				call_user_func( $control_data['options'][ $element ]['render_callback'], $options, $element, $position );
 | |
| 			}
 | |
| 			do_action( 'vpf_layout_elements', $options, $element, $position );
 | |
| 		}
 | |
| 		$elements_content = ob_get_clean();
 | |
| 
 | |
| 		if ( ! $elements_content ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		?>
 | |
| 		<div class="<?php echo esc_attr( $class_name ); ?>">
 | |
| 		<?php
 | |
| 
 | |
| 		// In this case, pluggable pagination, filters and sorting templates are displayed.
 | |
| 		// Included templates are cleared and passed as a ready-made cleared string in the callback function called above.
 | |
|         // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
 | |
| 		echo $elements_content;
 | |
| 
 | |
| 		?>
 | |
| 		</div>
 | |
| 		<?php
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print portfolio filter by post ID or options
 | |
| 	 *
 | |
| 	 * @param array $atts options for portfolio list to print.
 | |
| 	 *
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	public static function get_filter( $atts = array() ) {
 | |
| 		$options = self::get_options( $atts );
 | |
| 
 | |
| 		$options = array_merge(
 | |
| 			$options,
 | |
| 			array(
 | |
| 				'filter'            => $atts['type'],
 | |
| 				'filter_show_count' => 'true' === $atts['show_count'],
 | |
| 				'filter_text_all'   => $atts['text_all'] ?? esc_attr__( 'All', 'visual-portfolio' ),
 | |
| 			)
 | |
| 		);
 | |
| 
 | |
| 		$align = $atts['align'] ?? 'center';
 | |
| 
 | |
| 		// generate unique ID.
 | |
| 		$uid = ++self::$filter_id;
 | |
| 		$uid = hash( 'crc32b', $uid . $options['id'] );
 | |
| 
 | |
| 		$class = 'vp-single-filter vp-filter-uid-' . $uid . ' vp-id-' . $options['id'];
 | |
| 
 | |
| 		// Add custom class.
 | |
| 		if ( isset( $atts['class'] ) ) {
 | |
| 			$class .= ' ' . $atts['class'];
 | |
| 		}
 | |
| 
 | |
| 		ob_start();
 | |
| 
 | |
| 		?>
 | |
| 		<div class="vp-portfolio__layout-elements vp-portfolio__layout-elements-align-<?php echo esc_attr( $align ); ?>">
 | |
| 			<div class="<?php echo esc_attr( $class ); ?>">
 | |
| 				<?php self::filter( $options ); ?>
 | |
| 			</div>
 | |
| 		</div>
 | |
| 		<?php
 | |
| 
 | |
| 		return ob_get_clean();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print portfolio sort by post ID or options
 | |
| 	 *
 | |
| 	 * @param array $atts options for portfolio list to print.
 | |
| 	 *
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	public static function get_sort( $atts = array() ) {
 | |
| 		$options = self::get_options( $atts );
 | |
| 
 | |
| 		$options = array_merge(
 | |
| 			$options,
 | |
| 			array(
 | |
| 				'sort' => $atts['type'],
 | |
| 			)
 | |
| 		);
 | |
| 
 | |
| 		$align = $atts['align'] ?? 'center';
 | |
| 
 | |
| 		// generate unique ID.
 | |
| 		$uid = ++self::$sort_id;
 | |
| 		$uid = hash( 'crc32b', $uid . $options['id'] );
 | |
| 
 | |
| 		$class = 'vp-single-sort vp-sort-uid-' . $uid . ' vp-id-' . $options['id'];
 | |
| 
 | |
| 		// Add custom class.
 | |
| 		if ( isset( $atts['class'] ) ) {
 | |
| 			$class .= ' ' . $atts['class'];
 | |
| 		}
 | |
| 
 | |
| 		ob_start();
 | |
| 
 | |
| 		?>
 | |
| 		<div class="vp-portfolio__layout-elements vp-portfolio__layout-elements-align-<?php echo esc_attr( $align ); ?>">
 | |
| 			<div class="<?php echo esc_attr( $class ); ?>">
 | |
| 				<?php self::sort( $options ); ?>
 | |
| 			</div>
 | |
| 		</div>
 | |
| 		<?php
 | |
| 
 | |
| 		return ob_get_clean();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get current page number
 | |
| 	 * ?vp_page=2
 | |
| 	 *
 | |
| 	 * @return int
 | |
| 	 */
 | |
| 	private static function get_current_page_number() {
 | |
|         // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.NonceVerification
 | |
| 		return max( 1, isset( $_GET['vp_page'] ) ? Visual_Portfolio_Security::sanitize_number( $_GET['vp_page'] ) : 1 );
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * "rand" orderby don't work fine for paged, so we need to use custom solution.
 | |
| 	 * thanks to https://gist.github.com/hlashbrooke/6298714 .
 | |
| 	 */
 | |
| 	private static function get_rand_seed_session() {
 | |
| 		// already prepared.
 | |
| 		if ( self::$rand_seed_session ) {
 | |
| 			return self::$rand_seed_session;
 | |
| 		}
 | |
| 
 | |
| 		// Reset vpf_random_seed on load of initial archive page.
 | |
| 		if ( self::get_current_page_number() === 1 ) {
 | |
| 			if ( isset( self::$rand_seed_session ) ) {
 | |
| 				self::$rand_seed_session = false;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Get vpf_random_seed from request variable if it exists.
 | |
|         // phpcs:ignore WordPress.Security.NonceVerification
 | |
| 		if ( isset( $_REQUEST['vpf_random_seed'] ) && is_numeric( $_REQUEST['vpf_random_seed'] ) ) {
 | |
|             // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.NonceVerification
 | |
| 			self::$rand_seed_session = Visual_Portfolio_Security::sanitize_number( $_REQUEST['vpf_random_seed'] );
 | |
| 		}
 | |
| 
 | |
| 		// Set new vpf_random_seed if none exists.
 | |
| 		if ( ! self::$rand_seed_session ) {
 | |
| 			self::$rand_seed_session = wp_rand();
 | |
| 		}
 | |
| 
 | |
| 		return self::$rand_seed_session;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get query params array.
 | |
| 	 *
 | |
| 	 * @param array $options portfolio options.
 | |
| 	 * @param bool  $for_filter prevent retrieving GET variable if used for filter.
 | |
| 	 * @param int   $layout_id portfolio layout id.
 | |
| 	 *
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public static function get_query_params( $options, $for_filter = false, $layout_id = false ) {
 | |
| 		$options    = apply_filters( 'vpf_extend_options_before_query_args', $options, $layout_id );
 | |
| 		$query_opts = array();
 | |
| 		$is_images  = 'images' === $options['content_source'];
 | |
| 
 | |
| 		$paged = 0;
 | |
| 		if ( $options['pagination'] || $is_images ) {
 | |
| 			$paged = self::get_current_page_number();
 | |
| 		}
 | |
| 		$count = intval( $options['items_count'] );
 | |
| 
 | |
| 		if ( $is_images ) {
 | |
| 			$query_opts['images'] = array();
 | |
| 
 | |
| 			if ( ! isset( $options['images'] ) || ! is_array( $options['images'] ) ) {
 | |
| 				$options['images'] = array();
 | |
| 			}
 | |
| 
 | |
| 			// add unique IDs.
 | |
| 			foreach ( $options['images'] as $k => $img ) {
 | |
| 				$options['images'][ $k ]['uid'] = hash( 'crc32b', 'image-' . $k . $img['id'] );
 | |
| 			}
 | |
| 
 | |
| 			if ( $count < 0 ) {
 | |
| 				$count = 99999;
 | |
| 			}
 | |
| 
 | |
| 			// Load certain taxonomies.
 | |
| 			$images = array();
 | |
| 
 | |
|             // phpcs:ignore WordPress.Security.NonceVerification
 | |
| 			if ( ! $for_filter && ( isset( $_GET['vp_filter'] ) || isset( $query_opts['vp_filter'] ) ) ) {
 | |
|                 // phpcs:ignore WordPress.Security.NonceVerification
 | |
| 				$category = sanitize_text_field( wp_unslash( $_GET['vp_filter'] ?? $query_opts['vp_filter'] ) );
 | |
| 
 | |
| 				foreach ( $options['images'] as $img ) {
 | |
| 					if ( isset( $img['categories'] ) && is_array( $img['categories'] ) ) {
 | |
| 						foreach ( $img['categories'] as $cat ) {
 | |
| 							if ( self::create_slug( $cat ) === $category ) {
 | |
| 								$images[] = $img;
 | |
| 								break;
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			} else {
 | |
| 				$images = $options['images'];
 | |
| 			}
 | |
| 
 | |
| 			$images_ids = array();
 | |
| 			foreach ( $images as $k => $img ) {
 | |
| 				$images_ids[] = (int) $img['id'];
 | |
| 			}
 | |
| 
 | |
| 			// Find all used attachments.
 | |
| 			$all_attachments = get_posts(
 | |
| 				array(
 | |
| 					'post_type'              => 'attachment',
 | |
| 					'posts_per_page'         => -1,
 | |
| 					'paged'                  => -1,
 | |
| 					'post__in'               => $images_ids,
 | |
| 					'update_post_meta_cache' => false,
 | |
| 					'update_post_term_cache' => false,
 | |
| 				)
 | |
| 			);
 | |
| 
 | |
| 			// prepare titles and descriptions.
 | |
| 			foreach ( $images as $k => $img ) {
 | |
| 				$img_meta = array(
 | |
| 					'title'       => '',
 | |
| 					'description' => '',
 | |
| 					'caption'     => '',
 | |
| 					'alt'         => '',
 | |
| 					'none'        => '',
 | |
| 					'date'        => '',
 | |
| 				);
 | |
| 
 | |
| 				// Find current attachment post data.
 | |
| 				$attachment = false;
 | |
| 				foreach ( $all_attachments as $post ) {
 | |
| 					if ( $post->ID === (int) $img['id'] ) {
 | |
| 						$attachment = $post;
 | |
| 						break;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if ( $attachment ) {
 | |
| 					// get image meta if needed.
 | |
| 					if ( 'none' !== $options['images_titles_source'] || 'none' !== $options['images_descriptions_source'] ) {
 | |
| 						if ( $attachment && 'attachment' === $attachment->post_type ) {
 | |
| 							$img_meta['title']       = $attachment->post_title;
 | |
| 							$img_meta['description'] = $attachment->post_content;
 | |
| 							$img_meta['caption']     = wp_get_attachment_caption( $attachment->ID );
 | |
| 							$img_meta['alt']         = get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true );
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					// title.
 | |
| 					if ( 'custom' !== $options['images_titles_source'] ) {
 | |
| 						$images[ $k ]['title'] = isset( $img_meta[ $options['images_titles_source'] ] ) ? $img_meta[ $options['images_titles_source'] ] : '';
 | |
| 					}
 | |
| 
 | |
| 					// description.
 | |
| 					if ( 'custom' !== $options['images_descriptions_source'] ) {
 | |
| 						$images[ $k ]['description'] = isset( $img_meta[ $options['images_descriptions_source'] ] ) ? $img_meta[ $options['images_descriptions_source'] ] : '';
 | |
| 					}
 | |
| 
 | |
| 					// add published date.
 | |
| 					$images[ $k ]['published_time'] = get_the_date( 'Y-m-d H:i:s', $attachment );
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// order.
 | |
| 			$custom_order           = false;
 | |
| 			$custom_order_direction = $options['images_order_direction'];
 | |
| 
 | |
| 			if ( isset( $options['images_order_by'] ) ) {
 | |
| 				$custom_order = $options['images_order_by'];
 | |
| 			}
 | |
| 
 | |
| 			// custom sorting.
 | |
|             // phpcs:ignore WordPress.Security.NonceVerification
 | |
| 			if ( isset( $_GET['vp_sort'] ) ) {
 | |
|                 // phpcs:ignore WordPress.Security.NonceVerification
 | |
| 				$custom_get_order = sanitize_text_field( wp_unslash( $_GET['vp_sort'] ) );
 | |
| 
 | |
| 				switch ( $custom_get_order ) {
 | |
| 					case 'title':
 | |
| 					case 'date':
 | |
| 						$custom_order           = $custom_get_order;
 | |
| 						$custom_order_direction = 'asc';
 | |
| 						break;
 | |
| 					case 'title_desc':
 | |
| 						$custom_order           = 'title';
 | |
| 						$custom_order_direction = 'desc';
 | |
| 						break;
 | |
| 					case 'date_desc':
 | |
| 						$custom_order           = 'date';
 | |
| 						$custom_order_direction = 'desc';
 | |
| 						break;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			if ( $custom_order && ! empty( $images ) ) {
 | |
| 				switch ( $custom_order ) {
 | |
| 					case 'date':
 | |
| 					case 'title':
 | |
| 						$sort_tmp   = array();
 | |
| 						$new_images = array();
 | |
| 						$sort_by    = 'date';
 | |
| 
 | |
| 						if ( 'title' === $custom_order ) {
 | |
| 							$sort_by = 'title';
 | |
| 						}
 | |
| 
 | |
| 						foreach ( $images as &$ma ) {
 | |
| 							$sort_tmp[] = &$ma[ $sort_by ];
 | |
| 						}
 | |
| 
 | |
| 						array_multisort( $sort_tmp, $images );
 | |
| 						foreach ( $images as &$ma ) {
 | |
| 							$new_images[] = $ma;
 | |
| 						}
 | |
| 
 | |
| 						$images = $new_images;
 | |
| 						break;
 | |
| 					case 'rand':
 | |
| 						// We don't need to randomize order for filter,
 | |
| 						// because filter list will be always changed once AJAX loaded.
 | |
| 						if ( ! $for_filter ) {
 | |
|                             // phpcs:ignore WordPress.WP.AlternativeFunctions.rand_seeding_mt_srand
 | |
| 							mt_srand( self::get_rand_seed_session() );
 | |
| 							for ( $i = count( $images ) - 1; $i > 0; $i-- ) {
 | |
|                                 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.rand_mt_rand
 | |
| 								$j            = @mt_rand( 0, $i );
 | |
| 								$tmp          = $images[ $i ];
 | |
| 								$images[ $i ] = $images[ $j ];
 | |
| 								$images[ $j ] = $tmp;
 | |
| 							}
 | |
| 						}
 | |
| 
 | |
| 						break;
 | |
| 				}
 | |
| 				if ( 'desc' === $custom_order_direction ) {
 | |
| 					$images = array_reverse( $images );
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// pages count.
 | |
| 			$query_opts['max_num_pages'] = ceil( count( $images ) / $count );
 | |
| 
 | |
| 			$start_from_item = ( $paged - 1 ) * $count;
 | |
| 			$end_on_item     = $start_from_item + $count;
 | |
| 
 | |
| 			if ( $for_filter ) {
 | |
| 				$start_from_item = 0;
 | |
| 				$end_on_item     = 99999;
 | |
| 			}
 | |
| 
 | |
| 			// get images for current page only.
 | |
| 			foreach ( (array) $images as $k => $img ) {
 | |
| 				$i = $k + 1;
 | |
| 				if ( $i > $start_from_item && $i <= $end_on_item ) {
 | |
| 					$query_opts['images'][] = $img;
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			$query_opts = array(
 | |
| 				'posts_per_page' => $count,
 | |
| 				'paged'          => $paged,
 | |
| 				'orderby'        => 'post_date',
 | |
| 				'order'          => 'DESC',
 | |
| 				'post_type'      => 'portfolio',
 | |
| 			);
 | |
| 
 | |
| 			// Get all available categories for filter.
 | |
| 			if ( $for_filter ) {
 | |
| 				$query_opts['posts_per_page'] = -1;
 | |
| 				$query_opts['paged']          = -1;
 | |
| 			}
 | |
| 
 | |
| 			// Post based.
 | |
| 			if ( 'post-based' === $options['content_source'] ) {
 | |
| 				// Exclude IDs.
 | |
| 				if ( ! empty( $options['posts_excluded_ids'] ) ) {
 | |
| 					$query_opts['post__not_in'] = $options['posts_excluded_ids'];
 | |
| 				}
 | |
| 
 | |
| 				// Order By.
 | |
| 				switch ( $options['posts_order_by'] ) {
 | |
| 					case 'title':
 | |
| 						$query_opts['orderby'] = 'title';
 | |
| 						break;
 | |
| 
 | |
| 					case 'id':
 | |
| 						$query_opts['orderby'] = 'ID';
 | |
| 						break;
 | |
| 
 | |
| 					case 'post__in':
 | |
| 						$query_opts['orderby'] = 'post__in';
 | |
| 						break;
 | |
| 
 | |
| 					case 'menu_order':
 | |
| 						// We should order by `menu_order` and as fallback order by `post_date`.
 | |
| 						$query_opts['orderby'] = array(
 | |
| 							'menu_order' => $options['posts_order_direction'],
 | |
| 							'post_date'  => 'desc',
 | |
| 						);
 | |
| 						break;
 | |
| 
 | |
| 					case 'comment_count':
 | |
| 						$query_opts['orderby'] = 'comment_count';
 | |
| 						break;
 | |
| 
 | |
| 					case 'modified':
 | |
| 						$query_opts['orderby'] = 'modified';
 | |
| 						break;
 | |
| 
 | |
| 					case 'rand':
 | |
| 						// Update ORDER BY clause to use vpf_random_seed.
 | |
| 						$query_opts['orderby'] = 'RAND(' . self::get_rand_seed_session() . ')';
 | |
| 						break;
 | |
| 
 | |
| 					default:
 | |
| 						$query_opts['orderby'] = 'post_date';
 | |
| 						break;
 | |
| 				}
 | |
| 
 | |
| 				// Order.
 | |
| 				$query_opts['order'] = $options['posts_order_direction'];
 | |
| 
 | |
| 				if ( 'ids' === $options['posts_source'] ) { // IDs.
 | |
| 					$query_opts['post_type']    = 'any';
 | |
| 					$query_opts['post__not_in'] = array();
 | |
| 
 | |
| 					if ( ! empty( $options['posts_ids'] ) ) {
 | |
| 						$query_opts['post__in'] = $options['posts_ids'];
 | |
| 					}
 | |
| 				} elseif ( 'custom_query' === $options['posts_source'] ) { // Custom Query.
 | |
| 					$query_opts['post_type'] = 'any';
 | |
| 
 | |
| 					$tmp_arr = array();
 | |
| 					parse_str( html_entity_decode( $options['posts_custom_query'] ), $tmp_arr );
 | |
| 					$query_opts = array_merge( $query_opts, $tmp_arr );
 | |
| 				} elseif ( 'current_query' === $options['posts_source'] ) {
 | |
| 					global $wp_query;
 | |
| 
 | |
| 					if ( $wp_query && isset( $wp_query->query_vars ) && is_array( $wp_query->query_vars ) ) {
 | |
| 						$query_vars = $wp_query->query_vars;
 | |
| 
 | |
| 						// Unset `offset` because if is set, $wp_query overrides/ignores the paged parameter and breaks pagination.
 | |
| 						if ( isset( $query_vars['offset'] ) ) {
 | |
| 							unset( $query_vars['offset'] );
 | |
| 						}
 | |
| 
 | |
| 						// Add post type.
 | |
| 						if ( empty( $query_vars['post_type'] ) && is_singular() ) {
 | |
| 							$query_vars['post_type'] = get_post_type( get_the_ID() );
 | |
| 						}
 | |
| 
 | |
| 						// Add pagination paged value.
 | |
| 						if ( $query_opts['paged'] && ( ! isset( $query_vars['paged'] ) || ! $query_vars['paged'] ) ) {
 | |
| 							$query_vars['paged'] = $query_opts['paged'];
 | |
| 						}
 | |
| 
 | |
| 						$query_opts = $query_vars;
 | |
| 					}
 | |
| 				} else {
 | |
| 					$query_opts['post_type'] = $options['posts_source'];
 | |
| 
 | |
| 					// Post Types Set.
 | |
| 					if ( 'post_types_set' === $options['posts_source'] ) {
 | |
| 						$query_opts['post_type'] = (array) $options['post_types_set'];
 | |
| 					}
 | |
| 
 | |
| 					// Taxonomies.
 | |
| 					if ( ! empty( $options['posts_taxonomies'] ) && ! isset( $query_opts['tax_query'] ) ) {
 | |
| 						$terms_list = get_terms(
 | |
| 							get_object_taxonomies( is_array( $query_opts['post_type'] ) ? $query_opts['post_type'] : array( $query_opts['post_type'] ) ),
 | |
| 							array(
 | |
| 								'hide_empty' => false,
 | |
| 							)
 | |
| 						);
 | |
| 
 | |
|                         // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
 | |
| 						$query_opts['tax_query'] = array(
 | |
| 							// We save strings like 'or', 'and'
 | |
| 							// but to use it we need these strings in uppercase.
 | |
| 							'relation' => strtoupper( $options['posts_taxonomies_relation'] ),
 | |
| 						);
 | |
| 
 | |
| 						// We need this empty array, because when taxonomy selected,
 | |
| 						// and posts don't have this taxonomy, we will see all available posts.
 | |
| 						// Related topic: https://wordpress.org/support/topic/exclude-certain-category-from-filter/.
 | |
| 						if ( 'OR' === $query_opts['tax_query']['relation'] ) {
 | |
| 							$query_opts['tax_query'][] = array();
 | |
| 						}
 | |
| 
 | |
| 						foreach ( $options['posts_taxonomies'] as $taxonomy ) {
 | |
| 							$taxonomy_name = null;
 | |
| 
 | |
| 							foreach ( $terms_list as $term ) {
 | |
| 								if ( $term->term_id === (int) $taxonomy ) {
 | |
| 									$taxonomy_name = $term->taxonomy;
 | |
| 									continue;
 | |
| 								}
 | |
| 							}
 | |
| 
 | |
| 							if ( $taxonomy_name ) {
 | |
| 								$query_opts['tax_query'][] = array(
 | |
| 									'taxonomy' => $taxonomy_name,
 | |
| 									'field'    => 'id',
 | |
| 									'terms'    => $taxonomy,
 | |
| 								);
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					// Offset.
 | |
| 					if ( $options['posts_offset'] ) {
 | |
| 						$query_opts['offset'] = $options['posts_offset'] + ( $paged - 1 ) * $count;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				// Avoid duplicates.
 | |
| 				// We should prevent this when using filter, since all current posts will be excluded
 | |
| 				// from the filter query and we may not see all filter buttons.
 | |
| 				if ( ! $for_filter && $options['posts_avoid_duplicate_posts'] ) {
 | |
| 					$not_id                     = (array) ( isset( $query_opts['post__not_in'] ) ? $query_opts['post__not_in'] : array() );
 | |
| 					$query_opts['post__not_in'] = array_merge( $not_id, self::get_all_used_posts() );
 | |
| 
 | |
| 					// Remove posts from post__in.
 | |
| 					if ( isset( $query_opts['post__in'] ) ) {
 | |
| 						$query_opts['post__in'] = array_diff( (array) $query_opts['post__in'], (array) $query_opts['post__not_in'] );
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// Custom sorting.
 | |
|             // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 | |
| 			if ( isset( $_GET['vp_sort'] ) ) {
 | |
|                 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 | |
| 				$custom_get_order       = sanitize_text_field( wp_unslash( $_GET['vp_sort'] ) );
 | |
| 				$custom_order           = false;
 | |
| 				$custom_order_direction = false;
 | |
| 
 | |
| 				switch ( $custom_get_order ) {
 | |
| 					case 'title':
 | |
| 					case 'date':
 | |
| 						$custom_order           = 'post_' . $custom_get_order;
 | |
| 						$custom_order_direction = 'asc';
 | |
| 						break;
 | |
| 					case 'title_desc':
 | |
| 						$custom_order           = 'post_title';
 | |
| 						$custom_order_direction = 'desc';
 | |
| 						break;
 | |
| 					case 'date_desc':
 | |
| 						$custom_order           = 'post_date';
 | |
| 						$custom_order_direction = 'desc';
 | |
| 						break;
 | |
| 				}
 | |
| 
 | |
| 				if ( $custom_order && $custom_order_direction ) {
 | |
| 					$query_opts['orderby'] = $custom_order;
 | |
| 					$query_opts['order']   = $custom_order_direction;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			// Load certain taxonomies using custom filter.
 | |
|             // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 | |
| 			if ( ! $for_filter && ( isset( $_GET['vp_filter'] ) || isset( $query_opts['vp_filter'] ) ) ) {
 | |
|                 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 | |
| 				$taxonomies = sanitize_text_field( wp_unslash( $_GET['vp_filter'] ?? $query_opts['vp_filter'] ) );
 | |
| 				$taxonomies = explode( ':', $taxonomies );
 | |
| 
 | |
| 				if ( $taxonomies && isset( $taxonomies[0] ) && isset( $taxonomies[1] ) ) {
 | |
|                     // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
 | |
| 					$query_opts['tax_query'] = array(
 | |
| 						'relation' => 'AND',
 | |
| 						array(
 | |
| 							'taxonomy' => $taxonomies[0],
 | |
| 							'field'    => 'slug',
 | |
| 							'terms'    => $taxonomies[1],
 | |
| 						),
 | |
| 						isset( $query_opts['tax_query'] ) ? $query_opts['tax_query'] : '',
 | |
| 					);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		$query_opts = apply_filters( 'vpf_extend_query_args', $query_opts, $options, $layout_id );
 | |
| 
 | |
| 		return $query_opts;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print notice
 | |
| 	 *
 | |
| 	 * @param string $notice notice string.
 | |
| 	 */
 | |
| 	public static function notice( $notice ) {
 | |
| 		if ( ! $notice ) {
 | |
| 			return;
 | |
| 		}
 | |
| 		visual_portfolio()->include_template(
 | |
| 			'notices/notices',
 | |
| 			array(
 | |
| 				'notice' => $notice,
 | |
| 			)
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print error
 | |
| 	 *
 | |
| 	 * @param string $error error string.
 | |
| 	 */
 | |
| 	public static function error( $error ) {
 | |
| 		if ( ! $error ) {
 | |
| 			return;
 | |
| 		}
 | |
| 		visual_portfolio()->include_template(
 | |
| 			'errors/errors',
 | |
| 			array(
 | |
| 				'error' => $error,
 | |
| 			)
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print filters
 | |
| 	 *
 | |
| 	 * @param array $vp_options current vp_list options.
 | |
| 	 */
 | |
| 	public static function filter( $vp_options ) {
 | |
| 		if ( ! $vp_options['filter'] ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$is_images  = 'images' === $vp_options['content_source'];
 | |
| 		$is_social  = 'social-stream' === $vp_options['content_source'];
 | |
| 		$query_opts = self::get_query_params( $vp_options, true );
 | |
| 
 | |
| 		// Get active item.
 | |
| 		$active_item = self::get_filter_active_item( $query_opts );
 | |
| 
 | |
| 		if ( $is_images || $is_social ) {
 | |
| 			$term_items = self::get_images_terms( $query_opts, $active_item );
 | |
| 		} else {
 | |
| 			$portfolio_query = new WP_Query( $query_opts );
 | |
| 			$term_items      = self::get_posts_terms( $portfolio_query, $active_item );
 | |
| 		}
 | |
| 
 | |
| 		// Add 'All' active item.
 | |
| 		if ( ! empty( $term_items['terms'] ) && $vp_options['filter_text_all'] ) {
 | |
| 			array_unshift(
 | |
| 				$term_items['terms'],
 | |
| 				array(
 | |
| 					'filter'      => '*',
 | |
| 					'label'       => $vp_options['filter_text_all'],
 | |
| 					'description' => false,
 | |
| 					'count'       => false,
 | |
| 					'id'          => 0,
 | |
| 					'parent'      => 0,
 | |
| 					'active'      => ! $term_items['there_is_active'],
 | |
| 					'url'         => self::get_pagenum_link(
 | |
| 						array(
 | |
| 							'vp_filter' => '',
 | |
| 							'vp_page'   => 1,
 | |
| 						)
 | |
| 					),
 | |
| 					'class'       => 'vp-filter__item' . ( ! $term_items['there_is_active'] ? ' vp-filter__item-active' : '' ),
 | |
| 				)
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		// No filters available.
 | |
| 		if ( empty( $term_items['terms'] ) ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		// get options for the current filter.
 | |
| 		$filter_options      = array();
 | |
| 		$filter_options_slug = 'filter_' . $vp_options['filter'] . '__';
 | |
| 
 | |
| 		foreach ( $vp_options as $k => $opt ) {
 | |
| 			// add option to array.
 | |
| 			if ( substr( $k, 0, strlen( $filter_options_slug ) ) === $filter_options_slug ) {
 | |
| 				$opt_name                    = str_replace( $filter_options_slug, '', $k );
 | |
| 				$filter_options[ $opt_name ] = $opt;
 | |
| 			}
 | |
| 
 | |
| 			// remove style options from the options list.
 | |
| 			if ( substr( $k, 0, strlen( $filter_options_slug ) ) === $filter_options_slug ) {
 | |
| 				unset( $vp_options[ $k ] );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		$args = array(
 | |
| 			'class'      => 'vp-filter',
 | |
|             // phpcs:ignore Squiz.PHP.CommentedOutCode.Found, Squiz.Commenting.BlockComment.NoEmptyLineBefore
 | |
| 			/*
 | |
| 			 * Example:
 | |
| 				array(
 | |
| 					array(
 | |
| 						'filter'      => '*',
 | |
| 						'label'       => $options['filter_text_all'],
 | |
| 						'description' => false,
 | |
| 						'count'       => false,
 | |
| 						'active'      => true,
 | |
| 						'url'         => Visual_Portfolio_Get::get_pagenum_link(
 | |
| 							array(
 | |
| 								'vp_filter' => '',
 | |
| 								'vp_page' => 1,
 | |
| 							)
 | |
| 						),
 | |
| 						'class'       => 'vp-filter__item',
 | |
| 					),
 | |
| 				)
 | |
| 			 */
 | |
| 			'items'      => apply_filters( 'vpf_extend_filter_items', $term_items['terms'], $vp_options ),
 | |
| 			'show_count' => $vp_options['filter_show_count'],
 | |
| 			'opts'       => $filter_options,
 | |
| 			'vp_opts'    => $vp_options,
 | |
| 		);
 | |
| 
 | |
| 		?>
 | |
| 		<div class="vp-portfolio__filter-wrap">
 | |
| 		<?php
 | |
| 
 | |
| 		$filter_style_pref = '';
 | |
| 
 | |
| 		if ( 'default' !== $vp_options['filter'] ) {
 | |
| 			$filter_style_pref = '/' . $vp_options['filter'];
 | |
| 		}
 | |
| 
 | |
| 		visual_portfolio()->include_template( 'items-list/filter' . $filter_style_pref . '/filter', $args );
 | |
| 
 | |
| 		// We need to include these styles, since users can insert filters using separate shortcode.
 | |
| 		Visual_Portfolio_Assets::store_used_assets(
 | |
| 			'visual-portfolio-filter-' . $vp_options['filter'],
 | |
| 			'items-list/filter' . $filter_style_pref . '/style',
 | |
| 			'template_style'
 | |
| 		);
 | |
| 
 | |
| 		?>
 | |
| 		</div>
 | |
| 		<?php
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get post terms.
 | |
| 	 *
 | |
| 	 * @param WP_Query $portfolio_query - The WordPress Portfolio Query class.
 | |
| 	 * @param boolean  $active_item - Actime filter item.
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public static function get_posts_terms( $portfolio_query, $active_item ) {
 | |
| 		/**
 | |
| 		 * TODO: make caching using set_transient function. Info here - https://wordpress.stackexchange.com/a/145960
 | |
| 		 */
 | |
| 		$term_ids        = array();
 | |
| 		$term_taxonomies = array();
 | |
| 		$terms           = array();
 | |
| 		$there_is_active = false;
 | |
| 
 | |
| 		// stupid hack as wp_reset_postdata() function is not working for me...
 | |
| 		$old_post = $GLOBALS['post'];
 | |
| 		while ( $portfolio_query->have_posts() ) {
 | |
| 			$portfolio_query->the_post();
 | |
| 			$all_taxonomies = get_object_taxonomies( get_post() );
 | |
| 
 | |
| 			foreach ( $all_taxonomies as $cat ) {
 | |
| 				// allow only specific taxonomies for filter.
 | |
| 				if ( ! self::allow_taxonomies_for_filter( $cat ) ) {
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				// Retrieve terms.
 | |
| 				$category = get_the_terms( get_post(), $cat );
 | |
| 				if ( ! $category ) {
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				// Prepare each terms array.
 | |
| 				foreach ( $category as $cat_item ) {
 | |
| 					if ( ! in_array( $cat_item->term_id, $term_ids, true ) ) {
 | |
| 						$term_ids[] = $cat_item->term_id;
 | |
| 					}
 | |
| 					if ( ! in_array( $cat_item->taxonomy, $term_taxonomies, true ) ) {
 | |
| 						$term_taxonomies[] = $cat_item->taxonomy;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		$portfolio_query->reset_postdata();
 | |
| 
 | |
| 		// Sometimes, when we use WPBakery Page Builder, without this reset output is wrong.
 | |
| 		wp_reset_postdata();
 | |
| 
 | |
| 		// stupid hack as wp_reset_postdata() function is not working in some situations...
 | |
|         // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
 | |
| 		$GLOBALS['post'] = $old_post;
 | |
| 
 | |
| 		// Get all available terms and then pick only needed by ID
 | |
| 		// we need this to support reordering plugins.
 | |
| 		$all_terms = get_terms(
 | |
| 			array(
 | |
| 				'taxonomy'   => $term_taxonomies,
 | |
| 				'hide_empty' => true,
 | |
| 			)
 | |
| 		);
 | |
| 
 | |
| 		if ( isset( $all_terms ) && is_array( $all_terms ) ) {
 | |
| 			foreach ( $all_terms as $term ) {
 | |
| 				if ( in_array( $term->term_id, $term_ids, true ) ) {
 | |
| 					$unique_name = rawurlencode( $term->taxonomy . ':' ) . $term->slug;
 | |
| 
 | |
| 					$url = self::get_pagenum_link(
 | |
| 						array(
 | |
| 							'vp_filter' => $unique_name,
 | |
| 							'vp_page'   => 1,
 | |
| 						)
 | |
| 					);
 | |
| 
 | |
| 					$is_active = rawurldecode( $unique_name ) === $active_item;
 | |
| 
 | |
| 					$terms[ $unique_name ] = array(
 | |
| 						'filter'      => $term->slug,
 | |
| 						'label'       => $term->name,
 | |
| 						'description' => $term->description,
 | |
| 						'count'       => $term->count,
 | |
| 						'taxonomy'    => $term->taxonomy,
 | |
| 						'id'          => $term->term_id,
 | |
| 						'parent'      => $term->parent,
 | |
| 						'active'      => $is_active,
 | |
| 						'url'         => $url,
 | |
| 						'class'       => 'vp-filter__item' . ( $is_active ? ' vp-filter__item-active' : '' ),
 | |
| 					);
 | |
| 
 | |
| 					if ( $is_active ) {
 | |
| 						$there_is_active = true;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return array(
 | |
| 			'terms'           => $terms,
 | |
| 			'there_is_active' => $there_is_active,
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get images terms.
 | |
| 	 *
 | |
| 	 * @param array   $query_opts - Query array params.
 | |
| 	 * @param boolean $active_item - Active filter item.
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public static function get_images_terms( $query_opts, $active_item ) {
 | |
| 		$terms           = array();
 | |
| 		$there_is_active = false;
 | |
| 		// calculate categories count.
 | |
| 		$categories_count = array();
 | |
| 		foreach ( $query_opts['images'] as $img ) {
 | |
| 			if ( isset( $img['categories'] ) && is_array( $img['categories'] ) ) {
 | |
| 				foreach ( $img['categories'] as $cat ) {
 | |
| 					$categories_count[ $cat ] = ( isset( $categories_count[ $cat ] ) ? $categories_count[ $cat ] : 0 ) + 1;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		foreach ( $query_opts['images'] as $img ) {
 | |
| 			if ( isset( $img['categories'] ) && is_array( $img['categories'] ) ) {
 | |
| 				foreach ( $img['categories'] as $cat ) {
 | |
| 					$slug = self::create_slug( $cat );
 | |
| 					$url  = self::get_pagenum_link(
 | |
| 						array(
 | |
| 							'vp_filter' => rawurlencode( $slug ),
 | |
| 							'vp_page'   => 1,
 | |
| 						)
 | |
| 					);
 | |
| 
 | |
| 					// add in terms array.
 | |
| 					$terms[ $slug ] = array(
 | |
| 						'filter'      => $slug,
 | |
| 						'label'       => $cat,
 | |
| 						'description' => '',
 | |
| 						'count'       => isset( $categories_count[ $cat ] ) && $categories_count[ $cat ] ? $categories_count[ $cat ] : '',
 | |
| 						'taxonomy'    => 'category',
 | |
| 						'id'          => 0,
 | |
| 						'parent'      => 0,
 | |
| 						'active'      => $active_item === $slug,
 | |
| 						'url'         => $url,
 | |
| 						'class'       => 'vp-filter__item' . ( $active_item === $slug ? ' vp-filter__item-active' : '' ),
 | |
| 					);
 | |
| 
 | |
| 					if ( $active_item === $slug ) {
 | |
| 						$there_is_active = true;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return array(
 | |
| 			'terms'           => $terms,
 | |
| 			'there_is_active' => $there_is_active,
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get filter active item.
 | |
| 	 *
 | |
| 	 * @param array $query_opts - Query array params.
 | |
| 	 * @return boolean
 | |
| 	 */
 | |
| 	public static function get_filter_active_item( $query_opts ) {
 | |
| 		// Get active item.
 | |
| 		$active_item = false;
 | |
| 
 | |
|         // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 | |
| 		if ( ( isset( $_GET['vp_filter'] ) || isset( $query_opts['vp_filter'] ) ) ) {
 | |
|             // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 | |
| 			$active_item = sanitize_text_field( wp_unslash( $_GET['vp_filter'] ?? $query_opts['vp_filter'] ) );
 | |
| 		}
 | |
| 
 | |
| 		return $active_item;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print sort
 | |
| 	 *
 | |
| 	 * @param array $vp_options current vp_list options.
 | |
| 	 */
 | |
| 	public static function sort( $vp_options ) {
 | |
| 		if ( ! $vp_options['sort'] ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$terms = array();
 | |
| 
 | |
| 		// Get active item.
 | |
| 		$active_item = false;
 | |
| 
 | |
|         // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 | |
| 		if ( isset( $_GET['vp_sort'] ) ) {
 | |
|             // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 | |
| 			$active_item = sanitize_text_field( wp_unslash( $_GET['vp_sort'] ) );
 | |
| 		}
 | |
| 
 | |
| 		$sort_items = apply_filters(
 | |
| 			'vpf_extend_sort_items',
 | |
| 			array(
 | |
| 				''           => esc_html__( 'Default sorting', 'visual-portfolio' ),
 | |
| 				'date_desc'  => esc_html__( 'Sort by date (newest)', 'visual-portfolio' ),
 | |
| 				'date'       => esc_html__( 'Sort by date (oldest)', 'visual-portfolio' ),
 | |
| 				'title'      => esc_html__( 'Sort by title (A-Z)', 'visual-portfolio' ),
 | |
| 				'title_desc' => esc_html__( 'Sort by title (Z-A)', 'visual-portfolio' ),
 | |
| 			),
 | |
| 			$vp_options
 | |
| 		);
 | |
| 
 | |
| 		foreach ( $sort_items as $slug => $label ) {
 | |
| 			$url = apply_filters(
 | |
| 				'vpf_extend_sort_item_url',
 | |
| 				self::get_pagenum_link(
 | |
| 					array(
 | |
| 						'vp_sort' => rawurlencode( $slug ),
 | |
| 						'vp_page' => 1,
 | |
| 					)
 | |
| 				),
 | |
| 				$slug,
 | |
| 				$vp_options
 | |
| 			);
 | |
| 
 | |
| 			$is_active = ! $active_item && ! $slug ? true : $active_item === $slug;
 | |
| 
 | |
| 			// add in terms array.
 | |
| 			$terms[ $slug ] = array(
 | |
| 				'sort'        => $slug,
 | |
| 				'label'       => $label,
 | |
| 				'description' => '',
 | |
| 				'active'      => $is_active,
 | |
| 				'url'         => $url,
 | |
| 				'class'       => 'vp-sort__item' . ( $is_active ? ' vp-sort__item-active' : '' ),
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		// get options for the current sort.
 | |
| 		$sort_options      = array();
 | |
| 		$sort_options_slug = 'sort_' . $vp_options['sort'] . '__';
 | |
| 
 | |
| 		foreach ( $vp_options as $k => $opt ) {
 | |
| 			// add option to array.
 | |
| 			if ( substr( $k, 0, strlen( $sort_options_slug ) ) === $sort_options_slug ) {
 | |
| 				$opt_name                  = str_replace( $sort_options_slug, '', $k );
 | |
| 				$sort_options[ $opt_name ] = $opt;
 | |
| 			}
 | |
| 
 | |
| 			// remove style options from the options list.
 | |
| 			if ( substr( $k, 0, strlen( $sort_options_slug ) ) === $sort_options_slug ) {
 | |
| 				unset( $vp_options[ $k ] );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		$args = array(
 | |
| 			'class'   => 'vp-sort',
 | |
| 			'items'   => $terms,
 | |
| 			'opts'    => $sort_options,
 | |
| 			'vp_opts' => $vp_options,
 | |
| 		);
 | |
| 
 | |
| 		?>
 | |
| 		<div class="vp-portfolio__sort-wrap">
 | |
| 		<?php
 | |
| 
 | |
| 		$sort_style_pref = '';
 | |
| 
 | |
| 		if ( 'default' !== $vp_options['sort'] ) {
 | |
| 			$sort_style_pref = '/' . $vp_options['sort'];
 | |
| 		}
 | |
| 
 | |
| 		visual_portfolio()->include_template( 'items-list/sort' . $sort_style_pref . '/sort', $args );
 | |
| 
 | |
| 		// We need to include these styles, since users can insert sort using separate shortcode.
 | |
| 		Visual_Portfolio_Assets::store_used_assets(
 | |
| 			'visual-portfolio-sort-' . $vp_options['sort'],
 | |
| 			'items-list/sort' . $sort_style_pref . '/style',
 | |
| 			'template_style'
 | |
| 		);
 | |
| 
 | |
| 		?>
 | |
| 		</div>
 | |
| 		<?php
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print each item
 | |
| 	 *
 | |
| 	 * @param array $args current item data.
 | |
| 	 *      'url' - post/image url.
 | |
| 	 *      'title' - post/image title.
 | |
| 	 *      'format' - post/image format.
 | |
| 	 *      'published' - post/image published.
 | |
| 	 *      'published_time' - post/image published time.
 | |
| 	 *      'categories' - categories array.
 | |
| 	 *      'filter' - filters string.
 | |
| 	 *      'video' - video url.
 | |
| 	 *      'image_id' - image id.
 | |
| 	 *      'img_size_popup' - image size for popup.
 | |
| 	 *      'img_size_md_popup' - md image size for popup.
 | |
| 	 *      'img_size_sm_popup' - sm image size for popup.
 | |
| 	 *      'img_size' - image size.
 | |
| 	 *      'no_image' - no image id.
 | |
| 	 *      'opts' - style options.
 | |
| 	 *      'vp_opts' - vp options.
 | |
| 	 */
 | |
| 	private static function each_item( $args ) {
 | |
| 		global $post;
 | |
| 
 | |
| 		$is_posts = 'post-based' === $args['vp_opts']['content_source'] || 'portfolio' === $args['vp_opts']['content_source'];
 | |
| 
 | |
| 		// In older plugin versions we used the query objects in these templates.
 | |
| 		// And some theme authors used these data to run wp functions to output posts data.
 | |
| 		// In order to add back-compatibility, we need to "restore" such a possibility.
 | |
| 		//
 | |
| 		// Example: https://wordpress.org/support/topic/title-and-link-error-for-blog/.
 | |
| 		$set_post_object = $is_posts && isset( $args['post_id'] ) && $args['post_id'];
 | |
| 
 | |
| 		if ( $set_post_object ) {
 | |
|             // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
 | |
| 			$post = get_post( $args['post_id'] );
 | |
| 
 | |
| 			setup_postdata( $post );
 | |
| 		}
 | |
| 
 | |
| 		// prepare image.
 | |
| 		$args['image'] = Visual_Portfolio_Images::get_attachment_image( $args['image_id'], $args['img_size'], false, '' );
 | |
| 
 | |
| 		// prepare date.
 | |
| 		if ( isset( $args['opts']['show_date'] ) ) {
 | |
| 			if ( 'human' === $args['opts']['show_date'] ) {
 | |
| 				// translators: %s - published in human format.
 | |
| 				$args['published'] = sprintf( esc_html__( '%s ago', 'visual-portfolio' ), human_time_diff( mysql2date( 'U', $args['published_time'], true ), current_time( 'timestamp' ) ) ); //phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
 | |
| 			} elseif ( $args['opts']['show_date'] ) {
 | |
| 				$args['published'] = mysql2date( $args['opts']['date_format'] ? $args['opts']['date_format'] : 'F j, Y', $args['published_time'], true );
 | |
| 			}
 | |
| 
 | |
| 			// fallback for Visual Portfolio 1.2.1 version.
 | |
| 			$args['opts']['date_human_format'] = 'human' === $args['opts']['show_date'];
 | |
| 			$args['published_human_format']    = $args['published'];
 | |
| 		}
 | |
| 
 | |
| 		// add video format args.
 | |
| 		if ( 'video' === $args['format'] && $args['video'] ) {
 | |
| 			$args['format_video_url'] = $args['video'];
 | |
| 
 | |
| 			if ( ! $is_posts ) {
 | |
| 				$args['url'] = $args['video'];
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// prepare read more button.
 | |
| 		if ( isset( $args['opts']['show_read_more'] ) && $args['opts']['show_read_more'] ) {
 | |
| 			if ( $is_posts && 'more_tag' === $args['opts']['show_read_more'] ) {
 | |
| 				if ( strpos( get_post_field( 'post_content', $args['post_id'] ), '<!--more-->' ) ) {
 | |
| 					$args['opts']['read_more_url'] = $args['url'] . '#more-' . $args['post_id'];
 | |
| 				} else {
 | |
| 					$args['opts']['show_read_more'] = false;
 | |
| 				}
 | |
| 			} else {
 | |
| 				$args['opts']['read_more_url'] = $args['url'];
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Click action.
 | |
| 		$args['url_target'] = false;
 | |
| 		$args['url_rel']    = false;
 | |
| 
 | |
| 		switch ( $args['vp_opts']['items_click_action'] ) {
 | |
| 			case 'popup_gallery':
 | |
| 				break;
 | |
| 			case false:
 | |
| 				$args['url'] = false;
 | |
| 				break;
 | |
| 			default:
 | |
| 				$args['url_target'] = $args['vp_opts']['items_click_action_url_target'] ? $args['vp_opts']['items_click_action_url_target'] : false;
 | |
| 				$args['url_rel']    = $args['vp_opts']['items_click_action_url_rel'] ? $args['vp_opts']['items_click_action_url_rel'] : false;
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		// No Image.
 | |
| 		if ( ! $args['image'] && $args['no_image'] ) {
 | |
| 			$args['image'] = Visual_Portfolio_Images::get_attachment_image( $args['no_image'], $args['img_size'], false, '' );
 | |
| 		}
 | |
| 
 | |
| 		// Class.
 | |
| 		$args['class'] = 'vp-portfolio__item-wrap';
 | |
| 		if ( $is_posts ) {
 | |
| 			// post_class functionality.
 | |
| 			$args['class'] = join( ' ', get_post_class( $args['class'], $args['post_id'] ) );
 | |
| 		}
 | |
| 
 | |
| 		if ( ! $is_posts && is_array( $args['categories'] ) && ! empty( $args['categories'] ) ) {
 | |
| 			foreach ( $args['categories'] as $category ) {
 | |
| 				$args['class'] .= ' category-' . $category['slug'];
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if ( $args['uid'] ) {
 | |
| 			$args['class'] .= ' vp-portfolio__item-uid-' . esc_attr( $args['uid'] );
 | |
| 		}
 | |
| 
 | |
| 		$args = apply_filters( 'vpf_each_item_args', $args );
 | |
| 
 | |
| 		// Tag Name.
 | |
| 		$tag_name = $is_posts ? 'article' : 'div';
 | |
| 		$tag_name = apply_filters( 'vpf_each_item_tag_name', $tag_name, $args );
 | |
| 
 | |
| 		$attrs = array(
 | |
| 			'class'          => $args['class'],
 | |
| 			'data-vp-filter' => $args['filter'],
 | |
| 		);
 | |
| 
 | |
| 		if ( $args['focal_point'] && ! empty( $args['focal_point'] ) ) {
 | |
| 			$attrs['style'] = '--vp-images__object-position: ' . esc_attr( 100 * floatval( $args['focal_point']['x'] ) ) . '% ' . esc_attr( 100 * floatval( $args['focal_point']['y'] ) ) . '%;';
 | |
| 		}
 | |
| 
 | |
| 		$attrs = apply_filters( 'vpf_each_item_tag_attrs', $attrs, $args );
 | |
| 		?>
 | |
| 
 | |
| 		<<?php echo esc_attr( $tag_name ); ?>
 | |
| 		<?php
 | |
| 		foreach ( $attrs as $name => $val ) {
 | |
| 			echo esc_attr( $name ) . '="' . esc_attr( $val ) . '" ';
 | |
| 		}
 | |
| 		?>
 | |
| 		>
 | |
| 			<?php
 | |
| 			if ( $args['vp_opts']['items_click_action'] && 'url' !== $args['vp_opts']['items_click_action'] ) {
 | |
| 				self::item_popup_data( $args );
 | |
| 			}
 | |
| 			?>
 | |
| 			<?php do_action( 'vpf_before_each_item', $args ); ?>
 | |
| 			<figure class="vp-portfolio__item">
 | |
| 				<?php
 | |
| 				do_action( 'vpf_each_item_start', $args );
 | |
| 
 | |
| 				$items_style_pref = '';
 | |
| 				if ( 'default' !== $args['vp_opts']['items_style'] ) {
 | |
| 					$items_style_pref = '/' . $args['vp_opts']['items_style'];
 | |
| 				}
 | |
| 				visual_portfolio()->include_template( 'items-list/items-style' . $items_style_pref . '/image', $args );
 | |
| 				visual_portfolio()->include_template( 'items-list/items-style' . $items_style_pref . '/meta', $args );
 | |
| 
 | |
| 				do_action( 'vpf_each_item_end', $args );
 | |
| 				?>
 | |
| 			</figure>
 | |
| 			<?php do_action( 'vpf_after_each_item', $args ); ?>
 | |
| 		</<?php echo esc_attr( $tag_name ); ?>>
 | |
| 		<?php
 | |
| 
 | |
| 		if ( $set_post_object ) {
 | |
| 			wp_reset_postdata();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print item popup data.
 | |
| 	 *
 | |
| 	 * @param array $args - item args.
 | |
| 	 */
 | |
| 	private static function item_popup_data( $args ) {
 | |
| 		$popup_image  = false;
 | |
| 		$popup_video  = false;
 | |
| 		$popup_output = false;
 | |
| 
 | |
| 		if ( isset( $args['allow_popup'] ) && $args['allow_popup'] ) {
 | |
| 			if ( isset( $args['format_video_url'] ) && $args['format_video_url'] ) {
 | |
| 				$popup_video = self::get_popup_video( $args );
 | |
| 			} else {
 | |
| 				$img_id = $args['image_id'] ? $args['image_id'] : $args['no_image'];
 | |
| 
 | |
| 				$popup_image = self::get_popup_image( $img_id, $args );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if ( $popup_image ) {
 | |
| 			$popup_output = self::popup_image_output( $popup_image, $args );
 | |
| 		} elseif ( $popup_video ) {
 | |
| 			$popup_output = self::popup_video_output( $popup_video, $args );
 | |
| 		}
 | |
| 
 | |
| 		$popup_output = apply_filters( 'vpf_popup_output', $popup_output, $args );
 | |
| 
 | |
| 		echo wp_kses( $popup_output, 'vp_popup' );
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get item popup image data.
 | |
| 	 *
 | |
| 	 * @param  int   $img_id - image id.
 | |
| 	 * @param  array $args - item args.
 | |
| 	 * @return array|bool
 | |
| 	 */
 | |
| 	public static function get_popup_image( $img_id, $args ) {
 | |
| 		$popup_image = false;
 | |
| 		if ( $img_id ) {
 | |
| 			$attachment = get_post( $args['image_id'] );
 | |
| 
 | |
| 			if ( $attachment && 'attachment' === $attachment->post_type ) {
 | |
| 				$img_meta    = wp_get_attachment_image_src( $args['image_id'], $args['img_size_popup'] );
 | |
| 				$img_md_meta = wp_get_attachment_image_src( $args['image_id'], $args['img_size_md_popup'] );
 | |
| 				$img_sm_meta = wp_get_attachment_image_src( $args['image_id'], $args['img_size_sm_popup'] );
 | |
| 
 | |
| 				$popup_image = apply_filters(
 | |
| 					'vpf_popup_image_data',
 | |
| 					array(
 | |
| 						'id'               => $args['image_id'],
 | |
| 						'title'            => $attachment->post_title,
 | |
| 						'description'      => $attachment->post_content,
 | |
| 						'caption'          => wp_get_attachment_caption( $attachment->ID ),
 | |
| 						'alt'              => get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true ),
 | |
| 						'url'              => $img_meta[0],
 | |
| 						'srcset'           => wp_get_attachment_image_srcset( $args['image_id'], $args['img_size_popup'] ),
 | |
| 						'width'            => $img_meta[1],
 | |
| 						'height'           => $img_meta[2],
 | |
| 						'md_url'           => $img_md_meta[0],
 | |
| 						'md_width'         => $img_md_meta[1],
 | |
| 						'md_height'        => $img_md_meta[2],
 | |
| 						'sm_url'           => $img_sm_meta[0],
 | |
| 						'sm_width'         => $img_sm_meta[1],
 | |
| 						'sm_height'        => $img_sm_meta[2],
 | |
| 						'item_title'       => $args['title'],
 | |
| 						'item_description' => $args['content'],
 | |
| 						'item_author'      => $args['author'],
 | |
| 						'item_author_url'  => $args['author_url'],
 | |
| 					)
 | |
| 				);
 | |
| 			} elseif ( $args['image_id'] ) {
 | |
| 				$popup_image = apply_filters( 'vpf_popup_custom_image_data', false, $args['image_id'] );
 | |
| 
 | |
| 				// Check items title and description availability.
 | |
| 				if ( $popup_image && ! isset( $popup_image['item_title'] ) ) {
 | |
| 					$popup_image['item_title'] = $popup_image['title'] ?? '';
 | |
| 				}
 | |
| 				if ( $popup_image && ! isset( $popup_image['item_description'] ) ) {
 | |
| 					$popup_image['item_description'] = $popup_image['description'] ?? '';
 | |
| 				}
 | |
| 				if ( $popup_image && ! isset( $popup_image['item_author'] ) ) {
 | |
| 					$popup_image['item_author'] = $popup_image['author'] ?? '';
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return $popup_image;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get item popup video data.
 | |
| 	 *
 | |
| 	 * @param array $args - item args.
 | |
| 	 *
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public static function get_popup_video( $args ) {
 | |
| 		return array(
 | |
| 			'url'              => $args['format_video_url'],
 | |
| 			'poster'           => wp_get_attachment_image_url( $args['image_id'], 'full' ),
 | |
| 			'item_title'       => $args['title'] ?? null,
 | |
| 			'item_description' => $args['content'] ?? null,
 | |
| 			'item_author'      => $args['author'] ?? null,
 | |
| 			'item_author_url'  => $args['author_url'] ?? null,
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print image popup.
 | |
| 	 *
 | |
| 	 * @param array $image_data - item popup image data.
 | |
| 	 * @param array $args  - item args.
 | |
| 	 *
 | |
| 	 * @return string|bool
 | |
| 	 */
 | |
| 	public static function popup_image_output( $image_data, $args ) {
 | |
| 		$popup_output = false;
 | |
| 
 | |
| 		if ( $image_data ) {
 | |
| 			ob_start();
 | |
| 
 | |
| 			$title_source       = $args['vp_opts']['items_click_action_popup_title_source'] ? $args['vp_opts']['items_click_action_popup_title_source'] : '';
 | |
| 			$description_source = $args['vp_opts']['items_click_action_popup_description_source'] ? $args['vp_opts']['items_click_action_popup_description_source'] : '';
 | |
| 
 | |
| 			visual_portfolio()->include_template(
 | |
| 				'popup/image-popup-data',
 | |
| 				array(
 | |
| 					'title_source'       => $title_source,
 | |
| 					'description_source' => $description_source,
 | |
| 					'image_data'         => $image_data,
 | |
| 					'args'               => $args,
 | |
| 					'opts'               => $args['vp_opts'],
 | |
| 				)
 | |
| 			);
 | |
| 
 | |
| 			$popup_output = ob_get_clean();
 | |
| 		}
 | |
| 
 | |
| 		return $popup_output;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print video popup.
 | |
| 	 *
 | |
| 	 * @param array $video_data - item popup video data.
 | |
| 	 * @param array $args  - item args.
 | |
| 	 *
 | |
| 	 * @return string|bool
 | |
| 	 */
 | |
| 	public static function popup_video_output( $video_data, $args ) {
 | |
| 		$popup_output = false;
 | |
| 
 | |
| 		if ( $video_data ) {
 | |
| 			ob_start();
 | |
| 
 | |
| 			$title_source       = $args['vp_opts']['items_click_action_popup_title_source'] ? $args['vp_opts']['items_click_action_popup_title_source'] : '';
 | |
| 			$description_source = $args['vp_opts']['items_click_action_popup_description_source'] ? $args['vp_opts']['items_click_action_popup_description_source'] : '';
 | |
| 
 | |
| 			visual_portfolio()->include_template(
 | |
| 				'popup/video-popup-data',
 | |
| 				array(
 | |
| 					'title_source'       => $title_source,
 | |
| 					'description_source' => $description_source,
 | |
| 					'video_data'         => $video_data,
 | |
| 
 | |
| 					// We need to check existence of args to prevent possible conflicts.
 | |
| 					'args'               => isset( $args ) ? $args : null,
 | |
| 					'opts'               => isset( $args['vp_opts'] ) ? $args['vp_opts'] : null,
 | |
| 				)
 | |
| 			);
 | |
| 
 | |
| 			$popup_output = ob_get_clean();
 | |
| 		}
 | |
| 
 | |
| 		return $popup_output;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Print pagination
 | |
| 	 *
 | |
| 	 * @param array $vp_options - current vp_list options.
 | |
| 	 */
 | |
| 	public static function pagination( $vp_options ) {
 | |
| 		if ( ! $vp_options['pagination_style'] || ! $vp_options['pagination'] ) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		// get options for the current pagination.
 | |
| 		$pagination_options      = array();
 | |
| 		$pagination_options_slug = 'pagination_' . $vp_options['pagination_style'] . '__';
 | |
| 		foreach ( $vp_options as $k => $opt ) {
 | |
| 			// add option to array.
 | |
| 			if ( substr( $k, 0, strlen( $pagination_options_slug ) ) === $pagination_options_slug ) {
 | |
| 				$opt_name                        = str_replace( $pagination_options_slug, '', $k );
 | |
| 				$pagination_options[ $opt_name ] = $opt;
 | |
| 			}
 | |
| 
 | |
| 			// remove style options from the options list.
 | |
| 			if ( substr( $k, 0, strlen( $pagination_options_slug ) ) === $pagination_options_slug ) {
 | |
| 				unset( $vp_options[ $k ] );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		$args = array(
 | |
| 			'type'          => $vp_options['pagination'],
 | |
| 			'next_page_url' => $vp_options['next_page_url'],
 | |
| 			'start_page'    => $vp_options['start_page'],
 | |
| 			'max_pages'     => $vp_options['max_pages'],
 | |
| 			'class'         => 'vp-pagination',
 | |
| 			'opts'          => $pagination_options,
 | |
| 			'vp_opts'       => $vp_options,
 | |
| 		);
 | |
| 
 | |
| 		$args = apply_filters( 'vpf_pagination_args', $args, $vp_options );
 | |
| 
 | |
| 		// No more posts.
 | |
| 		if ( ! $args['next_page_url'] ) {
 | |
| 			$args['class'] .= ' vp-pagination__no-more';
 | |
| 		}
 | |
| 
 | |
| 		?>
 | |
| 		<div class="vp-portfolio__pagination-wrap">
 | |
| 		<?php
 | |
| 
 | |
| 		$pagination_style_pref = '';
 | |
| 		if ( 'default' !== $vp_options['pagination_style'] ) {
 | |
| 			$pagination_style_pref = '/' . $vp_options['pagination_style'];
 | |
| 		}
 | |
| 
 | |
| 		switch ( $vp_options['pagination'] ) {
 | |
| 			case 'infinite':
 | |
| 			case 'load-more':
 | |
| 				if ( 'infinite' === $vp_options['pagination'] ) {
 | |
| 					$args['text_load']     = $vp_options['pagination_infinite_text_load'];
 | |
| 					$args['text_loading']  = $vp_options['pagination_infinite_text_loading'];
 | |
| 					$args['text_end_list'] = $vp_options['pagination_infinite_text_end_list'];
 | |
| 				} else {
 | |
| 					$args['text_load']     = $vp_options['pagination_load_more_text_load'];
 | |
| 					$args['text_loading']  = $vp_options['pagination_load_more_text_loading'];
 | |
| 					$args['text_end_list'] = $vp_options['pagination_load_more_text_end_list'];
 | |
| 				}
 | |
| 
 | |
| 				if ( ! $vp_options['pagination_hide_on_end'] || $args['next_page_url'] ) {
 | |
| 					visual_portfolio()->include_template( 'items-list/pagination' . $pagination_style_pref . '/' . $vp_options['pagination'], $args );
 | |
| 				}
 | |
| 
 | |
| 				break;
 | |
| 			default:
 | |
| 				// Scroll to top.
 | |
| 				if ( $vp_options['pagination_paged__scroll_top'] ) {
 | |
| 					$args['class'] .= ' vp-pagination__scroll-top';
 | |
| 
 | |
| 					if ( isset( $vp_options['pagination_paged__scroll_top_offset'] ) ) {
 | |
| 						$args['scroll_top_offset'] = $vp_options['pagination_paged__scroll_top_offset'];
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				// parse html string and make arrays.
 | |
| 				$filtered_links = self::get_pagination_links( $args, $vp_options );
 | |
| 
 | |
| 				if ( ! empty( $filtered_links ) ) {
 | |
| 					$args['items'] = $filtered_links;
 | |
| 					visual_portfolio()->include_template( 'items-list/pagination' . $pagination_style_pref . '/paged', $args );
 | |
| 				}
 | |
| 
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		?>
 | |
| 		</div>
 | |
| 		<?php
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get pagination links.
 | |
| 	 *
 | |
| 	 * @param array $args - Block Arguments.
 | |
| 	 * @param array $vp_options - Block Options.
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public static function get_pagination_links( $args, $vp_options ) {
 | |
| 		$pagination_links = paginate_links(
 | |
| 			array(
 | |
| 				'base'      => esc_url_raw(
 | |
| 					str_replace(
 | |
| 						999999999,
 | |
| 						'%#%',
 | |
| 						remove_query_arg(
 | |
| 							'add-to-cart',
 | |
| 							self::get_pagenum_link(
 | |
| 								array(
 | |
| 									'vp_page' => 999999999,
 | |
| 								)
 | |
| 							)
 | |
| 						)
 | |
| 					)
 | |
| 				),
 | |
| 				'format'    => '',
 | |
| 				'type'      => 'array',
 | |
| 				'current'   => $args['start_page'],
 | |
| 				'total'     => $args['max_pages'],
 | |
| 				'prev_text' => '<',
 | |
| 				'next_text' => '>',
 | |
| 				'end_size'  => 1,
 | |
| 				'mid_size'  => 2,
 | |
| 			)
 | |
| 		);
 | |
| 
 | |
| 		// parse html string and make arrays.
 | |
| 		$filtered_links = array();
 | |
| 		if ( $pagination_links ) {
 | |
| 			foreach ( $pagination_links as $link ) {
 | |
| 				$tag_data = self::extract_tags( $link, array( 'a', 'span' ) );
 | |
| 				$tag_data = ! empty( $tag_data ) ? $tag_data[0] : $tag_data;
 | |
| 
 | |
| 				if ( ! empty( $tag_data ) ) {
 | |
| 					$atts  = isset( $tag_data['attributes'] ) ? $tag_data['attributes'] : false;
 | |
| 					$href  = $atts && isset( $atts['href'] ) ? $atts['href'] : false;
 | |
| 					$class = $atts && isset( $atts['class'] ) ? $atts['class'] : '';
 | |
| 					$label = isset( $tag_data['contents'] ) ? $tag_data['contents'] : false;
 | |
| 
 | |
| 					$arr = array(
 | |
| 						'url'           => $href,
 | |
| 						'label'         => $label,
 | |
| 						'class'         => 'vp-pagination__item',
 | |
| 						'active'        => strpos( $class, 'current' ) !== false,
 | |
| 						'is_prev_arrow' => strpos( $class, 'prev' ) !== false,
 | |
| 						'is_next_arrow' => strpos( $class, 'next' ) !== false,
 | |
| 						'is_dots'       => strpos( $class, 'dots' ) !== false,
 | |
| 					);
 | |
| 
 | |
| 					if ( $arr['active'] ) {
 | |
| 						$arr['class'] .= ' vp-pagination__item-active';
 | |
| 					}
 | |
| 					if ( $arr['is_prev_arrow'] ) {
 | |
| 						$arr['class'] .= ' vp-pagination__item-prev';
 | |
| 					}
 | |
| 					if ( $arr['is_next_arrow'] ) {
 | |
| 						$arr['class'] .= ' vp-pagination__item-next';
 | |
| 					}
 | |
| 					if ( $arr['is_dots'] ) {
 | |
| 						$arr['class'] .= ' vp-pagination__item-dots';
 | |
| 					}
 | |
| 
 | |
| 					// skip arrows if disabled.
 | |
| 					if ( ! $vp_options['pagination_paged__show_arrows'] && ( $arr['is_prev_arrow'] || $arr['is_next_arrow'] ) ) {
 | |
| 						continue;
 | |
| 					}
 | |
| 
 | |
| 					// skip numbers if disabled.
 | |
| 					if ( ! $vp_options['pagination_paged__show_numbers'] && ! $arr['is_prev_arrow'] && ! $arr['is_next_arrow'] ) {
 | |
| 						continue;
 | |
| 					}
 | |
| 
 | |
| 					$filtered_links[] = apply_filters( 'vpf_pagination_item_data', $arr, $args, $vp_options );
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $filtered_links;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Return current page url with paged support.
 | |
| 	 *
 | |
| 	 * @param array $query_arg - custom query arg.
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	public static function get_pagenum_link( $query_arg = array() ) {
 | |
| 		// Use current page url.
 | |
| 		global $wp;
 | |
| 		$current_url = '';
 | |
| 
 | |
| 		// We should use REQUEST_URI in case if the user used non-default permalinks.
 | |
| 		// For example, this one:
 | |
| 		// - /index.php/%postname% .
 | |
| 		if ( isset( $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'] ) ) {
 | |
| 			$current_url = esc_url_raw( wp_unslash( ( is_ssl() ? 'https' : 'http' ) . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) );
 | |
| 		} else {
 | |
| 			$current_url = trailingslashit( home_url( $wp->request ) );
 | |
| 
 | |
|             // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 | |
| 			if ( ! empty( $_GET ) ) {
 | |
|                 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 | |
| 				$current_url = add_query_arg( array_map( 'sanitize_text_field', wp_unslash( $_GET ) ), $current_url );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if ( isset( $query_arg['vp_filter'] ) && ! $query_arg['vp_filter'] ) {
 | |
| 			unset( $query_arg['vp_filter'] );
 | |
| 			$current_url = remove_query_arg( 'vp_filter', $current_url );
 | |
| 		}
 | |
| 		if ( isset( $query_arg['vp_sort'] ) && ! $query_arg['vp_sort'] ) {
 | |
| 			unset( $query_arg['vp_sort'] );
 | |
| 			$current_url = remove_query_arg( 'vp_sort', $current_url );
 | |
| 		}
 | |
| 		if ( isset( $query_arg['vp_page'] ) && 1 === $query_arg['vp_page'] ) {
 | |
| 			unset( $query_arg['vp_page'] );
 | |
| 			$current_url = remove_query_arg( 'vp_page', $current_url );
 | |
| 		}
 | |
| 
 | |
| 		// Add custom query args.
 | |
| 		$current_url = add_query_arg( $query_arg, $current_url );
 | |
| 
 | |
| 		$current_url = apply_filters( 'vpf_get_pagenum_link', $current_url, $query_arg );
 | |
| 
 | |
| 		return $current_url;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Create slug from user input string.
 | |
| 	 *
 | |
| 	 * @param string $str - user string.
 | |
| 	 * @param string $delimiter - words delimiter.
 | |
| 	 *
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	public static function create_slug( $str, $delimiter = '_' ) {
 | |
| 		$slug = $str;
 | |
| 
 | |
| 		if ( class_exists( 'Cocur\Slugify\Slugify' ) ) {
 | |
| 			$slugify = new Cocur\Slugify\Slugify();
 | |
| 			$slug    = $slugify->slugify( $str, $delimiter );
 | |
| 
 | |
| 			// Sometimes Slugify can't add slug to string with unicode.
 | |
| 			// We will return the standard string.
 | |
| 			if ( ! $slug ) {
 | |
| 				$slug = $str;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $slug;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get list with all used posts on the current page.
 | |
| 	 *
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public static function get_all_used_posts() {
 | |
| 		// add post IDs from main query.
 | |
| 		if ( self::$check_main_query && ! self::is_preview() ) {
 | |
| 			self::$check_main_query = false;
 | |
| 
 | |
| 			global $wp_query;
 | |
| 
 | |
| 			if ( isset( $wp_query ) && isset( $wp_query->posts ) ) {
 | |
| 				foreach ( $wp_query->posts as $post ) {
 | |
| 					self::$used_posts[] = $post->ID;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return self::$used_posts;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get list with all used portfolios on the current page.
 | |
| 	 *
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public static function get_all_used_layouts() {
 | |
| 		return self::$used_layouts;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Extract specific HTML tags and their attributes from a string.
 | |
| 	 *
 | |
| 	 * Found in http://w-shadow.com/blog/2009/10/20/how-to-extract-html-tags-and-their-attributes-with-php/
 | |
| 	 *
 | |
| 	 * You can either specify one tag, an array of tag names, or a regular expression that matches the tag name(s).
 | |
| 	 * If multiple tags are specified you must also set the $selfclosing parameter and it must be the same for
 | |
| 	 * all specified tags (so you can't extract both normal and self-closing tags in one go).
 | |
| 	 *
 | |
| 	 * The function returns a numerically indexed array of extracted tags. Each entry is an associative array
 | |
| 	 * with these keys :
 | |
| 	 *  tag_name    - the name of the extracted tag, e.g. "a" or "img".
 | |
| 	 *  offset      - the numberic offset of the first character of the tag within the HTML source.
 | |
| 	 *  contents    - the inner HTML of the tag. This is always empty for self-closing tags.
 | |
| 	 *  attributes  - a name -> value array of the tag's attributes, or an empty array if the tag has none.
 | |
| 	 *  full_tag    - the entire matched tag, e.g. '<a href="http://example.com">example.com</a>'. This key
 | |
| 	 *                will only be present if you set $return_the_entire_tag to true.
 | |
| 	 *
 | |
| 	 * @param string       $html The HTML code to search for tags.
 | |
| 	 * @param string|array $tag The tag(s) to extract.
 | |
| 	 * @param bool         $selfclosing Whether the tag is self-closing or not. Setting it to null will force the script to try and make an educated guess.
 | |
| 	 * @param bool         $return_the_entire_tag Return the entire matched tag in 'full_tag' key of the results array.
 | |
| 	 * @param string       $charset The character set of the HTML code. Defaults to ISO-8859-1.
 | |
| 	 *
 | |
| 	 * @return array An array of extracted tags, or an empty array if no matching tags were found.
 | |
| 	 */
 | |
| 	private static function extract_tags( $html, $tag, $selfclosing = null, $return_the_entire_tag = false, $charset = 'ISO-8859-1' ) {
 | |
| 
 | |
| 		if ( is_array( $tag ) ) {
 | |
| 			$tag = implode( '|', $tag );
 | |
| 		}
 | |
| 
 | |
| 		// If the user didn't specify if $tag is a self-closing tag we try to auto-detect it by checking against a list of known self-closing tags.
 | |
| 		$selfclosing_tags = array( 'area', 'base', 'basefont', 'br', 'hr', 'input', 'img', 'link', 'meta', 'col', 'param' );
 | |
| 		if ( is_null( $selfclosing ) ) {
 | |
| 			$selfclosing = in_array( $tag, $selfclosing_tags, true );
 | |
| 		}
 | |
| 
 | |
| 		// The regexp is different for normal and self-closing tags because I can't figure out how to make a sufficiently robust unified one.
 | |
| 		if ( $selfclosing ) {
 | |
| 			$tag_pattern =
 | |
| 				'@<(?P<tag>' . $tag . ')           # <tag
 | |
|                 (?P<attributes>\s[^>]+)?       # attributes, if any
 | |
|                 \s*/?>                   # /> or just >, being lenient here
 | |
|                 @xsi';
 | |
| 		} else {
 | |
| 			$tag_pattern =
 | |
| 				'@<(?P<tag>' . $tag . ')           # <tag
 | |
|                 (?P<attributes>\s[^>]+)?       # attributes, if any
 | |
|                 \s*>                 # >
 | |
|                 (?P<contents>.*?)         # tag contents
 | |
|                 </(?P=tag)>               # the closing </tag>
 | |
|                 @xsi';
 | |
| 		}
 | |
| 
 | |
| 		$attribute_pattern =
 | |
| 			'@
 | |
|             (?P<name>\w+)                         # attribute name
 | |
|             \s*=\s*
 | |
|             (
 | |
|                 (?P<quote>[\"\'])(?P<value_quoted>.*?)(?P=quote)    # a quoted value
 | |
|                 |                           # or
 | |
|                 (?P<value_unquoted>[^\s"\']+?)(?:\s+|$)           # an unquoted value (terminated by whitespace or EOF)
 | |
|             )
 | |
|             @xsi';
 | |
| 
 | |
| 		// Find all tags.
 | |
| 		if ( ! preg_match_all( $tag_pattern, $html, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE ) ) {
 | |
| 			// Return an empty array if we didn't find anything.
 | |
| 			return array();
 | |
| 		}
 | |
| 
 | |
| 		$tags = array();
 | |
| 		foreach ( $matches as $match ) {
 | |
| 
 | |
| 			// Parse tag attributes, if any.
 | |
| 			$attributes = array();
 | |
| 			if ( ! empty( $match['attributes'][0] ) ) {
 | |
| 
 | |
| 				if ( preg_match_all( $attribute_pattern, $match['attributes'][0], $attribute_data, PREG_SET_ORDER ) ) {
 | |
| 					// Turn the attribute data into a name->value array.
 | |
| 					foreach ( $attribute_data as $attr ) {
 | |
| 						if ( ! empty( $attr['value_quoted'] ) ) {
 | |
| 							$value = $attr['value_quoted'];
 | |
| 						} elseif ( ! empty( $attr['value_unquoted'] ) ) {
 | |
| 							$value = $attr['value_unquoted'];
 | |
| 						} else {
 | |
| 							$value = '';
 | |
| 						}
 | |
| 
 | |
| 						// Passing the value through html_entity_decode is handy when you want to extract link URLs or something like that. You might want to remove or modify this call if it doesn't fit your situation.
 | |
| 						$value = html_entity_decode( $value, ENT_QUOTES, $charset );
 | |
| 
 | |
| 						$attributes[ $attr['name'] ] = $value;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			$tag = array(
 | |
| 				'tag_name'   => $match['tag'][0],
 | |
| 				'offset'     => $match[0][1],
 | |
| 				'contents'   => ! empty( $match['contents'] ) ? $match['contents'][0] : '',   // empty for self-closing tags.
 | |
| 				'attributes' => $attributes,
 | |
| 			);
 | |
| 			if ( $return_the_entire_tag ) {
 | |
| 				$tag['full_tag'] = $match[0][0];
 | |
| 			}
 | |
| 
 | |
| 			$tags[] = $tag;
 | |
| 		}
 | |
| 
 | |
| 		return $tags;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| new Visual_Portfolio_Get();
 |