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(); ?>
include_template( 'items-list/wrapper-start', array( 'options' => $options, 'style_options' => $style_options, 'data_attrs' => $data_attrs, 'class' => $class, ) ); if ( ! empty( $errors ) ) { ?>
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, ) ); } } ?>
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; } ?>
$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(); ?>
$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(); ?>
$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, ); ?>
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' ); ?>
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, ); ?>
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' ); ?>
' ) ) { $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 ); ?> < $val ) { echo esc_attr( $name ) . '="' . esc_attr( $val ) . '" '; } ?> >
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 ); ?>
> 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'; } ?>
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; } ?>
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. 'example.com'. 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 . ') # \s[^>]+)? # attributes, if any \s*/?> # /> or just >, being lenient here @xsi'; } else { $tag_pattern = '@<(?P' . $tag . ') # \s[^>]+)? # attributes, if any \s*> # > (?P.*?) # tag contents # the closing @xsi'; } $attribute_pattern = '@ (?P\w+) # attribute name \s*=\s* ( (?P[\"\'])(?P.*?)(?P=quote) # a quoted value | # or (?P[^\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();