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();
|