PluginProbe ʕ •ᴥ•ʔ
Spider Elements – Premium Elementor Widgets & Addons Library / trunk
Spider Elements – Premium Elementor Widgets & Addons Library vtrunk
trunk 1.0.0 1.1.0 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.7.0 1.8.0 1.9.0
spider-elements / includes / functions.php
spider-elements / includes Last commit date
Admin 1 month ago Frontend 4 months ago freemius 4 months ago filters.php 1 month ago functions.php 1 month ago
functions.php
775 lines
1 <?php
2 // Exit if accessed directly
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 /**
8 * Check if the pro-plugin and plan is active.
9 *
10 * @return bool True if premium code can be used, false otherwise.
11 */
12 function spel_is_premium(): bool {
13 return spel_fs()->is_plan( 'pro' ) && spel_fs()->can_use_premium_code();
14 }
15
16 /**
17 * Check if the Docy theme is active
18 *
19 * @return bool
20 */
21 function spel_unlock_docy_theme(): bool {
22 $theme = wp_get_theme();
23 $theme_name = $theme->get( 'Name' );
24 $docy_themes = [ 'Docy', 'docy', 'Docy Child', 'docy-child' ];
25
26 return in_array( $theme_name, $docy_themes, true ) || spel_is_premium();
27 }
28
29 function spel_rtl(): string {
30 return is_rtl() ? 'true' : 'false';
31 }
32
33 /**
34 * Elementor is edit mode
35 *
36 * @return bool
37 */
38 function spider_elements_is_edit(): bool {
39 return \Elementor\Plugin::$instance->editor->is_edit_mode();
40 }
41
42 /**
43 * Elementor is preview mode
44 *
45 * @return bool
46 */
47 function spider_elements_is_preview(): bool {
48 return \Elementor\Plugin::$instance->preview->is_preview_mode();
49 }
50
51 /**
52 * Elementor Title tags
53 *
54 * @return array
55 */
56 if ( ! function_exists( 'spel_get_title_tags' ) ) {
57 function spel_get_title_tags(): array {
58 return [
59 'h1' => esc_html__( 'H1', 'spider-elements' ),
60 'h2' => esc_html__( 'H2', 'spider-elements' ),
61 'h3' => esc_html__( 'H3', 'spider-elements' ),
62 'h4' => esc_html__( 'H4', 'spider-elements' ),
63 'h5' => esc_html__( 'H5', 'spider-elements' ),
64 'h6' => esc_html__( 'H6', 'spider-elements' ),
65 'div' => esc_html__( 'Div', 'spider-elements' ),
66 'span' => esc_html__( 'Span', 'spider-elements' ),
67 'p' => esc_html__( 'Paragraph', 'spider-elements' ),
68 ];
69 }
70 }
71
72 /**
73 * Echo button link attributes.
74 *
75 * @param array $settings_key Settings array.
76 * @param bool $is_echo Whether to echo the attributes.
77 * @return void
78 */
79 if ( ! function_exists( 'spel_button_link' ) ) {
80 function spel_button_link( $settings_key, $is_echo = true ): void {
81 if ( $is_echo ) {
82 echo ! empty( $settings_key['url'] ) ? 'href="' . esc_url( $settings_key['url'] ) . '"' : '';
83 echo $settings_key['is_external'] ? ' target="_blank"' : '';
84 echo $settings_key['nofollow'] ? ' rel="nofollow"' : '';
85
86 if ( ! empty( $settings_key['custom_attributes'] ) ) {
87 $attrs = explode( ',', $settings_key['custom_attributes'] );
88
89 if ( is_array( $attrs ) ) {
90 foreach ( $attrs as $data ) {
91 $data_attrs = explode( '|', $data, 2 );
92 $attr_name = trim( $data_attrs[0] );
93 $attr_value = isset( $data_attrs[1] ) ? trim( $data_attrs[1] ) : '';
94
95 // Security: Sanitize attribute name (allow alphanumeric, dashes, colons)
96 $attr_name = preg_replace( '/[^a-zA-Z0-9_\-:]/', '', $attr_name );
97
98 // Security: Prevent XSS — block event handlers (on*), style injection,
99 // and attributes that can execute scripts or hijack navigation/tracking.
100 if ( preg_match( '/^(on|href|src|formaction|style|ping)/i', $attr_name ) ) {
101 continue;
102 }
103
104 if ( ! empty( $attr_name ) ) {
105 echo ' ' . esc_attr( $attr_name ) . '="' . esc_attr( $attr_value ) . '"';
106 }
107 }
108 }
109 }
110 }
111 }
112 }
113
114 /**
115 * Category IDs
116 *
117 * @return array
118 * @since 1.0.0
119 */
120 if ( ! function_exists( 'spel_cat_ids' ) ) {
121 function spel_cat_ids() {
122 $taxonomys = get_terms( [
123 'taxonomy' => 'category',
124 'hide_empty' => true,
125 ] );
126 $taxonomy = [];
127 if ( is_array( $taxonomys ) ) {
128 foreach ( $taxonomys as $cat_id ) {
129 $taxonomy[ $cat_id->term_id ] = $cat_id->name;
130 }
131 }
132
133 return $taxonomy;
134 }
135 }
136
137 /**
138 * Day link to archive page
139 *
140 * @return void
141 **/
142 if ( ! function_exists( 'spel_day_link' ) ) {
143 function spel_day_link(): void {
144 $archive_year = get_the_time( 'Y' );
145 $archive_month = get_the_time( 'm' );
146 $archive_day = get_the_time( 'd' );
147 echo esc_url( get_day_link( $archive_year, $archive_month, $archive_day ) );
148 }
149 }
150
151 /**
152 * Retrieve the trimmed post title based on settings.
153 *
154 * This function fetches the current post title, applies a custom length
155 * from the provided settings (or falls back to a default length), and
156 * returns the trimmed version of the title. If no title exists, it will
157 * safely return an empty string.
158 *
159 * @param array $settings Settings array that may contain the title length.
160 * @param string $settings_key Array key used to find the title length inside settings.
161 * @param int $default Default title length if no value is found in settings. Default 10.
162 *
163 * @return string The trimmed post title, or empty string if no title exists.
164 * @since 1.0.0
165 */
166 function spel_get_title_length( array $settings, string $settings_key, int $default = 10 ): string {
167 $title = get_the_title();
168 $title_length = ! empty( $settings[ $settings_key ] ) ? (int) $settings[ $settings_key ] : $default;
169
170 return $title ? wp_trim_words( $title, $title_length, '' ) : '';
171 }
172
173
174 /**
175 * Post's excerpt text
176 *
177 * @param array $settings
178 * @param string $settings_key
179 * @param int $default
180 *
181 * @return string
182 * @since 1.0.0
183 */
184 if ( ! function_exists( 'spel_get_excerpt_length' ) ) {
185 function spel_get_excerpt_length( $settings, $settings_key, $default = 10 ): string {
186 $excerpt_length = ! empty( $settings[ $settings_key ] ) ? $settings[ $settings_key ] : $default;
187
188 return get_the_excerpt() ? wp_trim_words( get_the_excerpt(), $excerpt_length, '...' ) : wp_trim_words( get_the_content(), $excerpt_length, '...' );
189 }
190 }
191
192
193 /**
194 * Get the first category name
195 *
196 * @param string $term
197 *
198 * @return string
199 * @since 1.0.0
200 */
201 if ( ! function_exists( 'spel_get_first_taxonomy' ) ) {
202 function spel_get_first_taxonomy( $term = 'category' ): string {
203 $cats = get_the_terms( get_the_ID(), $term );
204 $cat = is_array( $cats ) ? $cats[0]->name : '';
205
206 return esc_html( $cat );
207 }
208 }
209
210
211 /**
212 * Get the first category link
213 *
214 * @param string $term
215 *
216 * @return string
217 * @since 1.0.0
218 */
219 if ( ! function_exists( 'spel_get_first_taxonomy_link' ) ) {
220 function spel_get_first_taxonomy_link( $term = 'category' ): string {
221
222 $cats = get_the_terms( get_the_ID(), $term );
223 $cat = is_array( $cats ) ? get_category_link( $cats[0]->term_id ) : '';
224
225 return esc_url( $cat );
226 }
227 }
228
229
230 /**
231 * Get categories array
232 *
233 * @param string $term
234 *
235 * @return array
236 * @since 1.0.0
237 */
238 if ( ! function_exists( 'spel_get_categories' ) ) {
239 function spel_get_categories( $term = 'category' ) {
240
241 $cats = get_terms( [
242 'taxonomy' => $term,
243 'hide_empty' => true
244 ] );
245
246 $cat_array = [];
247 $cat_array['all'] = esc_html__( 'All', 'spider-elements' );
248
249 if ( is_array( $cats ) ) {
250 foreach ( $cats as $cat ) {
251 $cat_array[ $cat->term_id ] = $cat->name;
252 }
253 }
254
255 return $cat_array;
256 }
257 }
258
259
260 /**
261 * Get a category list
262 *
263 * @return void
264 * @since 1.0.0
265 */
266 if ( ! function_exists( 'spel_get_post_category_list' ) ) {
267 function spel_get_post_category_list(): void {
268 $categories = get_categories();
269
270 if ( ! empty( $categories ) ) {
271 echo '<span class="blog-category">';
272
273 $category_names = [];
274
275 if ( is_array( $categories ) ) {
276 foreach ( $categories as $category ) {
277 $category_link = get_category_link( $category->term_id );
278 $category_names[] = '<a href="' . esc_url( $category_link ) . '">' . esc_html( $category->name ) . '</a>';
279 }
280 }
281
282 echo esc_html( implode( ', ', $category_names ) );
283
284 echo '</span>';
285 } else {
286 echo esc_html__( 'No categories found.', 'spider-elements' );
287 }
288 }
289 }
290
291
292 /**
293 * Get an author name array
294 *
295 * @return void
296 * @since 1.0.0
297 */
298 if ( ! function_exists( 'spel_get_post_author_name' ) ) {
299 function spel_get_post_author_name(): void {
300 global $post;
301 $byline = sprintf(
302 /* translators: %s: post author. */
303 esc_html_x( 'By: %s', 'post author', 'spider-elements' ),
304 '<span class="author"><a class="url fn n" href="' . esc_url( get_author_posts_url( $post->post_author ) ) . '">' . esc_html( get_the_author_meta(
305 'display_name',
306 $post->post_author
307 ) ) . '</a></span>'
308 );
309
310 echo wp_kses_post( $byline ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
311 }
312 }
313
314 /**
315 * Get Default Image Elementor
316 *
317 * @param array $settings_key
318 * @param string $alt
319 * @param string $class
320 * @param array $atts
321 * @return void
322 * @since 1.0.0
323 */
324 if ( ! function_exists( 'spel_el_image' ) ) {
325 function spel_el_image( $settings_key = [], $alt = '', $class = '', $atts = [] ): void {
326 if ( ! empty( $settings_key['id'] ) ) {
327 // WordPress handles escaping internally here
328 echo wp_get_attachment_image( $settings_key['id'], 'full', false, [ 'class' => esc_attr( $class ) ] );
329 } elseif ( ! empty( $settings_key['url'] ) && empty( $settings_key['id'] ) ) {
330 $class_attr = ! empty( $class ) ? ' class="' . esc_attr( $class ) . '"' : '';
331 $atts_str = '';
332
333 if ( ! empty( $atts ) ) {
334 foreach ( $atts as $k => $att ) {
335 // Security: Sanitize attribute name (allow alphanumeric, dashes, colons)
336 $k = preg_replace( '/[^a-zA-Z0-9_\-:]/', '', $k );
337
338 // Security: Prevent XSS by blocking event handlers (on*) and critical attributes
339 if ( empty( $k ) || preg_match( '/^(on|style|formaction|src|href)/i', $k ) ) {
340 continue;
341 }
342
343 $atts_str .= ' ' . esc_attr( $k ) . '="' . esc_attr( $att ) . '"';
344 }
345 }
346
347 printf(
348 '<img src="%1$s"%2$s alt="%3$s"%4$s />',
349 esc_url( $settings_key['url'] ),
350 // $class_attr contains safe HTML attribute string
351 $class_attr, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
352 esc_attr( $alt ),
353 // $atts_str contains safe HTML attribute string
354 $atts_str // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
355 );
356 }
357 }
358 }
359
360
361 /**
362 * Get Default Image Elementor Caption
363 *
364 * @param $image_id
365 *
366 * @return array
367 */
368 if ( ! function_exists( 'spel_el_image_caption' ) ) {
369 function spel_el_image_caption( $image_id = '' ): array {
370 $img_attachment = get_post( $image_id );
371
372 return [
373 'alt' => get_post_meta( $img_attachment->ID, '_wp_attachment_image_alt', true ),
374 'caption' => $img_attachment->post_excerpt,
375 'href' => get_permalink( $img_attachment->ID ),
376 'src' => $img_attachment->guid,
377 'title' => $img_attachment->post_title
378 ];
379 }
380 }
381
382
383 /**
384 * Filter text content to allow only safe HTML.
385 *
386 * @param string $content Text content to filter.
387 *
388 * @return string Filtered content containing only the allowed HTML.
389 * @since 1.0.0
390 */
391 if ( ! function_exists( 'spel_kses_post' ) ) {
392 function spel_kses_post( $content ): string {
393 $allowed_tag = [
394 'strong' => [],
395 'br' => [],
396 'p' => [
397 'class' => [],
398 'style' => [],
399 ],
400 'i' => [
401 'class' => [],
402 'style' => [],
403 ],
404 'ul' => [
405 'class' => [],
406 'style' => [],
407 ],
408 'li' => [
409 'class' => [],
410 'style' => [],
411 ],
412 'span' => [
413 'class' => [],
414 'style' => [],
415 ],
416 'a' => [
417 'href' => [],
418 'class' => [],
419 'title' => []
420 ],
421 'div' => [
422 'class' => [],
423 'style' => [],
424 ],
425 'h1' => [
426 'class' => [],
427 'style' => []
428 ],
429 'h2' => [
430 'class' => [],
431 'style' => []
432 ],
433 'h3' => [
434 'class' => [],
435 'style' => []
436 ],
437 'h4' => [
438 'class' => [],
439 'style' => []
440 ],
441 'h5' => [
442 'class' => [],
443 'style' => []
444 ],
445 'h6' => [
446 'class' => [],
447 'style' => []
448 ],
449 'img' => [
450 'class' => [],
451 'style' => [],
452 'height' => [],
453 'width' => [],
454 'src' => [],
455 'srcset' => [],
456 'alt' => [],
457 ],
458
459 ];
460
461 return wp_kses( $content, $allowed_tag );
462 }
463 }
464
465
466 /**
467 * Tab data
468 *
469 * @param array $getCats
470 * @param array $schedule_cats
471 *
472 * @return array
473 * @since 1.0.0
474 */
475 if ( ! function_exists( 'spel_get_tab_data' ) ) {
476 function spel_get_tab_data( $getCats, $schedule_cats ): array {
477 $tab_data = [];
478
479 foreach ( $getCats as $val ) {
480 $matching_data = [];
481
482 foreach ( $schedule_cats as $data ) {
483 if ( $val === $data['tab_title'] ) {
484 $matching_data[] = $data;
485 }
486 }
487
488 $tab_data[ $val ] = $matching_data;
489 }
490
491 return $tab_data;
492 }
493 }
494
495
496 /**
497 * Get reading time
498 *
499 * @param int $words_per_minute
500 *
501 * @return string
502 * @since 1.0.0
503 */
504 if ( ! function_exists( 'spel_get_reading_time' ) ) {
505 function spel_get_reading_time( $words_per_minute = 200 ): string {
506 $content = get_post_field( 'post_content', get_the_ID() );
507 $word_count = str_word_count( wp_strip_all_tags( $content ) );
508 $reading_time = ceil( $word_count / $words_per_minute );
509 $timer = _n( 'minute', 'minutes', $reading_time, 'spider-elements' );
510
511 return sprintf( '%d %s', $reading_time, $timer );
512 }
513 }
514
515 /**
516 * Render Dynamic Image
517 * @param $key
518 * @param $size
519 * @param $atts
520 * @return void
521 * @since 1.0.0
522 */
523 if ( ! function_exists( 'spel_dynamic_image' ) ) {
524 function spel_dynamic_image( $key, $size = 'full', $atts = [] ): void {
525 $image = wp_get_attachment_image( $key['id'], $size, '', $atts );
526 echo wp_kses( $image, [
527 'img' => [
528 'class' => [],
529 'style' => [],
530 'height' => [],
531 'width' => [],
532 'src' => [],
533 'srcset' => [],
534 'alt' => [],
535 ],
536 ] );
537 }
538 }
539
540
541 /**
542 * Retrieve a list of posts based on specified parameters.
543 *
544 * @param string $post_type The post-type to query.
545 * @param int $limit The maximum number of posts to retrieve.
546 * @param string $search The search term for post-titles.
547 *
548 * @return array An associative array with post-IDs as keys and post-titles as values.
549 * @since 1.0.0
550 */
551 if ( ! function_exists( 'spel_get_query_post_list' ) ) {
552 function spel_get_query_post_list( $post_type = 'any', $limit = -1, $search = '' ): array {
553 $args = [
554 'post_type' => $post_type,
555 'post_status' => 'publish',
556 'posts_per_page' => $limit,
557 's' => $search, // Search term
558 ];
559
560 $query = new WP_Query( $args );
561
562 $data = [];
563 if ( $query->have_posts() ) {
564 while ( $query->have_posts() ) {
565 $query->the_post();
566 $data[ get_the_ID() ] = get_the_title();
567 }
568 }
569
570 wp_reset_postdata(); // Reset post data after custom query
571
572 return $data;
573 }
574 }
575
576
577 /**
578 * Get all elementor page templates
579 *
580 * @param string|null $type
581 *
582 * @return array
583 * @since 1.0.0
584 */
585 if ( ! function_exists( 'spel_get_el_templates' ) ) {
586 function spel_get_el_templates( $type = null ): array {
587 $options = [];
588
589 if ( $type ) {
590
591 $args = [
592 'post_type' => 'elementor_library',
593 'posts_per_page' => -1,
594 ];
595
596 $args['tax_query'] = [
597 [
598 'taxonomy' => 'elementor_library_type',
599 'field' => 'slug',
600 'terms' => $type,
601 ],
602 ];
603
604 $page_templates = get_posts( $args );
605
606 if ( ! empty( $page_templates ) && ! is_wp_error( $page_templates ) ) {
607 foreach ( $page_templates as $post ) {
608 $options[ $post->ID ] = $post->post_title;
609 }
610 }
611 } else {
612 $options = spel_get_query_post_list( 'elementor_library' );
613 }
614
615 return $options;
616 }
617 }
618
619
620 /**
621 * Get information about the server environment.
622 *
623 * @return array Server environment information.
624 * @since 1.0.0
625 */
626 if ( ! function_exists( 'spel_get_environment_info' ) ) {
627 function spel_get_environment_info(): array {
628
629 // Figure out cURL version, if installed.
630 $curl_version = '';
631 if ( function_exists( 'curl_version' ) ) {
632 $curl_version = curl_version();
633 $curl_version = $curl_version['version'] . ', ' . $curl_version['ssl_version'];
634 }
635
636 // WP memory limit.
637 $wp_memory_limit = spel_readable_number( WP_MEMORY_LIMIT );
638 if ( function_exists( 'memory_get_usage' ) ) {
639 $wp_memory_limit = max( $wp_memory_limit, spel_readable_number( @ini_get( 'memory_limit' ) ) );
640 }
641
642 return [
643 'home_url' => get_option( 'home' ),
644 'site_url' => get_option( 'siteurl' ),
645 'version' => SPEL_VERSION,
646 'wp_version' => get_bloginfo( 'version' ),
647 'wp_multisite' => is_multisite(),
648 'wp_memory_limit' => $wp_memory_limit,
649 'wp_debug_mode' => ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
650 'wp_cron' => ! ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ),
651 'language' => get_locale(),
652 'external_object_cache' => wp_using_ext_object_cache(),
653 'server_info' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) : '',
654 'php_version' => phpversion(),
655 'php_post_max_size' => spel_readable_number( ini_get( 'post_max_size' ) ),
656 'php_max_execution_time' => ini_get( 'max_execution_time' ),
657 'php_max_input_vars' => ini_get( 'max_input_vars' ),
658 'curl_version' => $curl_version,
659 'suhosin_installed' => extension_loaded( 'suhosin' ),
660 'max_upload_size' => wp_max_upload_size(),
661 'default_timezone' => date_default_timezone_get(),
662 'fsockopen_or_curl_enabled' => ( function_exists( 'fsockopen' ) || function_exists( 'curl_init' ) ),
663 'soapclient_enabled' => class_exists( 'SoapClient' ),
664 'domdocument_enabled' => class_exists( 'DOMDocument' ),
665 'gzip_enabled' => is_callable( 'gzopen' ),
666 'mbstring_enabled' => extension_loaded( 'mbstring' ),
667 ];
668
669 }
670 }
671
672
673 /**
674 * Convert a human-readable file size into bytes.
675 *
676 * @param string $size The size string (e.g., "1M", "2G", "500 K").
677 * @return int The equivalent size in bytes.
678 */
679 if ( ! function_exists( 'spel_readable_number' ) ) {
680 function spel_readable_number( $size ): int {
681
682 // Get the last character of the size string
683 $suffix = substr( $size, -1 );
684
685 // Remove the last character from the size string
686 $value = substr( $size, 0, -1 );
687
688 // Convert suffix to lowercase for case-insensitive comparison
689 $suffix = strtolower( $suffix );
690
691 $multipliers = [
692 'p' => 1024,
693 't' => 1024,
694 'g' => 1024,
695 'm' => 1024,
696 'k' => 1024,
697 ];
698
699 // Check if the suffix is a valid multiplier
700 if ( array_key_exists( $suffix, $multipliers ) ) {
701 $value *= $multipliers[ $suffix ];
702 }
703
704 // Return the result
705 return (int) $value;
706
707 }
708 }
709
710
711 if ( ! function_exists( 'spel_pagination' ) ) {
712 /**
713 * Pagination
714 *
715 * @param WP_Query $query
716 * @param string $class
717 * @param string $prev
718 * @param string $next
719 * @return void
720 */
721 function spel_pagination( $query, $class = 'spel-pagination', $prev = '', $next = '' ): void {
722
723 if ( $query->max_num_pages <= 1 ) {
724 return; // No pagination needed if only one page
725 }
726
727 $default_prev = '<img src="' . esc_url( SPEL_IMG . '/icons/prev.svg' ) . '" alt="' . esc_attr__( 'arrow-left', 'spider-elements' ) . '" class="me-2" />' . esc_html__( 'Prev', 'spider-elements' );
728 $default_next = esc_html__( 'Next', 'spider-elements' ) . '<img src="' . esc_url( SPEL_IMG . '/icons/next.svg' ) . '" alt="' . esc_attr__( 'arrow-right', 'spider-elements' ) . '" class="ms-2" />';
729
730 $prev_text = ! empty( $prev ) ? $prev : $default_prev;
731 $next_text = ! empty( $next ) ? $next : $default_next;
732
733 echo '<ul class="' . esc_attr( $class ) . '">';
734
735 $big = 999999999; // need an unlikely integer
736 $current = max( 1, get_query_var( 'paged' ) ? get_query_var( 'paged' ) : ( get_query_var( 'page' ) ? get_query_var( 'page' ) : 1 ) );
737
738 echo wp_kses_post(
739 paginate_links( [
740 'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
741 'format' => '?paged=%#%',
742 'current' => $current,
743 'total' => $query->max_num_pages,
744 'prev_text' => $prev_text,
745 'next_text' => $next_text,
746 ] )
747 );
748
749
750 echo '</ul>';
751 }
752 }
753
754 /**
755 * Jobus pagination (Deprecated)
756 *
757 * @param WP_Query $query
758 * @return void
759 * @since 1.0.0
760 * @deprecated 1.8.0 No longer needed as we rely on native query handling.
761 */
762 if ( ! function_exists( 'spel_archive_query' ) ) {
763 /**
764 * Archive Query
765 *
766 * @param WP_Query $query
767 * @return void
768 */
769 function spel_archive_query( $query ): void {
770 // Optimization: Removed unbounded query override to prevent performance issues on archive pages
771 }
772
773 add_action( 'pre_get_posts', 'spel_archive_query' );
774 }
775