wp/wp-admin/includes/class-wp-plugins-list-table.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
    30 	 * @param array $args An associative array of arguments.
    30 	 * @param array $args An associative array of arguments.
    31 	 */
    31 	 */
    32 	public function __construct( $args = array() ) {
    32 	public function __construct( $args = array() ) {
    33 		global $status, $page;
    33 		global $status, $page;
    34 
    34 
    35 		parent::__construct( array(
    35 		parent::__construct(
    36 			'plural' => 'plugins',
    36 			array(
    37 			'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
    37 				'plural' => 'plugins',
    38 		) );
    38 				'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
       
    39 			)
       
    40 		);
    39 
    41 
    40 		$status = 'all';
    42 		$status = 'all';
    41 		if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search' ) ) )
    43 		if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search', 'paused' ) ) ) {
    42 			$status = $_REQUEST['plugin_status'];
    44 			$status = $_REQUEST['plugin_status'];
    43 
    45 		}
    44 		if ( isset($_REQUEST['s']) )
    46 
    45 			$_SERVER['REQUEST_URI'] = add_query_arg('s', wp_unslash($_REQUEST['s']) );
    47 		if ( isset( $_REQUEST['s'] ) ) {
       
    48 			$_SERVER['REQUEST_URI'] = add_query_arg( 's', wp_unslash( $_REQUEST['s'] ) );
       
    49 		}
    46 
    50 
    47 		$page = $this->get_pagenum();
    51 		$page = $this->get_pagenum();
    48 	}
    52 	}
    49 
    53 
    50 	/**
    54 	/**
    56 
    60 
    57 	/**
    61 	/**
    58 	 * @return bool
    62 	 * @return bool
    59 	 */
    63 	 */
    60 	public function ajax_user_can() {
    64 	public function ajax_user_can() {
    61 		return current_user_can('activate_plugins');
    65 		return current_user_can( 'activate_plugins' );
    62 	}
    66 	}
    63 
    67 
    64 	/**
    68 	/**
    65 	 *
       
    66 	 * @global string $status
    69 	 * @global string $status
    67 	 * @global array  $plugins
    70 	 * @global array  $plugins
    68 	 * @global array  $totals
    71 	 * @global array  $totals
    69 	 * @global int    $page
    72 	 * @global int    $page
    70 	 * @global string $orderby
    73 	 * @global string $orderby
    94 			'inactive'           => array(),
    97 			'inactive'           => array(),
    95 			'recently_activated' => array(),
    98 			'recently_activated' => array(),
    96 			'upgrade'            => array(),
    99 			'upgrade'            => array(),
    97 			'mustuse'            => array(),
   100 			'mustuse'            => array(),
    98 			'dropins'            => array(),
   101 			'dropins'            => array(),
       
   102 			'paused'             => array(),
    99 		);
   103 		);
   100 
   104 
   101 		$screen = $this->screen;
   105 		$screen = $this->screen;
   102 
   106 
   103 		if ( ! is_multisite() || ( $screen->in_admin( 'network' ) && current_user_can( 'manage_network_plugins' ) ) ) {
   107 		if ( ! is_multisite() || ( $screen->in_admin( 'network' ) && current_user_can( 'manage_network_plugins' ) ) ) {
   120 			if ( apply_filters( 'show_advanced_plugins', true, 'mustuse' ) ) {
   124 			if ( apply_filters( 'show_advanced_plugins', true, 'mustuse' ) ) {
   121 				$plugins['mustuse'] = get_mu_plugins();
   125 				$plugins['mustuse'] = get_mu_plugins();
   122 			}
   126 			}
   123 
   127 
   124 			/** This action is documented in wp-admin/includes/class-wp-plugins-list-table.php */
   128 			/** This action is documented in wp-admin/includes/class-wp-plugins-list-table.php */
   125 			if ( apply_filters( 'show_advanced_plugins', true, 'dropins' ) )
   129 			if ( apply_filters( 'show_advanced_plugins', true, 'dropins' ) ) {
   126 				$plugins['dropins'] = get_dropins();
   130 				$plugins['dropins'] = get_dropins();
       
   131 			}
   127 
   132 
   128 			if ( current_user_can( 'update_plugins' ) ) {
   133 			if ( current_user_can( 'update_plugins' ) ) {
   129 				$current = get_site_transient( 'update_plugins' );
   134 				$current = get_site_transient( 'update_plugins' );
   130 				foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) {
   135 				foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) {
   131 					if ( isset( $current->response[ $plugin_file ] ) ) {
   136 					if ( isset( $current->response[ $plugin_file ] ) ) {
   132 						$plugins['all'][ $plugin_file ]['update'] = true;
   137 						$plugins['all'][ $plugin_file ]['update'] = true;
   133 						$plugins['upgrade'][ $plugin_file ] = $plugins['all'][ $plugin_file ];
   138 						$plugins['upgrade'][ $plugin_file ]       = $plugins['all'][ $plugin_file ];
   134 					}
   139 					}
   135 				}
   140 				}
   136 			}
   141 			}
   137 		}
   142 		}
   138 
   143 
   162 			$recently_activated = get_option( 'recently_activated', array() );
   167 			$recently_activated = get_option( 'recently_activated', array() );
   163 		}
   168 		}
   164 
   169 
   165 		foreach ( $recently_activated as $key => $time ) {
   170 		foreach ( $recently_activated as $key => $time ) {
   166 			if ( $time + WEEK_IN_SECONDS < time() ) {
   171 			if ( $time + WEEK_IN_SECONDS < time() ) {
   167 				unset( $recently_activated[$key] );
   172 				unset( $recently_activated[ $key ] );
   168 			}
   173 			}
   169 		}
   174 		}
   170 
   175 
   171 		if ( $screen->in_admin( 'network' ) ) {
   176 		if ( $screen->in_admin( 'network' ) ) {
   172 			update_site_option( 'recently_activated', $recently_activated );
   177 			update_site_option( 'recently_activated', $recently_activated );
   177 		$plugin_info = get_site_transient( 'update_plugins' );
   182 		$plugin_info = get_site_transient( 'update_plugins' );
   178 
   183 
   179 		foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) {
   184 		foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) {
   180 			// Extra info if known. array_merge() ensures $plugin_data has precedence if keys collide.
   185 			// Extra info if known. array_merge() ensures $plugin_data has precedence if keys collide.
   181 			if ( isset( $plugin_info->response[ $plugin_file ] ) ) {
   186 			if ( isset( $plugin_info->response[ $plugin_file ] ) ) {
   182 				$plugins['all'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->response[ $plugin_file ], $plugin_data );
   187 				$plugin_data                    = array_merge( (array) $plugin_info->response[ $plugin_file ], $plugin_data );
       
   188 				$plugins['all'][ $plugin_file ] = $plugin_data;
   183 				// Make sure that $plugins['upgrade'] also receives the extra info since it is used on ?plugin_status=upgrade
   189 				// Make sure that $plugins['upgrade'] also receives the extra info since it is used on ?plugin_status=upgrade
   184 				if ( isset( $plugins['upgrade'][ $plugin_file ] ) ) {
   190 				if ( isset( $plugins['upgrade'][ $plugin_file ] ) ) {
   185 					$plugins['upgrade'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->response[ $plugin_file ], $plugin_data );
   191 					$plugins['upgrade'][ $plugin_file ] = $plugin_data;
   186 				}
   192 				}
   187 
       
   188 			} elseif ( isset( $plugin_info->no_update[ $plugin_file ] ) ) {
   193 			} elseif ( isset( $plugin_info->no_update[ $plugin_file ] ) ) {
   189 				$plugins['all'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->no_update[ $plugin_file ], $plugin_data );
   194 				$plugin_data                    = array_merge( (array) $plugin_info->no_update[ $plugin_file ], $plugin_data );
       
   195 				$plugins['all'][ $plugin_file ] = $plugin_data;
   190 				// Make sure that $plugins['upgrade'] also receives the extra info since it is used on ?plugin_status=upgrade
   196 				// Make sure that $plugins['upgrade'] also receives the extra info since it is used on ?plugin_status=upgrade
   191 				if ( isset( $plugins['upgrade'][ $plugin_file ] ) ) {
   197 				if ( isset( $plugins['upgrade'][ $plugin_file ] ) ) {
   192 					$plugins['upgrade'][ $plugin_file ] = $plugin_data = array_merge( (array) $plugin_info->no_update[ $plugin_file ], $plugin_data );
   198 					$plugins['upgrade'][ $plugin_file ] = $plugin_data;
   193 				}
   199 				}
   194 			}
   200 			}
   195 
   201 
   196 			// Filter into individual sections
   202 			// Filter into individual sections
   197 			if ( is_multisite() && ! $screen->in_admin( 'network' ) && is_network_only_plugin( $plugin_file ) && ! is_plugin_active( $plugin_file ) ) {
   203 			if ( is_multisite() && ! $screen->in_admin( 'network' ) && is_network_only_plugin( $plugin_file ) && ! is_plugin_active( $plugin_file ) ) {
   213 			} elseif ( ( ! $screen->in_admin( 'network' ) && is_plugin_active( $plugin_file ) )
   219 			} elseif ( ( ! $screen->in_admin( 'network' ) && is_plugin_active( $plugin_file ) )
   214 				|| ( $screen->in_admin( 'network' ) && is_plugin_active_for_network( $plugin_file ) ) ) {
   220 				|| ( $screen->in_admin( 'network' ) && is_plugin_active_for_network( $plugin_file ) ) ) {
   215 				// On the non-network screen, populate the active list with plugins that are individually activated
   221 				// On the non-network screen, populate the active list with plugins that are individually activated
   216 				// On the network-admin screen, populate the active list with plugins that are network activated
   222 				// On the network-admin screen, populate the active list with plugins that are network activated
   217 				$plugins['active'][ $plugin_file ] = $plugin_data;
   223 				$plugins['active'][ $plugin_file ] = $plugin_data;
       
   224 
       
   225 				if ( ! $screen->in_admin( 'network' ) && is_plugin_paused( $plugin_file ) ) {
       
   226 					$plugins['paused'][ $plugin_file ] = $plugin_data;
       
   227 				}
   218 			} else {
   228 			} else {
   219 				if ( isset( $recently_activated[ $plugin_file ] ) ) {
   229 				if ( isset( $recently_activated[ $plugin_file ] ) ) {
   220 					// Populate the recently activated list with plugins that have been recently activated
   230 					// Populate the recently activated list with plugins that have been recently activated
   221 					$plugins['recently_activated'][ $plugin_file ] = $plugin_data;
   231 					$plugins['recently_activated'][ $plugin_file ] = $plugin_data;
   222 				}
   232 				}
   224 				$plugins['inactive'][ $plugin_file ] = $plugin_data;
   234 				$plugins['inactive'][ $plugin_file ] = $plugin_data;
   225 			}
   235 			}
   226 		}
   236 		}
   227 
   237 
   228 		if ( strlen( $s ) ) {
   238 		if ( strlen( $s ) ) {
   229 			$status = 'search';
   239 			$status            = 'search';
   230 			$plugins['search'] = array_filter( $plugins['all'], array( $this, '_search_callback' ) );
   240 			$plugins['search'] = array_filter( $plugins['all'], array( $this, '_search_callback' ) );
   231 		}
   241 		}
   232 
   242 
   233 		$totals = array();
   243 		$totals = array();
   234 		foreach ( $plugins as $type => $list )
   244 		foreach ( $plugins as $type => $list ) {
   235 			$totals[ $type ] = count( $list );
   245 			$totals[ $type ] = count( $list );
   236 
   246 		}
   237 		if ( empty( $plugins[ $status ] ) && !in_array( $status, array( 'all', 'search' ) ) )
   247 
       
   248 		if ( empty( $plugins[ $status ] ) && ! in_array( $status, array( 'all', 'search' ) ) ) {
   238 			$status = 'all';
   249 			$status = 'all';
       
   250 		}
   239 
   251 
   240 		$this->items = array();
   252 		$this->items = array();
   241 		foreach ( $plugins[ $status ] as $plugin_file => $plugin_data ) {
   253 		foreach ( $plugins[ $status ] as $plugin_file => $plugin_data ) {
   242 			// Translate, Don't Apply Markup, Sanitize HTML
   254 			// Translate, Don't Apply Markup, Sanitize HTML
   243 			$this->items[$plugin_file] = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, false, true );
   255 			$this->items[ $plugin_file ] = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, false, true );
   244 		}
   256 		}
   245 
   257 
   246 		$total_this_page = $totals[ $status ];
   258 		$total_this_page = $totals[ $status ];
   247 
   259 
   248 		$js_plugins = array();
   260 		$js_plugins = array();
   249 		foreach ( $plugins as $key => $list ) {
   261 		foreach ( $plugins as $key => $list ) {
   250 			$js_plugins[ $key ] = array_keys( (array) $list );
   262 			$js_plugins[ $key ] = array_keys( (array) $list );
   251 		}
   263 		}
   252 
   264 
   253 		wp_localize_script( 'updates', '_wpUpdatesItemCounts', array(
   265 		wp_localize_script(
   254 			'plugins' => $js_plugins,
   266 			'updates',
   255 			'totals'  => wp_get_update_data(),
   267 			'_wpUpdatesItemCounts',
   256 		) );
   268 			array(
       
   269 				'plugins' => $js_plugins,
       
   270 				'totals'  => wp_get_update_data(),
       
   271 			)
       
   272 		);
   257 
   273 
   258 		if ( ! $orderby ) {
   274 		if ( ! $orderby ) {
   259 			$orderby = 'Name';
   275 			$orderby = 'Name';
   260 		} else {
   276 		} else {
   261 			$orderby = ucfirst( $orderby );
   277 			$orderby = ucfirst( $orderby );
   267 
   283 
   268 		$plugins_per_page = $this->get_items_per_page( str_replace( '-', '_', $screen->id . '_per_page' ), 999 );
   284 		$plugins_per_page = $this->get_items_per_page( str_replace( '-', '_', $screen->id . '_per_page' ), 999 );
   269 
   285 
   270 		$start = ( $page - 1 ) * $plugins_per_page;
   286 		$start = ( $page - 1 ) * $plugins_per_page;
   271 
   287 
   272 		if ( $total_this_page > $plugins_per_page )
   288 		if ( $total_this_page > $plugins_per_page ) {
   273 			$this->items = array_slice( $this->items, $start, $plugins_per_page );
   289 			$this->items = array_slice( $this->items, $start, $plugins_per_page );
   274 
   290 		}
   275 		$this->set_pagination_args( array(
   291 
   276 			'total_items' => $total_this_page,
   292 		$this->set_pagination_args(
   277 			'per_page' => $plugins_per_page,
   293 			array(
   278 		) );
   294 				'total_items' => $total_this_page,
       
   295 				'per_page'    => $plugins_per_page,
       
   296 			)
       
   297 		);
   279 	}
   298 	}
   280 
   299 
   281 	/**
   300 	/**
   282 	 * @global string $s URL encoded search term.
   301 	 * @global string $s URL encoded search term.
   283 	 *
   302 	 *
   304 	 * @return int
   323 	 * @return int
   305 	 */
   324 	 */
   306 	public function _order_callback( $plugin_a, $plugin_b ) {
   325 	public function _order_callback( $plugin_a, $plugin_b ) {
   307 		global $orderby, $order;
   326 		global $orderby, $order;
   308 
   327 
   309 		$a = $plugin_a[$orderby];
   328 		$a = $plugin_a[ $orderby ];
   310 		$b = $plugin_b[$orderby];
   329 		$b = $plugin_b[ $orderby ];
   311 
   330 
   312 		if ( $a == $b )
   331 		if ( $a == $b ) {
   313 			return 0;
   332 			return 0;
       
   333 		}
   314 
   334 
   315 		if ( 'DESC' === $order ) {
   335 		if ( 'DESC' === $order ) {
   316 			return strcasecmp( $b, $a );
   336 			return strcasecmp( $b, $a );
   317 		} else {
   337 		} else {
   318 			return strcasecmp( $a, $b );
   338 			return strcasecmp( $a, $b );
   319 		}
   339 		}
   320 	}
   340 	}
   321 
   341 
   322 	/**
   342 	/**
   323 	 *
       
   324 	 * @global array $plugins
   343 	 * @global array $plugins
   325 	 */
   344 	 */
   326 	public function no_items() {
   345 	public function no_items() {
   327 		global $plugins;
   346 		global $plugins;
   328 
   347 
   329 		if ( ! empty( $_REQUEST['s'] ) ) {
   348 		if ( ! empty( $_REQUEST['s'] ) ) {
   330 			$s = esc_html( wp_unslash( $_REQUEST['s'] ) );
   349 			$s = esc_html( wp_unslash( $_REQUEST['s'] ) );
   331 
   350 
       
   351 			/* translators: %s: plugin search term */
   332 			printf( __( 'No plugins found for &#8220;%s&#8221;.' ), $s );
   352 			printf( __( 'No plugins found for &#8220;%s&#8221;.' ), $s );
   333 
   353 
   334 			// We assume that somebody who can install plugins in multisite is experienced enough to not need this helper link.
   354 			// We assume that somebody who can install plugins in multisite is experienced enough to not need this helper link.
   335 			if ( ! is_multisite() && current_user_can( 'install_plugins' ) ) {
   355 			if ( ! is_multisite() && current_user_can( 'install_plugins' ) ) {
   336 				echo ' <a href="' . esc_url( admin_url( 'plugin-install.php?tab=search&s=' . urlencode( $s ) ) ) . '">' . __( 'Search for plugins in the WordPress Plugin Directory.' ) . '</a>';
   356 				echo ' <a href="' . esc_url( admin_url( 'plugin-install.php?tab=search&s=' . urlencode( $s ) ) ) . '">' . __( 'Search for plugins in the WordPress Plugin Directory.' ) . '</a>';
   337 			}
   357 			}
   338 		} elseif ( ! empty( $plugins['all'] ) )
   358 		} elseif ( ! empty( $plugins['all'] ) ) {
   339 			_e( 'No plugins found.' );
   359 			_e( 'No plugins found.' );
   340 		else
   360 		} else {
   341 			_e( 'You do not appear to have any plugins available at this time.' );
   361 			_e( 'You do not appear to have any plugins available at this time.' );
       
   362 		}
   342 	}
   363 	}
   343 
   364 
   344 	/**
   365 	/**
   345 	 * Displays the search box.
   366 	 * Displays the search box.
   346 	 *
   367 	 *
   370 		</p>
   391 		</p>
   371 		<?php
   392 		<?php
   372 	}
   393 	}
   373 
   394 
   374 	/**
   395 	/**
   375 	 *
       
   376 	 * @global string $status
   396 	 * @global string $status
   377 	 * @return array
   397 	 * @return array
   378 	 */
   398 	 */
   379 	public function get_columns() {
   399 	public function get_columns() {
   380 		global $status;
   400 		global $status;
   381 
   401 
   382 		return array(
   402 		return array(
   383 			'cb'          => !in_array( $status, array( 'mustuse', 'dropins' ) ) ? '<input type="checkbox" />' : '',
   403 			'cb'          => ! in_array( $status, array( 'mustuse', 'dropins' ) ) ? '<input type="checkbox" />' : '',
   384 			'name'        => __( 'Plugin' ),
   404 			'name'        => __( 'Plugin' ),
   385 			'description' => __( 'Description' ),
   405 			'description' => __( 'Description' ),
   386 		);
   406 		);
   387 	}
   407 	}
   388 
   408 
   392 	protected function get_sortable_columns() {
   412 	protected function get_sortable_columns() {
   393 		return array();
   413 		return array();
   394 	}
   414 	}
   395 
   415 
   396 	/**
   416 	/**
   397 	 *
       
   398 	 * @global array $totals
   417 	 * @global array $totals
   399 	 * @global string $status
   418 	 * @global string $status
   400 	 * @return array
   419 	 * @return array
   401 	 */
   420 	 */
   402 	protected function get_views() {
   421 	protected function get_views() {
   403 		global $totals, $status;
   422 		global $totals, $status;
   404 
   423 
   405 		$status_links = array();
   424 		$status_links = array();
   406 		foreach ( $totals as $type => $count ) {
   425 		foreach ( $totals as $type => $count ) {
   407 			if ( !$count )
   426 			if ( ! $count ) {
   408 				continue;
   427 				continue;
       
   428 			}
   409 
   429 
   410 			switch ( $type ) {
   430 			switch ( $type ) {
   411 				case 'all':
   431 				case 'all':
       
   432 					/* translators: %s: plugin count */
   412 					$text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'plugins' );
   433 					$text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'plugins' );
   413 					break;
   434 					break;
   414 				case 'active':
   435 				case 'active':
       
   436 					/* translators: %s: plugin count */
   415 					$text = _n( 'Active <span class="count">(%s)</span>', 'Active <span class="count">(%s)</span>', $count );
   437 					$text = _n( 'Active <span class="count">(%s)</span>', 'Active <span class="count">(%s)</span>', $count );
   416 					break;
   438 					break;
   417 				case 'recently_activated':
   439 				case 'recently_activated':
       
   440 					/* translators: %s: plugin count */
   418 					$text = _n( 'Recently Active <span class="count">(%s)</span>', 'Recently Active <span class="count">(%s)</span>', $count );
   441 					$text = _n( 'Recently Active <span class="count">(%s)</span>', 'Recently Active <span class="count">(%s)</span>', $count );
   419 					break;
   442 					break;
   420 				case 'inactive':
   443 				case 'inactive':
       
   444 					/* translators: %s: plugin count */
   421 					$text = _n( 'Inactive <span class="count">(%s)</span>', 'Inactive <span class="count">(%s)</span>', $count );
   445 					$text = _n( 'Inactive <span class="count">(%s)</span>', 'Inactive <span class="count">(%s)</span>', $count );
   422 					break;
   446 					break;
   423 				case 'mustuse':
   447 				case 'mustuse':
       
   448 					/* translators: %s: plugin count */
   424 					$text = _n( 'Must-Use <span class="count">(%s)</span>', 'Must-Use <span class="count">(%s)</span>', $count );
   449 					$text = _n( 'Must-Use <span class="count">(%s)</span>', 'Must-Use <span class="count">(%s)</span>', $count );
   425 					break;
   450 					break;
   426 				case 'dropins':
   451 				case 'dropins':
   427 					$text = _n( 'Drop-ins <span class="count">(%s)</span>', 'Drop-ins <span class="count">(%s)</span>', $count );
   452 					/* translators: %s: plugin count */
       
   453 					$text = _n( 'Drop-in <span class="count">(%s)</span>', 'Drop-ins <span class="count">(%s)</span>', $count );
       
   454 					break;
       
   455 				case 'paused':
       
   456 					/* translators: %s: plugin count */
       
   457 					$text = _n( 'Paused <span class="count">(%s)</span>', 'Paused <span class="count">(%s)</span>', $count );
   428 					break;
   458 					break;
   429 				case 'upgrade':
   459 				case 'upgrade':
       
   460 					/* translators: %s: plugin count */
   430 					$text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count );
   461 					$text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count );
   431 					break;
   462 					break;
   432 			}
   463 			}
   433 
   464 
   434 			if ( 'search' !== $type ) {
   465 			if ( 'search' !== $type ) {
   435 				$status_links[$type] = sprintf( "<a href='%s'%s>%s</a>",
   466 				$status_links[ $type ] = sprintf(
   436 					add_query_arg('plugin_status', $type, 'plugins.php'),
   467 					"<a href='%s'%s>%s</a>",
       
   468 					add_query_arg( 'plugin_status', $type, 'plugins.php' ),
   437 					( $type === $status ) ? ' class="current" aria-current="page"' : '',
   469 					( $type === $status ) ? ' class="current" aria-current="page"' : '',
   438 					sprintf( $text, number_format_i18n( $count ) )
   470 					sprintf( $text, number_format_i18n( $count ) )
   439 					);
   471 				);
   440 			}
   472 			}
   441 		}
   473 		}
   442 
   474 
   443 		return $status_links;
   475 		return $status_links;
   444 	}
   476 	}
   445 
   477 
   446 	/**
   478 	/**
   447 	 *
       
   448 	 * @global string $status
   479 	 * @global string $status
   449 	 * @return array
   480 	 * @return array
   450 	 */
   481 	 */
   451 	protected function get_bulk_actions() {
   482 	protected function get_bulk_actions() {
   452 		global $status;
   483 		global $status;
   453 
   484 
   454 		$actions = array();
   485 		$actions = array();
   455 
   486 
   456 		if ( 'active' != $status )
   487 		if ( 'active' != $status ) {
   457 			$actions['activate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Activate' ) : __( 'Activate' );
   488 			$actions['activate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Activate' ) : __( 'Activate' );
   458 
   489 		}
   459 		if ( 'inactive' != $status && 'recent' != $status )
   490 
       
   491 		if ( 'inactive' != $status && 'recent' != $status ) {
   460 			$actions['deactivate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Deactivate' ) : __( 'Deactivate' );
   492 			$actions['deactivate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Deactivate' ) : __( 'Deactivate' );
   461 
   493 		}
   462 		if ( !is_multisite() || $this->screen->in_admin( 'network' ) ) {
   494 
   463 			if ( current_user_can( 'update_plugins' ) )
   495 		if ( ! is_multisite() || $this->screen->in_admin( 'network' ) ) {
       
   496 			if ( current_user_can( 'update_plugins' ) ) {
   464 				$actions['update-selected'] = __( 'Update' );
   497 				$actions['update-selected'] = __( 'Update' );
   465 			if ( current_user_can( 'delete_plugins' ) && ( 'active' != $status ) )
   498 			}
       
   499 			if ( current_user_can( 'delete_plugins' ) && ( 'active' != $status ) ) {
   466 				$actions['delete-selected'] = __( 'Delete' );
   500 				$actions['delete-selected'] = __( 'Delete' );
       
   501 			}
   467 		}
   502 		}
   468 
   503 
   469 		return $actions;
   504 		return $actions;
   470 	}
   505 	}
   471 
   506 
   474 	 * @param string $which
   509 	 * @param string $which
   475 	 */
   510 	 */
   476 	public function bulk_actions( $which = '' ) {
   511 	public function bulk_actions( $which = '' ) {
   477 		global $status;
   512 		global $status;
   478 
   513 
   479 		if ( in_array( $status, array( 'mustuse', 'dropins' ) ) )
   514 		if ( in_array( $status, array( 'mustuse', 'dropins' ) ) ) {
   480 			return;
   515 			return;
       
   516 		}
   481 
   517 
   482 		parent::bulk_actions( $which );
   518 		parent::bulk_actions( $which );
   483 	}
   519 	}
   484 
   520 
   485 	/**
   521 	/**
   487 	 * @param string $which
   523 	 * @param string $which
   488 	 */
   524 	 */
   489 	protected function extra_tablenav( $which ) {
   525 	protected function extra_tablenav( $which ) {
   490 		global $status;
   526 		global $status;
   491 
   527 
   492 		if ( ! in_array($status, array('recently_activated', 'mustuse', 'dropins') ) )
   528 		if ( ! in_array( $status, array( 'recently_activated', 'mustuse', 'dropins' ) ) ) {
   493 			return;
   529 			return;
       
   530 		}
   494 
   531 
   495 		echo '<div class="alignleft actions">';
   532 		echo '<div class="alignleft actions">';
   496 
   533 
   497 		if ( 'recently_activated' == $status ) {
   534 		if ( 'recently_activated' == $status ) {
   498 			submit_button( __( 'Clear List' ), '', 'clear-recent-list', false );
   535 			submit_button( __( 'Clear List' ), '', 'clear-recent-list', false );
   499 		} elseif ( 'top' === $which && 'mustuse' === $status ) {
   536 		} elseif ( 'top' === $which && 'mustuse' === $status ) {
   500 			/* translators: %s: mu-plugins directory name */
   537 			echo '<p>' . sprintf(
   501 			echo '<p>' . sprintf( __( 'Files in the %s directory are executed automatically.' ),
   538 				/* translators: %s: mu-plugins directory name */
       
   539 				__( 'Files in the %s directory are executed automatically.' ),
   502 				'<code>' . str_replace( ABSPATH, '/', WPMU_PLUGIN_DIR ) . '</code>'
   540 				'<code>' . str_replace( ABSPATH, '/', WPMU_PLUGIN_DIR ) . '</code>'
   503 			) . '</p>';
   541 			) . '</p>';
   504 		} elseif ( 'top' === $which && 'dropins' === $status ) {
   542 		} elseif ( 'top' === $which && 'dropins' === $status ) {
   505 			/* translators: %s: wp-content directory name */
   543 			echo '<p>' . sprintf(
   506 			echo '<p>' . sprintf( __( 'Drop-ins are advanced plugins in the %s directory that replace WordPress functionality when present.' ),
   544 				/* translators: %s: wp-content directory name */
       
   545 				__( 'Drop-ins are advanced plugins in the %s directory that replace WordPress functionality when present.' ),
   507 				'<code>' . str_replace( ABSPATH, '', WP_CONTENT_DIR ) . '</code>'
   546 				'<code>' . str_replace( ABSPATH, '', WP_CONTENT_DIR ) . '</code>'
   508 			) . '</p>';
   547 			) . '</p>';
   509 		}
   548 		}
   510 		echo '</div>';
   549 		echo '</div>';
   511 	}
   550 	}
   512 
   551 
   513 	/**
   552 	/**
   514 	 * @return string
   553 	 * @return string
   515 	 */
   554 	 */
   516 	public function current_action() {
   555 	public function current_action() {
   517 		if ( isset($_POST['clear-recent-list']) )
   556 		if ( isset( $_POST['clear-recent-list'] ) ) {
   518 			return 'clear-recent-list';
   557 			return 'clear-recent-list';
       
   558 		}
   519 
   559 
   520 		return parent::current_action();
   560 		return parent::current_action();
   521 	}
   561 	}
   522 
   562 
   523 	/**
   563 	/**
   524 	 *
       
   525 	 * @global string $status
   564 	 * @global string $status
   526 	 */
   565 	 */
   527 	public function display_rows() {
   566 	public function display_rows() {
   528 		global $status;
   567 		global $status;
   529 
   568 
   530 		if ( is_multisite() && ! $this->screen->in_admin( 'network' ) && in_array( $status, array( 'mustuse', 'dropins' ) ) )
   569 		if ( is_multisite() && ! $this->screen->in_admin( 'network' ) && in_array( $status, array( 'mustuse', 'dropins' ) ) ) {
   531 			return;
   570 			return;
   532 
   571 		}
   533 		foreach ( $this->items as $plugin_file => $plugin_data )
   572 
       
   573 		foreach ( $this->items as $plugin_file => $plugin_data ) {
   534 			$this->single_row( array( $plugin_file, $plugin_data ) );
   574 			$this->single_row( array( $plugin_file, $plugin_data ) );
       
   575 		}
   535 	}
   576 	}
   536 
   577 
   537 	/**
   578 	/**
   538 	 * @global string $status
   579 	 * @global string $status
   539 	 * @global int $page
   580 	 * @global int $page
   544 	 */
   585 	 */
   545 	public function single_row( $item ) {
   586 	public function single_row( $item ) {
   546 		global $status, $page, $s, $totals;
   587 		global $status, $page, $s, $totals;
   547 
   588 
   548 		list( $plugin_file, $plugin_data ) = $item;
   589 		list( $plugin_file, $plugin_data ) = $item;
   549 		$context = $status;
   590 		$context                           = $status;
   550 		$screen = $this->screen;
   591 		$screen                            = $this->screen;
   551 
   592 
   552 		// Pre-order.
   593 		// Pre-order.
   553 		$actions = array(
   594 		$actions = array(
   554 			'deactivate' => '',
   595 			'deactivate' => '',
   555 			'activate' => '',
   596 			'activate'   => '',
   556 			'details' => '',
   597 			'details'    => '',
   557 			'delete' => '',
   598 			'delete'     => '',
   558 		);
   599 		);
   559 
   600 
   560 		// Do not restrict by default
   601 		// Do not restrict by default
   561 		$restrict_network_active = false;
   602 		$restrict_network_active = false;
   562 		$restrict_network_only = false;
   603 		$restrict_network_only   = false;
   563 
   604 
   564 		if ( 'mustuse' === $context ) {
   605 		if ( 'mustuse' === $context ) {
   565 			$is_active = true;
   606 			$is_active = true;
   566 		} elseif ( 'dropins' === $context ) {
   607 		} elseif ( 'dropins' === $context ) {
   567 			$dropins = _get_dropins();
   608 			$dropins     = _get_dropins();
   568 			$plugin_name = $plugin_file;
   609 			$plugin_name = $plugin_file;
   569 			if ( $plugin_file != $plugin_data['Name'] )
   610 			if ( $plugin_file != $plugin_data['Name'] ) {
   570 				$plugin_name .= '<br/>' . $plugin_data['Name'];
   611 				$plugin_name .= '<br/>' . $plugin_data['Name'];
       
   612 			}
   571 			if ( true === ( $dropins[ $plugin_file ][1] ) ) { // Doesn't require a constant
   613 			if ( true === ( $dropins[ $plugin_file ][1] ) ) { // Doesn't require a constant
   572 				$is_active = true;
   614 				$is_active   = true;
   573 				$description = '<p><strong>' . $dropins[ $plugin_file ][0] . '</strong></p>';
   615 				$description = '<p><strong>' . $dropins[ $plugin_file ][0] . '</strong></p>';
   574 			} elseif ( defined( $dropins[ $plugin_file ][1] ) && constant( $dropins[ $plugin_file ][1] ) ) { // Constant is true
   616 			} elseif ( defined( $dropins[ $plugin_file ][1] ) && constant( $dropins[ $plugin_file ][1] ) ) { // Constant is true
   575 				$is_active = true;
   617 				$is_active   = true;
   576 				$description = '<p><strong>' . $dropins[ $plugin_file ][0] . '</strong></p>';
   618 				$description = '<p><strong>' . $dropins[ $plugin_file ][0] . '</strong></p>';
   577 			} else {
   619 			} else {
   578 				$is_active = false;
   620 				$is_active   = false;
   579 				$description = '<p><strong>' . $dropins[ $plugin_file ][0] . ' <span class="error-message">' . __( 'Inactive:' ) . '</span></strong> ' .
   621 				$description = '<p><strong>' . $dropins[ $plugin_file ][0] . ' <span class="error-message">' . __( 'Inactive:' ) . '</span></strong> ' .
   580 					/* translators: 1: drop-in constant name, 2: wp-config.php */
   622 					sprintf(
   581 					sprintf( __( 'Requires %1$s in %2$s file.' ),
   623 						/* translators: 1: drop-in constant name, 2: wp-config.php */
       
   624 						__( 'Requires %1$s in %2$s file.' ),
   582 						"<code>define('" . $dropins[ $plugin_file ][1] . "', true);</code>",
   625 						"<code>define('" . $dropins[ $plugin_file ][1] . "', true);</code>",
   583 						'<code>wp-config.php</code>'
   626 						'<code>wp-config.php</code>'
   584 					) . '</p>';
   627 					) . '</p>';
   585 			}
   628 			}
   586 			if ( $plugin_data['Description'] )
   629 			if ( $plugin_data['Description'] ) {
   587 				$description .= '<p>' . $plugin_data['Description'] . '</p>';
   630 				$description .= '<p>' . $plugin_data['Description'] . '</p>';
       
   631 			}
   588 		} else {
   632 		} else {
   589 			if ( $screen->in_admin( 'network' ) ) {
   633 			if ( $screen->in_admin( 'network' ) ) {
   590 				$is_active = is_plugin_active_for_network( $plugin_file );
   634 				$is_active = is_plugin_active_for_network( $plugin_file );
   591 			} else {
   635 			} else {
   592 				$is_active = is_plugin_active( $plugin_file );
   636 				$is_active               = is_plugin_active( $plugin_file );
   593 				$restrict_network_active = ( is_multisite() && is_plugin_active_for_network( $plugin_file ) );
   637 				$restrict_network_active = ( is_multisite() && is_plugin_active_for_network( $plugin_file ) );
   594 				$restrict_network_only = ( is_multisite() && is_network_only_plugin( $plugin_file ) && ! $is_active );
   638 				$restrict_network_only   = ( is_multisite() && is_network_only_plugin( $plugin_file ) && ! $is_active );
   595 			}
   639 			}
   596 
   640 
   597 			if ( $screen->in_admin( 'network' ) ) {
   641 			if ( $screen->in_admin( 'network' ) ) {
   598 				if ( $is_active ) {
   642 				if ( $is_active ) {
   599 					if ( current_user_can( 'manage_network_plugins' ) ) {
   643 					if ( current_user_can( 'manage_network_plugins' ) ) {
   600 						/* translators: %s: plugin name */
   644 						/* translators: %s: plugin name */
   601 						$actions['deactivate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'deactivate-plugin_' . $plugin_file ) . '" aria-label="' . esc_attr( sprintf( _x( 'Network Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Network Deactivate' ) . '</a>';
   645 						$actions['deactivate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'deactivate-plugin_' . $plugin_file ) . '" aria-label="' . esc_attr( sprintf( _x( 'Network Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Network Deactivate' ) . '</a>';
   602 						}
   646 					}
   603 				} else {
   647 				} else {
   604 					if ( current_user_can( 'manage_network_plugins' ) ) {
   648 					if ( current_user_can( 'manage_network_plugins' ) ) {
   605 						/* translators: %s: plugin name */
   649 						/* translators: %s: plugin name */
   606 						$actions['activate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'activate-plugin_' . $plugin_file ) . '" class="edit" aria-label="' . esc_attr( sprintf( _x( 'Network Activate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Network Activate' ) . '</a>';
   650 						$actions['activate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'activate-plugin_' . $plugin_file ) . '" class="edit" aria-label="' . esc_attr( sprintf( _x( 'Network Activate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Network Activate' ) . '</a>';
   607 					}
   651 					}
   622 				} elseif ( $is_active ) {
   666 				} elseif ( $is_active ) {
   623 					if ( current_user_can( 'deactivate_plugin', $plugin_file ) ) {
   667 					if ( current_user_can( 'deactivate_plugin', $plugin_file ) ) {
   624 						/* translators: %s: plugin name */
   668 						/* translators: %s: plugin name */
   625 						$actions['deactivate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'deactivate-plugin_' . $plugin_file ) . '" aria-label="' . esc_attr( sprintf( _x( 'Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Deactivate' ) . '</a>';
   669 						$actions['deactivate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'deactivate-plugin_' . $plugin_file ) . '" aria-label="' . esc_attr( sprintf( _x( 'Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Deactivate' ) . '</a>';
   626 					}
   670 					}
       
   671 					if ( current_user_can( 'resume_plugin', $plugin_file ) && is_plugin_paused( $plugin_file ) ) {
       
   672 						/* translators: %s: plugin name */
       
   673 						$actions['resume'] = '<a class="resume-link" href="' . wp_nonce_url( 'plugins.php?action=resume&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'resume-plugin_' . $plugin_file ) . '" aria-label="' . esc_attr( sprintf( _x( 'Resume %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Resume' ) . '</a>';
       
   674 					}
   627 				} else {
   675 				} else {
   628 					if ( current_user_can( 'activate_plugin', $plugin_file ) ) {
   676 					if ( current_user_can( 'activate_plugin', $plugin_file ) ) {
   629 						/* translators: %s: plugin name */
   677 						/* translators: %s: plugin name */
   630 						$actions['activate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'activate-plugin_' . $plugin_file ) . '" class="edit" aria-label="' . esc_attr( sprintf( _x( 'Activate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Activate' ) . '</a>';
   678 						$actions['activate'] = '<a href="' . wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'activate-plugin_' . $plugin_file ) . '" class="edit" aria-label="' . esc_attr( sprintf( _x( 'Activate %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Activate' ) . '</a>';
   631 					}
   679 					}
   633 					if ( ! is_multisite() && current_user_can( 'delete_plugins' ) ) {
   681 					if ( ! is_multisite() && current_user_can( 'delete_plugins' ) ) {
   634 						/* translators: %s: plugin name */
   682 						/* translators: %s: plugin name */
   635 						$actions['delete'] = '<a href="' . wp_nonce_url( 'plugins.php?action=delete-selected&amp;checked[]=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'bulk-plugins' ) . '" class="delete" aria-label="' . esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Delete' ) . '</a>';
   683 						$actions['delete'] = '<a href="' . wp_nonce_url( 'plugins.php?action=delete-selected&amp;checked[]=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'bulk-plugins' ) . '" class="delete" aria-label="' . esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ) . '">' . __( 'Delete' ) . '</a>';
   636 					}
   684 					}
   637 				} // end if $is_active
   685 				} // end if $is_active
   638 
   686 			} // end if $screen->in_admin( 'network' )
   639 			 } // end if $screen->in_admin( 'network' )
       
   640 
       
   641 		} // end if $context
   687 		} // end if $context
   642 
   688 
   643 		$actions = array_filter( $actions );
   689 		$actions = array_filter( $actions );
   644 
   690 
   645 		if ( $screen->in_admin( 'network' ) ) {
   691 		if ( $screen->in_admin( 'network' ) ) {
   647 			/**
   693 			/**
   648 			 * Filters the action links displayed for each plugin in the Network Admin Plugins list table.
   694 			 * Filters the action links displayed for each plugin in the Network Admin Plugins list table.
   649 			 *
   695 			 *
   650 			 * @since 3.1.0
   696 			 * @since 3.1.0
   651 			 *
   697 			 *
   652 			 * @param array  $actions     An array of plugin action links. By default this can include 'activate',
   698 			 * @param string[] $actions     An array of plugin action links. By default this can include 'activate',
   653 			 *                            'deactivate', and 'delete'.
   699 			 *                              'deactivate', and 'delete'.
   654 			 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
   700 			 * @param string   $plugin_file Path to the plugin file relative to the plugins directory.
   655 			 * @param array  $plugin_data An array of plugin data. See `get_plugin_data()`.
   701 			 * @param array    $plugin_data An array of plugin data. See `get_plugin_data()`.
   656 			 * @param string $context     The plugin context. By default this can include 'all', 'active', 'inactive',
   702 			 * @param string   $context     The plugin context. By default this can include 'all', 'active', 'inactive',
   657 			 *                            'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
   703 			 *                              'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
   658 			 */
   704 			 */
   659 			$actions = apply_filters( 'network_admin_plugin_action_links', $actions, $plugin_file, $plugin_data, $context );
   705 			$actions = apply_filters( 'network_admin_plugin_action_links', $actions, $plugin_file, $plugin_data, $context );
   660 
   706 
   661 			/**
   707 			/**
   662 			 * Filters the list of action links displayed for a specific plugin in the Network Admin Plugins list table.
   708 			 * Filters the list of action links displayed for a specific plugin in the Network Admin Plugins list table.
   664 			 * The dynamic portion of the hook name, `$plugin_file`, refers to the path
   710 			 * The dynamic portion of the hook name, `$plugin_file`, refers to the path
   665 			 * to the plugin file, relative to the plugins directory.
   711 			 * to the plugin file, relative to the plugins directory.
   666 			 *
   712 			 *
   667 			 * @since 3.1.0
   713 			 * @since 3.1.0
   668 			 *
   714 			 *
   669 			 * @param array  $actions     An array of plugin action links. By default this can include 'activate',
   715 			 * @param string[] $actions     An array of plugin action links. By default this can include 'activate',
   670 			 *                            'deactivate', and 'delete'.
   716 			 *                              'deactivate', and 'delete'.
   671 			 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
   717 			 * @param string   $plugin_file Path to the plugin file relative to the plugins directory.
   672 			 * @param array  $plugin_data An array of plugin data. See `get_plugin_data()`.
   718 			 * @param array    $plugin_data An array of plugin data. See `get_plugin_data()`.
   673 			 * @param string $context     The plugin context. By default this can include 'all', 'active', 'inactive',
   719 			 * @param string   $context     The plugin context. By default this can include 'all', 'active', 'inactive',
   674 			 *                            'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
   720 			 *                              'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
   675 			 */
   721 			 */
   676 			$actions = apply_filters( "network_admin_plugin_action_links_{$plugin_file}", $actions, $plugin_file, $plugin_data, $context );
   722 			$actions = apply_filters( "network_admin_plugin_action_links_{$plugin_file}", $actions, $plugin_file, $plugin_data, $context );
   677 
   723 
   678 		} else {
   724 		} else {
   679 
   725 
   682 			 *
   728 			 *
   683 			 * @since 2.5.0
   729 			 * @since 2.5.0
   684 			 * @since 2.6.0 The `$context` parameter was added.
   730 			 * @since 2.6.0 The `$context` parameter was added.
   685 			 * @since 4.9.0 The 'Edit' link was removed from the list of action links.
   731 			 * @since 4.9.0 The 'Edit' link was removed from the list of action links.
   686 			 *
   732 			 *
   687 			 * @param array  $actions     An array of plugin action links. By default this can include 'activate',
   733 			 * @param string[] $actions     An array of plugin action links. By default this can include 'activate',
   688 			 *                            'deactivate', and 'delete'. With Multisite active this can also include
   734 			 *                              'deactivate', and 'delete'. With Multisite active this can also include
   689 			 *                            'network_active' and 'network_only' items.
   735 			 *                              'network_active' and 'network_only' items.
   690 			 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
   736 			 * @param string   $plugin_file Path to the plugin file relative to the plugins directory.
   691 			 * @param array  $plugin_data An array of plugin data. See `get_plugin_data()`.
   737 			 * @param array    $plugin_data An array of plugin data. See `get_plugin_data()`.
   692 			 * @param string $context     The plugin context. By default this can include 'all', 'active', 'inactive',
   738 			 * @param string   $context     The plugin context. By default this can include 'all', 'active', 'inactive',
   693 			 *                            'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
   739 			 *                              'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
   694 			 */
   740 			 */
   695 			$actions = apply_filters( 'plugin_action_links', $actions, $plugin_file, $plugin_data, $context );
   741 			$actions = apply_filters( 'plugin_action_links', $actions, $plugin_file, $plugin_data, $context );
   696 
   742 
   697 			/**
   743 			/**
   698 			 * Filters the list of action links displayed for a specific plugin in the Plugins list table.
   744 			 * Filters the list of action links displayed for a specific plugin in the Plugins list table.
   701 			 * to the plugin file, relative to the plugins directory.
   747 			 * to the plugin file, relative to the plugins directory.
   702 			 *
   748 			 *
   703 			 * @since 2.7.0
   749 			 * @since 2.7.0
   704 			 * @since 4.9.0 The 'Edit' link was removed from the list of action links.
   750 			 * @since 4.9.0 The 'Edit' link was removed from the list of action links.
   705 			 *
   751 			 *
   706 			 * @param array  $actions     An array of plugin action links. By default this can include 'activate',
   752 			 * @param string[] $actions     An array of plugin action links. By default this can include 'activate',
   707 			 *                            'deactivate', and 'delete'. With Multisite active this can also include
   753 			 *                              'deactivate', and 'delete'. With Multisite active this can also include
   708 			 *                            'network_active' and 'network_only' items.
   754 			 *                              'network_active' and 'network_only' items.
   709 			 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
   755 			 * @param string   $plugin_file Path to the plugin file relative to the plugins directory.
   710 			 * @param array  $plugin_data An array of plugin data. See `get_plugin_data()`.
   756 			 * @param array    $plugin_data An array of plugin data. See `get_plugin_data()`.
   711 			 * @param string $context     The plugin context. By default this can include 'all', 'active', 'inactive',
   757 			 * @param string   $context     The plugin context. By default this can include 'all', 'active', 'inactive',
   712 			 *                            'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
   758 			 *                              'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
   713 			 */
   759 			 */
   714 			$actions = apply_filters( "plugin_action_links_{$plugin_file}", $actions, $plugin_file, $plugin_data, $context );
   760 			$actions = apply_filters( "plugin_action_links_{$plugin_file}", $actions, $plugin_file, $plugin_data, $context );
   715 
   761 
   716 		}
   762 		}
   717 
   763 
   718 		$class = $is_active ? 'active' : 'inactive';
   764 		$requires_php   = isset( $plugin_data['requires_php'] ) ? $plugin_data['requires_php'] : null;
   719 		$checkbox_id =  "checkbox_" . md5($plugin_data['Name']);
   765 		$compatible_php = is_php_version_compatible( $requires_php );
   720 		if ( $restrict_network_active || $restrict_network_only || in_array( $status, array( 'mustuse', 'dropins' ) ) ) {
   766 		$class          = $is_active ? 'active' : 'inactive';
       
   767 		$checkbox_id    = 'checkbox_' . md5( $plugin_data['Name'] );
       
   768 		if ( $restrict_network_active || $restrict_network_only || in_array( $status, array( 'mustuse', 'dropins' ) ) || ! $compatible_php ) {
   721 			$checkbox = '';
   769 			$checkbox = '';
   722 		} else {
   770 		} else {
   723 			$checkbox = "<label class='screen-reader-text' for='" . $checkbox_id . "' >" . sprintf( __( 'Select %s' ), $plugin_data['Name'] ) . "</label>"
   771 			/* translators: %s: plugin name */
       
   772 			$checkbox = "<label class='screen-reader-text' for='" . $checkbox_id . "' >" . sprintf( __( 'Select %s' ), $plugin_data['Name'] ) . '</label>'
   724 				. "<input type='checkbox' name='checked[]' value='" . esc_attr( $plugin_file ) . "' id='" . $checkbox_id . "' />";
   773 				. "<input type='checkbox' name='checked[]' value='" . esc_attr( $plugin_file ) . "' id='" . $checkbox_id . "' />";
   725 		}
   774 		}
   726 		if ( 'dropins' != $context ) {
   775 		if ( 'dropins' != $context ) {
   727 			$description = '<p>' . ( $plugin_data['Description'] ? $plugin_data['Description'] : '&nbsp;' ) . '</p>';
   776 			$description = '<p>' . ( $plugin_data['Description'] ? $plugin_data['Description'] : '&nbsp;' ) . '</p>';
   728 			$plugin_name = $plugin_data['Name'];
   777 			$plugin_name = $plugin_data['Name'];
   729 		}
   778 		}
   730 
   779 
   731 		if ( ! empty( $totals['upgrade'] ) && ! empty( $plugin_data['update'] ) )
   780 		if ( ! empty( $totals['upgrade'] ) && ! empty( $plugin_data['update'] ) ) {
   732 			$class .= ' update';
   781 			$class .= ' update';
       
   782 		}
       
   783 
       
   784 		$paused = ! $screen->in_admin( 'network' ) && is_plugin_paused( $plugin_file );
       
   785 
       
   786 		if ( $paused ) {
       
   787 			$class .= ' paused';
       
   788 		}
   733 
   789 
   734 		$plugin_slug = isset( $plugin_data['slug'] ) ? $plugin_data['slug'] : sanitize_title( $plugin_name );
   790 		$plugin_slug = isset( $plugin_data['slug'] ) ? $plugin_data['slug'] : sanitize_title( $plugin_name );
   735 		printf( '<tr class="%s" data-slug="%s" data-plugin="%s">',
   791 		printf(
       
   792 			'<tr class="%s" data-slug="%s" data-plugin="%s">',
   736 			esc_attr( $class ),
   793 			esc_attr( $class ),
   737 			esc_attr( $plugin_slug ),
   794 			esc_attr( $plugin_slug ),
   738 			esc_attr( $plugin_file )
   795 			esc_attr( $plugin_file )
   739 		);
   796 		);
   740 
   797 
   751 					echo "<th scope='row' class='check-column'>$checkbox</th>";
   808 					echo "<th scope='row' class='check-column'>$checkbox</th>";
   752 					break;
   809 					break;
   753 				case 'name':
   810 				case 'name':
   754 					echo "<td class='plugin-title column-primary'><strong>$plugin_name</strong>";
   811 					echo "<td class='plugin-title column-primary'><strong>$plugin_name</strong>";
   755 					echo $this->row_actions( $actions, true );
   812 					echo $this->row_actions( $actions, true );
   756 					echo "</td>";
   813 					echo '</td>';
   757 					break;
   814 					break;
   758 				case 'description':
   815 				case 'description':
   759 					$classes = 'column-description desc';
   816 					$classes = 'column-description desc';
   760 
   817 
   761 					echo "<td class='$classes{$extra_classes}'>
   818 					echo "<td class='$classes{$extra_classes}'>
   762 						<div class='plugin-description'>$description</div>
   819 						<div class='plugin-description'>$description</div>
   763 						<div class='$class second plugin-version-author-uri'>";
   820 						<div class='$class second plugin-version-author-uri'>";
   764 
   821 
   765 					$plugin_meta = array();
   822 					$plugin_meta = array();
   766 					if ( !empty( $plugin_data['Version'] ) )
   823 					if ( ! empty( $plugin_data['Version'] ) ) {
       
   824 						/* translators: %s: plugin version number */
   767 						$plugin_meta[] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
   825 						$plugin_meta[] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
   768 					if ( !empty( $plugin_data['Author'] ) ) {
   826 					}
       
   827 					if ( ! empty( $plugin_data['Author'] ) ) {
   769 						$author = $plugin_data['Author'];
   828 						$author = $plugin_data['Author'];
   770 						if ( !empty( $plugin_data['AuthorURI'] ) )
   829 						if ( ! empty( $plugin_data['AuthorURI'] ) ) {
   771 							$author = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>';
   830 							$author = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>';
       
   831 						}
       
   832 						/* translators: %s: plugin version number */
   772 						$plugin_meta[] = sprintf( __( 'By %s' ), $author );
   833 						$plugin_meta[] = sprintf( __( 'By %s' ), $author );
   773 					}
   834 					}
   774 
   835 
   775 					// Details link using API info, if available
   836 					// Details link using API info, if available
   776 					if ( isset( $plugin_data['slug'] ) && current_user_can( 'install_plugins' ) ) {
   837 					if ( isset( $plugin_data['slug'] ) && current_user_can( 'install_plugins' ) ) {
   777 						$plugin_meta[] = sprintf( '<a href="%s" class="thickbox open-plugin-details-modal" aria-label="%s" data-title="%s">%s</a>',
   838 						$plugin_meta[] = sprintf(
   778 							esc_url( network_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin_data['slug'] .
   839 							'<a href="%s" class="thickbox open-plugin-details-modal" aria-label="%s" data-title="%s">%s</a>',
   779 								'&TB_iframe=true&width=600&height=550' ) ),
   840 							esc_url(
       
   841 								network_admin_url(
       
   842 									'plugin-install.php?tab=plugin-information&plugin=' . $plugin_data['slug'] .
       
   843 									'&TB_iframe=true&width=600&height=550'
       
   844 								)
       
   845 							),
       
   846 							/* translators: %s: plugin name */
   780 							esc_attr( sprintf( __( 'More information about %s' ), $plugin_name ) ),
   847 							esc_attr( sprintf( __( 'More information about %s' ), $plugin_name ) ),
   781 							esc_attr( $plugin_name ),
   848 							esc_attr( $plugin_name ),
   782 							__( 'View details' )
   849 							__( 'View details' )
   783 						);
   850 						);
   784 					} elseif ( ! empty( $plugin_data['PluginURI'] ) ) {
   851 					} elseif ( ! empty( $plugin_data['PluginURI'] ) ) {
   785 						$plugin_meta[] = sprintf( '<a href="%s">%s</a>',
   852 						$plugin_meta[] = sprintf(
       
   853 							'<a href="%s">%s</a>',
   786 							esc_url( $plugin_data['PluginURI'] ),
   854 							esc_url( $plugin_data['PluginURI'] ),
   787 							__( 'Visit plugin site' )
   855 							__( 'Visit plugin site' )
   788 						);
   856 						);
   789 					}
   857 					}
   790 
   858 
   791 					/**
   859 					/**
   792 					 * Filters the array of row meta for each plugin in the Plugins list table.
   860 					 * Filters the array of row meta for each plugin in the Plugins list table.
   793 					 *
   861 					 *
   794 					 * @since 2.8.0
   862 					 * @since 2.8.0
   795 					 *
   863 					 *
   796 					 * @param array  $plugin_meta An array of the plugin's metadata,
   864 					 * @param string[] $plugin_meta An array of the plugin's metadata,
   797 					 *                            including the version, author,
   865 					 *                              including the version, author,
   798 					 *                            author URI, and plugin URI.
   866 					 *                              author URI, and plugin URI.
   799 					 * @param string $plugin_file Path to the plugin file, relative to the plugins directory.
   867 					 * @param string   $plugin_file Path to the plugin file relative to the plugins directory.
   800 					 * @param array  $plugin_data An array of plugin data.
   868 					 * @param array    $plugin_data An array of plugin data.
   801 					 * @param string $status      Status of the plugin. Defaults are 'All', 'Active',
   869 					 * @param string   $status      Status of the plugin. Defaults are 'All', 'Active',
   802 					 *                            'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use',
   870 					 *                              'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use',
   803 					 *                            'Drop-ins', 'Search'.
   871 					 *                              'Drop-ins', 'Search', 'Paused'.
   804 					 */
   872 					 */
   805 					$plugin_meta = apply_filters( 'plugin_row_meta', $plugin_meta, $plugin_file, $plugin_data, $status );
   873 					$plugin_meta = apply_filters( 'plugin_row_meta', $plugin_meta, $plugin_file, $plugin_data, $status );
   806 					echo implode( ' | ', $plugin_meta );
   874 					echo implode( ' | ', $plugin_meta );
   807 
   875 
   808 					echo "</div></td>";
   876 					echo '</div>';
       
   877 
       
   878 					if ( $paused ) {
       
   879 						$notice_text = __( 'This plugin failed to load properly and is paused during recovery mode.' );
       
   880 
       
   881 						printf( '<p><span class="dashicons dashicons-warning"></span> <strong>%s</strong></p>', $notice_text );
       
   882 
       
   883 						$error = wp_get_plugin_error( $plugin_file );
       
   884 
       
   885 						if ( false !== $error ) {
       
   886 							printf( '<div class="error-display"><p>%s</p></div>', wp_get_extension_error_description( $error ) );
       
   887 						}
       
   888 					}
       
   889 
       
   890 					echo '</td>';
   809 					break;
   891 					break;
   810 				default:
   892 				default:
   811 					$classes = "$column_name column-$column_name $class";
   893 					$classes = "$column_name column-$column_name $class";
   812 
   894 
   813 					echo "<td class='$classes{$extra_classes}'>";
   895 					echo "<td class='$classes{$extra_classes}'>";
   816 					 * Fires inside each custom column of the Plugins list table.
   898 					 * Fires inside each custom column of the Plugins list table.
   817 					 *
   899 					 *
   818 					 * @since 3.1.0
   900 					 * @since 3.1.0
   819 					 *
   901 					 *
   820 					 * @param string $column_name Name of the column.
   902 					 * @param string $column_name Name of the column.
   821 					 * @param string $plugin_file Path to the plugin file.
   903 					 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
   822 					 * @param array  $plugin_data An array of plugin data.
   904 					 * @param array  $plugin_data An array of plugin data.
   823 					 */
   905 					 */
   824 					do_action( 'manage_plugins_custom_column', $column_name, $plugin_file, $plugin_data );
   906 					do_action( 'manage_plugins_custom_column', $column_name, $plugin_file, $plugin_data );
   825 
   907 
   826 					echo "</td>";
   908 					echo '</td>';
   827 			}
   909 			}
   828 		}
   910 		}
   829 
   911 
   830 		echo "</tr>";
   912 		echo '</tr>';
   831 
   913 
   832 		/**
   914 		/**
   833 		 * Fires after each row in the Plugins list table.
   915 		 * Fires after each row in the Plugins list table.
   834 		 *
   916 		 *
   835 		 * @since 2.3.0
   917 		 * @since 2.3.0
   836 		 *
   918 		 *
   837 		 * @param string $plugin_file Path to the plugin file, relative to the plugins directory.
   919 		 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
   838 		 * @param array  $plugin_data An array of plugin data.
   920 		 * @param array  $plugin_data An array of plugin data.
   839 		 * @param string $status      Status of the plugin. Defaults are 'All', 'Active',
   921 		 * @param string $status      Status of the plugin. Defaults are 'All', 'Active',
   840 		 *                            'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use',
   922 		 *                            'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use',
   841 		 *                            'Drop-ins', 'Search'.
   923 		 *                            'Drop-ins', 'Search', 'Paused'.
   842 		 */
   924 		 */
   843 		do_action( 'after_plugin_row', $plugin_file, $plugin_data, $status );
   925 		do_action( 'after_plugin_row', $plugin_file, $plugin_data, $status );
   844 
   926 
   845 		/**
   927 		/**
   846 		 * Fires after each specific row in the Plugins list table.
   928 		 * Fires after each specific row in the Plugins list table.
   848 		 * The dynamic portion of the hook name, `$plugin_file`, refers to the path
   930 		 * The dynamic portion of the hook name, `$plugin_file`, refers to the path
   849 		 * to the plugin file, relative to the plugins directory.
   931 		 * to the plugin file, relative to the plugins directory.
   850 		 *
   932 		 *
   851 		 * @since 2.7.0
   933 		 * @since 2.7.0
   852 		 *
   934 		 *
   853 		 * @param string $plugin_file Path to the plugin file, relative to the plugins directory.
   935 		 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
   854 		 * @param array  $plugin_data An array of plugin data.
   936 		 * @param array  $plugin_data An array of plugin data.
   855 		 * @param string $status      Status of the plugin. Defaults are 'All', 'Active',
   937 		 * @param string $status      Status of the plugin. Defaults are 'All', 'Active',
   856 		 *                            'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use',
   938 		 *                            'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use',
   857 		 *                            'Drop-ins', 'Search'.
   939 		 *                            'Drop-ins', 'Search', 'Paused'.
   858 		 */
   940 		 */
   859 		do_action( "after_plugin_row_{$plugin_file}", $plugin_file, $plugin_data, $status );
   941 		do_action( "after_plugin_row_{$plugin_file}", $plugin_file, $plugin_data, $status );
   860 	}
   942 	}
   861 
   943 
   862 	/**
   944 	/**