diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cb9b5e8..b689cdc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,16 +8,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: setup git config run: | git config user.name "GitHub Actions Bot" git config user.email "<>" - name: Create artifact run : | - git archive -o classicpress-seo.zip --prefix classicpress-seo/ HEAD + git archive -o classicpress-seo.zip --prefix classicpress-seo/ HEAD - name: Upload artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: classicpress-seo path: classicpress-seo.zip diff --git a/CHANGES.md b/CHANGES.md index 7ff1e64..9f7a30e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,9 @@ +####v 2.3.0 / 2024-07-02 +* IMPROVED: Add option to remove data on uninstall (in General Settings > Misc) +* REMOVED: Remove automatic deactivation of conflicting plugins (that wasn't working) +* REMOVED: Remove unused Update class +* FIXED: Fix some more PHP deprecations + ####v 2.2.2 / 2024-06-20 * FIXED: Fix traits in authors sitemap (#173) diff --git a/classicpress-seo.php b/classicpress-seo.php index 19d4500..d95bb58 100644 --- a/classicpress-seo.php +++ b/classicpress-seo.php @@ -3,7 +3,7 @@ * Plugin Name: Classic SEO * Plugin URI: https://github.com/ClassicPress/classicpress-seo * Description: Classic SEO is the first SEO plugin built specifically to work with ClassicPress. The plugin contains many essential SEO tools to help optimize your website. - * Version: 2.2.2 + * Version: 2.3.0 * Author: ClassicPress * Author URI: https://github.com/ClassicPress * License: GPL v2 or later @@ -34,7 +34,7 @@ class Classic_SEO { * * @var string */ - public $version = '2.2.2'; + public $version = '2.3.0'; /** * Classic SEO database version. diff --git a/includes/admin/class-engine.php b/includes/admin/class-engine.php index cb4dc40..5f8b058 100644 --- a/includes/admin/class-engine.php +++ b/includes/admin/class-engine.php @@ -37,7 +37,7 @@ public function __construct() { $runners = [ cpseo()->admin, - cpseo()->admin_assets, + cpseo()->admin_assets, new Admin_Menu, new Option_Center, new Notices, @@ -46,7 +46,6 @@ public function __construct() { new Post_Columns, new Post_Filters, new Import_Export, - new Updates, new Watcher, ]; diff --git a/includes/admin/helpers/class-list-table.php b/includes/admin/helpers/class-list-table.php index f6bf8a2..41e8121 100644 --- a/includes/admin/helpers/class-list-table.php +++ b/includes/admin/helpers/class-list-table.php @@ -9,7 +9,7 @@ namespace Classic_SEO\Admin; - + use WP_List_Table; use Classic_SEO\Helpers\Param; @@ -52,7 +52,7 @@ protected function get_order() { * @return string */ protected function get_orderby( $default = 'create_date' ) { - return Param::get( 'orderby', $default, FILTER_SANITIZE_STRING ); + return Param::get( 'orderby', $default, FILTER_UNSAFE_RAW ); } /** @@ -61,7 +61,7 @@ protected function get_orderby( $default = 'create_date' ) { * @return bool|string */ protected function get_search() { - return Param::request( 's', false, FILTER_SANITIZE_STRING ); + return Param::request( 's', false, FILTER_UNSAFE_RAW ); } /** diff --git a/includes/admin/views/help/developers.php b/includes/admin/views/help/developers.php index b74d403..218df58 100644 --- a/includes/admin/views/help/developers.php +++ b/includes/admin/views/help/developers.php @@ -60,16 +60,6 @@
-

-

- - -
- -
-

diff --git a/includes/admin/watcher/class-watcher.php b/includes/admin/watcher/class-watcher.php index b8d0bd6..067f613 100644 --- a/includes/admin/watcher/class-watcher.php +++ b/includes/admin/watcher/class-watcher.php @@ -26,26 +26,10 @@ class Watcher implements Runner { * Register hooks. */ public function hooks() { - $this->action( 'init', 'init' ); $this->action( 'activated_plugin', 'check_activated_plugin' ); $this->action( 'deactivated_plugin', 'check_deactivated_plugin' ); } - /** - * Set/Deactivate conflicting SEO or Sitemap plugins. - */ - public function init() { - if ( isset( $_GET['cpseo_deactivate_seo_plugins'] ) ) { - $this->deactivate_conflicting_plugins( 'seo' ); - return; - } - - if ( isset( $_GET['cpseo_deactivate_sitemap_plugins'] ) ) { - $this->deactivate_conflicting_plugins( 'sitemap' ); - return; - } - } - /** * Function to run when new plugin is activated. */ @@ -93,21 +77,6 @@ public static function module_changed( $module, $state ) { self::check_activated_plugin(); } - /** - * Deactivate conflicting plugins. - * - * @param string $type Plugin type. - */ - private function deactivate_conflicting_plugins( $type ) { - foreach ( self::get_conflicting_plugins() as $plugin => $plugin_type ) { - if ( $type === $plugin_type && is_plugin_active( $plugin ) ) { - deactivate_plugins( $plugin ); - } - } - - wp_redirect( remove_query_arg( "cpseo_deactivate_{$type}_plugins" ) ); - } - /** * Function to set conflict plugin notification. * diff --git a/includes/class-installer.php b/includes/class-installer.php index eb6ab20..226874c 100644 --- a/includes/class-installer.php +++ b/includes/class-installer.php @@ -326,6 +326,7 @@ private function create_general_options() { 'cpseo_wc_remove_generator' => 'on', 'cpseo_remove_shop_snippet_data' => 'on', 'cpseo_usage_tracking' => 'on', + 'cpseo_remove_data_on_uninstall' => 'off', ])); } diff --git a/includes/class-updates.php b/includes/class-updates.php deleted file mode 100644 index 1930f7c..0000000 --- a/includes/class-updates.php +++ /dev/null @@ -1,89 +0,0 @@ - 'updates/update-2.0.0.php', - ]; - - /** - * Register hooks. - */ - public function hooks() { - $this->action( 'admin_init', 'do_updates' ); - } - - /** - * Check if any update is required. - */ - public static function do_updates() { - $installed_version = get_option( 'cpseo_version' ); - - // Maybe it's the first install. - if ( ! $installed_version ) { - return; - } - - if ( version_compare( $installed_version, cpseo()->version, '<' ) ) { - self::perform_updates(); - } - - self::perform_pre_release_updates(); - } - - /** - * Perform all updates. - */ - public static function perform_updates() { - $installed_version = get_option( 'cpseo_version' ); - - if ( ! empty(self::$updates ) ) { - foreach ( self::$updates as $version => $path ) { - if ( version_compare( $installed_version, $version, '<' ) ) { - include $path; - update_option( 'cpseo_version', $version ); // If the option does not exist, then the option will be added with the option value - } - } - } - - // Save install date. - if ( false === boolval( get_option( 'cpseo_install_date' ) ) ) { - update_option( 'cpseo_install_date', current_time( 'timestamp' ) ); - } - - update_option( 'cpseo_version', cpseo()->version ); - update_option( 'cpseo_db_version', cpseo()->db_version ); - } - - /** - * Perform pre-release updates. - * Not required since 1.0.0 - */ - public static function perform_pre_release_updates() { - delete_option( 'cpseo_pre_release_version' ); - } -} diff --git a/includes/helpers/class-wordpress.php b/includes/helpers/class-wordpress.php index d9f0eb6..5d120c9 100644 --- a/includes/helpers/class-wordpress.php +++ b/includes/helpers/class-wordpress.php @@ -354,7 +354,7 @@ private static function get_role_capabilities( $slug, $caps, &$data ) { */ public static function set_capabilities( $roles ) { $caps = array_keys( self::get_capabilities() ); - foreach ( WP_Helper::get_roles() as $slug => $role ) { + foreach ( self::get_roles() as $slug => $role ) { self::set_role_capabilities( $slug, $caps, $roles ); } } diff --git a/includes/modules/404-monitor/class-admin.php b/includes/modules/404-monitor/class-admin.php index 22b4558..a5ba7cf 100644 --- a/includes/modules/404-monitor/class-admin.php +++ b/includes/modules/404-monitor/class-admin.php @@ -26,6 +26,8 @@ #[\AllowDynamicProperties] class Admin extends Base { + use WordPress; + /** * The Constructor. * @@ -66,7 +68,7 @@ public function __construct() { * @codeCoverageIgnore */ public function init() { - $action = WordPress::get_request_action(); + $action = self::get_request_action(); if ( false === $action || ! in_array( $action, [ 'delete', 'clear_log' ], true ) ) { return; } diff --git a/includes/modules/redirections/class-table.php b/includes/modules/redirections/class-table.php index f0921dc..3a3b247 100644 --- a/includes/modules/redirections/class-table.php +++ b/includes/modules/redirections/class-table.php @@ -267,7 +267,7 @@ public function get_views() { $url . '&status=' . $key, $key === $current ? ' class="current"' : '', $label, - number_format_i18n( $counts[ $key ] ) + number_format_i18n( $counts[ $key ] ?? 0 ) ); } diff --git a/includes/modules/rich-snippet/snippets/class-event.php b/includes/modules/rich-snippet/snippets/class-event.php index 67dfdfb..a2bb175 100644 --- a/includes/modules/rich-snippet/snippets/class-event.php +++ b/includes/modules/rich-snippet/snippets/class-event.php @@ -20,6 +20,8 @@ #[\AllowDynamicProperties] class Event implements Snippet { + use Helper; + /** * Event rich snippet. * @@ -47,10 +49,10 @@ public function process( $data, $jsonld ) { ]; if ( $start_date = Helper::get_post_meta( 'snippet_event_startdate' ) ) { // phpcs:ignore - $entity['startDate'] = str_replace( ' ', 'T', Helper::convert_date( $start_date ) ); + $entity['startDate'] = str_replace( ' ', 'T', self::convert_date( $start_date ) ); } if ( $end_date = Helper::get_post_meta( 'snippet_event_enddate' ) ) { // phpcs:ignore - $entity['endDate'] = str_replace( ' ', 'T', Helper::convert_date( $end_date ) ); + $entity['endDate'] = str_replace( ' ', 'T', self::convert_date( $end_date ) ); } $jsonld->add_ratings( 'event', $entity ); @@ -65,7 +67,7 @@ public function process( $data, $jsonld ) { ], $entity['offers'] ); if ( ! empty( $entity['offers']['validFrom'] ) ) { - $entity['offers']['validFrom'] = str_replace( ' ', 'T', Helper::convert_date( $entity['offers']['validFrom'] ) ); + $entity['offers']['validFrom'] = str_replace( ' ', 'T', self::convert_date( $entity['offers']['validFrom'] ) ); } if ( empty( $entity['offers']['price'] ) ) { diff --git a/includes/settings/general/others.php b/includes/settings/general/others.php index 1d246b6..a17ffc9 100644 --- a/includes/settings/general/others.php +++ b/includes/settings/general/others.php @@ -16,6 +16,14 @@ 'default' => 'on', ]); +$cmb->add_field([ + 'id' => 'cpseo_remove_data_on_uninstall', + 'type' => 'switch', + 'name' => esc_html__( 'Remove all data on uninstall', 'cpseo' ), + 'desc' => esc_html__( 'Clean all options and delete database tables on uninstall.', 'cpseo' ), + 'default' => 'off', +]); + $cmb->add_field([ 'id' => 'cpseo_rss_before_content', 'type' => 'textarea_small', diff --git a/includes/updates/update-0.3.0.php b/includes/updates/update-0.3.0.php deleted file mode 100644 index 2645c24..0000000 --- a/includes/updates/update-0.3.0.php +++ /dev/null @@ -1,252 +0,0 @@ -query( "ALTER TABLE {$wpdb->prefix}{$old_table_name} RENAME TO {$wpdb->prefix}{$new_table_name} " ); // phpcs:ignore - } -} - -/** - * Replace cpseo with cpseo table column values - * - * @since 0.3.0 - */ -function cpseo_0_3_0_rename_table_settings_data( $table_name ) { - global $wpdb; - $old = 'cpseo'; - $new = 'cpseo'; - $update_sql = array(); - $where_sql = array(); - $upd = false; - - - if ( DB::check_table_exists( $table_name ) && $table_name == 'options' ) { - // Update option_name - $wpdb->query( "UPDATE {$wpdb->prefix}{$table_name} SET option_name = REPLACE(option_name, 'cpseo', 'cpseo')" ); // phpcs:ignore - - // Update option_value which contains serialized data - $sql = "SELECT option_id, option_value FROM {$wpdb->prefix}{$table_name} WHERE option_value LIKE '%$old%' AND option_name NOT LIKE 'rank%'"; - $data = $wpdb->get_results( $sql, ARRAY_A ); - - foreach ( $data as $col ) { - $upd = false; - $update_sql = []; - $data_to_fix = $col['option_value']; - $opt_id = $col['option_id']; - $edited_data = cpseo_0_3_0_recursive_unserialize_replace_meta( $data_to_fix, false ); - - if ( $edited_data != $data_to_fix ) { - $update_sql[] = 'option_value = "' . cpseo_0_3_0_mysql_escape_mimic( $edited_data ) . '"'; - $upd = true; - } - - if ( $upd ) { - $sql = 'UPDATE ' . $wpdb->prefix.$table_name . ' SET ' . implode( ', ', $update_sql ) . ' WHERE option_id = ' . $opt_id; - $result = $wpdb->query( $sql ); - } - } - - $wpdb->flush(); - } - - - if ( DB::check_table_exists( $table_name ) && $table_name == 'postmeta' ) { - $wpdb->query( "UPDATE {$wpdb->prefix}{$table_name} SET meta_key = REPLACE(meta_key, 'cpseo', 'cpseo') " ); // phpcs:ignore - } - - - if ( DB::check_table_exists( $table_name ) && $table_name == 'usermeta' ) { - // Update meta_key - $wpdb->query( "UPDATE {$wpdb->prefix}{$table_name} SET meta_key = REPLACE(meta_key, 'cpseo', 'cpseo') " ); // phpcs:ignore - - // Update meta_value which contains serialized data - $sql = "SELECT umeta_id, meta_value FROM {$wpdb->prefix}{$table_name} WHERE meta_value LIKE '%$old%' AND meta_key NOT LIKE 'rank%'"; - $data = $wpdb->get_results( $sql, ARRAY_A ); - - foreach ( $data as $col ) { - $upd = false; - $update_sql = []; - $data_to_fix = $col['meta_value']; - $meta_id = $col['umeta_id']; - $edited_data = cpseo_0_3_0_recursive_unserialize_replace_meta( $data_to_fix, false ); - - if ( $edited_data != $data_to_fix ) { - $update_sql[] = 'meta_value = "' . cpseo_0_3_0_mysql_escape_mimic( $edited_data ) . '"'; - $upd = true; - } - - if ( $upd ) { - $sql = 'UPDATE ' . $wpdb->prefix.$table_name . ' SET ' . implode( ', ', $update_sql ) . ' WHERE umeta_id = ' . $meta_id; - $result = $wpdb->query( $sql ); - } - } - - $wpdb->flush(); - } - -} - - -/** - * Adapated from interconnect/it's search/replace script. - * - * @link https://interconnectit.com/products/search-and-replace-for-wordpress-databases/ - * - * Take a serialised array and unserialise it replacing elements as needed and - * unserialising any subordinate arrays and performing the replace on those too. - * - * @access private - * @param array $data Used to pass any subordinate arrays back to in. - * @param boolean $serialised Does the array passed via $data need serialising. - * - * @return string|array The original array with all elements replaced as needed. - */ -function cpseo_0_3_0_recursive_unserialize_replace_meta( $data, $serialised = false ) { - $old = 'cpseo'; - $new = 'cpseo'; - - try { - - if ( is_string( $data ) && ! is_serialized_string( $data ) && ( $unserialized = cpseo_0_3_0_unserialize( $data ) ) !== false ) { - $data = cpseo_0_3_0_recursive_unserialize_replace_meta( $unserialized, true ); - } elseif ( is_array( $data ) ) { - $_tmp = array( ); - foreach ( $data as $key => $value ) { - $_tmp[ $key ] = cpseo_0_3_0_recursive_unserialize_replace_meta( $value, false ); - } - $data = $_tmp; - unset( $_tmp ); - } elseif ( is_object( $data ) ) { - $_tmp = $data; - $props = get_object_vars( $data ); - foreach ( $props as $key => $value ) { - $_tmp->$key = cpseo_0_3_0_recursive_unserialize_replace_meta( $value, false ); - } - $data = $_tmp; - unset( $_tmp ); - } elseif ( is_serialized_string( $data ) ) { - if ( $data = cpseo_0_3_0_unserialize( $data ) !== false ) { - $data = str_replace( $old, $new, $data ); - $data = serialize( $data ); - } - } else { - if ( is_string( $data ) ) { - $data = str_replace( $old, $new, $data ); - } - } - - if ( $serialised ) { - return serialize( $data ); - } - - } catch( Exception $error ) { - - } - - return $data; -} - - -/** - * Return unserialized object or array - * - * @param string $serialized_string Serialized string. - * @param string $method The name of the caller method. - * - * @return mixed, false on failure - */ -function cpseo_0_3_0_unserialize( $serialized_string ) { - if ( ! is_serialized( $serialized_string ) ) { - return false; - } - - $serialized_string = trim( $serialized_string ); - $unserialized_string = @unserialize( $serialized_string ); - - return $unserialized_string; -} - - - -/** - * Mimics the mysql_real_escape_string function but doesn't need an active mysql connection. - * @link https://www.php.net/manual/en/function.mysql-real-escape-string.php#101248 - * @access public - * @param string $input The string to escape. - * @return string - */ -function cpseo_0_3_0_mysql_escape_mimic( $input ) { - if ( is_array( $input ) ) { - return array_map( __METHOD__, $input ); - } - if ( ! empty( $input ) && is_string( $input ) ) { - return str_replace( array( '\\', "\0", "\n", "\r", "'", '"', "\x1a" ), array( '\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z' ), $input ); - } - - return $input; -} - - - -/** - * De-authorized search console - */ -function cpseo_0_3_0_clear_search_console() { - Helper::search_console_data( false ); -} - -/** - * Clear SEO Analysis result. - */ -function cpseo_0_3_0_reset_options() { - delete_option( 'cpseo_seo_analysis_results' ); -} - - -cpseo_0_3_0_reset_options(); -cpseo_0_3_0_update_tables(); -cpseo_0_3_0_clear_search_console(); diff --git a/includes/updates/update-1.0.0-beta.4.php b/includes/updates/update-1.0.0-beta.4.php deleted file mode 100644 index 689b975..0000000 --- a/includes/updates/update-1.0.0-beta.4.php +++ /dev/null @@ -1,112 +0,0 @@ -settings->all_raw(); - $titles = $all_opts['titles']; - - // Post Types. - $post_types = Helper::get_accessible_post_types(); - $post_types[] = 'product'; - - foreach ( $post_types as $post_type ) { - if ( isset( $titles[ 'cpseo_pt_' . $post_type . '_default_snippet_name' ] ) && '%title%' === $titles[ 'cpseo_pt_' . $post_type . '_default_snippet_name' ] ) { - $titles[ 'cpseo_pt_' . $post_type . '_default_snippet_name' ] = '%seo_title%'; - } - - if ( isset( $titles[ 'cpseo_pt_' . $post_type . '_default_snippet_desc' ] ) && '%excerpt%' === $titles[ 'cpseo_pt_' . $post_type . '_default_snippet_desc' ] ) { - $titles[ 'cpseo_pt_' . $post_type . '_default_snippet_desc' ] = '%seo_description%'; - } - } - - Helper::update_all_settings( null, $titles, null ); - cpseo()->settings->reset(); -} - - -/* - * GitHub issue #80 - * https://github.com/ClassicPress-plugins/classicpress-seo/issues/80 - * Fixes missing "cpseo_" prefix in some settings - */ -function cpseo_1_0_0_beta_4_add_settings_prefix() { - $prefix = 'cpseo_'; - $all_opts = cpseo()->settings->all_raw(); - $general_options = $all_opts['general']; - $titles_options = $all_opts['titles']; - $sitemap_options = $all_opts['sitemap']; - - $general_includes = [ - 'stopwords', - 'breadcrumbs_blog_page', - 'rss_before_content', - 'rss_after_content', - 'breadcrumbs', - 'console_profile', - ]; - - $titles_includes = [ - 'pt_post_default_article_type', - 'pt_page_default_article_type', - 'pt_attachment_default_article_type', - 'pt_product_default_article_type', - 'knowledgegraph_type', - ]; - - $sitemap_includes = [ - 'news_sitemap_post_type', - 'include_featured_image', - 'ping_search_engines', - 'exclude_posts', - 'exclude_terms', - 'include_images', - ]; - - foreach ( $general_includes as $general ) { - $gen_opt = Helper::get_settings( "general.{$general}" ); - if ( $gen_opt ) { - $general_options[ "{$prefix}{$general}" ] = $gen_opt; - } - } - if ( $general_options ) { - Helper::update_all_settings( $general_options, null, null ); - } - - foreach ( $titles_includes as $titles ) { - $title_opt = Helper::get_settings( "titles.{$titles}" ); - if ( $title_opt ) { - $titles_options[ "{$prefix}{$titles}" ] = $title_opt; - } - } - if ( $titles_options ) { - Helper::update_all_settings( null, $titles_options, null ); - } - - foreach ( $sitemap_includes as $sitemap ) { - $sitemap_opt = Helper::get_settings( "sitemap.{$sitemap}" ); - if ( $sitemap_opt ) { - $sitemap_options[ "{$prefix}{$sitemap}" ] = "{$prefix}{$sitemap}"; - } - } - if ( $sitemap_options ) { - Helper::update_all_settings( null, null, $sitemap_options ); - } -} - -cpseo_1_0_0_beta_4_convert_snippet_variables(); -cpseo_1_0_0_beta_4_add_settings_prefix(); diff --git a/includes/updates/update-2.0.0.php b/includes/updates/update-2.0.0.php deleted file mode 100644 index a2aca7e..0000000 --- a/includes/updates/update-2.0.0.php +++ /dev/null @@ -1,39 +0,0 @@ -prefix . 'cpseo_sc_analytics'; - $sql = "DROP TABLE IF EXISTS $table_name"; - $wpdb->query( $sql ); - - delete_option( 'cpseo_search_console_data' ); -} -cpseo_2_0_0_remove_gsc_table(); - - -/* - * Clear cpseo_search_console_get_analytics cron job. - */ -function cpseo_2_0_0_remove_gsc_scheduled_hook() { - wp_clear_scheduled_hook( 'cpseo_search_console_get_analytics' ); -} -cpseo_2_0_0_remove_gsc_scheduled_hook(); diff --git a/readme.txt b/readme.txt index 7c8585c..0073cf4 100644 --- a/readme.txt +++ b/readme.txt @@ -1,7 +1,7 @@ == Classic SEO == Description: Classic SEO is the first SEO plugin built specifically to work with ClassicPress. The plugin contains many essential SEO tools to help optimize your website. -Version: 2.2.2 +Version: 2.3.0 Text Domain: cpseo Domain Path: /languages Requires PHP: 7.4 @@ -57,7 +57,13 @@ If you need support for Classic SEO or find a bug, you have threee choices: == Changelog == -**v 2.2.1 / 2024-01-05** +**v 2.3.0 / 2024-07-02** +* IMPROVED: Add option to remove data on uninstall (in General Settings > Misc) +* REMOVED: Remove automatic deactivation of conflicting plugins (that wasn't working) +* REMOVED: Remove unused Update class +* FIXED: Fix some more PHP deprecations + +**v 2.2.2 / 2024-06-20** * FIXED: Fix traits in authors sitemap (#173) **v 2.2.1 / 2024-01-05** diff --git a/uninstall.php b/uninstall.php index 4013399..96cf55f 100644 --- a/uninstall.php +++ b/uninstall.php @@ -24,9 +24,8 @@ exit; } - -// Set cpseo_clear_data_on_uninstall to TRUE to delete all data on uninstall. -if ( true === apply_filters( 'cpseo_clear_data_on_uninstall', false ) ) { +$delete = get_option('cpseo-options-general', ['cpseo_remove_data_on_uninstall' => 'off']); +if ( $delete['cpseo_remove_data_on_uninstall'] === 'on' ) { if ( ! is_multisite() ) { cpseo_remove_data();