Skip to content
Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
54eb791
Filter the post types at save time
pattonwebz Aug 5, 2025
d9439d5
Show metabox on the virtual post
pattonwebz Aug 6, 2025
99cbbde
Add filter for customizable post view link in AJAX response
pattonwebz Aug 6, 2025
c45b2f5
Refactor post ID retrieval to use a filter for enhanced flexibility
pattonwebz Aug 11, 2025
6e5703f
Refactor filter name for virtual page links
pattonwebz Aug 12, 2025
d983812
Hide readability section for virtual pages with display:none
pattonwebz Aug 12, 2025
271affd
Refactor post type retrieval to use filter for scannable post types
pattonwebz Aug 12, 2025
df67427
No longer need to check for old pro settings class
pattonwebz Aug 18, 2025
ab13da2
Return pro value right away if pro get_scannable_posts_types exists
pattonwebz Aug 18, 2025
6142569
Include 'Archives' as a custom post type in
pattonwebz Aug 18, 2025
069d9e4
refactored: virtual page check to use dedicated function for improved…
SteveJonesDev Aug 18, 2025
0873d1e
Fire an action before clearing cpt issues
pattonwebz Aug 18, 2025
e345aed
added: unit tests for edac_is_virtual_page function to enhance coverage
SteveJonesDev Aug 18, 2025
6e5e3ed
Merge pull request #1201 from equalizedigital/steve/no-issue/update-c…
SteveJonesDev Aug 18, 2025
f2bd12e
Allow scannable posts to cover virtual posts
pattonwebz Aug 20, 2025
bc4bc75
Fix a badly named filter name
pattonwebz Aug 20, 2025
0c5ff01
Refactor to use Settings::get_scannable_post_types() for post type re…
pattonwebz Aug 21, 2025
e4310ca
Don't output archive as a scannable post type in settings or visible …
pattonwebz Aug 21, 2025
fd255b7
Merge remote-tracking branch 'origin/develop' into william/pro-169-fi…
pattonwebz Aug 21, 2025
15e4287
Add a flag to skip over filtering in virtual post type to the scannables
pattonwebz Aug 21, 2025
0942007
Skip the filtering when getting scannable_post_types in 2 places
pattonwebz Aug 21, 2025
8b7c400
Refactor virtual page references to use VirtualItem
pattonwebz Aug 21, 2025
1479e49
Refactor virtual content namespace and class references
pattonwebz Aug 21, 2025
b42c504
Add system settings section to options page and move the delete setti…
pattonwebz Aug 25, 2025
cc29ba6
Add helper to check if Pro version of the plugin is active and in use
pattonwebz Aug 25, 2025
b926db8
Genericize the pro upsell bubble name, classes and markers
pattonwebz Aug 25, 2025
f2c626e
Update 'General Settings' to 'Scan Settings' on options page
pattonwebz Aug 25, 2025
7b2fe61
Include some pro setting upsells here with nulling sanitizer callbacks
pattonwebz Aug 25, 2025
3da0f7a
Fix pro upsell tagging with utm-content
pattonwebz Aug 25, 2025
6cd7e6c
Rename scan speed settings for consistency and back compatibility
pattonwebz Aug 25, 2025
7cf16c9
Add settings fields for ignoring user roles and simplified summary he…
pattonwebz Aug 25, 2025
2841157
Update some names of scan settings to be edacp for consistency with w…
pattonwebz Aug 25, 2025
1fe997b
Refactor sanitization functions for scan speed and remove Pro-only fe…
pattonwebz Aug 25, 2025
6b7f7d1
Deregister all the settings registration that happens in pro plugin
pattonwebz Aug 25, 2025
fe5688c
Move the taxonomy scanning checkbox enable/disable code info free
pattonwebz Aug 25, 2025
bc5c1ce
Refactor pro settings sanitization for backwards compatibility
pattonwebz Aug 25, 2025
89d8213
Add option to ignore user roles during activation
pattonwebz Aug 25, 2025
b255202
Make edac_is_pro slightly more rigid
pattonwebz Aug 26, 2025
ec9da2c
Run the inline upsell bubble code if the classes exist on the page
pattonwebz Aug 26, 2025
1a6228f
Fix some label_for vales
pattonwebz Aug 26, 2025
04d33f6
Fix a mismatched label_for
pattonwebz Aug 26, 2025
aa56926
Move the Archive checkbox toggle code to inside existing window load …
pattonwebz Aug 26, 2025
93be94e
Handle disabled state on the fieldset for screen readers as well when…
pattonwebz Aug 26, 2025
ab9bdb2
Add some desciptions and labeling to some callbacks, use `selected()`…
pattonwebz Aug 26, 2025
c150739
Harden the upsell url generation against possible missing values
pattonwebz Aug 26, 2025
90aa188
fix some line endings and whitespace in helpers file
pattonwebz Aug 26, 2025
96a5305
Remove extra + character on line
pattonwebz Aug 26, 2025
0e4b108
Add some initial defaults for saving values on some settings when not…
pattonwebz Aug 26, 2025
7622b19
Handle possible array name keys in bubble upsell link generation
pattonwebz Aug 26, 2025
953d1e9
Merge branch 'william/pro-169-filter-post-types-at-save-time' into wi…
pattonwebz Aug 26, 2025
800271c
Update mock class in test to reflect the current path and name in pro
pattonwebz Aug 26, 2025
c59d654
Merge pull request #1208 from equalizedigital/william/no-issue/move-p…
pattonwebz Aug 26, 2025
f4bf0b2
Refactor post type change detection to compare sorted arrays
pattonwebz Aug 26, 2025
9d1d6af
Add a Permission section for ignore permissions setting
pattonwebz Aug 26, 2025
23e8fe6
Update documentation for virtual page check to use VirtualItemType:PO…
pattonwebz Aug 26, 2025
8a4c686
Add back in the old upsell bubble class for back compat
pattonwebz Aug 26, 2025
819c1c1
Update the scan settings messaging
pattonwebz Aug 26, 2025
d99c4ca
Use a more direct comparison for types before and after setting save
pattonwebz Aug 26, 2025
0a3b14b
Update messaging for settings permission section
pattonwebz Aug 26, 2025
f5b5fea
Do 'edac_pro_setting_saving_checkbox' when saving archive scan setting
pattonwebz Aug 27, 2025
a61432c
Remove an unused sanitizer wrapper
pattonwebz Aug 27, 2025
0ab9548
Refactor scan stats handling to improve clarity
pattonwebz Aug 27, 2025
57cce49
Use edac_is_pro helper instead of direct constant check
pattonwebz Aug 27, 2025
4fc1925
Make comment more clear
pattonwebz Aug 27, 2025
e2dca0b
Make edac_get_origin_url_for_virtual_page accept string as first param
pattonwebz Aug 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions admin/class-ajax.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ public function summary() {

$html['content'] .= '</li>';

// if this is a virtual page, we don't show the readability section.
$is_virtual_page = edac_is_virtual_page( $post_id );

$html['content'] .= '
' . edac_generate_summary_stat(
'edac-summary-errors',
Expand Down Expand Up @@ -145,7 +148,7 @@ public function summary() {
) . '

</ul>
<div class="edac-summary-readability">
<div class="edac-summary-readability" ' . ( $is_virtual_page ? 'style="display: none;"' : '' ) . '>
<div class="edac-summary-readability-level">
<div><img src="' . EDAC_PLUGIN_URL . 'assets/images/readability-icon-navy.png" alt="" width="54"></div>
<div class="edac-panel-number' . ( ( (int) $summary['content_grade'] <= 9 || 'none' === $simplified_summary_prompt ) ? ' passed-text-color' : ' failed-text-color' ) . '">
Expand Down Expand Up @@ -479,7 +482,7 @@ function ( $a, $b ) {

$landmark = isset( $row['landmark'] ) ? $row['landmark'] : '';
$landmark_selector = isset( $row['landmark_selector'] ) ? $row['landmark_selector'] : '';

$html .= edac_generate_landmark_link( $landmark, $landmark_selector, $postid );

$html .= '</div>';
Expand All @@ -488,12 +491,17 @@ function ( $a, $b ) {

if ( ! isset( $rule['viewable'] ) || $rule['viewable'] ) {

$post_view_link = apply_filters(
'edac_get_origin_url_for_virtual_page',
$postid
);
Comment on lines +491 to +498

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add PHPDoc documentation for the new filter hook.

The filter hook implementation is correct and follows the edac_ prefix convention, but it lacks proper documentation. According to the coding guidelines, all custom hooks should be documented with clear descriptions and parameter types.

Add PHPDoc documentation above the filter:

+			/**
+			 * Filters the URL used for the "View on page" link in accessibility issue details.
+			 *
+			 * This filter allows customization of the URL, which is particularly useful for
+			 * virtual pages or custom post types that may need special URL handling.
+			 *
+			 * @since [version]
+			 *
+			 * @param int $postid The post ID for which to generate the view link.
+			 * @return string The URL to use for the "View on page" link.
+			 */
 			$post_view_link = apply_filters(
 				'edac_details_post_view_link',
 				$postid
 			);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$post_view_link = apply_filters(
'edac_details_post_view_link',
$postid
);
/**
* Filters the URL used for the "View on page" link in accessibility issue details.
*
* This filter allows customization of the URL, which is particularly useful for
* virtual pages or custom post types that may need special URL handling.
*
* @since [version]
*
* @param int $postid The post ID for which to generate the view link.
* @return string The URL to use for the "View on page" link.
*/
$post_view_link = apply_filters(
'edac_details_post_view_link',
$postid
);
🤖 Prompt for AI Agents
In admin/class-ajax.php around lines 491 to 494, the new filter hook
'edac_details_post_view_link' lacks PHPDoc documentation. Add a PHPDoc block
above the apply_filters call describing the filter's purpose, the expected
parameter type (post ID as an integer), and the return type. Follow the existing
coding guidelines for documenting custom hooks with clear descriptions and
parameter annotations.


$url = add_query_arg(
[
'edac' => $id,
'edac_nonce' => wp_create_nonce( 'edac_highlight' ),
],
get_the_permalink( $postid )
is_string( $post_view_link ) ? $post_view_link : get_the_permalink( $postid )
);

// Translators: %d is the issue ID.
Expand Down
27 changes: 17 additions & 10 deletions admin/class-enqueue-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static function enqueue_styles() {
public static function maybe_enqueue_admin_and_editor_app_scripts() {

global $pagenow;
$post_types = get_option( 'edac_post_types' );
$post_types = Settings::get_scannable_post_types();
$current_post_type = get_post_type();
$page = self::get_current_page_slug();
$enabled_pages = apply_filters(
Expand Down Expand Up @@ -82,18 +82,18 @@ public static function maybe_enqueue_admin_and_editor_app_scripts() {
'edac',
'edac_script_vars',
[
'postID' => $post_id,
'nonce' => wp_create_nonce( 'ajax-nonce' ),
'edacApiUrl' => esc_url_raw( rest_url() . 'accessibility-checker/v1' ),
'restNonce' => wp_create_nonce( 'wp_rest' ),
'fixesProUrl' => esc_url_raw( edac_generate_link_type( [ 'utm-content', '__fix__' ] ) ),
'postID' => $post_id,
'nonce' => wp_create_nonce( 'ajax-nonce' ),
'edacApiUrl' => esc_url_raw( rest_url() . 'accessibility-checker/v1' ),
'restNonce' => wp_create_nonce( 'wp_rest' ),
'proUrl' => esc_url_raw( edac_generate_link_type( [ 'utm-content' => '__name__' ] ) ),
]
);

if ( 'post.php' === $pagenow || 'post-new.php' === $pagenow ) {

// Is this posttype setup to be checked?
$post_types = get_option( 'edac_post_types' );
$post_types = Settings::get_scannable_post_types();
$current_post_type = get_post_type();
$active = ( is_array( $post_types ) && in_array( $current_post_type, $post_types, true ) );

Expand All @@ -112,9 +112,16 @@ public static function maybe_enqueue_admin_and_editor_app_scripts() {
if ( (int) get_option( 'page_on_front' ) === $post_id || (int) get_option( 'page_for_posts' ) === $post_id ) {
$scan_url = add_query_arg( 'edac_pageScanner', 1, get_permalink( $post_id ) );
} else {
$scan_url = get_preview_post_link(
$post_id,
[ 'edac_pageScanner' => 1 ]
$post_view_link = apply_filters(
'edac_get_origin_url_for_virtual_page',
$post_id
);

$scan_url = add_query_arg(
[
'edac_pageScanner' => 1,
],
is_string( $post_view_link ) ? $post_view_link : get_preview_post_link( $post_id )
);
Comment on lines +115 to 126

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validate and document origin URL filter; block non-http(s) schemes.

A malicious filter could feed a javascript: URL. Validate the scheme and document the hook. Don’t let bad URLs punk your editor.

-					$post_view_link = apply_filters(
-						'edac_get_origin_url_for_virtual_page',
-						$post_id
-					);
-
-					$scan_url = add_query_arg(
-						[
-							'edac_pageScanner' => 1,
-						],
-						is_string( $post_view_link ) ? $post_view_link : get_preview_post_link( $post_id )
-					);
+					$default_base = get_preview_post_link( $post_id );
+					/**
+					 * Filter the base URL used when launching the page scanner from the editor.
+					 *
+					 * Allows mapping virtual/ghost posts to their canonical origin URL (e.g., archives/taxonomies).
+					 *
+					 * @since 1.29.0
+					 *
+					 * @param string $default_base Default preview URL for the post.
+					 * @param int    $post_id      Current post ID (can be virtual/ghost).
+					 * @return string Filtered base URL.
+					 */
+					$post_view_link = apply_filters( 'edac_get_origin_url_for_virtual_page', $default_base, $post_id );
+					$scan_base      = ( is_string( $post_view_link ) && '' !== $post_view_link ) ? $post_view_link : $default_base;
+					$parsed         = wp_parse_url( $scan_base );
+					if ( empty( $parsed['scheme'] ) || ! in_array( $parsed['scheme'], [ 'http', 'https' ], true ) ) {
+						$scan_base = $default_base;
+					}
+					$scan_url = add_query_arg( [ 'edac_pageScanner' => 1 ], $scan_base );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$post_view_link = apply_filters(
'edac_get_origin_url_for_virtual_page',
$post_id
);
$scan_url = add_query_arg(
[
'edac_pageScanner' => 1,
],
is_string( $post_view_link ) ? $post_view_link : get_preview_post_link( $post_id )
);
$default_base = get_preview_post_link( $post_id );
/**
* Filter the base URL used when launching the page scanner from the editor.
*
* Allows mapping virtual/ghost posts to their canonical origin URL (e.g., archives/taxonomies).
*
* @since 1.29.0
*
* @param string $default_base Default preview URL for the post.
* @param int $post_id Current post ID (can be virtual/ghost).
* @return string Filtered base URL.
*/
$post_view_link = apply_filters( 'edac_get_origin_url_for_virtual_page', $default_base, $post_id );
$scan_base = ( is_string( $post_view_link ) && '' !== $post_view_link ) ? $post_view_link : $default_base;
$parsed = wp_parse_url( $scan_base );
if ( empty( $parsed['scheme'] ) || ! in_array( $parsed['scheme'], [ 'http', 'https' ], true ) ) {
$scan_base = $default_base;
}
$scan_url = add_query_arg( [ 'edac_pageScanner' => 1 ], $scan_base );

}

Expand Down
2 changes: 1 addition & 1 deletion admin/class-meta-boxes.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function init_hooks(): void {
* @return void
*/
public function register_meta_boxes(): void {
$post_types = get_option( 'edac_post_types' );
$post_types = Settings::get_scannable_post_types();
if ( $post_types ) {
foreach ( $post_types as $post_type ) {
add_meta_box(
Expand Down
2 changes: 1 addition & 1 deletion admin/class-post-save.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Post_Save {
*/
public static function delete_issue_data_on_post_trashing( $post_ID, $post, $update ) {
// check post type.
$post_types = get_option( 'edac_post_types' );
$post_types = Settings::get_scannable_post_types();
if ( is_array( $post_types ) && ! in_array( $post->post_type, $post_types, true ) ) {
return;
}
Expand Down
9 changes: 9 additions & 0 deletions admin/class-purge-post-data.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ public static function delete_cpt_posts( string $post_type ) {

global $wpdb;

/**
* Fires before deleting posts of a specific post type.
*
* @since 1.31.0
*
* @param string $post_type Post Type.
*/
do_action( 'edac_before_delete_cpt_posts', $post_type );

// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Safe variable used for table name, caching not required for one time operation.
return $wpdb->query(
$wpdb->prepare(
Expand Down
2 changes: 1 addition & 1 deletion admin/class-scans-stats.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public function summary( $skip_cache = false ) {
$data['scannable_posts_count'] = (int) $scannable_posts_count;
$data['rule_count'] = (int) $this->rule_count;
$data['tests_count'] = (int) $tests_count;
$data['scannable_post_types_count'] = (int) count( Settings::get_scannable_post_types() );
$data['scannable_post_types_count'] = (int) count( Settings::get_scannable_post_types( true ) );

$post_types = get_post_types(
[
Expand Down
47 changes: 23 additions & 24 deletions admin/class-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,40 +35,39 @@ public static function get_scannable_post_statuses() {
/**
* Gets a list of post types that are scannable.
*
* @param bool $skip_filtering Whether to skip filtering when passing through alternative settings class.
*
* @return array
*/
public static function get_scannable_post_types() {
public static function get_scannable_post_types( $skip_filtering = false ) {

// Check if the new settings class exists. This is added to allow for backwards compatibility
// with the old settings class. The old settings class check should be removed after a few releases.
$new_settings_class_exists = class_exists( 'EqualizeDigital\AccessibilityCheckerPro\Admin\Settings' );
if ( ! class_exists( '\EDACP\Settings' ) && ! $new_settings_class_exists ) {
if (
class_exists( 'EqualizeDigital\AccessibilityCheckerPro\Admin\Settings' ) &&
method_exists( 'EqualizeDigital\AccessibilityCheckerPro\Admin\Settings', 'get_scannable_post_types' )
) {
return \EqualizeDigital\AccessibilityCheckerPro\Admin\Settings::get_scannable_post_types( $skip_filtering );
}
Comment on lines +42 to +49

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Back-compat booby trap: calling Pro::get_scannable_post_types with an extra arg can fatally error

If older Pro versions expose get_scannable_post_types() without the $skip_filtering parameter, this direct call with one argument will throw an ArgumentCountError at runtime. Don’t let that sucker punch your users—guard by inspecting the method’s parameter count and call accordingly.

Proposed back-compat safe delegation:

-    public static function get_scannable_post_types( $skip_filtering = false ) {
-
-        if (
-            class_exists( 'EqualizeDigital\AccessibilityCheckerPro\Admin\Settings' ) &&
-            method_exists( 'EqualizeDigital\AccessibilityCheckerPro\Admin\Settings', 'get_scannable_post_types' )
-        ) {
-            return \EqualizeDigital\AccessibilityCheckerPro\Admin\Settings::get_scannable_post_types( $skip_filtering );
-        }
+    public static function get_scannable_post_types( $skip_filtering = false ) {
+        $pro_class = '\EqualizeDigital\AccessibilityCheckerPro\Admin\Settings';
+        if ( class_exists( $pro_class ) && method_exists( $pro_class, 'get_scannable_post_types' ) ) {
+            // Back-compat: call with zero or one argument depending on the Pro signature.
+            try {
+                $ref = new \ReflectionMethod( $pro_class, 'get_scannable_post_types' );
+                if ( $ref->getNumberOfParameters() > 0 ) {
+                    return $pro_class::get_scannable_post_types( $skip_filtering );
+                }
+                return $pro_class::get_scannable_post_types();
+            } catch ( \ReflectionException $e ) {
+                // Fall back to the safest call.
+                return $pro_class::get_scannable_post_types();
+            }
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public static function get_scannable_post_types( $skip_filtering = false ) {
// Check if the new settings class exists. This is added to allow for backwards compatibility
// with the old settings class. The old settings class check should be removed after a few releases.
$new_settings_class_exists = class_exists( 'EqualizeDigital\AccessibilityCheckerPro\Admin\Settings' );
if ( ! class_exists( '\EDACP\Settings' ) && ! $new_settings_class_exists ) {
if (
class_exists( 'EqualizeDigital\AccessibilityCheckerPro\Admin\Settings' ) &&
method_exists( 'EqualizeDigital\AccessibilityCheckerPro\Admin\Settings', 'get_scannable_post_types' )
) {
return \EqualizeDigital\AccessibilityCheckerPro\Admin\Settings::get_scannable_post_types( $skip_filtering );
}
public static function get_scannable_post_types( $skip_filtering = false ) {
$pro_class = '\EqualizeDigital\AccessibilityCheckerPro\Admin\Settings';
if ( class_exists( $pro_class ) && method_exists( $pro_class, 'get_scannable_post_types' ) ) {
// Back-compat: call with zero or one argument depending on the Pro signature.
try {
$ref = new \ReflectionMethod( $pro_class, 'get_scannable_post_types' );
if ( $ref->getNumberOfParameters() > 0 ) {
return $pro_class::get_scannable_post_types( $skip_filtering );
}
return $pro_class::get_scannable_post_types();
} catch ( \ReflectionException $e ) {
// Fall back to the safest call.
return $pro_class::get_scannable_post_types();
}
}
🤖 Prompt for AI Agents
In admin/class-settings.php around lines 42 to 49, the direct call to
EqualizeDigital\AccessibilityCheckerPro\Admin\Settings::get_scannable_post_types($skip_filtering)
can fatal if the Pro method signature lacks that parameter; instead detect the
Pro method's parameter count (e.g. with
ReflectionMethod::getNumberOfParameters()) and if it accepts at least one
parameter call it with $skip_filtering, otherwise call it with no arguments so
older Pro versions remain compatible.


$post_types = Helpers::get_option_as_array( 'edac_post_types' );
$post_types = Helpers::get_option_as_array( 'edac_post_types' );

// remove duplicates.
$post_types = array_unique( $post_types );
// remove duplicates.
$post_types = array_unique( $post_types );

// validate post types.
$args = [
'public' => true,
'_builtin' => true,
];
$valid_post_types = get_post_types( $args, 'names', 'and' );
unset( $valid_post_types['attachment'] );
// validate post types.
$args = [
'public' => true,
'_builtin' => true,
];
$valid_post_types = get_post_types( $args, 'names', 'and' );
unset( $valid_post_types['attachment'] );

foreach ( $post_types as $key => $post_type ) {
foreach ( $post_types as $key => $post_type ) {

if ( ! post_type_exists( $post_type ) || ! array_key_exists( $post_type, $valid_post_types ) ) {
unset( $post_types[ $key ] );
}
if ( ! post_type_exists( $post_type ) || ! array_key_exists( $post_type, $valid_post_types ) ) {
unset( $post_types[ $key ] );
}
return $post_types;
}

return $new_settings_class_exists
? \EqualizeDigital\AccessibilityCheckerPro\Admin\Settings::get_scannable_post_types()
: \EDACP\Settings::get_scannable_post_types();
return $post_types;
}


Expand Down
3 changes: 3 additions & 0 deletions includes/activation.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ function edac_activation() {
update_option( 'edac_simplified_summary_position', 'after' );

Accessibility_Statement::add_page();

// This is an add_option on purpose to not overwrite user settings on update.
add_option( 'edacp_ignore_user_roles', [ 'administrator' ] );
}
6 changes: 4 additions & 2 deletions includes/classes/class-enqueue-frontend.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

namespace EDAC\Inc;

use EDAC\Admin\Settings;

/**
* Class that initializes and handles enqueueing styles and scripts for the frontend.
*/
Expand Down Expand Up @@ -50,7 +52,7 @@ public static function maybe_enqueue_frontend_highlighter() {

// Don't load on the frontend if we don't have a post to work with.
global $post;
$post_id = is_object( $post ) ? $post->ID : null;
$post_id = apply_filters( 'edac_filter_frontend_highlight_post_id', is_object( $post ) ? $post->ID : null );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add PHPDoc documentation for the new filter.

The filter implementation is correct and follows WordPress conventions with the proper edac_ prefix. However, according to the coding guidelines, custom hooks should be documented with clear descriptions and parameter types.

Add PHPDoc documentation above the filter:

+		/**
+		 * Filter the post ID used for frontend highlighting.
+		 *
+		 * Allows external code to override which post ID is used for frontend
+		 * highlighting functionality, supporting virtual posts for archive/taxonomy pages.
+		 *
+		 * @since 1.15.0
+		 *
+		 * @param int|null $post_id The post ID to use for frontend highlighting. Default is the current post ID or null.
+		 */
 		$post_id = apply_filters( 'edac_filter_frontend_highlight_post_id', is_object( $post ) ? $post->ID : null );
🤖 Prompt for AI Agents
In includes/classes/class-enqueue-frontend.php around line 53, there is a new
filter call that lacks PHPDoc; add a PHPDoc block immediately above the
apply_filters line documenting the filter name
edac_filter_frontend_highlight_post_id, a short description of what the filter
does, the expected parameter and types (int|null $post_id — the current post ID
or null, and optionally WP_Post|null $post if you want to document the original
variable), the return type (int|null), and tags like @since and @param/@return
following WP coding standards; ensure the comment is concise and placed directly
above the apply_filters call.


if ( null === $post_id ) {
Comment on lines +55 to 57

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Gate logic should use the filtered $post_id’s post type (and allow virtual pages).

Right now you ask get_post_type() without an ID, which can mismatch on archives. That’s sloppy—use the post type for the post you actually intend to highlight; also allow virtual/ghost posts to activate the highlighter.

-		$post_types        = Settings::get_scannable_post_types();
-		$current_post_type = get_post_type();
-		$active            = ( is_array( $post_types ) && in_array( $current_post_type, $post_types, true ) );
+		$post_types        = Settings::get_scannable_post_types();
+		$current_post_type = $post_id ? get_post_type( $post_id ) : get_post_type();
+		$active            = (
+			is_array( $post_types )
+			&& (
+				in_array( $current_post_type, $post_types, true )
+				|| ( function_exists( 'edac_is_virtual_page' ) && $post_id && edac_is_virtual_page( $post_id ) )
+			)
+		);

Also applies to: 86-90

🤖 Prompt for AI Agents
In includes/classes/class-enqueue-frontend.php around lines 55-57 (and similarly
update lines 86-90), the code calls get_post_type() with no ID which can
mismatch after you filter $post_id; change those calls to use the filtered
$post_id (i.e., get_post_type($post_id)). Also allow "virtual"/ghost pages by
treating a non-null filtered $post_id that yields an empty post type as valid
when the filter intentionally provided a string or non-numeric identifier (e.g.,
if is_string($post_id) || !empty($post_id) then proceed), ensuring both the
highlighted post logic and the gate checks use the same $post_id value.

return;
Expand Down Expand Up @@ -82,7 +84,7 @@ public static function maybe_enqueue_frontend_highlighter() {


// Don't load if this pagetype is not setup to be scanned.
$post_types = get_option( 'edac_post_types' );
$post_types = Settings::get_scannable_post_types();
$current_post_type = get_post_type();
$active = ( is_array( $post_types ) && in_array( $current_post_type, $post_types, true ) );

Expand Down
5 changes: 2 additions & 3 deletions includes/classes/class-rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

namespace EDAC\Inc;

use EDAC\Admin\Helpers;
use EDAC\Admin\Insert_Rule_Data;
use EDAC\Admin\Scans_Stats;
use EDAC\Admin\Settings;
Expand Down Expand Up @@ -248,7 +247,7 @@ public function clear_issues_for_post( $request ) {
}

$post_type = get_post_type( $post );
$post_types = Helpers::get_option_as_array( 'edac_post_types' );
$post_types = Settings::get_scannable_post_types();
if ( empty( $post_types ) || ! in_array( $post_type, $post_types, true ) ) {
return new \WP_REST_Response( [ 'message' => 'The post type is not set to be scanned.' ], 400 );
}
Expand Down Expand Up @@ -320,7 +319,7 @@ public function set_post_scan_results( $request ) {
}

$post_type = get_post_type( $post );
$post_types = Helpers::get_option_as_array( 'edac_post_types' );
$post_types = Settings::get_scannable_post_types();
if ( empty( $post_types ) || ! in_array( $post_type, $post_types, true ) ) {

return new \WP_REST_Response( [ 'message' => 'The post type is not set to be scanned.' ], 400 );
Expand Down
49 changes: 42 additions & 7 deletions includes/helper-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* @package Accessibility_Checker
*/

use EDAC\Admin\Settings;

/**
* Compare strings
*
Expand Down Expand Up @@ -408,7 +410,7 @@ function edac_get_posts_count() {

$output = [];

$post_types = get_option( 'edac_post_types' );
$post_types = Settings::get_scannable_post_types();
if ( $post_types ) {
foreach ( $post_types as $post_type ) {

Expand Down Expand Up @@ -767,22 +769,27 @@ function edac_generate_landmark_link( $landmark, $landmark_selector, $post_id, $
}
$landmark = ucwords( $landmark );
$landmark = esc_html( $landmark );

// If we have both landmark and selector, create a link.
if ( ! empty( $landmark_selector ) ) {
$link = apply_filters(
'edac_get_origin_url_for_virtual_page',
$post_id
);

Comment on lines +773 to +780

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use permalink as the filter’s default value and document the hook; remove type ambiguity and simplify fallback

Passing $post_id as the value to be filtered is non-standard for apply_filters and introduces type ambiguity (int vs string). Provide the permalink as the default value, pass useful context, and keep a defensive fallback. Also, per coding guidelines, document custom hooks with clear descriptions and parameter types.

Apply this diff:

-		$link = apply_filters(
-			'edac_get_origin_url_for_virtual_page',
-			$post_id
-		);
+		$base_link = get_the_permalink( $post_id );
+
+		/**
+		 * Filter the base URL used for "View on page"/landmark links.
+		 *
+		 * Allows mapping virtual/ghost posts to their canonical origin URL (e.g., archives/taxonomies).
+		 *
+		 * @since 1.x.x
+		 *
+		 * @param string $base_link         Default base URL (permalink for $post_id).
+		 * @param int    $post_id           Post ID represented by the link (may be a virtual/ghost post).
+		 * @param string $landmark_selector Unencoded CSS selector for the landmark being highlighted.
+		 * @param string $landmark          Human-readable landmark label.
+		 *
+		 * @return string Filtered base URL.
+		 */
+		$link = apply_filters( 'edac_get_origin_url_for_virtual_page', $base_link, $post_id, $landmark_selector, $landmark );
+		if ( ! is_string( $link ) || '' === $link ) {
+			$link = $base_link;
+		}
@@
-			is_string( $link ) ? $link : get_the_permalink( $post_id )
+			$link

Note: The hook name follows the edac_ prefix per team conventions (good). The added docblock aligns with “Document all custom hooks” guidance.

Also applies to: 783-784

🤖 Prompt for AI Agents
In includes/helper-functions.php around lines 773–777 (and similarly at
783–784), the filter call currently passes $post_id as the value which creates a
type ambiguity; change the apply_filters usage to use the permalink (e.g.
get_permalink($post_id) or a computed $permalink) as the default value and pass
$post_id as an additional context argument, keep a defensive fallback to a
string (e.g. get_permalink($post_id) ?? home_url('/')), and add a docblock
immediately above the filter showing the hook name, description, and
parameter/return types (string $url, int $post_id): describe that it filters the
origin URL for a virtual page and returns a string URL.

$landmark_url = add_query_arg(
[
'edac_landmark' => base64_encode( $landmark_selector ),
'edac_nonce' => wp_create_nonce( 'edac_highlight' ),
],
get_the_permalink( $post_id )
is_string( $link ) ? $link : get_the_permalink( $post_id )
);

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

$target_attr = $target_blank ? ' target="_blank"' : '';

return sprintf(
'<a href="%s" class="%s"%s aria-label="%s">%s</a>',
esc_url( $landmark_url ),
Expand All @@ -792,7 +799,35 @@ function edac_generate_landmark_link( $landmark, $landmark_selector, $post_id, $
$landmark
);
}

// If we only have landmark text, return it formatted.
return $landmark;
}

/**
* Check if a post is a virtual page.
*
* This function checks if a post is a virtual page using the pro plugin's
* VirtualItemType:POST_TYPE constant.
*
* @param int $post_id The post ID to check.
* @return bool True if the post is a virtual page, false otherwise.
*/
function edac_is_virtual_page( $post_id ) {
if ( class_exists( '\EqualizeDigital\AccessibilityCheckerPro\VirtualContent\PostType\VirtualItemType' ) ) {
$post_type = get_post_type( $post_id );
$pro_post_type = \EqualizeDigital\AccessibilityCheckerPro\VirtualContent\PostType\VirtualItemType::POST_TYPE;
return $pro_post_type === $post_type;
}

return false;
}

Comment thread
pattonwebz marked this conversation as resolved.
/**
* Check if the Pro version of the plugin is active.
*
* @return bool True if Pro version is active, false otherwise.
*/
function edac_is_pro() {
return defined( 'EDACP_VERSION' ) && defined( 'EDAC_KEY_VALID' ) && EDAC_KEY_VALID;
}
Loading