-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathclass-frontend-highlight.php
More file actions
247 lines (219 loc) · 8.29 KB
/
Copy pathclass-frontend-highlight.php
File metadata and controls
247 lines (219 loc) · 8.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
<?php
/**
* Accessibility Checker plugin file.
*
* @package Accessibility_Checker
*/
namespace EDAC\Admin;
use EqualizeDigital\AccessibilityChecker\Admin\AdminPage\FixesPage;
use EqualizeDigital\AccessibilityChecker\Fixes\FixesManager;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class Frontend_Highlight
*
* A class that handles AJAX requests for frontend highlighting of accessibility issues.
*/
class Frontend_Highlight {
/**
* Constructor function for the class.
*/
public function __construct() {
}
/**
* Initialize hooks.
*
* @return void
*/
public function init_hooks() {
add_action( 'wp_ajax_edac_frontend_highlight_ajax', [ $this, 'ajax' ] );
/**
* Filter the visibility of the frontend highlighter.
*
* 'edac_filter_frontend_highlighter_visibility' is a filter that can be used
* to allow users without edit permissions on the post to see the frontend
* highlighter. You can use the filter to perform additional permission checks
* on who can see it.
*
* @since 1.14.0
*
* @param bool $visibility The visibility of the frontend highlighter. Default is false, return true to show the frontend highlighter.
*/
if ( apply_filters( 'edac_filter_frontend_highlighter_visibility', false ) ) {
// A nopriv endpoint allows logged-out users to access the endpoint.
add_action( 'wp_ajax_nopriv_edac_frontend_highlight_ajax', [ $this, 'ajax' ] );
}
}
/**
* Retrieves accessibility issues for a specific post.
*
* @param int $post_id The ID of the post.
*
* @return array|null The array of issues or null if no issues found.
*/
public function get_issues( $post_id ) {
global $wpdb;
$table_name = $wpdb->prefix . 'accessibility_checker';
$post_id = (int) $post_id;
$siteid = get_current_blog_id();
$results = $wpdb->get_results( $wpdb->prepare( 'SELECT id, rule, ignre, object, ruletype, selector, ancestry, xpath, landmark, landmark_selector, source, extra_data FROM %i where postid = %d and siteid = %d', $table_name, $post_id, $siteid ), ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Safe variable used for table name.
if ( ! $results ) {
return null;
}
return Helpers::filter_results_to_only_active_rules( $results );
}
/**
* AJAX handler function for frontend highlighting requests.
*/
public function ajax() {
if ( ! check_ajax_referer( 'frontend-highlighter', 'nonce', false ) ) {
wp_send_json_error( new \WP_Error( '-1', __( 'Permission Denied', 'accessibility-checker' ) ) );
}
if ( ! isset( $_REQUEST['post_id'] ) ) {
$error = new \WP_Error( '-2', __( 'The id value was not set', 'accessibility-checker' ) );
wp_send_json_error( $error );
}
$post_id = isset( $_REQUEST['post_id'] ) ? (int) $_REQUEST['post_id'] : 0;
$post = get_post( $post_id );
if ( ! $post ) {
wp_send_json_error( new \WP_Error( '-4', __( 'Post not found', 'accessibility-checker' ) ) );
}
// Check if the user has permission to view this post.
if ( is_user_logged_in() ) {
// For authenticated users, use read_post capability.
if ( ! current_user_can( 'read_post', $post_id ) ) {
wp_send_json_error( new \WP_Error( '-1', __( 'Permission Denied', 'accessibility-checker' ) ) );
}
} elseif ( apply_filters( 'edac_filter_frontend_highlighter_visibility', false ) ) {
// For unauthenticated users, only allow access to publicly viewable posts.
if ( ! is_post_publicly_viewable( $post ) ) {
wp_send_json_error( new \WP_Error( '-1', __( 'Permission Denied', 'accessibility-checker' ) ) );
}
} else {
// Shouldn't ever reach this point but error just in case.
wp_send_json_error( new \WP_Error( '-1', __( 'Permission Denied', 'accessibility-checker' ) ) );
}
$results = $this->get_issues( $post_id );
if ( ! $results ) {
wp_send_json_error( new \WP_Error( '-3', __( 'Issue query returned no results', 'accessibility-checker' ) ) );
}
$rules = edac_register_rules();
$issues = [];
$fixes = [];
foreach ( $results as $result ) {
$array = [];
$rule = edac_filter_by_value( $rules, 'slug', $result['rule'] );
// When rules are filtered out, they are not in the rules array and this can be empty. Skip when the rule
// is empty to avoid php warnings and passing null values to the frontend highlighter.
if ( ! $rule ) {
continue;
}
$rule_type = ( true === (bool) $result['ignre'] ) ? 'ignored' : $rule[0]['rule_type'];
$array['rule_type'] = $rule_type;
$array['slug'] = $rule[0]['slug'];
$array['rule_title'] = $rule[0]['title'];
$array['wcag'] = $rule[0]['wcag'] ?? '';
$array['wcag_title'] = $this->get_wcag_title( $rule[0]['wcag'] ?? '' );
$array['summary'] = $rule[0]['summary'];
$array['why_it_matters'] = wp_kses_post( $rule[0]['why_it_matters'] ?? '' );
$array['how_to_fix'] = wp_kses_post( $rule[0]['how_to_fix'] ?? '' );
$array['link'] = edac_link_wrapper( $rule[0]['info_url'], 'frontend-highlighter', $rule[0]['slug'], false );
$array['object'] = html_entity_decode( $result['object'], ENT_QUOTES | ENT_HTML5 );
$array['id'] = $result['id'];
$array['ignored'] = $result['ignre'];
$array['selector'] = $result['selector'] ?? '';
$array['ancestry'] = $result['ancestry'] ?? '';
$array['xpath'] = $result['xpath'] ?? '';
$array['severity'] = $rule[0]['severity'] ?? '';
$array['landmark'] = $result['landmark'] ?? '';
$array['landmark_selector'] = $result['landmark_selector'] ?? '';
$array['source'] = $result['source'] ?? 'automated';
$array['extra_data'] = isset( $result['extra_data'] ) ? json_decode( $result['extra_data'], true ) : null;
$issues[] = $array;
if ( ! isset( $fixes[ $rule[0]['slug'] ] ) ) {
$fixes_for_rule = $rule[0]['fixes'] ?? [];
foreach ( $fixes_for_rule as $fix_for_rule ) {
$fix = FixesManager::get_instance()->get_fix( $fix_for_rule );
if ( $fix && method_exists( $fix, 'get_fields_array' ) ) {
$fixes[ $rule[0]['slug'] ] = isset( $fixes[ $rule[0]['slug'] ] ) ? array_merge( $fixes[ $rule[0]['slug'] ], $fix->get_fields_array() ) : $fix->get_fields_array();
}
}
}
}
if ( ! $issues ) {
wp_send_json_error( new \WP_Error( '-5', __( 'Object query returned no results', 'accessibility-checker' ) ) );
}
// if we have fixes then create fields for each of the groups.
if ( ! empty( $fixes ) ) {
foreach ( $fixes as $key => $fix ) {
// count the number of fields in the fix.
$fields_count = count( $fix );
$itteration = 0;
$fix_fields_markup = '';
foreach ( $fix as $index => $field ) {
++$itteration;
$field_type = $field['type'] ?? 'checkbox';
ob_start();
if ( isset( $field['group_name'] ) ) {
// if this is anything other than the first field in the group then close the fieldset.
if ( 1 !== $itteration ) {
?>
</fieldset>
<?php
}
?>
<fieldset>
<legend><h3 class="title"><?php echo esc_html( $field['group_name'] ); ?></h3></legend>
<?php
}
FixesPage::{$field_type}(
array_merge(
[
'name' => $index,
'location' => 'frontend-highlighter',
],
$field
)
);
if ( $fields_count === $itteration ) {
?>
</fieldset>
<?php
}
$fix_fields_markup .= ob_get_clean();
}
$fixes[ $key ]['fields'] = $fix_fields_markup . PHP_EOL . '</fieldset>';
}
}
wp_send_json_success(
wp_json_encode(
[
'issues' => $issues,
'fixes' => $fixes,
]
)
);
}
/**
* Get the WCAG criterion title for a given WCAG number.
*
* @param string $wcag_number The WCAG number (e.g., '1.1.1').
* @return string The criterion title, or empty string if not found.
*/
private function get_wcag_title( $wcag_number ) {
if ( ! $wcag_number ) {
return '';
}
static $wcag_lookup = null;
if ( null === $wcag_lookup ) {
$wcag_file = EDAC_PLUGIN_DIR . 'includes/wcag.php';
if ( ! file_exists( $wcag_file ) ) {
return '';
}
$wcag_data = include $wcag_file;
$wcag_lookup = is_array( $wcag_data ) ? array_column( $wcag_data, 'title', 'number' ) : [];
}
return $wcag_lookup[ $wcag_number ] ?? '';
}
}