isset( $settings['author'] ) ? (string) $settings['author'] : '', 'plugins' => isset( $settings['plugins'] ) ? array_values( (array) $settings['plugins'] ) : array(), 'themes' => isset( $settings['themes'] ) ? array_values( (array) $settings['themes'] ) : array(), 'shown' => isset( $settings['shown'] ) ? self::shown( $settings['shown'] ) : self::DEFAULT_SHOWN, ); } /** * How many listings the widget shows, between 1 and MAX_SHOWN. * * @param mixed $value The stored or submitted number. * @return int */ public static function shown( $value ) { $value = absint( $value ); return $value < 1 ? self::DEFAULT_SHOWN : min( $value, self::MAX_SHOWN ); } /** * Whether anything has been chosen yet. * * @return bool */ public static function chosen() { $settings = self::get(); return '' !== $settings['author'] || array() !== $settings['plugins'] || array() !== $settings['themes']; } /** * Register the option with its sanitizer. */ public static function register() { register_setting( 'trackpluse', self::OPTION, array( 'type' => 'array', 'sanitize_callback' => array( __CLASS__, 'sanitize' ), 'default' => array( 'author' => '', 'plugins' => array(), 'themes' => array(), 'shown' => self::DEFAULT_SHOWN, ), ) ); } /** * Keep only a valid username and valid slugs. * * @param mixed $input The submitted values. * @return array{author: string, plugins: string[], themes: string[], shown: int} */ public static function sanitize( $input ) { $input = is_array( $input ) ? $input : array(); return array( 'author' => self::username( isset( $input['author'] ) ? (string) $input['author'] : '' ), 'plugins' => self::slugs( isset( $input['plugins'] ) ? $input['plugins'] : '' ), 'themes' => self::slugs( isset( $input['themes'] ) ? $input['themes'] : '' ), 'shown' => self::shown( isset( $input['shown'] ) ? $input['shown'] : self::DEFAULT_SHOWN ), ); } /** * "@jordan", "jordan" or a profiles.wordpress.org address → "jordan". * * @param string $input What was typed. * @return string The username, or '' when it isn't one. */ public static function username( $input ) { $input = strtolower( trim( $input ) ); // phpcs:ignore WordPress.WP.CapitalPDangit.MisspelledInText -- a lowercase address, not the name. if ( preg_match( '#profiles\.wordpress\.org/([^/?\#\s]+)#', $input, $match ) ) { $input = $match[1]; } $input = ltrim( $input, '@' ); return preg_match( '/^[a-z0-9][a-z0-9._-]{0,59}$/', $input ) ? $input : ''; } /** * Slugs from a list typed one per line or separated by commas or spaces, * or pasted as WordPress.org addresses. * * @param mixed $input What was typed, or a list. * @return string[] */ public static function slugs( $input ) { $parts = is_array( $input ) ? $input : preg_split( '/[\s,]+/', (string) $input ); $slugs = array(); foreach ( (array) $parts as $part ) { $part = strtolower( trim( (string) $part ) ); // phpcs:ignore WordPress.WP.CapitalPDangit.MisspelledInText -- a lowercase address, not the name. if ( preg_match( '#wordpress\.org/(?:plugins|themes)/([a-z0-9-]+)#', $part, $match ) ) { $part = $match[1]; } if ( preg_match( '/^[a-z0-9-]{1,200}$/', $part ) && ! in_array( $part, $slugs, true ) ) { $slugs[] = $part; } } return array_slice( $slugs, 0, self::MAX_SLUGS ); } /** * Settings → TrackPluse. */ public static function menu() { add_options_page( __( 'TrackPluse', 'trackpluse' ), __( 'TrackPluse', 'trackpluse' ), 'manage_options', 'trackpluse', array( __CLASS__, 'page' ) ); } /** * A Settings link on the Plugins screen. * * @param string[] $links The plugin's links. * @return string[] */ public static function action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Settings', 'trackpluse' ) . '' ); return $links; } /** * Forget the kept figures and go back. */ public static function refresh() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Sorry, you are not allowed to do that.', 'trackpluse' ), 403 ); } check_admin_referer( 'trackpluse_refresh' ); TrackPluse_Api::flush(); wp_safe_redirect( wp_get_referer() ? wp_get_referer() : admin_url() ); exit; } /** * The address of the "Refresh now" action. * * @return string */ public static function refresh_url() { return wp_nonce_url( admin_url( 'admin-post.php?action=trackpluse_refresh' ), 'trackpluse_refresh' ); } /** * The settings page's styles and pickers, on that page only. * * @param string $hook The admin screen. */ public static function assets( $hook ) { if ( 'settings_page_trackpluse' !== $hook ) { return; } wp_enqueue_style( 'trackpluse-settings', TRACKPLUSE_URL . 'assets/settings.css', array(), TRACKPLUSE_VERSION ); wp_enqueue_script( 'trackpluse-settings', TRACKPLUSE_URL . 'assets/settings.js', array( 'wp-a11y', 'wp-api-fetch', 'wp-i18n', 'wp-url' ), TRACKPLUSE_VERSION, array( 'in_footer' => true ) ); wp_set_script_translations( 'trackpluse-settings', 'trackpluse' ); } /** * The settings page. */ public static function page() { if ( ! current_user_can( 'manage_options' ) ) { return; } $settings = self::get(); ?>