settings_page = new SettingsPage();
}
/**
* Register admin hooks.
*
* @return void
*/
public function boot() {
add_action( 'admin_menu', array( $this, 'register_menu' ) );
add_action( 'admin_init', array( $this, 'register_settings' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
add_action( 'admin_post_akava_btt_reset_settings', array( $this, 'handle_reset' ) );
add_action( 'admin_post_akava_btt_review_prompt', array( $this, 'handle_review_prompt' ) );
add_filter( 'plugin_action_links_' . plugin_basename( AKAVA_BTT_FILE ), array( $this, 'action_links' ) );
add_filter( 'plugin_row_meta', array( $this, 'row_meta' ), 10, 2 );
}
/**
* Register the compact settings array with one sanitization boundary.
*
* @return void
*/
public function register_settings() {
register_setting(
Config::SETTINGS_GROUP,
Config::OPTION,
array(
'type' => 'array',
'sanitize_callback' => array( Config::class, 'sanitize' ),
'default' => Config::defaults(),
)
);
}
/**
* Add the page under Settings to avoid another top-level admin menu.
*
* @return void
*/
public function register_menu() {
add_options_page(
__( 'AKAVA ScrollTop Settings', 'akava-back-to-top' ),
__( 'AKAVA ScrollTop', 'akava-back-to-top' ),
self::CAPABILITY,
self::PAGE_SLUG,
array( $this->settings_page, 'render' )
);
}
/**
* Load admin assets only on this plugin screen.
*
* @param string $hook Current admin page hook.
* @return void
*/
public function enqueue_assets( $hook ) {
if ( 'settings_page_' . self::PAGE_SLUG !== $hook ) {
return;
}
wp_enqueue_media();
wp_enqueue_style(
'akava-btt-admin',
AKAVA_BTT_URL . 'assets/css/admin.css',
array(),
AKAVA_BTT_VERSION
);
wp_enqueue_script(
'akava-btt-admin',
AKAVA_BTT_URL . 'assets/js/admin.js',
array(),
AKAVA_BTT_VERSION,
array(
'in_footer' => true,
'strategy' => 'defer',
)
);
wp_add_inline_script(
'akava-btt-admin',
'window.AkavaBttAdmin=' . wp_json_encode(
array(
'defaults' => Config::defaults(),
'strings' => array(
'chooseIcon' => __( 'Choose a button icon', 'akava-back-to-top' ),
'useIcon' => __( 'Use this image', 'akava-back-to-top' ),
'confirmReset' => __( 'Reset all settings to defaults?', 'akava-back-to-top' ),
'invalidImage' => __( 'Choose an image allowed by your WordPress media library.', 'akava-back-to-top' ),
/* translators: %s: currently previewed device name, e.g. Desktop, Tablet or Mobile. */
'previewDevice' => __( 'Preview device: %s.', 'akava-back-to-top' ),
'defaultLabel' => __( 'Back to top', 'akava-back-to-top' ),
),
)
) . ';',
'before'
);
}
/**
* Reset settings through POST with capability and nonce checks.
*
* @return void
*/
public function handle_reset() {
if ( ! current_user_can( self::CAPABILITY ) ) {
wp_die(
esc_html__( 'You are not allowed to change these settings.', 'akava-back-to-top' ),
esc_html__( 'Access denied', 'akava-back-to-top' ),
array( 'response' => 403 )
);
}
check_admin_referer( 'akava_btt_reset_settings' );
update_option( Config::OPTION, Config::defaults(), true );
Config::flush_cache();
wp_safe_redirect(
add_query_arg(
array(
'page' => self::PAGE_SLUG,
'akava-btt' => 'reset',
),
admin_url( 'options-general.php' )
)
);
exit;
}
/**
* Handle the optional review reminder without collecting user data.
*
* @return void
*/
public function handle_review_prompt() {
if ( ! current_user_can( self::CAPABILITY ) ) {
wp_die(
esc_html__( 'You are not allowed to change these settings.', 'akava-back-to-top' ),
esc_html__( 'Access denied', 'akava-back-to-top' ),
array( 'response' => 403 )
);
}
check_admin_referer( 'akava_btt_review_prompt' );
$choice = isset( $_POST['choice'] ) ? sanitize_key( wp_unslash( $_POST['choice'] ) ) : 'later';
$state = get_option( Config::REVIEW_PROMPT_OPTION, array() );
$state = is_array( $state ) ? $state : array();
if ( 'dismiss' === $choice ) {
$state['dismissed'] = true;
$state['next_at'] = 0;
} else {
$state['dismissed'] = false;
$state['next_at'] = time() + ( 30 * DAY_IN_SECONDS );
}
update_option( Config::REVIEW_PROMPT_OPTION, $state, true );
wp_safe_redirect( admin_url( 'options-general.php?page=' . self::PAGE_SLUG ) );
exit;
}
/**
* Show the review reminder at a deliberately low frequency.
*
* @return bool
*/
public static function should_show_review_prompt() {
$state = get_option( Config::REVIEW_PROMPT_OPTION, array() );
$state = is_array( $state ) ? $state : array();
if ( ! empty( $state['dismissed'] ) || ( ! empty( $state['next_at'] ) && time() < absint( $state['next_at'] ) ) ) {
return false;
}
$state['next_at'] = time() + ( 30 * DAY_IN_SECONDS );
update_option( Config::REVIEW_PROMPT_OPTION, $state, true );
return true;
}
/**
* Add product and documentation links on the Plugins screen.
*
* @param array $links Existing metadata links.
* @param string $file Plugin basename.
* @return array
*/
public function row_meta( $links, $file ) {
if ( plugin_basename( AKAVA_BTT_FILE ) !== $file ) {
return $links;
}
$links[] = '' . esc_html__( 'Plugin page', 'akava-back-to-top' ) . '';
$links[] = '' . esc_html__( 'Documentation', 'akava-back-to-top' ) . '';
return $links;
}
/**
* Add a single useful quick link on the Plugins screen.
*
* @param array $links Existing links.
* @return array
*/
public function action_links( $links ) {
$settings_link = '' . esc_html__( 'Settings', 'akava-back-to-top' ) . '';
array_unshift( $links, $settings_link );
return $links;
}
}