Skip to content

Commit

Permalink
Support 'wporg_status' and 'wporg_last_updated' as optional `wp plugi…
Browse files Browse the repository at this point in the history
…n list` fields (#382)

* Plugins show their .org status.

* Split the fields to check for wporg and date seperatatly.

* Temp behat file to check

* Added hints for behat that need to be implemented.

* phpcs fixes.

* Also add wp-org checks to mu-plugins.

* Added behat tests.

* Allow to select the status on wporg as a single field.

* Added Behat tests.

* Better check for active plugins.

* Renamed wporg keys

* Replaced the api.wp call with the appropriate class.

* replaced test plugin Akismet, because it's failing php version tests.

* Fixed a missed rename.

* Dropins need a hardcoded exception for wp.org status.

* Removed all no_wp_org key words.

* Fix the feature name in Behat

* Add an example to the command doc

* Regenerate README

---------

Co-authored-by: Daniel Bachhuber <[email protected]>
  • Loading branch information
janw-me and danielbachhuber authored Dec 12, 2023
1 parent 506a20a commit 12618c7
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 35 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,8 @@ These fields are optionally available:
* file
* auto_update
* author
* wporg_status
* wporg_last_updated

**EXAMPLES**

Expand All @@ -408,6 +410,17 @@ These fields are optionally available:
| hello | inactive | none | 1.6 | 1.7.2 |
+---------+----------------+--------+---------+----------------+

# Check whether plugins are still active on WordPress.org
$ wp plugin list --format=csv --fields=name,wporg_status,wporg_last_updated
+--------------------+--------------+--------------------+
| name | wporg_status | wporg_last_updated |
+--------------------+--------------+--------------------+
| akismet | active | 2023-12-11 |
| user-switching | active | 2023-11-17 |
| wordpress-importer | active | 2023-04-28 |
| local | | |
+--------------------+--------------+--------------------+



### wp plugin path
Expand Down
41 changes: 41 additions & 0 deletions features/plugin-list-wporg-status.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Feature: Check the status of plugins on WordPress.org

@require-wp-5.2
Scenario: Install plugins and check the status on wp.org.
Given a WP install

When I run `wp plugin install wordpress-importer --version=0.5 --force`
And I run `wp plugin install https://downloads.wordpress.org/plugin/no-longer-in-directory.1.0.62.zip`
And a wp-content/plugins/never-wporg/never-wporg.php file:
"""
<?php
/**
* Plugin Name: This plugin was never in the WordPress.org plugin directory
* Version: 2.0.2
*/
"""

When I run `wp plugin list --name=wordpress-importer --field=wporg_last_updated`
Then STDOUT should not be empty
And save STDOUT as {COMMIT_DATE}

When I run `wp plugin list --fields=name,wporg_status`
Then STDOUT should be a table containing rows:
| name | wporg_status |
| wordpress-importer | active |
| no-longer-in-directory | closed |
| never-wporg | |

When I run `wp plugin list --fields=name,wporg_last_updated`
Then STDOUT should be a table containing rows:
| name | wporg_last_updated |
| wordpress-importer | {COMMIT_DATE} |
| no-longer-in-directory | 2017-11-13 |
| never-wporg | |

When I run `wp plugin list --fields=name,wporg_status,wporg_last_updated`
Then STDOUT should be a table containing rows:
| name | wporg_status | wporg_last_updated |
| wordpress-importer | active | {COMMIT_DATE} |
| no-longer-in-directory | closed | 2017-11-13 |
| never-wporg | | |
168 changes: 133 additions & 35 deletions src/Plugin_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use WP_CLI\ParsePluginNameInput;
use WP_CLI\Utils;
use WP_CLI\WpOrgApi;

/**
* Manages plugins, including installs, activations, and updates.
Expand Down Expand Up @@ -46,6 +47,10 @@ class Plugin_Command extends \WP_CLI\CommandWithUpgrade {
protected $item_type = 'plugin';
protected $upgrade_refresh = 'wp_update_plugins';
protected $upgrade_transient = 'update_plugins';
protected $check_wporg = [
'status' => false,
'last_updated' => false,
];

protected $obj_fields = array(
'name',
Expand Down Expand Up @@ -245,19 +250,23 @@ protected function get_all_items() {
if ( ! empty( $mu_plugin['Description'] ) ) {
$mu_description = $mu_plugin['Description'];
}
$mu_name = Utils\get_plugin_name( $file );
$wporg_info = $this->get_wporg_data( $mu_name );

$items[ $file ] = array(
'name' => Utils\get_plugin_name( $file ),
'status' => 'must-use',
'update' => false,
'update_version' => null,
'update_package' => null,
'version' => $mu_version,
'update_id' => '',
'title' => $mu_title,
'description' => $mu_description,
'file' => $file,
'auto_update' => false,
'name' => $mu_name,
'status' => 'must-use',
'update' => false,
'update_version' => null,
'update_package' => null,
'version' => $mu_version,
'update_id' => '',
'title' => $mu_title,
'description' => $mu_description,
'file' => $file,
'auto_update' => false,
'wporg_status' => $wporg_info['status'],
'wporg_last_updated' => $wporg_info['last_updated'],
);
}

Expand All @@ -266,17 +275,19 @@ protected function get_all_items() {
foreach ( $raw_items as $name => $item_data ) {
$description = ! empty( $raw_data[ $name ][0] ) ? $raw_data[ $name ][0] : '';
$items[ $name ] = [
'name' => $name,
'title' => $item_data['Title'],
'description' => $description,
'status' => 'dropin',
'update' => false,
'update_version' => null,
'update_package' => null,
'update_id' => '',
'file' => $name,
'auto_update' => false,
'author' => $item_data['Author'],
'name' => $name,
'title' => $item_data['Title'],
'description' => $description,
'status' => 'dropin',
'update' => false,
'update_version' => null,
'update_package' => null,
'update_id' => '',
'file' => $name,
'auto_update' => false,
'author' => $item_data['Author'],
'wporg_status' => '',
'wporg_last_updated' => '',
];
}

Expand Down Expand Up @@ -703,29 +714,31 @@ protected function get_item_list() {
$all_update_info = $this->get_update_info();
$update_info = ( isset( $all_update_info->response[ $file ] ) && null !== $all_update_info->response[ $file ] ) ? (array) $all_update_info->response[ $file ] : null;
$name = Utils\get_plugin_name( $file );
$wporg_info = $this->get_wporg_data( $name );

if ( ! isset( $duplicate_names[ $name ] ) ) {
$duplicate_names[ $name ] = array();
}

$duplicate_names[ $name ][] = $file;
$items[ $file ] = [
'name' => $name,
'status' => $this->get_status( $file ),
'update' => (bool) $update_info,
'update_version' => isset( $update_info ) && isset( $update_info['new_version'] ) ? $update_info['new_version'] : null,
'update_package' => isset( $update_info ) && isset( $update_info['package'] ) ? $update_info['package'] : null,
'version' => $details['Version'],
'update_id' => $file,
'title' => $details['Name'],
'description' => wordwrap( $details['Description'] ),
'file' => $file,
'auto_update' => in_array( $file, $auto_updates, true ),
'author' => $details['Author'],
'name' => $name,
'status' => $this->get_status( $file ),
'update' => (bool) $update_info,
'update_version' => isset( $update_info ) && isset( $update_info['new_version'] ) ? $update_info['new_version'] : null,
'update_package' => isset( $update_info ) && isset( $update_info['package'] ) ? $update_info['package'] : null,
'version' => $details['Version'],
'update_id' => $file,
'title' => $details['Name'],
'description' => wordwrap( $details['Description'] ),
'file' => $file,
'auto_update' => in_array( $file, $auto_updates, true ),
'author' => $details['Author'],
'wporg_status' => $wporg_info['status'],
'wporg_last_updated' => $wporg_info['last_updated'],
];

if ( null === $update_info ) {

// Get info for all plugins that don't have an update.
$plugin_update_info = isset( $all_update_info->no_update[ $file ] ) ? $all_update_info->no_update[ $file ] : null;

Expand All @@ -748,6 +761,64 @@ protected function get_item_list() {
return $items;
}

/**
* Get the wordpress.org status of a plugin.
*
* @param string $plugin_name The plugin slug.
*
* @return string The status of the plugin, includes the last update date.
*/
protected function get_wporg_data( $plugin_name ) {
$data = [
'status' => '',
'last_updated' => '',
];
if ( ! $this->check_wporg['status'] && ! $this->check_wporg['last_updated'] ) {
return $data;
}

if ( $this->check_wporg ) {
try {
$plugin_data = ( new WpOrgApi() )->get_plugin_info( $plugin_name );
} catch ( Exception $e ) {
// Request failed. The plugin is not (active) on .org.
$plugin_data = false;
}
if ( $plugin_data ) {
$data['status'] = 'active';
if ( ! $this->check_wporg['last_updated'] ) {
return $data; // The plugin is active on .org, but we don't need the date.
}
}
// Just because the plugin is not in the api, does not mean it was never on .org.
}

$request = wp_remote_get( "https://plugins.trac.wordpress.org/log/{$plugin_name}/?limit=1&mode=stop_on_copy&format=rss" );
$response_code = wp_remote_retrieve_response_code( $request );
if ( 404 === $response_code ) {
return $data; // This plugin was never on .org, there is no date to check.
}
if ( 'active' !== $data['status'] ) {
$data['status'] = 'closed'; // This plugin was on .org at some point, but not anymore.
}
if ( ! class_exists( 'SimpleXMLElement' ) ) {
WP_CLI::error( "The PHP extension 'SimpleXMLElement' is not available but is required for XML-formatted output." );
}

// Check the last update date.
$r_body = wp_remote_retrieve_body( $request );
if ( str_contains( $r_body, 'pubDate' ) ) {
// Very raw check, not validating the format or anything else.
$xml = simplexml_load_string( $r_body );
$xml_pub_date = $xml->xpath( '//pubDate' );
if ( $xml_pub_date ) {
$data['last_updated'] = wp_date( 'Y-m-d', (string) strtotime( $xml_pub_date[0] ) );
}
}

return $data;
}

protected function filter_item_list( $items, $args ) {
$basenames = wp_list_pluck( $this->fetcher->get_many( $args ), 'file' );
return Utils\pick_fields( $items, $basenames );
Expand Down Expand Up @@ -1188,6 +1259,8 @@ public function delete( $args, $assoc_args = array() ) {
* * file
* * auto_update
* * author
* * wporg_status
* * wporg_last_updated
*
* ## EXAMPLES
*
Expand All @@ -1210,9 +1283,34 @@ public function delete( $args, $assoc_args = array() ) {
* | hello | inactive | none | 1.6 | 1.7.2 |
* +---------+----------------+--------+---------+----------------+
*
* # Check whether plugins are still active on WordPress.org
* $ wp plugin list --format=csv --fields=name,wporg_status,wporg_last_updated
* +--------------------+--------------+--------------------+
* | name | wporg_status | wporg_last_updated |
* +--------------------+--------------+--------------------+
* | akismet | active | 2023-12-11 |
* | user-switching | active | 2023-11-17 |
* | wordpress-importer | active | 2023-04-28 |
* | local | | |
* +--------------------+--------------+--------------------+
*
* @subcommand list
*/
public function list_( $_, $assoc_args ) {
$fields = Utils\get_flag_value( $assoc_args, 'fields' );
if ( ! empty( $fields ) ) {
$fields = explode( ',', $fields );
$this->check_wporg['status'] = in_array( 'wporg_status', $fields, true );
$this->check_wporg['last_updated'] = in_array( 'wporg_last_updated', $fields, true );
}

$field = Utils\get_flag_value( $assoc_args, 'field' );
if ( 'wporg_status' === $field ) {
$this->check_wporg['status'] = true;
} elseif ( 'wporg_last_updated' === $field ) {
$this->check_wporg['last_updated'] = true;
}

parent::_list( $_, $assoc_args );
}

Expand Down

0 comments on commit 12618c7

Please sign in to comment.