diff --git a/.github/workflows/php-lint.yml b/.github/workflows/php-lint.yml index d3f62e7a..e8484717 100644 --- a/.github/workflows/php-lint.yml +++ b/.github/workflows/php-lint.yml @@ -42,6 +42,4 @@ jobs: run: composer lint-ci | cs2pr - name: PHP coding standards - # continue-on-error until existing violations are addressed - continue-on-error: true run: composer cs -- --report=checkstyle | cs2pr diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index 3c37a970..52626fb1 100644 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -1,5 +1,5 @@ - + Custom ruleset for Edit Flow plugin. @@ -11,7 +11,9 @@ https://github.com/squizlabs/PHP_CodeSniffer/wiki/Advanced-Usage#ignoring-files-and-folders --> /node_modules/ /vendor/ - /tests/ + /build/ + + common/php/screen-options\.php @@ -66,32 +68,42 @@ - - - - - - - - - - - - - - - - + + + + + - - + + + + + + 0 + + + + 0 + + + + 0 + + + + 0 + + + + 0 + @@ -107,4 +119,164 @@ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + /tests/ + + + /tests/ + + + /tests/ + + + + /tests/ + + + + /tests/ + + + /tests/ + + + + /tests/ + + + + /tests/ + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + /tests/ + + + + edit_flow\.php + + + + common/php/screen-options\.php + + + + edit_flow\.php + + + + common/php/screen-options\.php + + + + edit_flow\.php + + + + modules/calendar/calendar\.php + modules/editorial-metadata/editorial-metadata\.php + modules/notifications/notifications\.php + modules/story-budget/story-budget\.php + + + + /tests/ + + diff --git a/common/php/class-module.php b/common/php/class-module.php index 5a5aba71..58dcf1a3 100644 --- a/common/php/class-module.php +++ b/common/php/class-module.php @@ -1,24 +1,45 @@ true if the module is enabled, false otherwise + * @return bool True if the module is enabled, false otherwise. */ public function is_enabled() { return 'on' === $this->module->options->enabled; @@ -37,8 +58,8 @@ public function is_enabled() { * * @since 0.7 * - * @param string module Slug of the module to check - * @return true if the module is enabled, false otherwise + * @param string $slug Slug of the module to check. + * @return bool True if the module is enabled, false otherwise. */ public function module_enabled( $slug ) { global $edit_flow; @@ -53,13 +74,13 @@ public function module_enabled( $slug ) { * * @since 0.10.0 * - * @return true, if analytics is enabled, false otherwise + * @return bool True if analytics is enabled, false otherwise. */ public function is_analytics_enabled() { - // Check if the site is a production WPVIP site and only then enable it + // Check if the site is a production WPVIP site and only then enable it. $is_analytics_enabled = $this->is_vip_site( true ); - // filter to disable it. + // Filter to disable it. $is_analytics_enabled = apply_filters( 'ef_should_analytics_be_enabled', $is_analytics_enabled ); return $is_analytics_enabled; @@ -70,8 +91,8 @@ public function is_analytics_enabled() { * * @since 0.10.0 * - * @param bool $only_production Whether to only allow production sites to be considered WPVIP sites - * @return true, if it is a WPVIP site, false otherwise + * @param bool $only_production Whether to only allow production sites to be considered WPVIP sites. + * @return bool True if it is a WPVIP site, false otherwise. */ protected function is_vip_site( $only_production = false ) { $is_vip_site = defined( 'VIP_GO_ENV' ) @@ -86,17 +107,17 @@ protected function is_vip_site( $only_production = false ) { } /** - * Gets an array of allowed post types for a module + * Gets an array of allowed post types for a module. * - * @return array post-type-slug => post-type-label + * @return array Post-type-slug => post-type-label. */ public function get_all_post_types() { $allowed_post_types = array( - 'post' => __( 'Post' ), - 'page' => __( 'Page' ), + 'post' => __( 'Post', 'edit-flow' ), + 'page' => __( 'Page', 'edit-flow' ), ); - $custom_post_types = $this->get_supported_post_types_for_module(); + $custom_post_types = $this->get_supported_post_types_for_module(); foreach ( $custom_post_types as $custom_post_type => $args ) { $allowed_post_types[ $custom_post_type ] = $args->label; @@ -105,19 +126,20 @@ public function get_all_post_types() { } /** - * Cleans up the 'on' and 'off' for post types on a given module (so we don't get warnings all over) - * For every post type that doesn't explicitly have the 'on' value, turn it 'off' - * If add_post_type_support() has been used anywhere (legacy support), inherit the state + * Cleans up the 'on' and 'off' for post types on a given module (so we don't get warnings all over). * - * @param array $module_post_types Current state of post type options for the module - * @param string $post_type_support What the feature is called for post_type_support (e.g. 'ef_calendar') - * @return array $normalized_post_type_options The setting for each post type, normalized based on rules + * For every post type that doesn't explicitly have the 'on' value, turn it 'off'. + * If add_post_type_support() has been used anywhere (legacy support), inherit the state. * * @since 0.7 + * + * @param array $module_post_types Current state of post type options for the module. + * @param string $post_type_support What the feature is called for post_type_support (e.g. 'ef_calendar'). + * @return array The setting for each post type, normalized based on rules. */ public function clean_post_type_options( $module_post_types = array(), $post_type_support = null ) { $normalized_post_type_options = array(); - $all_post_types = array_keys( $this->get_all_post_types() ); + $all_post_types = array_keys( $this->get_all_post_types() ); foreach ( $all_post_types as $post_type ) { if ( ( isset( $module_post_types[ $post_type ] ) && 'on' == $module_post_types[ $post_type ] ) || post_type_supports( $post_type, $post_type_support ) ) { $normalized_post_type_options[ $post_type ] = 'on'; @@ -129,30 +151,30 @@ public function clean_post_type_options( $module_post_types = array(), $post_typ } /** - * Get all of the possible post types that can be used with a given module - * - * @param object $module The full module - * @return array $post_types An array of post type objects + * Get all of the possible post types that can be used with a given module. * * @since 0.7.2 + * + * @param object $module The full module. + * @return array An array of post type objects. */ public function get_supported_post_types_for_module( $module = null ) { $pt_args = array( '_builtin' => false, - 'public' => true, + 'public' => true, ); $pt_args = apply_filters( 'edit_flow_supported_module_post_types_args', $pt_args, $module ); return get_post_types( $pt_args, 'objects' ); } /** - * Collect all of the active post types for a given module - * - * @param object $module Module's data - * @return array $post_types All of the post types that are 'on' + * Collect all of the active post types for a given module. * * @since 0.7 + * + * @param object $module Module's data. + * @return array All of the post types that are 'on'. */ public function get_post_types_for_module( $module ) { @@ -168,12 +190,13 @@ public function get_post_types_for_module( $module ) { } /** - * Get all of the currently available post statuses - * This should be used in favor of calling $edit_flow->custom_status->get_custom_statuses() directly + * Get all of the currently available post statuses. * - * @return array $post_statuses All of the post statuses that aren't a published state + * This should be used in favor of calling $edit_flow->custom_status->get_custom_statuses() directly. * * @since 0.7 + * + * @return array All of the post statuses that aren't a published state. */ public function get_post_statuses() { global $edit_flow; @@ -186,7 +209,7 @@ public function get_post_statuses() { } /** - * Get core's 'draft' and 'pending' post statuses, but include our special attributes + * Get core's 'draft' and 'pending' post statuses, but include our special attributes. * * @since 0.8.1 * @@ -196,16 +219,16 @@ protected function get_core_post_statuses() { return array( (object) array( - 'name' => __( 'Draft' ), - 'description' => '', - 'slug' => 'draft', - 'position' => 1, + 'name' => __( 'Draft', 'edit-flow' ), + 'description' => '', + 'slug' => 'draft', + 'position' => 1, ), (object) array( - 'name' => __( 'Pending Review' ), - 'description' => '', - 'slug' => 'pending', - 'position' => 2, + 'name' => __( 'Pending Review', 'edit-flow' ), + 'description' => '', + 'slug' => 'pending', + 'position' => 2, ), ); } @@ -214,11 +237,11 @@ protected function get_core_post_statuses() { * Gets the name of the default custom status. If custom statuses are disabled, * returns 'draft'. * - * @return str Name of the status + * @return string Name of the status. */ public function get_default_post_status() { - // Check if custom status module is enabled + // Check if custom status module is enabled. $custom_status_module = EditFlow()->custom_status->module->options; if ( 'on' == $custom_status_module->enabled ) { @@ -233,9 +256,9 @@ public function get_default_post_status() { * * @since 0.7 * - * @param string $slug The slug for the post status to which to filter - * @param string $post_type Optional post type to which to filter - * @return an edit.php link to all posts with the given post status and, optionally, the given post type + * @param string $slug The slug for the post status to which to filter. + * @param string $post_type Optional post type to which to filter. + * @return string An edit.php link to all posts with the given post status and, optionally, the given post type. */ public function filter_posts_link( $slug, $post_type = 'post' ) { $filter_link = add_query_arg( 'post_status', $slug, get_admin_url( null, 'edit.php' ) ); @@ -246,7 +269,7 @@ public function filter_posts_link( $slug, $post_type = 'post' ) { } /** - * Enqueue any resources (CSS or JS) associated with datepicker functionality + * Enqueue any resources (CSS or JS) associated with datepicker functionality. * * @since 0.7 */ @@ -263,20 +286,22 @@ public function enqueue_datepicker_resources() { wp_enqueue_script( 'edit_flow-date_picker', EDIT_FLOW_URL . 'common/js/ef_date.js', $dependencies, EDIT_FLOW_VERSION, true ); wp_add_inline_script( 'edit_flow-date_picker', sprintf( 'var ef_week_first_day = %s;', wp_json_encode( get_option( 'start_of_week' ) ) ), 'before' ); - // Now styles + // Now styles. wp_enqueue_style( 'jquery-ui-datepicker', EDIT_FLOW_URL . 'common/css/jquery.ui.datepicker.css', array( 'wp-jquery-ui-dialog' ), EDIT_FLOW_VERSION, 'screen' ); wp_enqueue_style( 'jquery-ui-theme', EDIT_FLOW_URL . 'common/css/jquery.ui.theme.css', false, EDIT_FLOW_VERSION, 'screen' ); } /** - * Checks for the current post type + * Checks for the current post type. * * @since 0.7 - * @return string|null $post_type The post type we've found, or null if no post type + * + * @return string|null The post type we've found, or null if no post type. */ public function get_current_post_type() { global $post, $typenow, $pagenow, $current_screen; - //get_post() needs a variable + // get_post() needs a variable. + // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Reading post type from REQUEST for context detection, not processing form data. $post_id = isset( $_REQUEST['post'] ) ? (int) $_REQUEST['post'] : false; if ( $post && $post->post_type ) { @@ -296,21 +321,22 @@ public function get_current_post_type() { } else { $post_type = null; } + // phpcs:enable WordPress.Security.NonceVerification.Recommended return $post_type; } /** - * Wrapper for the get_user_meta() function so we can replace it if we need to + * Wrapper for the get_user_meta() function so we can replace it if we need to. * * @since 0.7 * - * @param int $user_id Unique ID for the user - * @param string $key Key to search against - * @param bool $single Whether or not to return just one value - * @return string|bool|array $value Whatever the stored value was + * @param int $user_id Unique ID for the user. + * @param string $key Key to search against. + * @param bool $string Whether or not to return just one value. + * @return string|bool|array Whatever the stored value was. */ - public function get_user_meta( $user_id, $key, $string = true ) { + public function get_user_meta( $user_id, $key, $string = true ) { // phpcs:ignore Universal.NamingConventions.NoReservedKeywordParameterNames.stringFound -- Legacy parameter name. $response = null; $response = apply_filters( 'ef_get_user_meta', $response, $user_id, $key, $string ); @@ -322,15 +348,15 @@ public function get_user_meta( $user_id, $key, $string = true ) { } /** - * Wrapper for the update_user_meta() function so we can replace it if we need to + * Wrapper for the update_user_meta() function so we can replace it if we need to. * * @since 0.7 * - * @param int $user_id Unique ID for the user - * @param string $key Key to search against - * @param string|bool|array $value Whether or not to return just one value - * @param string|bool|array $previous (optional) Previous value to replace - * @return bool $success Whether we were successful in saving + * @param int $user_id Unique ID for the user. + * @param string $key Key to search against. + * @param string|bool|array $value The value to store. + * @param string|bool|array $previous Previous value to replace. + * @return bool Whether we were successful in saving. */ public function update_user_meta( $user_id, $key, $value, $previous = null ) { @@ -344,11 +370,13 @@ public function update_user_meta( $user_id, $key, $value, $previous = null ) { } /** - * Take a status and a message, JSON encode and print + * Take a status and a message, JSON encode and print. * * @since 0.7 * - * @param string $status Whether it was a 'success' or an 'error' + * @param string $status Whether it was a 'success' or an 'error'. + * @param string $message Optional message to include. + * @param int $http_code HTTP response code. */ protected function print_ajax_response( $status, $message = '', $http_code = 200 ) { header( 'Content-type: application/json;' ); @@ -395,7 +423,7 @@ public function is_post_management_page( $module_name = null ) { // If a module name is specified, check if this post type is supported by that module. if ( $module_name && isset( $edit_flow->modules->$module_name ) ) { - $module = $edit_flow->modules->$module_name; + $module = $edit_flow->modules->$module_name; $supported_post_types = $this->get_post_types_for_module( $module ); if ( ! in_array( $current_post_type, $supported_post_types, true ) ) { return false; @@ -406,31 +434,33 @@ public function is_post_management_page( $module_name = null ) { } /** - * Whether or not the current page is an Edit Flow settings view (either main or module) - * Determination is based on $pagenow, $_GET['page'], and the module's $settings_slug - * If there's no module name specified, it will return true against all Edit Flow settings views + * Whether or not the current page is an Edit Flow settings view (either main or module). + * + * Determination is based on $pagenow, $_GET['page'], and the module's $settings_slug. + * If there's no module name specified, it will return true against all Edit Flow settings views. * * @since 0.7 * - * @param string $module_name (Optional) Module name to check against - * @return bool $is_settings_view Return true if it is + * @param string $module_name Optional module name to check against. + * @return bool Return true if it is. */ public function is_whitelisted_settings_view( $module_name = null ) { global $pagenow, $edit_flow; - // All of the settings views are based on admin.php and a $_GET['page'] parameter + // All of the settings views are based on admin.php and a $_GET['page'] parameter. + // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Checking page parameter for context, not processing form data. if ( 'admin.php' != $pagenow || ! isset( $_GET['page'] ) ) { return false; } - // Load all of the modules that have a settings slug/ callback for the settings page + // Load all of the modules that have a settings slug/ callback for the settings page. foreach ( $edit_flow->modules as $mod_name => $mod_data ) { if ( isset( $mod_data->options->enabled ) && 'on' == $mod_data->options->enabled && $mod_data->configure_page_cb ) { $settings_view_slugs[] = $mod_data->settings_slug; } } - // The current page better be in the array of registered settings view slugs + // The current page better be in the array of registered settings view slugs. if ( ! in_array( $_GET['page'], $settings_view_slugs ) ) { return false; } @@ -438,6 +468,7 @@ public function is_whitelisted_settings_view( $module_name = null ) { if ( $module_name && $edit_flow->modules->$module_name->settings_slug != $_GET['page'] ) { return false; } + // phpcs:enable WordPress.Security.NonceVerification.Recommended return true; } @@ -445,38 +476,41 @@ public function is_whitelisted_settings_view( $module_name = null ) { /** * This is a hack, Hack, HACK!!! - * Encode all of the given arguments as a serialized array, and then base64_encode - * Used to store extra data in a term's description field + * + * Encode all of the given arguments as a serialized array, and then base64_encode. + * Used to store extra data in a term's description field. * * @since 0.7 * - * @param array $args The arguments to encode - * @return string Arguments encoded in base64 + * @param array $args The arguments to encode. + * @return string Arguments encoded in base64. */ public function get_encoded_description( $args = array() ) { + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Required for term description storage. return base64_encode( maybe_serialize( $args ) ); } /** * If given an encoded string from a term's description field, - * return an array of values. Otherwise, return the original string + * return an array of values. Otherwise, return the original string. * * @since 0.7 * - * @param string $string_to_unencode Possibly encoded string - * @return array Array if string was encoded, otherwise the string as the 'description' field + * @param string $string_to_unencode Possibly encoded string. + * @return array Array if string was encoded, otherwise the string as the 'description' field. */ public function get_unencoded_description( $string_to_unencode ) { + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Required for term description retrieval. return maybe_unserialize( base64_decode( $string_to_unencode ) ); } /** - * Get the publicly accessible URL for the module based on the filename + * Get the publicly accessible URL for the module based on the filename. * * @since 0.7 * - * @param string $filepath File path for the module - * @return string $module_url Publicly accessible URL for the module + * @param string $file File path for the module. + * @return string Publicly accessible URL for the module. */ public function get_module_url( $file ) { $module_url = plugins_url( '/', $file ); @@ -488,30 +522,30 @@ public function get_module_url( $file ) { * * @since 0.7 * - * @todo Add pagination support for blogs with billions of users + * @todo Add pagination support for blogs with billions of users. * - * @param ??? - * @param ??? + * @param array|null $selected Selected users. + * @param array|null $args Optional arguments for the form. */ public function users_select_form( $selected = null, $args = null ) { - // Set up arguments - $defaults = array( + // Set up arguments. + $defaults = array( 'list_class' => 'ef-users-select-form', - 'input_id' => 'ef-selected-users', + 'input_id' => 'ef-selected-users', ); $parsed_args = wp_parse_args( $args, $defaults ); extract( $parsed_args, EXTR_SKIP ); $args = array( 'capability' => 'publish_posts', - 'fields' => array( + 'fields' => array( 'ID', 'display_name', 'user_nicename', 'user_email', ), - 'orderby' => 'display_name', + 'orderby' => 'display_name', ); $args = apply_filters( 'ef_users_select_form_get_users_args', $args ); @@ -527,7 +561,7 @@ public function users_select_form( $selected = null, $args = null ) { ID, $selected ) ) ? 'checked="checked"' : ''; - // Add a class to checkbox of current user so we know not to add them in notified list during notifiedMessage() js function + // Add a class to checkbox of current user so we know not to add them in notified list during notifiedMessage() js function. $current_user_class = ( get_current_user_id() == $user->ID ) ? 'class="post_following_list-current_user" ' : ''; ?>
  • @@ -535,7 +569,7 @@ public function users_select_form( $selected = null, $args = null ) {
    ID, $checked ); ?> @@ -573,12 +607,12 @@ public function users_select_form( $selected = null, $args = null ) { * * @since 0.7 * - * @param string $role A standard WP user role like 'administrator' or 'author' - * @param array $caps One or more user caps to add + * @param string $role A standard WP user role like 'administrator' or 'author'. + * @param array $caps One or more user caps to add. */ public function add_caps_to_role( $role, $caps ) { - // In some contexts, we don't want to add caps to roles + // In some contexts, we don't want to add caps to roles. if ( apply_filters( 'ef_kill_add_caps_to_role', false, $role, $caps ) ) { return; } @@ -594,8 +628,9 @@ public function add_caps_to_role( $role, $caps ) { } /** - * Add settings help menus to our module screens if the values exist - * Auto-registered in Edit_Flow::register_module() + * Add settings help menus to our module screens if the values exist. + * + * Auto-registered in Edit_Flow::register_module(). * * @since 0.7 */ @@ -611,7 +646,7 @@ public function action_settings_help_menu() { return; } - // Make sure we have all of the required values for our tab + // Make sure we have all of the required values for our tab. if ( isset( $this->module->settings_help_tab['id'], $this->module->settings_help_tab['title'], $this->module->settings_help_tab['content'] ) ) { $screen->add_help_tab( $this->module->settings_help_tab ); @@ -622,7 +657,9 @@ public function action_settings_help_menu() { } /** - * Upgrade the term descriptions for all of the terms in a given taxonomy + * Upgrade the term descriptions for all of the terms in a given taxonomy. + * + * @param string $taxonomy The taxonomy to upgrade. */ public function upgrade_074_term_descriptions( $taxonomy ) { $args = array( @@ -632,21 +669,22 @@ public function upgrade_074_term_descriptions( $taxonomy ) { // phpcs:ignore WordPress.WP.DeprecatedParameters.Get_termsParam2Found $terms = get_terms( $taxonomy, $args ); foreach ( $terms as $term ) { - // If we can detect that this term already follows the new scheme, let's skip it + // If we can detect that this term already follows the new scheme, let's skip it. + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Required for term description retrieval. $maybe_serialized = base64_decode( $term->description ); if ( is_serialized( $maybe_serialized ) ) { continue; } $description_args = array(); - // This description has been JSON-encoded, so let's decode it + // This description has been JSON-encoded, so let's decode it. if ( 0 === strpos( $term->description, '{' ) ) { $string_to_unencode = stripslashes( htmlspecialchars_decode( $term->description ) ); - $unencoded_array = json_decode( $string_to_unencode, true ); - // Only continue processing if it actually was an array. Otherwise, set to the original string + $unencoded_array = json_decode( $string_to_unencode, true ); + // Only continue processing if it actually was an array. Otherwise, set to the original string. if ( is_array( $unencoded_array ) ) { foreach ( $unencoded_array as $key => $value ) { - // html_entity_decode only works on strings but sometimes we store nested arrays + // html_entity_decode only works on strings but sometimes we store nested arrays. if ( ! is_array( $value ) ) { $description_args[ $key ] = html_entity_decode( $value, ENT_QUOTES ); } else { diff --git a/common/php/screen-options.php b/common/php/screen-options.php index 5418238a..a1251f51 100644 --- a/common/php/screen-options.php +++ b/common/php/screen-options.php @@ -15,8 +15,8 @@ * @access public */ class wsScreenOptions10 { - public $registered_panels; //List of custom "Screen Options" panels - public $page_panels; //Index of panels registered for each page ($page => array of panel ids). + public $registered_panels; // List of custom "Screen Options" panels + public $page_panels; // Index of panels registered for each page ($page => array of panel ids). /** * Class constructor @@ -25,7 +25,7 @@ class wsScreenOptions10 { */ public function __construct() { $this->registered_panels = array(); - $this->page_panels = array(); + $this->page_panels = array(); add_filter( 'screen_settings', array( $this, 'append_screen_settings' ), 10, 2 ); add_action( 'admin_print_scripts', array( $this, 'add_autosave_script' ) ); @@ -34,35 +34,35 @@ public function __construct() { /** * Add a new settings panel to the "Screen Options" box. * - * @param string $id String to use in the 'id' attribute of the settings panel. Should be unique. - * @param string $title Title of the settings panel. Set to an empty string to omit title. - * @param callback $callback Function that fills the panel with the desired content. Should return its output. + * @param string $id String to use in the 'id' attribute of the settings panel. Should be unique. + * @param string $title Title of the settings panel. Set to an empty string to omit title. + * @param callback $callback Function that fills the panel with the desired content. Should return its output. * @param string|array $page The page(s) on which to show the panel (similar to add_meta_box()). - * @param callback $save_callback Optional. Function that saves the settings. - * @param bool $autosave Optional. If se, settings will be automatically saved (via AJAX) when the value of any input element in the panel changes. Defaults to false. + * @param callback $save_callback Optional. Function that saves the settings. + * @param bool $autosave Optional. If se, settings will be automatically saved (via AJAX) when the value of any input element in the panel changes. Defaults to false. * @return void */ public function add_screen_options_panel( $id, $title, $callback, $page, $save_callback = null, $autosave = false ) { if ( ! is_array( $page ) ) { $page = array( $page ); } - //Convert page hooks/slugs to screen IDs + // Convert page hooks/slugs to screen IDs $page = array_map( array( $this, 'page_to_screen_id' ), $page ); $page = array_unique( $page ); $new_panel = array( - 'title' => $title, - 'callback' => $callback, - 'page' => $page, + 'title' => $title, + 'callback' => $callback, + 'page' => $page, 'save_callback' => $save_callback, - 'autosave' => $autosave, + 'autosave' => $autosave, ); if ( $save_callback ) { add_action( 'wp_ajax_save_settings-' . $id, array( $this, 'ajax_save_callback' ) ); } - //Store the panel ID in each relevant page's list + // Store the panel ID in each relevant page's list foreach ( $page as $page_id ) { if ( ! isset( $this->page_panels[ $page_id ] ) ) { $this->page_panels[ $page_id ] = array(); @@ -108,29 +108,29 @@ public function page_to_screen_id( $page ) { public function append_screen_settings( $current, $screen ) { global $hook_suffix; - //Sanity check + // Sanity check if ( ! isset( $screen->id ) ) { return $current; } - //Are there any panels that want to appear on this page? + // Are there any panels that want to appear on this page? $panels = $this->get_panels_for_screen( $screen->id, $hook_suffix ); if ( empty( $panels ) ) { return $current; } - //Append all panels registered for this screen + // Append all panels registered for this screen foreach ( $panels as $panel_id ) { $panel = $this->registered_panels[ $panel_id ]; - //Add panel title + // Add panel title if ( ! empty( $panel['title'] ) ) { $current .= "\n
    " . $panel['title'] . "
    \n"; } - //Generate panel contents + // Generate panel contents if ( is_callable( $panel['callback'] ) ) { $contents = call_user_func( $panel['callback'] ); - $classes = array( + $classes = array( 'metabox-prefs', 'custom-options-panel', ); @@ -165,15 +165,15 @@ public function ajax_save_callback() { wp_die( '0' ); } - //The 'action' argument is in the form "save_settings-panel_id" + // The 'action' argument is in the form "save_settings-panel_id" // phpcs:ignore WordPress.Security.NonceVerification.Missing -- it's being verified 2 lines down $ids = explode( '-', $_POST['action'], 2 ); - $id = end( $ids ); + $id = end( $ids ); - //Basic security check. + // Basic security check. check_ajax_referer( 'save_settings-' . $id, '_wpnonce-' . $id ); - //Hand the request to the registered callback, if any + // Hand the request to the registered callback, if any if ( ! isset( $this->registered_panels[ $id ] ) ) { wp_die( '0' ); } @@ -195,10 +195,10 @@ public function ajax_save_callback() { * @return void */ public function add_autosave_script() { - //Get the page id/hook/slug/whatever. + // Get the page id/hook/slug/whatever. global $hook_suffix; - //Check if we have some panels with autosave registered for this page. + // Check if we have some panels with autosave registered for this page. $panels = $this->get_panels_for_screen( '', $hook_suffix ); if ( empty( $panels ) ) { return; @@ -213,7 +213,7 @@ public function add_autosave_script() { } if ( $got_autosave ) { - //Enqueue the script itself + // Enqueue the script itself $url = EDIT_FLOW_URL . '/common/js/screen-options.js'; wp_enqueue_script( 'screen-options-custom-autosave', $url, array( 'jquery' ), EDIT_FLOW_VERSION ); } @@ -242,14 +242,13 @@ public function get_panels_for_screen( $screen_id, $page = '' ) { } } - //All versions of the class are stored in a global array - //and only the latest version is actually used. + // All versions of the class are stored in a global array + // and only the latest version is actually used. global $ws_screen_options_versions; if ( ! isset( $ws_screen_options_versions ) ) { $ws_screen_options_versions = array(); } $ws_screen_options_versions['1.0'] = 'wsScreenOptions10'; - endif; if ( ! function_exists( 'add_screen_options_panel' ) ) { @@ -259,12 +258,12 @@ public function get_panels_for_screen( $screen_id, $page = '' ) { * * @see wsScreenOptions10::add_screen_options_panel() * - * @param string $id String to use in the 'id' attribute of the settings panel. Should be unique. - * @param string $title Title of the settings panel. Set to an empty string to omit title. - * @param callback $callback Function that fills the panel with the desired content. Should return its output. + * @param string $id String to use in the 'id' attribute of the settings panel. Should be unique. + * @param string $title Title of the settings panel. Set to an empty string to omit title. + * @param callback $callback Function that fills the panel with the desired content. Should return its output. * @param string|array $page The page(s) on which to show the panel (similar to add_meta_box()). - * @param callback $save_callback Optional. Function that saves the settings contained in the panel. - * @param bool $autosave Optional. If set, settings will be automatically saved (via AJAX) when the value of any input element in the panel changes. Defaults to false. + * @param callback $save_callback Optional. Function that saves the settings contained in the panel. + * @param bool $autosave Optional. If set, settings will be automatically saved (via AJAX) when the value of any input element in the panel changes. Defaults to false. * @return void */ function add_screen_options_panel( $id, $title, $callback, $page, $save_callback = null, $autosave = false ) { @@ -272,10 +271,10 @@ function add_screen_options_panel( $id, $title, $callback, $page, $save_callback static $instance = null; if ( is_null( $instance ) ) { - //Instantiate the latest version of the wsScreenOptions class + // Instantiate the latest version of the wsScreenOptions class uksort( $ws_screen_options_versions, 'version_compare' ); $class_name = end( $ws_screen_options_versions ); - $instance = new $class_name(); + $instance = new $class_name(); } return $instance->add_screen_options_panel( $id, $title, $callback, $page, $save_callback, $autosave ); diff --git a/common/php/util.php b/common/php/util.php index c9f966aa..cdb77842 100644 --- a/common/php/util.php +++ b/common/php/util.php @@ -1,4 +1,9 @@ post_status, array_merge( $custom_statuses, array( 'pending' ) ) ) ) { $url = add_query_arg( 'page', $i, get_permalink() ); - } else if ( 'page' == get_option( 'show_on_front' ) && get_option( 'page_on_front' ) == $post->ID ) { + } elseif ( 'page' == get_option( 'show_on_front' ) && get_option( 'page_on_front' ) == $post->ID ) { $url = trailingslashit( get_permalink() ) . user_trailingslashit( "$wp_rewrite->pagination_base/" . $i, 'single_paged' ); } else { $url = trailingslashit( get_permalink() ) . user_trailingslashit( $i, 'single_paged' ); @@ -53,12 +63,13 @@ function _ef_wp_link_page( $i, $custom_statuses ) { } if ( is_preview() ) { - - // Check for all custom post statuses, not just the draft + // Check for all custom post statuses, not just the draft. + // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Nonce verified by WordPress is_preview() and preview_nonce validation. if ( ( ! in_array( $post->post_status, $custom_statuses ) ) && isset( $_GET['preview_id'], $_GET['preview_nonce'] ) ) { - $query_args['preview_id'] = wp_unslash( $_GET['preview_id'] ); - $query_args['preview_nonce'] = wp_unslash( $_GET['preview_nonce'] ); + $query_args['preview_id'] = absint( wp_unslash( $_GET['preview_id'] ) ); + $query_args['preview_nonce'] = sanitize_text_field( wp_unslash( $_GET['preview_nonce'] ) ); } + // phpcs:enable WordPress.Security.NonceVerification.Recommended $url = get_preview_post_link( $post, $query_args, $url ); } diff --git a/edit-flow.php b/edit-flow.php index ea5be28e..7c489e76 100644 --- a/edit-flow.php +++ b/edit-flow.php @@ -1,11 +1,11 @@ -setup_globals(); self::$instance->setup_actions(); - // Backwards compat for when we promoted use of the $edit_flow global + // Backwards compat for when we promoted use of the $edit_flow global. global $edit_flow; $edit_flow = self::$instance; } return self::$instance; } + /** + * Constructor. + */ private function __construct() { - /** Do nothing **/ + // Do nothing. } + /** + * Set up global variables. + */ private function setup_globals() { $this->modules = new stdClass(); $this->modules_count = 0; } /** - * Include the common resources to Edit Flow and dynamically load the modules + * Include the common resources to Edit Flow and dynamically load the modules. */ private function load_modules() { - // We use the WP_List_Table API for some of the table gen + // We use the WP_List_Table API for some of the table gen. if ( ! class_exists( 'WP_List_Table' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php'; } - // Edit Flow base module + // Edit Flow base module. require_once EDIT_FLOW_ROOT . '/common/php/class-module.php'; - // Scan the modules directory and include any modules that exist there + // Scan the modules directory and include any modules that exist there. $module_dirs = scandir( EDIT_FLOW_ROOT . '/modules/' ); $class_names = array(); foreach ( $module_dirs as $module_dir ) { if ( file_exists( EDIT_FLOW_ROOT . "/modules/{$module_dir}/$module_dir.php" ) ) { include_once EDIT_FLOW_ROOT . "/modules/{$module_dir}/$module_dir.php"; - // Prepare the class name because it should be standardized + // Prepare the class name because it should be standardized. $tmp = explode( '-', $module_dir ); $class_name = ''; $slug_name = ''; @@ -138,15 +162,13 @@ private function load_modules() { } } - // Instantiate EF_Module as $helpers for back compat and so we can - // use it in this class + // Instantiate EF_Module as $helpers for back compat and so we can use it in this class. $this->helpers = new EF_Module(); - // Other utils + // Other utils. require_once EDIT_FLOW_ROOT . '/common/php/util.php'; - // Instantiate all of our classes onto the Edit Flow object - // but make sure they exist too + // Instantiate all of our classes onto the Edit Flow object but make sure they exist too. foreach ( $class_names as $slug => $class_name ) { if ( class_exists( $class_name ) ) { $this->$slug = new $class_name(); @@ -157,17 +179,16 @@ private function load_modules() { * Fires after edit_flow has loaded all Edit Flow internal modules. * * Plugin authors can hook into this action, include their own modules add them to the $edit_flow object - * */ do_action( 'ef_modules_loaded' ); } /** - * Setup the default hooks and actions + * Setup the default hooks and actions. * * @since EditFlow 0.7.4 * @access private - * @uses add_action() To add various actions + * @uses add_action() To add various actions. */ private function setup_actions() { add_action( 'init', array( $this, 'action_init' ) ); @@ -187,7 +208,8 @@ private function setup_actions() { /** * Inititalizes the Edit Flows! - * Loads options for each registered module and then initializes it if it's active + * + * Loads options for each registered module and then initializes it if it's active. */ public function action_init() { @@ -195,11 +217,10 @@ public function action_init() { $this->load_modules(); - // Load all of the module options + // Load all of the module options. $this->load_module_options(); - // Load all of the modules that are enabled. - // Modules won't have an options value if they aren't enabled + // Load all of the modules that are enabled. Modules won't have an options value if they aren't enabled. foreach ( $this->modules as $mod_name => $mod_data ) { if ( isset( $mod_data->options->enabled ) && 'on' == $mod_data->options->enabled ) { $this->$mod_name->init(); @@ -210,17 +231,16 @@ public function action_init() { * Fires after edit_flow has loaded all modules and module options. * * Plugin authors can hook into this action to trigger functionaltiy after all Edit Flow module's have been loaded. - * */ do_action( 'ef_init' ); } /** - * Initialize the plugin for the admin + * Initialize the plugin for the admin. */ public function action_admin_init() { - // Upgrade if need be but don't run the upgrade if the plugin has never been used + // Upgrade if need be but don't run the upgrade if the plugin has never been used. $previous_version = get_option( $this->options_group . 'version' ); if ( $previous_version && version_compare( $previous_version, EDIT_FLOW_VERSION, '<' ) ) { foreach ( $this->modules as $mod_name => $mod_data ) { @@ -233,9 +253,9 @@ public function action_admin_init() { update_option( $this->options_group . 'version', EDIT_FLOW_VERSION ); } - // For each module that's been loaded, auto-load data if it's never been run before + // For each module that's been loaded, auto-load data if it's never been run before. foreach ( $this->modules as $mod_name => $mod_data ) { - // If the module has never been loaded before, run the install method if there is one + // If the module has never been loaded before, run the install method if there is one. if ( ! isset( $mod_data->options->loaded_once ) || ! $mod_data->options->loaded_once ) { if ( method_exists( $this->$mod_name, 'install' ) ) { $this->$mod_name->install(); @@ -248,11 +268,15 @@ public function action_admin_init() { } /** - * Register a new module with Edit Flow + * Register a new module with Edit Flow. + * + * @param string $name Module name. + * @param array $args Module arguments. + * @return object|false Module object on success, false on failure. */ public function register_module( $name, $args = array() ) { - // A title and name is required for every module + // A title and name is required for every module. if ( ! isset( $args['title'], $name ) ) { return false; } @@ -268,7 +292,7 @@ public function register_module( $name, $args = array() ) { 'options' => false, 'configure_page_cb' => false, 'configure_link_text' => __( 'Configure', 'edit-flow' ), - // These messages are applied to modules and can be overridden if custom messages are needed + // These messages are applied to modules and can be overridden if custom messages are needed. 'messages' => array( 'settings-updated' => __( 'Settings updated.', 'edit-flow' ), 'form-error' => __( 'Please correct your form errors below and try again.', 'edit-flow' ), @@ -276,7 +300,7 @@ public function register_module( $name, $args = array() ) { 'invalid-permissions' => __( 'You do not have necessary permissions to complete this action.', 'edit-flow' ), 'missing-post' => __( 'Post does not exist', 'edit-flow' ), ), - 'autoload' => false, // autoloading a module will remove the ability to enable or disable it + 'autoload' => false, // Autoloading a module will remove the ability to enable or disable it. ); if ( isset( $args['messages'] ) ) { $args['messages'] = array_merge( (array) $args['messages'], $defaults['messages'] ); @@ -290,8 +314,7 @@ public function register_module( $name, $args = array() ) { if ( empty( $args['post_type_support'] ) ) { $args['post_type_support'] = 'ef_' . $name; } - // If there's a Help Screen registered for the module, make sure we - // auto-load it + // If there's a Help Screen registered for the module, make sure we auto-load it. if ( ! empty( $args['settings_help_tab'] ) ) { add_action( 'load-edit-flow_page_' . $args['settings_slug'], array( &$this->$name, 'action_settings_help_menu' ) ); } @@ -312,13 +335,13 @@ public function register_module( $name, $args = array() ) { } /** - * Load all of the module options from the database - * If a given option isn't yet set, then set it to the module's default (upgrades, etc.) + * Load all of the module options from the database. + * + * If a given option isn't yet set, then set it to the module's default (upgrades, etc.). */ public function load_module_options() { foreach ( $this->modules as $mod_name => $mod_data ) { - $this->modules->$mod_name->options = get_option( $this->options_group . $mod_name . '_options', new stdClass() ); foreach ( $mod_data->default_options as $default_key => $default_value ) { if ( ! isset( $this->modules->$mod_name->options->$default_key ) ) { @@ -333,19 +356,17 @@ public function load_module_options() { * Fires after edit_flow has loaded all of the module options from the database. * * Plugin authors can hook into this action to read and manipulate module settings. - * */ do_action( 'ef_module_options_loaded' ); } /** - * Load the post type options again so we give add_post_type_support() a chance to work + * Load the post type options again so we give add_post_type_support() a chance to work. * * @see http://dev.editflow.org/2011/11/17/edit-flow-v0-7-alpha2-notes/#comment-232 */ public function action_init_after() { foreach ( $this->modules as $mod_name => $mod_data ) { - if ( isset( $this->modules->$mod_name->options->post_types ) ) { $this->modules->$mod_name->options->post_types = $this->helpers->clean_post_type_options( $this->modules->$mod_name->options->post_types, $mod_data->post_type_support ); } @@ -355,15 +376,15 @@ public function action_init_after() { } /** - * Get a module by one of its descriptive values + * Get a module by one of its descriptive values. * - * @param string $key The property to use for searching a module (ex: 'name') - * @param string|int|array $value The value to compare (using ==) + * @param string $key The property to use for searching a module (ex: 'name'). + * @param string|int|array $value The value to compare (using ==). + * @return object|false Module object on success, false on failure. */ public function get_module_by( $key, $value ) { $module = false; foreach ( $this->modules as $mod_name => $mod_data ) { - if ( 'name' === $key && $value === $mod_name ) { $module = $this->modules->$mod_name; } else { @@ -378,7 +399,12 @@ public function get_module_by( $key, $value ) { } /** - * Update the $edit_flow object with new value and save to the database + * Update the $edit_flow object with new value and save to the database. + * + * @param string $mod_name Module name. + * @param string $key Option key. + * @param mixed $value Option value. + * @return bool True on success, false on failure. */ public function update_module_option( $mod_name, $key, $value ) { $this->modules->$mod_name->options->$key = $value; @@ -386,6 +412,13 @@ public function update_module_option( $mod_name, $key, $value ) { return update_option( $this->options_group . $mod_name . '_options', $this->modules->$mod_name->options ); } + /** + * Update all module options at once. + * + * @param string $mod_name Module name. + * @param array|object $new_options New options. + * @return bool True on success, false on failure. + */ public function update_all_module_options( $mod_name, $new_options ) { if ( is_array( $new_options ) ) { $new_options = (object) $new_options; @@ -396,7 +429,7 @@ public function update_all_module_options( $mod_name, $new_options ) { } /** - * Registers commonly used scripts + styles for easy enqueueing + * Registers commonly used scripts + styles for easy enqueueing. */ public function register_scripts_and_styles() { wp_enqueue_style( 'ef-admin-css', EDIT_FLOW_URL . 'common/css/edit-flow-admin.css', false, EDIT_FLOW_VERSION, 'all' ); @@ -416,8 +449,12 @@ public function register_scripts_and_styles() { } } -// phpcs:disable WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid -function EditFlow() { +/** + * Get the Edit Flow instance. + * + * @return edit_flow The Edit Flow instance. + */ +function EditFlow() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid -- Legacy function name. return edit_flow::instance(); } add_action( 'plugins_loaded', 'EditFlow' ); diff --git a/modules/calendar/calendar.php b/modules/calendar/calendar.php index aba60f65..61abdf81 100644 --- a/modules/calendar/calendar.php +++ b/modules/calendar/calendar.php @@ -1,12 +1,19 @@ max_weeks = 12; $this->module_url = $this->get_module_url( __FILE__ ); - // Register the module with Edit Flow - $args = array( - 'title' => __( 'Calendar', 'edit-flow' ), + // Register the module with Edit Flow. + $args = array( + 'title' => __( 'Calendar', 'edit-flow' ), /* translators: %s: URL to the calendar page */ - 'short_description' => sprintf( __( 'View upcoming content in a customizable calendar.', 'edit-flow' ), admin_url( 'index.php?page=calendar' ) ), - 'extended_description' => __( 'Edit Flow’s calendar lets you see your posts over a customizable date range. Filter by status or click on the post title to see its details. Drag and drop posts between days to change their publication date.', 'edit-flow' ), - 'module_url' => $this->module_url, - 'img_url' => $this->module_url . 'lib/calendar_s128.png', - 'slug' => 'calendar', - 'post_type_support' => 'ef_calendar', - 'default_options' => array( - 'enabled' => 'on', - 'post_types' => array( + 'short_description' => sprintf( __( 'View upcoming content in a customizable calendar.', 'edit-flow' ), admin_url( 'index.php?page=calendar' ) ), + 'extended_description' => __( 'Edit Flow’s calendar lets you see your posts over a customizable date range. Filter by status or click on the post title to see its details. Drag and drop posts between days to change their publication date.', 'edit-flow' ), + 'module_url' => $this->module_url, + 'img_url' => $this->module_url . 'lib/calendar_s128.png', + 'slug' => 'calendar', + 'post_type_support' => 'ef_calendar', + 'default_options' => array( + 'enabled' => 'on', + 'post_types' => array( 'post' => 'on', 'page' => 'off', ), 'quick_create_post_type' => 'post', - 'ics_subscription' => 'off', - 'ics_secret_key' => '', + 'ics_subscription' => 'off', + 'ics_secret_key' => '', ), - 'messages' => array( - 'post-date-updated' => __( 'Post date updated.', 'edit-flow' ), - 'update-error' => __( 'There was an error updating the post. Please try again.', 'edit-flow' ), + 'messages' => array( + 'post-date-updated' => __( 'Post date updated.', 'edit-flow' ), + 'update-error' => __( 'There was an error updating the post. Please try again.', 'edit-flow' ), /* translators: %s: URL to the published post */ 'published-post-ajax' => __( "Updating the post date dynamically doesn't work for published content. Please edit the post.", 'edit-flow' ), - 'key-regenerated' => __( 'iCal secret key regenerated. Please inform all users they will need to resubscribe.', 'edit-flow' ), + 'key-regenerated' => __( 'iCal secret key regenerated. Please inform all users they will need to resubscribe.', 'edit-flow' ), ), - 'configure_page_cb' => 'print_configure_view', - 'configure_link_text' => __( 'Calendar Options', 'edit-flow' ), - 'settings_help_tab' => array( - 'id' => 'ef-calendar-overview', - 'title' => __( 'Overview', 'edit-flow' ), + 'configure_page_cb' => 'print_configure_view', + 'configure_link_text' => __( 'Calendar Options', 'edit-flow' ), + 'settings_help_tab' => array( + 'id' => 'ef-calendar-overview', + 'title' => __( 'Overview', 'edit-flow' ), + // phpcs:ignore WordPress.WP.I18n.NoHtmlWrappedStrings -- HTML is intentional for help tab content. 'content' => __( '

    The calendar is a convenient week-by-week or month-by-month view into your content. Quickly see which stories are on track to being published on time, and which will need extra effort.

    ', 'edit-flow' ), ), 'settings_help_sidebar' => __( '

    For more information:

    Calendar Documentation

    Edit Flow Forum

    Edit Flow on Github

    ', 'edit-flow' ), @@ -88,18 +150,18 @@ public function __construct() { */ public function init() { - // .ics calendar subscriptions + // .ics calendar subscriptions. add_action( 'wp_ajax_ef_calendar_ics_subscription', array( $this, 'handle_ics_subscription' ) ); add_action( 'wp_ajax_nopriv_ef_calendar_ics_subscription', array( $this, 'handle_ics_subscription' ) ); - // Check whether the user should have the ability to view the calendar + // Check whether the user should have the ability to view the calendar. $view_calendar_cap = 'ef_view_calendar'; $view_calendar_cap = apply_filters( 'ef_view_calendar_cap', $view_calendar_cap ); if ( ! current_user_can( $view_calendar_cap ) ) { return false; } - // Define the create-post capability + // Define the create-post capability. $this->create_post_cap = apply_filters( 'ef_calendar_create_post_cap', 'edit_posts' ); add_action( 'admin_init', array( $this, 'add_screen_options_panel' ) ); @@ -110,19 +172,19 @@ public function init() { add_action( 'admin_print_styles', array( $this, 'add_admin_styles' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); - // Ajax manipulation for the calendar + // Ajax manipulation for the calendar. add_action( 'wp_ajax_ef_calendar_drag_and_drop', array( $this, 'handle_ajax_drag_and_drop' ) ); - // Ajax insert post placeholder for a specific date + // Ajax insert post placeholder for a specific date. add_action( 'wp_ajax_ef_insert_post', array( $this, 'handle_ajax_insert_post' ) ); - //Update metadata + // Update metadata. add_action( 'wp_ajax_ef_calendar_update_metadata', array( $this, 'handle_ajax_update_metadata' ) ); - // Action to regenerate the calendar feed sekret + // Action to regenerate the calendar feed secret. add_action( 'admin_init', array( $this, 'handle_regenerate_calendar_feed_secret' ) ); - // Hacks to fix deficiencies in core + // Hacks to fix deficiencies in core. add_action( 'pre_post_update', array( $this, 'fix_post_date_on_update_part_one' ), 10, 2 ); add_action( 'post_updated', array( $this, 'fix_post_date_on_update_part_two' ), 10, 3 ); } @@ -134,13 +196,13 @@ public function init() { */ public function install() { - // Add necessary capabilities to allow management of calendar - // view_calendar - administrator --> contributor + // Add necessary capabilities to allow management of calendar. + // Adds view_calendar capability from administrator to contributor. $calendar_roles = array( 'administrator' => array( 'ef_view_calendar' ), - 'editor' => array( 'ef_view_calendar' ), - 'author' => array( 'ef_view_calendar' ), - 'contributor' => array( 'ef_view_calendar' ), + 'editor' => array( 'ef_view_calendar' ), + 'author' => array( 'ef_view_calendar' ), + 'contributor' => array( 'ef_view_calendar' ), ); foreach ( $calendar_roles as $role => $caps ) { @@ -149,16 +211,18 @@ public function install() { } /** - * Upgrade our data in case we need to + * Upgrade our data in case we need to. * * @since 0.7 + * + * @param string $previous_version Previous plugin version. */ public function upgrade( $previous_version ) { global $edit_flow; - // Upgrade path to v0.7 + // Upgrade path to v0.7. if ( version_compare( $previous_version, '0.7', '<' ) ) { - // Migrate whether the calendar was enabled or not and clean up old option + // Migrate whether the calendar was enabled or not and clean up old option. $enabled = get_option( 'edit_flow_calendar_enabled' ); if ( $enabled ) { $enabled = 'on'; @@ -168,7 +232,7 @@ public function upgrade( $previous_version ) { $edit_flow->update_module_option( $this->module->name, 'enabled', $enabled ); delete_option( 'edit_flow_calendar_enabled' ); - // Technically we've run this code before so we don't want to auto-install new data + // Technically we've run this code before so we don't want to auto-install new data. $edit_flow->update_module_option( $this->module->name, 'loaded_once', true ); } } @@ -189,12 +253,16 @@ public function action_admin_menu() { */ public function add_admin_styles() { global $pagenow; - // Only load calendar styles on the calendar page + // Only load calendar styles on the calendar page. + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only checking page name, not processing data. if ( 'index.php' === $pagenow && isset( $_GET['page'] ) && 'calendar' === $_GET['page'] ) { wp_enqueue_style( 'edit-flow-calendar-css', $this->module_url . 'lib/calendar.css', false, EDIT_FLOW_VERSION ); $asset_file = EDIT_FLOW_ROOT . '/build/calendar-react.asset.php'; - $asset = file_exists( $asset_file ) ? require $asset_file : [ 'dependencies' => [], 'version' => EDIT_FLOW_VERSION ]; + $asset = file_exists( $asset_file ) ? require $asset_file : [ + 'dependencies' => [], + 'version' => EDIT_FLOW_VERSION, + ]; wp_enqueue_style( 'edit-flow-calendar-react-css', @@ -214,6 +282,7 @@ public function add_admin_styles() { public function enqueue_admin_scripts() { global $pagenow; + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only checking page name, not processing data. if ( 'index.php' === $pagenow && isset( $_GET['page'] ) && 'calendar' === $_GET['page'] ) { $this->enqueue_datepicker_resources(); @@ -223,7 +292,10 @@ public function enqueue_admin_scripts() { * that calendar.js depends on for drag-and-drop functionality. */ $asset_file = EDIT_FLOW_ROOT . '/build/calendar-react.asset.php'; - $asset = file_exists( $asset_file ) ? require $asset_file : [ 'dependencies' => [], 'version' => EDIT_FLOW_VERSION ]; + $asset = file_exists( $asset_file ) ? require $asset_file : [ + 'dependencies' => [], + 'version' => EDIT_FLOW_VERSION, + ]; wp_enqueue_script( 'edit-flow-calendar-react-js', @@ -267,15 +339,15 @@ public function generate_screen_options() { $output = ''; - $args = array( - 'action' => 'ef_calendar_ics_subscription', - 'user' => wp_get_current_user()->user_login, - 'user_key' => md5( wp_get_current_user()->user_login . $this->module->options->ics_secret_key ), + $args = array( + 'action' => 'ef_calendar_ics_subscription', + 'user' => wp_get_current_user()->user_login, + 'user_key' => md5( wp_get_current_user()->user_login . $this->module->options->ics_secret_key ), ); $subscription_link = add_query_arg( $args, admin_url( 'admin-ajax.php' ) ); - $output .= '
    '; - $output .= __( 'Subscribe in iCal or Google Calendar', 'edit-flow' ); - $output .= ':
    '; + $output .= '
    '; + $output .= __( 'Subscribe in iCal or Google Calendar', 'edit-flow' ); + $output .= ':
    '; return $output; } @@ -298,29 +370,33 @@ public function add_screen_options_panel() { * @since 0.7 */ public function handle_save_screen_options() { + // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified below. - // Only handle screen options submissions from the current screen + // Only handle screen options submissions from the current screen. if ( ! isset( $_POST['screen-options-apply'] ) ) { return; } - // Nonce check - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + // phpcs:enable WordPress.Security.NonceVerification.Missing + + // Nonce check. + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce(). if ( ! isset( $_POST[ '_wpnonce-' . self::usermeta_key_prefix . 'screen_options' ] ) || ! wp_verify_nonce( $_POST[ '_wpnonce-' . self::usermeta_key_prefix . 'screen_options' ], 'save_settings-' . self::usermeta_key_prefix . 'screen_options' ) ) { wp_die( esc_html( $this->module->messages['nonce-failed'] ) ); } - // Get the current screen options + // Get the current screen options. $screen_options = $this->get_screen_options(); - // Save the screen options + // Save the screen options. $current_user = wp_get_current_user(); $this->update_user_meta( $current_user->ID, self::usermeta_key_prefix . 'screen_options', $screen_options ); - // Redirect after we're complete + // Redirect after we're complete. $redirect_to = menu_page_url( $this->module->slug, false ); + // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Redirect URL is constructed internally. wp_redirect( $redirect_to ); - wp_die(); + exit; } /** @@ -336,8 +412,8 @@ public function handle_save_screen_options() { public function handle_ajax_drag_and_drop() { global $wpdb; - // Nonce check! - if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'ef-calendar-modify' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + // Nonce check. + if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'ef-calendar-modify' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce(). $this->print_ajax_response( 'error', $this->module->messages['nonce-failed'] ); } @@ -345,19 +421,19 @@ public function handle_ajax_drag_and_drop() { $this->print_ajax_response( 'error', $this->module->messages['missing-post'] ); } - // Check that we got a proper post + // Check that we got a proper post. $post_id = (int) $_POST['post_id']; - $post = get_post( $post_id ); + $post = get_post( $post_id ); if ( ! $post ) { $this->print_ajax_response( 'error', $this->module->messages['missing-post'] ); } - // Check that the user can modify the post + // Check that the user can modify the post. if ( ! $this->current_user_can_modify_post( $post ) ) { $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] ); } - // Check that it's not yet published + // Check that it's not yet published. if ( in_array( $post->post_status, $this->published_statuses ) ) { $this->print_ajax_response( 'error', sprintf( $this->module->messages['published-post-ajax'], get_edit_post_link( $post_id ) ) ); } @@ -366,32 +442,33 @@ public function handle_ajax_drag_and_drop() { $this->print_ajax_response( 'error', __( 'Missing new date.', 'edit-flow' ) ); } - // Check that the new date passed is a valid one - $next_date_full = strtotime( $_POST['next_date'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + // Check that the new date passed is a valid one. + $next_date_full = strtotime( $_POST['next_date'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Used with strtotime() for date parsing only. if ( ! $next_date_full ) { $this->print_ajax_response( 'error', __( 'Something is wrong with the format for the new date.', 'edit-flow' ) ); } - // Persist the old hourstamp because we can't manipulate the exact time on the calendar - // Bump the last modified timestamps too - $existing_time = date( 'H:i:s', strtotime( $post->post_date ) ); + // Persist the old hourstamp because we can't manipulate the exact time on the calendar. + // Bump the last modified timestamps too. + $existing_time = date( 'H:i:s', strtotime( $post->post_date ) ); $existing_time_gmt = date( 'H:i:s', strtotime( $post->post_date_gmt ) ); - $new_values = array( - 'post_date' => date( 'Y-m-d', $next_date_full ) . ' ' . $existing_time, - 'post_modified' => current_time( 'mysql' ), + $new_values = array( + 'post_date' => date( 'Y-m-d', $next_date_full ) . ' ' . $existing_time, + 'post_modified' => current_time( 'mysql' ), 'post_modified_gmt' => current_time( 'mysql', 1 ), ); // By default, changing a post on the calendar won't set the timestamp. - // If the user desires that to be the behaviour, they can set the result of this filter to 'true' - // With how WordPress works internally, setting 'post_date_gmt' will set the timestamp + // If the user desires that to be the behaviour, they can set the result of this filter to 'true'. + // With how WordPress works internally, setting 'post_date_gmt' will set the timestamp. if ( apply_filters( 'ef_calendar_allow_ajax_to_set_timestamp', false ) ) { $new_values['post_date_gmt'] = date( 'Y-m-d', $next_date_full ) . ' ' . $existing_time_gmt; } - // We have to do SQL unfortunately because of core bugginess - // Note to those reading this: bug Nacin to allow us to finish the custom status API - // See http://core.trac.wordpress.org/ticket/18362 + // We have to do SQL unfortunately because of core bugginess. + // Note to those reading this: bug Nacin to allow us to finish the custom status API. + // See http://core.trac.wordpress.org/ticket/18362. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Core workaround for custom status API limitations. $response = $wpdb->update( $wpdb->posts, $new_values, array( 'ID' => $post->ID ) ); clean_post_cache( $post->ID ); @@ -409,66 +486,71 @@ public function handle_ajax_drag_and_drop() { */ public function handle_ics_subscription() { - // Only do .ics subscriptions when the option is active + // Only do .ics subscriptions when the option is active. if ( 'on' != $this->module->options->ics_subscription ) { - wp_die(); // @todo return accepted response value. + wp_die(); // @todo Return accepted response value. } - // Confirm all of the arguments are present - if ( ! isset( $_GET['user'], $_GET['user_key'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended - wp_die(); // @todo return an error response + // Confirm all of the arguments are present. + if ( ! isset( $_GET['user'], $_GET['user_key'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public feed with secret key validation. + wp_die(); // @todo Return an error response. } - // Confirm this is a valid request - $user = sanitize_user( $_GET['user'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $user_key = sanitize_user( $_GET['user_key'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended + // Confirm this is a valid request. + $user = sanitize_user( $_GET['user'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public feed with secret key validation. + $user_key = sanitize_user( $_GET['user_key'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public feed with secret key validation. $ics_secret_key = $this->module->options->ics_secret_key; if ( ! $ics_secret_key || md5( $user . $ics_secret_key ) !== $user_key ) { wp_die( esc_html( $this->module->messages['nonce-failed'] ) ); } - // Set up the post data to be printed - $post_query_args = array(); + // Set up the post data to be printed. + $post_query_args = array(); $calendar_filters = $this->calendar_filters(); foreach ( $calendar_filters as $filter ) { - if ( isset( $_GET[ $filter ] ) && false !== ( $value = $this->sanitize_filter( $filter, $_GET[ $filter ] ) ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended - $post_query_args[ $filter ] = $value; + // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Public feed with secret key validation, sanitized by sanitize_filter(). + if ( isset( $_GET[ $filter ] ) ) { + // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Public feed with secret key validation, sanitized by sanitize_filter(). + $value = $this->sanitize_filter( $filter, $_GET[ $filter ] ); + if ( false !== $value ) { + $post_query_args[ $filter ] = $value; + } } } - // Set the start date for the posts_where filter + // Set the start date for the posts_where filter. + // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested -- Used for date calculation in calendar context. $this->start_date = apply_filters( 'ef_calendar_ics_subscription_start_date', $this->get_beginning_of_week( date( 'Y-m-d', current_time( 'timestamp' ) ) ) ); $this->total_weeks = apply_filters( 'ef_calendar_total_weeks', $this->total_weeks, 'ics_subscription' ); $formatted_posts = array(); for ( $current_week = 1; $current_week <= $this->total_weeks; $current_week++ ) { - // We need to set the object variable for our posts_where filter + // We need to set the object variable for our posts_where filter. $this->current_week = $current_week; - $week_posts = $this->get_calendar_posts_for_week( $post_query_args, 'ics_subscription' ); + $week_posts = $this->get_calendar_posts_for_week( $post_query_args, 'ics_subscription' ); foreach ( $week_posts as $date => $day_posts ) { foreach ( $day_posts as $num => $post ) { - - $start_date = self::ics_format_time( $post->post_date ); - $end_date = self::ics_format_time( $post->post_date, 5 * MINUTE_IN_SECONDS ); - $last_modified = self::ics_format_time( $post->post_modified ); + $start_date = self::ics_format_time( $post->post_date ); + $end_date = self::ics_format_time( $post->post_date, 5 * MINUTE_IN_SECONDS ); + $last_modified = self::ics_format_time( $post->post_modified ); $post_status_obj = get_post_status_object( get_post_status( $post->ID ) ); - // Remove the convert chars and wptexturize filters from the title + // Remove the convert chars and wptexturize filters from the title. remove_filter( 'the_title', 'convert_chars' ); remove_filter( 'the_title', 'wptexturize' ); $formatted_post = array( - 'BEGIN' => 'VEVENT', - 'UID' => $post->guid, - 'SUMMARY' => $this->do_ics_escaping( apply_filters( 'the_title', $post->post_title ) ) . ' - ' . $post_status_obj->label, - 'DTSTART' => $start_date, - 'DTEND' => $end_date, - 'LAST-MODIFIED' => $last_modified, - 'URL' => get_post_permalink( $post->ID ), + 'BEGIN' => 'VEVENT', + 'UID' => $post->guid, + 'SUMMARY' => $this->do_ics_escaping( apply_filters( 'the_title', $post->post_title ) ) . ' - ' . $post_status_obj->label, + 'DTSTART' => $start_date, + 'DTEND' => $end_date, + 'LAST-MODIFIED' => $last_modified, + 'URL' => get_post_permalink( $post->ID ), ); - // Description should include everything visible in the calendar popup - $information_fields = $this->get_post_information_fields( $post ); + // Description should include everything visible in the calendar popup. + $information_fields = $this->get_post_information_fields( $post ); $formatted_post['DESCRIPTION'] = ''; if ( ! empty( $information_fields ) ) { foreach ( $information_fields as $key => $values ) { @@ -479,25 +561,25 @@ public function handle_ics_subscription() { $formatted_post['END'] = 'VEVENT'; - // @todo auto format any field longer than 75 bytes + // @todo Auto format any field longer than 75 bytes. $formatted_posts[] = $formatted_post; } } } - // Other template data + // Other template data. $header = array( - 'BEGIN' => 'VCALENDAR', - 'VERSION' => '2.0', - 'PRODID' => '-//Edit Flow//Edit Flow ' . EDIT_FLOW_VERSION . '//EN', + 'BEGIN' => 'VCALENDAR', + 'VERSION' => '2.0', + 'PRODID' => '-//Edit Flow//Edit Flow ' . EDIT_FLOW_VERSION . '//EN', ); $footer = array( - 'END' => 'VCALENDAR', + 'END' => 'VCALENDAR', ); - // Render the .ics template and set the content type + // Render the .ics template and set the content type. header( 'Content-type: text/calendar' ); foreach ( array( $header, $formatted_posts, $footer ) as $section ) { foreach ( $section as $key => $value ) { @@ -519,7 +601,7 @@ public function handle_ics_subscription() { /** * Perform line folding according to RFC 5545. * - * @param string $line The line without trailing CRLF + * @param string $line The line without trailing CRLF. * @return string The line after line-folding with all necessary CRLF. */ public function do_ics_line_folding( $line ) { @@ -529,11 +611,11 @@ public function do_ics_line_folding( $line ) { } $chunks = array(); - $start = 0; + $start = 0; while ( true ) { - $chunk = mb_substr( $line, $start, 75 ); + $chunk = mb_substr( $line, $start, 75 ); $chunk_len = mb_strlen( $chunk ); - $start += $chunk_len; + $start += $chunk_len; if ( $start < $len ) { $chunks[] = $chunk . "\r\n "; } else { @@ -546,11 +628,10 @@ public function do_ics_line_folding( $line ) { /** * Perform the encoding necessary for ICS feed text. * - * @param string $text The string that needs to be escaped + * @param string $text The string that needs to be escaped. * @return string The string after escaping for ICS. * @since 0.8 - * */ - + */ public function do_ics_escaping( $text ) { $text = str_replace( ',', '\,', $text ); $text = str_replace( ';', '\:', $text ); @@ -559,26 +640,26 @@ public function do_ics_escaping( $text ) { } /** - * Convert a time string into a `.ics` formatted time string with the proper GMT offset + * Convert a time string into a `.ics` formatted time string with the proper GMT offset. * - * @param $time_string - Any time string that `strtotime()` can understand - * @param int $offset_in_seconds - Allows to offset the timestamp generated from $time_string + * @param string $time_string Any time string that `strtotime()` can understand. + * @param int $offset_in_seconds Allows to offset the timestamp generated from $time_string. * * @return string|false */ public static function ics_format_time( $time_string, $offset_in_seconds = 0 ) { - // Timestamp it + // Timestamp it. $timestamp = strtotime( $time_string ); if ( ! $timestamp ) { return false; } - // Subtract GMT Offset to return to UTC+0 + // Subtract GMT Offset to return to UTC+0. $timestamp -= get_option( 'gmt_offset' ) * HOUR_IN_SECONDS; - // Add manual offset + // Add manual offset. $timestamp += $offset_in_seconds; // \T and \Z are escaped for literal T and Z characters @@ -607,7 +688,7 @@ public function handle_regenerate_calendar_feed_secret() { EditFlow()->update_module_option( $this->module->name, 'ics_secret_key', wp_generate_password() ); wp_safe_redirect( add_query_arg( 'message', 'key-regenerated', menu_page_url( $this->module->settings_slug, false ) ) ); - wp_die(); + exit; } /** @@ -622,12 +703,13 @@ public function get_screen_options() { /** * `num_weeks` has been moved to a filter and out of screen options, it's maintained here for legacy purposes + * * @deprecated `num_weeks` */ - $defaults = array( + $defaults = array( 'num_weeks' => (int) $this->total_weeks, ); - $current_user = wp_get_current_user(); + $current_user = wp_get_current_user(); $screen_options = $this->get_user_meta( $current_user->ID, self::usermeta_key_prefix . 'screen_options', true ); $screen_options = array_merge( (array) $defaults, (array) $screen_options ); @@ -641,9 +723,9 @@ public function get_screen_options() { * @return array $filters All of the set or saved calendar filters */ public function get_filters() { - $current_user = wp_get_current_user(); - $filters = array(); - $old_filters = $this->get_user_meta( $current_user->ID, self::usermeta_key_prefix . 'filters', true ); + $current_user = wp_get_current_user(); + $filters = array(); + $old_filters = $this->get_user_meta( $current_user->ID, self::usermeta_key_prefix . 'filters', true ); /** * To support legacy screen option for num_weeks @@ -652,24 +734,28 @@ public function get_filters() { $default_filters = array( 'post_status' => '', - 'cpt' => '', - 'cat' => '', - 'author' => '', - 'num_weeks' => $this->total_weeks, - 'start_date' => date( 'Y-m-d', current_time( 'timestamp' ) ), + 'cpt' => '', + 'cat' => '', + 'author' => '', + 'num_weeks' => $this->total_weeks, + // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested -- Used for date calculation in calendar context. + 'start_date' => date( 'Y-m-d', current_time( 'timestamp' ) ), ); - $old_filters = array_merge( $default_filters, isset( $screen_options['num_weeks'] ) ? array( 'num_weeks' => $screen_options['num_weeks'] ) : array(), (array) $old_filters ); + $old_filters = array_merge( $default_filters, isset( $screen_options['num_weeks'] ) ? array( 'num_weeks' => $screen_options['num_weeks'] ) : array(), (array) $old_filters ); - // Sanitize and validate any newly added filters + // Sanitize and validate any newly added filters. foreach ( $old_filters as $key => $old_value ) { - if ( isset( $_GET[ $key ] ) && false !== ( $new_value = $this->sanitize_filter( $key, $_GET[ $key ] ) ) ) { - $filters[ $key ] = $new_value; - } else { - $filters[ $key ] = $old_value; + if ( isset( $_GET[ $key ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Filter values are sanitized below and stored per user. + $new_value = $this->sanitize_filter( $key, $_GET[ $key ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Filter values are sanitized by sanitize_filter(). + if ( false !== $new_value ) { + $filters[ $key ] = $new_value; + continue; + } } + $filters[ $key ] = $old_value; } - // Set the start date as the beginning of the week, according to blog settings + // Set the start date as the beginning of the week, according to blog settings. $filters['start_date'] = $this->get_beginning_of_week( $filters['start_date'] ); $filters = apply_filters( 'ef_calendar_filter_values', $filters, $old_filters ); @@ -680,16 +766,16 @@ public function get_filters() { } /** - * Build all of the HTML for the calendar view + * Build all of the HTML for the calendar view. */ public function view_calendar() { $supported_post_types = $this->get_post_types_for_module( $this->module ); - // Get filters either from $_GET or from user settings + // Get filters either from $_GET or from user settings. $filters = $this->get_filters(); // Total number of weeks to display on the calendar. Run it through a filter in case we want to override the - // user's standard + // user's standard. $this->total_weeks = apply_filters( 'ef_calendar_total_weeks', $filters['num_weeks'], 'dashboard' ); $dotw = array( @@ -698,8 +784,8 @@ public function view_calendar() { ); $dotw = apply_filters( 'ef_calendar_weekend_days', $dotw ); - // For generating the WP Query objects later on - $post_query_args = array( + // For generating the WP Query objects later on. + $post_query_args = array( 'post_status' => $filters['post_status'], 'post_type' => $filters['cpt'], 'cat' => $filters['cat'], @@ -707,18 +793,18 @@ public function view_calendar() { ); $this->start_date = $filters['start_date']; - // We use this later to label posts if they need labeling + // We use this later to label posts if they need labeling. if ( count( $supported_post_types ) > 1 ) { $all_post_types = get_post_types( null, 'objects' ); } - $dates = array(); + $dates = array(); $heading_date = $filters['start_date']; for ( $i = 0; $i < 7; $i++ ) { - $dates[ $i ] = $heading_date; + $dates[ $i ] = $heading_date; $heading_date = date( 'Y-m-d', strtotime( '+1 day', strtotime( $heading_date ) ) ); } - // we sort by post statuses....... eventually + // We sort by post statuses, eventually. $post_statuses = $this->get_calendar_post_stati(); ?>
    @@ -728,25 +814,26 @@ public function view_calendar() {

    '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - if ( isset( $_GET['trashed'] ) && (int) $_GET['trashed'] ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + if ( isset( $_GET['trashed'] ) && (int) $_GET['trashed'] ) { /* translators: %d: number of posts trashed */ - echo esc_html( sprintf( _n( 'Post moved to the trash.', '%d posts moved to the trash.', $_GET['trashed'] ), number_format_i18n( $_GET['trashed'] ) ) ); - $ids = isset( $_GET['ids'] ) ? $_GET['ids'] : 0; - $pid = explode( ',', $ids ); + echo esc_html( sprintf( _n( '%d post moved to the trash.', '%d posts moved to the trash.', $_GET['trashed'], 'edit-flow' ), number_format_i18n( $_GET['trashed'] ) ) ); + $ids = isset( $_GET['ids'] ) ? $_GET['ids'] : 0; + $pid = explode( ',', $ids ); $post_type = get_post_type( $pid[0] ); echo ' ' . esc_html__( 'Undo', 'edit-flow' ) . '
    '; unset( $_GET['trashed'] ); } - if ( isset( $_GET['untrashed'] ) && (int) $_GET['untrashed'] ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + if ( isset( $_GET['untrashed'] ) && (int) $_GET['untrashed'] ) { /* translators: %d: number of posts restored */ - echo esc_html( sprintf( _n( 'Post restored from the Trash.', '%d posts restored from the Trash.', $_GET['untrashed'] ), number_format_i18n( $_GET['untrashed'] ) ) ); + echo esc_html( sprintf( _n( '%d post restored from the Trash.', '%d posts restored from the Trash.', $_GET['untrashed'], 'edit-flow' ), number_format_i18n( $_GET['untrashed'] ) ) ); unset( $_GET['undeleted'] ); } echo '

    '; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + // phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized } ?> @@ -756,7 +843,7 @@ public function view_calendar() { total_weeks ) { $table_classes[] = 'one-week-showing'; } elseif ( 2 == $this->total_weeks ) { @@ -778,18 +865,18 @@ public function view_calendar() { total_weeks; $current_week++ ) : - // We need to set the object variable for our posts_where filter + // We need to set the object variable for our posts_where filter. $this->current_week = $current_week; - $week_posts = $this->get_calendar_posts_for_week( $post_query_args ); - $date_format = 'Y-m-d'; - $week_single_date = $this->get_beginning_of_week( $filters['start_date'], $date_format, $current_week ); - $week_dates = array(); - $split_month = false; + $week_posts = $this->get_calendar_posts_for_week( $post_query_args ); + $date_format = 'Y-m-d'; + $week_single_date = $this->get_beginning_of_week( $filters['start_date'], $date_format, $current_week ); + $week_dates = array(); + $split_month = false; for ( $i = 0; $i < 7; $i++ ) { - $week_dates[ $i ] = $week_single_date; + $week_dates[ $i ] = $week_single_date; $single_date_month = date_i18n( 'F', strtotime( $week_single_date ) ); if ( $single_date_month != $current_month ) { - $split_month = $single_date_month; + $split_month = $single_date_month; $current_month = $single_date_month; } $week_single_date = date( 'Y-m-d', strtotime( '+1 day', strtotime( $week_single_date ) ) ); @@ -802,7 +889,7 @@ public function view_calendar() { if ( date_i18n( 'F', strtotime( $week_single_date ) ) != $split_month && date_i18n( 'F', strtotime( '+1 day', strtotime( $week_single_date ) ) ) == $split_month ) { $previous_month = date_i18n( 'F', strtotime( $week_single_date ) ); echo '' . esc_html( $previous_month ) . ''; - } else if ( date_i18n( 'F', strtotime( $week_single_date ) ) == $split_month && date_i18n( 'F', strtotime( '-1 day', strtotime( $week_single_date ) ) ) != $split_month ) { + } elseif ( date_i18n( 'F', strtotime( $week_single_date ) ) == $split_month && date_i18n( 'F', strtotime( '-1 day', strtotime( $week_single_date ) ) ) != $split_month ) { echo '' . esc_html( $split_month ) . ''; } else { echo ''; @@ -815,16 +902,16 @@ public function view_calendar() { $week_single_date ) : ?> name ] = array(); } - // These statuses aren't handled by custom statuses or post statuses + // These statuses aren't handled by custom statuses or post statuses. $week_posts_by_status['private'] = array(); $week_posts_by_status['publish'] = array(); - $week_posts_by_status['future'] = array(); + $week_posts_by_status['future'] = array(); foreach ( $week_posts[ $week_single_date ] as $num => $post ) { $week_posts_by_status[ $post->post_status ][ $num ] = $post; } @@ -839,26 +926,29 @@ public function view_calendar() { $td_classes = array( 'day-unit', ); - $day_name = date( 'D', strtotime( $week_single_date ) ); + $day_name = date( 'D', strtotime( $week_single_date ) ); if ( in_array( $day_name, $dotw ) ) { $td_classes[] = 'weekend-day'; } + // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested -- Used for date comparison in calendar display. if ( date( 'Y-m-d', current_time( 'timestamp' ) ) == $week_single_date ) { $td_classes[] = 'today'; } - // Last day of the week + // Last day of the week. if ( 6 == $day_num ) { $td_classes[] = 'last-day'; } $td_classes = apply_filters( 'ef_calendar_table_td_classes', $td_classes, $week_single_date ); + // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested -- Used for date comparison in calendar display. + $is_today = date( 'Y-m-d', current_time( 'timestamp' ) ) == $week_single_date; ?> - +
    @@ -866,7 +956,6 @@ public function view_calendar() { hidden = 0; if ( ! empty( $week_posts[ $week_single_date ] ) ) { - $week_posts[ $week_single_date ] = apply_filters( 'ef_calendar_posts_for_week', $week_posts[ $week_single_date ], $week_single_date ); foreach ( $week_posts[ $week_single_date ] as $num => $post ) { @@ -880,7 +969,7 @@ public function view_calendar() { ?> hidden ) : ?> - hidden ); ?> + hidden ) ); ?> -

    get_quick_create_post_type_name(), $date_formatted ); ?>

    +

    get_quick_create_post_type_name() ), esc_html( $date_formatted ) ); ?>

    @@ -911,30 +1000,31 @@ public function view_calendar() {
    - + ID; + $post_id = $post->ID; $status_object = get_post_status_object( get_post_status( $post_id ) ); $post_classes = array( @@ -952,12 +1042,12 @@ public function generate_post_li_html( $post, $post_date, $num = 0 ) { $post_classes[] = 'is-published'; } - // Hide posts over a certain number to prevent clutter, unless user is only viewing 1 or 2 weeks + // Hide posts over a certain number to prevent clutter, unless user is only viewing 1 or 2 weeks. $max_visible_posts = apply_filters( 'ef_calendar_max_visible_posts_per_date', $this->max_visible_posts_per_date ); if ( $num >= $max_visible_posts && $this->total_weeks > 2 ) { $post_classes[] = 'hidden'; - $this->hidden++; + ++$this->hidden; } $post_classes = apply_filters( 'ef_calendar_table_td_li_classes', $post_classes, $post_date, $post->ID ); @@ -983,18 +1073,19 @@ public function generate_post_li_html( $post, $post_date, $num = 0 ) { ob_end_clean(); return $post_li_html; - } // generate_post_li_html() + } /** - * get_inner_information description + * Generate the inner HTML elements for a calendar item. + * * Functionality for generating the inner html elements on the calendar * has been separated out so various ajax functions can reload certain * parts of an inner html element. - * @param array $ef_calendar_item_information_fields - * @param WP_Post $post - * @param array $published_statuses * * @since 0.8 + * + * @param array $ef_calendar_item_information_fields Array of information fields. + * @param WP_Post $post The post object. */ public function get_inner_information( $ef_calendar_item_information_fields, $post ) { ?> @@ -1023,13 +1114,13 @@ public function get_inner_information( $ef_calendar_item_information_fields, $po post_type ); - $item_actions = array(); + $item_actions = array(); if ( $this->current_user_can_modify_post( $post ) ) { - // Edit this post + // Edit this post. $item_actions['edit'] = '' . __( 'Edit', 'edit-flow' ) . ''; - // Trash this post - $item_actions['trash'] = '' . __( 'Trash', 'edit-flow' ) . ''; - // Preview/view this post + // Trash this post. + $item_actions['trash'] = '' . __( 'Trash', 'edit-flow' ) . ''; + // Preview/view this post. if ( ! in_array( $post->post_status, $this->published_statuses ) ) { /* translators: %s: post title */ $item_actions['view'] = '' . __( 'Preview', 'edit-flow' ) . ''; @@ -1037,14 +1128,14 @@ public function get_inner_information( $ef_calendar_item_information_fields, $po /* translators: %s: post title */ $item_actions['view'] = '' . __( 'View', 'edit-flow' ) . ''; } - //Save metadata + // Save metadata. /* translators: %s: post title */ $item_actions['save hidden'] = '' . __( 'Save', 'edit-flow' ) . ''; } - // Allow other plugins to add actions + // Allow other plugins to add actions. $item_actions = apply_filters( 'ef_calendar_item_actions', $item_actions, $post->ID ); if ( count( $item_actions ) ) { - // Separate the save action to render it on its own row + // Separate the save action to render it on its own row. $save_action = ''; if ( isset( $item_actions['save hidden'] ) ) { $save_action = $item_actions['save hidden']; @@ -1058,7 +1149,7 @@ public function get_inner_information( $ef_calendar_item_information_fields, $po } echo rtrim( $html, ' | ' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - // Render save button on its own row (hidden by default, shown via JS when editing) + // Render save button on its own row (hidden by default, shown via JS when editing). if ( $save_action ) { echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } @@ -1067,8 +1158,15 @@ public function get_inner_information( $ef_calendar_item_information_fields, $po ?>
    '; - break; case 'paragraph': return ''; - break; case 'date': // Convert display value to datetime-local format (Y-m-d\TH:i). $datetime_value = ''; @@ -1090,7 +1186,6 @@ public function get_editable_html( $type, $value ) { } } return ''; - break; case 'checkbox': $output = ''; return $output; - break; case 'user': return wp_dropdown_users( array( 'echo' => false ) ); - break; case 'taxonomy': return ''; - break; } } /** - * Get the information fields to be presented with each post popup + * Get the information fields to be presented with each post popup. * * @since 0.8 * - * @param obj $post Post to gather information fields for - * @return array $information_fields All of the information fields to be presented + * @param object $post Post to gather information fields for. + * @return array $information_fields All of the information fields to be presented. */ public function get_post_information_fields( $post ) { $information_fields = array(); - // Post author + // Post author. $information_fields['author'] = array( - 'label' => __( 'Author', 'edit-flow' ), - 'value' => get_the_author_meta( 'display_name', $post->post_author ), - 'type' => 'author', + 'label' => __( 'Author', 'edit-flow' ), + 'value' => get_the_author_meta( 'display_name', $post->post_author ), + 'type' => 'author', ); - // If the calendar supports more than one post type, show the post type label + // If the calendar supports more than one post type, show the post type label. if ( count( $this->get_post_types_for_module( $this->module ) ) > 1 ) { $information_fields['post_type'] = array( 'label' => __( 'Post Type', 'edit-flow' ), 'value' => get_post_type_object( $post->post_type )->labels->singular_name, ); } - // Publication time for published statuses + // Publication time for published statuses. $published_statuses = array( 'publish', 'future', @@ -1157,13 +1249,13 @@ public function get_post_information_fields( $post ) { ); } } - // Taxonomies and their values - $args = array( + // Taxonomies and their values. + $args = array( 'post_type' => $post->post_type, ); $taxonomies = get_object_taxonomies( $args, 'object' ); foreach ( (array) $taxonomies as $taxonomy ) { - // Sometimes taxonomies skip by, so let's make sure it has a label too + // Sometimes taxonomies skip by, so let's make sure it has a label too. if ( ! $taxonomy->public || ! $taxonomy->label ) { continue; } @@ -1220,10 +1312,10 @@ public function get_post_information_fields( $post ) { } /** - * Generate the calendar header for a given range of dates + * Generate the calendar header for a given range of dates. * - * @param array $dates Date range for the header - * @return string $html Generated HTML for the header + * @param array $dates Date range for the header. + * @return string $html Generated HTML for the header. */ public function get_time_period_header( $dates ) { @@ -1238,21 +1330,22 @@ public function get_time_period_header( $dates ) { } /** - * Query to get all of the calendar posts for a given day + * Query to get all of the calendar posts for a given day. * - * @param array $args Any filter arguments we want to pass - * @param string $request_context Where the query is coming from, to distinguish dashboard and subscriptions - * @return array $posts All of the posts as an array sorted by date + * @param array $args Any filter arguments we want to pass. + * @param string $context Where the query is coming from, to distinguish dashboard and subscriptions. + * @return array $posts All of the posts as an array sorted by date. */ public function get_calendar_posts_for_week( $args = array(), $context = 'dashboard' ) { $supported_post_types = $this->get_post_types_for_module( $this->module ); - $defaults = array( - 'post_status' => null, - 'cat' => null, - 'author' => null, - 'post_type' => $supported_post_types, - 'posts_per_page' => 200, + $defaults = array( + 'post_status' => null, + 'cat' => null, + 'author' => null, + 'post_type' => $supported_post_types, + // phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page -- Calendar needs to show all posts for the week. + 'posts_per_page' => 200, ); $args = array_merge( $defaults, $args ); @@ -1260,7 +1353,7 @@ public function get_calendar_posts_for_week( $args = array(), $context = 'dashbo // Unpublished as a status is just an array of everything but 'publish'. if ( 'unpublish' == $args['post_status'] ) { $args['post_status'] = ''; - $post_stati = wp_filter_object_list( $this->get_calendar_post_stati(), array( 'name' => 'publish' ), 'not' ); + $post_stati = wp_filter_object_list( $this->get_calendar_post_stati(), array( 'name' => 'publish' ), 'not' ); if ( ! apply_filters( 'ef_show_scheduled_as_unpublished', false ) ) { $post_stati = wp_filter_object_list( $post_stati, array( 'name' => 'future' ), 'not' ); @@ -1291,15 +1384,15 @@ public function get_calendar_posts_for_week( $args = array(), $context = 'dashbo 'inclusive' => true, ); - // Filter for an end user to implement any of their own query args - $args = apply_filters( 'ef_calendar_posts_query_args', $args, $context ); + // Filter for an end user to implement any of their own query args. + $args = apply_filters( 'ef_calendar_posts_query_args', $args, $context ); $post_results = new WP_Query( $args ); $posts = array(); while ( $post_results->have_posts() ) { $post_results->the_post(); global $post; - $key_date = date( 'Y-m-d', strtotime( $post->post_date ) ); + $key_date = date( 'Y-m-d', strtotime( $post->post_date ) ); $posts[ $key_date ][] = $post; } @@ -1307,12 +1400,12 @@ public function get_calendar_posts_for_week( $args = array(), $context = 'dashbo } /** - * Gets the link for the next time period + * Gets the link for the next time period. * - * @param string $direction 'previous' or 'next', direction to go in time - * @param array $filters Any filters that need to be applied - * @param int $weeks_offset Number of weeks we're offsetting the range - * @return string $url The URL for the next page + * @param string $direction 'previous' or 'next', direction to go in time. + * @param array $filters Any filters that need to be applied. + * @param int $weeks_offset Number of weeks we're offsetting the range. + * @return string $url The URL for the next page. */ public function get_pagination_link( $direction = 'next', $filters = array(), $weeks_offset = null ) { @@ -1320,7 +1413,8 @@ public function get_pagination_link( $direction = 'next', $filters = array(), $w if ( ! isset( $weeks_offset ) ) { $weeks_offset = $this->total_weeks; - } else if ( 0 == $weeks_offset ) { + } elseif ( 0 == $weeks_offset ) { + // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested -- Used for date calculation in calendar context. $filters['start_date'] = $this->get_beginning_of_week( date( 'Y-m-d', current_time( 'timestamp' ) ) ); } @@ -1329,7 +1423,7 @@ public function get_pagination_link( $direction = 'next', $filters = array(), $w } $filters['start_date'] = date( 'Y-m-d', strtotime( $weeks_offset . ' weeks', strtotime( $filters['start_date'] ) ) ); - $url = add_query_arg( $filters, menu_page_url( $this->module->slug, false ) ); + $url = add_query_arg( $filters, menu_page_url( $this->module->slug, false ) ); if ( count( $supported_post_types ) > 1 ) { $url = add_query_arg( 'cpt', $filters['cpt'], $url ); @@ -1344,18 +1438,18 @@ public function get_pagination_link( $direction = 'next', $filters = array(), $w * * @see http://www.php.net/manual/en/datetime.formats.date.php for valid date formats * - * @param string $date String representing a date - * @param string $format Date format in which the beginning of the week should be returned - * @param int $week Number of weeks we're offsetting the range - * @return string $formatted_start_of_week Beginning of the week + * @param string $date String representing a date. + * @param string $format Date format in which the beginning of the week should be returned. + * @param int $week Number of weeks we're offsetting the range. + * @return string $formatted_start_of_week Beginning of the week. */ public function get_beginning_of_week( $date, $format = 'Y-m-d', $week = 1 ) { - $date = strtotime( $date ); - $start_of_week = get_option( 'start_of_week' ); - $day_of_week = date( 'w', $date ); - $date += ( ( $start_of_week - $day_of_week - 7 ) % 7 ) * 60 * 60 * 24; - $date = strtotime( '+' . ( $week - 1 ) . ' week', $date ); + $date = strtotime( $date ); + $start_of_week = get_option( 'start_of_week' ); + $day_of_week = date( 'w', $date ); + $date += ( ( $start_of_week - $day_of_week - 7 ) % 7 ) * 60 * 60 * 24; + $date = strtotime( '+' . ( $week - 1 ) . ' week', $date ); $formatted_start_of_week = date( $format, $date ); return $formatted_start_of_week; } @@ -1366,46 +1460,47 @@ public function get_beginning_of_week( $date, $format = 'Y-m-d', $week = 1 ) { * * @see http://www.php.net/manual/en/datetime.formats.date.php for valid date formats * - * @param string $date String representing a date - * @param string $format Date format in which the end of the week should be returned - * @param int $week Number of weeks we're offsetting the range - * @return string $formatted_end_of_week End of the week + * @param string $date String representing a date. + * @param string $format Date format in which the end of the week should be returned. + * @param int $week Number of weeks we're offsetting the range. + * @return string $formatted_end_of_week End of the week. */ public function get_ending_of_week( $date, $format = 'Y-m-d', $week = 1 ) { - $date = strtotime( $date ); - $end_of_week = get_option( 'start_of_week' ) - 1; - $day_of_week = date( 'w', $date ); - $date += ( ( $end_of_week - $day_of_week + 7 ) % 7 ) * 60 * 60 * 24; - $date = strtotime( '+' . ( $week - 1 ) . ' week', $date ); + $date = strtotime( $date ); + $end_of_week = get_option( 'start_of_week' ) - 1; + $day_of_week = date( 'w', $date ); + $date += ( ( $end_of_week - $day_of_week + 7 ) % 7 ) * 60 * 60 * 24; + $date = strtotime( '+' . ( $week - 1 ) . ' week', $date ); $formatted_end_of_week = date( $format, $date ); return $formatted_end_of_week; } /** - * Human-readable time range for the calendar - * Shows something like "for October 30th through November 26th" for a four-week period + * Human-readable time range for the calendar. + * + * Shows something like "for October 30th through November 26th" for a four-week period. * * @since 0.7 */ public function calendar_time_range() { $first_datetime = strtotime( $this->start_date ); - $first_date = date_i18n( get_option( 'date_format' ), $first_datetime ); - $total_days = ( $this->total_weeks * 7 ) - 1; - $last_datetime = strtotime( '+' . $total_days . ' days', date( 'U', strtotime( $this->start_date ) ) ); - $last_date = date_i18n( get_option( 'date_format' ), $last_datetime ); - // translators: %1$s = first date, %2$s = last date + $first_date = date_i18n( get_option( 'date_format' ), $first_datetime ); + $total_days = ( $this->total_weeks * 7 ) - 1; + $last_datetime = strtotime( '+' . $total_days . ' days', date( 'U', strtotime( $this->start_date ) ) ); + $last_date = date_i18n( get_option( 'date_format' ), $last_datetime ); + // translators: %1$s = first date, %2$s = last date. echo esc_html( sprintf( __( 'for %1$s through %2$s', 'edit-flow' ), $first_date, $last_date ) ); } /** - * Check whether the current user should have the ability to modify the post + * Check whether the current user should have the ability to modify the post. * * @since 0.7 * - * @param object $post The post object we're checking - * @return bool $can Whether or not the current user can modify the post + * @param object $post The post object we're checking. + * @return bool $can Whether or not the current user can modify the post. */ public function current_user_can_modify_post( $post ) { @@ -1415,15 +1510,15 @@ public function current_user_can_modify_post( $post ) { $post_type_object = get_post_type_object( $post->post_type ); - // Editors and admins are fine + // Editors and admins are fine. if ( current_user_can( $post_type_object->cap->edit_others_posts, $post->ID ) ) { return true; } - // Authors and contributors can move their own stuff if it's not published + // Authors and contributors can move their own stuff if it's not published. if ( current_user_can( $post_type_object->cap->edit_post, $post->ID ) && wp_get_current_user()->ID == $post->post_author && ! in_array( $post->post_status, $this->published_statuses ) ) { return true; } - // Those who can publish posts can move any of their own stuff + // Those who can publish posts can move any of their own stuff. if ( current_user_can( $post_type_object->cap->publish_posts, $post->ID ) && wp_get_current_user()->ID == $post->post_author ) { return true; } @@ -1479,8 +1574,8 @@ public function settings_quick_create_post_type_option() { */ public function settings_ics_subscription_option() { $options = array( - 'off' => __( 'Disabled', 'edit-flow' ), - 'on' => __( 'Enabled', 'edit-flow' ), + 'off' => __( 'Disabled', 'edit-flow' ), + 'on' => __( 'Enabled', 'edit-flow' ), ); echo ' @@ -2129,7 +2268,7 @@ public function inline_edit() {

    - + diff --git a/modules/custom-status/lib/class-cli.php b/modules/custom-status/lib/class-cli.php index 607cdde9..d0c6835f 100644 --- a/modules/custom-status/lib/class-cli.php +++ b/modules/custom-status/lib/class-cli.php @@ -11,7 +11,9 @@ } /** - * Manage Edit Flow custom statuses. + * WP-CLI commands to manage Edit Flow custom statuses. + * + * @package EditFlow */ class EF_Custom_Status_CLI extends WP_CLI_Command { @@ -183,7 +185,7 @@ public function migrate( $args, $assoc_args ) { global $wpdb; if ( ! empty( $post_type ) ) { - // Migrate only specific post type. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Bulk update for CLI migration command. $result = $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_status = %s WHERE post_status = %s AND post_type = %s", @@ -193,7 +195,7 @@ public function migrate( $args, $assoc_args ) { ) ); } else { - // Migrate all post types. + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Bulk update for CLI migration command. $result = $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->posts} SET post_status = %s WHERE post_status = %s", @@ -297,6 +299,7 @@ private function get_custom_status_module() { private function get_post_count_for_status( $status ) { global $wpdb; + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- CLI count query, caching not needed. return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_status = %s", diff --git a/modules/custom-status/views/configure.php b/modules/custom-status/views/configure.php index ddb3bd22..f936316e 100644 --- a/modules/custom-status/views/configure.php +++ b/modules/custom-status/views/configure.php @@ -1,4 +1,9 @@ - + get_post_count_for_status( $status->slug ); + $count = $this->get_post_count_for_status( $custom_status->slug ); ?> - @@ -84,9 +89,9 @@ + + settings->helper_print_error_or_description( 'name', __( 'The name is used to identify the status. (Max: 20 characters)', 'edit-flow' ) ); ?>

    - + + settings->helper_print_error_or_description( 'description', __( 'The description is primarily for administrative use, to give you some context on what the custom status is to be used for.', 'edit-flow' ) ); ?>
    diff --git a/modules/custom-status/views/edit-status.php b/modules/custom-status/views/edit-status.php index 085553bc..ee786058 100644 --- a/modules/custom-status/views/edit-status.php +++ b/modules/custom-status/views/edit-status.php @@ -1,4 +1,9 @@ module_url = $this->get_module_url( __FILE__ ); - $args = array( - 'title' => __( 'Dashboard Widgets', 'edit-flow' ), - 'short_description' => __( 'Track your content from the WordPress dashboard.', 'edit-flow' ), + $args = array( + 'title' => __( 'Dashboard Widgets', 'edit-flow' ), + 'short_description' => __( 'Track your content from the WordPress dashboard.', 'edit-flow' ), 'extended_description' => __( 'Enable dashboard widgets to quickly get an overview of what state your content is in.', 'edit-flow' ), - 'module_url' => $this->module_url, - 'img_url' => $this->module_url . 'lib/dashboard_s128.png', - 'slug' => 'dashboard', - 'post_type_support' => 'ef_dashboard', - 'default_options' => array( - 'enabled' => 'on', + 'module_url' => $this->module_url, + 'img_url' => $this->module_url . 'lib/dashboard_s128.png', + 'slug' => 'dashboard', + 'post_type_support' => 'ef_dashboard', + 'default_options' => array( + 'enabled' => 'on', 'post_status_widget' => 'on', - 'my_posts_widget' => 'on', - 'notepad_widget' => 'on', + 'my_posts_widget' => 'on', + 'notepad_widget' => 'on', ), - 'configure_page_cb' => 'print_configure_view', - 'configure_link_text' => __( 'Widget Options', 'edit-flow' ), + 'configure_page_cb' => 'print_configure_view', + 'configure_link_text' => __( 'Widget Options', 'edit-flow' ), ); - $this->module = EditFlow()->register_module( 'dashboard', $args ); + $this->module = EditFlow()->register_module( 'dashboard', $args ); } /** - * Initialize all of the class' functionality if its enabled + * Initialize all of the class' functionality if its enabled. */ public function init() { @@ -57,70 +66,59 @@ public function init() { $this->widgets->notepad_widget->init(); } - // Add the widgets to the dashboard + // Add the widgets to the dashboard. add_action( 'wp_dashboard_setup', array( $this, 'add_dashboard_widgets' ) ); - // Register our settings + // Register our settings. add_action( 'admin_init', array( $this, 'register_settings' ) ); } /** - * Upgrade our data in case we need to + * Upgrade our data in case we need to. * * @since 0.7 + * + * @param string $previous_version Previous plugin version. */ public function upgrade( $previous_version ) { global $edit_flow; - // Upgrade path to v0.7 + // Upgrade path to v0.7. if ( version_compare( $previous_version, '0.7', '<' ) ) { - // Migrate whether dashboard widgets were enabled or not - if ( $enabled = get_option( 'edit_flow_dashboard_widgets_enabled' ) ) { - $enabled = 'on'; - } else { - $enabled = 'off'; - } + // Migrate whether dashboard widgets were enabled or not. + $enabled = get_option( 'edit_flow_dashboard_widgets_enabled' ) ? 'on' : 'off'; $edit_flow->update_module_option( $this->module->name, 'enabled', $enabled ); delete_option( 'edit_flow_dashboard_widgets_enabled' ); - // Migrate whether the post status widget was on - if ( $post_status_widget = get_option( 'edit_flow_post_status_widget_enabled' ) ) { - $post_status_widget = 'on'; - } else { - $post_status_widget = 'off'; - } + // Migrate whether the post status widget was on. + $post_status_widget = get_option( 'edit_flow_post_status_widget_enabled' ) ? 'on' : 'off'; $edit_flow->update_module_option( $this->module->name, 'post_status_widget', $post_status_widget ); delete_option( 'edit_flow_post_status_widget_enabled' ); - // Migrate whether the my posts widget was on - if ( $my_posts_widget = get_option( 'edit_flow_myposts_widget_enabled' ) ) { - $my_posts_widget = 'on'; - } else { - $my_posts_widget = 'off'; - } + // Migrate whether the my posts widget was on. + $my_posts_widget = get_option( 'edit_flow_myposts_widget_enabled' ) ? 'on' : 'off'; $edit_flow->update_module_option( $this->module->name, 'post_status_widget', $my_posts_widget ); delete_option( 'edit_flow_myposts_widget_enabled' ); - // Delete legacy option + // Delete legacy option. delete_option( 'edit_flow_quickpitch_widget_enabled' ); - // Technically we've run this code before so we don't want to auto-install new data + // Technically we've run this code before so we don't want to auto-install new data. $edit_flow->update_module_option( $this->module->name, 'loaded_once', true ); } } /** - * Add Edit Flow dashboard widgets to the WordPress admin dashboard + * Add Edit Flow dashboard widgets to the WordPress admin dashboard. */ public function add_dashboard_widgets() { - // Only show dashboard widgets for Contributor or higher + // Only show dashboard widgets for Contributor or higher. if ( ! current_user_can( 'edit_posts' ) ) { return; } wp_enqueue_style( 'edit-flow-dashboard-css', $this->module_url . 'lib/dashboard.css', false, EDIT_FLOW_VERSION, 'all' ); - // Set up Post Status widget but, first, check to see if it's enabled + // Set up Post Status widget but, first, check to see if it's enabled. if ( 'on' == $this->module->options->post_status_widget ) { - $status_widget_post_types = apply_filters( 'ef_dashboard_psw_post_types', $this->get_all_post_types() ); foreach ( $status_widget_post_types as $post_type => $label ) { @@ -128,29 +126,29 @@ public function add_dashboard_widgets() { } } - // Set up the Notepad widget if it's enabled + // Set up the Notepad widget if it's enabled. if ( 'on' == $this->module->options->notepad_widget ) { wp_add_dashboard_widget( 'notepad_widget', __( 'Notepad', 'edit-flow' ), array( $this->widgets->notepad_widget, 'notepad_widget' ) ); } - // Add the MyPosts widget, if enabled + // Add the MyPosts widget, if enabled. if ( 'on' == $this->module->options->my_posts_widget && $this->module_enabled( 'notifications' ) ) { wp_add_dashboard_widget( 'myposts_widget', __( 'Posts I\'m Following', 'edit-flow' ), array( $this, 'myposts_widget' ) ); } } /** - * Dynamically add a "Unpublished Content" post status widget for any post type + * Dynamically add a "Unpublished Content" post status widget for any post type. * - * @param $post_type_slug + * @param string $post_type_slug Post type slug. */ public function add_dashboard_status_widget( $post_type_slug ) { $post_type_labels = get_post_type_labels( get_post_type_object( $post_type_slug ) ); if ( 'post' !== $post_type_slug ) { - $widget_id = 'post_status_widget_' . $post_type_slug; - // translators: %s is the post type name + $widget_id = 'post_status_widget_' . $post_type_slug; + // translators: %s is the post type name. $widget_title = sprintf( esc_html__( 'Unpublished Content: %s', 'edit-flow' ), $post_type_labels->name ); } else { $widget_id = 'post_status_widget'; @@ -167,22 +165,26 @@ public function add_dashboard_status_widget( $post_type_slug ) { } /** - * Creates Post Status widget - * Display an at-a-glance view of post counts for all (post|custom) statuses in the system + * Creates Post Status widget. + * + * Display an at-a-glance view of post counts for all (post|custom) statuses in the system. + * + * @param mixed $unused Unused parameter. + * @param array $args Widget arguments. */ public function post_status_widget( $unused, $args ) { $labels = $args['args']['labels']; $post_type = $args['args']['post_type']; - $statuses = $this->get_post_statuses(); + $statuses = $this->get_post_statuses(); $statuses[] = (object) array( - 'name' => __( 'Scheduled', 'edit-flow' ), + 'name' => __( 'Scheduled', 'edit-flow' ), 'description' => '', - 'slug' => 'future', + 'slug' => 'future', ); - $statuses = apply_filters( 'ef_dashboard_post_status_widget_statuses', $statuses ); - // If custom statuses are enabled, we'll output a link to edit the terms just below the post counts + $statuses = apply_filters( 'ef_dashboard_post_status_widget_statuses', $statuses ); + // If custom statuses are enabled, we'll output a link to edit the terms just below the post counts. if ( $this->module_enabled( 'custom_status' ) ) { $edit_custom_status_url = add_query_arg( 'page', 'ef-custom-status-settings', get_admin_url( null, 'admin.php' ) ); } @@ -235,7 +237,7 @@ public function myposts_widget() { ID ) ); + $url = esc_url( get_edit_post_link( $post->ID ) ); $title = esc_html( $post->post_title ); ?>
  • @@ -272,7 +274,7 @@ public function register_settings() { public function settings_post_status_widget_option() { $options = array( 'off' => __( 'Disabled', 'edit-flow' ), - 'on' => __( 'Enabled', 'edit-flow' ), + 'on' => __( 'Enabled', 'edit-flow' ), ); echo 'module_enabled( 'notifications' ) ) { echo ' disabled="disabled"'; $this->module->options->my_posts_widget = 'off'; @@ -313,14 +315,14 @@ public function settings_my_posts_widget_option() { } /** - * Enable or disable the Notepad widget for the dashboard + * Enable or disable the Notepad widget for the dashboard. * * @since 0.8 */ public function settings_notepad_widget_option() { $options = array( 'off' => __( 'Disabled', 'edit-flow' ), - 'on' => __( 'Enabled', 'edit-flow' ), + 'on' => __( 'Enabled', 'edit-flow' ), ); echo ' HTML for all of the metadata types + * Generate select HTML for all of the metadata types. + * + * @param object $description The term description object. */ public function get_select_html( $description ) { $current_metadata_type = $description->type; - $metadata_types = $this->get_supported_metadata_types(); + $metadata_types = $this->get_supported_metadata_types(); ?> "; if ( current_user_can( 'manage_options' ) ) { @@ -401,20 +425,20 @@ public function display_meta_box( $post ) { $terms = $this->get_editorial_metadata_terms(); if ( ! count( $terms ) ) { - $message = __( 'No editorial metadata available.' ); + $message = __( 'No editorial metadata available.', 'edit-flow' ); if ( current_user_can( 'manage_options' ) ) { /* translators: 1: The link to add editorial metadata fields */ - $message .= sprintf( __( ' Add fields to get started.' ), $this->get_link() ); + $message .= sprintf( __( ' Add fields to get started.', 'edit-flow' ), $this->get_link() ); } else { - $message .= esc_html__( ' Encourage your site administrator to configure your editorial workflow by adding editorial metadata.' ); + $message .= esc_html__( ' Encourage your site administrator to configure your editorial workflow by adding editorial metadata.', 'edit-flow' ); } echo '

    ' . wp_kses( $message, 'a' ) . '

    '; } else { foreach ( $terms as $term ) { - $postmeta_key = $this->get_postmeta_key( $term ); + $postmeta_key = $this->get_postmeta_key( $term ); $current_metadata = esc_attr( $this->get_postmeta_value( $term, $post->ID ) ); - $type = $term->type; - $description = $term->description; + $type = $term->type; + $description = $term->description; if ( $description ) { $description_span = "$description"; } else { @@ -458,7 +482,7 @@ public function display_meta_box( $post ) { } echo ''; if ( ! empty( $current_metadata ) ) { - // translators: %s is the google maps location + /* translators: %s is the google maps location. */ $google_maps_url = sprintf( esc_html__( 'View “%s” on Google Maps', 'edit-flow' ), esc_attr( $current_metadata ) ); echo '
    "' . esc_attr( $google_maps_url ) . '"
    '; } @@ -479,8 +503,8 @@ public function display_meta_box( $post ) { echo ''; $user_dropdown_args = array( 'show_option_all' => __( '-- Select a user --', 'edit-flow' ), - 'name' => $postmeta_key, - 'selected' => $current_metadata, + 'name' => $postmeta_key, + 'selected' => $current_metadata, ); $user_dropdown_args = apply_filters( 'ef_editorial_metadata_user_dropdown_args', $user_dropdown_args ); wp_dropdown_users( $user_dropdown_args ); @@ -494,16 +518,18 @@ public function display_meta_box( $post ) { } echo ''; echo "
    "; - } // Done iterating through metadata terms + } // Done iterating through metadata terms. } echo ''; } /** - * Show date or datetime - * @param int $current_date - * @return string + * Show date or datetime. + * * @since 0.8 + * + * @param int $current_date The timestamp to display. + * @return string The formatted date string. */ private function show_date_or_datetime( $current_date ) { @@ -525,6 +551,7 @@ private function show_date_or_datetime( $current_date ) { private function save_meta_box_data( $post_id ) { // Verify nonce. $nonce_key = self::metadata_taxonomy . '_nonce'; + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce(). if ( ! isset( $_POST[ $nonce_key ] ) || ! wp_verify_nonce( $_POST[ $nonce_key ], 'ef-save-metabox' ) ) { return; } @@ -537,7 +564,8 @@ private function save_meta_box_data( $post_id ) { $terms = $this->get_editorial_metadata_terms(); foreach ( $terms as $term ) { - $key = $this->get_postmeta_key( $term ); + $key = $this->get_postmeta_key( $term ); + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized based on type below. $new_metadata = isset( $_POST[ $key ] ) ? $_POST[ $key ] : ''; $type = $term->type; @@ -545,6 +573,7 @@ private function save_meta_box_data( $post_id ) { delete_post_meta( $post_id, $key ); } else { if ( 'date' === $type ) { + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated by DateTime::createFromFormat below. $date_to_parse = isset( $_POST[ $key . '_hidden' ] ) ? $_POST[ $key . '_hidden' ] : ''; $date = DateTime::createFromFormat( 'Y-m-d H:i', $date_to_parse ); @@ -559,7 +588,7 @@ private function save_meta_box_data( $post_id ) { $new_metadata = (int) $new_metadata; } - $new_metadata = strip_tags( $new_metadata ); + $new_metadata = wp_strip_all_tags( $new_metadata ); update_post_meta( $post_id, $key, $new_metadata ); } @@ -568,10 +597,10 @@ private function save_meta_box_data( $post_id ) { } /** - * Save any values in the editorial metadata post meta box + * Save any values in the editorial metadata post meta box. * - * @param int $id Unique ID for the post being saved - * @param object $post Post object + * @param int $id Unique ID for the post being saved. + * @param object $post Post object. */ public function save_meta_box( $id, $post ) { // Skip autosave. @@ -589,24 +618,25 @@ public function save_meta_box( $id, $post ) { } /** - * Generate a unique key based on the term + * Generate a unique key based on the term. * - * @param object $term Term object - * @return string $postmeta_key Unique key + * @param object $term Term object. + * @return string $postmeta_key Unique key. */ public function get_postmeta_key( $term ) { - $key = self::metadata_postmeta_key; - $type = $term->type; - $prefix = "{$key}_{$type}"; + $key = self::metadata_postmeta_key; + $type = $term->type; + $prefix = "{$key}_{$type}"; $postmeta_key = "{$prefix}_" . ( is_object( $term ) ? $term->slug : $term ); return $postmeta_key; } /** - * Returns the value for the given metadata + * Returns the value for the given metadata. * - * @param object|string|int term The term object, slug or ID for the metadata field term - * @param int post_id The ID of the post + * @param object|string|int $term The term object, slug or ID for the metadata field term. + * @param int $post_id The ID of the post. + * @return mixed The metadata value. */ public function get_postmeta_value( $term, $post_id ) { if ( ! is_object( $term ) ) { @@ -621,15 +651,17 @@ public function get_postmeta_value( $term, $post_id ) { } /** - * Get all of the editorial metadata terms as objects and sort by position - * @todo Figure out what we should do with the filter... + * Get all of the editorial metadata terms as objects and sort by position. + * + * @todo Figure out what we should do with the filter. * - * @param array $filter_args Filter to specific arguments - * @return array $ordered_terms The terms as they should be ordered + * @param array $filter_args Filter to specific arguments. + * @return array $ordered_terms The terms as they should be ordered. */ public function get_editorial_metadata_terms( $filter_args = array() ) { - // Try to fetch from internal object cache + // Try to fetch from internal object cache. + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize -- Used for cache key generation only. $arg_hash = md5( serialize( $filter_args ) ); if ( isset( $this->editorial_metadata_terms_cache[ $arg_hash ] ) ) { return $this->editorial_metadata_terms_cache[ $arg_hash ]; @@ -642,58 +674,59 @@ public function get_editorial_metadata_terms( $filter_args = array() ) { )); $ordered_terms = array(); - $hold_to_end = array(); - // Order the terms + $hold_to_end = array(); + // Order the terms. foreach ( $terms as $key => $term ) { - // Unencode and set all of our pseudo term meta because we need the position and viewable if they exist + // Unencode and set all of our pseudo term meta because we need the position and viewable if they exist. // First do an array_merge() on the term object to make sure the keys exist, then array_merge() - // any values that may already exist + // any values that may already exist. $unencoded_description = $this->get_unencoded_description( $term->description ); - $defaults = array( + $defaults = array( 'description' => '', - 'viewable' => false, - 'position' => false, + 'viewable' => false, + 'position' => false, ); - $term = array_merge( $defaults, (array) $term ); + $term = array_merge( $defaults, (array) $term ); if ( is_array( $unencoded_description ) ) { $term = array_merge( $term, $unencoded_description ); } $term = (object) $term; - // We used to store the description field in a funny way + // We used to store the description field in a funny way. if ( isset( $term->desc ) ) { $term->description = $term->desc; unset( $term->desc ); } - // Only add the term to the ordered array if it has a set position and doesn't conflict with another key - // Otherwise, hold it for later + // Only add the term to the ordered array if it has a set position and doesn't conflict with another key. + // Otherwise, hold it for later. if ( $term->position && ! array_key_exists( $term->position, $ordered_terms ) ) { $ordered_terms[ (int) $term->position ] = $term; } else { $hold_to_end[] = $term; } } - // Sort the items numerically by key + // Sort the items numerically by key. ksort( $ordered_terms, SORT_NUMERIC ); - // Append all of the terms that didn't have an existing position + // Append all of the terms that didn't have an existing position. foreach ( $hold_to_end as $unpositioned_term ) { $ordered_terms[] = $unpositioned_term; } - // If filter arguments were passed, do our filtering + // If filter arguments were passed, do our filtering. $ordered_terms = wp_filter_object_list( $ordered_terms, $filter_args ); - // Set the internal object cache + // Set the internal object cache. $this->editorial_metadata_terms_cache[ $arg_hash ] = $ordered_terms; return $ordered_terms; } /** - * Returns a term for single metadata field + * Returns a term for single metadata field. * - * @param int|string $field The slug or ID for the metadata field term to return - * @return object $term Term's object representation + * @param string $field The field to match (id, slug, or name). + * @param int|string $value The value to match against the field. + * @return object|false $term Term's object representation or false if not found. */ public function get_editorial_metadata_term_by( $field, $value ) { @@ -706,7 +739,7 @@ public function get_editorial_metadata_term_by( $field, $value ) { } $terms = $this->get_editorial_metadata_terms(); - $term = wp_filter_object_list( $terms, array( $field => $value ) ); + $term = wp_filter_object_list( $terms, array( $field => $value ) ); if ( ! empty( $term ) ) { return array_shift( $term ); @@ -716,14 +749,15 @@ public function get_editorial_metadata_term_by( $field, $value ) { } /** - * Register editorial metadata fields as columns in the manage posts view - * Only adds columns for the currently active post types - logic controlled in $this->init() + * Register editorial metadata fields as columns in the manage posts view. + * + * Only adds columns for the currently active post types - logic controlled in $this->init(). * * @since 0.7 * @uses apply_filters( 'manage_posts_columns' ) in wp-admin/includes/class-wp-posts-list-table.php * - * @param array $posts_columns Existing post columns prepared by WP_List_Table - * @param array $posts_columns Previous post columns with the new values + * @param array $posts_columns Existing post columns prepared by WP_List_Table. + * @return array $posts_columns Previous post columns with the new values. */ public function filter_manage_posts_columns( $posts_columns ) { $screen = get_current_screen(); @@ -731,8 +765,8 @@ public function filter_manage_posts_columns( $posts_columns ) { add_filter( "manage_{$screen->id}_sortable_columns", array( $this, 'filter_manage_posts_sortable_columns' ) ); $terms = $this->get_editorial_metadata_terms( array( 'viewable' => true ) ); foreach ( $terms as $term ) { - // Prefixing slug with module slug because it isn't stored prefixed and we want to avoid collisions - $key = $this->module->slug . '-' . $term->slug; + // Prefixing slug with module slug because it isn't stored prefixed and we want to avoid collisions. + $key = $this->module->slug . '-' . $term->slug; $posts_columns[ $key ] = $term->name; } } @@ -740,57 +774,60 @@ public function filter_manage_posts_columns( $posts_columns ) { } /** - * Register any viewable date editorial metadata as a sortable column + * Register any viewable date editorial metadata as a sortable column. * * @since 0.7.4 * - * @param array $sortable_columns Any existing sortable columns (e.g. Title) - * @return array $sortable_columms Sortable columns with editorial metadata date fields added + * @param array $sortable_columns Any existing sortable columns (e.g. Title). + * @return array $sortable_columns Sortable columns with editorial metadata date fields added. */ public function filter_manage_posts_sortable_columns( $sortable_columns ) { $terms = $this->get_editorial_metadata_terms( array( 'viewable' => true, - 'type' => 'date', + 'type' => 'date', ) ); foreach ( $terms as $term ) { - // Prefixing slug with module slug because it isn't stored prefixed and we want to avoid collisions - $key = $this->module->slug . '-' . $term->slug; + // Prefixing slug with module slug because it isn't stored prefixed and we want to avoid collisions. + $key = $this->module->slug . '-' . $term->slug; $sortable_columns[ $key ] = $key; } return $sortable_columns; } /** - * If we're ordering by a sortable column, let's modify the query + * If we're ordering by a sortable column, let's modify the query. * * @since 0.7.4 + * + * @param WP_Query $query The query object. */ public function action_parse_query( $query ) { if ( is_admin() && false !== stripos( get_query_var( 'orderby' ), $this->module->slug ) ) { $term_slug = sanitize_key( str_replace( $this->module->slug . '-', '', get_query_var( 'orderby' ) ) ); - $term = $this->get_editorial_metadata_term_by( 'slug', $term_slug ); - $meta_key = $this->get_postmeta_key( $term ); + $term = $this->get_editorial_metadata_term_by( 'slug', $term_slug ); + $meta_key = $this->get_postmeta_key( $term ); set_query_var( 'meta_key', $meta_key ); set_query_var( 'orderby', 'meta_value_num' ); } } /** - * Handle the output of an editorial metadata custom column - * Logic for the post types this is called on is controlled in $this->init() + * Handle the output of an editorial metadata custom column. + * + * Logic for the post types this is called on is controlled in $this->init(). * * @since 0.7 * @uses do_action( 'manage_posts_custom_column' ) in wp-admin/includes/class-wp-posts-list-table.php * - * @param string $column_name Unique string for the column - * @param int $post_id ID for the post of the row + * @param string $column_name Unique string for the column. + * @param int $post_id ID for the post of the row. */ public function action_manage_posts_custom_column( $column_name, $post_id ) { $terms = $this->get_editorial_metadata_terms(); - // We're looking for the proper term to display its saved value + // We're looking for the proper term to display its saved value. foreach ( $terms as $term ) { $key = $this->module->slug . '-' . $term->slug; if ( $column_name != $key ) { @@ -803,19 +840,19 @@ public function action_manage_posts_custom_column( $column_name, $post_id ) { } /** - * If the Edit Flow Calendar is enabled, add viewable Editorial Metadata terms + * If the Edit Flow Calendar is enabled, add viewable Editorial Metadata terms. * * @since 0.7 * @uses apply_filters( 'ef_calendar_item_information_fields' ) * - * @param array $calendar_fields Additional data fields to include on the calendar - * @param int $post_id Unique ID for the post data we're building - * @return array $calendar_fields Calendar fields with our viewable Editorial Metadata added + * @param array $calendar_fields Additional data fields to include on the calendar. + * @param int $post_id Unique ID for the post data we're building. + * @return array $calendar_fields Calendar fields with our viewable Editorial Metadata added. */ public function filter_calendar_item_fields( $calendar_fields, $post_id ) { - // Make sure we respect which post type we're on + // Make sure we respect which post type we're on. if ( ! in_array( get_post_type( $post_id ), $this->get_post_types_for_module( $this->module ) ) ) { return $calendar_fields; } @@ -825,80 +862,82 @@ public function filter_calendar_item_fields( $calendar_fields, $post_id ) { foreach ( $terms as $term ) { $key = $this->module->slug . '-' . $term->slug; - // Default values - $current_metadata = $this->get_postmeta_value( $term, $post_id ); - $term_data = array( + // Default values. + $current_metadata = $this->get_postmeta_value( $term, $post_id ); + $term_data = array( 'label' => $term->name, 'value' => $this->generate_editorial_metadata_term_output( $term, $current_metadata ), ); - $term_data['editable'] = true; - $term_data['type'] = $term->type; + $term_data['editable'] = true; + $term_data['type'] = $term->type; $calendar_fields[ $key ] = $term_data; } return $calendar_fields; } /** - * If the Edit Flow Story Budget is enabled, register our viewable terms as columns + * If the Edit Flow Story Budget is enabled, register our viewable terms as columns. * * @since 0.7 * @uses apply_filters( 'ef_story_budget_term_columns' ) * - * @param array $term_columns The existing columns on the story budget - * @return array $term_columns Term columns with viewable Editorial Metadata terms + * @param array $term_columns The existing columns on the story budget. + * @return array $term_columns Term columns with viewable Editorial Metadata terms. */ public function filter_story_budget_term_columns( $term_columns ) { $terms = $this->get_editorial_metadata_terms( array( 'viewable' => true ) ); foreach ( $terms as $term ) { - // Prefixing slug with module slug because it isn't stored prefixed and we want to avoid collisions + // Prefixing slug with module slug because it isn't stored prefixed and we want to avoid collisions. $key = $this->module->slug . '-' . $term->slug; - // Switch to underscores - $key = str_replace( '-', '_', $key ); + // Switch to underscores. + $key = str_replace( '-', '_', $key ); $term_columns[ $key ] = $term->name; } return $term_columns; } /** - * If the Edit Flow Story Budget is enabled, + * If the Edit Flow Story Budget is enabled, generate metadata column values. * * @since 0.7 * @uses apply_filters( 'ef_story_budget_term_column_value' ) * - * @param object $post The post we're displaying - * @param string $column_name Name of the column, as registered with EF_Story_Budget::register_term_columns - * @param object $parent_term The parent term for the term column + * @param string $column_name Name of the column, as registered with EF_Story_Budget::register_term_columns. + * @param object $post The post we're displaying. + * @param object $parent_term The parent term for the term column. + * @return string The column value or the original column name. */ public function filter_story_budget_term_column_values( $column_name, $post, $parent_term ) { $local_column_name = str_replace( '_', '-', $column_name ); - // Don't accidentally handle values not our own + // Don't accidentally handle values not our own. if ( false === strpos( $local_column_name, $this->module->slug ) ) { return $column_name; } $term_slug = str_replace( $this->module->slug . '-', '', $local_column_name ); - $term = $this->get_editorial_metadata_term_by( 'slug', $term_slug ); + $term = $this->get_editorial_metadata_term_by( 'slug', $term_slug ); - // Don't allow non-viewable term data to be displayed + // Don't allow non-viewable term data to be displayed. if ( ! $term->viewable ) { return $column_name; } $current_metadata = $this->get_postmeta_value( $term, $post->ID ); - $output = $this->generate_editorial_metadata_term_output( $term, $current_metadata ); + $output = $this->generate_editorial_metadata_term_output( $term, $current_metadata ); return $output; } /** - * Generate the presentational output for an editorial metadata term + * Generate the presentational output for an editorial metadata term. * * @since 0.8 * - * @param object $term The editorial metadata term - * @return string $html How the term should be rendered + * @param object $term The editorial metadata term. + * @param mixed $pm_value The post meta value. + * @return string $html How the term should be rendered. */ private function generate_editorial_metadata_term_output( $term, $pm_value ) { @@ -909,13 +948,13 @@ private function generate_editorial_metadata_term_output( $term, $pm_value ) { break; } - // All day vs. day and time + // All day vs. day and time. $date = date( get_option( 'date_format' ), $pm_value ); $time = date( get_option( 'time_format' ), $pm_value ); if ( '0000' == date( 'Hi', $pm_value ) ) { $pm_value = $date; } else { - // translators: 1: date, 2: time + // translators: 1: date, 2: time. $pm_value = sprintf( __( '%1$s at %2$s', 'edit-flow' ), $date, $time ); } $output = esc_html( $pm_value ); @@ -951,13 +990,13 @@ private function generate_editorial_metadata_term_output( $term, $pm_value ) { } /** - * Update an existing editorial metadata term if the term_id exists + * Update an existing editorial metadata term if the term_id exists. * * @since 0.7 * - * @param int $term_id The term's unique ID - * @param array $args Any values that need to be updated for the term - * @return object|WP_Error $updated_term The updated term or a WP_Error object if something disastrous happened + * @param int $term_id The term's unique ID. + * @param array $args Any values that need to be updated for the term. + * @return object|WP_Error $updated_term The updated term or a WP_Error object if something disastrous happened. */ public function update_editorial_metadata_term( $term_id, $args ) { @@ -965,29 +1004,29 @@ public function update_editorial_metadata_term( $term_id, $args ) { $old_term = $this->get_editorial_metadata_term_by( 'id', $term_id ); if ( $old_term ) { $old_args = array( - 'position' => $old_term->position, - 'name' => $old_term->name, - 'slug' => $old_term->slug, + 'position' => $old_term->position, + 'name' => $old_term->name, + 'slug' => $old_term->slug, 'description' => $old_term->description, - 'type' => $old_term->type, - 'viewable' => $old_term->viewable, + 'type' => $old_term->type, + 'viewable' => $old_term->viewable, ); } $new_args = array_merge( $old_args, $args ); - // We're encoding metadata that isn't supported by default in the term's description field - $args_to_encode = array( + // We're encoding metadata that isn't supported by default in the term's description field. + $args_to_encode = array( 'description' => $new_args['description'], - 'position' => $new_args['position'], - 'type' => $new_args['type'], - 'viewable' => $new_args['viewable'], + 'position' => $new_args['position'], + 'type' => $new_args['type'], + 'viewable' => $new_args['viewable'], ); - $encoded_description = $this->get_encoded_description( $args_to_encode ); + $encoded_description = $this->get_encoded_description( $args_to_encode ); $new_args['description'] = $encoded_description; $updated_term = wp_update_term( $term_id, self::metadata_taxonomy, $new_args ); - // Reset the internal object cache + // Reset the internal object cache. $this->editorial_metadata_terms_cache = array(); $updated_term = $this->get_editorial_metadata_term_by( 'id', $term_id ); @@ -995,74 +1034,78 @@ public function update_editorial_metadata_term( $term_id, $args ) { } /** - * Insert a new editorial metadata term - * @todo Handle conflicts with existing terms at that position (if relevant) + * Insert a new editorial metadata term. + * + * @todo Handle conflicts with existing terms at that position (if relevant). * * @since 0.7 + * + * @param array $args Arguments for creating the term. + * @return object|WP_Error The new term or a WP_Error object. */ public function insert_editorial_metadata_term( $args ) { - // Term is always added to the end of the list + // Term is always added to the end of the list. $default_position = count( $this->get_editorial_metadata_terms() ) + 2; - $defaults = array( - 'position' => $default_position, - 'name' => '', - 'slug' => '', + $defaults = array( + 'position' => $default_position, + 'name' => '', + 'slug' => '', 'description' => '', - 'type' => '', - 'viewable' => false, + 'type' => '', + 'viewable' => false, ); - $args = array_merge( $defaults, $args ); - $term_name = $args['name']; + $args = array_merge( $defaults, $args ); + $term_name = $args['name']; unset( $args['name'] ); - // We're encoding metadata that isn't supported by default in the term's description field - $args_to_encode = array( + // We're encoding metadata that isn't supported by default in the term's description field. + $args_to_encode = array( 'description' => $args['description'], - 'position' => $args['position'], - 'type' => $args['type'], - 'viewable' => $args['viewable'], + 'position' => $args['position'], + 'type' => $args['type'], + 'viewable' => $args['viewable'], ); $encoded_description = $this->get_encoded_description( $args_to_encode ); $args['description'] = $encoded_description; $inserted_term = wp_insert_term( $term_name, self::metadata_taxonomy, $args ); - // Reset the internal object cache + // Reset the internal object cache. $this->editorial_metadata_terms_cache = array(); return $inserted_term; } /** - * Settings and other management code + * Settings and other management code. */ /** - * Delete an existing editorial metadata term + * Delete an existing editorial metadata term. * * @since 0.7 * - * @param int $term_id The term we want deleted - * @return bool $result Whether or not the term was deleted + * @param int $term_id The term we want deleted. + * @return bool $result Whether or not the term was deleted. */ public function delete_editorial_metadata_term( $term_id ) { $result = wp_delete_term( $term_id, self::metadata_taxonomy ); - // Reset the internal object cache + // Reset the internal object cache. $this->editorial_metadata_terms_cache = array(); return $result; } /** - * Generate a link to one of the editorial metadata actions + * Generate a link to one of the editorial metadata actions. * * @since 0.7 * - * @param array $args (optional) Action and any query args to add to the URL - * @return string $link Direct link to complete the action + * @param array $args (optional) Action and any query args to add to the URL. + * @return string $link Direct link to complete the action. */ public function get_link( $args = array() ) { if ( ! isset( $args['action'] ) ) { @@ -1071,7 +1114,7 @@ public function get_link( $args = array() ) { if ( ! isset( $args['page'] ) ) { $args['page'] = $this->module->settings_slug; } - // Add other things we may need depending on the action + // Add other things we may need depending on the action. switch ( $args['action'] ) { case 'make-viewable': case 'make-hidden': @@ -1085,15 +1128,17 @@ public function get_link( $args = array() ) { } /** - * Handles a request to add a new piece of editorial metadata + * Handles a request to add a new piece of editorial metadata. */ public function handle_add_editorial_metadata() { - + // phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended -- Nonce verified below. if ( ! isset( $_POST['submit'], $_POST['form-action'], $_GET['page'] ) || $_GET['page'] != $this->module->settings_slug || 'add-term' != $_POST['form-action'] ) { return; } + // phpcs:enable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce(). if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'editorial-metadata-add-nonce' ) ) { wp_die( esc_html( $this->module->messages['nonce-failed'] ) ); } @@ -1102,70 +1147,73 @@ public function handle_add_editorial_metadata() { wp_die( esc_html( $this->module->messages['invalid-permissions'] ) ); } - // Sanitize all of the user-entered values + // Sanitize all of the user-entered values. + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized by sanitize_text_field(). $term_name = isset( $_POST['metadata_name'] ) ? sanitize_text_field( trim( $_POST['metadata_name'] ) ) : ''; + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized by sanitize_title(). $term_slug = ( ! empty( $_POST['metadata_slug'] ) ) ? sanitize_title( $_POST['metadata_slug'] ) : sanitize_title( $term_name ); + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized by wp_filter_nohtml_kses(). $term_description = isset( $_POST['metadata_description'] ) ? stripslashes( wp_filter_nohtml_kses( trim( $_POST['metadata_description'] ) ) ) : ''; - $term_type = isset( $_POST['metadata_type'] ) ? sanitize_key( $_POST['metadata_type'] ) : ''; + $term_type = isset( $_POST['metadata_type'] ) ? sanitize_key( $_POST['metadata_type'] ) : ''; $_REQUEST['form-errors'] = array(); /** - * Form validation for adding new editorial metadata term + * Form validation for adding new editorial metadata term. * - * Details + * Details: * - "name", "slug", and "type" are required fields * - "description" can accept a limited amount of HTML, and is optional */ - // Field is required + // Field is required. if ( empty( $term_name ) ) { $_REQUEST['form-errors']['name'] = __( 'Please enter a name for the editorial metadata.', 'edit-flow' ); } - // Field is required + // Field is required. if ( empty( $term_slug ) ) { $_REQUEST['form-errors']['slug'] = __( 'Please enter a slug for the editorial metadata.', 'edit-flow' ); } if ( term_exists( $term_slug ) ) { $_REQUEST['form-errors']['name'] = __( 'Name conflicts with existing term. Please choose another.', 'edit-flow' ); } - // Check to ensure a term with the same name doesn't exist + // Check to ensure a term with the same name doesn't exist. if ( $this->get_editorial_metadata_term_by( 'name', $term_name, self::metadata_taxonomy ) ) { $_REQUEST['form-errors']['name'] = __( 'Name already in use. Please choose another.', 'edit-flow' ); } - // Check to ensure a term with the same slug doesn't exist + // Check to ensure a term with the same slug doesn't exist. if ( $this->get_editorial_metadata_term_by( 'slug', $term_slug ) ) { $_REQUEST['form-errors']['slug'] = __( 'Slug already in use. Please choose another.', 'edit-flow' ); } - // Check to make sure the status doesn't already exist as another term because otherwise we'd get a weird slug - // Check that the term name doesn't exceed 200 chars + // Check to make sure the status doesn't already exist as another term because otherwise we'd get a weird slug. + // Check that the term name doesn't exceed 200 chars. if ( strlen( $term_name ) > 200 ) { $_REQUEST['form-errors']['name'] = __( 'Name cannot exceed 200 characters. Please try a shorter name.', 'edit-flow' ); } - // Metadata type needs to pass our whitelist check + // Metadata type needs to pass our whitelist check. $metadata_types = $this->get_supported_metadata_types(); if ( empty( $_POST['metadata_type'] ) || ! isset( $metadata_types[ $_POST['metadata_type'] ] ) ) { $_REQUEST['form-errors']['type'] = __( 'Please select a valid metadata type.', 'edit-flow' ); } - // Metadata viewable needs to be a valid Yes or No + // Metadata viewable needs to be a valid Yes or No. $term_viewable = false; if ( isset( $_POST['metadata_viewable'] ) && 'yes' == $_POST['metadata_viewable'] ) { $term_viewable = true; } - // Kick out if there are any errors + // Kick out if there are any errors. // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated if ( count( $_REQUEST['form-errors'] ) ) { $_REQUEST['error'] = 'form-error'; return; } - // Try to add the status - $args = array( - 'name' => $term_name, + // Try to add the status. + $args = array( + 'name' => $term_name, 'description' => $term_description, - 'slug' => $term_slug, - 'type' => $term_type, - 'viewable' => $term_viewable, + 'slug' => $term_slug, + 'type' => $term_type, + 'viewable' => $term_viewable, ); $return = $this->insert_editorial_metadata_term( $args ); if ( is_wp_error( $return ) ) { @@ -1173,11 +1221,12 @@ public function handle_add_editorial_metadata() { } $redirect_url = add_query_arg( array( - 'page' => $this->module->settings_slug, + 'page' => $this->module->settings_slug, 'message' => 'term-added', ), get_admin_url( null, 'admin.php' ) ); + // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Redirect URL is constructed internally. wp_redirect( $redirect_url ); - wp_die(); + exit; } /** @@ -1189,6 +1238,7 @@ public function handle_edit_editorial_metadata() { return; } + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce(). if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'editorial-metadata-edit-nonce' ) ) { wp_die( esc_html( $this->module->messages['nonce-failed'] ) ); } @@ -1197,12 +1247,15 @@ public function handle_edit_editorial_metadata() { wp_die( esc_html( $this->module->messages['invalid-permissions'] ) ); } - if ( ! $existing_term = $this->get_editorial_metadata_term_by( 'id', (int) $_GET['term-id'] ) ) { + $existing_term = $this->get_editorial_metadata_term_by( 'id', (int) $_GET['term-id'] ); + if ( ! $existing_term ) { wp_die( esc_html( $this->module->messages['term-missing'] ) ); } + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized by sanitize_text_field(). $new_name = isset( $_POST['name'] ) ? sanitize_text_field( trim( $_POST['name'] ) ) : ''; - $denew_descriptionscription = isset( $_POST['description'] ) ? stripslashes( wp_filter_nohtml_kses( trim( $_POST['description'] ) ) ) : ''; + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized by wp_filter_nohtml_kses(). + $new_description = isset( $_POST['description'] ) ? stripslashes( wp_filter_nohtml_kses( trim( $_POST['description'] ) ) ) : ''; /** * Form validation for editing editorial metadata term @@ -1212,12 +1265,12 @@ public function handle_edit_editorial_metadata() { * - "description" can accept a limited amount of HTML, and is optional */ $_REQUEST['form-errors'] = array(); - // Check if name field was filled in + // Check if name field was filled in. if ( empty( $new_name ) ) { $_REQUEST['form-errors']['name'] = __( 'Please enter a name for the editorial metadata', 'edit-flow' ); } - // Check that the name isn't numeric + // Check that the name isn't numeric. if ( is_numeric( $new_name ) ) { $_REQUEST['form-errors']['name'] = __( 'Please enter a valid, non-numeric name for the editorial metadata.', 'edit-flow' ); } @@ -1227,39 +1280,39 @@ public function handle_edit_editorial_metadata() { $_REQUEST['form-errors']['name'] = __( 'Metadata name conflicts with existing term. Please choose another.', 'edit-flow' ); } - // Check to ensure a term with the same name doesn't exist, + // Check to ensure a term with the same name doesn't exist. $search_term = $this->get_editorial_metadata_term_by( 'name', $new_name ); if ( is_object( $search_term ) && $search_term->term_id != $existing_term->term_id ) { $_REQUEST['form-errors']['name'] = __( 'Name already in use. Please choose another.', 'edit-flow' ); } - // or that the term name doesn't map to an existing term's slug + // Or that the term name doesn't map to an existing term's slug. $search_term = $this->get_editorial_metadata_term_by( 'slug', sanitize_title( $new_name ) ); if ( is_object( $search_term ) && $search_term->term_id != $existing_term->term_id ) { $_REQUEST['form-errors']['name'] = __( 'Name conflicts with slug for another term. Please choose something else.', 'edit-flow' ); } - // Check that the term name doesn't exceed 200 chars + // Check that the term name doesn't exceed 200 chars. if ( strlen( $new_name ) > 200 ) { $_REQUEST['form-errors']['name'] = __( 'Name cannot exceed 200 characters. Please try a shorter name.', 'edit-flow' ); } - // Make sure the viewable state is valid + // Make sure the viewable state is valid. $new_viewable = false; if ( isset( $_POST['viewable'] ) && 'yes' == $_POST['viewable'] ) { $new_viewable = true; } - // Kick out if there are any errors + // Kick out if there are any errors. // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated if ( count( $_REQUEST['form-errors'] ) ) { $_REQUEST['error'] = 'form-error'; return; } - // Try to add the metadata term - $args = array( - 'name' => $new_name, + // Try to add the metadata term. + $args = array( + 'name' => $new_name, 'description' => $new_description, - 'viewable' => $new_viewable, + 'viewable' => $new_viewable, ); $return = $this->update_editorial_metadata_term( $existing_term->term_id, $args ); if ( is_wp_error( $return ) ) { @@ -1267,38 +1320,41 @@ public function handle_edit_editorial_metadata() { } $redirect_url = add_query_arg( array( - 'page' => $this->module->settings_slug, + 'page' => $this->module->settings_slug, 'message' => 'term-updated', ), get_admin_url( null, 'admin.php' ) ); + // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Redirect URL is constructed internally. wp_redirect( $redirect_url ); - wp_die(); + exit; } /** - * Handle a $_GET request to change the visibility of an Editorial Metadata term + * Handle a $_GET request to change the visibility of an Editorial Metadata term. * * @since 0.7 */ public function handle_change_editorial_metadata_visibility() { - - // Check that the current GET request is our GET request + // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Nonce verified below. + // Check that the current GET request is our GET request. if ( ! isset( $_GET['page'], $_GET['action'], $_GET['term-id'], $_GET['nonce'] ) || $_GET['page'] != $this->module->settings_slug || ! in_array( $_GET['action'], array( 'make-viewable', 'make-hidden' ) ) ) { return; } + // phpcs:enable WordPress.Security.NonceVerification.Recommended - // Check for proper nonce + // Check for proper nonce. + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce(). if ( ! isset( $_GET['nonce'] ) || ( ! wp_verify_nonce( $_GET['nonce'], 'make-viewable' ) && ! wp_verify_nonce( $_GET['nonce'], 'make-hidden' ) ) ) { wp_die( esc_html( $this->module->messages['nonce-failed'] ) ); } - // Only allow users with the proper caps + // Only allow users with the proper caps. if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html( $this->module->messages['invalid-permissions'] ) ); } $term_id = (int) $_GET['term-id']; - $args = array(); + $args = array(); if ( 'make-viewable' == $_GET['action'] ) { $args['viewable'] = true; } elseif ( 'make-hidden' == $_GET['action'] ) { @@ -1311,17 +1367,18 @@ public function handle_change_editorial_metadata_visibility() { } $redirect_url = $this->get_link( array( 'message' => 'term-visibility-changed' ) ); + // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Redirect URL is constructed internally. wp_redirect( $redirect_url ); - wp_die(); + exit; } /** - * Handle the request to update a given Editorial Metadata term via inline edit + * Handle the request to update a given Editorial Metadata term via inline edit. * * @since 0.7 */ public function handle_ajax_inline_save_term() { - + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce(). if ( ! isset( $_POST['inline_edit'] ) || ! wp_verify_nonce( $_POST['inline_edit'], 'editorial-metadata-inline-edit-nonce' ) ) { wp_die( esc_html( $this->module->messages['nonce-failed'] ) ); } @@ -1330,59 +1387,62 @@ public function handle_ajax_inline_save_term() { wp_die( esc_html( $this->module->messages['invalid-permissions'] ) ); } - $term_id = isset( $_POST['term_id'] ) ? (int) $_POST['term_id'] : 0; - if ( ! $existing_term = $this->get_editorial_metadata_term_by( 'id', $term_id ) ) { + $term_id = isset( $_POST['term_id'] ) ? (int) $_POST['term_id'] : 0; + $existing_term = $this->get_editorial_metadata_term_by( 'id', $term_id ); + if ( ! $existing_term ) { wp_die( esc_html( $this->module->messages['term-missing'] ) ); } + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized by sanitize_text_field(). $metadata_name = isset( $_POST['name'] ) ? sanitize_text_field( trim( $_POST['name'] ) ) : ''; + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized by wp_filter_nohtml_kses(). $metadata_description = isset( $_POST['description'] ) ? stripslashes( wp_filter_nohtml_kses( trim( $_POST['description'] ) ) ) : ''; /** - * Form validation for editing editorial metadata term + * Form validation for editing editorial metadata term. */ - // Check if name field was filled in + // Check if name field was filled in. if ( empty( $metadata_name ) ) { - $change_error = new WP_Error( 'invalid', _esc_html__( 'Please enter a name for the editorial metadata', 'edit-flow' ) ); + $change_error = new WP_Error( 'invalid', esc_html__( 'Please enter a name for the editorial metadata', 'edit-flow' ) ); wp_die( esc_html( $change_error->get_error_message() ) ); } - // Check that the name isn't numeric + // Check that the name isn't numeric. if ( is_numeric( $metadata_name ) ) { $change_error = new WP_Error( 'invalid', esc_html__( 'Please enter a valid, non-numeric name for the editorial metadata.', 'edit-flow' ) ); wp_die( esc_html( $change_error->get_error_message() ) ); } - // Check that the term name doesn't exceed 200 chars + // Check that the term name doesn't exceed 200 chars. if ( strlen( $metadata_name ) > 200 ) { - $change_error = new WP_Error( 'invalid', esc_html__( 'Name cannot exceed 200 characters. Please try a shorter name.' ) ); + $change_error = new WP_Error( 'invalid', esc_html__( 'Name cannot exceed 200 characters. Please try a shorter name.', 'edit-flow' ) ); wp_die( esc_html( $change_error->get_error_message() ) ); } - // Check to make sure the status doesn't already exist as another term because otherwise we'd get a fatal error + // Check to make sure the status doesn't already exist as another term because otherwise we'd get a fatal error. $term_exists = term_exists( sanitize_title( $metadata_name ) ); if ( $term_exists && $term_exists != $term_id ) { - $change_error = new WP_Error( 'invalid', esc_html____( 'Metadata name conflicts with existing term. Please choose another.', 'edit-flow' ) ); + $change_error = new WP_Error( 'invalid', esc_html__( 'Metadata name conflicts with existing term. Please choose another.', 'edit-flow' ) ); wp_die( esc_html( $change_error->get_error_message() ) ); } - // Check to ensure a term with the same name doesn't exist, + // Check to ensure a term with the same name doesn't exist. $search_term = $this->get_editorial_metadata_term_by( 'name', $metadata_name ); if ( is_object( $search_term ) && $search_term->term_id != $existing_term->term_id ) { $change_error = new WP_Error( 'invalid', esc_html__( 'Name already in use. Please choose another.', 'edit-flow' ) ); wp_die( esc_html( $change_error->get_error_message() ) ); } - // or that the term name doesn't map to an existing term's slug + // Or that the term name doesn't map to an existing term's slug. $search_term = $this->get_editorial_metadata_term_by( 'slug', sanitize_title( $metadata_name ) ); if ( is_object( $search_term ) && $search_term->term_id != $existing_term->term_id ) { $change_error = new WP_Error( 'invalid', esc_html__( 'Name conflicts with slug for another term. Please choose again.', 'edit-flow' ) ); wp_die( esc_html( $change_error->get_error_message() ) ); } - // Prepare the term name and description for saving - $args = array( - 'name' => $metadata_name, + // Prepare the term name and description for saving. + $args = array( + 'name' => $metadata_name, 'description' => $metadata_description, ); $return = $this->update_editorial_metadata_term( $existing_term->term_id, $args ); @@ -1400,12 +1460,12 @@ public function handle_ajax_inline_save_term() { } /** - * Handle the ajax request to update all of the term positions + * Handle the ajax request to update all of the term positions. * * @since 0.7 */ public function handle_ajax_update_term_positions() { - + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce(). if ( ! isset( $_POST['editorial_metadata_sortable_nonce'] ) || ! wp_verify_nonce( $_POST['editorial_metadata_sortable_nonce'], 'editorial-metadata-sortable' ) ) { $this->print_ajax_response( 'error', $this->module->messages['nonce-failed'] ); } @@ -1418,27 +1478,31 @@ public function handle_ajax_update_term_positions() { $this->print_ajax_response( 'error', __( 'Terms not set.', 'edit-flow' ) ); } + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Values are cast to int below. foreach ( $_POST['term_positions'] as $position => $term_id ) { - // Have to add 1 to the position because the index started with zero - $args = array( + // Have to add 1 to the position because the index started with zero. + $args = array( 'position' => (int) $position + 1, ); $return = $this->update_editorial_metadata_term( (int) $term_id, $args ); - // @todo check that this was a valid return + // @todo Check that this was a valid return. } $this->print_ajax_response( 'success', $this->module->messages['term-position-updated'] ); } /** - * Handles a request to delete an editorial metadata term + * Handles a request to delete an editorial metadata term. */ public function handle_delete_editorial_metadata() { + // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Nonce verified below. if ( ! isset( $_GET['page'], $_GET['action'], $_GET['term-id'] ) || $_GET['page'] != $this->module->settings_slug || 'delete-term' != $_GET['action'] ) { return; } + // phpcs:enable WordPress.Security.NonceVerification.Recommended + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce value passed directly to wp_verify_nonce(). if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( $_GET['nonce'], 'delete-term' ) ) { wp_die( esc_html( $this->module->messages['nonce-failed'] ) ); } @@ -1447,7 +1511,8 @@ public function handle_delete_editorial_metadata() { wp_die( esc_html( $this->module->messages['invalid-permissions'] ) ); } - if ( ! $existing_term = $this->get_editorial_metadata_term_by( 'id', (int) $_GET['term-id'] ) ) { + $existing_term = $this->get_editorial_metadata_term_by( 'id', (int) $_GET['term-id'] ); + if ( ! $existing_term ) { wp_die( esc_html( $this->module->messages['term-missing'] ) ); } @@ -1457,11 +1522,12 @@ public function handle_delete_editorial_metadata() { } $redirect_url = add_query_arg( array( - 'page' => $this->module->settings_slug, + 'page' => $this->module->settings_slug, 'message' => 'term-deleted', ), get_admin_url( null, 'admin.php' ) ); + // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Redirect URL is constructed internally. wp_redirect( $redirect_url ); - wp_die(); + exit; } /** @@ -1487,16 +1553,16 @@ public function settings_post_types_option() { } /** - * Validate data entered by the user + * Validate data entered by the user. * * @since 0.7 * - * @param array $new_options New values that have been entered by the user - * @return array $new_options Form values after they've been sanitized + * @param array $new_options New values that have been entered by the user. + * @return array $new_options Form values after they've been sanitized. */ public function settings_validate( $new_options ) { - // Whitelist validation for the post type options + // Whitelist validation for the post type options. if ( ! isset( $new_options['post_types'] ) ) { $new_options['post_types'] = array(); } @@ -1507,17 +1573,17 @@ public function settings_validate( $new_options ) { /** * Prepare and display the configuration view for editorial metadata. + * * There are four primary components: * - Form to add a new Editorial Metadata term * - Form generated by the settings API for managing Editorial Metadata options * - Table of existing Editorial Metadata terms with ability to take actions on each * - Full page width view for editing a single Editorial Metadata term * - * 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 * @since 0.7 */ public function print_configure_view() { + // phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- This is a view function only, data is escaped on output. global $edit_flow; $wp_list_table = new EF_Editorial_Metadata_List_Table(); $wp_list_table->prepare_items(); @@ -1538,23 +1604,24 @@ public function print_configure_view() { - + get_editorial_metadata_term_by( 'id', $term_id ); + $term = $this->get_editorial_metadata_term_by( 'id', $term_id ); if ( ! $term ) { echo '

    ' . esc_html( $this->module->messages['term-missing'] ) . '

    '; return; } $metadata_types = $this->get_supported_metadata_types(); - $type = $term->type; + $type = $term->type; $edit_term_link = $this->get_link( array( - 'action' => 'edit-term', + 'action' => 'edit-term', 'term-id' => $term->term_id, ) ); - $name = ( isset( $_POST['name'] ) ) ? stripslashes( $_POST['name'] ) : $term->name; + // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Values are used for form re-population, escaped on output. + $name = ( isset( $_POST['name'] ) ) ? stripslashes( $_POST['name'] ) : $term->name; $description = ( isset( $_POST['description'] ) ) ? stripslashes( $_POST['description'] ) : $term->description; if ( $term->viewable ) { $viewable = 'yes'; @@ -1562,6 +1629,7 @@ public function print_configure_view() { $viewable = 'no'; } $viewable = ( isset( $_POST['viewable'] ) ) ? stripslashes( $_POST['viewable'] ) : $viewable; + // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized ?>
    @@ -1574,7 +1642,7 @@ public function print_configure_view() { ?> - + @@ -1604,7 +1672,7 @@ public function print_configure_view() { ' ); echo wp_kses_post( $this->single_row_columns( $term ) ); @@ -1818,12 +1904,12 @@ public function single_row( $term, $level = 0 ) { } /** - * Handle the column output when there's no method for it + * Handle the column output when there's no method for it. * * @since 0.7 * - * @param object $item Editorial Metadata term as an object - * @param string $column_name How the column was registered at birth + * @param object $item Editorial Metadata term as an object. + * @param string $column_name How the column was registered at birth. */ public function column_default( $item, $column_name ) { @@ -1832,7 +1918,6 @@ public function column_default( $item, $column_name ) { case 'type': case 'description': return esc_html( $item->$column_name ); - break; case 'viewable': if ( $item->viewable ) { return __( 'Yes', 'edit-flow' ); @@ -1846,36 +1931,36 @@ public function column_default( $item, $column_name ) { } /** - * Column for displaying the term's name and associated actions + * Column for displaying the term's name and associated actions. * * @since 0.7 * - * @param object $item Editorial Metadata term as an object + * @param object $item Editorial Metadata term as an object. */ public function column_name( $item ) { global $edit_flow; - $item_edit_link = esc_url( $edit_flow->editorial_metadata->get_link( array( - 'action' => 'edit-term', + $item_edit_link = esc_url( $edit_flow->editorial_metadata->get_link( array( + 'action' => 'edit-term', 'term-id' => $item->term_id, ) ) ); $item_delete_link = esc_url( $edit_flow->editorial_metadata->get_link( array( - 'action' => 'delete-term', + 'action' => 'delete-term', 'term-id' => $item->term_id, ) ) ); $out = '' . esc_html( $item->name ) . ''; - $actions = array(); - $actions['edit'] = "" . __( 'Edit', 'edit-flow' ) . ''; - $actions['inline hide-if-no-js'] = '' . __( 'Quick Edit' ) . ''; + $actions = array(); + $actions['edit'] = "" . __( 'Edit', 'edit-flow' ) . ''; + $actions['inline hide-if-no-js'] = '' . __( 'Quick Edit', 'edit-flow' ) . ''; if ( $item->viewable ) { $actions['change-visibility make-hidden'] = '' . __( 'Make Hidden', 'edit-flow' ) . ''; } else { $actions['change-visibility make-viewable'] = '' . __( 'Make Viewable', 'edit-flow' ) . ''; } @@ -1891,7 +1976,7 @@ public function column_name( $item ) { } /** - * Admins can use the inline edit capability to quickly make changes to the title or description + * Admins can use the inline edit capability to quickly make changes to the title or description. * * @since 0.7 */ @@ -1901,7 +1986,7 @@ public function inline_edit() {
    settings->helper_print_error_or_description( 'name', __( 'The name is for labeling the metadata field.', 'edit-flow' ) ); ?>
    __( 'No', 'edit-flow' ), + 'no' => __( 'No', 'edit-flow' ), 'yes' => __( 'Yes', 'edit-flow' ), ); ?> @@ -1636,7 +1704,7 @@ public function print_configure_view() {
    - + settings->helper_print_error_or_description( 'name', __( 'The name is for labeling the metadata field.', 'edit-flow' ) ); ?>
    - + settings->helper_print_error_or_description( 'slug', __( 'The "slug" is the URL-friendly version of the name. It is usually all lowercase and contains only letters, numbers, and hyphens.', 'edit-flow' ) ); ?>
    - + settings->helper_print_error_or_description( 'description', __( 'The description can be used to communicate with your team about what the metadata is for.', 'edit-flow' ) ); ?>
    get_supported_metadata_types(); - // Select the previously selected metadata type if a valid one exists + // Select the previously selected metadata type if a valid one exists. $current_metadata_type = ( isset( $_POST['metadata_type'] ) && in_array( $_POST['metadata_type'], array_keys( $metadata_types ) ) ) ? $_POST['metadata_type'] : false; ?> +
    @@ -1716,9 +1786,8 @@ public function print_configure_view() { tax = get_taxonomy( $this->taxonomy ); - $columns = $this->get_columns(); - $hidden = array( + $columns = $this->get_columns(); + $hidden = array( 'position', ); $sortable = array(); @@ -1753,7 +1839,7 @@ public function __construct() { $this->_column_headers = array( $columns, $hidden, $sortable ); parent::__construct( array( - 'plural' => 'editorial metadata', + 'plural' => 'editorial metadata', 'singular' => 'editorial metadata', ) ); } @@ -1769,7 +1855,7 @@ public function prepare_items() { $this->set_pagination_args( array( 'total_items' => count( $this->items ), - 'per_page' => count( $this->items ), + 'per_page' => count( $this->items ), ) ); } @@ -1800,17 +1886,17 @@ public function get_columns() { } /** - * Prepare a single row of Editorial Metadata + * Prepare a single row of Editorial Metadata. * * @since 0.7 * - * @param object $term The current term we're displaying - * @param int $level Level is always zero because it isn't a parent-child tax + * @param object $term The current term we're displaying. + * @param int $level Level is always zero because it isn't a parent-child tax. */ public function single_row( $term, $level = 0 ) { static $alternate_class = ''; - $alternate_class = ( '' == $alternate_class ? ' alternate' : '' ); - $row_class = ' class="term-static' . $alternate_class . '"'; + $alternate_class = ( '' == $alternate_class ? ' alternate' : '' ); + $row_class = ' class="term-static' . $alternate_class . '"'; echo wp_kses_post( '