• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

equalizedigital / accessibility-checker / 18726287399

22 Oct 2025 06:37PM UTC coverage: 57.551%. First build
18726287399

Pull #1263

github

web-flow
Merge b62f6f00c into 0b5b9ed0a
Pull Request #1263: Release v1.34.0

3 of 236 new or added lines in 6 files covered. (1.27%)

4154 of 7218 relevant lines covered (57.55%)

3.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.44
/includes/helper-functions.php
1
<?php
2
/**
3
 * Accessibility Checker plugin file.
4
 *
5
 * @package Accessibility_Checker
6
 */
7

8
use EDAC\Admin\Settings;
9

10
/**
11
 * Compare strings
12
 *
13
 * @param string $string1 String to compare.
14
 * @param string $string2 String to compare.
15
 * @return boolean
16
 */
17
function edac_compare_strings( $string1, $string2 ) {
18
        /**
19
         * Prepare strings for our comparison.
20
         *
21
         * @param string $content String to prepare.
22
         * @return string
23
         */
24
        $prepare_strings = function ( $content ) {
12✔
25
                // Text to remove.
26
                $remove_text = [
12✔
27
                        __( 'permalink of ', 'accessibility-checker' ),
12✔
28
                        __( 'permalink to ', 'accessibility-checker' ),
12✔
29
                        __( '&nbsp;', 'accessibility-checker' ),
12✔
30
                ];
12✔
31

32
                $content = strtolower( $content );
12✔
33
                $content = str_ireplace( $remove_text, '', $content );
12✔
34
                $content = wp_strip_all_tags( $content );
12✔
35
                $content = trim( $content, " \t\n\r\0\x0B\xC2\xA0" );
12✔
36

37
                return html_entity_decode( $content, ENT_QUOTES | ENT_HTML5 );
12✔
38
        };
12✔
39

40
        return $prepare_strings( $string1 ) === $prepare_strings( $string2 );
12✔
41
}
42

43
/**
44
 * Check if plugin is installed by getting all plugins from the plugins dir
45
 *
46
 * @param string $plugin_slug Slug of the plugin.
47
 * @return bool
48
 */
49
function edac_check_plugin_installed( $plugin_slug ) {
50
        $installed_plugins = get_plugins();
12✔
51

52
        return array_key_exists( $plugin_slug, $installed_plugins ) || in_array( $plugin_slug, $installed_plugins, true );
12✔
53
}
54

55
/**
56
 * Convert cardinal number into ordinal number
57
 *
58
 * @param int|string $number Number to make ordinal.
59
 * @return string
60
 */
61
function edac_ordinal( $number ) {
62

63
        $number = (int) $number;
34✔
64

65
        if ( class_exists( 'NumberFormatter' ) ) {
34✔
66
                return (
34✔
67
                        new NumberFormatter(
34✔
68
                                get_locale(),
34✔
69
                                NumberFormatter::ORDINAL
34✔
70
                        )
34✔
71
                )->format( $number );
34✔
72

73
        } else {
74
                if ( $number % 100 >= 11 && $number % 100 <= 13 ) {
×
75
                        $ordinal = $number . 'th';
×
76
                } else {
77
                        switch ( $number % 10 ) {
×
78
                                case 1:
×
79
                                        $ordinal = $number . 'st';
×
80
                                        break;
×
81
                                case 2:
×
82
                                        $ordinal = $number . 'nd';
×
83
                                        break;
×
84
                                case 3:
×
85
                                        $ordinal = $number . 'rd';
×
86
                                        break;
×
87
                                default:
88
                                        $ordinal = $number . 'th';
×
89
                                        break;
×
90
                        }
91
                }
92
                return $ordinal;
×
93

94
        }
95
}
96

97
/**
98
 * Remove element from multi-dimensional array
99
 *
100
 * @param array  $items The multi-dimensional array.
101
 * @param string $key The key of the element.
102
 * @param string $value The value of the element.
103
 * @return array
104
 */
105
function edac_remove_element_with_value( $items, $key, $value ) {
106
        foreach ( $items as $sub_key => $sub_array ) {
12✔
107
                if ( $sub_array[ $key ] === $value ) {
10✔
108
                        unset( $items[ $sub_key ] );
8✔
109
                }
110
        }
111
        return $items;
12✔
112
}
113

114
/**
115
 * Filter a multi-dimensional array
116
 *
117
 * @param array  $items The multi-dimensional array.
118
 * @param string $index The index of the element.
119
 * @param string $value The element value to match.
120
 * @return array
121
 */
122
function edac_filter_by_value( $items, $index, $value ) {
123
        if ( is_array( $items ) && count( $items ) > 0 ) {
14✔
124
                foreach ( array_keys( $items ) as $key ) {
12✔
125
                        $temp[ $key ] = $items[ $key ][ $index ];
12✔
126

127
                        if ( $temp[ $key ] === $value ) {
12✔
128
                                $newarray[ $key ] = $items[ $key ];
10✔
129
                        }
130
                }
131
        }
132

133
        if ( isset( $newarray ) && is_array( $newarray ) && count( $newarray ) ) {
14✔
134
                return array_values( $newarray );
10✔
135
        }
136
        return [];
4✔
137
}
138

139
/**
140
 * Get days plugin has been active
141
 *
142
 * @return int
143
 */
144
function edac_days_active() {
145
        $activation_date = get_option( 'edac_activation_date' );
24✔
146
        if ( $activation_date ) {
24✔
147
                $diff = strtotime( $activation_date ) - strtotime( gmdate( 'Y-m-d H:i:s' ) );
20✔
148
                return abs( round( $diff / 86400 ) );
20✔
149
        }
150
        return 0;
4✔
151
}
152

153
/**
154
 * Custom Post Types
155
 *
156
 * @return array
157
 */
158
function edac_custom_post_types() {
159
        $args = [
×
160
                'public'   => true,
×
161
                '_builtin' => false,
×
162
        ];
×
163

164
        $output   = 'names'; // names or objects, note names is the default.
×
165
        $operator = 'and'; // Options 'and' or 'or'.
×
166

167
        return get_post_types( $args, $output, $operator );
×
168
}
169

170
/**
171
 * Available Post Types
172
 *
173
 * @return array
174
 */
175
function edac_post_types() {
176
        /**
177
         * Filter the post types that the plugin will check.
178
         *
179
         * @since 1.4.0
180
         *
181
         * @param array $post_types post types.
182
         */
183
        $post_types = apply_filters( 'edac_filter_post_types', [ 'post', 'page' ] );
×
184

185
        // remove duplicates.
186
        $post_types = array_unique( $post_types );
×
187

188
        // validate post types.
189
        foreach ( $post_types as $key => $post_type ) {
×
190
                if ( ! post_type_exists( $post_type ) ) {
×
191
                        unset( $post_types[ $key ] );
×
192
                }
193
        }
194

195
        return $post_types;
×
196
}
197

198
/**
199
 * Retrieve a human readable post type label.
200
 *
201
 * @param string $post_type Post type slug.
202
 * @return string
203
 */
204
function edac_get_post_type_label( string $post_type ): string {
NEW
205
        $post_type = sanitize_key( (string) $post_type );
×
206

NEW
207
        if ( '' === $post_type ) {
×
NEW
208
                return '';
×
209
        }
210

NEW
211
        $post_type_object = get_post_type_object( $post_type );
×
212

NEW
213
        if ( $post_type_object instanceof \WP_Post_Type && ! empty( $post_type_object->labels->name ) ) {
×
NEW
214
                return $post_type_object->labels->name;
×
215
        }
216

NEW
217
        return ucfirst( $post_type );
×
218
}
219

220
/**
221
 * This function validates a table name against WordPress naming conventions and checks its existence in the database.
222
 *
223
 * The function first checks if the provided table name only contains alphanumeric characters, underscores, or hyphens.
224
 * If not, it returns null.
225
 *
226
 * After that, it checks if a table with that name actually exists in the database using the SHOW TABLES LIKE query.
227
 * If the table doesn't exist, it also returns null.
228
 *
229
 * If both checks are passed, it returns the valid table name.
230
 *
231
 * @param string $table_name The name of the table to be validated.
232
 *
233
 * @return string|null The validated table name, or null if the table name is invalid or the table does not exist.
234
 */
235
function edac_get_valid_table_name( $table_name ) {
236
        global $wpdb;
46✔
237

238
        // Check if table name only contains alphanumeric characters, underscores, or hyphens.
239
        if ( ! preg_match( '/^[a-zA-Z0-9_\-]+$/', $table_name ) ) {
46✔
240
                // Invalid table name.
241
                return null;
×
242
        }
243

244
        // Verify that the table actually exists in the database.
245
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
246
        if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) !== $table_name ) {
46✔
247
                // Table does not exist.
248
                return null;
×
249
        }
250

251
        return $table_name;
46✔
252
}
253

254
/**
255
 * Upcoming meetups in json format
256
 *
257
 * @param string  $meetup meetup name.
258
 * @param integer $count number of meetups to return.
259
 * @return json
260
 */
261
function edac_get_upcoming_meetups_json( $meetup, $count = 5 ) {
262

263
        if ( empty( $meetup ) || ! is_string( $meetup ) ) {
2✔
264
                return;
×
265
        }
266

267
        // Min of 1 and max of 25.
268
        $count = absint( max( 1, min( 25, $count ) ) );
2✔
269

270
        $key    = '_upcoming_meetups__' . sanitize_title( $meetup ) . '__' . (int) $count;
2✔
271
        $output = get_transient( $key );
2✔
272

273
        if ( false === $output ) {
2✔
274
                $request_uri = 'https://api.meetup.com/gql-ext';
2✔
275
                $query       = '
2✔
276
                query Group {
277
                        groupByUrlname(urlname: "' . (string) $meetup . '") {
2✔
278
                                events(first: ' . (int) $count . ') {
2✔
279
                                        totalCount
280
                                        edges {
281
                                                node {
282
                                                        dateTime
283
                                                        eventUrl
284
                                                        id
285
                                                        title
286
                                                }
287
                                        }
288
                                }
289
                        }
290
                }';
2✔
291

292
                // Make POST request with the GraphQL query.
293
                $request = wp_remote_post(
2✔
294
                        $request_uri,
2✔
295
                        [
2✔
296
                                'headers' => [
2✔
297
                                        'Content-Type' => 'application/json',
2✔
298
                                ],
2✔
299
                                'body'    => wp_json_encode(
2✔
300
                                        [
2✔
301
                                                'query' => $query,
2✔
302
                                        ]
2✔
303
                                ),
2✔
304
                        ]
2✔
305
                );
2✔
306

307
                if ( is_wp_error( $request ) || 200 !== (int) wp_remote_retrieve_response_code( $request ) ) {
2✔
308
                        return;
×
309
                }
310

311
                $response_body = json_decode( wp_remote_retrieve_body( $request ) );
2✔
312

313
                // Return early if we don't have the expected data.
314
                if ( empty( $response_body ) || ! isset( $response_body->data->groupByUrlname->events->edges ) ) {
2✔
315
                        return;
×
316
                }
317

318
                // Transform the GraphQL response to match the format expected from old rest response.
319
                $output = [];
2✔
320
                foreach ( $response_body->data->groupByUrlname->events->edges as $edge ) {
2✔
321
                        $event = $edge->node;
2✔
322

323
                        // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- GraphQL response uses camelCase.
324
                        $event_data       = new stdClass();
2✔
325
                        $event_data->name = $event->title;
2✔
326
                        $event_data->time = strtotime( $event->dateTime ) * 1000; // Convert to milliseconds to match old format.
2✔
327
                        $event_data->link = $event->eventUrl;
2✔
328
                        $event_data->id   = $event->id;
2✔
329
                        // phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase.
330

331
                        $output[] = $event_data;
2✔
332
                }
333

334
                if ( empty( $output ) ) {
2✔
335
                        return;
×
336
                }
337

338
                set_transient( $key, $output, DAY_IN_SECONDS );
2✔
339
        }
340

341
        return $output;
2✔
342
}
343

344
/**
345
 * Upcoming meetups in html
346
 *
347
 * @param  string  $meetup meetup name.
348
 * @param  integer $count number of meetups to return.
349
 * @param  string  $heading heading level.
350
 * @return json
351
 */
352
function edac_get_upcoming_meetups_html( $meetup, $count = 5, $heading = '3' ) {
353

354
        $json = edac_get_upcoming_meetups_json( $meetup, $count );
2✔
355

356
        if ( empty( $json ) ) {
2✔
357
                return;
×
358
        }
359

360
        $html = '<ul class="edac-upcoming-meetup-list">';
2✔
361

362
        foreach ( $json as $event ) {
2✔
363
                $link_text = esc_html__( 'Attend Free', 'accessibility-checker' );
2✔
364

365
                $html .= '
2✔
366
                <li class="edac-upcoming-meetup-item edac-mb-3">
367
                        <h' . esc_html( $heading ) . ' class="edac-upcoming-meetup-item-name">' . esc_html( $event->name ) . '</h' . esc_html( $heading ) . '>
2✔
368
                        <div class="edac-upcoming-meetup-item-time edac-timestamp-to-local">' . ( (int) $event->time / 1000 ) . '</div>
2✔
369
                        <a aria-label="' . esc_attr( $link_text . ': ' . $event->name ) . '" class="edac-upcoming-meetup-item-link" href="' . esc_url( $event->link ) . '">' . $link_text . '</a>
2✔
370
                </li>';
2✔
371
        }
372

373
        $html .= '</ul>';
2✔
374

375
        return $html;
2✔
376
}
377

378
/**
379
 * Calculate the issue density
380
 *
381
 * @param  int $issue_count number of issues.
382
 * @param  int $element_count number of elements.
383
 * @param  int $content_length length of content.
384
 * @return int
385
 */
386
function edac_get_issue_density( $issue_count, $element_count, $content_length ) {
387

388
        if ( $element_count < 1 || $content_length < 1 ) {
4✔
389
                return 0;
4✔
390
        }
391

392
        $element_weight = .8;
×
393
        $content_weight = .2;
×
394

395
        $error_elements_percentage = $issue_count / $element_count;
×
396
        $error_content_percentage  = $issue_count / $content_length;
×
397

398
        $score = (
×
399
                ( $error_elements_percentage * $element_weight ) +
×
400
                ( $error_content_percentage * $content_weight )
×
401
        );
×
402

403
        return round( $score * 100, 2 );
×
404
}
405

406
/**
407
 * Get simplified summary
408
 *
409
 * @param integer $post Post ID.
410
 * @return void
411
 */
412
function edac_get_simplified_summary( $post = null ) {
413
        if ( null === $post ) {
×
414
                $post = get_the_ID();
×
415
        }
416

417
        if ( null === $post ) {
×
418
                return;
×
419
        }
420

421
        echo wp_kses_post(
×
422
                ( new \EDAC\Inc\Simplified_Summary() )->simplified_summary_markup( $post )
×
423
        );
×
424
}
425

426
/**
427
 * Get Post Count by available custom post types
428
 *
429
 * @return mixed
430
 */
431
function edac_get_posts_count() {
432

433
        $output = [];
×
434

435
        $post_types = Settings::get_scannable_post_types();
×
436
        if ( $post_types ) {
×
437
                foreach ( $post_types as $post_type ) {
×
438

439
                        $counts = wp_count_posts( $post_type );
×
440

441
                        if ( $counts ) {
×
442
                                foreach ( $counts as $key => $value ) {
×
443
                                        // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual
444
                                        if ( 0 == $value ) {
×
445
                                                unset( $counts->{$key} );
×
446
                                        }
447
                                }
448
                        }
449

450
                        if ( $counts ) {
×
451
                                $array = [];
×
452
                                foreach ( $counts as $key => $value ) {
×
453
                                        $array[] = $key . ' = ' . $value;
×
454
                                }
455
                                if ( $array ) {
×
456
                                        $output[] = $post_type . ': ' . implode( ', ', $array );
×
457
                                }
458
                        }
459
                }
460
        }
461

462
        if ( $output ) {
×
463
                return implode( ', ', $output );
×
464
        }
465
        return false;
×
466
}
467

468
/**
469
 * Get Raw Global Error Count
470
 *
471
 * @return array
472
 */
473
function edac_get_error_count() {
474
        global $wpdb;
×
475

476
        // Define a unique cache key for our data.
477
        $cache_key     = 'edac_errors_' . get_current_blog_id();
×
478
        $stored_errors = wp_cache_get( $cache_key );
×
479

480
        // Check if the result exists in the cache.
481
        if ( false === $stored_errors ) {
×
482
                // If not, perform the database query.
483
                $table_name = $wpdb->prefix . 'accessibility_checker';
×
484

485
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
486
                $stored_errors = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT count(*) FROM %i WHERE siteid = %d AND ruletype = %s', $table_name, get_current_blog_id(), 'error' ) );
×
487

488
                // Save the result in the cache for future use.
489
                wp_cache_set( $cache_key, $stored_errors );
×
490
        }
491

492
        return $stored_errors;
×
493
}
494

495
/**
496
 * Get Raw Global Warning Count
497
 *
498
 * @return array Array of.
499
 */
500
function edac_get_warning_count() {
501
        global $wpdb;
×
502

503
        // Define a unique cache key for our data.
504
        $cache_key       = 'edac_warnings_' . get_current_blog_id();
×
505
        $stored_warnings = wp_cache_get( $cache_key );
×
506

507
        // Check if the result exists in the cache.
508
        if ( false === $stored_warnings ) {
×
509
                // If not, perform the database query.
510
                $table_name = $wpdb->prefix . 'accessibility_checker';
×
511

512
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
513
                $stored_warnings = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT count(*) FROM %i WHERE siteid = %d AND ruletype = %s', $table_name, get_current_blog_id(), 'warning' ) );
×
514

515
                // Save the result in the cache for future use.
516
                wp_cache_set( $cache_key, $stored_warnings );
×
517
        }
518

519
        return $stored_warnings;
×
520
}
521

522
/**
523
 * Get Database Table Count
524
 *
525
 * @param string $table Database table.
526
 * @return int
527
 */
528
function edac_database_table_count( $table ) {
529
        global $wpdb;
×
530

531
        // Create a unique cache key based on the table's name.
532
        $cache_key = 'edac_table_count_' . $table;
×
533

534
        // Try to get the count from the cache first.
535
        $count = wp_cache_get( $cache_key );
×
536

537
        if ( false === $count ) {
×
538
                // If the count is not in the cache, perform the database query.
539
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
540
                $count = $wpdb->get_var( $wpdb->prepare( 'SELECT count(*) FROM %i', $wpdb->prefix . $table ) );
×
541

542
                // Save the count to the cache for future use.
543
                wp_cache_set( $cache_key, $count );
×
544
        }
545

546
        return $count;
×
547
}
548

549
/**
550
 * Generate a summary statistic list item.
551
 *
552
 * @since 1.14.0
553
 *
554
 * @param string $item_class     The base CSS class for the list item.
555
 * @param int    $count     The count of items to display.
556
 * @param string $label      The translated label with count included.
557
 *
558
 * @return string The generated HTML list item.
559
 */
560
function edac_generate_summary_stat( string $item_class, int $count, string $label ): string {
561
        $has_error_class = ( $count > 0 ) ? ' has-errors' : '';
×
562

563
        return '
×
564
                <li class="edac-summary-stat ' . $item_class . $has_error_class . '" aria-label="' . $label . '">
×
565
                        <div class="edac-panel-number">
566
                                ' . $count . '
×
567
                        </div>
568
                        <div class="edac-panel-number-label">' . $label . '</div>
×
569
                </li>';
×
570
}
571

572
/**
573
 * Generate links to pro page with some params.
574
 *
575
 * @param array  $query_args A list of key value pairs to add as query vars to the link.
576
 * @param string $type The type of link to generate. Default is 'pro'.
577
 * @param array  $args Additional arguments to pass on the link.
578
 * @return string
579
 */
580
function edac_generate_link_type( $query_args = [], $type = 'pro', $args = [] ): string {
581

582
        if ( ! is_array( $query_args ) ) {
58✔
583
                $query_args = [];
×
584
        }
585

586
        if ( ! is_array( $args ) ) {
58✔
587
                $args = [];
×
588
        }
589

590
        $date_now        = new DateTime( gmdate( 'Y-m-d H:i:s' ) );
58✔
591
        $activation_date = new DateTime( get_option( 'edac_activation_date', gmdate( 'Y-m-d H:i:s' ) ) );
58✔
592
        $interval        = $date_now->diff( $activation_date );
58✔
593
        $days_active     = $interval->days;
58✔
594
        $query_defaults  = [
58✔
595
                'utm_source'       => 'accessibility-checker',
58✔
596
                'utm_medium'       => 'software',
58✔
597
                'utm_campaign'     => 'wordpress-general',
58✔
598
                'php_version'      => PHP_VERSION,
58✔
599
                'platform'         => 'wordpress',
58✔
600
                'platform_version' => $GLOBALS['wp_version'],
58✔
601
                'software'         => defined( 'EDACP_KEY_VALID' ) && EDACP_KEY_VALID ? 'pro' : 'free',
58✔
602
                'software_version' => defined( 'EDACP_VERSION' ) ? EDACP_VERSION : EDAC_VERSION,
58✔
603
                'days_active'      => $days_active,
58✔
604
        ];
58✔
605

606
        // Add the ref parameter if one is set via filter.
607
        $ref = apply_filters( 'edac_filter_generate_link_type_ref', '' );
58✔
608
        if ( ! empty( $ref ) && is_string( $ref ) ) {
58✔
609
                $query_args['ref'] = $ref;
×
610
        }
611

612
        $query_args = array_merge( $query_defaults, $query_args );
58✔
613

614
        switch ( $type ) {
615
                case 'help':
58✔
616
                        $base_link = trailingslashit( 'https://a11ychecker.com/help' . $args['help_id'] ?? '' );
×
617
                        break;
×
618
                case 'custom': // phpcs:ignore -- intentially only breaking inside the condition because if it's not set we want to hit default.
58✔
619
                        if ( $args['base_link'] ) {
42✔
620
                                $base_link = $args['base_link'];
42✔
621
                                break;
42✔
622
                        }
623
                case 'pro':
16✔
624
                default:
625
                        $base_link = 'https://equalizedigital.com/accessibility-checker/pricing/';
16✔
626
                        break;
16✔
627
        }
628
        return add_query_arg( $query_args, $base_link );
58✔
629
}
630

631
/**
632
 * Echo or return a link with some utms.
633
 *
634
 * This is just a simplified wrapper around `edac_generate_link_type` to generate a link with UTM parameters.
635
 *
636
 * @param string $base_url the base URL to which UTM parameters will be added.
637
 * @param string $campaign the UTM campaign name, optional.
638
 * @param string $content the UTM content name, optional.
639
 * @param bool   $directly_echo whether to echo the link or return it. Default is true.
640
 *
641
 * @return void|string
642
 */
643
function edac_link_wrapper( $base_url, $campaign = '', $content = '', $directly_echo = true ) {
644
        if ( empty( $base_url ) || ! is_string( $base_url ) ) {
42✔
645
                return;
×
646
        }
647

648
        $params = [];
42✔
649
        if ( ! empty( $campaign ) ) {
42✔
650
                $params['utm_campaign'] = $campaign;
42✔
651
        }
652

653
        if ( ! empty( $content ) ) {
42✔
654
                $params['utm_content'] = $content;
42✔
655
        }
656

657
        $link = edac_generate_link_type(
42✔
658
                $params,
42✔
659
                'custom',
42✔
660
                [ 'base_link' => $base_url ]
42✔
661
        );
42✔
662

663
        if ( ! $directly_echo ) {
42✔
664
                return $link;
40✔
665
        }
666

667
        echo esc_url( $link );
2✔
668
}
669

670
/**
671
 * Check if WooCommerce is enabled.
672
 *
673
 * This just checks for existence of the main WooCommerce function and class.
674
 *
675
 * @return bool
676
 */
677
function edac_is_woocommerce_enabled() {
678
        return function_exists( 'WC' ) && class_exists( 'WooCommerce' );
×
679
}
680

681
/**
682
 * Check if a given post id is the WooCommerce checkout page.
683
 *
684
 * @param int $post_id The post ID to check.
685
 * @return bool
686
 */
687
function edac_check_if_post_id_is_woocommerce_checkout_page( $post_id ) {
688
        if ( ! edac_is_woocommerce_enabled() ) {
×
689
                return false;
×
690
        }
691

692
        return wc_get_page_id( 'checkout' ) === $post_id;
×
693
}
694

695
/**
696
 * Parse HTML content to extract image or SVG elements
697
 *
698
 * @param string $html The HTML content to parse.
699
 * @return array Array containing 'img' (string) and 'svg' (string) keys.
700
 */
701
function edac_parse_html_for_media( $html ) {
702
        if ( empty( $html ) ) {
32✔
703
                return [
2✔
704
                        'img' => null,
2✔
705
                        'svg' => null,
2✔
706
                ];
2✔
707
        }
708

709
        // Decode HTML entities before processing.
710
        $decoded_html = html_entity_decode( $html, ENT_QUOTES | ENT_HTML5 );
30✔
711

712
        // Early return if no media tags found.
713
        if ( stripos( $decoded_html, '<img' ) === false && stripos( $decoded_html, '<svg' ) === false ) {
30✔
714
                return [
2✔
715
                        'img' => null,
2✔
716
                        'svg' => null,
2✔
717
                ];
2✔
718
        }
719

720
        // More specific img tag regex pattern.
721
        if ( preg_match( '/<img[^>]+src=([\'"])(.*?)\1[^>]*>/i', $decoded_html, $matches ) ) {
28✔
722
                return [
18✔
723
                        'img' => $matches[2] ?? null,
18✔
724
                        'svg' => null,
18✔
725
                ];
18✔
726
        }
727

728
        // SVG pattern remains the same.
729
        if ( preg_match( '/<svg[^>]*>.*?<\/svg>/is', $decoded_html, $matches ) ) {
10✔
730
                return [
8✔
731
                        'img' => null,
8✔
732
                        'svg' => $matches[0],
8✔
733
                ];
8✔
734
        }
735

736
        return [
2✔
737
                'img' => null,
2✔
738
                'svg' => null,
2✔
739
        ];
2✔
740
}
741

742
/**
743
 * Remove corrected posts
744
 *
745
 * @param int    $post_ID The ID of the post.
746
 * @param string $type    The type of the post.
747
 * @param int    $pre     The flag indicating the removal stage (1 for before validation php based rules, 2 for after validation).
748
 * @param string $ruleset    The type of the ruleset to correct (php or js). For backwards compatibility, defaults to 'php'.
749
 *
750
 * @return void
751
 */
752
function edac_remove_corrected_posts( $post_ID, $type, $pre = 1, $ruleset = 'php' ) {  // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $ruleset is for backwards compatibility.
753
        global $wpdb;
4✔
754

755
        $rules = edac_register_rules();
4✔
756

757
        if ( 0 === count( $rules ) ) {
4✔
758
                return;
×
759
        }
760

761
        $sql = 1 === $pre
4✔
762
                ? "UPDATE {$wpdb->prefix}accessibility_checker SET recordcheck = %d WHERE siteid = %d AND postid = %d AND type = %s"
4✔
763
                : "DELETE FROM {$wpdb->prefix}accessibility_checker WHERE recordcheck = %d AND siteid = %d AND postid = %d AND type = %s";
4✔
764

765
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Using direct query for adding data to database, caching not required for one time operation.
766
        $wpdb->query(
4✔
767
                $wpdb->prepare(
4✔
768
                        $sql, // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
4✔
769
                        0,
4✔
770
                        get_current_blog_id(),
4✔
771
                        $post_ID,
4✔
772
                        $type
4✔
773
                )
4✔
774
        );
4✔
775
}
776

777
/**
778
 * Generate a landmark link with proper URL and ARIA label
779
 *
780
 * @param string $landmark The landmark type (e.g., "header", "navigation", "main").
781
 * @param string $landmark_selector The CSS selector for the landmark.
782
 * @param int    $post_id The post ID to link to.
783
 * @param string $css_class Optional CSS class for the link. Default 'edac-details-rule-records-record-landmark-link'.
784
 * @param bool   $target_blank Whether to open link in new window. Default true.
785
 *
786
 * @return string The HTML for the landmark link or just the landmark text if no selector.
787
 */
788
function edac_generate_landmark_link( $landmark, $landmark_selector, $post_id, $css_class = 'edac-details-rule-records-record-landmark-link', $target_blank = true ) {
789
        if ( empty( $landmark ) ) {
64✔
790
                return '';
6✔
791
        }
792
        $landmark = ucwords( $landmark );
60✔
793
        $landmark = esc_html( $landmark );
60✔
794

795
        // If we have both landmark and selector, create a link.
796
        if ( ! empty( $landmark_selector ) ) {
60✔
797
                $link = apply_filters(
52✔
798
                        'edac_get_origin_url_for_virtual_page',
52✔
799
                        get_the_permalink( $post_id ),
52✔
800
                        $post_id
52✔
801
                );
52✔
802

803
                $landmark_url = add_query_arg(
52✔
804
                        [
52✔
805
                                'edac_landmark' => base64_encode( $landmark_selector ),
52✔
806
                                'edac_nonce'    => wp_create_nonce( 'edac_highlight' ),
52✔
807
                        ],
52✔
808
                        $link
52✔
809
                );
52✔
810

811
                // translators: %s is the landmark type (e.g., "Header", "Navigation", "Main").
812
                $landmark_aria_label = sprintf( __( 'View %s landmark on website, opens a new window', 'accessibility-checker' ), $landmark );
52✔
813

814
                $target_attr = $target_blank ? ' target="_blank"' : '';
52✔
815

816
                return sprintf(
52✔
817
                        '<a href="%s" class="%s"%s aria-label="%s">%s</a>',
52✔
818
                        esc_url( $landmark_url ),
52✔
819
                        esc_attr( $css_class ),
52✔
820
                        $target_attr,
52✔
821
                        esc_attr( $landmark_aria_label ),
52✔
822
                        $landmark
52✔
823
                );
52✔
824
        }
825

826
        // If we only have landmark text, return it formatted.
827
        return $landmark;
8✔
828
}
829

830
/**
831
 * Check if a post is a virtual page.
832
 *
833
 * This function checks if a post is a virtual page using the pro plugin's
834
 * VirtualItemType:POST_TYPE constant.
835
 *
836
 * @param int $post_id The post ID to check.
837
 * @return bool True if the post is a virtual page, false otherwise.
838
 */
839
function edac_is_virtual_page( $post_id ) {
840
        if ( class_exists( '\EqualizeDigital\AccessibilityCheckerPro\VirtualContent\PostType\VirtualItemType' ) ) {
20✔
841
                $post_type     = get_post_type( $post_id );
16✔
842
                $pro_post_type = \EqualizeDigital\AccessibilityCheckerPro\VirtualContent\PostType\VirtualItemType::POST_TYPE;
16✔
843
                return $pro_post_type === $post_type;
16✔
844
        }
845

846
        return false;
4✔
847
}
848

849
/**
850
 * Check if the Pro version of the plugin is active.
851
 *
852
 * @return bool True if Pro version is active, false otherwise.
853
 */
854
function edac_is_pro() {
855
        return defined( 'EDACP_VERSION' ) && defined( 'EDAC_KEY_VALID' ) && EDAC_KEY_VALID;
×
856
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc