self::SLUG, 'tab' => $tab ), $args ), admin_url( 'admin.php' ) ); } /** Хук страницы плагина (страница внутри пункта WP-ADDON). */ public static function hook() { return YSNO_Wpaddon_Menu::hook( self::SLUG ); } public static function menu() { YSNO_Wpaddon_Menu::add_page( __( 'Русский язык 168-ФЗ', 'yasno' ), __( 'Русский язык 168-ФЗ', 'yasno' ), self::SLUG, array( __CLASS__, 'render' ) ); } public static function action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Проверить сайт', 'yasno' ) . '' ); if ( ! YSNO_Plugin::has_pro_addon() ) { $links[] = 'Pro'; } return $links; } public static function assets( $hook ) { if ( self::hook() !== $hook && 'edit.php' !== $hook ) { return; } wp_enqueue_style( 'ysno-admin', YSNO_URL . 'assets/css/admin.css', array(), YSNO_VERSION ); if ( 'edit.php' === $hook ) { return; } wp_enqueue_script( 'ysno-admin', YSNO_URL . 'assets/js/admin.js', array(), YSNO_VERSION, true ); wp_localize_script( 'ysno-admin', 'ysnoAdmin', array( 'ajax' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'ysno_ajax' ), 'back' => self::url( 'check' ), 'i18n' => array( /* translators: 1: проверено, 2: всего */ 'progress' => __( 'Проверено %1$s из %2$s', 'yasno' ), 'starting' => __( 'Собираю список страниц…', 'yasno' ), 'done' => __( 'Готово. Обновляю результаты…', 'yasno' ), 'error' => __( 'Ошибка запроса. Попробуйте ещё раз.', 'yasno' ), 'added' => __( 'В исключениях', 'yasno' ), /* translators: %s: слово */ 'confirm' => __( 'Считать «%s» товарным знаком или фирменным наименованием и не проверять?', 'yasno' ), ), ) ); } public static function notices() { $screen = get_current_screen(); if ( ! $screen || self::hook() !== $screen->id ) { return; } // phpcs:disable WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['ysno_saved'] ) ) { echo '

' . esc_html__( 'Настройки сохранены. Запустите проверку заново, чтобы обновить результаты.', 'yasno' ) . '

'; } if ( isset( $_GET['ysno_msg'] ) ) { echo '

' . esc_html( sanitize_text_field( wp_unslash( $_GET['ysno_msg'] ) ) ) . '

'; } // phpcs:enable } /* ===================================================================== * Сохранение настроек * =================================================================== */ public static function save() { if ( empty( $_POST['ysno_tab'] ) || ! current_user_can( self::CAP ) ) { return; } $tab = sanitize_key( wp_unslash( $_POST['ysno_tab'] ) ); check_admin_referer( 'ysno_save_' . $tab ); $post = map_deep( wp_unslash( $_POST ), 'sanitize_textarea_field' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- очищается map_deep(). $s = YSNO_Settings::all(); if ( 'settings' === $tab ) { $types = array_keys( YSNO_Settings::available_post_types() ); $s['post_types'] = array_values( array_intersect( array_keys( (array) ( $post['post_types'] ?? array() ) ), $types ) ); foreach ( array( 'check_latin', 'check_words', 'check_abbr', 'common_terms', 'menus', 'site_title' ) as $k ) { $s[ $k ] = empty( $post[ $k ] ) ? 0 : 1; } $lines = array(); foreach ( preg_split( '/\R/', (string) ( $post['exceptions'] ?? '' ) ) as $line ) { $line = trim( sanitize_text_field( $line ) ); if ( '' !== $line ) { $lines[ YSNO_Checker::lower( $line ) ] = $line; } } $s['exceptions'] = implode( "\n", array_slice( $lines, 0, 2000 ) ); } /** * Сохранение настроек дополнений. * * @param array $s Настройки. * @param string $tab Вкладка. * @param array $post Очищенные данные формы. */ $s = apply_filters( 'ysno/save_settings', $s, $tab, $post ); YSNO_Settings::update( $s ); YSNO_Checker::reset(); wp_safe_redirect( self::url( $tab, array( 'ysno_saved' => 1 ) ) ); exit; } /* ===================================================================== * Колонка «Русский язык» в списке записей * =================================================================== */ public static function columns() { foreach ( YSNO_Sources::post_types() as $type ) { add_filter( "manage_{$type}_posts_columns", array( __CLASS__, 'column_head' ) ); add_action( "manage_{$type}_posts_custom_column", array( __CLASS__, 'column_cell' ), 10, 2 ); } } public static function column_head( $cols ) { $cols['ysno'] = '' . esc_html__( 'Русский язык', 'yasno' ) . ''; return $cols; } public static function column_cell( $col, $post_id ) { if ( 'ysno' !== $col ) { return; } $n = get_post_meta( $post_id, YSNO_Scan::META, true ); if ( '' === $n ) { echo '—'; } elseif ( 0 === (int) $n ) { echo '✓'; } else { printf( '%d', esc_url( self::url( 'check', array( 'view' => 'pages', 'item' => 'post:' . (int) $post_id ) ) . '#ysno-results' ), esc_attr__( 'Слов, которые стоит проверить', 'yasno' ), (int) $n ); } } /* ===================================================================== * Отрисовка * =================================================================== */ public static function tabs() { $tabs = array( 'overview' => __( 'Обзор', 'yasno' ), 'check' => __( 'Проверка сайта', 'yasno' ), 'settings' => __( 'Настройки и исключения', 'yasno' ), ); if ( ! YSNO_Plugin::has_pro_addon() ) { $tabs['pro'] = '★ Pro-версия'; } /** * Вкладки страницы плагина. * * @param array $tabs Вкладки. */ return (array) apply_filters( 'ysno/admin_tabs', $tabs ); } private static function tab_icon( $tab ) { $icons = array( 'overview' => 'dashicons-yes-alt', 'check' => 'dashicons-search', 'settings' => 'dashicons-admin-settings', 'pro' => 'dashicons-star-filled', ); /** * Иконка вкладки. * * @param string $icon Класс dashicons. * @param string $tab Вкладка. */ return (string) apply_filters( 'ysno/admin_tab_icon', $icons[ $tab ] ?? 'dashicons-admin-generic', $tab ); } public static function header() { $is_pro = YSNO_Plugin::has_pro_addon(); echo '
'; echo ''; echo '

' . esc_html__( 'Русский язык 168-ФЗ', 'yasno' ); echo $is_pro ? ' Pro' : ' ' . esc_html__( 'Бесплатная версия', 'yasno' ) . ''; echo '

' . esc_html__( 'Проверка текстов сайта на слова латиницей и англицизмы по закону о защите русского языка', 'yasno' ) . '

'; echo '
'; if ( ! $is_pro ) { printf( '%s', esc_url( self::url( 'pro' ) ), esc_html__( '★ Получить Pro', 'yasno' ) ); } echo '
'; } public static function render() { if ( ! current_user_can( self::CAP ) ) { return; } $tabs = self::tabs(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'overview'; $tab = isset( $tabs[ $tab ] ) ? $tab : 'overview'; echo '
'; self::header(); echo '
'; echo '
'; if ( 'pro' === $tab ) { YSNO_Admin_Pro_Page::render(); } elseif ( method_exists( 'YSNO_Admin_Views', $tab ) ) { YSNO_Admin_Views::$tab(); } else { /** * Вкладка дополнения. * * @param string $tab Вкладка. */ do_action( 'ysno/admin_tab', $tab ); } echo '
'; } /** Карточка-раздел (используется и Pro). */ public static function card_open( $title, $desc = '', array $args = array() ) { echo '
'; if ( '' !== $title ) { echo '
'; if ( ! empty( $args['icon'] ) ) { echo ''; } echo '

' . wp_kses_post( $title ) . '

'; if ( '' !== $desc ) { echo '

' . wp_kses_post( $desc ) . '

'; } echo '
'; } echo '
'; } public static function card_close() { echo '
'; } }