` markup. */ function openstation_content_graph_icon_svg() { return '' // Threads first: the discs below are drawn over their ends, so // the joins stay round instead of showing a stroke cap. . '' // The focused post. . '' // Its satellites, sized just under the hub so the eye lands on // the middle first. . '' . '' . '' . ''; } /** * Whether the current user should see Content Graph. * * Mirrors the my-wordpress gate, anyone who can edit posts can view * the link map of the content they author and maintain. * * @return bool */ function openstation_content_graph_user_can_use() { $can = current_user_can( 'edit_posts' ); /** * Filter whether the current user can see the Content Graph * desktop icon and window. * * @param bool $can Default: edit_posts capability. */ return (bool) apply_filters( 'openstation_content_graph_user_can_use', $can ); } /** * Build the descriptor list of post types eligible for the graph. * Defaults to every public post type (so CPTs participate by * default), matching the user-facing filter bar that ships ON for all * of them. * * @return array[] Each entry: `array( 'slug', 'label', 'icon', 'taxonomies' )`. */ function openstation_content_graph_post_types() { $types = get_post_types( array( 'public' => true ), 'objects' ); $result = array(); foreach ( $types as $type ) { // `attachment` is public but participates in the graph as media // (rendered in the side panel) rather than as a node — skip it // on the node-type axis to avoid every image becoming a node. if ( 'attachment' === $type->name ) { continue; } $result[] = array( 'slug' => (string) $type->name, 'label' => (string) $type->labels->name, 'icon' => (string) ( ! empty( $type->menu_icon ) ? $type->menu_icon : 'dashicons-admin-post' ), 'taxonomies' => array( 'category' => is_object_in_taxonomy( $type->name, 'category' ), 'post_tag' => is_object_in_taxonomy( $type->name, 'post_tag' ), ), ); } /** * Filter the list of post types shown in the Content Graph filter * bar. Each entry declares `slug`, `label`, `icon`, and optionally * `taxonomies` (`array( 'category' => bool, 'post_tag' => bool )`); * entries missing `taxonomies` get it derived from * `is_object_in_taxonomy()` after filtering. Removing * an entry hides it from the filter bar AND excludes it from the * graph entirely. * * @param array[] $result Default: every public post type except attachment. */ $filtered = apply_filters( 'openstation_content_graph_post_types', $result ); $filtered = is_array( $filtered ) ? array_values( $filtered ) : $result; foreach ( $filtered as $i => $entry ) { $slug = isset( $entry['slug'] ) ? (string) $entry['slug'] : ''; $filtered[ $i ]['taxonomies'] = array( 'category' => isset( $entry['taxonomies'] ) ? ! empty( $entry['taxonomies']['category'] ) : is_object_in_taxonomy( $slug, 'category' ), 'post_tag' => isset( $entry['taxonomies'] ) ? ! empty( $entry['taxonomies']['post_tag'] ) : is_object_in_taxonomy( $slug, 'post_tag' ), ); } return $filtered; } /** * Render the Content Graph window's static template body. The bundle * mounts its UI into `[data-os-content-graph-root]`. */ function openstation_content_graph_render_template() { ob_start(); ?>
__( 'Corkboard', 'desktop-mode' ), 'icon' => $icon_uri, 'template' => 'openstation_content_graph_render_template', 'script' => 'desktop-mode-content-graph', 'styles' => array( 'desktop-mode-content-graph' ), 'width' => 1080, 'height' => 720, 'min_width' => 720, 'min_height' => 480, 'placement' => 'none', 'config' => array( 'restRoot' => esc_url_raw( rest_url() ), 'restNonce' => wp_create_nonce( 'wp_rest' ), 'apiBase' => esc_url_raw( rest_url( 'desktop-mode/v1/content-graph' ) ), 'editPostUrl' => esc_url_raw( admin_url( 'post.php' ) ), 'editTermUrl' => esc_url_raw( admin_url( 'term.php' ) ), 'editUserUrl' => esc_url_raw( admin_url( 'user-edit.php' ) ), 'editCommentUrl' => esc_url_raw( admin_url( 'comment.php' ) ), 'mediaUrl' => esc_url_raw( admin_url( 'upload.php' ) ), // Labels the "Open in " action on the detail panel, // which hands off to the WP Explorer window. 'siteName' => openstation_site_title(), 'postTypes' => openstation_content_graph_post_types(), ), ); /** * Filter the args used to register the Content Graph native window. * * @param array $window_args Args passed to `openstation_register_window()`. */ $window_args = (array) apply_filters( 'openstation_content_graph_window_args', $window_args ); $registered = openstation_register_window( 'desktop-mode-content-graph', $window_args ); if ( is_wp_error( $registered ) ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log( '[openstation] Content Graph window registration failed: ' . $registered->get_error_message() ); return; } $icon_args = array( 'title' => __( 'Corkboard', 'desktop-mode' ), 'icon_svg' => openstation_content_graph_icon_svg(), 'window' => 'desktop-mode-content-graph', 'pinned' => false, 'position' => 20, ); /** * Filter the args used to register the Content Graph desktop icon. * * @param array $icon_args Args passed to `openstation_register_icon()`. */ $icon_args = (array) apply_filters( 'openstation_content_graph_icon_args', $icon_args ); openstation_register_icon( 'desktop-mode-content-graph', $icon_args ); } add_action( 'init', 'openstation_content_graph_register_window', 20 );