db = $db; $this->scanner = $scanner; } // ── メニュー登録 ────────────────────────────────────────────────────── public function register_menus(): void { $hooks = array(); $hooks[] = add_menu_page( __( 'Internal Links', 'acs-internal-links' ), __( 'Internal Links', 'acs-internal-links' ), 'edit_posts', 'acsil-link-map', array( $this, 'render_link_map_page' ), 'dashicons-admin-links', 30 ); $hooks[] = add_submenu_page( 'acsil-link-map', __( 'Link Map', 'acs-internal-links' ), __( 'Link Map', 'acs-internal-links' ), 'edit_posts', 'acsil-link-map', array( $this, 'render_link_map_page' ) ); $hooks[] = add_submenu_page( 'acsil-link-map', __( 'Orphaned Posts', 'acs-internal-links' ), __( 'Orphaned Posts', 'acs-internal-links' ), 'edit_posts', 'acsil-orphan-posts', array( $this, 'render_orphan_page' ) ); $hooks[] = add_submenu_page( 'acsil-link-map', __( 'Settings', 'acs-internal-links' ), __( 'Settings', 'acs-internal-links' ), 'manage_options', 'acsil-settings', array( $this, 'render_settings_page' ) ); // メニュータイトルは翻訳されるため、フックサフィックスは実測値を保持する $this->page_hooks = array_values( array_filter( $hooks, 'is_string' ) ); } // ── スクリプト・スタイル読み込み ────────────────────────────────────── public function enqueue_scripts( string $hook ): void { // CSS:プラグインページ全体 + 投稿一覧/編集画面 $is_plugin_page = in_array( $hook, $this->page_hooks, true ); $is_post_screen = in_array( $hook, array( 'edit.php', 'post.php', 'post-new.php' ), true ); if ( $is_plugin_page || $is_post_screen ) { wp_enqueue_style( 'acsil-admin', ACSIL_URL . 'assets/css/admin.css', array(), ACSIL_VERSION ); } if ( $is_plugin_page ) { $scan_nonce = wp_create_nonce( 'acsil_scan_nonce' ); $delete_nonce = wp_create_nonce( 'acsil_delete_nonce' ); $settings = ACSIL_Core::get_settings(); wp_enqueue_script( 'acsil-admin', ACSIL_URL . 'assets/js/admin.js', array(), ACSIL_VERSION, true ); wp_localize_script( 'acsil-admin', 'acsilData', array( 'restUrl' => esc_url_raw( rest_url( 'acsil/v1/' ) ), 'restNonce' => wp_create_nonce( 'wp_rest' ), 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'ajaxNonce' => $scan_nonce, 'deleteNonce' => $delete_nonce, 'settings' => array( 'maxLabelLength' => (int) $settings['max_label_length'], 'defaultDisplayCount' => (int) $settings['default_limit'], ), 'i18n' => array( 'scanComplete' => __( 'Scan complete', 'acs-internal-links' ), 'scanError' => __( 'An error occurred during the scan', 'acs-internal-links' ), 'confirmDelete' => __( 'Delete all link data? This action cannot be undone.', 'acs-internal-links' ), 'noLinks' => __( 'No internal links', 'acs-internal-links' ), 'outgoing' => __( 'Outbound', 'acs-internal-links' ), 'incoming' => __( 'Inbound', 'acs-internal-links' ), 'orphanWarning' => __( 'Orphaned Posts', 'acs-internal-links' ), 'scanning' => __( 'Scanning…', 'acs-internal-links' ), 'articles' => __( 'posts processed', 'acs-internal-links' ), 'category' => __( 'Category', 'acs-internal-links' ), 'date' => __( 'Date', 'acs-internal-links' ), /* translators: %d: number of posts shown on the link map */ 'mapManyNodes' => __( '%d posts — labels are shown on hover. Using the filters is recommended.', 'acs-internal-links' ), 'mapTooManyNodes' => __( '⚠ This site has more than 1,000 posts. Please use the category filter.', 'acs-internal-links' ), 'mapLoadError' => __( 'Failed to load the link map. Please reload the page.', 'acs-internal-links' ), ), ) ); // リンクマップページのみ D3.js + link-map.js を読み込む if ( 'toplevel_page_acsil-link-map' === $hook ) { wp_enqueue_script( 'acsil-d3', ACSIL_URL . 'assets/js/d3.v7.min.js', array(), '7.9.0', true ); wp_enqueue_script( 'acsil-link-map', ACSIL_URL . 'assets/js/link-map.js', array( 'acsil-d3', 'acsil-admin' ), ACSIL_VERSION, true ); } } } // ── リンクマップページ ──────────────────────────────────────────────── public function render_link_map_page(): void { if ( ! current_user_can( 'edit_posts' ) ) { wp_die( esc_html__( 'Sorry, you are not allowed to access this page.', 'acs-internal-links' ) ); } $categories = get_categories( array( 'hide_empty' => false ) ); $scan_nonce = wp_create_nonce( 'acsil_scan_nonce' ); ?>


render_footer(); ?>
db->get_orphan_posts( $active_tab, $post_types ); $total = count( $orphans_all ); $offset = ( $page_num - 1 ) * $per_page; $orphans = array_slice( $orphans_all, $offset, $per_page ); $counts = $this->db->get_all_link_counts(); ?>

1 ) : $base_url = admin_url( 'admin.php?page=acsil-orphan-posts&tab=' . esc_attr( $active_tab ) . '&paged=%#%' ); echo wp_kses_post( paginate_links( array( 'base' => $base_url, 'format' => '', 'current' => $page_num, 'total' => $total_pages, 'prev_text' => '«', 'next_text' => '»', ) ) ); endif; ?> render_footer(); ?>
'acsil-settings', 'acsil_dismiss_review' => '1', ), admin_url( 'admin.php' ) ), 'acsil_dismiss_review' ); ?>

save_settings(); } $settings = ACSIL_Core::get_settings(); $post_types = get_post_types( array( 'public' => true ), 'objects' ); unset( $post_types['attachment'] ); ?>

render_review_request(); ?>

 

render_footer(); ?>
$post_types, 'show_list_columns' => ! empty( $_POST['show_list_columns'] ), // phpcs:ignore WordPress.Security.NonceVerification 'show_dashboard' => ! empty( $_POST['show_dashboard'] ), // phpcs:ignore WordPress.Security.NonceVerification 'show_meta_box' => ! empty( $_POST['show_meta_box'] ), // phpcs:ignore WordPress.Security.NonceVerification 'default_limit' => isset( $_POST['default_limit'] ) ? absint( $_POST['default_limit'] ) : 100, // phpcs:ignore WordPress.Security.NonceVerification 'max_label_length' => isset( $_POST['max_label_length'] ) ? min( 30, max( 10, absint( $_POST['max_label_length'] ) ) ) : 20, // phpcs:ignore WordPress.Security.NonceVerification 'delete_on_uninstall' => ! empty( $_POST['delete_on_uninstall'] ), // phpcs:ignore WordPress.Security.NonceVerification ); update_option( 'acsil_settings', $settings ); delete_transient( 'acsil_dashboard_data' ); add_settings_error( 'acsil_settings', 'settings_saved', __( 'Settings saved.', 'acs-internal-links' ), 'success' ); settings_errors( 'acsil_settings' ); } // ── フッター ────────────────────────────────────────────────────────── public function render_footer(): void { ?>