From 3c16b2335eacb7aa5f2eb1869a8b3cce7018b670 Mon Sep 17 00:00:00 2001 From: ingeniumed Date: Mon, 24 Jun 2024 15:30:06 +1000 Subject: [PATCH 1/9] Add the VIP feature flag, and fix all the phpcs errors in the settings files --- edit_flow.php | 4 +- modules/settings/settings.php | 245 ++++++++++++++++++++-------------- phpcs.xml.dist | 23 +++- 3 files changed, 169 insertions(+), 103 deletions(-) diff --git a/edit_flow.php b/edit_flow.php index 6192579e3..de29082a3 100644 --- a/edit_flow.php +++ b/edit_flow.php @@ -367,11 +367,11 @@ function get_module_by( $key, $value ) { $module = false; foreach ( $this->modules as $mod_name => $mod_data ) { - if ( $key == 'name' && $value == $mod_name ) { + if ( 'name' === $key && $value === $mod_name ) { $module = $this->modules->$mod_name; } else { foreach ( $mod_data as $mod_data_key => $mod_data_value ) { - if ( $mod_data_key == $key && $mod_data_value == $value ) { + if ( $mod_data_key === $key && $mod_data_value === $value ) { $module = $this->modules->$mod_name; } } diff --git a/modules/settings/settings.php b/modules/settings/settings.php index 133f52f36..8262e60f2 100644 --- a/modules/settings/settings.php +++ b/modules/settings/settings.php @@ -3,14 +3,13 @@ if ( !class_exists('EF_Settings') ) { class EF_Settings extends EF_Module { - + var $module; - + /** * Register the module with Edit Flow but don't do anything else */ - function __construct() { - + function __construct() { // Register the module with Edit Flow $this->module_url = $this->get_module_url( __FILE__ ); $args = array( @@ -23,29 +22,31 @@ function __construct() { 'settings_slug' => 'ef-settings', 'default_options' => array( 'enabled' => 'on', + 'vip_features' => 'off', + 'analytics' => 'off', ), 'configure_page_cb' => 'print_default_settings', 'autoload' => true, ); $this->module = EditFlow()->register_module( 'settings', $args ); } - + /** * Initialize the rest of the stuff in the class if the module is active */ function init() { - - add_action( 'admin_init', array( $this, 'helper_settings_validate_and_save' ), 100 ); - + add_action( 'admin_init', array( $this, 'helper_settings_validate_and_save' ), 100 ); + add_action( 'admin_init', array( $this, 'register_settings' ) ); + add_action( 'admin_print_styles', array( $this, 'action_admin_print_styles' ) ); add_action( 'admin_print_scripts', array( $this, 'action_admin_print_scripts' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'action_admin_enqueue_scripts' ) ); add_action( 'admin_menu', array( $this, 'action_admin_menu' ) ); - + add_action( 'wp_ajax_change_edit_flow_module_state', array( $this, 'ajax_change_edit_flow_module_state' ) ); - + } - + /** * Add necessary things to the admin menu */ @@ -53,34 +54,29 @@ function action_admin_menu() { global $edit_flow; $ef_logo = 'lib/eflogo_s32w.png'; - + add_menu_page( $this->module->title, $this->module->title, 'manage_options', $this->module->settings_slug, array( $this, 'settings_page_controller' ), $this->module->module_url . $ef_logo ) ; - + foreach ( $edit_flow->modules as $mod_name => $mod_data ) { if ( isset( $mod_data->options->enabled ) && $mod_data->options->enabled == 'on' && $mod_data->configure_page_cb && $mod_name != $this->module->name ) add_submenu_page( $this->module->settings_slug, $mod_data->title, $mod_data->title, 'manage_options', $mod_data->settings_slug, array( $this, 'settings_page_controller' ) ) ; } } - + function action_admin_enqueue_scripts() { - if ( $this->is_whitelisted_settings_view() ) wp_enqueue_script( 'edit-flow-settings-js', $this->module_url . 'lib/settings.js', array( 'jquery' ), EDIT_FLOW_VERSION, true ); - } - + /** * Add settings styles to the settings page */ - function action_admin_print_styles() { - + function action_admin_print_styles() { if ( $this->is_whitelisted_settings_view() ) wp_enqueue_style( 'edit_flow-settings-css', $this->module_url . 'lib/settings.css', false, EDIT_FLOW_VERSION ); - - } - + /** * Extra data we need on the page for transitions, etc. * @@ -89,68 +85,83 @@ function action_admin_print_styles() { function action_admin_print_scripts() { ?> get_module_by( 'slug', $slug ); - + if ( !$module ) die('-1'); - + if ( $module_action == 'enable' ) $return = $edit_flow->update_module_option( $module->name, 'enabled', 'on' ); else if ( $module_action == 'disable' ) $return = $edit_flow->update_module_option( $module->name, 'enabled', 'off' ); - + if ( $return ) die('1'); else die('-1'); } - + /** * Handles all settings and configuration page requests. Required element for Edit Flow */ function settings_page_controller() { global $edit_flow; - - $requested_module = $edit_flow->get_module_by( 'settings_slug', $_GET['page'] ); + + $page_requested = isset( $_GET['page'] ) ? sanitize_key($_GET['page']) : 'settings'; + $requested_module = $edit_flow->get_module_by( 'settings_slug', $page_requested ); if ( !$requested_module ) - wp_die( __( 'Not a registered Edit Flow module', 'edit-flow' ) ); + wp_die( esc_html__( 'Not a registered Edit Flow module', 'edit-flow' ) ); + $configure_callback = $requested_module->configure_page_cb; - $requested_module_name = $requested_module->name; - + $requested_module_name = $requested_module->name; + // Don't show the settings page for the module if the module isn't activated if ( !$this->module_enabled( $requested_module_name ) ) { - echo '

' . sprintf( __( 'Module not enabled. Please enable it from the Edit Flow settings page.', 'edit-flow' ), EDIT_FLOW_SETTINGS_PAGE ) . '

'; + echo '

' . wp_kses(sprintf( __( 'Module not enabled. Please enable it from the Edit Flow settings page.', 'edit-flow' ), esc_url(EDIT_FLOW_SETTINGS_PAGE )), 'a') . '

'; return; - } - + } + $this->print_default_header( $requested_module ); $edit_flow->$requested_module_name->$configure_callback(); $this->print_default_footer( $requested_module ); - + } - + /** + * Register settings for notifications so we can partially use the Settings API + * We use the Settings API for form generation, but not saving because we have our + * own way of handling the data. * + * @since 0.7 + */ + function register_settings() { + add_settings_section( $this->module->options_group_name . '_general', false, '__return_false', $this->module->options_group_name ); + add_settings_field( 'analytics', __( 'Turn on analytics', 'edit-flow' ), array( $this, 'settings_analytics_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' ); + add_settings_field( 'vip_features', __( 'Turn on WordPress VIP features', 'edit-flow' ), array( $this, 'settings_vip_features_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' ); + } + + /** + * Disabling nonce verification because that is not available here, it's just rendering it. The actual save is done in helper_settings_validate_and_save and that's guarded well. + * phpcs:disable:WordPress.Security.NonceVerification.Missing */ function print_default_header( $current_module ) { - // If there's been a message, let's display it if ( isset( $_GET['message'] ) ) $message = $_GET['message']; @@ -162,7 +173,7 @@ function print_default_header( $current_module ) { $message = false; if ( $message && isset( $current_module->messages[$message] ) ) $display_text = '' . esc_html( $current_module->messages[$message] ) . ''; - + // If there's been an error, let's display it if ( isset( $_GET['error'] ) ) $error = $_GET['error']; @@ -174,7 +185,7 @@ function print_default_header( $current_module ) { $error = false; if ( $error && isset( $current_module->messages[$error] ) ) $display_text = '' . esc_html( $current_module->messages[$error] ) . ''; - + if ( $current_module->img_url ) $page_icon = ''; else @@ -182,44 +193,80 @@ function print_default_header( $current_module ) { ?>
name != 'settings' ): ?> - -

title; ?>

+ +

title); ?>

- -

+ +

- +
short_description ): ?> -

short_description; ?>

+

short_description, 'a'); ?>

extended_description ): ?> -

extended_description; ?>

- +

extended_description, 'a'); ?>

+
print_modules(); ?>
- module->settings_slug, false ) ); ?>" method="post"> + module->options_group_name ); ?> + module->options_group_name ); ?> + module->name ) . '" />'; + ?> +

+ + __( 'Disabled', 'edit-flow' ), + 'on' => __( 'Enabled', 'edit-flow' ), + ); + echo ''; + } + + function settings_analytics_option() { + $options = array( + 'off' => __( 'Disabled', 'edit-flow' ), + 'on' => __( 'Enabled', 'edit-flow' ), + ); + echo ''; + } + function print_default_footer( $current_module ) { ?> slug == 'settings' ): ?>
-

Edit Flow is produced by Daniel Bachhuber, Mo Jangda, and Scott Bressler, with special help from Andrew Spittle and Andrew Witherspoon.', 'edit-flow' ); ?> -
-
Noun Project.', 'edit-flow' ); ?> -
Please give us your feedback, ideas, bug reports and comments in the WordPress.org forums.', 'edit-flow' ); ?> +

Edit Flow is produced by Daniel Bachhuber, Mo Jangda, and Scott Bressler, with special help from Andrew Spittle and Andrew Witherspoon.', 'edit-flow' ), 'a'); ?> +
+
Noun Project.', 'edit-flow' ), 'a'); ?> +
Please give us your feedback, ideas, bug reports and comments in the WordPress.org forums.', 'edit-flow' ), 'a'); ?>

@@ -230,7 +277,7 @@ function print_modules() { global $edit_flow; if ( ! $edit_flow->modules_count ) { - echo '
' . __( 'There are no Edit Flow modules registered', 'edit-flow' ) . '
'; + echo '
' . esc_html__( 'There are no Edit Flow modules registered', 'edit-flow' ) . '
'; } else { foreach ( $edit_flow->modules as $mod_name => $mod_data ) { @@ -246,38 +293,38 @@ function print_modules() { $classes[] = 'module-disabled'; if ( $mod_data->configure_page_cb ) $classes[] = 'has-configure-link'; - echo '
'; + echo '
'; if ( $mod_data->img_url ) echo ''; - echo '
'; + echo ''; echo '

' . esc_html( $mod_data->title ) . '

'; if ( 'on' == $mod_data->options->enabled ) echo '

' . wp_kses($mod_data->short_description, 'a') . '

'; else - echo '

' . strip_tags( $mod_data->short_description ) . '

'; + echo '

' . esc_html( $mod_data->short_description ) . '

'; echo '

'; if ( $mod_data->configure_page_cb ) { $configure_url = add_query_arg( 'page', $mod_data->settings_slug, get_admin_url( null, 'admin.php' ) ); - echo ''; + echo '">' . esc_html__($mod_data->configure_link_text) . ''; } echo 'options->enabled == 'on' ) echo ' style="display:none;"'; - echo ' value="' . __( 'Enable', 'edit-flow' ) . '" />'; + if ( $mod_data->options->enabled == 'on' ) echo ' style="display:none;"'; + echo ' value="' . esc_textarea(__( 'Enable', 'edit-flow' )) . '" />'; echo 'options->enabled == 'off' ) echo ' style="display:none;"'; - echo ' value="' . __( 'Disable', 'edit-flow' ) . '" />'; + if ( $mod_data->options->enabled == 'off' ) echo ' style="display:none;"'; + echo ' value="' . esc_textarea(__( 'Disable', 'edit-flow' )) . '" />'; echo '

'; wp_nonce_field( 'change-edit-flow-module-nonce', 'change-module-nonce-' . $mod_data->slug, false ); echo '
'; echo '
'; } - + } - + } - + /** * Given a form field and a description, prints either the error associated with the field or the description. * @@ -289,13 +336,13 @@ function print_modules() { function helper_print_error_or_description( $field, $description ) { if ( isset( $_REQUEST['form-errors'][$field] ) ): ?>
-

+

__( 'Posts' ), 'page' => __( 'Pages' ), @@ -320,58 +367,58 @@ function helper_option_custom_post_type( $module, $args = array() ) { foreach( $all_post_types as $post_type => $title ) { echo ''; // Leave a note to the admin as a reminder that add_post_type_support has been used somewhere in their code if ( post_type_supports( $post_type, $module->post_type_support ) ) - echo '   ' . sprintf( __( 'Disabled because add_post_type_support( \'%1$s\', \'%2$s\' ) is included in a loaded file.', 'edit-flow' ), $post_type, $module->post_type_support ) . ''; + echo '   ' . esc_html(sprintf( __( 'Disabled because add_post_type_support( \'%1$s\', \'%2$s\' ) is included in a loaded file.', 'edit-flow' ), $post_type, $module->post_type_support )) . ''; echo '
'; } - + } - + /** * Validation and sanitization on the settings field * This method is called automatically/ doesn't need to be registered anywhere * - * @since 0.7 + * @since 0.7 */ function helper_settings_validate_and_save() { - + if ( !isset( $_POST['action'], $_POST['_wpnonce'], $_POST['option_page'], $_POST['_wp_http_referer'], $_POST['edit_flow_module_name'], $_POST['submit'] ) || !is_admin() ) return false; - - global $edit_flow; + + global $edit_flow; $module_name = sanitize_key( $_POST['edit_flow_module_name'] ); - - if ( $_POST['action'] != 'update' + + if ( $_POST['action'] != 'update' || $_POST['option_page'] != $edit_flow->$module_name->module->options_group_name ) return false; - + if ( !current_user_can( 'manage_options' ) || !wp_verify_nonce( $_POST['_wpnonce'], $edit_flow->$module_name->module->options_group_name . '-options' ) ) - wp_die( __( 'Cheatin’ uh?' ) ); - + wp_die( esc_html__( 'Cheatin’ uh?' ) ); + $new_options = ( isset( $_POST[$edit_flow->$module_name->module->options_group_name] ) ) ? $_POST[$edit_flow->$module_name->module->options_group_name] : array(); // Only call the validation callback if it exists? if ( method_exists( $edit_flow->$module_name, 'settings_validate' ) ) $new_options = $edit_flow->$module_name->settings_validate( $new_options ); - + // Cast our object and save the data. $new_options = (object)array_merge( (array)$edit_flow->$module_name->module->options, $new_options ); $edit_flow->update_all_module_options( $edit_flow->$module_name->module->name, $new_options ); - + // Redirect back to the settings page that was submitted without any previous messages $goback = add_query_arg( 'message', 'settings-updated', remove_query_arg( array( 'message'), wp_get_referer() ) ); wp_safe_redirect( $goback ); - exit; + exit; } - + } } diff --git a/phpcs.xml.dist b/phpcs.xml.dist index e48234432..c5317b5cc 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -2,7 +2,26 @@ - + + + + + + + + + + + + + + + + + + + + . @@ -10,4 +29,4 @@ */nodeapp/* */vendor/* */wordpress/* - \ No newline at end of file + From 00e0cee3f602821a5fb99a0a06fe3eb18d8eb01d Mon Sep 17 00:00:00 2001 From: ingeniumed Date: Mon, 24 Jun 2024 15:47:38 +1000 Subject: [PATCH 2/9] Bump the plugin's version, and add in access methods to the vip features and analytics flags --- common/php/class-module.php | 34 +++++++++++++++++++++++++++++++++- edit_flow.php | 4 ++-- modules/settings/settings.php | 4 ++-- package.json | 2 +- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/common/php/class-module.php b/common/php/class-module.php index cadb1b72c..fee0162d5 100644 --- a/common/php/class-module.php +++ b/common/php/class-module.php @@ -29,7 +29,7 @@ function __construct() {} * @return true if the module is enabled, false otherwise */ public function is_enabled() { - return $this->module->options->enabled === 'on'; + return 'on' === $this->module->options->enabled; } /** @@ -46,6 +46,38 @@ function module_enabled( $slug ) { return isset( $edit_flow->$slug ) && $edit_flow->$slug->is_enabled(); } + /** + * Returns whether vip features have been enabled or not. + * + * @since 0.10.0 + * + * @return true, if the module is enabled, false otherwise + */ + function are_vip_features_enabled() { + global $edit_flow; + + return 'on' === $edit_flow->settings->module->options->vip_features; + } + + /** + * Returns whether vip features have been enabled or not. + * + * @since 0.10.0 + * + * @return true, if the module is enabled, false otherwise + */ + function is_analytics_enabled() { + global $edit_flow; + + return 'on' === $edit_flow->settings->module->options->analytics; + } + + function is_vip_site() { + return defined( 'WPCOM_IS_VIP_ENV' ) && constant( 'WPCOM_IS_VIP_ENV' ) === true + && defined( 'WPCOM_SANDBOXED' ) && constant( 'WPCOM_SANDBOXED' ) === false + && defined( 'FILES_CLIENT_SITE_ID' ); + } + /** * Gets an array of allowed post types for a module * diff --git a/edit_flow.php b/edit_flow.php index de29082a3..daa527e04 100644 --- a/edit_flow.php +++ b/edit_flow.php @@ -4,7 +4,7 @@ * Plugin URI: http://editflow.org/ * Description: Remixing the WordPress admin for better editorial workflow options. * Author: Daniel Bachhuber, Scott Bressler, Mohammad Jangda, Automattic, and others - * Version: 0.9.9 + * Version: 0.10.0 * Requires at least: 6.0 * Requires PHP: 8.0 * License: GPL-3 @@ -32,7 +32,7 @@ function _ef_print_php_version_admin_notice() { } // Define contants -define( 'EDIT_FLOW_VERSION', '0.9.9' ); +define( 'EDIT_FLOW_VERSION', '0.10.0' ); define( 'EDIT_FLOW_ROOT', __DIR__ ); define( 'EDIT_FLOW_FILE_PATH', EDIT_FLOW_ROOT . '/' . basename( __FILE__ ) ); define( 'EDIT_FLOW_URL', plugins_url( '/', __FILE__ ) ); diff --git a/modules/settings/settings.php b/modules/settings/settings.php index 8262e60f2..0fcb65251 100644 --- a/modules/settings/settings.php +++ b/modules/settings/settings.php @@ -22,8 +22,8 @@ function __construct() { 'settings_slug' => 'ef-settings', 'default_options' => array( 'enabled' => 'on', - 'vip_features' => 'off', - 'analytics' => 'off', + 'vip_features' => $this->is_vip_site() ? 'on' : 'off', + 'analytics' => $this->is_vip_site() ? 'on' : 'off', ), 'configure_page_cb' => 'print_default_settings', 'autoload' => true, diff --git a/package.json b/package.json index c31ab0b5d..b08e240f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "edit-flow", - "version": "2.0.0", + "version": "0.10.0", "description": "Edit Flow", "main": "dist/index.js", "directories": { From d27c7431f299bd14be971955833b778ad6599709 Mon Sep 17 00:00:00 2001 From: ingeniumed Date: Mon, 24 Jun 2024 15:50:32 +1000 Subject: [PATCH 3/9] Add a missing documentation link --- common/php/class-module.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/php/class-module.php b/common/php/class-module.php index fee0162d5..99d220bf8 100644 --- a/common/php/class-module.php +++ b/common/php/class-module.php @@ -72,6 +72,12 @@ function is_analytics_enabled() { return 'on' === $edit_flow->settings->module->options->analytics; } + /** + * Check if the site is a WPVIP site. + * + * @since 0.10.0 + * @return true, if it is a WPVIP site, false otherwise + */ function is_vip_site() { return defined( 'WPCOM_IS_VIP_ENV' ) && constant( 'WPCOM_IS_VIP_ENV' ) === true && defined( 'WPCOM_SANDBOXED' ) && constant( 'WPCOM_SANDBOXED' ) === false From 3ec7432fd49fd5fe7671032f76b4306a458285d5 Mon Sep 17 00:00:00 2001 From: ingeniumed Date: Mon, 24 Jun 2024 16:00:44 +1000 Subject: [PATCH 4/9] Fix the phpcs warnings for class-module --- common/php/class-module.php | 48 +--- modules/user-groups/user-groups.php | 330 ++++++++++++++-------------- 2 files changed, 166 insertions(+), 212 deletions(-) diff --git a/common/php/class-module.php b/common/php/class-module.php index 99d220bf8..4e2c45098 100644 --- a/common/php/class-module.php +++ b/common/php/class-module.php @@ -436,52 +436,6 @@ function get_module_url( $file ) { return trailingslashit( $module_url ); } - /** - * Produce a human-readable version of the time since a timestamp - * - * @param int $original The UNIX timestamp we're producing a relative time for - * @return string $relative_time Human-readable version of the difference between the timestamp and now - */ - function timesince( $original ) { - // array of time period chunks - $chunks = array( - array(60 * 60 * 24 * 365 , 'year'), - array(60 * 60 * 24 * 30 , 'month'), - array(60 * 60 * 24 * 7, 'week'), - array(60 * 60 * 24 , 'day'), - array(60 * 60 , 'hour'), - array(60 , 'minute'), - array(1 , 'second'), - ); - - $today = time(); /* Current unix time */ - $since = $today - $original; - - if ( $since > $chunks[2][0] ) { - $print = date("M jS", $original); - - if( $since > $chunks[0][0] ) { // Seconds in a year - $print .= ", " . date( "Y", $original ); - } - - return $print; - } - - // $j saves performing the count function each time around the loop - for ($i = 0, $j = count($chunks); $i < $j; $i++) { - - $seconds = $chunks[$i][0]; - $name = $chunks[$i][1]; - - // finding the biggest chunk (if the chunk fits, break) - if (($count = floor($since / $seconds)) != 0) { - break; - } - } - - return sprintf( _n( "1 $name ago", "$count {$name}s ago", $count), $count); - } - /** * Displays a list of users that can be selected! * @@ -529,7 +483,7 @@ function users_select_form( $selected = null, $args = null ) {