0) { $p = get_post($id); if ($p && $p->post_type === 'page' && $p->post_status !== 'trash') { return self::describe($p, 'option'); } } $ids = get_posts(array( 'post_type' => 'page', 'post_status' => array('publish', 'draft', 'pending', 'private'), 'meta_key' => self::META_KEY, 'posts_per_page' => 1, 'fields' => 'ids', )); if (!empty($ids)) { $p = get_post((int) $ids[0]); if ($p) { return self::describe($p, 'meta'); } } $p = get_page_by_path(self::SLUG, OBJECT, 'page'); if ($p && $p->post_status !== 'trash') { return self::describe($p, 'slug'); } $found = get_posts(array( 'post_type' => 'page', 'post_status' => array('publish', 'draft'), 's' => 'accessibility statement', 'posts_per_page' => 5, )); foreach ($found as $cand) { // Needs both words: "Accessibility Services" is not a statement. if (preg_match('/accessib/i', $cand->post_title) && preg_match('/statement/i', $cand->post_title)) { return self::describe($cand, 'title'); } } return null; } private static function describe($p, $source) { return array( 'id' => (int) $p->ID, 'url' => (string) get_permalink($p), 'status' => (string) $p->post_status, 'source' => $source, 'title' => (string) $p->post_title, ); } /** * The statement kept in the AllAccessible dashboard, fetched fresh * (the on-page menu reads the same endpoint). '' when the account has none. */ public static function statement_html() { if (!class_exists('AllAccessible_ApiClient')) { return ''; } $client = AllAccessible_ApiClient::get_instance(); if (!method_exists($client, 'get_statement_html')) { return ''; } return wp_kses_post((string) $client->get_statement_html(true)); } /** * "Tell us about a problem" block from the assigned contact details. * Empty string when neither email nor phone is set. * * @param array $c contact_email, contact_phone */ public static function contact_block(array $c) { $email = sanitize_email(isset($c['contact_email']) ? $c['contact_email'] : ''); $phone = sanitize_text_field(isset($c['contact_phone']) ? $c['contact_phone'] : ''); if ($email === '' && $phone === '') { return ''; } $contact = $email !== '' ? sprintf('%1$s', esc_attr($email)) : ''; if ($phone !== '') { $contact .= ($contact !== '' ? ' ' . esc_html__('or call', 'allaccessible') . ' ' : '') . esc_html($phone); } return '

' . esc_html__('Accessibility contact', 'allaccessible') . '

' . '

' . esc_html__('If something on this site is hard to use, please let us know', 'allaccessible') . ': ' . $contact . '. ' . esc_html__('Include the page address and what went wrong.', 'allaccessible') . '

'; } /** * Create the page: dashboard statement + contact block, as an ordinary * WordPress page the customer edits afterwards. Registration with * AllAccessible happens in the browser; PHP only reports the URL. * * @param array $a contact_email, contact_phone, publish (bool) * @return array|WP_Error describe() of the new page */ public static function create_page(array $a) { $publish = !empty($a['publish']); $contact = array( 'contact_email' => sanitize_email(isset($a['contact_email']) ? $a['contact_email'] : ''), 'contact_phone' => sanitize_text_field(isset($a['contact_phone']) ? $a['contact_phone'] : ''), ); update_option(self::OPTION_CONTACT, $contact, false); $body = self::statement_html(); if (trim(strip_tags($body)) === '') { $body = '

' . esc_html__('Accessibility Statement', 'allaccessible') . '

'; } $id = wp_insert_post(array( 'post_type' => 'page', 'post_status' => $publish ? 'publish' : 'draft', 'post_title' => __('Accessibility statement', 'allaccessible'), 'post_name' => self::SLUG, 'post_content' => $body . self::contact_block($contact), ), true); if (is_wp_error($id)) { return $id; } update_post_meta($id, self::META_KEY, 1); update_option(self::OPTION_PAGE_ID, (int) $id, false); return self::describe(get_post($id), 'option'); } /** * Replace the statement text on a page we created with the current wording * from the dashboard. The contact block is rebuilt from the saved contact * details; WordPress keeps the previous body as a revision. * * @return array|WP_Error describe() of the page */ public static function refresh_page() { $s = self::status(); if (!$s['page'] || !in_array($s['page']['source'], array('option', 'meta'), true)) { return new WP_Error('aacb_not_owned', __('Only a page created by the plugin can be refreshed.', 'allaccessible')); } $body = self::statement_html(); if (trim(strip_tags($body)) === '') { return new WP_Error('aacb_statement_empty', __('Could not fetch the current statement wording. Try again in a minute.', 'allaccessible')); } $contact = get_option(self::OPTION_CONTACT, array()); $contact = is_array($contact) ? $contact : array(); $id = wp_update_post(array('ID' => (int) $s['page']['id'], 'post_content' => $body . self::contact_block($contact)), true); if (is_wp_error($id)) { return $id; } return self::describe(get_post((int) $id), 'option'); } /** * Use a page the site already has as its statement page. * * @return array|WP_Error */ public static function adopt_existing($post_id) { $p = get_post((int) $post_id); if (!$p || $p->post_type !== 'page') { return new WP_Error('aacb_no_page', __('That page could not be found.', 'allaccessible')); } update_post_meta($p->ID, self::META_KEY, 1); update_option(self::OPTION_PAGE_ID, (int) $p->ID, false); return self::describe($p, 'option'); } /** * Server-side registration, used only by on_status_change() (no browser * there). Empty string clears it. * * @return true|WP_Error */ public static function register_url($url) { $url = (string) $url; if (!class_exists('AllAccessible_ApiClient')) { return new WP_Error('aacb_no_client', 'API client unavailable'); } $r = AllAccessible_ApiClient::get_instance()->save_site_options(array('statementUrl' => $url)); if ($r === true) { update_option(self::OPTION_REGISTERED, $url, false); } return $r; } /** Keep the registration honest when our page is published, unpublished or trashed. */ public static function on_status_change($new_status, $old_status, $post) { if (!$post || $post->post_type !== 'page') return; if ((int) $post->ID !== (int) get_option(self::OPTION_PAGE_ID)) return; if ($new_status === $old_status) return; if ($new_status === 'publish') { self::register_url(get_permalink($post)); } elseif ($old_status === 'publish') { self::register_url(''); } } /** Everything the card, the checklist and the wizard need, in one array. */ public static function status() { $page = self::detect_existing_page(); $registered = (string) get_option(self::OPTION_REGISTERED); return array( 'page' => $page, 'published' => (bool) ($page && $page['status'] === 'publish'), 'registered' => (bool) ($page && $registered !== '' && $registered === $page['url']), 'footer' => (bool) get_option(self::OPTION_FOOTER), 'edit_url' => ($page && function_exists('get_edit_post_link')) ? (string) get_edit_post_link($page['id'], 'raw') : '', ); } /** Label the page in the Pages list. */ public static function post_state($states, $post) { if ($post && (int) $post->ID === (int) get_option(self::OPTION_PAGE_ID)) { $states['aacb_statement'] = __('Accessibility statement', 'allaccessible'); } return $states; } /** Optional plain footer link, for themes without a statement link. */ public static function print_footer_link() { if (!get_option(self::OPTION_FOOTER)) return; $page = self::detect_existing_page(); if (!$page || $page['status'] !== 'publish') return; echo '' . "\n"; } /* ───────────────────────── Admin guide (AJAX + UI) ───────────────────────── */ /** AJAX: create | adopt | registered | footer | status. Admin-only, nonce-checked. */ public static function ajax_handle() { if (!current_user_can('manage_options')) { wp_send_json_error(array('message' => __('Unauthorized', 'allaccessible')), 403); } check_ajax_referer(self::AJAX_ACTION, '_wpnonce'); $do = isset($_POST['do']) ? sanitize_key($_POST['do']) : ''; switch ($do) { case 'create': $r = self::create_page(array( 'contact_email' => isset($_POST['contact_email']) ? sanitize_email(wp_unslash($_POST['contact_email'])) : '', 'contact_phone' => isset($_POST['contact_phone']) ? sanitize_text_field(wp_unslash($_POST['contact_phone'])) : '', 'publish' => isset($_POST['publish']) && (string) $_POST['publish'] === '1', )); break; case 'adopt': $r = self::adopt_existing(isset($_POST['page_id']) ? (int) $_POST['page_id'] : 0); break; case 'refresh': $r = self::refresh_page(); break; case 'registered': // the browser posted statementUrl to the app and got 200 $url = isset($_POST['url']) ? esc_url_raw(wp_unslash($_POST['url'])) : ''; update_option(self::OPTION_REGISTERED, $url, false); $r = array('registered' => $url); break; case 'footer': $on = isset($_POST['on']) && (string) $_POST['on'] === '1'; update_option(self::OPTION_FOOTER, $on, false); $r = array('footer' => $on); break; case 'status': $r = array(); break; default: $r = new WP_Error('aacb_bad_action', __('Unknown action', 'allaccessible')); } if (is_wp_error($r)) { wp_send_json_error(array('message' => $r->get_error_message())); } wp_send_json_success(array_merge(self::status(), array('result' => $r))); } /** * The guide. $context = 'dashboard' | 'wizard'. Three states: * a page we do not own was found → offer to use it or create a new one * a page we own exists → status, link-it, footer toggle, edit * nothing → two questions (contact, publish/draft) * Everything here is admin-initiated; nothing runs on page load. */ public static function render_guide($context = 'dashboard') { $s = self::status(); $nonce = wp_create_nonce(self::AJAX_ACTION); $owned = $s['page'] && ($s['page']['source'] === 'option' || $s['page']['source'] === 'meta'); $found = $s['page'] && !$owned; $saved = get_option(self::OPTION_CONTACT, array()); $saved = is_array($saved) ? $saved : array(); ?>
post_date)) : ''; $f_author = ($fp && $fp->post_author) ? get_the_author_meta('display_name', (int) $fp->post_author) : ''; ?>

· ·

onsubmit="return false;">

__('Working…', 'allaccessible'), 'failed' => __('Something went wrong. Please try again.', 'allaccessible'), 'unlinked' => __('Page saved, but AllAccessible could not be told about it yet. Use “Link it now” on the Dashboard.', 'allaccessible'), 'dashboard' => admin_url('admin.php?page=allaccessible#aacb-statement-card'), ); ?>