Server IP : 172.67.214.6 / Your IP : 216.73.216.84 Web Server : LiteSpeed System : Linux premium900.web-hosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 User : redwjova ( 1790) PHP Version : 8.1.32 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/redwjova/clevorio.com/wp-content/plugins/smartmag-core/blocks/base/ |
Upload File : |
<?php namespace Bunyad\Blocks\Base; use \Bunyad; use \WP_Query; /** * Base Blocks Class for Loops type blocks - should be extended for other blocks * * Note: This is not an abstract class and may be used standalone. More of template method. */ class LoopBlock extends Block { /** * @var string Block type */ public $type = 'loop'; /** * @var boolean Is the block processed yet */ public $processed = false; /** * @var array Internal data, extended by data retrieved from Query::setup() */ public $data = [ 'unique_id' => null ]; /** * @var \WP_Query */ public $query; /** * @var string ID for the view file. Defaults to $this->id. */ public $view_id; /** * @var integer Number of posts rendered so far. */ protected $rendered_posts = 0; /** * @param array $props */ public function __construct($props = []) { parent::__construct($props); // Resolve aliases $this->props = $this->resolve_aliases($this->props); // Setup enqueues if any add_action('wp_enqueue_scripts', [$this, 'register_assets']); } /** * Get props related to query. * * @return array */ public static function get_query_props() { $query_props = [ 'posts' => 4, 'offset' => '', // Sticky posts generally only enabled for home/blog archives. 'sticky_posts' => false, // Internal for section queries, and passed 'query' objects. // Different from offset: These posts are manually skipped in the loop. 'skip_posts' => 0, // Main category 'cat' => '', // Tag slugs - separated by commas. Not in 'terms' to allow limit by tags AND cats. 'tags' => [], // Categories, or custom taxonomies' term ids, or author ids. 'terms' => [], 'exclude_terms' => [], // Limit to a specific custom taxonomy 'taxonomy' => '', // Custom taxonomy IDs to use 'tax_ids' => [], 'sort_order' => '', 'sort_by' => '', // Only for JetPack views sort. 'sort_days' => 30, 'post_formats' => [], // Multiple supported only for legacy compat. Recommended: Single value. 'post_type' => '', // Specific post IDs 'post_ids' => [], // Skip posts by ids. // Special: Also supported for main query. 'exclude_ids' => [], // Exclude posts by tags, by ids or slugs. 'exclude_tags' => [], 'pagination' => '', // Only show review posts if enabled. 'reviews_only' => false, // A custom identifier for programmatically changing things. 'query_id' => '', // Exclude current post on a single post page. 'exclude_current' => false, // Others that may be used: // 'page' => 0 ]; return $query_props; } /** * Default properties for the block. * * @param string $type Type of props to return. * @return array */ public static function get_default_props() { // Config props (not for sc) $props = [ // Expected: // null|empty: Use global $wp_query or legacy $bunyad_loop, ignores all query props // 'custom': Create create based on provided props. // 'section': Use section query. Data must be provided in section_query. 'query_type' => '', // Forces to use the specified query. 'query' => null, // WP_Query|array Section query data. 'section_query' => [], // Whether is it called via a shortcode / builder - adds .block-sc to wrap 'is_sc_call' => false, // Style Variation for the block. 'style' => '', // Dark or normal. 'scheme' => '', // Many blocks support columns setting. 'columns' => '', 'columns_main' => '', 'columns_medium' => '', 'columns_small' => '', 'heading' => '', 'heading_type' => '', 'heading_align' => 'left', 'heading_link' => '', 'heading_more' => '', 'heading_more_text' => '', 'heading_more_link' => '', 'heading_colors' => '', 'heading_tag' => 'h4', // Values: category, tag, or taxonomy - if empty, defaults to category 'filters' => false, 'filters_terms' => [], 'filters_tags' => [], // When using custom taxonomy 'filters_tax' => '', // Current filter to apply. 'filter' => '', // Loop specific props 'excerpts' => true, 'excerpt_length' => 0, 'excerpt_lines' => '', 'cat_labels' => '', 'cat_labels_pos' => '', 'read_more' => '', 'reviews' => '', 'show_post_formats' => true, 'post_formats_pos' => '', 'show_content' => true, 'content_center' => false, // When empty, automatically inferred based on columns. 'show_media' => true, 'force_image' => '', 'media_ratio' => '', 'media_ratio_custom' => '', // Only for some blocks: list, small. 'media_width' => '', 'media_style_shadow' => '', // Define a parent container to get relative width from. 'container_width' => '', 'separators' => false, 'separators_cols' => false, // Margin below block 'space_below' => '', 'column_gap' => '', 'meta_items_default' => true, 'meta_above' => [], 'meta_below' => [], // Do not change default of empty string value for loop options field to work. 'meta_sponsor_items_default' => true, 'meta_sponsor' => '', 'meta_sponsor_label' => '', 'meta_sponsor_logo' => '', 'meta_sponsor_info' => '', 'meta_sponsor_above' => [], 'meta_sponsor_below' => [], 'show_title' => true, 'title_tag' => 'h2', 'title_lines' => '', // These meta items should not get a default value as global settings are important. 'meta_align' => '', 'meta_cat_style' => 'text', 'meta_author_img' => false, // Pagination settings. 'pagination_type' => 'numbers-ajax', 'load_more_style' => '', 'pagination_links' => true, // Pagination may be enabled, but may not be always rendered, such as for multi-blocks. 'pagination_render' => true, // Number of posts to skip lazyload image for. 'skip_lazy_number' => 0, // Only when query_type is related. 'query_yarpp' => false, // Legacy Aliases (DEPRECATED) 'link' => ['alias' => 'heading_link'], 'post_format' => ['alias' => 'post_formats'], 'title' => ['alias' => 'heading'], 'cats' => ['alias' => 'terms'], ]; $props = $props + static::get_query_props(); return $props; } /** * Add in some values from global options. */ public function map_global_props($props) { $global = [ 'cat_labels' => Bunyad::options()->get('cat_labels'), 'cat_labels_pos' => Bunyad::options()->get('cat_labels_pos'), 'reviews' => Bunyad::options()->get('loops_reviews'), 'post_formats_pos' => Bunyad::options()->get('post_formats_pos'), 'load_more_style' => Bunyad::options()->get('load_more_style'), 'meta_cat_style' => Bunyad::options()->get('post_meta_cat_style'), 'media_style_shadow' => Bunyad::options()->get('loops_media_style_shadow'), 'meta_sponsor' => Bunyad::options()->get('post_meta_sponsor'), 'meta_sponsor_logo' => Bunyad::options()->get('post_meta_sponsor_logo'), 'meta_sponsor_label' => Bunyad::options()->get('post_meta_sponsor_label'), ]; // Only set this default for listings/internal calls. Blocks/SC do not use the global // setting for these. if (empty($props['is_sc_call'])) { $global += [ 'show_post_formats' => Bunyad::options()->get('post_format_icons'), ]; } // Setting it as it will be frequently used by inheritance too. $props['meta_items_default'] = !isset($props['meta_items_default']) || $props['meta_items_default']; // If not known or explicitly set to default, global meta should be used. if ($props['meta_items_default']) { $global += [ 'meta_above' => Bunyad::options()->get('post_meta_above'), 'meta_below' => Bunyad::options()->get('post_meta_below'), ]; } // Setting it as it will be frequently used by inheritance too. $props['meta_sponsor_items_default'] = !isset($props['meta_sponsor_items_default']) || $props['meta_sponsor_items_default']; // If not known or explicitly set to default, global meta should be used. if ($props['meta_sponsor_items_default']) { $global += [ 'meta_sponsor_above' => Bunyad::options()->get('post_meta_sponsor_above'), 'meta_sponsor_below' => Bunyad::options()->get('post_meta_sponsor_below'), ]; } return array_replace($global, $props); } /** * @inheritDoc */ public function init() { // Setup internal props. These are not set from external call. $this->props += [ 'image' => '', 'image_props' => [], 'class_grid' => ['grid'], // Not all support it yet. 'class' => [], // Loop wrapper div attributes. Some loops use it, not all. 'wrap_attrs' => [], ]; } /** * @inheritDoc */ public function setup_props(array $props) { $props = parent::setup_props($props); if (isset($props['columns_main'])) { $props['columns'] = $props['columns_main']; } // Clean up section_query props by only using valid. if (isset($props['section_query'])) { $props['section_query_type'] = isset($props['section_query']['query_type']) ? $props['section_query']['query_type'] : ''; $props['section_query'] = array_intersect_key( $props['section_query'], $this->get_query_props() ); } else if (isset($props['query_type']) && $props['query_type'] === 'section') { // section_query doesn't exist, so remove it. $props['query_type'] = 'custom'; } return $props; } /** * Set the block identifier * * @return $this */ public function set_id($id) { $this->id = $id; return $this; } /** * Set a single property - sets on $this->props[] array * * @return $this */ public function set($key, $value) { $this->props[$key] = $value; return $this; } /** * Get all props. * * @see $this::get_default_props() * @return array */ public function get_props($original = false) { if ($original) { return $this->orig_props; } return $this->props; } /** * Setup aliases for provided props. */ public function resolve_aliases($props) { $default = $this->get_default_props(); foreach ($default as $key => $prop) { // Is not an alias? Skip if (!is_array($prop) OR !array_key_exists('alias', $prop)) { continue; } // Alias used here if (array_key_exists($key, $props)) { // A stray config prop, remove it if (!empty($props[$key]['alias'])) { unset($props[$key]); continue; } // Example: 'terms' set from $props['cats'] $props[ $prop['alias'] ] = $props[$key]; } } return $props; } /** * Any assets to register for this block. */ public function register_assets() {} /** * Process and setup query. * * @uses \Bunyad\Blocks\Base\Query */ public function process() { $this->create_unique_id(); // Currently applying a filter. Query type can only be custom. if ($this->props['filter']) { $this->props = array_replace($this->props, [ 'query' => '', 'query_type' => 'custom' ]); } /** * Determine current page based on main query or provided paged param in AJAX. * Only done if pagination is enabled. */ if ($this->props['pagination']) { $page = (get_query_var('paged') ? get_query_var('paged') : get_query_var('page')); if (empty($page) && isset($_REQUEST['paged'])) { $this->props['page'] = intval($_REQUEST['paged']); } else { $this->props['page'] = intval($page); } } /** * Query type: Global or a custom defined. */ // Section query may be using main query, instead of a custom one. if ($this->props['query_type'] === 'section' && $this->props['section_query_type'] === 'main-custom') { $this->props['query_type'] = 'main-custom'; $this->props = array_replace( $this->props, (array) $this->props['section_query'] ); } // This query type re-executes main query, with support for some extra query props. if ($this->props['query_type'] === 'main-custom') { // Remove unsupported. $this->props = array_diff_key( $this->props, array_flip([ 'cat', 'terms', 'taxonomy', 'tax_ids', 'post_type', 'exclude_terms', 'exclude_ids', 'exclude_current', 'exclude_tags', ]) ); $this->props['query'] = $GLOBALS['wp_query']; } // Global query for 'main' or if no query_type is defined. if ( $this->props['query_type'] === 'main' || (!$this->props['query_type'] && !$this->props['query']) ) { $this->props['query'] = !empty($GLOBALS['bunyad_loop']) ? $GLOBALS['bunyad_loop'] : $GLOBALS['wp_query']; } /** * Use a pre-defined query. May also be the main query (defined above). * * Note: This is different from an undefined 'query', which fallbacks to global (core archives). * * @todo refactor to MainQuery. */ if ($this->props['query'] instanceof WP_Query) { // Clone to prevent modification on original query object. // As it may be re-used again, modifications such as post_count below would // affect the object passed by ref. $this->query = clone $this->props['query']; /** @var WP_Query $query */ $query = $this->query; $new_args = []; /** * For Main custom query, we'll add or replace query_vars based on provided props * and re-execute the current main wp_query. */ if ($this->props['query_type'] === 'main-custom') { $query_process = new Query(array_replace( $this->props, [ // Enable sticky posts unless disabled in original query. 'sticky_posts' => empty($query->query_vars['ignore_sticky_posts']) ? true : false ] )); $new_args = $query_process->setup_query_data(); $this->data = array_replace($this->data, $query_process->get_data()); } /** * Fallback to WordPress main/global query. Mainly for native archives. */ else if ($this->props['query']->is_main_query()) { // Exclude IDs can also be used on main query, ex. skip archive featured posts. if ($this->props['exclude_ids']) { $vars = &$query->query_vars; $new_args['post__not_in'] = array_merge( isset($vars['post__not_in']) ? (array) $vars['post__not_in'] : [], $this->props['exclude_ids'] ); } } /** * Used for a mixed internal block, mega menu etc. Or for blocks that call * another block and use same query, like news focus and highlights. * * - Change post_count to limit to post number specified. */ else { $do_skip_posts = $this->props['skip_posts']; // Adjust posts if number of posts specified. if ($this->props['posts']) { if ($query->post_count > $this->props['posts']) { $query->post_count = $this->props['posts']; } else if ($query->post_count < $this->props['posts']) { $new_args['post_per_page'] = $this->props['posts']; } } } // Pagination fix for all queries. if (!$this->props['pagination'] && $query->get('paged') > 1) { $new_args['paged'] = 1; } /** * Re-execute the global query with added vars, if needed. This is done for: * - 'main-custom': Always. * - Global Main query: If exclude_ids provided. * - Others: If more posts are required than in the original query, pagination fix etc. */ if (count($new_args)) { /** * We use the same filter in Query::setup(), so to remain consistent do the same when the * pre-provided query has to be re-executed. * * Note: We don't always run this filter as that would cause issues with no duplicate posts * feature. */ $new_args = apply_filters( 'bunyad_block_query_args', array_replace($query->query_vars, $new_args), $this ); $query->query($new_args); } // Just sets the internal pointer to skip over some posts. Used internally. if (!empty($do_skip_posts)) { $query->current_post = $this->props['skip_posts'] - 1; } $this->query = $query; // Setup data required to match Query::setup(). if (!$this->data) { $this->data = array_replace($this->data, [ 'heading' => $this->props['heading'], 'term_link' => '', 'term' => '', 'display_filters' => [], ]); } } else { // Skip current post. if ($this->props['exclude_current'] && is_single()) { $this->props['exclude_ids'] = array_merge( isset($this->props['exclude_ids']) ? (array) $this->props['exclude_ids'] : [], [get_the_ID()] ); } // All the other type of queries. switch ($this->props['query_type']) { case 'custom': // Setup the block query $query = new Query($this->props, $this); $this->data = array_merge($this->data, $query->setup()); $this->query = $this->data['query']; unset($this->data['query']); break; case 'section': $query = $this->props['section_query']; if (!is_object($query)) { $query_props = array_replace( $this->props, (array) $this->props['section_query'] ); // Add posts limit. $query_props = array_replace($query_props, [ 'posts' => $this->props['posts'], ]); $query = new Query($query_props); } $this->data = array_merge($this->data, $query->setup()); $this->query = $this->data['query']; break; case 'related': // Related posts query. if (Bunyad::posts()) { $this->query = Bunyad::posts()->get_related( $this->props['posts'], null, [ 'custom' => true, 'yarpp' => $this->props['query_yarpp'], ] ); $this->data = array_merge($this->data, [ 'heading' => $this->props['heading'] ]); } break; } } // Flag to mark processed $this->processed = true; } /** * Render the partial view for this loop. * * @uses \Bunyad_Core::partial() */ public function render($options = array()) { $options = wp_parse_args($options, array( 'block_markup' => true )); /** * Run an action before rendering the loop block. * * @param self $this */ do_action('bunyad_blocks_loop_render', $this); $this->_pre_render(); $this->setup_media_ratio(); $this->infer_image_sizes(); /** * Filter block image before. * * @param string $image Current image. * @param string $id Block id. */ $this->props['image'] = apply_filters('bunyad_blocks_loop_image', $this->props['image'], $this->id); if (!$this->processed) { $this->process(); } // Odd case. if (!$this->query) { return; } // Render with or without block markup ($options['block_markup'] ? $this->render_block_markup() : $this->render_view()); // Restore post data as views will override it. wp_reset_postdata(); /** * Run an action after rendering the loop block. * * @param self $this */ do_action('bunyad_blocks_loop_render_after', $this); } /** * Checks to perform and settings to do prior to render */ public function _pre_render() {} /** * Render the view file for this block - usually a loop */ public function render_view() { $view_id = $this->view_id ? $this->view_id : $this->id; Bunyad::core()->partial( 'blocks/loops/' . $view_id . '/html/' . $view_id, [ 'block' => $this, 'query' => $this->query ] ); } /** * Get block attributes for rendering the block markup. * * @return array */ public function get_block_wrap_attribs() { $classes = [ 'block-wrap', 'block-' . $this->id, $this->props['is_sc_call'] ? 'block-sc' : '', // Preset column gaps. $this->props['column_gap'] ? 'cols-gap-' . $this->props['column_gap'] : '', // Dark scheme class if active. $this->props['scheme'] === 'dark' ? 's-dark' : '', // Margins below class. $this->props['space_below'] ? 'mb-' . $this->props['space_below'] : '', // Media shadows. Note: It's best done in wrapper instead of loop div for style consistency. $this->props['media_style_shadow'] && $this->props['media_style_shadow'] !== 'none' ? 'has-media-shadows' : '', ]; $attribs = [ 'class' => $classes, 'data-id' => $this->data['unique_id'] ]; /** * Add block query data for dynamic pagination or blocks with filters. */ $ajax_pagination = in_array( $this->props['pagination_type'], ['load-more', 'numbers-ajax', 'infinite'] ); if ($this->props['filters'] || ($this->props['pagination'] && $ajax_pagination)) { $block_data = [ 'id' => $this->id, 'props' => $this->orig_props, ]; unset($block_data['props']['section_query_type']); // Archive, or other main query will require extra data from the original query // for AJAX pagination. if (!in_array($this->props['query_type'], ['custom', 'section'])) { $term_data = $this->query->get_queried_object(); if (is_object($term_data)) { $block_data['props'] += [ 'post_type' => $this->query->get('post_type'), 'posts' => $this->query->get('posts_per_page'), 'sticky_posts' => !$this->query->get('ignore_sticky_posts'), 'taxonomy' => !empty($term_data->taxonomy) ? $term_data->taxonomy : '', 'terms' => !empty($term_data->term_id) ? $term_data->term_id : '', ]; } } $attribs['data-block'] = json_encode($block_data); } return $attribs; } /** * Output common block markup and render the specific view file. * * Note: Do not call directly, use render() instead. * * @uses self::render_view() */ public function render_block_markup() { ?> <section <?php Bunyad::markup()->attribs( $this->id .'-block', $this->get_block_wrap_attribs() ); ?>> <?php $this->the_heading(); ?> <div class="block-content"> <?php $this->render_view(); ?> </div> </section> <?php } /** * Created unique ID for the block */ protected function create_unique_id() { Bunyad::registry()->block_count++; $this->data['unique_id'] = Bunyad::registry()->block_count; } /** * Output block heading markup */ public function the_heading() { // Custom heading HTML set progrmmatically (not via prop), use this instead. if (!empty($this->data['heading_custom'])) { echo $this->data['heading_custom']; // phpcs:ignore WordPress.Security.EscapeOutput Only settable internally programmatically. return; } // This check is also performed in Heading block. Done here to save resources. if (empty($this->data['heading']) || $this->props['heading_type'] === 'none') { return; } Bunyad::blocks()->load( 'Heading', [ 'heading' => $this->data['heading'], 'align' => $this->props['heading_align'], 'type' => $this->props['heading_type'], 'link' => $this->props['heading_link'] ? $this->props['heading_link'] : $this->data['term_link'], 'term' => $this->data['term'], 'filters' => $this->data['display_filters'], 'more' => $this->props['heading_more'], 'more_text' => $this->props['heading_more_text'], 'more_link' => $this->props['heading_more_link'] ? $this->props['heading_more_link'] : $this->data['term_link'], 'accent_colors' => $this->props['heading_colors'], 'html_tag' => $this->props['heading_tag'], ] ) ->render(); } /** * Load a loop post class * * Example: Load the loop class from inc/loop-posts/grid.php if it exists * or fallback to inc/loop-posts/base.php * * @uses \Bunyad\Blocks\Helpers::load_post() * * @param string $id Loop post id * @param array $props Props to set for loop post * * @return \Bunyad\Blocks\LoopPosts\BasePost */ public function loop_post($id, $props = array()) { $this->rendered_posts++; $props = array_replace($this->get_props(), $props); $props['loop_number'] = $this->rendered_posts; // Load post $post = Bunyad::blocks()->load_post($id, $props); $post->block = $this; return $post; } /** * Get relative width for current block, based on parent column width in * relation to the whole page. * * @return float Column width in percent number, example 66 */ public function get_relative_width() { // Container defined in props, force it if (!empty($this->props['container_width'])) { return floatval($this->props['container_width']); } return Bunyad::blocks()->get_relative_width(); } /** * Set columns size based on provided columns. * * @param array $args Configs for columns of devices. * @return void */ protected function setup_columns($args = []) { if (empty($this->props['columns'])) { $this->props['columns'] = 1; } // Add the grid columns class. $this->props['class_grid'][] = 'grid-' . $this->props['columns']; $col_types = [ 'md' => 'medium', 'sm' => 'small', 'xs' => 'xsmall' ]; /** * Add responsive column classes based on props of type columns_medium etc. * OR, override via args provided. */ foreach ($col_types as $key => $columns) { $cols = null; if (!empty($this->props[ 'columns_' . $columns ])) { $cols = $this->props[ 'columns_' . $columns ]; } else if (!empty($args[ $columns ])) { $cols = $args[ $columns ]; } if ($cols) { array_push( $this->props['class_grid'], "{$key}:grid-{$cols}" ); } } // 1/3 * 12 to get col-4 $column = (1 / absint($this->props['columns']) * 12); $this->props['col_class'] = 'col-' . str_replace('.', '-', $column); } /** * Setup media ratio if provided. */ public function setup_media_ratio() { if (empty($this->props['media_ratio'])) { return; } $ratio = $this->props['media_ratio']; if ($ratio === 'custom' && !empty($this->props['media_ratio_custom'])) { $ratio = $this->props['media_ratio_custom']; } $this->props['media_ratio'] = $ratio; } /** * Decide the image to use for this block */ public function infer_image_sizes() { // If an image is forced, don't bother setting it if (!empty($this->props['image'])) { return; } $this->props['image'] = 'bunyad-thumb'; } /** * Render pagination for current block * * @uses \Bunyad_Core::partial() * @param array $props */ public function the_pagination($props = []) { if (!$this->props['pagination'] || !$this->props['pagination_render']) { return; } $props = array_merge( [ 'query' => $this->query, 'page' => $this->props['page'], 'pagination' => $this->props['pagination'], 'pagination_type' => $this->props['pagination_type'], 'load_more_style' => $this->props['load_more_style'], 'pagination_links' => $this->props['pagination_links'] ], $props ); // AJAX pagination is not possible in author and search archives, yet. if (is_author() || is_search()) { $props['pagination_type'] = 'numbers'; } Bunyad::core()->partial('partials/pagination', $props); } }