true ), 'objects' );
unset( $types['attachment'] );
return array_filter(
$types,
function ( $type ) {
return is_post_type_viewable( $type );
}
);
}
/**
* Content types written by visitors rather than the site's own editors
* (forums, Q&A, user-submitted listings). Visitors could plant
* misleading text or instructions there, so they get a warning.
*
* @return string[]
*/
public static function visitor_written_types() {
/**
* Filters the post types AskMyPages marks as written by visitors.
*
* @param string[] $types Post type names.
*/
return (array) apply_filters(
'askmypages_visitor_written_types',
array( 'forum', 'topic', 'reply', 'question', 'answer', 'dwqa-question', 'dwqa-answer', 'job_listing', 'resume', 'wpdiscuz', 'testimonial' )
);
}
/**
* Saves which content goes into search, then runs a full check so newly
* included items are sent and excluded ones removed.
*/
public static function handle_content() {
self::guard( 'askmypages_content' );
// phpcs:disable WordPress.Security.NonceVerification.Missing -- checked in guard().
$posted = isset( $_POST['post_types'] ) ? array_map( 'sanitize_key', (array) wp_unslash( $_POST['post_types'] ) ) : array();
$terms = isset( $_POST['exclude_terms'] ) ? array_map( 'absint', (array) wp_unslash( $_POST['exclude_terms'] ) ) : array();
// phpcs:enable WordPress.Security.NonceVerification.Missing
$types = array_values( array_intersect( $posted, array_keys( self::selectable_types() ) ) );
update_option( Sync::OPT_POST_TYPES, $types, false );
update_option( Sync::OPT_PDFS, isset( $_POST['pdfs'] ) ? '1' : '0', false ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- checked in guard().
update_option( Sync::OPT_EXCLUDE_TERMS, array_values( array_filter( $terms ) ), false );
if ( Keys::is_connected() ) {
as_enqueue_async_action( Sync::HOOK_RECONCILE, array(), Sync::GROUP );
}
delete_transient( 'askmypages_status' );
self::back( 'content_saved' );
}
/**
* "Leave out of search" box on the edit screen of indexed types.
*
* @param string $post_type Post type of the screen.
*/
public static function add_exclude_box( $post_type = '' ) {
foreach ( Sync::post_types() as $type ) {
add_meta_box( 'askmypages-exclude', 'AskMyPages', array( __CLASS__, 'render_exclude_box' ), $type, 'side', 'low' );
}
// PDFs, on their Media Library edit screen.
$post = get_post();
if ( 'attachment' === $post_type && Sync::pdfs_enabled() && $post && 'application/pdf' === $post->post_mime_type ) {
add_meta_box( 'askmypages-exclude', 'AskMyPages', array( __CLASS__, 'render_exclude_box' ), 'attachment', 'side', 'low' );
}
}
/**
* Renders the box: exclude from the index, or mark as members-only
* (with roles) when members-only content is on.
*
* @param \WP_Post $post Post.
*/
public static function render_exclude_box( $post ) {
wp_nonce_field( 'askmypages_exclude', 'askmypages_exclude_nonce' );
printf(
'
%s
',
checked( '1', (string) get_post_meta( $post->ID, Sync::META_EXCLUDE, true ), false ),
esc_html__( 'Exclude from AskMyPages', 'askmypages' )
);
if ( ! Access::members_enabled() ) {
return;
}
$vis = (string) get_post_meta( $post->ID, Access::META_VISIBILITY, true );
$roles = array_map( 'sanitize_key', (array) get_post_meta( $post->ID, Access::META_ROLES, true ) );
printf(
' %s
',
checked( true, in_array( $vis, array( 'members', 'roles' ), true ), false ),
esc_html__( 'Members only', 'askmypages' )
);
echo '';
foreach ( wp_roles()->get_names() as $role => $role_name ) {
printf(
' %s ',
esc_attr( $role ),
checked( in_array( $role, $roles, true ), true, false ),
esc_html( translate_user_role( $role_name ) )
);
}
echo '
';
echo '' . esc_html__( 'Members only: answered from for logged-in users with these roles (none ticked: any logged-in user).', 'askmypages' ) . '
';
}
/**
* Saves the box and updates the index for this post.
*
* @param int $post_id Post id.
* @param \WP_Post $post Post.
*/
public static function save_exclude_box( $post_id, $post ) {
if ( ! isset( $_POST['askmypages_exclude_nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['askmypages_exclude_nonce'] ) ), 'askmypages_exclude' ) ) {
return;
}
if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
if ( isset( $_POST['askmypages_exclude'] ) ) {
update_post_meta( $post_id, Sync::META_EXCLUDE, '1' );
} else {
delete_post_meta( $post_id, Sync::META_EXCLUDE );
}
if ( isset( $_POST['askmypages_members'] ) ) {
$roles = isset( $_POST['askmypages_roles'] ) ? array_map( 'sanitize_key', (array) wp_unslash( $_POST['askmypages_roles'] ) ) : array();
$roles = array_values( array_intersect( $roles, array_keys( wp_roles()->get_names() ) ) );
update_post_meta( $post_id, Access::META_VISIBILITY, $roles ? 'roles' : 'members' );
update_post_meta( $post_id, Access::META_ROLES, $roles );
} else {
delete_post_meta( $post_id, Access::META_VISIBILITY );
delete_post_meta( $post_id, Access::META_ROLES );
}
if ( ! Keys::is_connected() ) {
return;
}
if ( Sync::is_eligible( $post ) ) {
Sync::queue( Sync::HOOK_SYNC, $post_id );
} elseif ( get_post_meta( $post_id, Sync::META_HASH, true ) ) {
Sync::queue( Sync::HOOK_DELETE, $post_id );
}
}
/**
* Content tab: what goes into search.
*
* @param array|null $status Status from the service, if available.
*/
private static function tab_content( $status ) {
$selected = Sync::post_types();
$allowed = Sync::custom_types_allowed();
$excluded = Sync::excluded_terms();
$total = 0;
self::render_counts( $status );
self::render_skipped_pdfs();
?>
>
|
>
' . esc_html__( 'The list could not be loaded right now. Check the Connection tab.', 'askmypages' ) . '
';
return;
}
$items = (array) ( $result['items'] ?? array() );
$total = (int) ( $result['total'] ?? 0 );
$per = max( 1, (int) ( $result['perPage'] ?? 20 ) );
if ( ! $items ) {
echo '' . esc_html( $problems ? __( 'No problems: everything sent is in search.', 'askmypages' ) : __( 'Nothing has been sent yet.', 'askmypages' ) ) . '
';
return;
}
?>
labels->singular_name : (string) ( $item['postType'] ?? '' ) );
}
?>
1 ) {
$link = static function ( $n ) use ( $base, $problems ) {
$args = array( 'items_page' => $n );
if ( $problems ) {
$args['items'] = 'problems';
}
return add_query_arg( $args, $base ) . '#askmypages-items';
};
echo '';
}
?>
'attachment',
'post_status' => 'inherit',
'meta_key' => Sync::META_PDF_ISSUE, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- small admin-only query.
'posts_per_page' => 50,
'fields' => 'ids',
)
);
if ( ! $skipped ) {
return;
}
$reasons = array(
'pdf_pages_exceeded' => __( 'over the plan\'s PDF page allowance (Free: 150 pages in total; Starter: 2,500; Business: 10,000)', 'askmypages' ),
'pdf_bytes_exceeded' => __( 'over the plan\'s PDF storage allowance (Free: 50 MB in total; Starter: 500 MB; Business: 2 GB)', 'askmypages' ),
'no_text' => __( 'no readable text (scanned?)', 'askmypages' ),
'invalid_pdf' => __( 'could not be read', 'askmypages' ),
'quota_exceeded' => __( 'over the plan\'s item limit', 'askmypages' ),
);
echo '' . esc_html__( 'Some PDFs are not in search:', 'askmypages' ) . '
';
foreach ( $skipped as $id ) {
$issue = (string) get_post_meta( $id, Sync::META_PDF_ISSUE, true );
printf( '%s: %s ', esc_html( get_the_title( $id ) ), esc_html( $reasons[ $issue ] ?? $issue ) );
}
echo ' ';
}
/**
* The menu icon: the AskMyPages mark in one colour, which WordPress
* recolours to match the admin colour scheme.
*
* @return string Data URI.
*/
private static function menu_icon() {
$svg = file_get_contents( dirname( __DIR__ ) . '/assets/askmypages-mark-mono.svg' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- local plugin file.
return 'data:image/svg+xml;base64,' . base64_encode( (string) $svg ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- data URI for the menu icon.
}
/**
* Adds the AskMyPages menu, with a submenu entry per tab.
*/
public static function menu() {
$hook = add_menu_page( 'AskMyPages', 'AskMyPages', 'manage_options', self::PAGE, array( __CLASS__, 'render' ), self::menu_icon(), 81 );
add_action( 'load-' . $hook, array( __CLASS__, 'handle_return' ) );
if ( ! Keys::is_connected() ) {
return;
}
foreach ( self::tabs() as $tab => $label ) {
add_submenu_page( self::PAGE, 'AskMyPages', $label, 'manage_options', 'overview' === $tab ? self::PAGE : self::PAGE . '&tab=' . $tab, '__return_null' );
}
}
/**
* Highlights the submenu entry of the tab being viewed.
*
* @param string|null $submenu_file Current submenu file.
* @param string $parent_file Parent menu.
* @return string|null
*/
public static function current_submenu( $submenu_file, $parent_file ) {
if ( self::PAGE !== $parent_file ) {
return $submenu_file;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display only.
$tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'overview';
return array_key_exists( $tab, self::tabs() ) && 'overview' !== $tab ? self::PAGE . '&tab=' . $tab : self::PAGE;
}
/**
* The page used to live under Settings; links and in-progress
* connections that still use that address land on the new one.
*/
public static function redirect_old_url() {
global $pagenow;
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- a redirect only; the target page does every check (state for connections, nonces for actions).
if ( 'options-general.php' !== $pagenow || ! isset( $_GET['page'] ) || self::PAGE !== $_GET['page'] ) {
return;
}
$query = array();
foreach ( array( 'tab', 'code', 'state', 'error', 'askmypages_notice' ) as $key ) {
if ( isset( $_GET[ $key ] ) ) {
$query[ $key ] = sanitize_text_field( wp_unslash( $_GET[ $key ] ) );
}
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
wp_safe_redirect( add_query_arg( $query, Connect::return_url() ) );
exit;
}
/**
* The portal sends the owner back here with ?code=&state= (or ?error=).
* The state check is the CSRF protection for this request.
*/
public static function handle_return() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- verified by the one-time state instead of a nonce.
if ( isset( $_GET['error'] ) ) {
// Only a matching state cancels; anything else is ignored.
$state = isset( $_GET['state'] ) ? sanitize_text_field( wp_unslash( $_GET['state'] ) ) : '';
if ( Connect::cancel( $state ) ) {
self::back( 'cancelled' );
}
return;
}
if ( ! isset( $_GET['code'], $_GET['state'] ) ) {
return;
}
$code = sanitize_text_field( wp_unslash( $_GET['code'] ) );
$state = sanitize_text_field( wp_unslash( $_GET['state'] ) );
// phpcs:enable WordPress.Security.NonceVerification.Recommended
$result = Connect::complete( $code, $state );
if ( is_wp_error( $result ) ) {
set_transient( 'askmypages_notice_error', $result->get_error_message(), MINUTE_IN_SECONDS );
self::back( 'error' );
}
self::back( 'connected' );
}
/**
* Starts connecting.
*/
public static function handle_connect() {
self::guard( 'askmypages_connect' );
wp_redirect( Connect::start_url() ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- the portal is on another host by design.
exit;
}
/**
* Tests the connection with a signed request.
*/
public static function handle_test() {
self::guard( 'askmypages_test' );
$result = Api::signed( 'GET', '/v1/plugin/site' );
if ( is_wp_error( $result ) ) {
set_transient( 'askmypages_notice_error', $result->get_error_message(), MINUTE_IN_SECONDS );
self::back( 'error' );
}
self::back( 'test_ok' );
}
/**
* Queues a full reconciliation now.
*/
public static function handle_sync() {
self::guard( 'askmypages_sync' );
as_enqueue_async_action( Sync::HOOK_RECONCILE, array(), Sync::GROUP );
delete_transient( 'askmypages_status' );
self::back( 'sync_queued' );
}
/**
* Turns the search box upgrade on or off.
*/
public static function handle_search_toggle() {
self::guard( 'askmypages_search_toggle' );
update_option( Search::OPTION_ENABLED, '1' === (string) get_option( Search::OPTION_ENABLED, '1' ) ? '0' : '1', false );
self::back( 'search_saved' );
}
/**
* Disconnects.
*/
public static function handle_disconnect() {
self::guard( 'askmypages_disconnect' );
Connect::disconnect();
self::back( 'disconnected' );
}
/**
* Capability and nonce check for admin-post actions.
*
* @param string $action Nonce action.
*/
private static function guard( $action ) {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'You do not have permission to do this.', 'askmypages' ), 403 );
}
check_admin_referer( $action );
}
/**
* Redirects back to the settings page with a notice.
*
* @param string $notice Notice key.
*/
private static function back( $notice ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- checked in guard() before any redirect.
$tab = isset( $_POST['tab'] ) ? sanitize_key( wp_unslash( $_POST['tab'] ) ) : 'overview';
$tab = array_key_exists( $tab, self::tabs() ) ? $tab : 'overview';
wp_safe_redirect( add_query_arg( 'askmypages_notice', $notice, self::tab_url( $tab ) ) );
exit;
}
/**
* Renders a form button for an admin-post action.
*
* @param string $action Action name.
* @param string $label Button label.
* @param string $css Button class.
* @param string $tab Tab to return to.
* @param string $confirm Optional confirmation question.
*/
private static function button( $action, $label, $css = 'button', $tab = 'overview', $confirm = '' ) {
?>
' . esc_html__( 'Could not load counts from the service right now.', 'askmypages' ) . '
';
return;
}
$counts = isset( $status['counts'] ) && is_array( $status['counts'] ) ? $status['counts'] : array();
$labels = array(
'INDEXED' => __( 'Searchable', 'askmypages' ),
'PENDING' => __( 'Being prepared', 'askmypages' ),
'NO_TEXT' => __( 'No readable text', 'askmypages' ),
'FAILED' => __( 'Failed', 'askmypages' ),
);
echo '';
foreach ( $labels as $key => $label ) {
if ( 'INDEXED' !== $key && empty( $counts[ $key ] ) ) {
continue;
}
printf( '
%s %s
', esc_html( number_format_i18n( (int) ( $counts[ $key ] ?? 0 ) ) ), esc_html( $label ) );
}
echo '
';
$last = (int) get_option( 'askmypages_last_reconcile' );
if ( $last ) {
echo '' . esc_html(
sprintf(
/* translators: %s: how long ago, e.g. "5 mins". */
__( 'Last full check: %s ago.', 'askmypages' ),
human_time_diff( $last )
)
) . '
';
}
}
/**
* A usage meter.
*
* @param int $used Used.
* @param int $limit Limit.
* @param string $label What is counted.
*/
private static function meter( $used, $limit, $label ) {
$ratio = $limit > 0 ? min( 1, $used / $limit ) : 0;
$class = $ratio >= 1 ? ' is-full' : ( $ratio >= 0.8 ? ' is-warn' : '' );
printf(
'
%s
',
esc_attr( $class ),
esc_attr( sprintf( '%s: %s / %s', $label, number_format_i18n( $used ), number_format_i18n( $limit ) ) ),
esc_attr( (string) round( $ratio * 100 ) ),
esc_html(
sprintf(
/* translators: 1: used, 2: limit, 3: what is counted, e.g. "items on your plan". */
__( '%1$s of %2$s %3$s', 'askmypages' ),
number_format_i18n( $used ),
number_format_i18n( $limit ),
$label
)
)
);
}
/**
* Tab names and labels.
*
* @return array
*/
private static function tabs() {
return array(
'overview' => __( 'Overview', 'askmypages' ),
'content' => __( 'Content', 'askmypages' ),
'search' => __( 'Search', 'askmypages' ),
'chat' => __( 'Chat', 'askmypages' ),
'access' => __( 'Access', 'askmypages' ),
'connection' => __( 'Connection', 'askmypages' ),
);
}
/**
* The settings page URL for a tab.
*
* @param string $tab Tab.
* @return string
*/
public static function tab_url( $tab ) {
return 'overview' === $tab ? Connect::return_url() : add_query_arg( 'tab', $tab, Connect::return_url() );
}
/**
* Loads the page's stylesheet.
*
* @param string $hook Admin page hook.
*/
public static function assets( $hook ) {
if ( 'toplevel_page_' . self::PAGE !== $hook ) {
return;
}
wp_enqueue_style( 'askmypages-admin', plugins_url( 'assets/admin.css', __DIR__ ), array(), ASKMYPAGES_VERSION );
// The Media Library picker for the assistant's picture (Chat tab).
wp_enqueue_media();
wp_enqueue_script( 'askmypages-admin', plugins_url( 'assets/admin.js', __DIR__ ), array( 'media-editor' ), ASKMYPAGES_VERSION, array( 'in_footer' => true ) );
wp_localize_script(
'askmypages-admin',
'AskMyPagesAdmin',
array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'askmypages_try' ),
'i18n' => array(
'thinking' => __( 'Looking through your site…', 'askmypages' ),
'sources' => __( 'Sources', 'askmypages' ),
'failed' => __( 'Something went wrong. Please try again.', 'askmypages' ),
/* translators: 1: test answers left, 2: test answers per month. */
'left' => __( '%1$s of %2$s test answers left this month.', 'askmypages' ),
'errors' => array(
'quota_exceeded' => __( 'This month\'s test answers are used up. They come back next month.', 'askmypages' ),
'budget_exhausted' => __( 'AI answers are paused for this month.', 'askmypages' ),
'unavailable' => __( 'The answer service is busy. Please try again.', 'askmypages' ),
),
),
)
);
}
/**
* One request for a WordPress.org review, on this plugin's own page only,
* once the site's visitors have had 25 answers this month. Shown until
* the owner clicks either button.
*
* @param int $answers AI answers used this month.
*/
private static function review_request( $answers ) {
if ( $answers < 25 || get_option( 'askmypages_review' ) || ! current_user_can( 'manage_options' ) ) {
return;
}
?>
id, array( 'plugins', 'dashboard' ), true ) ) {
return;
}
if ( ! get_option( 'askmypages_welcome' ) || Keys::is_connected() || ! current_user_can( 'manage_options' ) ) {
return;
}
?>
true,
'title' => __( 'Connect this site', 'askmypages' ),
'text' => __( 'Connected to your Hook & Plug account.', 'askmypages' ),
'link' => null,
),
array(
'done' => $ready > 0 && 0 === $waiting,
'title' => __( 'Prepare your content', 'askmypages' ),
'text' => $waiting
/* translators: 1: items ready, 2: items in total. */
? sprintf( __( '%1$s of %2$s items ready. This runs in the background; you can carry on.', 'askmypages' ), number_format_i18n( $ready ), number_format_i18n( $ready + $waiting ) )
/* translators: %s: items ready. */
: sprintf( __( '%s items ready for search and answers.', 'askmypages' ), number_format_i18n( $ready ) ),
'link' => array( self::tab_url( 'content' ), __( 'Choose content', 'askmypages' ) ),
),
array(
'done' => Search::enabled(),
'title' => __( 'Upgrade your search box', 'askmypages' ),
'text' => Search::enabled() ? __( 'Your search box now finds pages by meaning as well as exact words.', 'askmypages' ) : __( 'Turn on the search upgrade.', 'askmypages' ),
'link' => Search::enabled() ? array( home_url( '/?s=' ), __( 'Try it on your site', 'askmypages' ) ) : array( self::tab_url( 'search' ), __( 'Turn on', 'askmypages' ) ),
),
array(
'done' => true,
'title' => __( 'Chat for visitors', 'askmypages' ),
'text' => ! empty( $chat['settings']['enabled'] )
? __( 'On by default: a button on every page answers questions from your content, with links to the sources. Turn it off in Chat settings if you are not ready.', 'askmypages' )
: __( 'Off. Turn it on in Chat settings when you are ready.', 'askmypages' ),
'link' => array( self::tab_url( 'chat' ), __( 'Chat settings', 'askmypages' ) ),
),
array(
'done' => ! empty( $usage['tryUsed'] ) || ! empty( $usage['answersUsed'] ),
'title' => __( 'Ask a test question', 'askmypages' ),
'text' => __( 'See an answer from your own content before visitors do.', 'askmypages' ),
'link' => array( self::tab_url( 'chat' ), __( 'Try it', 'askmypages' ) ),
),
);
if ( ! in_array( false, wp_list_pluck( $steps, 'done' ), true ) ) {
update_option( 'askmypages_setup_done', '1', false );
return;
}
?>
= $cap ) {
$limit_notes[] = __( 'Your plan\'s item limit is reached, so new and changed content is not being added to search.', 'askmypages' );
} elseif ( $cap && $items >= 0.9 * $cap ) {
$limit_notes[] = __( 'Your plan\'s item limit is almost reached. After that, new content is not added to search.', 'askmypages' );
}
if ( $notes || $limit_notes ) {
echo '';
foreach ( $notes as $note ) {
printf( '
', esc_html( $note ) );
}
foreach ( $limit_notes as $note ) {
printf( '
', esc_html( $note ), wp_kses_post( self::upgrade_link() ) );
}
echo '
';
}
self::render_skipped_pdfs();
self::render_setup( $status, $chat );
?>
' . esc_html(
sprintf(
/* translators: %s: reason. */
__( 'In the last hour some searches used WordPress search instead: %s', 'askmypages' ),
$fallback
)
) . '
';
}
?>
array( 'yes-alt', '#00a32a' ),
'recommended' => array( 'warning', '#dba617' ),
'critical' => array( 'dismiss', '#d63638' ),
);
?>
%s', esc_url( admin_url( 'admin.php?page=' . self::PAGE ) ), esc_html__( 'Settings', 'askmypages' ) );
array_unshift( $links, $settings );
if ( Keys::is_connected() && 'FREE' === ( Chat::data()['plan'] ?? '' ) ) {
$links[] = sprintf( '%s ', esc_url( self::upgrade_url() ), esc_html__( 'Upgrade to Starter', 'askmypages' ) );
}
return $links;
}
/**
* Where upgrades happen: the AskMyPages page of the Hook & Plug account,
* with checkout and plan management.
*
* @return string
*/
public static function upgrade_url() {
return ASKMYPAGES_APP_URL . '/askmypages?upgrade=1';
}
/**
* A link to the plans, opening in a new tab. Only used on AskMyPages's
* own screens, next to the limit or feature it is about.
*
* @param string $text Link text; "See plans" when empty.
* @param string $css Classes, e.g. "button button-primary" for a button.
* @return string Safe HTML.
*/
public static function upgrade_link( $text = '', $css = '' ) {
return sprintf(
'%s %s ',
esc_url( self::upgrade_url() ),
$css ? ' class="' . esc_attr( $css ) . '"' : '',
esc_html( $text ? $text : __( 'See plans', 'askmypages' ) ),
esc_html__( '(opens in a new tab)', 'askmypages' )
);
}
/**
* On/off status pill.
*
* @param bool $on On.
*/
private static function pill( $on ) {
printf( '%s ', $on ? 'is-ok' : 'is-off', esc_html( $on ? __( 'On', 'askmypages' ) : __( 'Off', 'askmypages' ) ) );
}
/**
* Renders the page.
*/
public static function render() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- display only.
$notice = isset( $_GET['askmypages_notice'] ) ? sanitize_key( wp_unslash( $_GET['askmypages_notice'] ) ) : '';
$tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'overview';
// phpcs:enable WordPress.Security.NonceVerification.Recommended
$tab = array_key_exists( $tab, self::tabs() ) ? $tab : 'overview';
$messages = array(
'connected' => array( 'success', __( 'Connected. Your content is being prepared for search; this takes a few minutes.', 'askmypages' ) ),
'test_ok' => array( 'success', __( 'Connection works.', 'askmypages' ) ),
'disconnected' => array( 'success', __( 'Disconnected. The keys on this site were deleted.', 'askmypages' ) ),
'cancelled' => array( 'warning', __( 'Connecting was cancelled.', 'askmypages' ) ),
'search_saved' => array( 'success', __( 'Search setting saved.', 'askmypages' ) ),
'sync_queued' => array( 'success', __( 'Sync started. It runs in the background; counts update within a few minutes.', 'askmypages' ) ),
'reindex_queued' => array( 'success', __( 'Re-index started. It runs in the background; reload this page in a minute to see the new status.', 'askmypages' ) ),
'reindex_ineligible' => array( 'warning', __( 'That item is not in your content settings any more (or is no longer public), so it was not sent.', 'askmypages' ) ),
'chat_saved' => array( 'success', __( 'Chat settings saved.', 'askmypages' ) ),
'access_saved' => array( 'success', __( 'Access saved. Items are being checked again in the background.', 'askmypages' ) ),
'health_done' => array( 'success', __( 'Checks run again.', 'askmypages' ) ),
'content_saved' => array( 'success', __( 'Content settings saved. Search is being updated in the background; counts change within a few minutes.', 'askmypages' ) ),
);
// The status call is signed: if the service says the key was revoked
// elsewhere, the keys are dropped during it, so decide "connected" after.
$status = Keys::is_connected() ? Sync::status() : null;
$connected = Keys::is_connected();
$plan = $connected ? ( Chat::data()['plan'] ?? '' ) : '';
$revoked = ! $connected ? (int) get_option( Connect::OPT_REVOKED ) : 0;
?>
', esc_html( $error ? $error : __( 'Something went wrong.', 'askmypages' ) ) );
} elseif ( isset( $messages[ $notice ] ) ) {
printf( '', esc_attr( $messages[ $notice ][0] ), esc_html( $messages[ $notice ][1] ) );
}
?>
$label ) : ?>
>