-
- {
- __('Instead of running on every site, allow this snippet to be activated on individual sites on the network.', 'code-snippets')
- }
-
+
+ {__('Instead of running on every site, allow this snippet to be activated on individual sites on the network.', 'code-snippets')}
+
-
- setSnippet(previous => ({
- ...previous,
- active: false,
- shared_network: event.target.checked
- }))}
- />
+
)
}
diff --git a/src/js/utils/snippets/api.ts b/src/js/utils/snippets/api.ts
index e9072fd9..64bfb155 100644
--- a/src/js/utils/snippets/api.ts
+++ b/src/js/utils/snippets/api.ts
@@ -35,6 +35,7 @@ const mapToSchema = ({
priority,
active,
network,
+ shared_network,
conditionId
}: Partial): WritableSnippetSchema => ({
name,
@@ -45,6 +46,7 @@ const mapToSchema = ({
priority,
active,
network,
+ shared_network,
condition_id: conditionId
})
diff --git a/src/php/class-admin.php b/src/php/class-admin.php
index 70c293a9..dfdf093d 100644
--- a/src/php/class-admin.php
+++ b/src/php/class-admin.php
@@ -63,6 +63,7 @@ public function run() {
add_action( 'init', array( $this, 'load_classes' ), 11 );
add_filter( 'mu_menu_items', array( $this, 'mu_menu_items' ) );
+ add_filter( 'manage_sites_action_links', array( $this, 'add_sites_row_action' ), 10, 2 );
add_filter( 'plugin_action_links_' . plugin_basename( PLUGIN_FILE ), array( $this, 'plugin_action_links' ), 10, 2 );
add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 );
add_filter( 'debug_information', array( $this, 'debug_information' ) );
@@ -89,6 +90,29 @@ public function mu_menu_items( array $menu_items ): array {
return $menu_items;
}
+ /**
+ * Add a "Snippets" row action to the Network Sites table.
+ *
+ * @param array $actions Existing row actions.
+ * @param int $site_id Current site ID.
+ *
+ * @return array
+ */
+ public function add_sites_row_action( array $actions, int $site_id ): array {
+ if ( ! is_multisite() || ! current_user_can( code_snippets()->get_network_cap_name() ) ) {
+ return $actions;
+ }
+
+ $menu_slug = code_snippets()->get_menu_slug();
+ $actions['code_snippets'] = sprintf(
+ '%s',
+ esc_url( get_admin_url( $site_id, 'admin.php?page=' . $menu_slug ) ),
+ esc_html__( 'Snippets', 'code-snippets' )
+ );
+
+ return $actions;
+ }
+
/**
* Modify the action links for this plugin.
*
diff --git a/src/php/class-list-table.php b/src/php/class-list-table.php
index 703765cf..41ed1379 100644
--- a/src/php/class-list-table.php
+++ b/src/php/class-list-table.php
@@ -37,7 +37,7 @@ class List_Table extends WP_List_Table {
*
* @var array
*/
- public array $statuses = [ 'all', 'active', 'inactive', 'recently_activated', 'trashed' ];
+ public array $statuses = [ 'all', 'active', 'inactive', 'recently_activated', 'shared_network', 'trashed' ];
/**
* Column name to use when ordering the snippets list.
@@ -246,6 +246,23 @@ public function get_action_link( string $action, Snippet $snippet ): string {
private function get_snippet_action_links( Snippet $snippet ): array {
$actions = array();
+ if ( $snippet->shared_network && ! $this->is_network ) {
+ $actions['network_shared'] = sprintf(
+ '%s',
+ esc_html__( 'Network Snippet', 'code-snippets' )
+ );
+
+ if ( is_multisite() && is_super_admin() ) {
+ $actions['edit'] = sprintf(
+ '%s',
+ esc_url( $this->get_action_link( 'edit', $snippet ) ),
+ esc_html__( 'Edit', 'code-snippets' )
+ );
+ }
+
+ return apply_filters( 'code_snippets/list_table/row_actions', $actions, $snippet );
+ }
+
if ( $snippet->is_trashed() ) {
$actions['restore'] = sprintf(
'%s',
@@ -307,7 +324,14 @@ protected function column_activate( Snippet $snippet ): string {
return '';
}
- if ( $this->is_network && ( $snippet->shared_network || ( ! $this->is_network && $snippet->network && ! $snippet->shared_network ) ) ) {
+ // Show icon for shared network snippets on network admin.
+ if ( $snippet->shared_network && $this->is_network ) {
+ return '';
+ }
+
+ if ( ! $this->is_network && $snippet->network && ! $snippet->shared_network ) {
return '';
}
@@ -367,18 +391,17 @@ protected function column_name( Snippet $snippet ): string {
);
$out = esc_html( $snippet->display_name );
+ $user_can_manage_network = current_user_can( code_snippets()->get_network_cap_name() );
// Add a link to the snippet if it isn't an unreadable network-only snippet and isn't trashed.
- if ( ! $snippet->is_trashed() && ( $this->is_network || ! $snippet->network || current_user_can( code_snippets()->get_network_cap_name() ) ) ) {
+ if ( ! $snippet->is_trashed() && ( $this->is_network || ! $snippet->network || $user_can_manage_network ) ) {
$out = sprintf(
'%s',
esc_attr( code_snippets()->get_snippet_edit_url( $snippet->id, $snippet->network ? 'network' : 'admin' ) ),
$out
);
- }
-
- if ( $snippet->shared_network ) {
- $out .= ' ' . esc_html__( 'Shared on Network', 'code-snippets' ) . '';
+ } else {
+ $out = sprintf( '%s', $out );
}
$out = apply_filters( 'code_snippets/list_table/column_name', $out, $snippet );
@@ -545,60 +568,88 @@ public function get_views(): array {
// Loop through the view counts.
foreach ( $totals as $type => $count ) {
- $labels = [];
-
if ( ! $count ) {
continue;
}
- // translators: %s: total number of snippets.
- $labels['all'] = _n(
- 'All (%s)',
- 'All (%s)',
- $count,
- 'code-snippets'
- );
-
- // translators: %s: total number of active snippets.
- $labels['active'] = _n(
- 'Active (%s)',
- 'Active (%s)',
- $count,
- 'code-snippets'
- );
+ switch ( $type ) {
+ case 'all':
+ // translators: %s: total number of snippets.
+ $template = _n(
+ 'All (%s)',
+ 'All (%s)',
+ $count,
+ 'code-snippets'
+ );
+ break;
+
+ case 'active':
+ // translators: %s: total number of active snippets.
+ $template = _n(
+ 'Active (%s)',
+ 'Active (%s)',
+ $count,
+ 'code-snippets'
+ );
+ break;
+
+ case 'inactive':
+ // translators: %s: total number of inactive snippets.
+ $template = _n(
+ 'Inactive (%s)',
+ 'Inactive (%s)',
+ $count,
+ 'code-snippets'
+ );
+ break;
+
+ case 'recently_activated':
+ // translators: %s: total number of recently activated snippets.
+ $template = _n(
+ 'Recently Active (%s)',
+ 'Recently Active (%s)',
+ $count,
+ 'code-snippets'
+ );
+ break;
- // translators: %s: total number of inactive snippets.
- $labels['inactive'] = _n(
- 'Inactive (%s)',
- 'Inactive (%s)',
- $count,
- 'code-snippets'
- );
+ case 'shared_network':
+ if ( ! is_multisite() ) {
+ continue 2;
+ }
- // translators: %s: total number of recently activated snippets.
- $labels['recently_activated'] = _n(
- 'Recently Active (%s)',
- 'Recently Active (%s)',
- $count,
- 'code-snippets'
- );
+ $shared_label_template = $this->is_network
+ ? _n_noop(
+ 'Shared with Subsites (%s)',
+ 'Shared with Subsites (%s)',
+ 'code-snippets'
+ )
+ : _n_noop(
+ 'Network Snippets (%s)',
+ 'Network Snippets (%s)',
+ 'code-snippets'
+ );
+
+ $template = translate_nooped_plural( $shared_label_template, $count, 'code-snippets' );
+ break;
+
+ case 'trashed':
+ // translators: %s: total number of trashed snippets.
+ $template = _n(
+ 'Trashed (%s)',
+ 'Trashed (%s)',
+ $count,
+ 'code-snippets'
+ );
+ break;
- // translators: %s: total number of trashed snippets.
- $labels['trashed'] = _n(
- 'Trashed (%s)',
- 'Trashed (%s)',
- $count,
- 'code-snippets'
- );
+ default:
+ continue 2;
+ }
- // The page URL with the status parameter.
$url = esc_url( add_query_arg( 'status', $type ) );
-
- // Add a class if this view is currently being viewed.
$class = $type === $status ? ' class="current"' : '';
-
- // Add the view count to the label.
- $text = sprintf( $labels[ $type ], number_format_i18n( $count ) );
+ $text = sprintf( $template, number_format_i18n( $count ) );
$status_links[ $type ] = sprintf( '%s', $url, $class, $text );
}
@@ -986,46 +1037,43 @@ public function no_items() {
/**
* Fetch all shared network snippets for the current site.
*
- * @return void
+ * @param array $all_snippets List of snippets to merge with.
+ *
+ * @return array Updated list of snippets.
*/
- private function fetch_shared_network_snippets() {
- /**
- * Table data.
- *
- * @var $snippets array
- */
- global $snippets;
+ private function fetch_shared_network_snippets( array $all_snippets ): array {
+ if ( ! is_multisite() ) {
+ return $all_snippets;
+ }
- $ids = get_site_option( 'shared_network_snippets' );
+ $shared_ids = get_site_option( 'shared_network_snippets' );
- if ( ! is_multisite() || ! $ids ) {
- return;
+ if ( ! $shared_ids || ! is_array( $shared_ids ) ) {
+ return $all_snippets;
}
if ( $this->is_network ) {
- $limit = count( $snippets['all'] );
-
- for ( $i = 0; $i < $limit; $i++ ) {
- $snippet = &$snippets['all'][ $i ];
-
- if ( in_array( $snippet->id, $ids, true ) ) {
+ // Mark shared network snippets on the network admin page.
+ foreach ( $all_snippets as $snippet ) {
+ if ( in_array( $snippet->id, $shared_ids, true ) ) {
$snippet->shared_network = true;
- $snippet->tags = array_merge( $snippet->tags, array( 'shared on network' ) );
$snippet->active = false;
}
}
} else {
+ // Fetch shared network snippets for subsites.
$active_shared_snippets = get_option( 'active_shared_network_snippets', array() );
- $shared_snippets = get_snippets( $ids, true );
+ $shared_snippets = get_snippets( $shared_ids, true );
foreach ( $shared_snippets as $snippet ) {
$snippet->shared_network = true;
- $snippet->tags = array_merge( $snippet->tags, array( 'shared on network' ) );
$snippet->active = in_array( $snippet->id, $active_shared_snippets, true );
}
- $snippets['all'] = array_merge( $snippets['all'], $shared_snippets );
+ $all_snippets = array_merge( $all_snippets, $shared_snippets );
}
+
+ return $all_snippets;
}
/**
@@ -1061,8 +1109,7 @@ public function prepare_items() {
$this->process_requested_actions();
$snippets = array_fill_keys( $this->statuses, array() );
- $all_snippets = apply_filters( 'code_snippets/list_table/get_snippets', get_snippets() );
- $this->fetch_shared_network_snippets();
+ $all_snippets = apply_filters( 'code_snippets/list_table/get_snippets', $this->fetch_shared_network_snippets( get_snippets() ) );
// Separate trashed snippets from the main collection
$snippets['trashed'] = array_filter( $all_snippets, function( $snippet ) {
@@ -1125,6 +1172,19 @@ function ( Snippet $snippet ) use ( $type ) {
$snippets['trashed'] = array_filter( $snippets['trashed'], array( $this, 'search_by_line_callback' ) );
}
+ if ( is_multisite() ) {
+ $snippets['shared_network'] = array_values(
+ array_filter(
+ $snippets['all'],
+ static function ( Snippet $snippet ) {
+ return $snippet->shared_network;
+ }
+ )
+ );
+ } else {
+ $snippets['shared_network'] = array();
+ }
+
// Clear recently activated snippets older than a week.
$recently_activated = $this->is_network ?
get_site_option( 'recently_activated_snippets', array() ) :
diff --git a/src/php/class-plugin.php b/src/php/class-plugin.php
index a38e216e..c05613e2 100644
--- a/src/php/class-plugin.php
+++ b/src/php/class-plugin.php
@@ -309,25 +309,52 @@ public function get_network_cap_name(): string {
return apply_filters( 'code_snippets_network_cap', 'manage_network_options' );
}
+ /**
+ * Determine if a subsite user menu is enabled via *Network Settings > Enable administration menus*.
+ *
+ * @return bool
+ */
+ public function is_subsite_menu_enabled(): bool {
+ if ( ! is_multisite() ) {
+ return true;
+ }
+
+ $menu_perms = get_site_option( 'menu_items', array() );
+ return ! empty( $menu_perms['snippets'] );
+ }
+
+ /**
+ * Determine if the current user should have the network snippets capability.
+ *
+ * @return bool
+ */
+ public function user_can_manage_network_snippets(): bool {
+ return is_super_admin() || current_user_can( $this->get_network_cap_name() );
+ }
+
+ /**
+ * Determine whether the current request originates in the network admin.
+ *
+ * @return bool
+ */
+ public function is_network_context(): bool {
+ return is_network_admin();
+ }
+
/**
* Get the required capability to perform a certain action on snippets.
* Does not check if the user has this capability or not.
*
- * If multisite, checks if *Enable Administration Menus: Snippets* is active
- * under the *Settings > Network Settings* network admin menu
+ * If multisite, adjusts the capability based on whether the user is viewing
+ * the network dashboard or a subsite and whether the menu is enabled for subsites.
*
* @return string The capability required to manage snippets.
*
* @since 2.0
*/
public function get_cap(): string {
- if ( is_multisite() ) {
- $menu_perms = get_site_option( 'menu_items', array() );
-
- // If multisite is enabled and the snippet menu is not activated, restrict snippet operations to super admins only.
- if ( empty( $menu_perms['snippets'] ) ) {
- return $this->get_network_cap_name();
- }
+ if ( is_multisite() && $this->is_network_context() ) {
+ return $this->get_network_cap_name();
}
return $this->get_cap_name();
diff --git a/src/php/rest-api/class-snippets-rest-controller.php b/src/php/rest-api/class-snippets-rest-controller.php
index 4027d46b..2cfec385 100644
--- a/src/php/rest-api/class-snippets-rest-controller.php
+++ b/src/php/rest-api/class-snippets-rest-controller.php
@@ -198,6 +198,7 @@ public function register_routes() {
public function get_items( $request ): WP_REST_Response {
$network = $request->get_param( 'network' );
$all_snippets = get_snippets( [], $network );
+ $all_snippets = $this->get_network_items( $all_snippets, $network );
// Get collection params (page, per_page).
$collection_params = $this->get_collection_params();
@@ -229,6 +230,36 @@ public function get_items( $request ): WP_REST_Response {
return $response;
}
+ /**
+ * Retrieve and merge shared network snippets.
+ *
+ * @param array $all_snippets List of snippets to merge with.
+ * @param bool|null $network Whether fetching network snippets.
+ *
+ * @return array Modified list of snippets.
+ */
+ private function get_network_items( array $all_snippets, $network ): array {
+ if ( ! is_multisite() || $network ) {
+ return $all_snippets;
+ }
+
+ $shared_ids = get_site_option( 'shared_network_snippets' );
+
+ if ( ! $shared_ids || ! is_array( $shared_ids ) ) {
+ return $all_snippets;
+ }
+
+ $active_shared_snippets = get_option( 'active_shared_network_snippets', array() );
+ $shared_snippets = get_snippets( $shared_ids, true );
+
+ foreach ( $shared_snippets as $snippet ) {
+ $snippet->shared_network = true;
+ $snippet->active = in_array( $snippet->id, $active_shared_snippets, true );
+ }
+
+ return array_merge( $all_snippets, $shared_snippets );
+ }
+
/**
* Retrieves one item from the collection.
*