Skip to content

Commit

Permalink
1.3.0
Browse files Browse the repository at this point in the history
[Added] Plugin Settings Page (under Admin > Settings > Manage CP Repos)
[Added] Setting to add custom GitHub Repositories of Orgs, Users, and single repos
[Added] Setting to store Personal GitHub Token, which increases the API Limits to 5k hourly instead of 60.
[Added] Verified Orgs (not users) are pre-selected. A PR can be used to add new Orgs to the vetted list.
[Added] Fundations to read remote readme, README, (both in md or txt) files. Currently used ony for below [Fixed] item.
[Added] POT file for translators
[Fixed] Untranslatable strings
[Fixed] Problem where plugins with foldername/distinct-filename.php AND a unguessable Plugin Title could not be managed.
[Improved] Make less calls to the GitHub API by re-using already queried data as much as possible.
[Changed] Refactored code base
  • Loading branch information
smileBeda authored Jul 8, 2022
2 parents 0c1694a + 0169970 commit 7a8e505
Show file tree
Hide file tree
Showing 21 changed files with 2,243 additions and 791 deletions.
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# Auto detect text files and perform LF normalization
* text=auto
.gitattributes export-ignore
.gitignore export-ignore
.git export-ignore
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

cp-plugins.txt
.DS_Store
27 changes: 19 additions & 8 deletions README.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
=== ClassicPress Plugin Directory ===
=== ClassicPress Directory Integration ===
Contributors: bedas
Donate link: https://paypal.me/tukutoi
Tags: directory, plugins
Requires at least: 1.0.0
Tested up to: 4.9.15
Stable tag: 1.2.0
Stable tag: 1.3.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Expand All @@ -16,7 +16,7 @@ Install and activate like any other plugin.

Navigate to Dashboard > Plugins > Manage CP Plugins and start managing ClassicPress Plugins.
You can install, activate, deactivate, update, delete, and also search Plugins all from within the same screen.
The Directory results are cached locally for fast performance, and you can refresh the local cache on the click of a button.
The results are cached locally for fast performance, and you can refresh the local cache on the click of a button.

It has a pagination and a total plugins display to navigate (15 plugins a time) through the assets.
A "more info" will display all information known to ClassicPress about the plugin and developer.
Expand All @@ -27,11 +27,22 @@ The plugin requires wp_remote_get and file_put_contents to work properly on the

It is possible to manage plugins that are not listed in the ClassicPress Directory with this plugin as well.
The conditions for this to work are:
- the GitHub stored Plugin MUST have a tag `classicpress-plugin`
- the GitHub Repository MUST have a valid Release tag named witha SemVer release version (like `1.0.0`) and Public Release with a manually uploaded Release Asset in Zip Format. This ZIP MUST be uploaded to the release section for `Attach binaries by dropping them here or selecting them.`
- currently only plugins stored by the TukuToi Organization are available - in the next release, a setting will be offered to end users in order to register any organziation or user.
- the GitHub stored Plugin MUST have a tag `classicpress-plugin`.
- the GitHub Repository MUST have a valid Release tag named with a SemVer release version (like `1.0.0`) .
- the release MUST have a manually uploaded Zip Asset uploaded to the release section for `Attach binaries by dropping them here or selecting them.` holding the plugin.
- the repository MUST have EITHER OR BOTH a readme.txt OR readme.md (can be all uppercase too). The readme.txt is prioritized and MUST follow the WordPress readme.txt rules with the EXCEPTION that the first line MUST match the plugin name from the plugin main file.
- The readme.md file is used only as backup, and if used, MUST have at least one line featuring `# Plugin Name Here`.
- the repository MUST be public.

By default, there is a _vetted list_ of _Organizations_ added to the plugin. If a Developer wants to appear on said list,
they can submit a PR to the `github-orgs.txt` File of this Plugin, by adding their Guthub Organization data to the JSON array.
The Organization AND the PR initiator will be reviewed both by the author of this plugin as well the ClassicPress Plugin Review Team.
Only after careful assessment the Developer will be added to the Verified List of Organizations, and thus appear pre-selected in the Repositories queried by this plugin.

Other, non verified Repositories (both users and orgs) can still be added by an end user in the dedicated Settings page (Dashboard > Settings > Manage CP Repos).

== Disclaimers ==
- The plugin does not take any responsibility for Plugins downloaded from the ClassicPress Directory or GitHub.
- The plugin does not take any responsibility for Plugins downloaded from the ClassicPress Directory or GitHub, not even if verified Organization's software.
- The ClassicPress Plugin Repository is not always well maintained by the Developers who list their plugins. They forget often to bump the Version Number of their Plugins. This means, you *might* not see an update, even if there is one, or you might see an update to a certain version and get an update to a much higher version.
- If a GitHub stored plugin is not following above (MUST) clauses, it will not be possible for this plugin to find, pull or else manage such repos.
- If a GitHub stored plugin is not following above (MUST) clauses, it will not be possible for this plugin to find, pull or else manage such repos.
- If you run into GitHub API Limits (it is not so generous) you should create a Personal Authentication Token as shown [here](https://docs.github.com/en/[email protected]/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token). You should only give this Token "read" rights, no post or edit rights. You should _never_ share this Token with anyone. You should then store this Token on the setting for it under Dashboard > Settings > Manage CP Repos. This will bump your GitHub API limits to 5000 per hours (which is far enough).
243 changes: 74 additions & 169 deletions admin/class-cp-plgn-drctry-admin.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/**
* The admin-specific functionality of the plugin.
* Adds a class to handle general Admin Area features.
*
* @link https://www.tukutoi.com/
* @since 1.0.0
Expand All @@ -10,11 +10,11 @@
*/

/**
* The admin-specific functionality of the plugin.
* Class to handle general Admin Area Features.
*
* Defines the plugin name, version, and two hooks to
* enqueue the admin-facing stylesheet and JavaScript.
* As you add hooks and methods, update this description.
* Enqueues scripts and styles
* Adds Plugin Pages to Admin Menu
* Renders settings and plugin list page
*
* @package Cp_Plgn_Drctry
* @subpackage Cp_Plgn_Drctry/admin
Expand Down Expand Up @@ -49,15 +49,6 @@ class Cp_Plgn_Drctry_Admin {
*/
private $version;

/**
* The CP Dir Class instance.
*
* @since 1.0.0
* @access private
* @var object $cp_dir The Class reference for the ClassicPress Plugin Directory.
*/
private $cp_dir;

/**
* Initialize the class and set its properties.
*
Expand All @@ -71,7 +62,6 @@ public function __construct( $plugin_name, $plugin_prefix, $version ) {
$this->plugin_name = $plugin_name;
$this->plugin_prefix = $plugin_prefix;
$this->version = $version;
$this->cp_dir = new Cp_Plgn_Drctry_Cp_Dir( $plugin_name, $plugin_prefix, $version );

}

Expand All @@ -84,7 +74,10 @@ public function __construct( $plugin_name, $plugin_prefix, $version ) {
public function enqueue_styles( $hook_suffix ) {

if ( 'plugins_page_cp-plugins' === $hook_suffix ) {
wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/cp-plgn-drctry-admin.css', array(), $this->version, 'all' );
wp_enqueue_style( $this->plugin_prefix . 'plugins', plugin_dir_url( __FILE__ ) . 'css/cp-plgn-drctry-admin.css', array(), $this->version, 'all' );
} elseif ( 'settings_page_cp_dir_opts' === $hook_suffix ) {
wp_enqueue_style( 'select2', 'https://cdn.jsdelivr.net/npm/[email protected]/dist/css/select2.min.css', array(), '4.1.0-rc.0', 'all' );
wp_enqueue_style( $this->plugin_prefix . 'settings', plugin_dir_url( __FILE__ ) . 'css/cp-plgn-drctry-settings.css', array( 'select2' ), $this->version, 'all' );
}

}
Expand All @@ -99,170 +92,36 @@ public function enqueue_scripts( $hook_suffix ) {

if ( 'plugins_page_cp-plugins' === $hook_suffix ) {
add_thickbox();
wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/cp-plgn-drctry-admin.js', array( 'jquery' ), $this->version, false );
wp_enqueue_script( $this->plugin_prefix . 'plugins', plugin_dir_url( __FILE__ ) . 'js/cp-plgn-drctry-admin.js', array( 'jquery' ), $this->version, false );
wp_localize_script(
$this->plugin_name,
$this->plugin_prefix . 'plugins',
'ajax_object',
array(
'ajax_url' => esc_url( admin_url( 'admin-ajax.php' ) ),
'admin_url' => esc_url( get_admin_url( null, '', 'admin' ) ),
'nonce' => wp_create_nonce( 'updates' ),
)
);
} elseif ( 'settings_page_cp_dir_opts' === $hook_suffix ) {
wp_enqueue_script( 'select2', 'https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js', array( 'jquery' ), '4.1.0-rc.0', false );
wp_enqueue_script( $this->plugin_prefix . 'settings', plugin_dir_url( __FILE__ ) . 'js/cp-plgn-drctry-settings.js', array( 'select2' ), $this->version, false );
wp_localize_script(
$this->plugin_prefix . 'settings',
'settings_object',
array(
'placeholder' => esc_html_( 'Type and press return to add a new Item.', 'cp-plgn-drctry' ),
)
);
}
}

/**
* Install a Plugin.
* Add Menu Pages.
*
* @since 1.1.3 Added overwrite_package argument
* @param bool $overwrite Whether to overwrite the plugin or not. Default False.
*/
public function install_cp_plugin( $overwrite = false ) {

if ( ! isset( $_POST['_ajax_nonce'] )
|| empty( $_POST['_ajax_nonce'] )
|| ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_ajax_nonce'] ) ), 'updates' ) ) {
die( 'Invalid or missing Nonce!' );
}

if ( ! isset( $_POST['url'] ) ) {
wp_send_json( 'Something went wrong' );
}

/**
* We include Upgrader Class.
*
* @todo Check this path on EACH CP UPDATE. It might change!
*/
include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
$upgrader = new Plugin_Upgrader();
$response = $upgrader->install( esc_url_raw( wp_unslash( $_POST['url'] ) ), array( 'overwrite_package' => $overwrite ) );

wp_send_json( $response );

}

/**
* Update a Plugin.
* @since 1.0.0 Add Plugins List Page.
* @since 1.3.0 Add Settings Page.
*/
public function update_cp_plugin() {

if ( ! isset( $_POST['_ajax_nonce'] )
|| empty( $_POST['_ajax_nonce'] )
|| ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_ajax_nonce'] ) ), 'updates' ) ) {
die( 'Invalid or missing Nonce!' );
}

if ( ! isset( $_POST['slug'] ) ) {
wp_send_json( 'Something went wrong' );
}

/**
* We cannot use Upgrader Class, because CP has no way of
* selecting custom file URL. Only WP Can do that.
*
* We simply replace the plugin entirely.
*
* @since 1.0.0 Update Plugin
* @since 1.1.3 Update itself
*/
$this->install_cp_plugin( true );

}

/**
* Delete a Plugin.
*/
public function delete_cp_plugin() {

if ( ! isset( $_POST['_ajax_nonce'] )
|| empty( $_POST['_ajax_nonce'] )
|| ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_ajax_nonce'] ) ), 'updates' ) ) {
die( 'Invalid or missing Nonce!' );
}

if ( ! isset( $_POST['plugin'] ) ) {
wp_send_json( 'Something went wrong' );
}

/**
* This returns true on success, false if $Plugin is empty,
* null if creds are missing, WP Error on failure.
*/
$deleted = delete_plugins( array( sanitize_text_field( wp_unslash( $_POST['plugin'] ) ) ) );

if ( false === $deleted ) {
// creds are missing.
$deleted = 'The Plugin Slug is missing from delete_plugins() function.';
} elseif ( null === $deleted ) {
$deleted = 'Filesystem Credentials are required. You are not allowed to perform this action.';
} elseif ( is_wp_error( $deleted ) ) {
$deleted = 'There has been an error. Please check the error logs.';
} elseif ( true !== $deleted ) {
$deleted = 'Unknown error occurred';
}

wp_send_json( $deleted );

}

/**
* Deactivate a Plugin.
*/
public function deactivate_cp_plugin() {

if ( ! isset( $_POST['_ajax_nonce'] )
|| empty( $_POST['_ajax_nonce'] )
|| ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_ajax_nonce'] ) ), 'updates' ) ) {
die( 'Invalid or missing Nonce!' );
}

if ( ! isset( $_POST['slug'] ) ) {
wp_send_json( 'Something went wrong' );
}

/**
* This function does not return anything.
* We have no way of knowing whether the plugin was deactivated or not.
* We however reload the page in JS after this operation, so the new status will tell.
*/
deactivate_plugins( sanitize_text_field( wp_unslash( $_POST['slug'] ) ), true );

wp_send_json( 'Plugin Possibly Updated' );

}

/**
* Activate a Plugin.
*/
public function activate_cp_plugin() {

if ( ! isset( $_POST['_ajax_nonce'] )
|| empty( $_POST['_ajax_nonce'] )
|| ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_ajax_nonce'] ) ), 'updates' ) ) {
die( 'Invalid or missing Nonce!' );
}

if ( ! isset( $_POST['slug'] ) ) {
wp_send_json( 'Something went wrong' );
}

/**
* The function returns a WP error if something went wrong,
* null otherwise.
*/
$activated = activate_plugin( sanitize_text_field( wp_unslash( $_POST['slug'] ) ) );

wp_send_json( $activated );

}

/**
* Creates the submenu item and calls on the Submenu Page object to render
* the actual contents of the page.
*/
public function add_plugins_list() {
public function add_menu_pages() {

add_submenu_page(
'plugins.php',
Expand All @@ -274,19 +133,65 @@ public function add_plugins_list() {
3
);

add_submenu_page(
'options-general.php',
esc_html__( 'ClassicPress Repositories', 'cp-plgn-drctry' ),
esc_html__( 'Manage CP Repos', 'cp-plgn-drctry' ),
'manage_options',
'cp_dir_opts',
array( $this, 'dir_settings_render' ),
3
);

}

/**
* Render the admin page.
* Render the plugins list page.
*/
public function render() {
?>
<div id="loadingDiv" style="display:none"><span class="spinner"></span></div>
<div class="wrap">
<h1><?php esc_html_e( 'ClassicPress Plugins', 'cp-plgn-drctry' ); ?></h1>
<p><?php esc_html_e( 'Browse, Install and Activate ClassicPress Plugins', 'cp-plgn-drctry' ); ?></p>
<p><?php esc_html_e( 'Browse, Install, Activate, Deactivate, Update and Delete ClassicPress Plugins', 'cp-plgn-drctry' ); ?></p>
<div class="notice notice-error" id="cp-plgn-drctry-error" style="display:none;"></div>
<?php $this->cp_dir->list_plugins(); ?>
<?php
$cp_dir = new Cp_Plgn_Drctry_Cp_Plugins_Dir( $this->plugin_name, $this->plugin_prefix, $this->version );
$cp_dir->list_plugins();
?>
</div>
<?php
}

/**
* Render the settings page.
*
* @since 1.3.0
*/
public function dir_settings_render() {

/**
* Show error/update message.
*/
settings_errors( 'cp_dir_opts_messages' );

?>
<div class="wrap">
<h1><?php esc_html_e( 'ClassicPress Repositories', 'cp-plgn-drctry' ); ?></h1>
<form action="options.php" method="post">
<?php
// output security fields for the registered setting "cp_dir_opts".
settings_fields( 'cp_dir_opts' );
/**
* Output setting sections and their fields
* Sections are registered for "cp_dir_opts",
* each field is registered to a specific section)
*/
do_settings_sections( 'cp_dir_opts' );
// output save settings button.
submit_button( 'Save Settings' );
?>
</form>
</div>
<?php
}
Expand Down
Loading

0 comments on commit 7a8e505

Please sign in to comment.