From 301eec72edfaea5026cc2252ce7f222c403bb7e2 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 23 Dec 2023 10:34:34 -0800 Subject: [PATCH 01/20] Update .editorconfig --- .editorconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.editorconfig b/.editorconfig index f6c1c41..c3e5bed 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,12 +14,12 @@ insert_final_newline = true trim_trailing_whitespace = true indent_style = tab -[*.yml] +[*.{yml,yaml,json,js,css}] indent_style = space indent_size = 2 [*.md] trim_trailing_whitespace = false -[{*.txt,wp-config-sample.php}] -end_of_line = crlf \ No newline at end of file +[*.{txt,php}] +end_of_line = crlf From 6883e000be66aa32acdb47cfb76897afb59f17f0 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 15 Jun 2024 19:15:12 -0700 Subject: [PATCH 02/20] bump supported php and wp versions --- .github/workflows/integrate.yaml | 6 +- .wp-env.json | 2 +- CHANGELOG.md | 6 ++ README.md | 10 +- disable-blog.php | 164 +++++++++++++++---------------- package-lock.json | 4 +- package.json | 2 +- readme.txt | 20 +++- 8 files changed, 116 insertions(+), 98 deletions(-) diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml index 8e97bf4..326f8a7 100644 --- a/.github/workflows/integrate.yaml +++ b/.github/workflows/integrate.yaml @@ -12,7 +12,7 @@ jobs: runs-on: "ubuntu-latest" strategy: matrix: - php-versions: ['7.4', '8.0', '8.1', '8.2.'] + php-versions: ['8.1', '8.2.', '8.3'] steps: - name: "Checkout" uses: "actions/checkout@v3" @@ -32,7 +32,7 @@ jobs: - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: - php-version: "8.1" + php-version: "8.2" - name: "Install PHPCS" run: | composer require --no-plugins --dev wp-coding-standards/wpcs @@ -50,7 +50,7 @@ jobs: - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: - php-version: "8.1" + php-version: "8.2" - name: "Install PHPStan" run: "composer require --dev szepeviktor/phpstan-wordpress" - name: "Run PHPStan" diff --git a/.wp-env.json b/.wp-env.json index c5ef1a6..9d5b534 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -3,7 +3,7 @@ "WP_DEBUG": true, "WP_DEBUG_LOG": true }, - "phpVersion": "8.2", + "phpVersion": "8.3", "plugins": [ "." ] diff --git a/CHANGELOG.md b/CHANGELOG.md index ba8f297..9c08b00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.5.5 +- Tested up to WordPress 6.5.4 +- Bump minimum WordPress version to 5.9, aligning with minimum PHP support. +- Tested up to PHP 8.3 +- Bump minimum PHP to 8.1, lowest version being maintained. + ## 0.5.4 - Tested up to WordPress 6.4.2. - Bump minimum WordPress version to 5.3, aligning with minimum PHP support. diff --git a/README.md b/README.md index 91e81b4..95bf228 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,12 @@ Disable Blog [![WP compatibility](https://plugintests.com/plugins/wporg/disable-blog/wp-badge.svg)](https://plugintests.com/plugins/wporg/disable-blog/latest) [![PHP compatibility](https://plugintests.com/plugins/wporg/disable-blog/php-badge.svg)](https://plugintests.com/plugins/wporg/disable-blog/latest) -**Requires at least WordPress:** 5.3 -**Tested up to WordPress:** 6.4.2 -**Stable version:** 0.5.4 +**Requires at least WordPress:** 5.9 +**Tested up to WordPress:** 6.5.4 +**Stable version:** 0.5.5 **License:** GPLv2 or later -**Requires PHP:** 7.4 -**Tested up to PHP:** 8.2 +**Requires PHP:** 8.1 +**Tested up to PHP:** 8.3 All the power of WordPress, without a blog. diff --git a/disable-blog.php b/disable-blog.php index f0cd54b..4757394 100644 --- a/disable-blog.php +++ b/disable-blog.php @@ -1,82 +1,82 @@ -run(); -} -add_action( 'plugins_loaded', 'run_disable_blog', 10, 0 ); +run(); +} +add_action( 'plugins_loaded', 'run_disable_blog', 10, 0 ); diff --git a/package-lock.json b/package-lock.json index 1c4c88d..f8057c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "disable-blog", - "version": "0.5.4", + "version": "0.5.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "disable-blog", - "version": "0.5.4", + "version": "0.5.5", "license": "GPL-2.0-or-later", "devDependencies": { "@types/node": "^18.0.0", diff --git a/package.json b/package.json index 723ed3d..6134f07 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "disable-blog", - "version": "0.5.4", + "version": "0.5.5", "description": "A comprehensive WordPress plugin to disable the blog-related functionality.", "author": "Joshua David Nelson", "license": "GPL-2.0-or-later", diff --git a/readme.txt b/readme.txt index 05c1ccc..16af721 100644 --- a/readme.txt +++ b/readme.txt @@ -2,10 +2,10 @@ Contributors: joshuadnelson Donate link: https://joshuadnelson.com/donate/ Tags: remove blog, disable blog, disable settings, disable blogging, disable feeds, posts, feeds, disable rest api, disable xml-rpc, disable author archives -Requires at least: 5.3 -Requires PHP: 7.4 -Tested up to: 6.4.2 -Stable tag: 0.5.4 +Requires at least: 5.9 +Requires PHP: 8.1 +Tested up to: 6.5.4 +Stable tag: 0.5.5 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -87,6 +87,12 @@ There are numerous filters available to change the way this plugin works. Refer == Changelog == += 0.5.5 = +- Tested up to WordPress 6.5.4 +- Bump minimum WordPress version to 5.9, aligning with minimum PHP support. +- Tested up to PHP 8.3 +- Bump minimum PHP to 8.1, lowest version being maintained. + = 0.5.4 = - Tested up to WordPress 6.4.2. - Bump minimum WordPress version to 5.3, aligning with minimum PHP support. @@ -298,6 +304,12 @@ A bunch of stuff: == Upgrade Notice == += 0.5.5 = +- Tested up to WordPress 6.5.4 +- Bump minimum WordPress version to 5.9, aligning with minimum PHP support. +- Tested up to PHP 8.3 +- Bump minimum PHP to 8.1, lowest version being maintained. + = 0.5.4 = - Tested up to WordPress 6.4.2. - Bump minimum WordPress version to 5.3, aligning with minimum PHP support. From 8e49661991ac311b9b0d16053a38532fca656449 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 15 Jun 2024 21:01:40 -0700 Subject: [PATCH 03/20] Update class-disable-blog-functions.php --- includes/class-disable-blog-functions.php | 460 +++++++++++----------- 1 file changed, 238 insertions(+), 222 deletions(-) diff --git a/includes/class-disable-blog-functions.php b/includes/class-disable-blog-functions.php index 4e6eb79..34b5a2d 100644 --- a/includes/class-disable-blog-functions.php +++ b/includes/class-disable-blog-functions.php @@ -1,222 +1,238 @@ -request ) ); - } else { - $current_url = home_url( add_query_arg( array(), $wp->request ) ); - - // Filter the safe redirect to avoid redirectin non-admin urls to the dashboard - // if the fallback is used by the core wp_redirect. - add_filter( 'wp_safe_redirect_fallback', array( $this, 'wp_safe_redirect_fallback' ), 9, 1 ); - } - - // Compare the current url to the redirect url, if they are the same, bail to avoid a loop. - // If there is no valid redirect url, then also bail. - if ( $redirect_url === $current_url || ! esc_url_raw( $redirect_url ) ) { - return; - } - - /** - * Should we pass url query string in the redirect? - * - * Default is false. - * - * @since 0.5.0 - * @param bool $bool true to allow query strings on redirect. - * @return bool - */ - if ( apply_filters( 'dwpb_pass_query_string_on_redirect', false ) ) { - $redirect_url = $this->parse_query_string( $redirect_url ); - } - - wp_safe_redirect( esc_url_raw( $redirect_url ), $this->get_redirect_status_code( $current_url, $redirect_url ) ); - exit; - } - - /** - * Parse the current query string and add it, clean and filter, to the url. - * - * @since 0.5.0 - * @param string $url the url any query string will be added to. - * @return string - */ - private function parse_query_string( $url ) { - - if ( ! isset( $_SERVER['QUERY_STRING'] ) || empty( $_SERVER['QUERY_STRING'] ) ) { - return $url; - } - - // Setup an array of the current query string variables. - $query_vars = array(); - wp_parse_str( $_SERVER['QUERY_STRING'], $query_vars ); // phpcs:ignore - - /** - * Filter for allowed queary string variables. - * - * @since 0.5.0 - * @param array $allowed_query_vars an array of the allowed query variable keys. - * @return array - */ - $allowed_query_vars = apply_filters( 'dwpb_allowed_query_vars', array() ); - if ( ! empty( $allowed_query_vars ) && is_array( $allowed_query_vars ) ) { - $allowed_query_vars = array_filter( $allowed_query_vars, 'esc_html' ); - $query_vars = array_intersect_key( $query_vars, array_flip( $allowed_query_vars ) ); - } - - // Escaping and sanitization are important. - $query_vars = array_filter( $query_vars, 'esc_html' ); - $query_vars = array_filter( $query_vars, 'esc_html', ARRAY_FILTER_USE_KEY ); - - // if we have any query variables, add it to the url. - if ( ! empty( $query_vars ) && is_array( $query_vars ) ) { - $url = add_query_arg( $query_vars, $url ); - } - - return $url; - } - - /** - * Return the status code used in the main redirect function. - * - * @since 0.5.0 - * @param string $current_url the url being redirected FROM. - * @param string $redirect_url the url being redirected TO. - * @return int - */ - private function get_redirect_status_code( $current_url, $redirect_url ) { - - /** - * Filter the status code, must be a valid number to pass, defaults to 301. - * - * @since 0.5.0 - * @param int $status_code the status code returned with the redirect. - * @param string $current_url the url being redirected FROM. - * @param string $redirect_url the url being redirected TO. - * @return int $status_code - */ - $status_code = apply_filters( 'dwpb_redirect_status_code', 301, $current_url, $redirect_url ); - - // Make sure we have a valid redirect status code, if not we set to 301. - // helps to stop a wp_die in wp_redirect if the filtered value returns something invalid. - if ( ! absint( $status_code ) || 300 > $status_code || 399 < $status_code ) { - return 301; - } - - return absint( $status_code ); - } - - /** - * Filter the safe redirect fallback to prevent front-end users - * in public redirects from being redirected to the admin url - * which is the WP core default for safe redirects. - * - * @since 0.5.0 - * @param string $url the fallback url. - * @return string - */ - public function wp_safe_redirect_fallback( $url ) { - - if ( ! is_admin() ) { - return home_url(); - } - - return $url; - } - - /** - * Check what post types are supporting author archives. - * - * @since 0.5.0 - * @return bool|array $post_types Either an array of post types or false. - */ - public function author_archive_post_types() { - - /** - * The post types supported on author archives. - * - * By default only posts are shown on author archives, if other post types are to appear - * on the author archives, pass them with this filter. - * - * @since 0.5.0 - * @param array|bool $post_types an array of post type slugs for author archives, false to disable. - */ - $post_types = apply_filters( 'dwpb_author_archive_post_types', array() ); - - if ( ! empty( $post_types ) ) { - return $post_types; - } - - // Return false if there are no supported post types. - return false; - } - - /** - * Check if we are disabling author archives. - * - * Will only return true if the filter is actively set to true - * AND there are valid post types to show on the author archives. - * - * @since 0.5.0 - * @return bool - */ - public function disable_author_archives() { - - /** - * Disable the author archives. - * - * Because many plugins and themes use author archives for profile pages, - * the ability to disable the author archive defaults to false. - * - * @since 0.5.0 - * @param bool $bool True to disable author archives. - * @return bool - */ - return (bool) apply_filters( 'dwpb_disable_author_archives', false ); - } - - /** - * Toggle the disable feed via this filter. - * - * @since 0.5.1 - * @param object $post Global post object. - * @param bool $is_comment_feed True if the feed is a comment feed. - */ - public function disable_feeds( $post, $is_comment_feed = false ) { - - /** - * Toggle the disable feed via this filter. - * - * @since 0.4.0 - * @param bool $bool True to cancel the feed, assuming it's a post feed. - * @param object $post Global post object. - * @param bool $is_comment_feed True if the feed is a comment feed. - */ - return (bool) apply_filters( 'dwpb_disable_feed', true, $post, $is_comment_feed ); - } -} +request ) ); + } else { + $current_url = home_url( add_query_arg( array(), $wp->request ) ); + + // Filter the safe redirect to avoid redirectin non-admin urls to the dashboard + // if the fallback is used by the core wp_redirect. + add_filter( 'wp_safe_redirect_fallback', array( $this, 'wp_safe_redirect_fallback' ), 9, 1 ); + } + + // Compare the current url to the redirect url, if they are the same, bail to avoid a loop. + // If there is no valid redirect url, then also bail. + if ( $redirect_url === $current_url || ! esc_url_raw( $redirect_url ) ) { + return; + } + + /** + * Should we pass url query string in the redirect? + * + * Default is false. + * + * @since 0.5.0 + * @param bool $bool true to allow query strings on redirect. + * @return bool + */ + if ( apply_filters( 'dwpb_pass_query_string_on_redirect', false ) ) { + $redirect_url = $this->parse_query_string( $redirect_url ); + } + + wp_safe_redirect( esc_url_raw( $redirect_url ), $this->get_redirect_status_code( $current_url, $redirect_url ) ); + exit; + } + + /** + * Parse the current query string and add it, clean and filter, to the url. + * + * @since 0.5.0 + * @param string $url the url any query string will be added to. + * @return string + */ + private function parse_query_string( $url ) { + + if ( ! isset( $_SERVER['QUERY_STRING'] ) || empty( $_SERVER['QUERY_STRING'] ) ) { + return $url; + } + + // Setup an array of the current query string variables. + $query_vars = array(); + wp_parse_str( $_SERVER['QUERY_STRING'], $query_vars ); // phpcs:ignore + + /** + * Filter for allowed queary string variables. + * + * @since 0.5.0 + * @param array $allowed_query_vars an array of the allowed query variable keys. + * @return array + */ + $allowed_query_vars = apply_filters( 'dwpb_allowed_query_vars', array() ); + if ( ! empty( $allowed_query_vars ) && is_array( $allowed_query_vars ) ) { + $allowed_query_vars = array_filter( + $allowed_query_vars, + function ( $value ) { + return ! empty( esc_html( $value ) ); + } + ); + $query_vars = array_intersect_key( $query_vars, array_flip( $allowed_query_vars ) ); + } + + // Escaping and sanitization are important. + $query_vars = array_filter( + $query_vars, + function ( $value ) { + return ! empty( esc_html( $value ) ); + } + ); + $query_vars = array_filter( + $query_vars, + function ( $value ) { + return ! empty( esc_html( $value ) ); + }, + ARRAY_FILTER_USE_KEY + ); + + // if we have any query variables, add it to the url. + if ( ! empty( $query_vars ) && is_array( $query_vars ) ) { + $url = add_query_arg( $query_vars, $url ); + } + + return $url; + } + + /** + * Return the status code used in the main redirect function. + * + * @since 0.5.0 + * @param string $current_url the url being redirected FROM. + * @param string $redirect_url the url being redirected TO. + * @return int + */ + private function get_redirect_status_code( $current_url, $redirect_url ) { + + /** + * Filter the status code, must be a valid number to pass, defaults to 301. + * + * @since 0.5.0 + * @param int $status_code the status code returned with the redirect. + * @param string $current_url the url being redirected FROM. + * @param string $redirect_url the url being redirected TO. + * @return int $status_code + */ + $status_code = apply_filters( 'dwpb_redirect_status_code', 301, $current_url, $redirect_url ); + + // Make sure we have a valid redirect status code, if not we set to 301. + // helps to stop a wp_die in wp_redirect if the filtered value returns something invalid. + if ( ! absint( $status_code ) || 300 > $status_code || 399 < $status_code ) { + return 301; + } + + return absint( $status_code ); + } + + /** + * Filter the safe redirect fallback to prevent front-end users + * in public redirects from being redirected to the admin url + * which is the WP core default for safe redirects. + * + * @since 0.5.0 + * @param string $url the fallback url. + * @return string + */ + public function wp_safe_redirect_fallback( $url ) { + + if ( ! is_admin() ) { + return home_url(); + } + + return $url; + } + + /** + * Check what post types are supporting author archives. + * + * @since 0.5.0 + * @return bool|array $post_types Either an array of post types or false. + */ + public function author_archive_post_types() { + + /** + * The post types supported on author archives. + * + * By default only posts are shown on author archives, if other post types are to appear + * on the author archives, pass them with this filter. + * + * @since 0.5.0 + * @param array|bool $post_types an array of post type slugs for author archives, false to disable. + */ + $post_types = apply_filters( 'dwpb_author_archive_post_types', array() ); + + if ( ! empty( $post_types ) ) { + return $post_types; + } + + // Return false if there are no supported post types. + return false; + } + + /** + * Check if we are disabling author archives. + * + * Will only return true if the filter is actively set to true + * AND there are valid post types to show on the author archives. + * + * @since 0.5.0 + * @return bool + */ + public function disable_author_archives() { + + /** + * Disable the author archives. + * + * Because many plugins and themes use author archives for profile pages, + * the ability to disable the author archive defaults to false. + * + * @since 0.5.0 + * @param bool $bool True to disable author archives. + * @return bool + */ + return (bool) apply_filters( 'dwpb_disable_author_archives', false ); + } + + /** + * Toggle the disable feed via this filter. + * + * @since 0.5.1 + * @param object $post Global post object. + * @param bool $is_comment_feed True if the feed is a comment feed. + */ + public function disable_feeds( $post, $is_comment_feed = false ) { + + /** + * Toggle the disable feed via this filter. + * + * @since 0.4.0 + * @param bool $bool True to cancel the feed, assuming it's a post feed. + * @param object $post Global post object. + * @param bool $is_comment_feed True if the feed is a comment feed. + */ + return (bool) apply_filters( 'dwpb_disable_feed', true, $post, $is_comment_feed ); + } +} From e2d2a1a40a0136e951e324e09d4ced19fff343d4 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 1 Jan 2025 21:10:46 -0800 Subject: [PATCH 04/20] update readmes and changelog --- CHANGELOG.md | 5 +++-- README.md | 2 +- readme.txt | 11 ++++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c08b00..55cfcea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,11 @@ # Changelog ## 0.5.5 -- Tested up to WordPress 6.5.4 -- Bump minimum WordPress version to 5.9, aligning with minimum PHP support. +- Tested up to WordPress 6.7.1 - Tested up to PHP 8.3 - Bump minimum PHP to 8.1, lowest version being maintained. +- Bump minimum WordPress version to 5.9, aligning with minimum PHP support. +- Added `composer.json` ## 0.5.4 - Tested up to WordPress 6.4.2. diff --git a/README.md b/README.md index 95bf228..49d2e6e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Disable Blog [![WP compatibility](https://plugintests.com/plugins/wporg/disable-blog/wp-badge.svg)](https://plugintests.com/plugins/wporg/disable-blog/latest) [![PHP compatibility](https://plugintests.com/plugins/wporg/disable-blog/php-badge.svg)](https://plugintests.com/plugins/wporg/disable-blog/latest) **Requires at least WordPress:** 5.9 -**Tested up to WordPress:** 6.5.4 +**Tested up to WordPress:** 6.7.1 **Stable version:** 0.5.5 **License:** GPLv2 or later **Requires PHP:** 8.1 diff --git a/readme.txt b/readme.txt index 16af721..270a8fc 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Donate link: https://joshuadnelson.com/donate/ Tags: remove blog, disable blog, disable settings, disable blogging, disable feeds, posts, feeds, disable rest api, disable xml-rpc, disable author archives Requires at least: 5.9 Requires PHP: 8.1 -Tested up to: 6.5.4 +Tested up to: 6.7.1 Stable tag: 0.5.5 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -88,10 +88,11 @@ There are numerous filters available to change the way this plugin works. Refer == Changelog == = 0.5.5 = -- Tested up to WordPress 6.5.4 -- Bump minimum WordPress version to 5.9, aligning with minimum PHP support. +- Tested up to WordPress 6.7.1 - Tested up to PHP 8.3 - Bump minimum PHP to 8.1, lowest version being maintained. +- Bump minimum WordPress version to 5.9, aligning with minimum PHP support. +- Added `composer.json` = 0.5.4 = - Tested up to WordPress 6.4.2. @@ -305,10 +306,10 @@ A bunch of stuff: == Upgrade Notice == = 0.5.5 = -- Tested up to WordPress 6.5.4 -- Bump minimum WordPress version to 5.9, aligning with minimum PHP support. +- Tested up to WordPress 6.7.1 - Tested up to PHP 8.3 - Bump minimum PHP to 8.1, lowest version being maintained. +- Bump minimum WordPress version to 5.9, aligning with minimum PHP support. = 0.5.4 = - Tested up to WordPress 6.4.2. From be315c37952b972aed36c2e215d0cd1b9e034171 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 20:45:27 -0800 Subject: [PATCH 05/20] Add composer support --- .gitignore | 1 - composer.json | 26 ++++++++ composer.lock | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 composer.json create mode 100644 composer.lock diff --git a/.gitignore b/.gitignore index f539f87..dcadc48 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /vendor/ /node_modules/ .DS_Store -*.lock diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..c6e561b --- /dev/null +++ b/composer.json @@ -0,0 +1,26 @@ +{ + "name": "joshuanelson/disable-blog", + "description": "All the power of WordPress, without a blog.", + "type": "wordpress-plugin", + "license": "GPL-2.0+", + "homepage": "https://disable.blog", + "authors": [ + { + "name": "Joshua Nelson", + "email": "josh@joshuadnelson.com", + "homepage": "https://joshuadnelson.com" + } + ], + "minimum-stability": "stable", + "prefer-stable": true, + "support": { + "issues": "https://github.com/joshuadavidnelson/disable-blog/issues/", + "forum": "https://wordpress.org/support/plugin/disable-blog/", + "source": "https://github.com/joshuadavidnelson/disable-blog/" + }, + "require": { + "php": ">=8.1", + "composer/installers": "^2.0" + } +} + diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..6d2a2eb --- /dev/null +++ b/composer.lock @@ -0,0 +1,167 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "389da4d9f8e2e67a8ba0069dd2faac2b", + "packages": [ + { + "name": "composer/installers", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/composer/installers.git", + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/installers/zipball/12fb2dfe5e16183de69e784a7b84046c43d97e8e", + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "composer/composer": "^1.10.27 || ^2.7", + "composer/semver": "^1.7.2 || ^3.4.0", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-phpunit": "^1", + "symfony/phpunit-bridge": "^7.1.1", + "symfony/process": "^5 || ^6 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "Composer\\Installers\\Plugin", + "branch-alias": { + "dev-main": "2.x-dev" + }, + "plugin-modifies-install-path": true + }, + "autoload": { + "psr-4": { + "Composer\\Installers\\": "src/Composer/Installers" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kyle Robinson Young", + "email": "kyle@dontkry.com", + "homepage": "https://github.com/shama" + } + ], + "description": "A multi-framework Composer library installer", + "homepage": "https://composer.github.io/installers/", + "keywords": [ + "Dolibarr", + "Eliasis", + "Hurad", + "ImageCMS", + "Kanboard", + "Lan Management System", + "MODX Evo", + "MantisBT", + "Mautic", + "Maya", + "OXID", + "Plentymarkets", + "Porto", + "RadPHP", + "SMF", + "Starbug", + "Thelia", + "Whmcs", + "WolfCMS", + "agl", + "annotatecms", + "attogram", + "bitrix", + "cakephp", + "chef", + "cockpit", + "codeigniter", + "concrete5", + "concreteCMS", + "croogo", + "dokuwiki", + "drupal", + "eZ Platform", + "elgg", + "expressionengine", + "fuelphp", + "grav", + "installer", + "itop", + "known", + "kohana", + "laravel", + "lavalite", + "lithium", + "magento", + "majima", + "mako", + "matomo", + "mediawiki", + "miaoxing", + "modulework", + "modx", + "moodle", + "osclass", + "pantheon", + "phpbb", + "piwik", + "ppi", + "processwire", + "puppet", + "pxcms", + "reindex", + "roundcube", + "shopware", + "silverstripe", + "sydes", + "sylius", + "tastyigniter", + "wordpress", + "yawik", + "zend", + "zikula" + ], + "support": { + "issues": "https://github.com/composer/installers/issues", + "source": "https://github.com/composer/installers/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-06-24T20:46:46+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": ">=8.1" + }, + "platform-dev": [], + "plugin-api-version": "2.3.0" +} From f4104163066945b7b8c01a44d5165589f5c411ba Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 21:09:34 -0800 Subject: [PATCH 06/20] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55cfcea..943384e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - Tested up to PHP 8.3 - Bump minimum PHP to 8.1, lowest version being maintained. - Bump minimum WordPress version to 5.9, aligning with minimum PHP support. -- Added `composer.json` +- Add composer support. ## 0.5.4 - Tested up to WordPress 6.4.2. From 2ed586dfc0405a600427492c8954a56a80c425ab Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 21:12:33 -0800 Subject: [PATCH 07/20] remove version from package.json --- package-lock.json | 1 - package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f8057c6..1d859c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,6 @@ "packages": { "": { "name": "disable-blog", - "version": "0.5.5", "license": "GPL-2.0-or-later", "devDependencies": { "@types/node": "^18.0.0", diff --git a/package.json b/package.json index 6134f07..85a9ae1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,5 @@ { "name": "disable-blog", - "version": "0.5.5", "description": "A comprehensive WordPress plugin to disable the blog-related functionality.", "author": "Joshua David Nelson", "license": "GPL-2.0-or-later", From 36b65b39674273076587e35c80b91d6f3c2c32fc Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 21:49:19 -0800 Subject: [PATCH 08/20] upgrade to actions/checkout@v4 --- .github/workflows/integrate.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml index 326f8a7..58e33f3 100644 --- a/.github/workflows/integrate.yaml +++ b/.github/workflows/integrate.yaml @@ -15,7 +15,7 @@ jobs: php-versions: ['8.1', '8.2.', '8.3'] steps: - name: "Checkout" - uses: "actions/checkout@v3" + uses: "actions/checkout@v4" - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: @@ -28,7 +28,7 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Checkout" - uses: "actions/checkout@v3" + uses: "actions/checkout@v4" - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: @@ -46,7 +46,7 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Checkout" - uses: "actions/checkout@v3" + uses: "actions/checkout@v4" - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: From a6f5050f461dc1af69977c162e33b6157f21312b Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 21:49:45 -0800 Subject: [PATCH 09/20] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 943384e..a18a317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Bump minimum PHP to 8.1, lowest version being maintained. - Bump minimum WordPress version to 5.9, aligning with minimum PHP support. - Add composer support. +- Upgrade Github actions to `actions/checkout@v4` ## 0.5.4 - Tested up to WordPress 6.4.2. From 54d9a8a8ace31e4e7739c3404f0c2c881d62d545 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 21:50:35 -0800 Subject: [PATCH 10/20] whitespace changes --- includes/class-disable-blog-admin.php | 2880 ++++++++++++------------ includes/class-disable-blog-i18n.php | 75 +- includes/class-disable-blog-loader.php | 375 ++- includes/class-disable-blog-public.php | 1135 +++++----- includes/class-disable-blog.php | 814 +++---- 5 files changed, 2632 insertions(+), 2647 deletions(-) diff --git a/includes/class-disable-blog-admin.php b/includes/class-disable-blog-admin.php index 52f449e..825d341 100644 --- a/includes/class-disable-blog-admin.php +++ b/includes/class-disable-blog-admin.php @@ -1,1445 +1,1435 @@ - - */ - -/** - * The admin-specific functionality of the plugin. - * - * Defines the plugin name, version, and contains all the admin functions. - * - * @since 0.4.0 - */ -class Disable_Blog_Admin { - - /** - * The ID of this plugin. - * - * @since 0.4.0 - * @access private - * @var string $plugin_name The ID of this plugin. - */ - private $plugin_name; - - /** - * The version of this plugin. - * - * @since 0.4.0 - * @access private - * @var string $version The current version of this plugin. - */ - private $version; - - /** - * Object with common utility functions. - * - * @since 0.5.0 - * @access private - * @var object - */ - private $functions; - - /** - * Initialize the class and set its properties. - * - * @since 0.4.0 - * @param string $plugin_name The name of this plugin. - * @param string $version The version of this plugin. - */ - public function __construct( $plugin_name, $version ) { - - $this->plugin_name = $plugin_name; - $this->version = $version; - $this->functions = new Disable_Blog_Functions(); - } - - /** - * Add various links to plugin page - * - * @since 0.5.1 - * @param array $links the array of plugin links. - * @param string $file the current plugin file. - * @return array - */ - public function plugin_links( $links, $file ) { - - /** Capability Check */ - if ( ! current_user_can( 'install_plugins' ) ) { - return $links; - } - - if ( basename( dirname( $file ) ) === $this->plugin_name ) { - $meta = array( - 'support' => ' ' . __( 'Support', 'disable-blog' ) . '', - 'review' => ' ' . __( 'Review', 'disable-blog' ) . '', - 'donate' => ' ' . __( 'Donate', 'disable-blog' ) . '', - 'github' => ' ' . __( 'GitHub', 'disable-blog' ) . '', - ); - $links = array_merge( $links, $meta ); - } - - return $links; - } - - /** - * Disable public arguments of the 'post' post type. - * - * @since 0.4.2 - * @since 0.4.9 removed rest api specific filter and updated function - * for disabling all public-facing aspects of the 'post' post type. - */ - public function modify_post_type_arguments() { - - global $wp_post_types; - - if ( isset( $wp_post_types['post'] ) ) { - $arguments_to_remove = array( - 'has_archive', - 'public', - 'publicly_queryable', - 'rewrite', - 'query_var', - 'show_ui', - 'show_in_admin_bar', - 'show_in_nav_menus', - 'show_in_menu', - 'show_in_rest', - ); - - foreach ( $arguments_to_remove as $arg ) { - if ( isset( $wp_post_types['post']->$arg ) ) { - // @codingStandardsIgnoreStart - phpcs doesn't like using variables like this. - $wp_post_types['post']->$arg = false; - // @codingStandardsIgnoreEnd - } - } - - // exclude from search. - $wp_post_types['post']->exclude_from_search = true; - - // remove supports. - $wp_post_types['post']->supports = array(); - - } - } - - /** - * Disable public arguments of the 'category' and 'post_tag' taxonomies. - * - * Only disables these if the 'post' post type is the only post type using them. - * - * @since 0.4.9 - * @uses dwpb_post_types_with_tax() - * @return void - */ - public function modify_taxonomies_arguments() { - - global $wp_taxonomies; - $taxonomies = array( 'category', 'post_tag' ); - - foreach ( $taxonomies as $tax ) { - if ( isset( $wp_taxonomies[ $tax ] ) ) { - - // remove 'post' from object types. - if ( isset( $wp_taxonomies[ $tax ]->object_type ) ) { - if ( is_array( $wp_taxonomies[ $tax ]->object_type ) ) { - $key = array_search( 'post', $wp_taxonomies[ $tax ]->object_type, true ); - if ( false !== $key ) { - unset( $wp_taxonomies[ $tax ]->object_type[ $key ] ); - } - } - } - - // only modify the public arguments if 'post' is the only post type. - // using this taxonomy. - if ( ! dwpb_post_types_with_tax( $tax ) ) { - - // public arguments to remove. - $arguments_to_remove = array( - 'has_archive', - 'public', - 'publicly_queryable', - 'query_var', - 'show_ui', - 'show_tagcloud', - 'show_in_admin_bar', - 'show_in_quick_edit', - 'show_in_nav_menus', - 'show_admin_column', - 'show_in_menu', - 'show_in_rest', - ); - - foreach ( $arguments_to_remove as $arg ) { - if ( isset( $wp_taxonomies[ $tax ]->$arg ) ) { - // @codingStandardsIgnoreStart - phpcs doesn't like using variables like this. - $wp_taxonomies[ $tax ]->$arg = false; - // @codingStandardsIgnoreEnd - } - } - } - } - } - } - - /** - * Redirect blog-related admin pages - * - * @uses dwpb_post_types_with_tax() - * @since 0.1.0 - * @since 0.4.0 added single post edit screen redirect - * @since 0.5.0 condensed page rediects into a foreach loop with common structure - * for filters and functions used to check redirect conditions. - * @return void - */ - public function redirect_admin_pages() { - - global $pagenow; - - if ( ! isset( $pagenow ) ) { - return; - } - - $screen = get_current_screen(); - - // on multisite: Do not redirect if we are on a network page. - if ( is_multisite() && is_callable( array( $screen, 'in_admin' ) ) && $screen->in_admin( 'network' ) ) { - return; - } - - // setup false redirect url value for default/final check. - $dashboard_url = admin_url( 'index.php' ); - $redirect_url = false; - - // The admin page slugs to potentially be redirected. - $admin_redirects = array( - 'post', - 'edit', - 'post-new', - 'edit-tags', - 'term', - 'edit-comments', - 'options-discussion', - 'options-writing', - 'tools', - ); - - // cycle through each admin page, checking if we need to redirect. - foreach ( $admin_redirects as $pagename ) { - - // Filter names are all underscores. - $filternme = str_replace( '-', '_', $pagename ); - - // Custom function within this class used to check if the page needs to be redirected. - $function = 'redirect_admin_' . $filternme; - - // build the filter. - $filter = 'dwpb_' . $function; - - // If this is the right page, then setup the redirect url. - // make sure we're on that page right now. - // make sure the function used to check/provide the url is callable. - if ( $this->is_admin_page( $pagename ) - && is_callable( array( $this, $function ) ) ) { - - // Check the function for redirect clearance, or custom url. - $redirect = $this->$function(); - - // Set a redirect url variable to check against. - $potential_redirect_url = esc_url_raw( $redirect ); - - // If it's set to `true` then redirect to the dashboard, - // if it's set to a url, redirect to that url. - if ( true === $redirect || ! empty( $potential_redirect_url ) ) { - - // Either this is a custom redirect url or 'true', which defaults the url to the dashboard. - if ( ! empty( $potential_redirect_url ) ) { - $url = $potential_redirect_url; - } else { - $url = $dashboard_url; - } - - /** - * The redirect url used for this admin page. - * - * Example: use 'dwpb_redirect_admin_options_tools' to change the redirect url - * used for the options-tools.php page. Note `-` strings are converted to `_` - * in the filter name. - * - * @since 0.4.0 - * @since 0.5.0 combine common filters. - * @param string $url the url to redirct to, defaults to dashboard. - */ - $redirect_url = apply_filters( $filter, $url ); - - break; // no need to keep looping. - - } - } - } - - /** - * Global admin url redirect filter. - * - * @since 0.5.0 - * @param string $redirect_url The redirect url. - */ - $redirect_url = apply_filters( 'dwpb_admin_redirect_url', $redirect_url ); - - /** - * Redirect blog related admin pages. - * - * @since 0.4.0 - * @since 0.5.0 removed 3rd `$current_url` param. - * @param bool $bool True to enable, default is true. - * @param string $redirect_url The url to being used in the redirect. - */ - if ( $redirect_url && apply_filters( 'dwpb_redirect_admin', true, $redirect_url ) ) { - $this->functions->redirect( $redirect_url ); - } - } - - /** - * The admin redirect arguments checked to redirect the post.php screen. - * - * @since 0.5.0 - * @return bool - */ - public function redirect_admin_post() { - - // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. - return ( isset( $_GET['post'] ) && 'post' == get_post_type( $_GET['post'] ) ); - // @codingStandardsIgnoreEnd - } - - /** - * The admin redirect arguments checked to redirect the edit.php screen. - * - * @since 0.5.0 - * @return bool|string - */ - public function redirect_admin_edit() { - - // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. - if ( ! isset( $_GET['post_type'] ) || isset( $_GET['post_type'] ) && $_GET['post_type'] == 'post' ) { - // @codingStandardsIgnoreEnd - - return admin_url( 'edit.php?post_type=page' ); - - } - - return false; - } - - /** - * The admin redirect arguments checked to redirect the post-new.php screen. - * - * @since 0.5.0 - * @return bool|string - */ - public function redirect_admin_post_new() { - - // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. - if ( ( ! isset( $_GET['post_type'] ) || isset( $_GET['post_type'] ) && $_GET['post_type'] == 'post' ) ) { - // @codingStandardsIgnoreEnd - - return admin_url( 'post-new.php?post_type=page' ); - - } - - return false; - } - - /** - * The admin redirect arguments checked to redirect the term.php screen. - * - * @since 0.5.0 - * @return bool|string - */ - public function reidrect_admin_term() { - - // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. - return ( isset( $_GET['taxonomy'] ) && ! dwpb_post_types_with_tax( $_GET['taxonomy'] ) ); - // @codingStandardsIgnoreEnd - } - - /** - * The admin redirect arguments checked to redirect the edit-tags.php screen. - * - * @since 0.5.0 - * @return bool|string - */ - public function redirect_admin_edit_tags() { - - // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. - return ( isset( $_GET['taxonomy'] ) && ! dwpb_post_types_with_tax( $_GET['taxonomy'] ) ); - // @codingStandardsIgnoreEnd - } - - /** - * The admin redirect arguments checked to redirect the edit-comments.php screen. - * - * @uses dwpb_post_types_with_feature() - * @since 0.5.0 - * @return bool - */ - public function redirect_admin_edit_comments() { - - /** - * Will only work comments are only supported by 'post' type, - * note that pages and attachments support comments by default. - */ - return ! dwpb_post_types_with_feature( 'comments' ); - } - - /** - * The admin redirect arguments checked to redirect the options-discussion.php screen. - * - * The same checks are performed by the edit-comments check, so this is a wrapper function. - * - * @since 0.5.0 - * @return bool - */ - public function redirect_admin_options_discussion() { - - return $this->redirect_admin_edit_comments(); - } - - /** - * The admin redirect arguments checked to redirect the options-writing.php screen. - * - * @since 0.5.0 - * @return bool|string - */ - public function redirect_admin_options_writing() { - - // Redirect writing options to general options. - if ( $this->remove_writing_options() ) { - - return admin_url( 'options-general.php' ); - - } - - return false; - } - - /** - * The admin redirect arguments checked to redirect the options-tools.php screen. - * - * @since 0.5.0 - * @return bool - */ - public function redirect_admin_options_tools() { - - /** - * The isset( $_GET['page'] ) check is to confirm the page - * isn't a 3rd party plugin's option page built into the tools page. - */ - // @codingStandardsIgnoreStart - phpcs wants to nounce this, but that's not needed. - return ! isset( $_GET['page'] ); - // @codingStandardsIgnoreEnd - } - - /** - * Remove Post Related Menus - * - * @uses dwpb_post_types_with_tax() - * @uses dwpb_post_types_with_feature() - * @link http://wordpress.stackexchange.com/questions/57464/remove-posts-from-admin-but-show-a-custom-post - * @since 0.1.0 - * @since 0.4.0 added tools and discussion subpages. - * @return void - */ - public function remove_menu_pages() { - - // Main pages to be removed. - $remove_pages = array( 'edit.php' ); - - // If no post type supports comments, then remove the edit-comments.php page from the admin. - if ( ! dwpb_post_types_with_feature( 'comments' ) ) { - $remove_pages[] = 'edit-comments.php'; - } - - /** - * Top level admin pages to remove. - * - * @since 0.4.0 - * @param array $remove_pages Array of page strings. - */ - $pages = apply_filters( 'dwpb_menu_pages_to_remove', $remove_pages ); - foreach ( $pages as $page ) { - remove_menu_page( $page ); - } - - // Submenu Pages. - $remove_subpages = array( - 'tools.php' => array( 'tools.php' ), - 'options-general.php' => array(), - ); - - // Remove the writings page, if the filter tells us so. - if ( $this->remove_writing_options() ) { - $remove_subpages['options-general.php'][] = 'options-writing.php'; - } - - // If there are no other post types supporting comments, remove the discussion page. - if ( ! dwpb_post_types_with_feature( 'comments' ) ) { - $remove_subpages['options-general.php'][] = 'options-discussion.php'; // Settings > Discussion. - } - - /** - * Admin subpages to be removed. - * - * @since 0.4.0 - * @since 0.5.0 in order to account for multiple subpages with a common parent - * the `subpages` are now in arrays - * @param array $remove_subpages Array of page => subpages where subpages is an array of strings. - */ - $subpages = apply_filters( 'dwpb_menu_subpages_to_remove', $remove_subpages ); - foreach ( $subpages as $page => $subpages ) { - if ( is_array( $subpages ) && ! empty( $subpages ) ) { - foreach ( $subpages as $subpage ) { - remove_submenu_page( $page, $subpage ); - } - } elseif ( is_string( $subpages ) ) { // for backwards compatibility. - remove_submenu_page( $page, $subpages ); - } - } - } - - /** - * Filter the body classes for admin screens to toggle on plugin specific styles. - * - * @since 0.4.7 - * @param string $classes the admin body classes, which is a string *not* an array. - * @return string - */ - public function admin_body_class( $classes ) { - - if ( $this->has_front_page() ) { - $classes .= ' disabled-blog'; - } - - return $classes; - } - - /** - * Filter for removing the writing options page. - * - * @since 0.4.5 - * @return bool - */ - public function remove_writing_options() { - - /** - * Toggle the options-writing page on/off. - * - * Defaults to false because other plugins often extend this page. - * Setting this to true will create a redirect for the page - * and remove it from the admin menu. - * - * See: https://wordpress.org/support/topic/disabling-writing-settings-panel-is-a-problem/ - * - * @since 0.4.5 - * @since 0.5.0 renamed from `dwpb_redirect_admin_options_writing` - * to `dwpb_remove_options_writing`. The old filter name - * is not used by the admin redirect function to filter - * the redirect url used for this page. - * @param bool $bool Defaults to false, keeping the writing page visible. - */ - return apply_filters( 'dwpb_remove_options_writing', false ); - } - - /** - * Remove blog-related admin bar links - * - * @uses dwpb_post_types_with_feature() - * @link http://www.paulund.co.uk/how-to-remove-links-from-wordpress-admin-bar - * @since 0.1.0 - * @return void - */ - public function remove_admin_bar_links() { - - global $wp_admin_bar; - - // If only posts support comments, then remove comment from admin bar. - if ( ! dwpb_post_types_with_feature( 'comments' ) ) { - $wp_admin_bar->remove_menu( 'comments' ); - } - - // Remove New Post from Content. - $wp_admin_bar->remove_node( 'new-post' ); - } - - /** - * Hide all comments from 'post' post type - * - * @uses dwpb_post_types_with_feature() - * @since 0.1.0 - * @param object $comments the comments query object. - * @return object - */ - public function comment_filter( $comments ) { - - global $pagenow; - - if ( ! isset( $pagenow ) ) { - return $comments; - } - - // Filter out comments from post. - $post_types = dwpb_post_types_with_feature( 'comments' ); - if ( $post_types && $this->is_admin_page( 'edit-comments' ) ) { - $comments->query_vars['post_type'] = $post_types; - } - - return $comments; - } - - /** - * Remove post-related dashboard widgets - * - * @uses dwpb_post_types_with_feature() - * @since 0.1.0 - * @since 0.4.1 dry out the code with a foreach loop - * @return void - */ - public function remove_dashboard_widgets() { - - // Remove post-specific widgets only, others obscured/modified elsewhere as necessary. - $metabox = array( - 'dashboard_quick_press' => 'side', // Quick Press. - 'dashboard_recent_drafts' => 'side', // Recent Drafts. - 'dashboard_incoming_links' => 'normal', // Incoming Links. - 'dashboard_activity' => 'normal', // Activity. - ); - - foreach ( $metabox as $metabox_id => $context ) { - - /** - * Filter to change the dashboard widgets beinre removed. - * - * Filter name based on the name of the widget above, - * For instance: `dwpb_disable_dashboard_quick_press` for the Quick Press widget. - * - * @since 0.4.1 - * @param bool $bool True to remove the dashboard widget. - */ - if ( apply_filters( "dwpb_disable_{$metabox_id}", true ) ) { - remove_meta_box( $metabox_id, 'dashboard', $context ); - } - } - } - - /** - * Throw an admin notice if settings are misconfigured. - * - * If there is not a homepage correctly set, then redirects don't work. - * The intention with this notice is to highlight what is needed for the plugin to function properly. - * - * @since 0.2.0 - * @since 0.4.7 Changed this to an error notice function. - * @return void - */ - public function admin_notices() { - - // only throwing this notice in the edit.php, plugins.php, and options-reading.php admin pages. - $current_screen = get_current_screen(); - $screens = array( - 'plugins', - 'options-reading', - 'edit', - ); - if ( ! ( isset( $current_screen->base ) && in_array( $current_screen->base, $screens, true ) ) ) { - return; - } - - // Throw a notice if the we don't have a front page. - if ( ! $this->has_front_page() ) { - - // The second part of the notice depends on which screen we're on. - if ( 'options-reading' === $current_screen->base ) { - - // translators: Direct the user to set a homepage in the current screen. - $message_link = ' ' . __( 'Select a page for your homepage below.', 'disable-blog' ); - - } else { // If we're not on the Reading Options page, then direct the user there. - - $reading_options_page = get_admin_url( null, 'options-reading.php' ); - - // translators: Direct the user to the Reading Settings admin page. - $message_link = ' ' . sprintf( __( 'Change in Reading Settings.', 'disable-blog' ), $reading_options_page ); - - } - - // translators: Prompt to configure the site for static homepage and posts page. - $message = __( 'Disable Blog is not fully active until a static page is selected for the site\'s homepage.', 'disable-blog' ) . $message_link; - - printf( '

%s

', 'notice notice-error', wp_kses_post( $message ) ); - - // If we have a front page set, but no posts page or they are the same,. - // Then let the user know the expected behavior of these two. - } elseif ( 'options-reading' === $current_screen->base - && get_option( 'page_for_posts' ) === get_option( 'page_on_front' ) ) { - - // translators: Warning that the homepage and blog page cannot be the same, the post page is redirected to the homepage. - $message = __( 'Disable Blog requires a homepage that is different from the post page. The "posts page" will be redirected to the homepage.', 'disable-blog' ); - - printf( '

%s

', 'notice notice-error', esc_attr( $message ) ); - - } - } - - /** - * Check that the site has a front page set in the Settings > Reading. - * - * @since 0.4.7 - * @return bool - */ - public function has_front_page() { - - return 'page' === get_option( 'show_on_front' ) && absint( get_option( 'page_on_front' ) ); - } - - /** - * Kill the Press This functionality - * - * @since 0.2.0 - * @return void - */ - public function disable_press_this() { - - wp_die( '"Press This" functionality has been disabled.' ); - } - - /** - * Remove post related widgets - * - * @since 0.2.0 - * @since 0.4.0 simplified unregistering and added dwpb_unregister_widgets filter. - * @return void - */ - public function remove_widgets() { - - // Unregister blog-related widgets. - $widgets = array( - 'WP_Widget_Recent_Comments', // Recent Comments. - 'WP_Widget_Tag_Cloud', // Tag Cloud. - 'WP_Widget_Categories', // Categories. - 'WP_Widget_Archives', // Archives. - 'WP_Widget_Calendar', // Calendar. - 'WP_Widget_Links', // Links. - 'WP_Widget_Recent_Posts', // Recent Posts. - 'WP_Widget_RSS', // RSS. - 'WP_Widget_Tag_Cloud', // Tag Cloud. - ); - foreach ( $widgets as $widget ) { - - /** - * The ability to stop the widget unregister. - * - * @since 0.4.0 - * - * @param bool $bool True to unregister the widget. - * @param string $widget The name of the widget to be unregistered. - */ - if ( apply_filters( 'dwpb_unregister_widgets', true, $widget ) ) { - unregister_widget( $widget ); - } - } - } - - /** - * Filter the widget removal & check for reasons to not remove specific widgets. - * - * If there are post types using the comments or built-in taxonomies outside of the default 'post' - * then we stop the plugin from removing the widget. - * - * @uses dwpb_post_types_with_feature() - * @since 0.4.0 - * @param bool $show true to show. - * @param string $widget the widget name. - * @return bool - */ - public function filter_widget_removal( $show, $widget ) { - - // Remove Categories Widget. - if ( 'WP_Widget_Categories' === $widget && dwpb_post_types_with_tax( 'category' ) ) { - $show = false; - } - - // Remove Recent Comments Widget if posts are the only type with comments. - if ( 'WP_Widget_Recent_Comments' === $widget && dwpb_post_types_with_feature( 'comments' ) ) { - $show = false; - } - - // Remove Tag Cloud. - if ( 'WP_Widget_Tag_Cloud' === $widget && dwpb_post_types_with_tax( 'post_tag' ) ) { - $show = false; - } - - return $show; - } - - /** - * Register the stylesheets for the admin area. - * - * @since 0.4.0 - * @return void - */ - public function enqueue_styles() { - - wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . '../assets/css/disable-blog-admin.css', array(), $this->version, 'all' ); - } - - /** - * Register the scripts used in the admin area. - * - * @since 0.5.0 - * @return void - */ - public function enqueue_scripts() { - - wp_enqueue_script( $this->plugin_name, DWPB_URL . 'assets/js/disable-blog-admin.js', array(), $this->version, true ); - - // Localize some information on the page. - global $pagenow; - $js_vars = array( - 'page' => str_replace( '.php', '', $pagenow ), - 'categoriesSupported' => (bool) dwpb_post_types_with_tax( 'category' ), - 'tagsSupported' => (bool) dwpb_post_types_with_tax( 'post_tag' ), - 'commentsSupported' => (bool) dwpb_post_types_with_feature( 'comments' ), - ); - - wp_localize_script( $this->plugin_name, 'dwpb', $js_vars ); - } - - /** - * Check that we're on a specific admin page. - * - * @since 0.4.8 - * @param string $page the page slug. - * @return bool - */ - public function is_admin_page( $page ) { - - global $pagenow; - - return is_admin() && isset( $pagenow ) && is_string( $pagenow ) && $page . '.php' === $pagenow; - } - - /** - * Filter the comment counts to remove comments to 'post' post type. - * - * @since 0.4.0 - * @since 0.4.3 Moved everything into the post id check and updated caching functions to match wp_get_comments. - * - * @see wp_get_comments() - * @param object $comments the comment count object. - * @param int $post_id the post id. - * @return object - */ - public function filter_wp_count_comments( $comments, $post_id ) { - - // if this is grabbing all the comments, filter out the 'post' comments. - if ( 0 === $post_id ) { - - // Keep caching in place, note that on activation the plugin clears this cache. - // see class-disable-blog-activator.php. - $count = wp_cache_get( 'comments-0', 'counts' ); - if ( false !== $count ) { - return $count; - } - - $comments = $this->get_comment_counts(); - - $comments['moderated'] = $comments['awaiting_moderation']; - unset( $comments['awaiting_moderation'] ); - - $comments = (object) $comments; - wp_cache_set( 'comments-0', $comments, 'counts' ); - - } - - return $comments; - } - - /** - * Alter the comment counts on the admin comment table to remove comments associated with posts. - * - * @since 0.4.0 - * @param array $views all the views. - * @return array - */ - public function filter_admin_table_comment_count( $views ) { - - global $current_screen; - - if ( 'edit-comments' === $current_screen->id ) { - - $updated_counts = $this->get_comment_counts(); - foreach ( $views as $view => $text ) { - if ( isset( $updated_counts[ $view ] ) ) { - $views[ $view ] = preg_replace( '/\([^)]+\)/', '(' . $updated_counts[ $view ] . ')', $views[ $view ] ); - } - } - } - - return $views; - } - - /** - * Retrieve the comment counts without the 'post' comments. - * - * @since 0.4.0 - * @since 0.4.3 Removed Unused "count" function. - * @see get_comment_count() - * @see https://developer.wordpress.org/reference/functions/esc_sql/ - * @return array - */ - public function get_comment_counts() { - - global $wpdb; - - // Set up the counts, we'll be adding to this array. - $comment_count = array( - 'moderated' => 0, - 'approved' => 0, - 'awaiting_moderation' => 0, - 'spam' => 0, - 'trash' => 0, - 'post-trashed' => 0, - 'total_comments' => 0, - 'all' => 0, - ); - - // Get the post types that support comments. - $supported_post_types = dwpb_post_types_with_feature( 'comments' ); - - // Return an array of empty counts if there are no post types that support comments. - if ( empty( $supported_post_types ) || ! is_array( $supported_post_types ) ) { - return $comment_count; - } - - // Sanitizing the post type strings. - $sanitized_post_types = (array) array_map( 'esc_sql', $supported_post_types ); - - // Implode the post types into a string for the query. - $in_post_types = implode( "','", $sanitized_post_types ); - - // Grab the comments that are associated with supported post types only. - // @codingStandardsIgnoreStart -- The get_results function doesn't need a wpdb->prepare here because $in_post_types is sanitized above. - $totals = (array) $wpdb->get_results( - "SELECT comment_approved, COUNT( * ) AS total - FROM {$wpdb->comments} - WHERE comment_post_ID in ( - SELECT ID - FROM {$wpdb->posts} - WHERE post_type in ('{$in_post_types}') - AND post_status = 'publish') - GROUP BY comment_approved", - ARRAY_A - ); - // @codingStandardsIgnoreEnd - - foreach ( $totals as $row ) { - switch ( $row['comment_approved'] ) { - case 'trash': - $comment_count['trash'] = $row['total']; - break; - case 'post-trashed': - $comment_count['post-trashed'] = $row['total']; - break; - case 'spam': - $comment_count['spam'] = $row['total']; - $comment_count['total_comments'] += $row['total']; - break; - case '1': - $comment_count['approved'] = $row['total']; - $comment_count['total_comments'] += $row['total']; - $comment_count['all'] += $row['total']; - break; - case '0': - $comment_count['awaiting_moderation'] = $row['total']; - $comment_count['moderated'] = $comment_count['awaiting_moderation']; - $comment_count['total_comments'] += $row['total']; - $comment_count['all'] += $row['total']; - break; - default: - break; - } - } - - return array_map( 'intval', $comment_count ); - } - - /** - * Clear out the status of all post comments. - * - * @since 0.4.0 - * @param bool $open true if comments are open. - * @param int $post_id the post id. - * @return bool - */ - public function filter_comment_status( $open, $post_id ) { - - return ( 'post' === get_post_type( $post_id ) ) ? false : $open; - } - - /** - * Clear comments from 'post' post type. - * - * @since 0.4.0 - * @param array $comments the array of comments. - * @param int $post_id the post id. - * @return array - */ - public function filter_existing_comments( $comments, $post_id ) { - - return ( 'post' === get_post_type( $post_id ) ) ? array() : $comments; - } - - /** - * Filters the default post display states used in the posts list table. - * - * @since 0.5.0 - * @param string[] $post_states An array of post display states. - * @param WP_Post $post The current post object. - * @return array - */ - public function page_post_states( $post_states, $post ) { - - if ( $this->has_front_page() && absint( get_option( 'page_for_posts' ) ) === $post->ID ) { - // translators: This string is used to indicate that the blog page is redirected to the homepage. - $post_states['dwpb-redirected'] = __( 'Redirected to the homepage', 'disable-blog' ); - } - - return $post_states; - } - - /** - * Removes the Site Health check for the post REST API. - * - * @uses dwpb_get_test_rest_availability() - * @since 0.5.0 - * @param array $tests the tests, of course. - * @return array - */ - public function site_status_tests( $tests ) { - - if ( isset( $tests['direct']['rest_availability'] ) && is_callable( array( $this, 'get_test_rest_availability' ) ) ) { - $tests['direct']['rest_availability']['test'] = array( $this, 'get_test_rest_availability' ); - } - - return $tests; - } - - /** - * Replaces the core REST Availability Site Health check. - * - * Used by the site_status_tests filter in class-disable-blog-admin.php. - * - * Copied directly from https://developer.wordpress.org/reference/classes/wp_site_health/get_test_rest_availability/ but with the 'post' type updated to 'page' in the rest url. - * - * @see https://make.wordpress.org/core/2019/04/25/site-health-check-in-5-2/ - * @since 0.5.0 - * @return array - */ - public function get_test_rest_availability() { - - $result = array( - 'label' => __( 'The REST API is available' ), - 'status' => 'good', - 'badge' => array( - 'label' => __( 'Performance' ), - 'color' => 'blue', - ), - 'description' => sprintf( - '

%s

', - __( 'The REST API is one way WordPress, and other applications, communicate with the server. One example is the block editor screen, which relies on this to display, and save, your posts and pages.' ) - ), - 'actions' => '', - 'test' => 'rest_availability', - ); - - $cookies = wp_unslash( $_COOKIE ); - $timeout = 10; - $headers = array( - 'Cache-Control' => 'no-cache', - 'X-WP-Nonce' => wp_create_nonce( 'wp_rest' ), - ); - - /** This filter is documented in wp-includes/class-wp-http-streams.php */ - $sslverify = apply_filters( 'https_local_ssl_verify', false ); - - // Include Basic auth in loopback requests. - // @codingStandardsIgnoreStart - if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { - $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); - } - // @codingStandardsIgnoreEnd - - // -- here's the money, change this from 'post' to 'page'. - $url = rest_url( 'wp/v2/types/page' ); - - // The context for this is editing with the new block editor. - $url = add_query_arg( - array( - 'context' => 'edit', - ), - $url - ); - - $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) ); - - if ( is_wp_error( $r ) ) { - $result['status'] = 'critical'; - - $result['label'] = __( 'The REST API encountered an error' ); - - $result['description'] .= sprintf( - '

%s

', - sprintf( - '%s
%s', - __( 'The REST API request failed due to an error.' ), - sprintf( - /* translators: 1: The WordPress error message. 2: The WordPress error code. */ - __( 'Error: %1$s (%2$s)' ), - $r->get_error_message(), - $r->get_error_code() - ) - ) - ); - } elseif ( 200 !== wp_remote_retrieve_response_code( $r ) ) { - $result['status'] = 'recommended'; - - $result['label'] = __( 'The REST API encountered an unexpected result' ); - - $result['description'] .= sprintf( - '

%s

', - sprintf( - /* translators: 1: The HTTP error code. 2: The HTTP error message. */ - __( 'The REST API call gave the following unexpected result: (%1$d) %2$s.' ), - wp_remote_retrieve_response_code( $r ), - esc_html( wp_remote_retrieve_body( $r ) ) - ) - ); - } else { - $json = json_decode( wp_remote_retrieve_body( $r ), true ); - - if ( false !== $json && ! isset( $json['capabilities'] ) ) { - $result['status'] = 'recommended'; - - $result['label'] = __( 'The REST API did not behave correctly' ); - - $result['description'] .= sprintf( - '

%s

', - sprintf( - /* translators: %s: The name of the query parameter being tested. */ - __( 'The REST API did not process the %s query parameter correctly.' ), - 'context' - ) - ); - } - } - - return $result; - } - - /** - * Filter the taxonomy count on post_tag and category screens. - * - * Used on the post_tag and category screens for custom post types. - * - * @since 0.5.0 - * @param array $actions an array of actions. - * @param object $tag the current taxonomy object. - * @return array - */ - public function filter_taxonomy_count( $actions, $tag ) { - - if ( isset( $tag->taxonomy, $tag->count, $tag->term_id ) - && ( 'post_tag' === $tag->taxonomy || 'category' === $tag->taxonomy ) ) { - $screen = get_current_screen(); - $count = $this->get_term_post_count_by_type( $tag->term_id, $tag->taxonomy, $screen->post_type ); - $tag->count = $count; - } - - return $actions; - } - - /** - * Return the post count for a term based by post type. - * - * @since 0.5.0 - * @param int $term_id the current term id. - * @param string $taxonomy the taxonomy slug. - * @param string $post_type the post type slug. - * @return int - */ - public function get_term_post_count_by_type( $term_id, $taxonomy, $post_type ) { - - $args = array( - 'fields' => 'ids', - 'posts_per_page' => 500, - 'post_type' => $post_type, - 'no_found_rows' => true, - 'update_post_meta_cache' => false, - 'tax_query' => array( - array( - 'taxonomy' => $taxonomy, - 'field' => 'id', - 'terms' => intval( $term_id ), - ), - ), - ); - $query = new WP_Query( $args ); - - if ( count( $query->posts ) > 0 ) { - return count( $query->posts ); - } else { - return 0; - } - } - - /** - * Remove posts column form user table. - * - * @since 0.5.0 - * @param array $columns the column slugs => title array. - * @return array - */ - public function manage_users_columns( $columns ) { - - /** - * Disable the user post column. - * - * @since 0.5.0 - * @param bool $bool True to remove the column, defaults to true. - * @return bool - */ - $disable_user_posts_column = apply_filters( 'dpwb_disable_user_post_column', true ); - - if ( isset( $columns['posts'] ) && true === (bool) $disable_user_posts_column ) { - unset( $columns['posts'] ); - } - - // Get the current screen. - $screen = get_current_screen(); - - // cycle through the post types to be displayed. - $post_types = $this->user_column_post_types(); - foreach ( $post_types as $post_type ) { - /** - * Create a new column for 'pages' similar to the original 'post' column. - * - * @since 0.5.0 - * @param bool $bool True to remove the column, defaults to true. - * @return bool - */ - if ( apply_filters( "dpwb_create_user_{$post_type}_column", true ) - // Taken from core functions for users page, don't display the posts column on site-users-network core page. - // see wp-admin/includes/class-wp-users-list-table.php. - && isset( $screen->id ) && 'site-users-network' !== $screen->id ) { - - $post_type_obj = get_post_type_object( $post_type ); - if ( isset( $post_type_obj->labels->name ) ) { - $columns[ $post_type ] = $post_type_obj->labels->name; - } - } - } - - return $columns; - } - - /** - * Mange the new custom user columns. - * - * @since 0.5.0 - * @see wp-admin/includes/class-wp-users-list-table.php. - * @param string $output the column output. - * @param string $column_name the current column slug. - * @param int $user_id the current user's id. - * @return string - */ - public function manage_users_custom_column( $output, $column_name, $user_id ) { - - $post_types = $this->user_column_post_types(); - foreach ( $post_types as $post_type ) { - // Create new column output, mimicking core's 'post' column but for the 'page' and supported custom post types. - if ( $post_type === $column_name ) { - - // make sure we can grab valid post type labels before continuing. - $post_type_obj = get_post_type_object( $post_type ); - if ( ! isset( $post_type_obj->labels->name, $post_type_obj->labels->singular_name ) ) { - continue; - } - - // Get the couunts. - $page_counts = count_many_users_posts( array( $user_id ), $post_type ); - $page_count = absint( $page_counts[ $user_id ] ); - - // Note that we have to explicitly add the query variable for post type in order - // to avoid it being stripped by the admin redirect on the edit.php page. - // @codingStandardsIgnoreStart - phpcs doesn't like the mismatched placeholders, but it works. - $output = sprintf( - '%s', - "edit.php?post_type={$post_type}&author={$user_id}", - $page_count, - sprintf( - // translators: %1$s: Number of pieces of content by this author. %2$s and %3$s are the singular and plural names, respectively, for the content type. For example: "1 page by this author" and "2 pages by this author". - _n( '%1$s %2$s by this author', '%1$s %3$s by this author', $page_count, 'disable-blog' ), - number_format_i18n( $page_count ), - $post_type_obj->labels->singular_name, - $post_type_obj->labels->name - ) - ); - // @codingStandardsIgnoreEnd - } - } - - return $output; - } - - /** - * Grab the post types that - * - * @since 0.5.0 - * @return array - */ - private function user_column_post_types() { - - // Include any post types using author archives. - $post_types = $this->functions->author_archive_post_types(); - - // The author_archive_post_type function returns false if empty, but we need an array. - $post_types = empty( $post_types ) ? array() : $post_types; - - // Also include pages, which is not in the author archive by default, - // however we run array_unique in case the 'page' post type has been added - // to the author archives. - $post_types = array_unique( array_merge( array( 'page' ), $post_types ) ); - - /** - * Filter the post types that appear in the user table. - * - * @since 0.5.0 - * @param array $post_types an array of post type slugs. - * @return array - */ - return apply_filters( 'dwpb_admin_user_post_types', $post_types ); - } - - /** - * Remove the user's "view" link if we are not supporting author archives. - * - * @since 0.5.0 - * @param array $actions an array of actions in a key => output format. - * @param object $user_object the current user, WP_User object. - * @return array - */ - public function user_row_actions( $actions, $user_object ) { - - if ( true === $this->functions->disable_author_archives() - && isset( $actions['view'] ) ) { - unset( $actions['view'] ); - } - - return $actions; - } - - /** - * Alter the customizer view to mimic the reading settings. - * - * @since 0.5.0 - * @return void - */ - public function customizer_styles() { - - if ( $this->has_front_page() ) { - ?> - - plugin_name . '-customizer-scripts', DWPB_URL . 'assets/js/disable-blog-customizer.js', array( 'jquery', 'customize-controls' ), $this->version, true ); - - // Localize some information on the page. - $js_vars = array( - // translators: This text appears in the Customizer > Homepage Settings and provides the context for the homepage select field there. - 'homepageSettingsText' => __( 'You can choose what\'s displayed on the homepage of your site. To set a static homepage, create or select the page below.', 'disable-blog' ), - ); - wp_localize_script( $this->plugin_name . '-customizer-scripts', 'dwpbCustomizer', $js_vars ); - } - - /** - * Remove the default posts page notice and replace it with a new one. - * - * @since 0.5.0 - * @return void - */ - public function update_posts_page_notice() { - - if ( (int) get_option( 'page_for_posts' ) === get_the_ID() ) { - remove_action( 'edit_form_after_title', '_wp_posts_page_notice' ); - add_action( 'edit_form_after_title', array( $this, 'posts_page_notice' ) ); - } - } - - /** - * Replaces the default blog page posts notice. - * - * @since 0.5.0 - * @return void - */ - public function posts_page_notice() { - - // translators: this notice informs the user why the blog page editor is disabled and that it is redirected to the homepage. - echo '

' . __( 'You are currently editing the page that shows your latest posts, which is redirected to the homepage because the blog is disabled.', 'disable-blog' ) . '

'; // phpcs:ignore - } - - /** - * Filter the available tags in the permalink settings. - * - * @since 0.5.1 - * @param array $available_tags The available permalink tags. - * @return array - */ - public function available_permalink_structure_tags( $available_tags ) { - - // Remove the category permalink structure if categories are not supported. - if ( isset( $available_tags['category'] ) && ! dwpb_post_types_with_tax( 'category' ) ) { - unset( $available_tags['category'] ); - } - - // Remove the author tag if author archives are disabled. - if ( isset( $available_tags['author'] ) && $this->functions->disable_author_archives() ) { - unset( $available_tags['author'] ); - } - - return $available_tags; - } -} + + */ + +/** + * The admin-specific functionality of the plugin. + * + * Defines the plugin name, version, and contains all the admin functions. + * + * @since 0.4.0 + */ +class Disable_Blog_Admin { + + /** + * The ID of this plugin. + * + * @since 0.4.0 + * @access private + * @var string $plugin_name The ID of this plugin. + */ + private $plugin_name; + + /** + * The version of this plugin. + * + * @since 0.4.0 + * @access private + * @var string $version The current version of this plugin. + */ + private $version; + + /** + * Object with common utility functions. + * + * @since 0.5.0 + * @access private + * @var object + */ + private $functions; + + /** + * Initialize the class and set its properties. + * + * @since 0.4.0 + * @param string $plugin_name The name of this plugin. + * @param string $version The version of this plugin. + */ + public function __construct( $plugin_name, $version ) { + $this->plugin_name = $plugin_name; + $this->version = $version; + $this->functions = new Disable_Blog_Functions(); + } + + /** + * Add various links to plugin page + * + * @since 0.5.1 + * @param array $links the array of plugin links. + * @param string $file the current plugin file. + * @return array + */ + public function plugin_links( $links, $file ) { + + /** Capability Check */ + if ( ! current_user_can( 'install_plugins' ) ) { + return $links; + } + + if ( basename( dirname( $file ) ) === $this->plugin_name ) { + $meta = array( + 'support' => ' ' . __( 'Support', 'disable-blog' ) . '', + 'review' => ' ' . __( 'Review', 'disable-blog' ) . '', + 'donate' => ' ' . __( 'Donate', 'disable-blog' ) . '', + 'github' => ' ' . __( 'GitHub', 'disable-blog' ) . '', + ); + $links = array_merge( $links, $meta ); + } + + return $links; + } + + /** + * Disable public arguments of the 'post' post type. + * + * @since 0.4.2 + * @since 0.4.9 removed rest api specific filter and updated function + * for disabling all public-facing aspects of the 'post' post type. + * @return void + */ + public function modify_post_type_arguments() { + + global $wp_post_types; + + if ( isset( $wp_post_types['post'] ) ) { + $arguments_to_remove = array( + 'has_archive', + 'public', + 'publicly_queryable', + 'rewrite', + 'query_var', + 'show_ui', + 'show_in_admin_bar', + 'show_in_nav_menus', + 'show_in_menu', + 'show_in_rest', + ); + + foreach ( $arguments_to_remove as $arg ) { + if ( isset( $wp_post_types['post']->$arg ) ) { + // @codingStandardsIgnoreStart - phpcs doesn't like using variables like this. + $wp_post_types['post']->$arg = false; + // @codingStandardsIgnoreEnd + } + } + + // exclude from search. + $wp_post_types['post']->exclude_from_search = true; + + // remove supports. + $wp_post_types['post']->supports = array(); + } + } + + /** + * Disable public arguments of the 'category' and 'post_tag' taxonomies. + * + * Only disables these if the 'post' post type is the only post type using them. + * + * @since 0.4.9 + * @uses dwpb_post_types_with_tax() + * @return void + */ + public function modify_taxonomies_arguments() { + + global $wp_taxonomies; + $taxonomies = array( 'category', 'post_tag' ); + + foreach ( $taxonomies as $tax ) { + if ( isset( $wp_taxonomies[ $tax ] ) ) { + + // remove 'post' from object types. + if ( isset( $wp_taxonomies[ $tax ]->object_type ) ) { + if ( is_array( $wp_taxonomies[ $tax ]->object_type ) ) { + $key = array_search( 'post', $wp_taxonomies[ $tax ]->object_type, true ); + if ( false !== $key ) { + unset( $wp_taxonomies[ $tax ]->object_type[ $key ] ); + } + } + } + + // only modify the public arguments if 'post' is the only post type. + // using this taxonomy. + if ( ! dwpb_post_types_with_tax( $tax ) ) { + + // public arguments to remove. + $arguments_to_remove = array( + 'has_archive', + 'public', + 'publicly_queryable', + 'query_var', + 'show_ui', + 'show_tagcloud', + 'show_in_admin_bar', + 'show_in_quick_edit', + 'show_in_nav_menus', + 'show_admin_column', + 'show_in_menu', + 'show_in_rest', + ); + + foreach ( $arguments_to_remove as $arg ) { + if ( isset( $wp_taxonomies[ $tax ]->$arg ) ) { + // @codingStandardsIgnoreStart - phpcs doesn't like using variables like this. + $wp_taxonomies[ $tax ]->$arg = false; + // @codingStandardsIgnoreEnd + } + } + } + } + } + } + + /** + * Redirect blog-related admin pages + * + * @uses dwpb_post_types_with_tax() + * @since 0.1.0 + * @since 0.4.0 added single post edit screen redirect + * @since 0.5.0 condensed page rediects into a foreach loop with common structure + * for filters and functions used to check redirect conditions. + * @return void + */ + public function redirect_admin_pages() { + + global $pagenow; + + if ( ! isset( $pagenow ) ) { + return; + } + + $screen = get_current_screen(); + + // on multisite: Do not redirect if we are on a network page. + if ( is_multisite() && is_callable( array( $screen, 'in_admin' ) ) && $screen->in_admin( 'network' ) ) { + return; + } + + // setup false redirect url value for default/final check. + $dashboard_url = admin_url( 'index.php' ); + $redirect_url = false; + + // The admin page slugs to potentially be redirected. + $admin_redirects = array( + 'post', + 'edit', + 'post-new', + 'edit-tags', + 'term', + 'edit-comments', + 'options-discussion', + 'options-writing', + 'tools', + ); + + // cycle through each admin page, checking if we need to redirect. + foreach ( $admin_redirects as $pagename ) { + + // Filter names are all underscores. + $filternme = str_replace( '-', '_', $pagename ); + + // Custom function within this class used to check if the page needs to be redirected. + $function = 'redirect_admin_' . $filternme; + + // build the filter. + $filter = 'dwpb_' . $function; + + // If this is the right page, then setup the redirect url. + // make sure we're on that page right now. + // make sure the function used to check/provide the url is callable. + if ( $this->is_admin_page( $pagename ) + && is_callable( array( $this, $function ) ) ) { + + // Check the function for redirect clearance, or custom url. + $redirect = $this->$function(); + + // Set a redirect url variable to check against. + $potential_redirect_url = esc_url_raw( $redirect ); + + // If it's set to `true` then redirect to the dashboard, + // if it's set to a url, redirect to that url. + if ( true === $redirect || ! empty( $potential_redirect_url ) ) { + + // Either this is a custom redirect url or 'true', which defaults the url to the dashboard. + if ( ! empty( $potential_redirect_url ) ) { + $url = $potential_redirect_url; + } else { + $url = $dashboard_url; + } + + /** + * The redirect url used for this admin page. + * + * Example: use 'dwpb_redirect_admin_options_tools' to change the redirect url + * used for the options-tools.php page. Note `-` strings are converted to `_` + * in the filter name. + * + * @since 0.4.0 + * @since 0.5.0 combine common filters. + * @param string $url the url to redirct to, defaults to dashboard. + */ + $redirect_url = apply_filters( $filter, $url ); + + break; // no need to keep looping. + + } + } + } + + /** + * Global admin url redirect filter. + * + * @since 0.5.0 + * @param string $redirect_url The redirect url. + */ + $redirect_url = apply_filters( 'dwpb_admin_redirect_url', $redirect_url ); + + /** + * Redirect blog related admin pages. + * + * @since 0.4.0 + * @since 0.5.0 removed 3rd `$current_url` param. + * @param bool $bool True to enable, default is true. + * @param string $redirect_url The url to being used in the redirect. + */ + if ( $redirect_url && apply_filters( 'dwpb_redirect_admin', true, $redirect_url ) ) { + $this->functions->redirect( $redirect_url ); + } + } + + /** + * The admin redirect arguments checked to redirect the post.php screen. + * + * @since 0.5.0 + * @return bool + */ + public function redirect_admin_post() { + + // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. + return ( isset( $_GET['post'] ) && 'post' == get_post_type( $_GET['post'] ) ); + // @codingStandardsIgnoreEnd + } + + /** + * The admin redirect arguments checked to redirect the edit.php screen. + * + * @since 0.5.0 + * @return bool|string + */ + public function redirect_admin_edit() { + + // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. + if ( ! isset( $_GET['post_type'] ) || isset( $_GET['post_type'] ) && $_GET['post_type'] == 'post' ) { + // @codingStandardsIgnoreEnd + + return admin_url( 'edit.php?post_type=page' ); + + } + + return false; + } + + /** + * The admin redirect arguments checked to redirect the post-new.php screen. + * + * @since 0.5.0 + * @return bool|string + */ + public function redirect_admin_post_new() { + + // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. + if ( ( ! isset( $_GET['post_type'] ) || isset( $_GET['post_type'] ) && $_GET['post_type'] == 'post' ) ) { + // @codingStandardsIgnoreEnd + + return admin_url( 'post-new.php?post_type=page' ); + + } + + return false; + } + + /** + * The admin redirect arguments checked to redirect the term.php screen. + * + * @since 0.5.0 + * @return bool|string + */ + public function reidrect_admin_term() { + + // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. + return ( isset( $_GET['taxonomy'] ) && ! dwpb_post_types_with_tax( $_GET['taxonomy'] ) ); + // @codingStandardsIgnoreEnd + } + + /** + * The admin redirect arguments checked to redirect the edit-tags.php screen. + * + * @since 0.5.0 + * @return bool|string + */ + public function redirect_admin_edit_tags() { + + // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. + return ( isset( $_GET['taxonomy'] ) && ! dwpb_post_types_with_tax( $_GET['taxonomy'] ) ); + // @codingStandardsIgnoreEnd + } + + /** + * The admin redirect arguments checked to redirect the edit-comments.php screen. + * + * @uses dwpb_post_types_with_feature() + * @since 0.5.0 + * @return bool + */ + public function redirect_admin_edit_comments() { + + /** + * Will only work comments are only supported by 'post' type, + * note that pages and attachments support comments by default. + */ + return ! dwpb_post_types_with_feature( 'comments' ); + } + + /** + * The admin redirect arguments checked to redirect the options-discussion.php screen. + * + * The same checks are performed by the edit-comments check, so this is a wrapper function. + * + * @since 0.5.0 + * @return bool + */ + public function redirect_admin_options_discussion() { + return $this->redirect_admin_edit_comments(); + } + + /** + * The admin redirect arguments checked to redirect the options-writing.php screen. + * + * @since 0.5.0 + * @return bool|string + */ + public function redirect_admin_options_writing() { + + // Redirect writing options to general options. + if ( $this->remove_writing_options() ) { + return admin_url( 'options-general.php' ); + } + + return false; + } + + /** + * The admin redirect arguments checked to redirect the options-tools.php screen. + * + * @since 0.5.0 + * @return bool + */ + public function redirect_admin_options_tools() { + + /** + * The isset( $_GET['page'] ) check is to confirm the page + * isn't a 3rd party plugin's option page built into the tools page. + */ + // @codingStandardsIgnoreStart - phpcs wants to nounce this, but that's not needed. + return ! isset( $_GET['page'] ); + // @codingStandardsIgnoreEnd + } + + /** + * Remove Post Related Menus + * + * @uses dwpb_post_types_with_tax() + * @uses dwpb_post_types_with_feature() + * @link http://wordpress.stackexchange.com/questions/57464/remove-posts-from-admin-but-show-a-custom-post + * @since 0.1.0 + * @since 0.4.0 added tools and discussion subpages. + * @return void + */ + public function remove_menu_pages() { + + // Main pages to be removed. + $remove_pages = array( 'edit.php' ); + + // If no post type supports comments, then remove the edit-comments.php page from the admin. + if ( ! dwpb_post_types_with_feature( 'comments' ) ) { + $remove_pages[] = 'edit-comments.php'; + } + + /** + * Top level admin pages to remove. + * + * @since 0.4.0 + * @param array $remove_pages Array of page strings. + */ + $pages = apply_filters( 'dwpb_menu_pages_to_remove', $remove_pages ); + foreach ( $pages as $page ) { + remove_menu_page( $page ); + } + + // Submenu Pages. + $remove_subpages = array( + 'tools.php' => array( 'tools.php' ), + 'options-general.php' => array(), + ); + + // Remove the writings page, if the filter tells us so. + if ( $this->remove_writing_options() ) { + $remove_subpages['options-general.php'][] = 'options-writing.php'; + } + + // If there are no other post types supporting comments, remove the discussion page. + if ( ! dwpb_post_types_with_feature( 'comments' ) ) { + $remove_subpages['options-general.php'][] = 'options-discussion.php'; // Settings > Discussion. + } + + /** + * Admin subpages to be removed. + * + * @since 0.4.0 + * @since 0.5.0 in order to account for multiple subpages with a common parent + * the `subpages` are now in arrays + * @param array $remove_subpages Array of page => subpages where subpages is an array of strings. + */ + $subpages = apply_filters( 'dwpb_menu_subpages_to_remove', $remove_subpages ); + foreach ( $subpages as $page => $subpages ) { + if ( is_array( $subpages ) && ! empty( $subpages ) ) { + foreach ( $subpages as $subpage ) { + remove_submenu_page( $page, $subpage ); + } + } elseif ( is_string( $subpages ) ) { // for backwards compatibility. + remove_submenu_page( $page, $subpages ); + } + } + } + + /** + * Filter the body classes for admin screens to toggle on plugin specific styles. + * + * @since 0.4.7 + * @param string $classes the admin body classes, which is a string *not* an array. + * @return string + */ + public function admin_body_class( $classes ) { + + if ( $this->has_front_page() ) { + $classes .= ' disabled-blog'; + } + + return $classes; + } + + /** + * Filter for removing the writing options page. + * + * @since 0.4.5 + * @return bool + */ + public function remove_writing_options() { + + /** + * Toggle the options-writing page on/off. + * + * Defaults to false because other plugins often extend this page. + * Setting this to true will create a redirect for the page + * and remove it from the admin menu. + * + * See: https://wordpress.org/support/topic/disabling-writing-settings-panel-is-a-problem/ + * + * @since 0.4.5 + * @since 0.5.0 renamed from `dwpb_redirect_admin_options_writing` + * to `dwpb_remove_options_writing`. The old filter name + * is not used by the admin redirect function to filter + * the redirect url used for this page. + * @param bool $bool Defaults to false, keeping the writing page visible. + */ + return (bool) apply_filters( 'dwpb_remove_options_writing', false ); + } + + /** + * Remove blog-related admin bar links + * + * @uses dwpb_post_types_with_feature() + * @link http://www.paulund.co.uk/how-to-remove-links-from-wordpress-admin-bar + * @since 0.1.0 + * @return void + */ + public function remove_admin_bar_links() { + + global $wp_admin_bar; + + // If only posts support comments, then remove comment from admin bar. + if ( ! dwpb_post_types_with_feature( 'comments' ) ) { + $wp_admin_bar->remove_menu( 'comments' ); + } + + // Remove New Post from Content. + $wp_admin_bar->remove_node( 'new-post' ); + } + + /** + * Hide all comments from 'post' post type + * + * @uses dwpb_post_types_with_feature() + * @since 0.1.0 + * @param object $comments the comments query object. + * @return object + */ + public function comment_filter( $comments ) { + + global $pagenow; + + if ( ! isset( $pagenow ) ) { + return $comments; + } + + // Filter out comments from post. + $post_types = dwpb_post_types_with_feature( 'comments' ); + if ( $post_types && $this->is_admin_page( 'edit-comments' ) ) { + $comments->query_vars['post_type'] = $post_types; + } + + return $comments; + } + + /** + * Remove post-related dashboard widgets + * + * @uses dwpb_post_types_with_feature() + * @since 0.1.0 + * @since 0.4.1 dry out the code with a foreach loop + * @return void + */ + public function remove_dashboard_widgets() { + + // Remove post-specific widgets only, others obscured/modified elsewhere as necessary. + $metabox = array( + 'dashboard_quick_press' => 'side', // Quick Press. + 'dashboard_recent_drafts' => 'side', // Recent Drafts. + 'dashboard_incoming_links' => 'normal', // Incoming Links. + 'dashboard_activity' => 'normal', // Activity. + ); + + foreach ( $metabox as $metabox_id => $context ) { + + /** + * Filter to change the dashboard widgets beinre removed. + * + * Filter name based on the name of the widget above, + * For instance: `dwpb_disable_dashboard_quick_press` for the Quick Press widget. + * + * @since 0.4.1 + * @param bool $bool True to remove the dashboard widget. + */ + if ( apply_filters( "dwpb_disable_{$metabox_id}", true ) ) { + remove_meta_box( $metabox_id, 'dashboard', $context ); + } + } + } + + /** + * Throw an admin notice if settings are misconfigured. + * + * If there is not a homepage correctly set, then redirects don't work. + * The intention with this notice is to highlight what is needed for the plugin to function properly. + * + * @since 0.2.0 + * @since 0.4.7 Changed this to an error notice function. + * @return void + */ + public function admin_notices() { + + // only throwing this notice in the edit.php, plugins.php, and options-reading.php admin pages. + $current_screen = get_current_screen(); + $screens = array( + 'plugins', + 'options-reading', + 'edit', + ); + if ( ! ( isset( $current_screen->base ) && in_array( $current_screen->base, $screens, true ) ) ) { + return; + } + + // Throw a notice if the we don't have a front page. + if ( ! $this->has_front_page() ) { + + // The second part of the notice depends on which screen we're on. + if ( 'options-reading' === $current_screen->base ) { + + // translators: Direct the user to set a homepage in the current screen. + $message_link = ' ' . __( 'Select a page for your homepage below.', 'disable-blog' ); + + } else { // If we're not on the Reading Options page, then direct the user there. + + $reading_options_page = get_admin_url( null, 'options-reading.php' ); + + // translators: Direct the user to the Reading Settings admin page. + $message_link = ' ' . sprintf( __( 'Change in Reading Settings.', 'disable-blog' ), $reading_options_page ); + + } + + // translators: Prompt to configure the site for static homepage and posts page. + $message = __( 'Disable Blog is not fully active until a static page is selected for the site\'s homepage.', 'disable-blog' ) . $message_link; + + printf( '

%s

', 'notice notice-error', wp_kses_post( $message ) ); + + // If we have a front page set, but no posts page or they are the same,. + // Then let the user know the expected behavior of these two. + } elseif ( 'options-reading' === $current_screen->base + && get_option( 'page_for_posts' ) === get_option( 'page_on_front' ) ) { + + // translators: Warning that the homepage and blog page cannot be the same, the post page is redirected to the homepage. + $message = __( 'Disable Blog requires a homepage that is different from the post page. The "posts page" will be redirected to the homepage.', 'disable-blog' ); + + printf( '

%s

', 'notice notice-error', esc_attr( $message ) ); + + } + } + + /** + * Check that the site has a front page set in the Settings > Reading. + * + * @since 0.4.7 + * @return bool + */ + public function has_front_page() { + return 'page' === get_option( 'show_on_front' ) && absint( get_option( 'page_on_front' ) ); + } + + /** + * Kill the Press This functionality + * + * @since 0.2.0 + * @return void + */ + public function disable_press_this() { + wp_die( '"Press This" functionality has been disabled.' ); + } + + /** + * Remove post related widgets + * + * @since 0.2.0 + * @since 0.4.0 simplified unregistering and added dwpb_unregister_widgets filter. + * @return void + */ + public function remove_widgets() { + + // Unregister blog-related widgets. + $widgets = array( + 'WP_Widget_Recent_Comments', // Recent Comments. + 'WP_Widget_Tag_Cloud', // Tag Cloud. + 'WP_Widget_Categories', // Categories. + 'WP_Widget_Archives', // Archives. + 'WP_Widget_Calendar', // Calendar. + 'WP_Widget_Links', // Links. + 'WP_Widget_Recent_Posts', // Recent Posts. + 'WP_Widget_RSS', // RSS. + 'WP_Widget_Tag_Cloud', // Tag Cloud. + ); + foreach ( $widgets as $widget ) { + + /** + * The ability to stop the widget unregister. + * + * @since 0.4.0 + * + * @param bool $bool True to unregister the widget. + * @param string $widget The name of the widget to be unregistered. + */ + if ( apply_filters( 'dwpb_unregister_widgets', true, $widget ) ) { + unregister_widget( $widget ); + } + } + } + + /** + * Filter the widget removal & check for reasons to not remove specific widgets. + * + * If there are post types using the comments or built-in taxonomies outside of the default 'post' + * then we stop the plugin from removing the widget. + * + * @uses dwpb_post_types_with_feature() + * @since 0.4.0 + * @param bool $show true to show. + * @param string $widget the widget name. + * @return bool + */ + public function filter_widget_removal( $show, $widget ) { + + // Remove Categories Widget. + if ( 'WP_Widget_Categories' === $widget && dwpb_post_types_with_tax( 'category' ) ) { + $show = false; + } + + // Remove Recent Comments Widget if posts are the only type with comments. + if ( 'WP_Widget_Recent_Comments' === $widget && dwpb_post_types_with_feature( 'comments' ) ) { + $show = false; + } + + // Remove Tag Cloud. + if ( 'WP_Widget_Tag_Cloud' === $widget && dwpb_post_types_with_tax( 'post_tag' ) ) { + $show = false; + } + + return $show; + } + + /** + * Register the stylesheets for the admin area. + * + * @since 0.4.0 + * @return void + */ + public function enqueue_styles() { + wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . '../assets/css/disable-blog-admin.css', array(), $this->version, 'all' ); + } + + /** + * Register the scripts used in the admin area. + * + * @since 0.5.0 + * @return void + */ + public function enqueue_scripts() { + + wp_enqueue_script( $this->plugin_name, DWPB_URL . 'assets/js/disable-blog-admin.js', array(), $this->version, true ); + + // Localize some information on the page. + global $pagenow; + $js_vars = array( + 'page' => str_replace( '.php', '', $pagenow ), + 'categoriesSupported' => (bool) dwpb_post_types_with_tax( 'category' ), + 'tagsSupported' => (bool) dwpb_post_types_with_tax( 'post_tag' ), + 'commentsSupported' => (bool) dwpb_post_types_with_feature( 'comments' ), + ); + + wp_localize_script( $this->plugin_name, 'dwpb', $js_vars ); + } + + /** + * Check that we're on a specific admin page. + * + * @since 0.4.8 + * @param string $page the page slug. + * @return bool + */ + public function is_admin_page( $page ) { + global $pagenow; + return is_admin() && isset( $pagenow ) && is_string( $pagenow ) && $page . '.php' === $pagenow; + } + + /** + * Filter the comment counts to remove comments to 'post' post type. + * + * @since 0.4.0 + * @since 0.4.3 Moved everything into the post id check and updated caching functions to match wp_get_comments. + * + * @see wp_get_comments() + * @param object $comments the comment count object. + * @param int $post_id the post id. + * @return object + */ + public function filter_wp_count_comments( $comments, $post_id ) { + + // if this is grabbing all the comments, filter out the 'post' comments. + if ( 0 === $post_id ) { + + // Keep caching in place, note that on activation the plugin clears this cache. + // see class-disable-blog-activator.php. + $count = wp_cache_get( 'comments-0', 'counts' ); + if ( false !== $count ) { + return $count; + } + + $comments = $this->get_comment_counts(); + + $comments['moderated'] = $comments['awaiting_moderation']; + unset( $comments['awaiting_moderation'] ); + + $comments = (object) $comments; + wp_cache_set( 'comments-0', $comments, 'counts' ); + + } + + return $comments; + } + + /** + * Alter the comment counts on the admin comment table to remove comments associated with posts. + * + * @since 0.4.0 + * @param array $views all the views. + * @return array + */ + public function filter_admin_table_comment_count( $views ) { + + global $current_screen; + + if ( 'edit-comments' === $current_screen->id ) { + + $updated_counts = $this->get_comment_counts(); + foreach ( $views as $view => $text ) { + if ( isset( $updated_counts[ $view ] ) ) { + $views[ $view ] = preg_replace( '/\([^)]+\)/', '(' . $updated_counts[ $view ] . ')', $views[ $view ] ); + } + } + } + + return $views; + } + + /** + * Retrieve the comment counts without the 'post' comments. + * + * @since 0.4.0 + * @since 0.4.3 Removed Unused "count" function. + * @see get_comment_count() + * @see https://developer.wordpress.org/reference/functions/esc_sql/ + * @return array + */ + public function get_comment_counts() { + + global $wpdb; + + // Set up the counts, we'll be adding to this array. + $comment_count = array( + 'moderated' => 0, + 'approved' => 0, + 'awaiting_moderation' => 0, + 'spam' => 0, + 'trash' => 0, + 'post-trashed' => 0, + 'total_comments' => 0, + 'all' => 0, + ); + + // Get the post types that support comments. + $supported_post_types = dwpb_post_types_with_feature( 'comments' ); + + // Return an array of empty counts if there are no post types that support comments. + if ( empty( $supported_post_types ) || ! is_array( $supported_post_types ) ) { + return $comment_count; + } + + // Sanitizing the post type strings. + $sanitized_post_types = (array) array_map( 'esc_sql', $supported_post_types ); + + // Implode the post types into a string for the query. + $in_post_types = implode( "','", $sanitized_post_types ); + + // Grab the comments that are associated with supported post types only. + // @codingStandardsIgnoreStart -- The get_results function doesn't need a wpdb->prepare here because $in_post_types is sanitized above. + $totals = (array) $wpdb->get_results( + "SELECT comment_approved, COUNT( * ) AS total + FROM {$wpdb->comments} + WHERE comment_post_ID in ( + SELECT ID + FROM {$wpdb->posts} + WHERE post_type in ('{$in_post_types}') + AND post_status = 'publish') + GROUP BY comment_approved", + ARRAY_A + ); + // @codingStandardsIgnoreEnd + + foreach ( $totals as $row ) { + switch ( $row['comment_approved'] ) { + case 'trash': + $comment_count['trash'] = $row['total']; + break; + case 'post-trashed': + $comment_count['post-trashed'] = $row['total']; + break; + case 'spam': + $comment_count['spam'] = $row['total']; + $comment_count['total_comments'] += $row['total']; + break; + case '1': + $comment_count['approved'] = $row['total']; + $comment_count['total_comments'] += $row['total']; + $comment_count['all'] += $row['total']; + break; + case '0': + $comment_count['awaiting_moderation'] = $row['total']; + $comment_count['moderated'] = $comment_count['awaiting_moderation']; + $comment_count['total_comments'] += $row['total']; + $comment_count['all'] += $row['total']; + break; + default: + break; + } + } + + return array_map( 'intval', $comment_count ); + } + + /** + * Clear out the status of all post comments. + * + * @since 0.4.0 + * @param bool $open true if comments are open. + * @param int $post_id the post id. + * @return bool + */ + public function filter_comment_status( $open, $post_id ) { + + return ( 'post' === get_post_type( $post_id ) ) ? false : $open; + } + + /** + * Clear comments from 'post' post type. + * + * @since 0.4.0 + * @param array $comments the array of comments. + * @param int $post_id the post id. + * @return array + */ + public function filter_existing_comments( $comments, $post_id ) { + return ( 'post' === get_post_type( $post_id ) ) ? array() : $comments; + } + + /** + * Filters the default post display states used in the posts list table. + * + * @since 0.5.0 + * @param string[] $post_states An array of post display states. + * @param WP_Post $post The current post object. + * @return array + */ + public function page_post_states( $post_states, $post ) { + + if ( $this->has_front_page() && absint( get_option( 'page_for_posts' ) ) === $post->ID ) { + // translators: This string is used to indicate that the blog page is redirected to the homepage. + $post_states['dwpb-redirected'] = __( 'Redirected to the homepage', 'disable-blog' ); + } + + return $post_states; + } + + /** + * Removes the Site Health check for the post REST API. + * + * @uses dwpb_get_test_rest_availability() + * @since 0.5.0 + * @param array $tests the tests, of course. + * @return array + */ + public function site_status_tests( $tests ) { + + if ( isset( $tests['direct']['rest_availability'] ) && is_callable( array( $this, 'get_test_rest_availability' ) ) ) { + $tests['direct']['rest_availability']['test'] = array( $this, 'get_test_rest_availability' ); + } + + return $tests; + } + + /** + * Replaces the core REST Availability Site Health check. + * + * Used by the site_status_tests filter in class-disable-blog-admin.php. + * + * Copied directly from https://developer.wordpress.org/reference/classes/wp_site_health/get_test_rest_availability/ but with the 'post' type updated to 'page' in the rest url. + * + * @see https://make.wordpress.org/core/2019/04/25/site-health-check-in-5-2/ + * @since 0.5.0 + * @return array + */ + public function get_test_rest_availability() { + + $result = array( + 'label' => __( 'The REST API is available' ), + 'status' => 'good', + 'badge' => array( + 'label' => __( 'Performance' ), + 'color' => 'blue', + ), + 'description' => sprintf( + '

%s

', + __( 'The REST API is one way WordPress, and other applications, communicate with the server. One example is the block editor screen, which relies on this to display, and save, your posts and pages.' ) + ), + 'actions' => '', + 'test' => 'rest_availability', + ); + + $cookies = wp_unslash( $_COOKIE ); + $timeout = 10; + $headers = array( + 'Cache-Control' => 'no-cache', + 'X-WP-Nonce' => wp_create_nonce( 'wp_rest' ), + ); + + /** This filter is documented in wp-includes/class-wp-http-streams.php */ + $sslverify = apply_filters( 'https_local_ssl_verify', false ); + + // Include Basic auth in loopback requests. + // @codingStandardsIgnoreStart + if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { + $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); + } + // @codingStandardsIgnoreEnd + + // -- here's the money, change this from 'post' to 'page'. + $url = rest_url( 'wp/v2/types/page' ); + + // The context for this is editing with the new block editor. + $url = add_query_arg( + array( + 'context' => 'edit', + ), + $url + ); + + $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) ); + + if ( is_wp_error( $r ) ) { + $result['status'] = 'critical'; + + $result['label'] = __( 'The REST API encountered an error' ); + + $result['description'] .= sprintf( + '

%s

', + sprintf( + '%s
%s', + __( 'The REST API request failed due to an error.' ), + sprintf( + /* translators: 1: The WordPress error message. 2: The WordPress error code. */ + __( 'Error: %1$s (%2$s)' ), + $r->get_error_message(), + $r->get_error_code() + ) + ) + ); + } elseif ( 200 !== wp_remote_retrieve_response_code( $r ) ) { + $result['status'] = 'recommended'; + + $result['label'] = __( 'The REST API encountered an unexpected result' ); + + $result['description'] .= sprintf( + '

%s

', + sprintf( + /* translators: 1: The HTTP error code. 2: The HTTP error message. */ + __( 'The REST API call gave the following unexpected result: (%1$d) %2$s.' ), + wp_remote_retrieve_response_code( $r ), + esc_html( wp_remote_retrieve_body( $r ) ) + ) + ); + } else { + $json = json_decode( wp_remote_retrieve_body( $r ), true ); + + if ( false !== $json && ! isset( $json['capabilities'] ) ) { + $result['status'] = 'recommended'; + + $result['label'] = __( 'The REST API did not behave correctly' ); + + $result['description'] .= sprintf( + '

%s

', + sprintf( + /* translators: %s: The name of the query parameter being tested. */ + __( 'The REST API did not process the %s query parameter correctly.' ), + 'context' + ) + ); + } + } + + return $result; + } + + /** + * Filter the taxonomy count on post_tag and category screens. + * + * Used on the post_tag and category screens for custom post types. + * + * @since 0.5.0 + * @param array $actions an array of actions. + * @param object $tag the current taxonomy object. + * @return array + */ + public function filter_taxonomy_count( $actions, $tag ) { + + if ( isset( $tag->taxonomy, $tag->count, $tag->term_id ) + && ( 'post_tag' === $tag->taxonomy || 'category' === $tag->taxonomy ) ) { + $screen = get_current_screen(); + $count = $this->get_term_post_count_by_type( $tag->term_id, $tag->taxonomy, $screen->post_type ); + $tag->count = $count; + } + + return $actions; + } + + /** + * Return the post count for a term based by post type. + * + * @since 0.5.0 + * @param int $term_id the current term id. + * @param string $taxonomy the taxonomy slug. + * @param string $post_type the post type slug. + * @return int + */ + public function get_term_post_count_by_type( $term_id, $taxonomy, $post_type ) { + + $args = array( + 'fields' => 'ids', + 'posts_per_page' => 500, + 'post_type' => $post_type, + 'no_found_rows' => true, + 'update_post_meta_cache' => false, + 'tax_query' => array( + array( + 'taxonomy' => $taxonomy, + 'field' => 'id', + 'terms' => intval( $term_id ), + ), + ), + ); + $query = new WP_Query( $args ); + + if ( count( $query->posts ) > 0 ) { + return count( $query->posts ); + } else { + return 0; + } + } + + /** + * Remove posts column form user table. + * + * @since 0.5.0 + * @param array $columns the column slugs => title array. + * @return array + */ + public function manage_users_columns( $columns ) { + + /** + * Disable the user post column. + * + * @since 0.5.0 + * @param bool $bool True to remove the column, defaults to true. + * @return bool + */ + $disable_user_posts_column = apply_filters( 'dpwb_disable_user_post_column', true ); + + if ( isset( $columns['posts'] ) && true === (bool) $disable_user_posts_column ) { + unset( $columns['posts'] ); + } + + // Get the current screen. + $screen = get_current_screen(); + + // cycle through the post types to be displayed. + $post_types = $this->user_column_post_types(); + foreach ( $post_types as $post_type ) { + /** + * Create a new column for 'pages' similar to the original 'post' column. + * + * @since 0.5.0 + * @param bool $bool True to remove the column, defaults to true. + * @return bool + */ + if ( apply_filters( "dpwb_create_user_{$post_type}_column", true ) + // Taken from core functions for users page, don't display the posts column on site-users-network core page. + // see wp-admin/includes/class-wp-users-list-table.php. + && isset( $screen->id ) && 'site-users-network' !== $screen->id ) { + + $post_type_obj = get_post_type_object( $post_type ); + if ( isset( $post_type_obj->labels->name ) ) { + $columns[ $post_type ] = $post_type_obj->labels->name; + } + } + } + + return $columns; + } + + /** + * Mange the new custom user columns. + * + * @since 0.5.0 + * @see wp-admin/includes/class-wp-users-list-table.php. + * @param string $output the column output. + * @param string $column_name the current column slug. + * @param int $user_id the current user's id. + * @return string + */ + public function manage_users_custom_column( $output, $column_name, $user_id ) { + + $post_types = $this->user_column_post_types(); + foreach ( $post_types as $post_type ) { + // Create new column output, mimicking core's 'post' column but for the 'page' and supported custom post types. + if ( $post_type === $column_name ) { + + // make sure we can grab valid post type labels before continuing. + $post_type_obj = get_post_type_object( $post_type ); + if ( ! isset( $post_type_obj->labels->name, $post_type_obj->labels->singular_name ) ) { + continue; + } + + // Get the couunts. + $page_counts = count_many_users_posts( array( $user_id ), $post_type ); + $page_count = absint( $page_counts[ $user_id ] ); + + // Note that we have to explicitly add the query variable for post type in order + // to avoid it being stripped by the admin redirect on the edit.php page. + // @codingStandardsIgnoreStart - phpcs doesn't like the mismatched placeholders, but it works. + $output = sprintf( + '%s', + "edit.php?post_type={$post_type}&author={$user_id}", + $page_count, + sprintf( + // translators: %1$s: Number of pieces of content by this author. %2$s and %3$s are the singular and plural names, respectively, for the content type. For example: "1 page by this author" and "2 pages by this author". + _n( '%1$s %2$s by this author', '%1$s %3$s by this author', $page_count, 'disable-blog' ), + number_format_i18n( $page_count ), + $post_type_obj->labels->singular_name, + $post_type_obj->labels->name + ) + ); + // @codingStandardsIgnoreEnd + } + } + + return $output; + } + + /** + * Grab the post types that + * + * @since 0.5.0 + * @return array + */ + private function user_column_post_types() { + + // Include any post types using author archives. + $post_types = $this->functions->author_archive_post_types(); + + // The author_archive_post_type function returns false if empty, but we need an array. + $post_types = empty( $post_types ) ? array() : $post_types; + + // Also include pages, which is not in the author archive by default, + // however we run array_unique in case the 'page' post type has been added + // to the author archives. + $post_types = array_unique( array_merge( array( 'page' ), $post_types ) ); + + /** + * Filter the post types that appear in the user table. + * + * @since 0.5.0 + * @param array $post_types an array of post type slugs. + * @return array + */ + return apply_filters( 'dwpb_admin_user_post_types', $post_types ); + } + + /** + * Remove the user's "view" link if we are not supporting author archives. + * + * @since 0.5.0 + * @param array $actions an array of actions in a key => output format. + * @param object $user_object the current user, WP_User object. + * @return array + */ + public function user_row_actions( $actions, $user_object ) { + + if ( true === $this->functions->disable_author_archives() + && isset( $actions['view'] ) ) { + unset( $actions['view'] ); + } + + return $actions; + } + + /** + * Alter the customizer view to mimic the reading settings. + * + * @since 0.5.0 + * @return void + */ + public function customizer_styles() { + + if ( $this->has_front_page() ) { + ?> + + plugin_name . '-customizer-scripts', DWPB_URL . 'assets/js/disable-blog-customizer.js', array( 'jquery', 'customize-controls' ), $this->version, true ); + + // Localize some information on the page. + $js_vars = array( + // translators: This text appears in the Customizer > Homepage Settings and provides the context for the homepage select field there. + 'homepageSettingsText' => __( 'You can choose what\'s displayed on the homepage of your site. To set a static homepage, create or select the page below.', 'disable-blog' ), + ); + wp_localize_script( $this->plugin_name . '-customizer-scripts', 'dwpbCustomizer', $js_vars ); + } + + /** + * Remove the default posts page notice and replace it with a new one. + * + * @since 0.5.0 + * @return void + */ + public function update_posts_page_notice() { + + if ( (int) get_option( 'page_for_posts' ) === get_the_ID() ) { + remove_action( 'edit_form_after_title', '_wp_posts_page_notice' ); + add_action( 'edit_form_after_title', array( $this, 'posts_page_notice' ) ); + } + } + + /** + * Replaces the default blog page posts notice. + * + * @since 0.5.0 + * @return void + */ + public function posts_page_notice() { + + // translators: this notice informs the user why the blog page editor is disabled and that it is redirected to the homepage. + echo '

' . __( 'You are currently editing the page that shows your latest posts, which is redirected to the homepage because the blog is disabled.', 'disable-blog' ) . '

'; // phpcs:ignore + } + + /** + * Filter the available tags in the permalink settings. + * + * @since 0.5.1 + * @param array $available_tags The available permalink tags. + * @return array + */ + public function available_permalink_structure_tags( $available_tags ) { + + // Remove the category permalink structure if categories are not supported. + if ( isset( $available_tags['category'] ) && ! dwpb_post_types_with_tax( 'category' ) ) { + unset( $available_tags['category'] ); + } + + // Remove the author tag if author archives are disabled. + if ( isset( $available_tags['author'] ) && $this->functions->disable_author_archives() ) { + unset( $available_tags['author'] ); + } + + return $available_tags; + } +} diff --git a/includes/class-disable-blog-i18n.php b/includes/class-disable-blog-i18n.php index 01cc9a3..7dffd1f 100644 --- a/includes/class-disable-blog-i18n.php +++ b/includes/class-disable-blog-i18n.php @@ -1,38 +1,37 @@ - - */ - -/** - * Define the internationalization functionality. - * - * Loads and defines the internationalization files for this plugin - * so that it is ready for translation. - * - * @since 0.4.0 - */ -class Disable_Blog_I18n { - - /** - * Load the plugin text domain for translation. - * - * @since 0.4.0 - */ - public function load_plugin_textdomain() { - - load_plugin_textdomain( - 'disable-blog', - false, - dirname( plugin_basename( __FILE__ ), 2 ) . '/languages/' - ); - } -} + + */ + +/** + * Define the internationalization functionality. + * + * Loads and defines the internationalization files for this plugin + * so that it is ready for translation. + * + * @since 0.4.0 + */ +class Disable_Blog_I18n { + + /** + * Load the plugin text domain for translation. + * + * @since 0.4.0 + */ + public function load_plugin_textdomain() { + load_plugin_textdomain( + 'disable-blog', + false, + dirname( plugin_basename( __FILE__ ), 2 ) . '/languages/' + ); + } +} diff --git a/includes/class-disable-blog-loader.php b/includes/class-disable-blog-loader.php index 087aff2..9a82942 100644 --- a/includes/class-disable-blog-loader.php +++ b/includes/class-disable-blog-loader.php @@ -1,188 +1,187 @@ - - */ - -/** - * Register all actions and filters for the plugin. - * - * Maintain a list of all hooks that are registered throughout - * the plugin, and register them with the WordPress API. Call the - * run function to execute the list of actions and filters. - * - * @since 0.4.0 - */ -class Disable_Blog_Loader { - - /** - * The array of actions registered with WordPress. - * - * @since 0.4.0 - * @access protected - * @var array $actions The actions registered with WordPress to fire when the plugin loads. - */ - protected $actions; - - /** - * The array of filters registered with WordPress. - * - * @since 0.4.0 - * @access protected - * @var array $filters The filters registered with WordPress to fire when the plugin loads. - */ - protected $filters; - - /** - * Initialize the collections used to maintain the actions and filters. - * - * @since 0.4.0 - */ - public function __construct() { - - $this->actions = array(); - $this->filters = array(); - } - - /** - * Simple Autoloader. - * - * @since 0.5.1 - * @param string $requested_class The Class to autoload. - */ - public function autoloader( $requested_class ) { - - $requested_class = 'class-' . str_replace( '_', '-', strtolower( $requested_class ) ) . '.php'; - $path = plugin_dir_path( __DIR__ ); - $sources = array( 'includes' ); - - foreach ( $sources as $source ) { - if ( file_exists( $path . $source . '/' . $requested_class ) ) { - include $path . $source . '/' . $requested_class; - } - } - } - - /** - * Add a new action to the collection to be registered with WordPress. - * - * @since 0.4.0 - * @param string $hook The name of the WordPress action that is being registered. - * @param object $component A reference to the instance of the object on which the action is defined. - * @param string $callback The name of the function definition on the $component. - * @param int $priority Optional. he priority at which the function should be fired. Default is 10. - * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1. - */ - public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { - $this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args ); - } - - /** - * Add a new filter to the collection to be registered with WordPress. - * - * @since 0.4.0 - * @param string $hook The name of the WordPress filter that is being registered. - * @param object $component A reference to the instance of the object on which the filter is defined. - * @param string $callback The name of the function definition on the $component. - * @param int $priority Optional. he priority at which the function should be fired. Default is 10. - * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1. - */ - public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { - $this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args ); - } - - /** - * A utility function that is used to register the actions and hooks into a single - * collection. - * - * @since 0.4.0 - * @access private - * @param array $hooks The collection of hooks that is being registered (that is, actions or filters). - * @param string $hook The name of the WordPress filter that is being registered. - * @param object $component A reference to the instance of the object on which the filter is defined. - * @param string $callback The name of the function definition on the $component. - * @param int $priority The priority at which the function should be fired. - * @param int $accepted_args The number of arguments that should be passed to the $callback. - * @return array The collection of actions and filters registered with WordPress. - */ - private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args ) { - - $hooks[] = array( - 'hook' => $hook, - 'component' => $component, - 'callback' => $callback, - 'priority' => $priority, - 'accepted_args' => $accepted_args, - ); - - return $hooks; - } - - /** - * Remove a filter from the collection registered with WordPress. - * - * @since 0.5.1 - * @param string $tag The filter hook to which the function to be removed is hooked. - * @param string $class_name Class name registering the filter callback. - * @param string $method_to_remove Method name for the filter's callback. - * @param int $priority The priority of the method (default 10). - * - * @return bool $removed Whether the function is removed. - */ - public function remove_filter( $tag, $class_name = '', $method_to_remove = '', $priority = 10 ) { - - global $wp_filter; - $removed = false; - - foreach ( $wp_filter[ $tag ]->callbacks as $filter_priority => $filters ) { - - if ( $filter_priority === $priority ) { - foreach ( $filters as $filter ) { - if ( $filter['function'][1] === $method_to_remove - && is_object( $filter['function'][0] ) // only WP 4.7 and above. This plugin is requiring at least WP 4.9. - && $filter['function'][0] instanceof $class_name ) { - $removed = $wp_filter[ $tag ]->remove_filter( $tag, array( $filter['function'][0], $method_to_remove ), $priority ); - } - } - } - } - - return $removed; - } - - /** - * Remove an action from the collection registered with WordPress. - * - * @since 0.5.1 - * @param string $tag The filter hook to which the function to be removed is hooked. - * @param string $class_name Class name registering the filter callback. - * @param string $method_to_remove Method name for the filter's callback. - * @param int $priority The priority of the method (default 10). - * - * @return bool $removed Whether the function is removed. - */ - public function remove_action( $tag, $class_name = '', $method_to_remove = '', $priority = 10 ) { - return $this->remove_filter( $tag, $class_name, $method_to_remove, $priority ); - } - - /** - * Register the filters and actions with WordPress. - * - * @since 0.4.0 - */ - public function run() { - - foreach ( $this->filters as $hook ) { - add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); - } - - foreach ( $this->actions as $hook ) { - add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); - } - } -} + + */ + +/** + * Register all actions and filters for the plugin. + * + * Maintain a list of all hooks that are registered throughout + * the plugin, and register them with the WordPress API. Call the + * run function to execute the list of actions and filters. + * + * @since 0.4.0 + */ +class Disable_Blog_Loader { + + /** + * The array of actions registered with WordPress. + * + * @since 0.4.0 + * @access protected + * @var array $actions The actions registered with WordPress to fire when the plugin loads. + */ + protected $actions; + + /** + * The array of filters registered with WordPress. + * + * @since 0.4.0 + * @access protected + * @var array $filters The filters registered with WordPress to fire when the plugin loads. + */ + protected $filters; + + /** + * Initialize the collections used to maintain the actions and filters. + * + * @since 0.4.0 + */ + public function __construct() { + $this->actions = array(); + $this->filters = array(); + } + + /** + * Simple Autoloader. + * + * @since 0.5.1 + * @param string $requested_class The Class to autoload. + */ + public function autoloader( $requested_class ) { + + $requested_class = 'class-' . str_replace( '_', '-', strtolower( $requested_class ) ) . '.php'; + $path = plugin_dir_path( __DIR__ ); + $sources = array( 'includes' ); + + foreach ( $sources as $source ) { + if ( file_exists( $path . $source . '/' . $requested_class ) ) { + include $path . $source . '/' . $requested_class; + } + } + } + + /** + * Add a new action to the collection to be registered with WordPress. + * + * @since 0.4.0 + * @param string $hook The name of the WordPress action that is being registered. + * @param object $component A reference to the instance of the object on which the action is defined. + * @param string $callback The name of the function definition on the $component. + * @param int $priority Optional. he priority at which the function should be fired. Default is 10. + * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1. + */ + public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { + $this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args ); + } + + /** + * Add a new filter to the collection to be registered with WordPress. + * + * @since 0.4.0 + * @param string $hook The name of the WordPress filter that is being registered. + * @param object $component A reference to the instance of the object on which the filter is defined. + * @param string $callback The name of the function definition on the $component. + * @param int $priority Optional. he priority at which the function should be fired. Default is 10. + * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1. + */ + public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { + $this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args ); + } + + /** + * A utility function that is used to register the actions and hooks into a single + * collection. + * + * @since 0.4.0 + * @access private + * @param array $hooks The collection of hooks that is being registered (that is, actions or filters). + * @param string $hook The name of the WordPress filter that is being registered. + * @param object $component A reference to the instance of the object on which the filter is defined. + * @param string $callback The name of the function definition on the $component. + * @param int $priority The priority at which the function should be fired. + * @param int $accepted_args The number of arguments that should be passed to the $callback. + * @return array The collection of actions and filters registered with WordPress. + */ + private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args ) { + + $hooks[] = array( + 'hook' => $hook, + 'component' => $component, + 'callback' => $callback, + 'priority' => $priority, + 'accepted_args' => $accepted_args, + ); + + return $hooks; + } + + /** + * Remove a filter from the collection registered with WordPress. + * + * @since 0.5.1 + * @param string $tag The filter hook to which the function to be removed is hooked. + * @param string $class_name Class name registering the filter callback. + * @param string $method_to_remove Method name for the filter's callback. + * @param int $priority The priority of the method (default 10). + * + * @return bool $removed Whether the function is removed. + */ + public function remove_filter( $tag, $class_name = '', $method_to_remove = '', $priority = 10 ) { + + global $wp_filter; + $removed = false; + + foreach ( $wp_filter[ $tag ]->callbacks as $filter_priority => $filters ) { + + if ( $filter_priority === $priority ) { + foreach ( $filters as $filter ) { + if ( $filter['function'][1] === $method_to_remove + && is_object( $filter['function'][0] ) // only WP 4.7 and above. This plugin is requiring at least WP 4.9. + && $filter['function'][0] instanceof $class_name ) { + $removed = $wp_filter[ $tag ]->remove_filter( $tag, array( $filter['function'][0], $method_to_remove ), $priority ); + } + } + } + } + + return $removed; + } + + /** + * Remove an action from the collection registered with WordPress. + * + * @since 0.5.1 + * @param string $tag The filter hook to which the function to be removed is hooked. + * @param string $class_name Class name registering the filter callback. + * @param string $method_to_remove Method name for the filter's callback. + * @param int $priority The priority of the method (default 10). + * + * @return bool $removed Whether the function is removed. + */ + public function remove_action( $tag, $class_name = '', $method_to_remove = '', $priority = 10 ) { + return $this->remove_filter( $tag, $class_name, $method_to_remove, $priority ); + } + + /** + * Register the filters and actions with WordPress. + * + * @since 0.4.0 + */ + public function run() { + + foreach ( $this->filters as $hook ) { + add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); + } + + foreach ( $this->actions as $hook ) { + add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); + } + } +} diff --git a/includes/class-disable-blog-public.php b/includes/class-disable-blog-public.php index e2dd994..659db75 100644 --- a/includes/class-disable-blog-public.php +++ b/includes/class-disable-blog-public.php @@ -1,569 +1,566 @@ -plugin_name = $plugin_name; - $this->version = $version; - $this->functions = new Disable_Blog_Functions(); - } - - /** - * Redirect single post pages - * - * @uses dwpb_post_types_with_tax() - * @link http://codex.wordpress.org/Plugin_API/Action_Reference/template_redirect - * @since 0.2.0 - * @since 0.4.9 added sitemap checks to avoid redirects on new sitemaps in WP v5.5. - * @since 0.5.0 renamed to redirect_public_pages - * @return void - */ - public function redirect_public_pages() { - - // Don't redirect on admin or sitemap, and only if there is a homepage to redirect to. - $sitemap = get_query_var( 'sitemap', false ); - $sitemap_styelsheet = get_query_var( 'sitemap-stylesheet', false ); - if ( is_admin() - || ! get_option( 'page_on_front' ) - || ! empty( $sitemap ) - || ! empty( $sitemap_styelsheet ) ) { - return; - } - - // Get the front page id and url. - $page_id = get_option( 'page_on_front' ); - $homepage_url = get_permalink( $page_id ); - $redirect_url = false; - - // The public pages to potentially be redirected. - global $post; - $public_redirects = array( - 'post' => ( $post instanceof WP_Post && is_singular( 'post' ) ), - 'post_tag_archive' => ( is_tag() && ! dwpb_post_types_with_tax( 'post_tag' ) ), - 'category_archive' => ( is_category() && ! dwpb_post_types_with_tax( 'category' ) ), - 'blog_page' => is_home(), - 'date_archive' => is_date(), - 'author_archive' => ( is_author() && true === $this->functions->disable_author_archives() ), - ); - - // cycle through each public page, checking if we need to redirect. - foreach ( $public_redirects as $filtername => $bool ) { - - // If this is the right page, then setup the redirect url. - if ( true === $bool ) { - - // Custom function within this class used to check if the page needs to be redirected. - $filter = 'dwpb_redirect_' . $filtername; - - /** - * The redirect url used for this public page. - * - * Example: use 'dwpb_redirect_post' to change the redirect url used - * on a post, or 'dwpb_redirect_post_tag_archive' to redirect tag archives. - * - * @since 0.4.0 - * @since 0.5.0 combine filters. - * @param string $url the url to redirect to, defaults to homepage. - */ - $redirect_url = apply_filters( $filter, $homepage_url ); - - break; // no need to keep looping. - - } // end if - } // end foreach - - // Only continue if we have a redirect url. - if ( ! $redirect_url ) { - return; - } - - /** - * Filter to toggle the plugin's front-end redirection. - * - * @since 0.2.0 - * @since 0.4.0 added the current_url param. - * @since 0.5.0 removed 'redirect_url' && 'current_url' params. - * @param bool $bool True to enable, false to disable. - */ - if ( apply_filters( 'dwpb_redirect_front_end', true ) ) { - - /** - * Global public url redirect filter. - * - * @since 0.5.0 - * @param string $redirect_url The redirect url. - */ - $redirect_url = apply_filters( 'dwpb_front_end_redirect_url', $redirect_url ); - - $this->functions->redirect( $redirect_url ); - } - } - - /** - * Modify query. - * - * Remove 'post' post type from any searches and archives. - * - * @uses $this->remove_post_from_array_in_query - * - * @link http://codex.wordpress.org/Plugin_API/Action_Reference/template_redirect - * @link http://stackoverflow.com/questions/7225070/php-array-delete-by-value-not-key#7225113 - * - * @since 0.2.0 - * @since 0.4.0 added remove_post_from_array_in_query function - * @since 0.4.9 remove 'post' from all archives. - * @since 0.4.10 update to just remove 'post' from built-in taxonomy archives, - * @since 0.5.0 remove 'post' type from author archives. - * @param object $query the query object. - * @return void - */ - public function modify_query( $query ) { - - // Bail if we're in the admin or not on the main query. - if ( is_admin() || ! $query->is_main_query() ) { - return; - } - - // Let's see if there are any post types supporting build-in taxonomies. - $tag_post_types = dwpb_post_types_with_tax( 'post_tag' ); - $category_post_types = dwpb_post_types_with_tax( 'category' ); - $author_post_types = $this->functions->author_archive_post_types(); - - // Remove existing posts from built-in taxonomy archives, if they are supported by another post type. - if ( $query->is_tag() && $tag_post_types ) { // tag archives. - - $this->set_post_types_in_query( $query, $tag_post_types, 'dwpb_tag_post_types' ); - - } elseif ( $query->is_category() && $category_post_types ) { // category archives. - - $this->set_post_types_in_query( $query, $category_post_types, 'dwpb_category_post_types' ); - - } elseif ( $query->is_author() && ! empty( $author_post_types ) ) { // author archives, if supported, have a filter for setting the post types. - - $this->set_post_types_in_query( $query, $author_post_types ); - - } - } - - /** - * Set post types for tag and category archive queries, excluding 'post' as the default type. - * - * Used in $this->modify_query to remove 'post' type from built-in archive queries. - * - * @since 0.4.0 - * @param object $query the main query object. - * @param array $post_types the array of post types. - * @param string $filter the filter to be applied. - * @return bool - */ - public function set_post_types_in_query( $query, $post_types = array(), $filter = '' ) { - - /** - * If there is a filter name passed, then a filter is applied on the array and query. - * - * Used for 'dwpb_tag_post_types' and 'dwpb_category_post_types' filters. - * Note that the 'dwpb_author_archive_post_types' filter is passed in another function, - * hence the reason $filter can be empty and not passed in this function. - * - * @see Disable_Blog_Public->modify_query - * @since 0.4.0 - * @since 0.4.10 fix bug in 0.4.9 causing cpt weirdness, now always using the filter. - * @since 0.5.0 made the filter part of this function optional, since the author - * post type filter is located in the functions class. - * @param array $post_types An array of post type slugs. - * @param object $query The query object being modified. - * @return array - */ - if ( ! empty( $filter ) ) { - $set_to = apply_filters( $filter, $post_types, $query ); - } else { - $set_to = $post_types; - } - if ( ! empty( $set_to ) && method_exists( $query, 'set' ) && is_array( $set_to ) ) { - $query->set( 'post_type', $set_to ); - return true; - } - - return false; - } - - /** - * Disable Blog feeds. - * - * @since 0.1.0 - * @since 0.4.0 add $is_comment_feed variable to feeds and check $is_comment_feed prior to redirect. - * @param bool $is_comment_feed true if a comment feed. - * @return void - */ - public function disable_feed( $is_comment_feed ) { - - // If this is a comment feed and comments are supported by other post types, bail. - if ( $is_comment_feed && dwpb_post_types_with_feature( 'comments' ) ) { - return; - } - - // Option to override this via filter and check to confirm post type. - global $post; - - // Check that we're disabling feeds and everything is good to go. - if ( $this->functions->disable_feeds( $post, $is_comment_feed ) && isset( $post->post_type ) && 'post' === $post->post_type ) { - - /** - * Filter the feed redirect url. - * - * @since 0.4.0 - * @param string $url The redirect url (defaults to homepage) - * @param object $post The global post object. - * @param bool $is_comment_feed True if the feed is a comment feed. - */ - $redirect_url = apply_filters( 'dwpb_redirect_feeds', home_url(), $post, $is_comment_feed ); - - /** - * Filter to toggle on a message instead of a redirect. - * - * Defaults to false, so a redirect is the expacted default behavior. - * - * @since 0.4.0 - * @since 0.4.9 updated variables passed to match other feed filters, - * previously only $is_comment_feed was passed and now - * the order is: bool, $post, $is_comment_feed. - * Note that if you used this filter before - * and relied on the $is_comment_feed, you'll need to update. - * @param bool $bool True to use a message, false to redirect. - * @param object $post Global post object. - * @param bool $is_comment_feed True if the feed is a comment feed. - */ - if ( apply_filters( 'dwpb_feed_message', false, $post, $is_comment_feed ) ) { - - // translators: This message appears when the feed is disabled instead of redirect, it should point to the homepage. - $message = sprintf( '%s: %s', __( 'No feed available, please visit our homepage:', 'disable-blog' ), esc_url_raw( $redirect_url ), esc_url_raw( $redirect_url ) ); - - /** - * Filter the feed die message. - * - * If the `dwpb_feed_message` is set to true, use this filter to set a custom message. - * - * @since 0.4.0 - * @param string $message - */ - $message = apply_filters( 'dwpb_feed_die_message', $message ); - $allowed_html = array( - 'a' => array( - 'href' => array(), - 'name' => array(), - 'id' => array(), - ), - ); - wp_die( wp_kses( $message, $allowed_html ) ); - - } else { // Default option: redirect to homepage. - - $this->functions->redirect( $redirect_url ); - - } - } - } - - /** - * Turn off the feed link. - * - * Only works for WordPress >= 4.4.0. - * - * @since 0.4.0 - * @param bool $show true to show the posts feed link. - * @return bool - */ - public function feed_links_show_posts_feed( $show ) { - - return false; - } - - /** - * Turn off the comment's feed link. - * - * Only works for WordPress >= 4.4.0. - * - * @since 0.4.0 - * @param bool $show true to show the comments feed link. - * @return bool - */ - public function feed_links_show_comments_feed( $show ) { - - // If 'post' type is the only type supporting comments, then disable the comment feed link. - if ( ! dwpb_post_types_with_feature( 'comments' ) ) { - $show = false; - } - - return $show; - } - - /** - * Remove feed urls from head. - * - * @since 0.4.9 - * @return void - */ - public function header_feeds() { - - // Various feed links. - $feed = array( - 'feed_links' => 2, - 'feed_links_extra' => 3, - 'rsd_link' => 10, - 'wlwmanifest_link' => 10, - ); - - // Remove from head. - foreach ( $feed as $function => $priority ) { - remove_action( 'wp_head', $function, $priority ); - } - } - - /** - * Unset all post-related xmlrpc methods. - * - * @see wp-includes/class-wp-xmlrpc-server.php - * @since 0.4.9 - * @param array $methods the arrayve of xmlrpc methods. - * @return array - */ - public function xmlrpc_methods( $methods ) { - - $methods_to_remove = $this->get_disabled_xmlrpc_methods(); - - if ( ! empty( $methods_to_remove ) && is_array( $methods_to_remove ) ) { - foreach ( $methods_to_remove as $method ) { - if ( isset( $methods[ $method ] ) ) { - unset( $methods[ $method ] ); - } - } - } - - return $methods; - } - - /** - * Get the XML-RPC methods to disable. - * - * @since 0.5.0 - * @return array|bool - */ - private function get_disabled_xmlrpc_methods() { - - // The methods to remove. - $methods_to_remove = array( - 'wp.getUsersBlogs', - 'wp.newPost', - 'wp.editPost', - 'wp.deletePost', - 'wp.getPost', - 'wp.getPosts', - 'blogger.getPost', - 'blogger.getRecentPosts', - 'blogger.newPost', - 'blogger.editPost', - 'blogger.deletePost', - 'metaWeblog.newPost', - 'metaWeblog.editPost', - 'metaWeblog.getPost', - 'metaWeblog.getRecentPosts', - 'metaWeblog.deletePost', - 'mt.getRecentPostTitles', - 'mt.getTrackbackPings', - 'mt.publishPost', - 'pingback.ping', - 'pingback.extensions.getPingbacks', - 'system.multicall', - 'system.listMethods', - 'system.getCapabilities', - 'demo.sayHello', - 'demo.addTwoNumbers', - ); - - // Remove category / post tag terms, if not supported by another post type. - $taxonomy_methods = array(); - if ( ! dwpb_post_types_with_tax( 'category' ) ) { - $taxonomy_methods = array( - 'wp.newCategory', - 'wp.deleteeCategory', - 'mt.getCategoryList', - 'wp.suggestCategories', - 'mt.getPostCategories', - 'mt.setPostCategories', - 'metaWeblog.getCategories', - ); - } - if ( ! dwpb_post_types_with_tax( 'post_tag' ) ) { - $taxonomy_methods[] = 'wp.getTags'; - } - - $methods_to_remove = array_merge( $methods_to_remove, $taxonomy_methods ); - - /** - * Filter the methods being disabled by the plugin. - * - * Return false to disable this functionality entirely and keep all methods in place. - * - * @since 0.5.0 - * @param array $methods_to_remove an array of all the XMLRPC methods to disable. - * @return array|bool - */ - $methods_to_remove = apply_filters( 'dwpb_disabled_xmlrpc_methods', $methods_to_remove ); - - // filter any invalid entries out before returning the array. - return is_array( $methods_to_remove ) ? array_filter( $methods_to_remove, 'is_string' ) : false; // phpcs:ignore - } - - /** - * Remove the X-Pingback HTTP header. - * - * @since 0.4.0 - * @since 0.5.1 moved to the public class. - * @param array $headers the pingback headers. - * @return array - */ - public function filter_wp_headers( $headers ) { - - /** - * Toggle the disable pinback header feature. - * - * @since 0.4.0 - * @param bool $bool True to disable the header, false to keep it. - */ - if ( apply_filters( 'dwpb_remove_pingback_header', true ) && isset( $headers['X-Pingback'] ) ) { - unset( $headers['X-Pingback'] ); - } - - return $headers; - } - - /** - * Remove 'post' post type from sitemaps. - * - * @since 0.4.9 - * @param array $post_types an array of post type strings supported in sitemaps. - * @return array - */ - public function wp_sitemaps_post_types( $post_types ) { - - if ( isset( $post_types['post'] ) ) { - unset( $post_types['post'] ); - } - - return $post_types; - } - - /** - * Conditionally remove built-in taxonomies from sitemaps, if they are not being used by a custom post type. - * - * @since 0.4.9 - * @uses dwpb_post_types_with_tax() - * @param array $taxonomies an array of taxonomy strings supported in sitemaps. - * @return array - */ - public function wp_sitemaps_taxonomies( $taxonomies ) { - - $built_in_taxonomies = array( - 'post_tag', - 'category', - ); - foreach ( $built_in_taxonomies as $tax ) { - if ( isset( $taxonomies[ $tax ] ) && ! dwpb_post_types_with_tax( $tax ) ) { - unset( $taxonomies[ $tax ] ); - } - } - - return $taxonomies; - } - - /** - * Remove author sitemaps. - * - * @since 0.5.0 - * @link https://developer.wordpress.org/reference/hooks/wp_sitemaps_add_provider/ - * @param object $provider Instance of a WP_Sitemaps_Provider. - * @param string $name Name of the sitemap provider. - * @return object|bool Instance of a WP_Sitemaps_Provider or false. - */ - public function wp_author_sitemaps( $provider, $name ) { - - // If there are no author archives, then don't show the sitemap. - $disable_author_archives = $this->functions->disable_author_archives(); - if ( true === $disable_author_archives ) { - - $disable_sitemap = true; - - } else { // Otherwise, we may show it? - - // Check if we have any post types supporting author archives. - $author_archives_supported = $this->functions->author_archive_post_types(); - - // Only show the sitemap if there are post types support on the archives. - $disable_sitemap = empty( $author_archives_supported ); - - } - - /** - * Turn off user/author sitemaps. - * - * @since 0.5.0 - * @param bool $bool True to disable, defaults to true. - * @return bool - */ - if ( 'users' === $name && apply_filters( 'dwpb_disable_user_sitemap', $disable_sitemap ) ) { - return false; - } - - return $provider; - } -} +plugin_name = $plugin_name; + $this->version = $version; + $this->functions = new Disable_Blog_Functions(); + } + + /** + * Redirect single post pages + * + * @uses dwpb_post_types_with_tax() + * @link http://codex.wordpress.org/Plugin_API/Action_Reference/template_redirect + * @since 0.2.0 + * @since 0.4.9 added sitemap checks to avoid redirects on new sitemaps in WP v5.5. + * @since 0.5.0 renamed to redirect_public_pages + * @return void + */ + public function redirect_public_pages() { + + // Don't redirect on admin or sitemap, and only if there is a homepage to redirect to. + $sitemap = get_query_var( 'sitemap', false ); + $sitemap_styelsheet = get_query_var( 'sitemap-stylesheet', false ); + if ( is_admin() + || ! get_option( 'page_on_front' ) + || ! empty( $sitemap ) + || ! empty( $sitemap_styelsheet ) ) { + return; + } + + // Get the front page id and url. + $page_id = get_option( 'page_on_front' ); + $homepage_url = get_permalink( $page_id ); + $redirect_url = false; + + // The public pages to potentially be redirected. + global $post; + $public_redirects = array( + 'post' => ( $post instanceof WP_Post && is_singular( 'post' ) ), + 'post_tag_archive' => ( is_tag() && ! dwpb_post_types_with_tax( 'post_tag' ) ), + 'category_archive' => ( is_category() && ! dwpb_post_types_with_tax( 'category' ) ), + 'blog_page' => is_home(), + 'date_archive' => is_date(), + 'author_archive' => ( is_author() && true === $this->functions->disable_author_archives() ), + ); + + // cycle through each public page, checking if we need to redirect. + foreach ( $public_redirects as $filtername => $bool ) { + + // If this is the right page, then setup the redirect url. + if ( true === $bool ) { + + // Custom function within this class used to check if the page needs to be redirected. + $filter = 'dwpb_redirect_' . $filtername; + + /** + * The redirect url used for this public page. + * + * Example: use 'dwpb_redirect_post' to change the redirect url used + * on a post, or 'dwpb_redirect_post_tag_archive' to redirect tag archives. + * + * @since 0.4.0 + * @since 0.5.0 combine filters. + * @param string $url the url to redirect to, defaults to homepage. + */ + $redirect_url = apply_filters( $filter, $homepage_url ); + + break; // no need to keep looping. + + } // end if + } // end foreach + + // Only continue if we have a redirect url. + if ( ! $redirect_url ) { + return; + } + + /** + * Filter to toggle the plugin's front-end redirection. + * + * @since 0.2.0 + * @since 0.4.0 added the current_url param. + * @since 0.5.0 removed 'redirect_url' && 'current_url' params. + * @param bool $bool True to enable, false to disable. + */ + if ( apply_filters( 'dwpb_redirect_front_end', true ) ) { + + /** + * Global public url redirect filter. + * + * @since 0.5.0 + * @param string $redirect_url The redirect url. + */ + $redirect_url = apply_filters( 'dwpb_front_end_redirect_url', $redirect_url ); + + $this->functions->redirect( $redirect_url ); + } + } + + /** + * Modify query. + * + * Remove 'post' post type from any searches and archives. + * + * @uses $this->remove_post_from_array_in_query + * + * @link http://codex.wordpress.org/Plugin_API/Action_Reference/template_redirect + * @link http://stackoverflow.com/questions/7225070/php-array-delete-by-value-not-key#7225113 + * + * @since 0.2.0 + * @since 0.4.0 added remove_post_from_array_in_query function + * @since 0.4.9 remove 'post' from all archives. + * @since 0.4.10 update to just remove 'post' from built-in taxonomy archives, + * @since 0.5.0 remove 'post' type from author archives. + * @param object $query the query object. + * @return void + */ + public function modify_query( $query ) { + + // Bail if we're in the admin or not on the main query. + if ( is_admin() || ! $query->is_main_query() ) { + return; + } + + // Let's see if there are any post types supporting build-in taxonomies. + $tag_post_types = dwpb_post_types_with_tax( 'post_tag' ); + $category_post_types = dwpb_post_types_with_tax( 'category' ); + $author_post_types = $this->functions->author_archive_post_types(); + + // Remove existing posts from built-in taxonomy archives, if they are supported by another post type. + if ( $query->is_tag() && $tag_post_types ) { // tag archives. + + $this->set_post_types_in_query( $query, $tag_post_types, 'dwpb_tag_post_types' ); + + } elseif ( $query->is_category() && $category_post_types ) { // category archives. + + $this->set_post_types_in_query( $query, $category_post_types, 'dwpb_category_post_types' ); + + } elseif ( $query->is_author() && ! empty( $author_post_types ) ) { // author archives, if supported, have a filter for setting the post types. + + $this->set_post_types_in_query( $query, $author_post_types ); + + } + } + + /** + * Set post types for tag and category archive queries, excluding 'post' as the default type. + * + * Used in $this->modify_query to remove 'post' type from built-in archive queries. + * + * @since 0.4.0 + * @param object $query the main query object. + * @param array $post_types the array of post types. + * @param string $filter the filter to be applied. + * @return bool + */ + public function set_post_types_in_query( $query, $post_types = array(), $filter = '' ) { + + /** + * If there is a filter name passed, then a filter is applied on the array and query. + * + * Used for 'dwpb_tag_post_types' and 'dwpb_category_post_types' filters. + * Note that the 'dwpb_author_archive_post_types' filter is passed in another function, + * hence the reason $filter can be empty and not passed in this function. + * + * @see Disable_Blog_Public->modify_query + * @since 0.4.0 + * @since 0.4.10 fix bug in 0.4.9 causing cpt weirdness, now always using the filter. + * @since 0.5.0 made the filter part of this function optional, since the author + * post type filter is located in the functions class. + * @param array $post_types An array of post type slugs. + * @param object $query The query object being modified. + * @return array + */ + if ( ! empty( $filter ) ) { + $set_to = apply_filters( $filter, $post_types, $query ); + } else { + $set_to = $post_types; + } + if ( ! empty( $set_to ) && method_exists( $query, 'set' ) && is_array( $set_to ) ) { + $query->set( 'post_type', $set_to ); + return true; + } + + return false; + } + + /** + * Disable Blog feeds. + * + * @since 0.1.0 + * @since 0.4.0 add $is_comment_feed variable to feeds and check $is_comment_feed prior to redirect. + * @param bool $is_comment_feed true if a comment feed. + * @return void + */ + public function disable_feed( $is_comment_feed ) { + + // If this is a comment feed and comments are supported by other post types, bail. + if ( $is_comment_feed && dwpb_post_types_with_feature( 'comments' ) ) { + return; + } + + // Option to override this via filter and check to confirm post type. + global $post; + + // Check that we're disabling feeds and everything is good to go. + if ( $this->functions->disable_feeds( $post, $is_comment_feed ) && isset( $post->post_type ) && 'post' === $post->post_type ) { + + /** + * Filter the feed redirect url. + * + * @since 0.4.0 + * @param string $url The redirect url (defaults to homepage) + * @param object $post The global post object. + * @param bool $is_comment_feed True if the feed is a comment feed. + */ + $redirect_url = apply_filters( 'dwpb_redirect_feeds', home_url(), $post, $is_comment_feed ); + + /** + * Filter to toggle on a message instead of a redirect. + * + * Defaults to false, so a redirect is the expacted default behavior. + * + * @since 0.4.0 + * @since 0.4.9 updated variables passed to match other feed filters, + * previously only $is_comment_feed was passed and now + * the order is: bool, $post, $is_comment_feed. + * Note that if you used this filter before + * and relied on the $is_comment_feed, you'll need to update. + * @param bool $bool True to use a message, false to redirect. + * @param object $post Global post object. + * @param bool $is_comment_feed True if the feed is a comment feed. + */ + if ( apply_filters( 'dwpb_feed_message', false, $post, $is_comment_feed ) ) { + + // translators: This message appears when the feed is disabled instead of redirect, it should point to the homepage. + $message = sprintf( '%s: %s', __( 'No feed available, please visit our homepage:', 'disable-blog' ), esc_url_raw( $redirect_url ), esc_url_raw( $redirect_url ) ); + + /** + * Filter the feed die message. + * + * If the `dwpb_feed_message` is set to true, use this filter to set a custom message. + * + * @since 0.4.0 + * @param string $message + */ + $message = apply_filters( 'dwpb_feed_die_message', $message ); + $allowed_html = array( + 'a' => array( + 'href' => array(), + 'name' => array(), + 'id' => array(), + ), + ); + wp_die( wp_kses( $message, $allowed_html ) ); + + } else { // Default option: redirect to homepage. + + $this->functions->redirect( $redirect_url ); + + } + } + } + + /** + * Turn off the feed link. + * + * Only works for WordPress >= 4.4.0. + * + * @since 0.4.0 + * @param bool $show true to show the posts feed link. + * @return bool + */ + public function feed_links_show_posts_feed( $show ) { + return false; + } + + /** + * Turn off the comment's feed link. + * + * Only works for WordPress >= 4.4.0. + * + * @since 0.4.0 + * @param bool $show true to show the comments feed link. + * @return bool + */ + public function feed_links_show_comments_feed( $show ) { + + // If 'post' type is the only type supporting comments, then disable the comment feed link. + if ( ! dwpb_post_types_with_feature( 'comments' ) ) { + $show = false; + } + + return $show; + } + + /** + * Remove feed urls from head. + * + * @since 0.4.9 + * @return void + */ + public function header_feeds() { + + // Various feed links. + $feed = array( + 'feed_links' => 2, + 'feed_links_extra' => 3, + 'rsd_link' => 10, + 'wlwmanifest_link' => 10, + ); + + // Remove from head. + foreach ( $feed as $function => $priority ) { + remove_action( 'wp_head', $function, $priority ); + } + } + + /** + * Unset all post-related xmlrpc methods. + * + * @see wp-includes/class-wp-xmlrpc-server.php + * @since 0.4.9 + * @param array $methods the arrayve of xmlrpc methods. + * @return array + */ + public function xmlrpc_methods( $methods ) { + + $methods_to_remove = $this->get_disabled_xmlrpc_methods(); + + if ( ! empty( $methods_to_remove ) && is_array( $methods_to_remove ) ) { + foreach ( $methods_to_remove as $method ) { + if ( isset( $methods[ $method ] ) ) { + unset( $methods[ $method ] ); + } + } + } + + return $methods; + } + + /** + * Get the XML-RPC methods to disable. + * + * @since 0.5.0 + * @return array|bool + */ + private function get_disabled_xmlrpc_methods() { + + // The methods to remove. + $methods_to_remove = array( + 'wp.getUsersBlogs', + 'wp.newPost', + 'wp.editPost', + 'wp.deletePost', + 'wp.getPost', + 'wp.getPosts', + 'blogger.getPost', + 'blogger.getRecentPosts', + 'blogger.newPost', + 'blogger.editPost', + 'blogger.deletePost', + 'metaWeblog.newPost', + 'metaWeblog.editPost', + 'metaWeblog.getPost', + 'metaWeblog.getRecentPosts', + 'metaWeblog.deletePost', + 'mt.getRecentPostTitles', + 'mt.getTrackbackPings', + 'mt.publishPost', + 'pingback.ping', + 'pingback.extensions.getPingbacks', + 'system.multicall', + 'system.listMethods', + 'system.getCapabilities', + 'demo.sayHello', + 'demo.addTwoNumbers', + ); + + // Remove category / post tag terms, if not supported by another post type. + $taxonomy_methods = array(); + if ( ! dwpb_post_types_with_tax( 'category' ) ) { + $taxonomy_methods = array( + 'wp.newCategory', + 'wp.deleteeCategory', + 'mt.getCategoryList', + 'wp.suggestCategories', + 'mt.getPostCategories', + 'mt.setPostCategories', + 'metaWeblog.getCategories', + ); + } + if ( ! dwpb_post_types_with_tax( 'post_tag' ) ) { + $taxonomy_methods[] = 'wp.getTags'; + } + + $methods_to_remove = array_merge( $methods_to_remove, $taxonomy_methods ); + + /** + * Filter the methods being disabled by the plugin. + * + * Return false to disable this functionality entirely and keep all methods in place. + * + * @since 0.5.0 + * @param array $methods_to_remove an array of all the XMLRPC methods to disable. + * @return array|bool + */ + $methods_to_remove = apply_filters( 'dwpb_disabled_xmlrpc_methods', $methods_to_remove ); + + // filter any invalid entries out before returning the array. + return is_array( $methods_to_remove ) ? array_filter( $methods_to_remove, 'is_string' ) : false; // phpcs:ignore + } + + /** + * Remove the X-Pingback HTTP header. + * + * @since 0.4.0 + * @since 0.5.1 moved to the public class. + * @param array $headers the pingback headers. + * @return array + */ + public function filter_wp_headers( $headers ) { + + /** + * Toggle the disable pinback header feature. + * + * @since 0.4.0 + * @param bool $bool True to disable the header, false to keep it. + */ + if ( apply_filters( 'dwpb_remove_pingback_header', true ) && isset( $headers['X-Pingback'] ) ) { + unset( $headers['X-Pingback'] ); + } + + return $headers; + } + + /** + * Remove 'post' post type from sitemaps. + * + * @since 0.4.9 + * @param array $post_types an array of post type strings supported in sitemaps. + * @return array + */ + public function wp_sitemaps_post_types( $post_types ) { + + if ( isset( $post_types['post'] ) ) { + unset( $post_types['post'] ); + } + + return $post_types; + } + + /** + * Conditionally remove built-in taxonomies from sitemaps, if they are not being used by a custom post type. + * + * @since 0.4.9 + * @uses dwpb_post_types_with_tax() + * @param array $taxonomies an array of taxonomy strings supported in sitemaps. + * @return array + */ + public function wp_sitemaps_taxonomies( $taxonomies ) { + + $built_in_taxonomies = array( + 'post_tag', + 'category', + ); + foreach ( $built_in_taxonomies as $tax ) { + if ( isset( $taxonomies[ $tax ] ) && ! dwpb_post_types_with_tax( $tax ) ) { + unset( $taxonomies[ $tax ] ); + } + } + + return $taxonomies; + } + + /** + * Remove author sitemaps. + * + * @since 0.5.0 + * @link https://developer.wordpress.org/reference/hooks/wp_sitemaps_add_provider/ + * @param object $provider Instance of a WP_Sitemaps_Provider. + * @param string $name Name of the sitemap provider. + * @return object|bool Instance of a WP_Sitemaps_Provider or false. + */ + public function wp_author_sitemaps( $provider, $name ) { + + // If there are no author archives, then don't show the sitemap. + $disable_author_archives = $this->functions->disable_author_archives(); + if ( true === $disable_author_archives ) { + + $disable_sitemap = true; + + } else { // Otherwise, we may show it? + + // Check if we have any post types supporting author archives. + $author_archives_supported = $this->functions->author_archive_post_types(); + + // Only show the sitemap if there are post types support on the archives. + $disable_sitemap = empty( $author_archives_supported ); + } + + /** + * Turn off user/author sitemaps. + * + * @since 0.5.0 + * @param bool $bool True to disable, defaults to true. + * @return bool + */ + if ( 'users' === $name && apply_filters( 'dwpb_disable_user_sitemap', $disable_sitemap ) ) { + return false; + } + + return $provider; + } +} diff --git a/includes/class-disable-blog.php b/includes/class-disable-blog.php index 31e7f35..17ae6ca 100644 --- a/includes/class-disable-blog.php +++ b/includes/class-disable-blog.php @@ -1,407 +1,407 @@ - - */ - -/** - * The core plugin class. - * - * This is used to define internationalization, admin-specific hooks, and - * public-facing site hooks. - * - * Also maintains the unique identifier of this plugin as well as the current - * version of the plugin. - * - * @since 0.4.0 - */ -class Disable_Blog { - - /** - * The loader that's responsible for maintaining and registering all hooks that power - * the plugin. - * - * @since 0.4.0 - * @access protected - * @var Disable_Blog_Loader $loader Maintains and registers all hooks for the plugin. - */ - protected $loader; - - /** - * The unique identifier of this plugin. - * - * @since 0.4.0 - * @access protected - * @var string $plugin_name The string used to uniquely identify this plugin. - */ - protected $plugin_name; - - /** - * The current version of the plugin. - * - * @since 0.4.0 - * @access protected - * @var string $version The current version of the plugin. - */ - protected $version; - - /** - * Define the core functionality of the plugin. - * - * Set the plugin name and the plugin version that can be used throughout the plugin. - * Load the dependencies, define the locale, and set the hooks for the admin area and - * the public-facing side of the site. - * - * @since 0.4.0 - * @access public - * @param string $plugin_name The name of this plugin. - * @param string $version The version of this plugin. - */ - public function __construct( $plugin_name, $version ) { - - $this->plugin_name = $plugin_name; - $this->version = $version; - - do_action( 'dwpb_init' ); - - $this->upgrade_check(); - $this->load_dependencies(); - $this->set_locale(); - $this->plugin_integrations(); - $this->define_admin_hooks(); - $this->define_public_hooks(); - } - - /** - * Upgrade check. - * - * @since 0.4.0 - * @access private - */ - private static function upgrade_check() { - - // let's only run these checks on the admin page load. - if ( ! is_admin() ) { - return; - } - - // Get the current version option. - $current_version = get_option( 'dwpb_version', false ); - - // Update the previous version if we're upgrading. - if ( $current_version && DWPB_VERSION !== $current_version ) { - update_option( 'dwpb_previous_version', $current_version, false ); - } - - // See if it's a previous version, which may not have set the version option. - if ( false === $current_version || DWPB_VERSION !== $current_version ) { - // do things on update. - - // Save current version. - update_option( 'dwpb_version', DWPB_VERSION, false ); - } - } - - /** - * Load the required dependencies for this plugin. - * - * Include the following files that make up the plugin: - * - * - Disable_Blog_Loader. Orchestrates the hooks of the plugin. - * - Disable_Blog_I18n. Defines internationalization functionality. - * - Disable_Blog_Admin. Defines all hooks for the admin area. - * - Disable_Blog_Public. Defines all hooks for the public side of the site. - * - * Create an instance of the loader which will be used to register the hooks - * with WordPress. - * - * @since 0.4.0 - * @since 0.5.3 Added Integrations class. - * @access private - */ - private function load_dependencies() { - - // Includes directory. - $includes_dir = plugin_dir_path( __DIR__ ) . 'includes'; - - /** - * The class responsible for orchestrating the actions and filters of the - * core plugin. - */ - require_once $includes_dir . '/class-disable-blog-loader.php'; - - /** - * File with common functions. - */ - require_once $includes_dir . '/functions.php'; - - /** - * Make it so. - */ - $this->loader = new Disable_Blog_Loader(); - - $classes = array( - 'Disable_Blog_I18n', - 'Disable_Blog_Functions', - 'Disable_Blog_Admin', - 'Disable_Blog_Public', - 'Disable_Blog_Integrations', - ); - foreach ( $classes as $class ) { - $this->loader->autoLoader( $class ); - } - } - - /** - * Define the locale for this plugin for internationalization. - * - * Uses the Disable_Blog_I18n class in order to set the domain and to register the hook - * with WordPress. - * - * @since 0.4.0 - * @access private - */ - private function set_locale() { - - $plugin_i18n = new Disable_Blog_I18n(); - - $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' ); - } - - /** - * Register all of the hooks related to the admin area functionality - * of the plugin. - * - * @since 0.4.0 - * @since 0.5.3 Separated comment functions to run only if comments are supported. - * @access private - */ - private function define_admin_hooks() { - - $plugin_admin = new Disable_Blog_Admin( $this->get_plugin_name(), $this->get_version() ); - - // Add Links to Plugin Bar. - $this->loader->add_filter( 'plugin_row_meta', $plugin_admin, 'plugin_links', 10, 2 ); - - // Hide items with CSS. - $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' ); - - // Hide items with JavaScript where CSS doesn't do the job as well. - $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts', 100 ); - - // Hide Blog Related Admin pages. - $this->loader->add_action( 'admin_menu', $plugin_admin, 'remove_menu_pages' ); - - // Modify the main 'post' post_type arguments to shut pubic things down. - $this->loader->add_action( 'init', $plugin_admin, 'modify_post_type_arguments', 25 ); - - // Modify the core taxonomy arguments to filter out posts and shut down public things. - $this->loader->add_action( 'init', $plugin_admin, 'modify_taxonomies_arguments', 25 ); - - // Redirect Blog-related Admin Pages. - $this->loader->add_action( 'current_screen', $plugin_admin, 'redirect_admin_pages' ); - - // Filter post open status for pings. - $this->loader->add_action( 'pings_open', $plugin_admin, 'filter_comment_status', 20, 2 ); - - // Remove Admin Bar Links. - $this->loader->add_action( 'wp_before_admin_bar_render', $plugin_admin, 'remove_admin_bar_links' ); - - // Disable Update Services configuration, no pingbacks. - add_filter( 'enable_update_services_configuration', '__return_false' ); - - // Remove Dashboard Widgets. - $this->loader->add_action( 'admin_init', $plugin_admin, 'remove_dashboard_widgets' ); - - // Admin notices. - $this->loader->add_action( 'admin_notices', $plugin_admin, 'admin_notices' ); - - // Add a class to the admin body for the reading options page. - $this->loader->add_filter( 'admin_body_class', $plugin_admin, 'admin_body_class', 10, 1 ); - - // Remove Post via Email Settings. - add_filter( 'enable_post_by_email_configuration', '__return_false' ); - - // Disable Press This Function. - $this->loader->add_action( 'load-press-this.php', $plugin_admin, 'disable_press_this' ); - - // Remove Post Related Widgets. - $this->loader->add_action( 'widgets_init', $plugin_admin, 'remove_widgets' ); - - // Filter removal of widgets for some checks. - $this->loader->add_filter( 'dwpb_unregister_widgets', $plugin_admin, 'filter_widget_removal', 10, 2 ); - - // Custom Post State for the Blog Page redirect. - $this->loader->add_filter( 'display_post_states', $plugin_admin, 'page_post_states', 10, 2 ); - - // Remove REST API site health check related to posts. - $this->loader->add_filter( 'site_status_tests', $plugin_admin, 'site_status_tests', 10, 1 ); - - // Replace 'post' column with 'page' column. - $this->loader->add_action( 'manage_users_columns', $plugin_admin, 'manage_users_columns', 10, 1 ); - $this->loader->add_filter( 'manage_users_custom_column', $plugin_admin, 'manage_users_custom_column', 10, 3 ); - - // Remove the "view" link from the user options if author archives are not supported. - $this->loader->add_filter( 'user_row_actions', $plugin_admin, 'user_row_actions', 10, 2 ); - - // Filter post counts on post-related taxonomy edit screens for custom post types. - $this->loader->add_filter( 'post_tag_row_actions', $plugin_admin, 'filter_taxonomy_count', 10, 2 ); - $this->loader->add_filter( 'category_row_actions', $plugin_admin, 'filter_taxonomy_count', 10, 2 ); - - // Update customizer homepage settings panel to match the Reading settings. - $this->loader->add_action( 'customize_controls_print_styles', $plugin_admin, 'customizer_styles', 999 ); - $this->loader->add_action( 'customize_controls_enqueue_scripts', $plugin_admin, 'customizer_scripts', 999 ); - - // Update Blog page notice. - $this->loader->add_action( 'post_edit_form_tag', $plugin_admin, 'update_posts_page_notice', 10, 1 ); - - // Remove and update available permalink structure tags. - $this->loader->add_filter( 'available_permalink_structure_tags', $plugin_admin, 'available_permalink_structure_tags', 10, 1 ); - - // Only run comment related functions if comments are supported. - if ( dwpb_post_types_with_feature( 'comments' ) ) { - - // Filter comment counts in admin table. - $this->loader->add_filter( 'views_edit-comments', $plugin_admin, 'filter_admin_table_comment_count', 20, 1 ); - - // Filter post open status for comments. - $this->loader->add_action( 'comments_open', $plugin_admin, 'filter_comment_status', 20, 2 ); - - // Filter wp_count_comments, which addresses comments in admin bar. - $this->loader->add_filter( 'wp_count_comments', $plugin_admin, 'filter_wp_count_comments', 10, 2 ); - - // Filter Comments off Admin Page. - $this->loader->add_action( 'pre_get_comments', $plugin_admin, 'comment_filter', 10, 1 ); - - // Clear comments from 'post' post type. - $this->loader->add_filter( 'comments_array', $plugin_admin, 'filter_existing_comments', 20, 2 ); - - } - } - - /** - * Register all of the hooks related to the public-facing functionality - * of the plugin. - * - * @since 0.4.0 - * @access private - */ - private function define_public_hooks() { - - $plugin_public = new Disable_Blog_Public( $this->get_plugin_name(), $this->get_version() ); - - // Redirect Public pages (single posts, archives, etc). - $this->loader->add_action( 'template_redirect', $plugin_public, 'redirect_public_pages' ); - - // Modify Query. Setting to priority 9 to allow default filter priority to override. - $this->loader->add_action( 'pre_get_posts', $plugin_public, 'modify_query', 9 ); - - // Disable Feed. - $this->loader->add_action( 'do_feed', $plugin_public, 'disable_feed', 1, 2 ); - $this->loader->add_action( 'do_feed_rdf', $plugin_public, 'disable_feed', 1, 2 ); - $this->loader->add_action( 'do_feed_rss', $plugin_public, 'disable_feed', 1, 2 ); - $this->loader->add_action( 'do_feed_rss2', $plugin_public, 'disable_feed', 1, 2 ); - $this->loader->add_action( 'do_feed_atom', $plugin_public, 'disable_feed', 1, 2 ); - - // Remove feed links from the header. - $this->loader->add_action( 'wp_loaded', $plugin_public, 'header_feeds', 1, 1 ); - - // Remove the X-Pingback HTTP header. - $this->loader->add_filter( 'wp_headers', $plugin_public, 'filter_wp_headers', 10, 1 ); - - // Hide Feed links. - $this->loader->add_filter( 'feed_links_show_posts_feed', $plugin_public, 'feed_links_show_posts_feed', 10, 1 ); - $this->loader->add_filter( 'feed_links_show_comments_feed', $plugin_public, 'feed_links_show_comments_feed', 10, 1 ); - - // Disable XML-RPC methods related to posts and built-in taxonomies. - $this->loader->add_filter( 'xmlrpc_methods', $plugin_public, 'xmlrpc_methods', 10, 1 ); - - // Remove posts from xml sitemaps. - $this->loader->add_filter( 'wp_sitemaps_post_types', $plugin_public, 'wp_sitemaps_post_types', 10, 1 ); - - // Conditionally remove built-in taxonomies from sitemaps, if they are not being used by a custom post type. - $this->loader->add_filter( 'wp_sitemaps_taxonomies', $plugin_public, 'wp_sitemaps_taxonomies', 10, 1 ); - - // Conditionally remove author sitemaps, if author archives are not being supported. - $this->loader->add_filter( 'wp_sitemaps_add_provider', $plugin_public, 'wp_author_sitemaps', 100, 2 ); - } - - /** - * Integrate with other plugins. - * - * @since 0.5.3 - * @return void - */ - public function plugin_integrations() { - - $plugin_integrations = new Disable_Blog_Integrations(); - - // Disable Comments. - if ( $plugin_integrations->is_disable_comments_active() ) { - - // If Disabled Comments is active, return false for post types supporting comments, - // and functionality in Disable Blog related to comments will be turned off, - // the assumption being that the Disable Comments plugin is handling it. - add_filter( 'dwpb_post_types_supporting_comments', '__return_false' ); - } - - // WooCommerce. - if ( $plugin_integrations->is_woocommerce_active() && dwpb_post_types_with_feature( 'comments' ) ) { - - // Convert the $comments object back into an array if older version of WooCommerce is active. - $this->loader->add_filter( 'wp_count_comments', $plugin_integrations, 'filter_woocommerce_comment_count', 15, 2 ); - } - } - - /** - * Run the loader to execute all of the hooks with WordPress. - * - * @since 0.4.0 - * @access public - */ - public function run() { - $this->loader->run(); - } - - /** - * The name of the plugin used to uniquely identify it within the context of - * WordPress and to define internationalization functionality. - * - * @since 0.4.0 - * @access public - * @return string The name of the plugin. - */ - public function get_plugin_name() { - return $this->plugin_name; - } - - /** - * The reference to the class that orchestrates the hooks with the plugin. - * - * @since 0.4.0 - * @access public - * @return Disable_Blog_Loader Orchestrates the hooks of the plugin. - */ - public function get_loader() { - return $this->loader; - } - - /** - * Retrieve the version number of the plugin. - * - * @since 0.4.0 - * @access public - * @return string The version number of the plugin. - */ - public function get_version() { - return $this->version; - } -} + + */ + +/** + * The core plugin class. + * + * This is used to define internationalization, admin-specific hooks, and + * public-facing site hooks. + * + * Also maintains the unique identifier of this plugin as well as the current + * version of the plugin. + * + * @since 0.4.0 + */ +class Disable_Blog { + + /** + * The loader that's responsible for maintaining and registering all hooks that power + * the plugin. + * + * @since 0.4.0 + * @access protected + * @var Disable_Blog_Loader $loader Maintains and registers all hooks for the plugin. + */ + protected $loader; + + /** + * The unique identifier of this plugin. + * + * @since 0.4.0 + * @access protected + * @var string $plugin_name The string used to uniquely identify this plugin. + */ + protected $plugin_name; + + /** + * The current version of the plugin. + * + * @since 0.4.0 + * @access protected + * @var string $version The current version of the plugin. + */ + protected $version; + + /** + * Define the core functionality of the plugin. + * + * Set the plugin name and the plugin version that can be used throughout the plugin. + * Load the dependencies, define the locale, and set the hooks for the admin area and + * the public-facing side of the site. + * + * @since 0.4.0 + * @access public + * @param string $plugin_name The name of this plugin. + * @param string $version The version of this plugin. + */ + public function __construct( $plugin_name, $version ) { + + $this->plugin_name = $plugin_name; + $this->version = $version; + + do_action( 'dwpb_init' ); + + $this->upgrade_check(); + $this->load_dependencies(); + $this->set_locale(); + $this->plugin_integrations(); + $this->define_admin_hooks(); + $this->define_public_hooks(); + } + + /** + * Upgrade check. + * + * @since 0.4.0 + * @access private + */ + private static function upgrade_check() { + + // let's only run these checks on the admin page load. + if ( ! is_admin() ) { + return; + } + + // Get the current version option. + $current_version = get_option( 'dwpb_version', false ); + + // Update the previous version if we're upgrading. + if ( $current_version && DWPB_VERSION !== $current_version ) { + update_option( 'dwpb_previous_version', $current_version, false ); + } + + // See if it's a previous version, which may not have set the version option. + if ( false === $current_version || DWPB_VERSION !== $current_version ) { + // do things on update. + + // Save current version. + update_option( 'dwpb_version', DWPB_VERSION, false ); + } + } + + /** + * Load the required dependencies for this plugin. + * + * Include the following files that make up the plugin: + * + * - Disable_Blog_Loader. Orchestrates the hooks of the plugin. + * - Disable_Blog_I18n. Defines internationalization functionality. + * - Disable_Blog_Admin. Defines all hooks for the admin area. + * - Disable_Blog_Public. Defines all hooks for the public side of the site. + * + * Create an instance of the loader which will be used to register the hooks + * with WordPress. + * + * @since 0.4.0 + * @since 0.5.3 Added Integrations class. + * @access private + */ + private function load_dependencies() { + + // Includes directory. + $includes_dir = plugin_dir_path( __DIR__ ) . 'includes'; + + /** + * The class responsible for orchestrating the actions and filters of the + * core plugin. + */ + require_once $includes_dir . '/class-disable-blog-loader.php'; + + /** + * File with common functions. + */ + require_once $includes_dir . '/functions.php'; + + /** + * Make it so. + */ + $this->loader = new Disable_Blog_Loader(); + + $classes = array( + 'Disable_Blog_I18n', + 'Disable_Blog_Functions', + 'Disable_Blog_Admin', + 'Disable_Blog_Public', + 'Disable_Blog_Integrations', + ); + foreach ( $classes as $class ) { + $this->loader->autoLoader( $class ); + } + } + + /** + * Define the locale for this plugin for internationalization. + * + * Uses the Disable_Blog_I18n class in order to set the domain and to register the hook + * with WordPress. + * + * @since 0.4.0 + * @access private + */ + private function set_locale() { + + $plugin_i18n = new Disable_Blog_I18n(); + + $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' ); + } + + /** + * Register all of the hooks related to the admin area functionality + * of the plugin. + * + * @since 0.4.0 + * @since 0.5.3 Separated comment functions to run only if comments are supported. + * @access private + */ + private function define_admin_hooks() { + + $plugin_admin = new Disable_Blog_Admin( $this->get_plugin_name(), $this->get_version() ); + + // Add Links to Plugin Bar. + $this->loader->add_filter( 'plugin_row_meta', $plugin_admin, 'plugin_links', 10, 2 ); + + // Hide items with CSS. + $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' ); + + // Hide items with JavaScript where CSS doesn't do the job as well. + $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts', 100 ); + + // Hide Blog Related Admin pages. + $this->loader->add_action( 'admin_menu', $plugin_admin, 'remove_menu_pages' ); + + // Modify the main 'post' post_type arguments to shut pubic things down. + $this->loader->add_action( 'init', $plugin_admin, 'modify_post_type_arguments', 25 ); + + // Modify the core taxonomy arguments to filter out posts and shut down public things. + $this->loader->add_action( 'init', $plugin_admin, 'modify_taxonomies_arguments', 25 ); + + // Redirect Blog-related Admin Pages. + $this->loader->add_action( 'current_screen', $plugin_admin, 'redirect_admin_pages' ); + + // Filter post open status for pings. + $this->loader->add_action( 'pings_open', $plugin_admin, 'filter_comment_status', 20, 2 ); + + // Remove Admin Bar Links. + $this->loader->add_action( 'wp_before_admin_bar_render', $plugin_admin, 'remove_admin_bar_links' ); + + // Disable Update Services configuration, no pingbacks. + add_filter( 'enable_update_services_configuration', '__return_false' ); + + // Remove Dashboard Widgets. + $this->loader->add_action( 'admin_init', $plugin_admin, 'remove_dashboard_widgets' ); + + // Admin notices. + $this->loader->add_action( 'admin_notices', $plugin_admin, 'admin_notices' ); + + // Add a class to the admin body for the reading options page. + $this->loader->add_filter( 'admin_body_class', $plugin_admin, 'admin_body_class', 10, 1 ); + + // Remove Post via Email Settings. + add_filter( 'enable_post_by_email_configuration', '__return_false' ); + + // Disable Press This Function. + $this->loader->add_action( 'load-press-this.php', $plugin_admin, 'disable_press_this' ); + + // Remove Post Related Widgets. + $this->loader->add_action( 'widgets_init', $plugin_admin, 'remove_widgets' ); + + // Filter removal of widgets for some checks. + $this->loader->add_filter( 'dwpb_unregister_widgets', $plugin_admin, 'filter_widget_removal', 10, 2 ); + + // Custom Post State for the Blog Page redirect. + $this->loader->add_filter( 'display_post_states', $plugin_admin, 'page_post_states', 10, 2 ); + + // Remove REST API site health check related to posts. + $this->loader->add_filter( 'site_status_tests', $plugin_admin, 'site_status_tests', 10, 1 ); + + // Replace 'post' column with 'page' column. + $this->loader->add_action( 'manage_users_columns', $plugin_admin, 'manage_users_columns', 10, 1 ); + $this->loader->add_filter( 'manage_users_custom_column', $plugin_admin, 'manage_users_custom_column', 10, 3 ); + + // Remove the "view" link from the user options if author archives are not supported. + $this->loader->add_filter( 'user_row_actions', $plugin_admin, 'user_row_actions', 10, 2 ); + + // Filter post counts on post-related taxonomy edit screens for custom post types. + $this->loader->add_filter( 'post_tag_row_actions', $plugin_admin, 'filter_taxonomy_count', 10, 2 ); + $this->loader->add_filter( 'category_row_actions', $plugin_admin, 'filter_taxonomy_count', 10, 2 ); + + // Update customizer homepage settings panel to match the Reading settings. + $this->loader->add_action( 'customize_controls_print_styles', $plugin_admin, 'customizer_styles', 999 ); + $this->loader->add_action( 'customize_controls_enqueue_scripts', $plugin_admin, 'customizer_scripts', 999 ); + + // Update Blog page notice. + $this->loader->add_action( 'post_edit_form_tag', $plugin_admin, 'update_posts_page_notice', 10, 1 ); + + // Remove and update available permalink structure tags. + $this->loader->add_filter( 'available_permalink_structure_tags', $plugin_admin, 'available_permalink_structure_tags', 10, 1 ); + + // Only run comment related functions if comments are supported. + if ( dwpb_post_types_with_feature( 'comments' ) ) { + + // Filter comment counts in admin table. + $this->loader->add_filter( 'views_edit-comments', $plugin_admin, 'filter_admin_table_comment_count', 20, 1 ); + + // Filter post open status for comments. + $this->loader->add_action( 'comments_open', $plugin_admin, 'filter_comment_status', 20, 2 ); + + // Filter wp_count_comments, which addresses comments in admin bar. + $this->loader->add_filter( 'wp_count_comments', $plugin_admin, 'filter_wp_count_comments', 10, 2 ); + + // Filter Comments off Admin Page. + $this->loader->add_action( 'pre_get_comments', $plugin_admin, 'comment_filter', 10, 1 ); + + // Clear comments from 'post' post type. + $this->loader->add_filter( 'comments_array', $plugin_admin, 'filter_existing_comments', 20, 2 ); + + } + } + + /** + * Register all of the hooks related to the public-facing functionality + * of the plugin. + * + * @since 0.4.0 + * @access private + */ + private function define_public_hooks() { + + $plugin_public = new Disable_Blog_Public( $this->get_plugin_name(), $this->get_version() ); + + // Redirect Public pages (single posts, archives, etc). + $this->loader->add_action( 'template_redirect', $plugin_public, 'redirect_public_pages' ); + + // Modify Query. Setting to priority 9 to allow default filter priority to override. + $this->loader->add_action( 'pre_get_posts', $plugin_public, 'modify_query', 9 ); + + // Disable Feed. + $this->loader->add_action( 'do_feed', $plugin_public, 'disable_feed', 1, 2 ); + $this->loader->add_action( 'do_feed_rdf', $plugin_public, 'disable_feed', 1, 2 ); + $this->loader->add_action( 'do_feed_rss', $plugin_public, 'disable_feed', 1, 2 ); + $this->loader->add_action( 'do_feed_rss2', $plugin_public, 'disable_feed', 1, 2 ); + $this->loader->add_action( 'do_feed_atom', $plugin_public, 'disable_feed', 1, 2 ); + + // Remove feed links from the header. + $this->loader->add_action( 'wp_loaded', $plugin_public, 'header_feeds', 1, 1 ); + + // Remove the X-Pingback HTTP header. + $this->loader->add_filter( 'wp_headers', $plugin_public, 'filter_wp_headers', 10, 1 ); + + // Hide Feed links. + $this->loader->add_filter( 'feed_links_show_posts_feed', $plugin_public, 'feed_links_show_posts_feed', 10, 1 ); + $this->loader->add_filter( 'feed_links_show_comments_feed', $plugin_public, 'feed_links_show_comments_feed', 10, 1 ); + + // Disable XML-RPC methods related to posts and built-in taxonomies. + $this->loader->add_filter( 'xmlrpc_methods', $plugin_public, 'xmlrpc_methods', 10, 1 ); + + // Remove posts from xml sitemaps. + $this->loader->add_filter( 'wp_sitemaps_post_types', $plugin_public, 'wp_sitemaps_post_types', 10, 1 ); + + // Conditionally remove built-in taxonomies from sitemaps, if they are not being used by a custom post type. + $this->loader->add_filter( 'wp_sitemaps_taxonomies', $plugin_public, 'wp_sitemaps_taxonomies', 10, 1 ); + + // Conditionally remove author sitemaps, if author archives are not being supported. + $this->loader->add_filter( 'wp_sitemaps_add_provider', $plugin_public, 'wp_author_sitemaps', 100, 2 ); + } + + /** + * Integrate with other plugins. + * + * @since 0.5.3 + * @return void + */ + public function plugin_integrations() { + + $plugin_integrations = new Disable_Blog_Integrations(); + + // Disable Comments. + if ( $plugin_integrations->is_disable_comments_active() ) { + + // If Disabled Comments is active, return false for post types supporting comments, + // and functionality in Disable Blog related to comments will be turned off, + // the assumption being that the Disable Comments plugin is handling it. + add_filter( 'dwpb_post_types_supporting_comments', '__return_false' ); + } + + // WooCommerce. + if ( $plugin_integrations->is_woocommerce_active() && dwpb_post_types_with_feature( 'comments' ) ) { + + // Convert the $comments object back into an array if older version of WooCommerce is active. + $this->loader->add_filter( 'wp_count_comments', $plugin_integrations, 'filter_woocommerce_comment_count', 15, 2 ); + } + } + + /** + * Run the loader to execute all of the hooks with WordPress. + * + * @since 0.4.0 + * @access public + */ + public function run() { + $this->loader->run(); + } + + /** + * The name of the plugin used to uniquely identify it within the context of + * WordPress and to define internationalization functionality. + * + * @since 0.4.0 + * @access public + * @return string The name of the plugin. + */ + public function get_plugin_name() { + return $this->plugin_name; + } + + /** + * The reference to the class that orchestrates the hooks with the plugin. + * + * @since 0.4.0 + * @access public + * @return Disable_Blog_Loader Orchestrates the hooks of the plugin. + */ + public function get_loader() { + return $this->loader; + } + + /** + * Retrieve the version number of the plugin. + * + * @since 0.4.0 + * @access public + * @return string The version number of the plugin. + */ + public function get_version() { + return $this->version; + } +} From 344c1c92a362fa4593c043c312bf624ab0e78a16 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 21:51:39 -0800 Subject: [PATCH 11/20] simplify some if statements --- includes/class-disable-blog-activator.php | 311 +++++++++---------- includes/class-disable-blog-deactivator.php | 311 +++++++++---------- includes/class-disable-blog-integrations.php | 232 +++++++------- 3 files changed, 414 insertions(+), 440 deletions(-) diff --git a/includes/class-disable-blog-activator.php b/includes/class-disable-blog-activator.php index d00d56a..55bc12b 100644 --- a/includes/class-disable-blog-activator.php +++ b/includes/class-disable-blog-activator.php @@ -1,158 +1,153 @@ - - */ - -/** - * Fired during plugin activation. - * - * This class defines all code necessary to run during the plugin's activation. - * - * @since 0.4.3 - */ -class Disable_Blog_Activator { - - /** - * The $_REQUEST during plugin activation. - * - * @since 0.5.1 - * @access private - * @var array $request The $_REQUEST array during plugin activation. - */ - private static $request = array(); - - /** - * The $_REQUEST['plugin'] during plugin activation. - * - * @since 0.5.1 - * @access private - * @var string $plugin The $_REQUEST['plugin'] value during plugin activation. - */ - private static $plugin = 'disable-blog'; - - /** - * Activate the plugin. - * - * Checks if the plugin was (safely) activated. - * Place to add any custom action during plugin activation. - * - * @since 0.4.3 - * @since 0.4.9 flush rewrite rules. - * @since 0.5.1 update to "Better WP Plugin Boilerplate" functions. - */ - public static function activate() { - - if ( false === self::get_request() - || false === self::validate_request( self::$plugin ) - || false === self::check_caps() - ) { - if ( isset( $_REQUEST['plugin'] ) ) { - if ( ! check_admin_referer( 'activate-plugin_' . self::$request['plugin'] ) ) { - exit; - } - } elseif ( isset( $_REQUEST['checked'] ) ) { - if ( ! check_admin_referer( 'bulk-plugins' ) ) { - exit; - } - } - } - - /** - * The plugin is now safely activated. - */ - - wp_cache_delete( 'comments-0', 'counts' ); - delete_transient( 'wc_count_comments' ); - flush_rewrite_rules(); - } - - /** - * Get the request. - * - * Gets the $_REQUEST array and checks if necessary keys are set. - * Populates self::request with necessary and sanitized values. - * - * @since 0.5.1 - * @return bool|array false or self::$request array. - */ - private static function get_request() { - - if ( ! empty( $_REQUEST ) - && isset( $_REQUEST['_wpnonce'] ) - && isset( $_REQUEST['action'] ) - ) { - if ( isset( $_REQUEST['plugin'] ) ) { - if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'activate-plugin_' . sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ) ) ) { - - self::$request['plugin'] = sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ); - self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); - - return self::$request; - - } - } elseif ( isset( $_REQUEST['checked'] ) ) { - if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'bulk-plugins' ) ) { - - self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); - self::$request['plugins'] = array_map( 'sanitize_text_field', (array) wp_unslash( $_REQUEST['checked'] ) ); - - return self::$request; - - } - } - } - - return false; - } - - /** - * Validate the Request data. - * - * Validates the data in $_REQUEST is matching this plugin and action. - * - * @since 0.5.1 - * @param string $plugin The Plugin folder/name.php. - * @return bool false if either plugin or action does not match, else true. - */ - private static function validate_request( $plugin ) { - - if ( isset( self::$request['plugin'] ) - && $plugin === self::$request['plugin'] - && 'activate' === self::$request['action'] - ) { - - return true; - - } elseif ( isset( self::$request['plugins'] ) - && 'activate-selected' === self::$request['action'] - && in_array( $plugin, self::$request['plugins'], true ) - ) { - return true; - } - - return false; - } - - /** - * Check Capabilities. - * - * We want no one else but users with activate_plugins or above to be able to active this plugin. - * - * @since 0.5.1 - * @return bool false if no caps, else true. - */ - private static function check_caps() { - - if ( current_user_can( 'activate_plugins' ) ) { - return true; - } - - return false; - } -} + + */ + +/** + * Fired during plugin activation. + * + * This class defines all code necessary to run during the plugin's activation. + * + * @since 0.4.3 + */ +class Disable_Blog_Activator { + + /** + * The $_REQUEST during plugin activation. + * + * @since 0.5.1 + * @access private + * @var array $request The $_REQUEST array during plugin activation. + */ + private static $request = array(); + + /** + * The $_REQUEST['plugin'] during plugin activation. + * + * @since 0.5.1 + * @access private + * @var string $plugin The $_REQUEST['plugin'] value during plugin activation. + */ + private static $plugin = 'disable-blog'; + + /** + * Activate the plugin. + * + * Checks if the plugin was (safely) activated. + * Place to add any custom action during plugin activation. + * + * @since 0.4.3 + * @since 0.4.9 flush rewrite rules. + * @since 0.5.1 update to "Better WP Plugin Boilerplate" functions. + */ + public static function activate() { + + if ( false === self::get_request() + || false === self::validate_request( self::$plugin ) + || false === self::check_caps() + ) { + if ( isset( $_REQUEST['plugin'] ) ) { + if ( ! check_admin_referer( 'activate-plugin_' . self::$request['plugin'] ) ) { + exit; + } + } elseif ( isset( $_REQUEST['checked'] ) ) { + if ( ! check_admin_referer( 'bulk-plugins' ) ) { + exit; + } + } + } + + /** + * The plugin is now safely activated. + */ + + wp_cache_delete( 'comments-0', 'counts' ); + delete_transient( 'wc_count_comments' ); + flush_rewrite_rules(); + } + + /** + * Get the request. + * + * Gets the $_REQUEST array and checks if necessary keys are set. + * Populates self::request with necessary and sanitized values. + * + * @since 0.5.1 + * @return bool|array false or self::$request array. + */ + private static function get_request() { + + if ( ! empty( $_REQUEST ) + && isset( $_REQUEST['_wpnonce'] ) + && isset( $_REQUEST['action'] ) + ) { + if ( isset( $_REQUEST['plugin'] ) ) { + if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'activate-plugin_' . sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ) ) ) { + + self::$request['plugin'] = sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ); + self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); + + return self::$request; + + } + } elseif ( isset( $_REQUEST['checked'] ) ) { + if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'bulk-plugins' ) ) { + + self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); + self::$request['plugins'] = array_map( 'sanitize_text_field', (array) wp_unslash( $_REQUEST['checked'] ) ); + + return self::$request; + + } + } + } + + return false; + } + + /** + * Validate the Request data. + * + * Validates the data in $_REQUEST is matching this plugin and action. + * + * @since 0.5.1 + * @param string $plugin The Plugin folder/name.php. + * @return bool false if either plugin or action does not match, else true. + */ + private static function validate_request( $plugin ) { + + if ( isset( self::$request['plugin'] ) + && $plugin === self::$request['plugin'] + && 'activate' === self::$request['action'] + ) { + + return true; + + } elseif ( isset( self::$request['plugins'] ) + && 'activate-selected' === self::$request['action'] + && in_array( $plugin, self::$request['plugins'], true ) + ) { + return true; + } + + return false; + } + + /** + * Check Capabilities. + * + * We want no one else but users with activate_plugins or above to be able to active this plugin. + * + * @since 0.5.1 + * @return bool false if no caps, else true. + */ + private static function check_caps() { + return current_user_can( 'activate_plugins' ); + } +} diff --git a/includes/class-disable-blog-deactivator.php b/includes/class-disable-blog-deactivator.php index 666442a..b6f53d3 100644 --- a/includes/class-disable-blog-deactivator.php +++ b/includes/class-disable-blog-deactivator.php @@ -1,158 +1,153 @@ - - */ - -/** - * Fired during plugin deactivation. - * - * This class defines all code necessary to run during the plugin's deactivation. - * - * @since 0.4.3 - */ -class Disable_Blog_Deactivator { - - /** - * The $_REQUEST during plugin deactivation. - * - * @since 0.5.1 - * @access private - * @var array $request The $_REQUEST array during plugin deactivation. - */ - private static $request = array(); - - /** - * The $_REQUEST['plugin'] during plugin deactivation. - * - * @since 0.5.1 - * @access private - * @var string $plugin The $_REQUEST['plugin'] value during plugin deactivation. - */ - private static $plugin = 'disable-blog'; - - /** - * Deactivate the plugin. - * - * Checks if the plugin was (safely) deactivated. - * Place to add any custom action during plugin deactivation. - * - * @since 0.4.3 - * @since 0.4.9 add flush rewrite rules. - * @since 0.5.1 - */ - public static function deactivate() { - - if ( false === self::get_request() - || false === self::validate_request( self::$plugin ) - || false === self::check_caps() - ) { - if ( isset( $_REQUEST['plugin'] ) ) { - if ( ! check_admin_referer( 'deactivate-plugin_' . self::$request['plugin'] ) ) { - exit; - } - } elseif ( isset( $_REQUEST['checked'] ) ) { - if ( ! check_admin_referer( 'bulk-plugins' ) ) { - exit; - } - } - } - - /** - * The plugin is now safely deactivated. - */ - - wp_cache_delete( 'comments-0', 'counts' ); - delete_transient( 'wc_count_comments' ); - flush_rewrite_rules(); - } - - /** - * Get the request. - * - * Gets the $_REQUEST array and checks if necessary keys are set. - * Populates self::request with necessary and sanitized values. - * - * @since 0.5.1 - * @return bool|array false or self::$request array. - */ - private static function get_request() { - - if ( ! empty( $_REQUEST ) - && isset( $_REQUEST['_wpnonce'] ) - && isset( $_REQUEST['action'] ) - ) { - if ( isset( $_REQUEST['plugin'] ) ) { - if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'deactivate-plugin_' . sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ) ) ) { - - self::$request['plugin'] = sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ); - self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); - - return self::$request; - - } - } elseif ( isset( $_REQUEST['checked'] ) ) { - if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'bulk-plugins' ) ) { - - self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); - self::$request['plugins'] = array_map( 'sanitize_text_field', (array) wp_unslash( $_REQUEST['checked'] ) ); - - return self::$request; - - } - } - } - - return false; - } - - /** - * Validate the Request data. - * - * Validates the data in $_REQUEST is matching this plugin and action. - * - * @since 0.5.1 - * @param string $plugin The Plugin folder/name.php. - * @return bool false if either plugin or action does not match, else true. - */ - private static function validate_request( $plugin ) { - - if ( isset( self::$request['plugin'] ) - && $plugin === self::$request['plugin'] - && 'deactivate' === self::$request['action'] - ) { - - return true; - - } elseif ( isset( self::$request['plugins'] ) - && 'deactivate-selected' === self::$request['action'] - && in_array( $plugin, self::$request['plugins'], true ) - ) { - return true; - } - - return false; - } - - /** - * Check Capabilities. - * - * We want no one else but users with activate_plugins or above to be able to active this plugin. - * - * @since 0.5.1 - * @return bool false if no caps, else true. - */ - private static function check_caps() { - - if ( current_user_can( 'activate_plugins' ) ) { - return true; - } - - return false; - } -} + + */ + +/** + * Fired during plugin deactivation. + * + * This class defines all code necessary to run during the plugin's deactivation. + * + * @since 0.4.3 + */ +class Disable_Blog_Deactivator { + + /** + * The $_REQUEST during plugin deactivation. + * + * @since 0.5.1 + * @access private + * @var array $request The $_REQUEST array during plugin deactivation. + */ + private static $request = array(); + + /** + * The $_REQUEST['plugin'] during plugin deactivation. + * + * @since 0.5.1 + * @access private + * @var string $plugin The $_REQUEST['plugin'] value during plugin deactivation. + */ + private static $plugin = 'disable-blog'; + + /** + * Deactivate the plugin. + * + * Checks if the plugin was (safely) deactivated. + * Place to add any custom action during plugin deactivation. + * + * @since 0.4.3 + * @since 0.4.9 add flush rewrite rules. + * @since 0.5.1 + */ + public static function deactivate() { + + if ( false === self::get_request() + || false === self::validate_request( self::$plugin ) + || false === self::check_caps() + ) { + if ( isset( $_REQUEST['plugin'] ) ) { + if ( ! check_admin_referer( 'deactivate-plugin_' . self::$request['plugin'] ) ) { + exit; + } + } elseif ( isset( $_REQUEST['checked'] ) ) { + if ( ! check_admin_referer( 'bulk-plugins' ) ) { + exit; + } + } + } + + /** + * The plugin is now safely deactivated. + */ + + wp_cache_delete( 'comments-0', 'counts' ); + delete_transient( 'wc_count_comments' ); + flush_rewrite_rules(); + } + + /** + * Get the request. + * + * Gets the $_REQUEST array and checks if necessary keys are set. + * Populates self::request with necessary and sanitized values. + * + * @since 0.5.1 + * @return bool|array false or self::$request array. + */ + private static function get_request() { + + if ( ! empty( $_REQUEST ) + && isset( $_REQUEST['_wpnonce'] ) + && isset( $_REQUEST['action'] ) + ) { + if ( isset( $_REQUEST['plugin'] ) ) { + if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'deactivate-plugin_' . sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ) ) ) { + + self::$request['plugin'] = sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ); + self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); + + return self::$request; + + } + } elseif ( isset( $_REQUEST['checked'] ) ) { + if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'bulk-plugins' ) ) { + + self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); + self::$request['plugins'] = array_map( 'sanitize_text_field', (array) wp_unslash( $_REQUEST['checked'] ) ); + + return self::$request; + + } + } + } + + return false; + } + + /** + * Validate the Request data. + * + * Validates the data in $_REQUEST is matching this plugin and action. + * + * @since 0.5.1 + * @param string $plugin The Plugin folder/name.php. + * @return bool false if either plugin or action does not match, else true. + */ + private static function validate_request( $plugin ) { + + if ( isset( self::$request['plugin'] ) + && $plugin === self::$request['plugin'] + && 'deactivate' === self::$request['action'] + ) { + + return true; + + } elseif ( isset( self::$request['plugins'] ) + && 'deactivate-selected' === self::$request['action'] + && in_array( $plugin, self::$request['plugins'], true ) + ) { + return true; + } + + return false; + } + + /** + * Check Capabilities. + * + * We want no one else but users with activate_plugins or above to be able to active this plugin. + * + * @since 0.5.1 + * @return bool false if no caps, else true. + */ + private static function check_caps(): bool { + return current_user_can( 'activate_plugins' ); + } +} diff --git a/includes/class-disable-blog-integrations.php b/includes/class-disable-blog-integrations.php index a17c19a..d95fa3d 100644 --- a/includes/class-disable-blog-integrations.php +++ b/includes/class-disable-blog-integrations.php @@ -1,124 +1,108 @@ -is_plugin_active( 'disable-comments/disable-comments.php' ) || class_exists( 'Disable_Comments' ) ) { - return true; - } - - return false; - } - - /** - * Check if WooCommerce is active. - * - * @since 0.5.3 - * @return bool - */ - public function is_woocommerce_active() { - - // Check if the Disable Comments plugin is active. - if ( $this->is_plugin_active( 'woocommerce/woocommerce.php' ) || function_exists( 'WC' ) ) { - return true; - } - - return false; - } - - /** - * Turn the comments object back into an array if WooCommerce is active. - * - * This is only necessary for version of WooCommerce prior to 2.6.3, where it failed - * to check/convert the $comment object into an array. - * - * @since 0.4.3 - * @since 0.5.3 Moved to the Disable_Blog_Integrations class. - * @param object $comments the array of comments. - * @param int $post_id the post id. - * @return array - */ - public function filter_woocommerce_comment_count( $comments, $post_id ) { - - if ( 0 === $post_id && $this->woocommerce_version_check( '2.6.2' ) ) { - $comments = (array) $comments; - } - - return $comments; - } - - /** - * Check if the WooCommerce version is less than the checked version. - * - * @since 0.5.4 - * @param string $checked_version The version to check against. - * @param string $check The comparison operator. - * @return bool - */ - private function woocommerce_version_check( $checked_version, $check = '<=' ) { - - // Check if WooCommerce is active. - if ( $this->is_woocommerce_active() ) { - - // Figure out the version of WooCommerce. - if ( defined( 'WC_VERSION' ) ) { - $woo_version = WC_VERSION; - } elseif ( defined( 'WOOCOMMERCE_VERSION' ) ) { - $woo_version = WOOCOMMERCE_VERSION; - } else { - return false; - } - - // Check if the WooCommerce version is less than the checked version. - return version_compare( $woo_version, $checked_version, $check ); - } - - return false; - } -} +is_plugin_active( 'disable-comments/disable-comments.php' ) || class_exists( 'Disable_Comments' ) ); + } + + /** + * Check if WooCommerce is active. + * + * @since 0.5.3 + * @return bool + */ + public function is_woocommerce_active() { + return ( $this->is_plugin_active( 'woocommerce/woocommerce.php' ) || function_exists( 'WC' ) ); + } + + /** + * Turn the comments object back into an array if WooCommerce is active. + * + * This is only necessary for version of WooCommerce prior to 2.6.3, where it failed + * to check/convert the $comment object into an array. + * + * @since 0.4.3 + * @since 0.5.3 Moved to the Disable_Blog_Integrations class. + * @param object $comments the array of comments. + * @param int $post_id the post id. + * @return array + */ + public function filter_woocommerce_comment_count( $comments, $post_id ) { + + if ( 0 === $post_id && $this->woocommerce_version_check( '2.6.2' ) ) { + $comments = (array) $comments; + } + + return $comments; + } + + /** + * Check if the WooCommerce version is less than the checked version. + * + * @since 0.5.4 + * @param string $checked_version The version to check against. + * @param string $check The comparison operator. + * @return bool + */ + private function woocommerce_version_check( $checked_version, $check = '<=' ) { + + // Check if WooCommerce is active. + if ( $this->is_woocommerce_active() ) { + + // Figure out the version of WooCommerce. + if ( defined( 'WC_VERSION' ) ) { + $woo_version = WC_VERSION; + } elseif ( defined( 'WOOCOMMERCE_VERSION' ) ) { + $woo_version = WOOCOMMERCE_VERSION; + } else { + return false; + } + + // Check if the WooCommerce version is less than the checked version. + return version_compare( $woo_version, $checked_version, $check ); + } + + return false; + } +} From 49c8c23c1569569ed65b09bbd01d6d2dbefeae98 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 21:52:07 -0800 Subject: [PATCH 12/20] escape value separately from empty check --- includes/class-disable-blog-functions.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/includes/class-disable-blog-functions.php b/includes/class-disable-blog-functions.php index 34b5a2d..e5cf667 100644 --- a/includes/class-disable-blog-functions.php +++ b/includes/class-disable-blog-functions.php @@ -88,7 +88,8 @@ private function parse_query_string( $url ) { $allowed_query_vars = array_filter( $allowed_query_vars, function ( $value ) { - return ! empty( esc_html( $value ) ); + $esc_value = esc_html( $value ); + return ! empty( $esc_value ); } ); $query_vars = array_intersect_key( $query_vars, array_flip( $allowed_query_vars ) ); @@ -98,13 +99,15 @@ function ( $value ) { $query_vars = array_filter( $query_vars, function ( $value ) { - return ! empty( esc_html( $value ) ); + $esc_value = esc_html( $value ); + return ! empty( $esc_value ); } ); $query_vars = array_filter( $query_vars, function ( $value ) { - return ! empty( esc_html( $value ) ); + $esc_value = esc_html( $value ); + return ! empty( $esc_value ); }, ARRAY_FILTER_USE_KEY ); @@ -153,7 +156,7 @@ private function get_redirect_status_code( $current_url, $redirect_url ) { * which is the WP core default for safe redirects. * * @since 0.5.0 - * @param string $url the fallback url. + * @param string $url the fallback url. * @return string */ public function wp_safe_redirect_fallback( $url ) { From fc4726a7f2c757fd10df5376cdb8ee856c90b5af Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 21:57:08 -0800 Subject: [PATCH 13/20] Update composer.json --- composer.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c6e561b..fcfa66c 100644 --- a/composer.json +++ b/composer.json @@ -21,6 +21,10 @@ "require": { "php": ">=8.1", "composer/installers": "^2.0" + }, + "config": { + "allow-plugins": { + "composer/installers": true + } } } - From d574e695e4e82525dda50ecda95c3a6476a32451 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 22:41:02 -0800 Subject: [PATCH 14/20] attempt to resolve phpstan and cs issues --- includes/class-disable-blog-admin.php | 2 +- includes/class-disable-blog-functions.php | 4 +- includes/class-disable-blog-integrations.php | 2 +- includes/class-disable-blog-public.php | 2 +- includes/functions.php | 306 +++++++++---------- phpstan.neon.dist | 1 + 6 files changed, 158 insertions(+), 159 deletions(-) diff --git a/includes/class-disable-blog-admin.php b/includes/class-disable-blog-admin.php index 825d341..26cae7b 100644 --- a/includes/class-disable-blog-admin.php +++ b/includes/class-disable-blog-admin.php @@ -1023,7 +1023,7 @@ public function page_post_states( $post_states, $post ) { */ public function site_status_tests( $tests ) { - if ( isset( $tests['direct']['rest_availability'] ) && is_callable( array( $this, 'get_test_rest_availability' ) ) ) { + if ( isset( $tests['direct']['rest_availability'] ) ) { $tests['direct']['rest_availability']['test'] = array( $this, 'get_test_rest_availability' ); } diff --git a/includes/class-disable-blog-functions.php b/includes/class-disable-blog-functions.php index e5cf667..d6bfa63 100644 --- a/includes/class-disable-blog-functions.php +++ b/includes/class-disable-blog-functions.php @@ -83,7 +83,7 @@ private function parse_query_string( $url ) { * @param array $allowed_query_vars an array of the allowed query variable keys. * @return array */ - $allowed_query_vars = apply_filters( 'dwpb_allowed_query_vars', array() ); + $allowed_query_vars = (array) apply_filters( 'dwpb_allowed_query_vars', array() ); if ( ! empty( $allowed_query_vars ) && is_array( $allowed_query_vars ) ) { $allowed_query_vars = array_filter( $allowed_query_vars, @@ -113,7 +113,7 @@ function ( $value ) { ); // if we have any query variables, add it to the url. - if ( ! empty( $query_vars ) && is_array( $query_vars ) ) { + if ( ! empty( $query_vars ) ) { $url = add_query_arg( $query_vars, $url ); } diff --git a/includes/class-disable-blog-integrations.php b/includes/class-disable-blog-integrations.php index d95fa3d..5c020d3 100644 --- a/includes/class-disable-blog-integrations.php +++ b/includes/class-disable-blog-integrations.php @@ -29,7 +29,7 @@ public function is_plugin_active( $plugin ) { // Check if the is_plugin_active function is available. if ( ! function_exists( 'is_plugin_active' ) ) { - include_once ABSPATH . 'wp-admin/includes/plugin.php'; + include_once ABSPATH . 'wp-admin/includes/plugin.php'; // @phpstan-ignore includeOnce.fileNotFound } // Check if the the plugin is active. diff --git a/includes/class-disable-blog-public.php b/includes/class-disable-blog-public.php index 659db75..758c5c3 100644 --- a/includes/class-disable-blog-public.php +++ b/includes/class-disable-blog-public.php @@ -456,7 +456,7 @@ private function get_disabled_xmlrpc_methods() { * @param array $methods_to_remove an array of all the XMLRPC methods to disable. * @return array|bool */ - $methods_to_remove = apply_filters( 'dwpb_disabled_xmlrpc_methods', $methods_to_remove ); + $methods_to_remove = (array) apply_filters( 'dwpb_disabled_xmlrpc_methods', $methods_to_remove ); // filter any invalid entries out before returning the array. return is_array( $methods_to_remove ) ? array_filter( $methods_to_remove, 'is_string' ) : false; // phpcs:ignore diff --git a/includes/functions.php b/includes/functions.php index bf11399..3f37e8e 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -1,154 +1,152 @@ - - */ - -/** - * Get all the post types that support a featured (like 'comments') - * - * @link http://codex.wordpress.org/Function_Reference/register_post_type#Arguments - * - * @since 0.1.0 - * @since 0.4.0 pulled out of class, unique function. - * @since 0.5.0 added $args parameter for passing specific arguments to get_post_types. - * @since 0.5.3 added caching. - * @see register_post_types(), get_post_types(), get_object_taxonomies() - * @param string $feature the feature in question. - * @param array $args the arguments passed to get_post_types. - * @return array|bool A list of post type names that support the featured or false if nothing found. - */ -function dwpb_post_types_with_feature( $feature, $args = array() ) { - - // Bail if no feature is passed. - if ( ! $feature || ! is_string( $feature ) ) { - return false; - } - - // Check the cache. - $cache_name = 'post-types-supporting-' . esc_attr( $feature ); - $post_types_with_feature = wp_cache_get( $cache_name, 'post-types-by-feature' ); - - // If the cache is empty, then get the post types. - if ( false === $post_types_with_feature || ! is_array( $post_types_with_feature ) ) { - - $post_types = get_post_types( $args, 'names' ); - - $post_types_with_feature = array(); - foreach ( $post_types as $post_type ) { - if ( post_type_supports( $post_type, $feature ) && 'post' !== $post_type ) { - $post_types_with_feature[] = $post_type; - } - } - - // Keep the array if there are any, otherwise make it return false. - $post_types_with_feature = empty( $post_types_with_feature ) ? false : $post_types_with_feature; - - wp_cache_set( $cache_name, $post_types_with_feature, 'post-types-by-feature' ); - - } - - /** - * Filter the returned "post types with feature". - * - * This function is used to determine if there are any post types with a specific - * feature, not including the `post` type. Often this toggles on/off options in - * the plugin. For instance, if comments are only support by posts, then they will - * be disabled and the options-discussion.php admin page redirected. - * - * @since 0.4.0 - * @since 0.5.0 Added the $args parameter. - * @param array|bool $post_types_with_feature an array of post types support this feature or false if none. - * @param array $args the arguments passed to get_post_types. - * @return array|bool A list of post type names that support the featured or false if nothing found. - */ - return apply_filters( "dwpb_post_types_supporting_{$feature}", $post_types_with_feature, $args ); -} - -/** - * Get post types that have a specific taxonomy - * (a combination of get_post_types and get_object_taxonomies) - * - * Basically, we need to know if there are post types, other than 'post' - * that support the taxonomy. - * - * @since 0.2.0 - * @since 0.4.0 pulled out of class, unique function. - * @see register_post_types(), get_post_types(), get_object_taxonomies() - * @uses get_post_types(), get_object_taxonomies(), apply_filters() - * @param string|object $taxonomy Required. The taxonomy object or taxonomy slug. - * @param array|string $args Optional. An array of key => value arguments to match against the post type objects. Default empty array. - * @param string $output Optional. The type of output to return. Accepts post type 'names' or 'objects'. Default 'names'. - * @return array|bool A list of post type names or objects that have the taxonomy or false if nothing found. - */ -function dwpb_post_types_with_tax( $taxonomy, $args = array(), $output = 'names' ) { - - $post_types = get_post_types( $args, $output ); - - // We just need the taxonomy name. - if ( is_object( $taxonomy ) ) { - $taxonomy = $taxonomy->name; - - // If it's not an object or a string, it won't work, so send it back. - } elseif ( ! is_string( $taxonomy ) ) { - return false; - } - - // setup the finished product. - $post_types_with_tax = array(); - foreach ( $post_types as $post_type ) { - - // If post types are objects. - if ( is_object( $post_type ) ) { - $type = $post_type->name; - // If post types are strings. - } elseif ( is_string( $post_type ) ) { - $type = $post_type; - } else { - $type = ''; - } - - // is the post included in this post type, but not 'post' type. - if ( ! empty( $type ) && 'post' !== $type ) { - $taxonomies = get_object_taxonomies( $type, 'names' ); - if ( in_array( $taxonomy, $taxonomies, true ) ) { - $post_types_with_tax[] = $post_type; - } - } - } - - /** - * Filter the returned value of "post types with tax". - * - * This function is used to determine if there are any post types using a taxonomy, - * not including the `post` type. This is used to determine if there are custom - * post types using the built-in `post_tag` and `category` taxonomies and toggle - * off related features if they are not being used by anything other than built-in posts. - * - * @since 0.4.0 - * @param mixed $null Null for no override, otherwise pass an array of post type slugs. - * @param string|object $taxonomy The current taxonomy slug. - * @param array|bool $post_types_with_tax An array of post types use this taxonomy or false if none. - * @param array $args An array of key => value arguments to match against the post type objects. Default empty array. - * @param string $output The type of output to return, either 'names' or 'objects'. - * @return mixed A list of post type names that use this taxonomy or false if nothing found. - */ - $override = apply_filters( 'dwpb_taxonomy_support', null, $taxonomy, $post_types, $args, $output ); - if ( ! is_null( $override ) ) { - return $override; - } - - // If there aren't any results, return false. - if ( empty( $post_types_with_tax ) ) { - return false; - } else { - return $post_types_with_tax; - } -} + + */ + +/** + * Get all the post types that support a featured (like 'comments') + * + * @link http://codex.wordpress.org/Function_Reference/register_post_type#Arguments + * + * @since 0.1.0 + * @since 0.4.0 pulled out of class, unique function. + * @since 0.5.0 added $args parameter for passing specific arguments to get_post_types. + * @since 0.5.3 added caching. + * @see register_post_types(), get_post_types(), get_object_taxonomies() + * @param string $feature the feature in question. + * @param array $args the arguments passed to get_post_types. + * @return array|bool A list of post type names that support the featured or false if nothing found. + */ +function dwpb_post_types_with_feature( $feature, $args = array() ) { + + // Bail if no feature is passed. + if ( ! $feature ) { + return false; + } + + // Check the cache. + $cache_name = 'post-types-supporting-' . esc_attr( $feature ); + $post_types_with_feature = wp_cache_get( $cache_name, 'post-types-by-feature' ); + + // If the cache is empty, then get the post types. + if ( false === $post_types_with_feature || ! is_array( $post_types_with_feature ) ) { + + $post_types = get_post_types( $args, 'names' ); + + $post_types_with_feature = array(); + foreach ( $post_types as $post_type ) { + if ( post_type_supports( $post_type, $feature ) && 'post' !== $post_type ) { + $post_types_with_feature[] = $post_type; + } + } + + // Keep the array if there are any, otherwise make it return false. + $post_types_with_feature = empty( $post_types_with_feature ) ? false : $post_types_with_feature; + + wp_cache_set( $cache_name, $post_types_with_feature, 'post-types-by-feature' ); + } + + /** + * Filter the returned "post types with feature". + * + * This function is used to determine if there are any post types with a specific + * feature, not including the `post` type. Often this toggles on/off options in + * the plugin. For instance, if comments are only support by posts, then they will + * be disabled and the options-discussion.php admin page redirected. + * + * @since 0.4.0 + * @since 0.5.0 Added the $args parameter. + * @param array|bool $post_types_with_feature an array of post types support this feature or false if none. + * @param array $args the arguments passed to get_post_types. + * @return array|bool A list of post type names that support the featured or false if nothing found. + */ + return apply_filters( "dwpb_post_types_supporting_{$feature}", $post_types_with_feature, $args ); +} + +/** + * Get post types that have a specific taxonomy + * (a combination of get_post_types and get_object_taxonomies) + * + * Basically, we need to know if there are post types, other than 'post' + * that support the taxonomy. + * + * @since 0.2.0 + * @since 0.4.0 pulled out of class, unique function. + * @see register_post_types(), get_post_types(), get_object_taxonomies() + * @uses get_post_types(), get_object_taxonomies(), apply_filters() + * @param string|object $taxonomy Required. The taxonomy object or taxonomy slug. + * @param array|string $args Optional. An array of key => value arguments to match against the post type objects. Default empty array. + * @param string $output Optional. The type of output to return. Accepts post type 'names' or 'objects'. Default 'names'. + * @return array|bool A list of post type names or objects that have the taxonomy or false if nothing found. + */ +function dwpb_post_types_with_tax( $taxonomy, $args = array(), $output = 'names' ) { + + if ( ! $taxonomy || ! is_object( $taxonomy ) ) { + return false; + } + + // We just need the taxonomy name. + if ( is_object( $taxonomy ) ) { + $taxonomy = $taxonomy->name; + } + + // Get all the post types. + $post_types = get_post_types( $args, $output ); + + // setup the finished product. + $post_types_with_tax = array(); + foreach ( $post_types as $post_type ) { + + // If post types are objects. + if ( is_object( $post_type ) ) { + $type = $post_type->name; + // If post types are strings. + } else { + $type = (string) $post_type; + } + + // is the post included in this post type, but not 'post' type. + if ( ! empty( $type ) && 'post' !== $type ) { + $taxonomies = get_object_taxonomies( $type, 'names' ); + if ( in_array( $taxonomy, $taxonomies, true ) ) { + $post_types_with_tax[] = $post_type; + } + } + } + + /** + * Filter the returned value of "post types with tax". + * + * This function is used to determine if there are any post types using a taxonomy, + * not including the `post` type. This is used to determine if there are custom + * post types using the built-in `post_tag` and `category` taxonomies and toggle + * off related features if they are not being used by anything other than built-in posts. + * + * @since 0.4.0 + * @param mixed $null Null for no override, otherwise pass an array of post type slugs. + * @param string|object $taxonomy The current taxonomy slug. + * @param array|bool $post_types_with_tax An array of post types use this taxonomy or false if none. + * @param array $args An array of key => value arguments to match against the post type objects. Default empty array. + * @param string $output The type of output to return, either 'names' or 'objects'. + * @return mixed A list of post type names that use this taxonomy or false if nothing found. + */ + $override = apply_filters( 'dwpb_taxonomy_support', null, $taxonomy, $post_types, $args, $output ); + if ( ! is_null( $override ) ) { + return $override; + } + + // If there aren't any results, return false. + if ( empty( $post_types_with_tax ) ) { + return false; + } else { + return $post_types_with_tax; + } +} diff --git a/phpstan.neon.dist b/phpstan.neon.dist index d089034..c1750ff 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,6 +3,7 @@ includes: parameters: level: 5 inferPrivatePropertyTypeFromConstructor: true + treatPhpDocTypesAsCertain: false scanFiles: - tests/bootstrap.php - includes/functions.php From e74356a3d9c2c8492d34ad7dd7043c680281faea Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 22:43:17 -0800 Subject: [PATCH 15/20] fix tab vs spacing in phpstan.neon.dist --- .editorconfig | 4 ++++ phpstan.neon.dist | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index c3e5bed..88f2cc5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -23,3 +23,7 @@ trim_trailing_whitespace = false [*.{txt,php}] end_of_line = crlf + +[*.neon.dist] +indent_style = space +indent_size = 2 diff --git a/phpstan.neon.dist b/phpstan.neon.dist index c1750ff..5968c2d 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,7 +3,7 @@ includes: parameters: level: 5 inferPrivatePropertyTypeFromConstructor: true - treatPhpDocTypesAsCertain: false + treatPhpDocTypesAsCertain: false scanFiles: - tests/bootstrap.php - includes/functions.php From 79fe7373eb995f441f14db81868b88a02c585b91 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 22:48:18 -0800 Subject: [PATCH 16/20] address some phpstan issues --- includes/class-disable-blog-functions.php | 2 +- includes/class-disable-blog-public.php | 2 +- includes/functions.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/class-disable-blog-functions.php b/includes/class-disable-blog-functions.php index d6bfa63..b314ece 100644 --- a/includes/class-disable-blog-functions.php +++ b/includes/class-disable-blog-functions.php @@ -84,7 +84,7 @@ private function parse_query_string( $url ) { * @return array */ $allowed_query_vars = (array) apply_filters( 'dwpb_allowed_query_vars', array() ); - if ( ! empty( $allowed_query_vars ) && is_array( $allowed_query_vars ) ) { + if ( ! empty( $allowed_query_vars ) ) { $allowed_query_vars = array_filter( $allowed_query_vars, function ( $value ) { diff --git a/includes/class-disable-blog-public.php b/includes/class-disable-blog-public.php index 758c5c3..659db75 100644 --- a/includes/class-disable-blog-public.php +++ b/includes/class-disable-blog-public.php @@ -456,7 +456,7 @@ private function get_disabled_xmlrpc_methods() { * @param array $methods_to_remove an array of all the XMLRPC methods to disable. * @return array|bool */ - $methods_to_remove = (array) apply_filters( 'dwpb_disabled_xmlrpc_methods', $methods_to_remove ); + $methods_to_remove = apply_filters( 'dwpb_disabled_xmlrpc_methods', $methods_to_remove ); // filter any invalid entries out before returning the array. return is_array( $methods_to_remove ) ? array_filter( $methods_to_remove, 'is_string' ) : false; // phpcs:ignore diff --git a/includes/functions.php b/includes/functions.php index 3f37e8e..2011178 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -94,7 +94,7 @@ function dwpb_post_types_with_tax( $taxonomy, $args = array(), $output = 'names' } // We just need the taxonomy name. - if ( is_object( $taxonomy ) ) { + if ( is_object( $taxonomy ) ) { // phpstan:ignore function.alreadyNarrowedType $taxonomy = $taxonomy->name; } From 860edc949e0ffb3c5aa127a3cace5a0b19c66057 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 22:48:22 -0800 Subject: [PATCH 17/20] Update readme.txt --- readme.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/readme.txt b/readme.txt index 270a8fc..5b70a0a 100644 --- a/readme.txt +++ b/readme.txt @@ -92,7 +92,6 @@ There are numerous filters available to change the way this plugin works. Refer - Tested up to PHP 8.3 - Bump minimum PHP to 8.1, lowest version being maintained. - Bump minimum WordPress version to 5.9, aligning with minimum PHP support. -- Added `composer.json` = 0.5.4 = - Tested up to WordPress 6.4.2. From eff111c0739a1639f67e844c11346e4254b960e6 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 22:50:40 -0800 Subject: [PATCH 18/20] another go at php stan issues --- includes/functions.php | 2 +- phpstan.neon.dist | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index 2011178..ceb9c61 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -94,7 +94,7 @@ function dwpb_post_types_with_tax( $taxonomy, $args = array(), $output = 'names' } // We just need the taxonomy name. - if ( is_object( $taxonomy ) ) { // phpstan:ignore function.alreadyNarrowedType + if ( is_object( $taxonomy ) ) { // @phpstan-ignore function.alreadyNarrowedType $taxonomy = $taxonomy->name; } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 5968c2d..cf36602 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -12,8 +12,6 @@ parameters: paths: - includes/ ignoreErrors: - # Filtered value can return a bool, so this error is incorrect. - - '#^Else branch is unreachable because ternary operator condition is always true\.$#' # Method can return bool via filter. - '#^Method Disable_Blog_Public::get_disabled_xmlrpc_methods\(\) never returns bool so it can be removed from the return type\.#' # errors we don't care about. From 0463df57df8cb1c16d12b6d0e94f990d368a944c Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 22:59:09 -0800 Subject: [PATCH 19/20] fix phpcs line ending error, us lf not crlf --- .editorconfig | 2 +- disable-blog.php | 164 +- includes/class-disable-blog-activator.php | 306 +- includes/class-disable-blog-admin.php | 2870 +++++++++--------- includes/class-disable-blog-deactivator.php | 306 +- includes/class-disable-blog-functions.php | 482 +-- includes/class-disable-blog-i18n.php | 74 +- includes/class-disable-blog-integrations.php | 216 +- includes/class-disable-blog-loader.php | 374 +-- includes/class-disable-blog-public.php | 1132 +++---- includes/class-disable-blog.php | 814 ++--- includes/functions.php | 304 +- 12 files changed, 3522 insertions(+), 3522 deletions(-) diff --git a/.editorconfig b/.editorconfig index 88f2cc5..adcdbd1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -22,7 +22,7 @@ indent_size = 2 trim_trailing_whitespace = false [*.{txt,php}] -end_of_line = crlf +end_of_line = lf [*.neon.dist] indent_style = space diff --git a/disable-blog.php b/disable-blog.php index 4757394..0f84ff6 100644 --- a/disable-blog.php +++ b/disable-blog.php @@ -1,82 +1,82 @@ -run(); -} -add_action( 'plugins_loaded', 'run_disable_blog', 10, 0 ); +run(); +} +add_action( 'plugins_loaded', 'run_disable_blog', 10, 0 ); diff --git a/includes/class-disable-blog-activator.php b/includes/class-disable-blog-activator.php index 55bc12b..436a4bc 100644 --- a/includes/class-disable-blog-activator.php +++ b/includes/class-disable-blog-activator.php @@ -1,153 +1,153 @@ - - */ - -/** - * Fired during plugin activation. - * - * This class defines all code necessary to run during the plugin's activation. - * - * @since 0.4.3 - */ -class Disable_Blog_Activator { - - /** - * The $_REQUEST during plugin activation. - * - * @since 0.5.1 - * @access private - * @var array $request The $_REQUEST array during plugin activation. - */ - private static $request = array(); - - /** - * The $_REQUEST['plugin'] during plugin activation. - * - * @since 0.5.1 - * @access private - * @var string $plugin The $_REQUEST['plugin'] value during plugin activation. - */ - private static $plugin = 'disable-blog'; - - /** - * Activate the plugin. - * - * Checks if the plugin was (safely) activated. - * Place to add any custom action during plugin activation. - * - * @since 0.4.3 - * @since 0.4.9 flush rewrite rules. - * @since 0.5.1 update to "Better WP Plugin Boilerplate" functions. - */ - public static function activate() { - - if ( false === self::get_request() - || false === self::validate_request( self::$plugin ) - || false === self::check_caps() - ) { - if ( isset( $_REQUEST['plugin'] ) ) { - if ( ! check_admin_referer( 'activate-plugin_' . self::$request['plugin'] ) ) { - exit; - } - } elseif ( isset( $_REQUEST['checked'] ) ) { - if ( ! check_admin_referer( 'bulk-plugins' ) ) { - exit; - } - } - } - - /** - * The plugin is now safely activated. - */ - - wp_cache_delete( 'comments-0', 'counts' ); - delete_transient( 'wc_count_comments' ); - flush_rewrite_rules(); - } - - /** - * Get the request. - * - * Gets the $_REQUEST array and checks if necessary keys are set. - * Populates self::request with necessary and sanitized values. - * - * @since 0.5.1 - * @return bool|array false or self::$request array. - */ - private static function get_request() { - - if ( ! empty( $_REQUEST ) - && isset( $_REQUEST['_wpnonce'] ) - && isset( $_REQUEST['action'] ) - ) { - if ( isset( $_REQUEST['plugin'] ) ) { - if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'activate-plugin_' . sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ) ) ) { - - self::$request['plugin'] = sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ); - self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); - - return self::$request; - - } - } elseif ( isset( $_REQUEST['checked'] ) ) { - if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'bulk-plugins' ) ) { - - self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); - self::$request['plugins'] = array_map( 'sanitize_text_field', (array) wp_unslash( $_REQUEST['checked'] ) ); - - return self::$request; - - } - } - } - - return false; - } - - /** - * Validate the Request data. - * - * Validates the data in $_REQUEST is matching this plugin and action. - * - * @since 0.5.1 - * @param string $plugin The Plugin folder/name.php. - * @return bool false if either plugin or action does not match, else true. - */ - private static function validate_request( $plugin ) { - - if ( isset( self::$request['plugin'] ) - && $plugin === self::$request['plugin'] - && 'activate' === self::$request['action'] - ) { - - return true; - - } elseif ( isset( self::$request['plugins'] ) - && 'activate-selected' === self::$request['action'] - && in_array( $plugin, self::$request['plugins'], true ) - ) { - return true; - } - - return false; - } - - /** - * Check Capabilities. - * - * We want no one else but users with activate_plugins or above to be able to active this plugin. - * - * @since 0.5.1 - * @return bool false if no caps, else true. - */ - private static function check_caps() { - return current_user_can( 'activate_plugins' ); - } -} + + */ + +/** + * Fired during plugin activation. + * + * This class defines all code necessary to run during the plugin's activation. + * + * @since 0.4.3 + */ +class Disable_Blog_Activator { + + /** + * The $_REQUEST during plugin activation. + * + * @since 0.5.1 + * @access private + * @var array $request The $_REQUEST array during plugin activation. + */ + private static $request = array(); + + /** + * The $_REQUEST['plugin'] during plugin activation. + * + * @since 0.5.1 + * @access private + * @var string $plugin The $_REQUEST['plugin'] value during plugin activation. + */ + private static $plugin = 'disable-blog'; + + /** + * Activate the plugin. + * + * Checks if the plugin was (safely) activated. + * Place to add any custom action during plugin activation. + * + * @since 0.4.3 + * @since 0.4.9 flush rewrite rules. + * @since 0.5.1 update to "Better WP Plugin Boilerplate" functions. + */ + public static function activate() { + + if ( false === self::get_request() + || false === self::validate_request( self::$plugin ) + || false === self::check_caps() + ) { + if ( isset( $_REQUEST['plugin'] ) ) { + if ( ! check_admin_referer( 'activate-plugin_' . self::$request['plugin'] ) ) { + exit; + } + } elseif ( isset( $_REQUEST['checked'] ) ) { + if ( ! check_admin_referer( 'bulk-plugins' ) ) { + exit; + } + } + } + + /** + * The plugin is now safely activated. + */ + + wp_cache_delete( 'comments-0', 'counts' ); + delete_transient( 'wc_count_comments' ); + flush_rewrite_rules(); + } + + /** + * Get the request. + * + * Gets the $_REQUEST array and checks if necessary keys are set. + * Populates self::request with necessary and sanitized values. + * + * @since 0.5.1 + * @return bool|array false or self::$request array. + */ + private static function get_request() { + + if ( ! empty( $_REQUEST ) + && isset( $_REQUEST['_wpnonce'] ) + && isset( $_REQUEST['action'] ) + ) { + if ( isset( $_REQUEST['plugin'] ) ) { + if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'activate-plugin_' . sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ) ) ) { + + self::$request['plugin'] = sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ); + self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); + + return self::$request; + + } + } elseif ( isset( $_REQUEST['checked'] ) ) { + if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'bulk-plugins' ) ) { + + self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); + self::$request['plugins'] = array_map( 'sanitize_text_field', (array) wp_unslash( $_REQUEST['checked'] ) ); + + return self::$request; + + } + } + } + + return false; + } + + /** + * Validate the Request data. + * + * Validates the data in $_REQUEST is matching this plugin and action. + * + * @since 0.5.1 + * @param string $plugin The Plugin folder/name.php. + * @return bool false if either plugin or action does not match, else true. + */ + private static function validate_request( $plugin ) { + + if ( isset( self::$request['plugin'] ) + && $plugin === self::$request['plugin'] + && 'activate' === self::$request['action'] + ) { + + return true; + + } elseif ( isset( self::$request['plugins'] ) + && 'activate-selected' === self::$request['action'] + && in_array( $plugin, self::$request['plugins'], true ) + ) { + return true; + } + + return false; + } + + /** + * Check Capabilities. + * + * We want no one else but users with activate_plugins or above to be able to active this plugin. + * + * @since 0.5.1 + * @return bool false if no caps, else true. + */ + private static function check_caps() { + return current_user_can( 'activate_plugins' ); + } +} diff --git a/includes/class-disable-blog-admin.php b/includes/class-disable-blog-admin.php index 26cae7b..16c343b 100644 --- a/includes/class-disable-blog-admin.php +++ b/includes/class-disable-blog-admin.php @@ -1,1435 +1,1435 @@ - - */ - -/** - * The admin-specific functionality of the plugin. - * - * Defines the plugin name, version, and contains all the admin functions. - * - * @since 0.4.0 - */ -class Disable_Blog_Admin { - - /** - * The ID of this plugin. - * - * @since 0.4.0 - * @access private - * @var string $plugin_name The ID of this plugin. - */ - private $plugin_name; - - /** - * The version of this plugin. - * - * @since 0.4.0 - * @access private - * @var string $version The current version of this plugin. - */ - private $version; - - /** - * Object with common utility functions. - * - * @since 0.5.0 - * @access private - * @var object - */ - private $functions; - - /** - * Initialize the class and set its properties. - * - * @since 0.4.0 - * @param string $plugin_name The name of this plugin. - * @param string $version The version of this plugin. - */ - public function __construct( $plugin_name, $version ) { - $this->plugin_name = $plugin_name; - $this->version = $version; - $this->functions = new Disable_Blog_Functions(); - } - - /** - * Add various links to plugin page - * - * @since 0.5.1 - * @param array $links the array of plugin links. - * @param string $file the current plugin file. - * @return array - */ - public function plugin_links( $links, $file ) { - - /** Capability Check */ - if ( ! current_user_can( 'install_plugins' ) ) { - return $links; - } - - if ( basename( dirname( $file ) ) === $this->plugin_name ) { - $meta = array( - 'support' => ' ' . __( 'Support', 'disable-blog' ) . '', - 'review' => ' ' . __( 'Review', 'disable-blog' ) . '', - 'donate' => ' ' . __( 'Donate', 'disable-blog' ) . '', - 'github' => ' ' . __( 'GitHub', 'disable-blog' ) . '', - ); - $links = array_merge( $links, $meta ); - } - - return $links; - } - - /** - * Disable public arguments of the 'post' post type. - * - * @since 0.4.2 - * @since 0.4.9 removed rest api specific filter and updated function - * for disabling all public-facing aspects of the 'post' post type. - * @return void - */ - public function modify_post_type_arguments() { - - global $wp_post_types; - - if ( isset( $wp_post_types['post'] ) ) { - $arguments_to_remove = array( - 'has_archive', - 'public', - 'publicly_queryable', - 'rewrite', - 'query_var', - 'show_ui', - 'show_in_admin_bar', - 'show_in_nav_menus', - 'show_in_menu', - 'show_in_rest', - ); - - foreach ( $arguments_to_remove as $arg ) { - if ( isset( $wp_post_types['post']->$arg ) ) { - // @codingStandardsIgnoreStart - phpcs doesn't like using variables like this. - $wp_post_types['post']->$arg = false; - // @codingStandardsIgnoreEnd - } - } - - // exclude from search. - $wp_post_types['post']->exclude_from_search = true; - - // remove supports. - $wp_post_types['post']->supports = array(); - } - } - - /** - * Disable public arguments of the 'category' and 'post_tag' taxonomies. - * - * Only disables these if the 'post' post type is the only post type using them. - * - * @since 0.4.9 - * @uses dwpb_post_types_with_tax() - * @return void - */ - public function modify_taxonomies_arguments() { - - global $wp_taxonomies; - $taxonomies = array( 'category', 'post_tag' ); - - foreach ( $taxonomies as $tax ) { - if ( isset( $wp_taxonomies[ $tax ] ) ) { - - // remove 'post' from object types. - if ( isset( $wp_taxonomies[ $tax ]->object_type ) ) { - if ( is_array( $wp_taxonomies[ $tax ]->object_type ) ) { - $key = array_search( 'post', $wp_taxonomies[ $tax ]->object_type, true ); - if ( false !== $key ) { - unset( $wp_taxonomies[ $tax ]->object_type[ $key ] ); - } - } - } - - // only modify the public arguments if 'post' is the only post type. - // using this taxonomy. - if ( ! dwpb_post_types_with_tax( $tax ) ) { - - // public arguments to remove. - $arguments_to_remove = array( - 'has_archive', - 'public', - 'publicly_queryable', - 'query_var', - 'show_ui', - 'show_tagcloud', - 'show_in_admin_bar', - 'show_in_quick_edit', - 'show_in_nav_menus', - 'show_admin_column', - 'show_in_menu', - 'show_in_rest', - ); - - foreach ( $arguments_to_remove as $arg ) { - if ( isset( $wp_taxonomies[ $tax ]->$arg ) ) { - // @codingStandardsIgnoreStart - phpcs doesn't like using variables like this. - $wp_taxonomies[ $tax ]->$arg = false; - // @codingStandardsIgnoreEnd - } - } - } - } - } - } - - /** - * Redirect blog-related admin pages - * - * @uses dwpb_post_types_with_tax() - * @since 0.1.0 - * @since 0.4.0 added single post edit screen redirect - * @since 0.5.0 condensed page rediects into a foreach loop with common structure - * for filters and functions used to check redirect conditions. - * @return void - */ - public function redirect_admin_pages() { - - global $pagenow; - - if ( ! isset( $pagenow ) ) { - return; - } - - $screen = get_current_screen(); - - // on multisite: Do not redirect if we are on a network page. - if ( is_multisite() && is_callable( array( $screen, 'in_admin' ) ) && $screen->in_admin( 'network' ) ) { - return; - } - - // setup false redirect url value for default/final check. - $dashboard_url = admin_url( 'index.php' ); - $redirect_url = false; - - // The admin page slugs to potentially be redirected. - $admin_redirects = array( - 'post', - 'edit', - 'post-new', - 'edit-tags', - 'term', - 'edit-comments', - 'options-discussion', - 'options-writing', - 'tools', - ); - - // cycle through each admin page, checking if we need to redirect. - foreach ( $admin_redirects as $pagename ) { - - // Filter names are all underscores. - $filternme = str_replace( '-', '_', $pagename ); - - // Custom function within this class used to check if the page needs to be redirected. - $function = 'redirect_admin_' . $filternme; - - // build the filter. - $filter = 'dwpb_' . $function; - - // If this is the right page, then setup the redirect url. - // make sure we're on that page right now. - // make sure the function used to check/provide the url is callable. - if ( $this->is_admin_page( $pagename ) - && is_callable( array( $this, $function ) ) ) { - - // Check the function for redirect clearance, or custom url. - $redirect = $this->$function(); - - // Set a redirect url variable to check against. - $potential_redirect_url = esc_url_raw( $redirect ); - - // If it's set to `true` then redirect to the dashboard, - // if it's set to a url, redirect to that url. - if ( true === $redirect || ! empty( $potential_redirect_url ) ) { - - // Either this is a custom redirect url or 'true', which defaults the url to the dashboard. - if ( ! empty( $potential_redirect_url ) ) { - $url = $potential_redirect_url; - } else { - $url = $dashboard_url; - } - - /** - * The redirect url used for this admin page. - * - * Example: use 'dwpb_redirect_admin_options_tools' to change the redirect url - * used for the options-tools.php page. Note `-` strings are converted to `_` - * in the filter name. - * - * @since 0.4.0 - * @since 0.5.0 combine common filters. - * @param string $url the url to redirct to, defaults to dashboard. - */ - $redirect_url = apply_filters( $filter, $url ); - - break; // no need to keep looping. - - } - } - } - - /** - * Global admin url redirect filter. - * - * @since 0.5.0 - * @param string $redirect_url The redirect url. - */ - $redirect_url = apply_filters( 'dwpb_admin_redirect_url', $redirect_url ); - - /** - * Redirect blog related admin pages. - * - * @since 0.4.0 - * @since 0.5.0 removed 3rd `$current_url` param. - * @param bool $bool True to enable, default is true. - * @param string $redirect_url The url to being used in the redirect. - */ - if ( $redirect_url && apply_filters( 'dwpb_redirect_admin', true, $redirect_url ) ) { - $this->functions->redirect( $redirect_url ); - } - } - - /** - * The admin redirect arguments checked to redirect the post.php screen. - * - * @since 0.5.0 - * @return bool - */ - public function redirect_admin_post() { - - // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. - return ( isset( $_GET['post'] ) && 'post' == get_post_type( $_GET['post'] ) ); - // @codingStandardsIgnoreEnd - } - - /** - * The admin redirect arguments checked to redirect the edit.php screen. - * - * @since 0.5.0 - * @return bool|string - */ - public function redirect_admin_edit() { - - // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. - if ( ! isset( $_GET['post_type'] ) || isset( $_GET['post_type'] ) && $_GET['post_type'] == 'post' ) { - // @codingStandardsIgnoreEnd - - return admin_url( 'edit.php?post_type=page' ); - - } - - return false; - } - - /** - * The admin redirect arguments checked to redirect the post-new.php screen. - * - * @since 0.5.0 - * @return bool|string - */ - public function redirect_admin_post_new() { - - // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. - if ( ( ! isset( $_GET['post_type'] ) || isset( $_GET['post_type'] ) && $_GET['post_type'] == 'post' ) ) { - // @codingStandardsIgnoreEnd - - return admin_url( 'post-new.php?post_type=page' ); - - } - - return false; - } - - /** - * The admin redirect arguments checked to redirect the term.php screen. - * - * @since 0.5.0 - * @return bool|string - */ - public function reidrect_admin_term() { - - // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. - return ( isset( $_GET['taxonomy'] ) && ! dwpb_post_types_with_tax( $_GET['taxonomy'] ) ); - // @codingStandardsIgnoreEnd - } - - /** - * The admin redirect arguments checked to redirect the edit-tags.php screen. - * - * @since 0.5.0 - * @return bool|string - */ - public function redirect_admin_edit_tags() { - - // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. - return ( isset( $_GET['taxonomy'] ) && ! dwpb_post_types_with_tax( $_GET['taxonomy'] ) ); - // @codingStandardsIgnoreEnd - } - - /** - * The admin redirect arguments checked to redirect the edit-comments.php screen. - * - * @uses dwpb_post_types_with_feature() - * @since 0.5.0 - * @return bool - */ - public function redirect_admin_edit_comments() { - - /** - * Will only work comments are only supported by 'post' type, - * note that pages and attachments support comments by default. - */ - return ! dwpb_post_types_with_feature( 'comments' ); - } - - /** - * The admin redirect arguments checked to redirect the options-discussion.php screen. - * - * The same checks are performed by the edit-comments check, so this is a wrapper function. - * - * @since 0.5.0 - * @return bool - */ - public function redirect_admin_options_discussion() { - return $this->redirect_admin_edit_comments(); - } - - /** - * The admin redirect arguments checked to redirect the options-writing.php screen. - * - * @since 0.5.0 - * @return bool|string - */ - public function redirect_admin_options_writing() { - - // Redirect writing options to general options. - if ( $this->remove_writing_options() ) { - return admin_url( 'options-general.php' ); - } - - return false; - } - - /** - * The admin redirect arguments checked to redirect the options-tools.php screen. - * - * @since 0.5.0 - * @return bool - */ - public function redirect_admin_options_tools() { - - /** - * The isset( $_GET['page'] ) check is to confirm the page - * isn't a 3rd party plugin's option page built into the tools page. - */ - // @codingStandardsIgnoreStart - phpcs wants to nounce this, but that's not needed. - return ! isset( $_GET['page'] ); - // @codingStandardsIgnoreEnd - } - - /** - * Remove Post Related Menus - * - * @uses dwpb_post_types_with_tax() - * @uses dwpb_post_types_with_feature() - * @link http://wordpress.stackexchange.com/questions/57464/remove-posts-from-admin-but-show-a-custom-post - * @since 0.1.0 - * @since 0.4.0 added tools and discussion subpages. - * @return void - */ - public function remove_menu_pages() { - - // Main pages to be removed. - $remove_pages = array( 'edit.php' ); - - // If no post type supports comments, then remove the edit-comments.php page from the admin. - if ( ! dwpb_post_types_with_feature( 'comments' ) ) { - $remove_pages[] = 'edit-comments.php'; - } - - /** - * Top level admin pages to remove. - * - * @since 0.4.0 - * @param array $remove_pages Array of page strings. - */ - $pages = apply_filters( 'dwpb_menu_pages_to_remove', $remove_pages ); - foreach ( $pages as $page ) { - remove_menu_page( $page ); - } - - // Submenu Pages. - $remove_subpages = array( - 'tools.php' => array( 'tools.php' ), - 'options-general.php' => array(), - ); - - // Remove the writings page, if the filter tells us so. - if ( $this->remove_writing_options() ) { - $remove_subpages['options-general.php'][] = 'options-writing.php'; - } - - // If there are no other post types supporting comments, remove the discussion page. - if ( ! dwpb_post_types_with_feature( 'comments' ) ) { - $remove_subpages['options-general.php'][] = 'options-discussion.php'; // Settings > Discussion. - } - - /** - * Admin subpages to be removed. - * - * @since 0.4.0 - * @since 0.5.0 in order to account for multiple subpages with a common parent - * the `subpages` are now in arrays - * @param array $remove_subpages Array of page => subpages where subpages is an array of strings. - */ - $subpages = apply_filters( 'dwpb_menu_subpages_to_remove', $remove_subpages ); - foreach ( $subpages as $page => $subpages ) { - if ( is_array( $subpages ) && ! empty( $subpages ) ) { - foreach ( $subpages as $subpage ) { - remove_submenu_page( $page, $subpage ); - } - } elseif ( is_string( $subpages ) ) { // for backwards compatibility. - remove_submenu_page( $page, $subpages ); - } - } - } - - /** - * Filter the body classes for admin screens to toggle on plugin specific styles. - * - * @since 0.4.7 - * @param string $classes the admin body classes, which is a string *not* an array. - * @return string - */ - public function admin_body_class( $classes ) { - - if ( $this->has_front_page() ) { - $classes .= ' disabled-blog'; - } - - return $classes; - } - - /** - * Filter for removing the writing options page. - * - * @since 0.4.5 - * @return bool - */ - public function remove_writing_options() { - - /** - * Toggle the options-writing page on/off. - * - * Defaults to false because other plugins often extend this page. - * Setting this to true will create a redirect for the page - * and remove it from the admin menu. - * - * See: https://wordpress.org/support/topic/disabling-writing-settings-panel-is-a-problem/ - * - * @since 0.4.5 - * @since 0.5.0 renamed from `dwpb_redirect_admin_options_writing` - * to `dwpb_remove_options_writing`. The old filter name - * is not used by the admin redirect function to filter - * the redirect url used for this page. - * @param bool $bool Defaults to false, keeping the writing page visible. - */ - return (bool) apply_filters( 'dwpb_remove_options_writing', false ); - } - - /** - * Remove blog-related admin bar links - * - * @uses dwpb_post_types_with_feature() - * @link http://www.paulund.co.uk/how-to-remove-links-from-wordpress-admin-bar - * @since 0.1.0 - * @return void - */ - public function remove_admin_bar_links() { - - global $wp_admin_bar; - - // If only posts support comments, then remove comment from admin bar. - if ( ! dwpb_post_types_with_feature( 'comments' ) ) { - $wp_admin_bar->remove_menu( 'comments' ); - } - - // Remove New Post from Content. - $wp_admin_bar->remove_node( 'new-post' ); - } - - /** - * Hide all comments from 'post' post type - * - * @uses dwpb_post_types_with_feature() - * @since 0.1.0 - * @param object $comments the comments query object. - * @return object - */ - public function comment_filter( $comments ) { - - global $pagenow; - - if ( ! isset( $pagenow ) ) { - return $comments; - } - - // Filter out comments from post. - $post_types = dwpb_post_types_with_feature( 'comments' ); - if ( $post_types && $this->is_admin_page( 'edit-comments' ) ) { - $comments->query_vars['post_type'] = $post_types; - } - - return $comments; - } - - /** - * Remove post-related dashboard widgets - * - * @uses dwpb_post_types_with_feature() - * @since 0.1.0 - * @since 0.4.1 dry out the code with a foreach loop - * @return void - */ - public function remove_dashboard_widgets() { - - // Remove post-specific widgets only, others obscured/modified elsewhere as necessary. - $metabox = array( - 'dashboard_quick_press' => 'side', // Quick Press. - 'dashboard_recent_drafts' => 'side', // Recent Drafts. - 'dashboard_incoming_links' => 'normal', // Incoming Links. - 'dashboard_activity' => 'normal', // Activity. - ); - - foreach ( $metabox as $metabox_id => $context ) { - - /** - * Filter to change the dashboard widgets beinre removed. - * - * Filter name based on the name of the widget above, - * For instance: `dwpb_disable_dashboard_quick_press` for the Quick Press widget. - * - * @since 0.4.1 - * @param bool $bool True to remove the dashboard widget. - */ - if ( apply_filters( "dwpb_disable_{$metabox_id}", true ) ) { - remove_meta_box( $metabox_id, 'dashboard', $context ); - } - } - } - - /** - * Throw an admin notice if settings are misconfigured. - * - * If there is not a homepage correctly set, then redirects don't work. - * The intention with this notice is to highlight what is needed for the plugin to function properly. - * - * @since 0.2.0 - * @since 0.4.7 Changed this to an error notice function. - * @return void - */ - public function admin_notices() { - - // only throwing this notice in the edit.php, plugins.php, and options-reading.php admin pages. - $current_screen = get_current_screen(); - $screens = array( - 'plugins', - 'options-reading', - 'edit', - ); - if ( ! ( isset( $current_screen->base ) && in_array( $current_screen->base, $screens, true ) ) ) { - return; - } - - // Throw a notice if the we don't have a front page. - if ( ! $this->has_front_page() ) { - - // The second part of the notice depends on which screen we're on. - if ( 'options-reading' === $current_screen->base ) { - - // translators: Direct the user to set a homepage in the current screen. - $message_link = ' ' . __( 'Select a page for your homepage below.', 'disable-blog' ); - - } else { // If we're not on the Reading Options page, then direct the user there. - - $reading_options_page = get_admin_url( null, 'options-reading.php' ); - - // translators: Direct the user to the Reading Settings admin page. - $message_link = ' ' . sprintf( __( 'Change in Reading Settings.', 'disable-blog' ), $reading_options_page ); - - } - - // translators: Prompt to configure the site for static homepage and posts page. - $message = __( 'Disable Blog is not fully active until a static page is selected for the site\'s homepage.', 'disable-blog' ) . $message_link; - - printf( '

%s

', 'notice notice-error', wp_kses_post( $message ) ); - - // If we have a front page set, but no posts page or they are the same,. - // Then let the user know the expected behavior of these two. - } elseif ( 'options-reading' === $current_screen->base - && get_option( 'page_for_posts' ) === get_option( 'page_on_front' ) ) { - - // translators: Warning that the homepage and blog page cannot be the same, the post page is redirected to the homepage. - $message = __( 'Disable Blog requires a homepage that is different from the post page. The "posts page" will be redirected to the homepage.', 'disable-blog' ); - - printf( '

%s

', 'notice notice-error', esc_attr( $message ) ); - - } - } - - /** - * Check that the site has a front page set in the Settings > Reading. - * - * @since 0.4.7 - * @return bool - */ - public function has_front_page() { - return 'page' === get_option( 'show_on_front' ) && absint( get_option( 'page_on_front' ) ); - } - - /** - * Kill the Press This functionality - * - * @since 0.2.0 - * @return void - */ - public function disable_press_this() { - wp_die( '"Press This" functionality has been disabled.' ); - } - - /** - * Remove post related widgets - * - * @since 0.2.0 - * @since 0.4.0 simplified unregistering and added dwpb_unregister_widgets filter. - * @return void - */ - public function remove_widgets() { - - // Unregister blog-related widgets. - $widgets = array( - 'WP_Widget_Recent_Comments', // Recent Comments. - 'WP_Widget_Tag_Cloud', // Tag Cloud. - 'WP_Widget_Categories', // Categories. - 'WP_Widget_Archives', // Archives. - 'WP_Widget_Calendar', // Calendar. - 'WP_Widget_Links', // Links. - 'WP_Widget_Recent_Posts', // Recent Posts. - 'WP_Widget_RSS', // RSS. - 'WP_Widget_Tag_Cloud', // Tag Cloud. - ); - foreach ( $widgets as $widget ) { - - /** - * The ability to stop the widget unregister. - * - * @since 0.4.0 - * - * @param bool $bool True to unregister the widget. - * @param string $widget The name of the widget to be unregistered. - */ - if ( apply_filters( 'dwpb_unregister_widgets', true, $widget ) ) { - unregister_widget( $widget ); - } - } - } - - /** - * Filter the widget removal & check for reasons to not remove specific widgets. - * - * If there are post types using the comments or built-in taxonomies outside of the default 'post' - * then we stop the plugin from removing the widget. - * - * @uses dwpb_post_types_with_feature() - * @since 0.4.0 - * @param bool $show true to show. - * @param string $widget the widget name. - * @return bool - */ - public function filter_widget_removal( $show, $widget ) { - - // Remove Categories Widget. - if ( 'WP_Widget_Categories' === $widget && dwpb_post_types_with_tax( 'category' ) ) { - $show = false; - } - - // Remove Recent Comments Widget if posts are the only type with comments. - if ( 'WP_Widget_Recent_Comments' === $widget && dwpb_post_types_with_feature( 'comments' ) ) { - $show = false; - } - - // Remove Tag Cloud. - if ( 'WP_Widget_Tag_Cloud' === $widget && dwpb_post_types_with_tax( 'post_tag' ) ) { - $show = false; - } - - return $show; - } - - /** - * Register the stylesheets for the admin area. - * - * @since 0.4.0 - * @return void - */ - public function enqueue_styles() { - wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . '../assets/css/disable-blog-admin.css', array(), $this->version, 'all' ); - } - - /** - * Register the scripts used in the admin area. - * - * @since 0.5.0 - * @return void - */ - public function enqueue_scripts() { - - wp_enqueue_script( $this->plugin_name, DWPB_URL . 'assets/js/disable-blog-admin.js', array(), $this->version, true ); - - // Localize some information on the page. - global $pagenow; - $js_vars = array( - 'page' => str_replace( '.php', '', $pagenow ), - 'categoriesSupported' => (bool) dwpb_post_types_with_tax( 'category' ), - 'tagsSupported' => (bool) dwpb_post_types_with_tax( 'post_tag' ), - 'commentsSupported' => (bool) dwpb_post_types_with_feature( 'comments' ), - ); - - wp_localize_script( $this->plugin_name, 'dwpb', $js_vars ); - } - - /** - * Check that we're on a specific admin page. - * - * @since 0.4.8 - * @param string $page the page slug. - * @return bool - */ - public function is_admin_page( $page ) { - global $pagenow; - return is_admin() && isset( $pagenow ) && is_string( $pagenow ) && $page . '.php' === $pagenow; - } - - /** - * Filter the comment counts to remove comments to 'post' post type. - * - * @since 0.4.0 - * @since 0.4.3 Moved everything into the post id check and updated caching functions to match wp_get_comments. - * - * @see wp_get_comments() - * @param object $comments the comment count object. - * @param int $post_id the post id. - * @return object - */ - public function filter_wp_count_comments( $comments, $post_id ) { - - // if this is grabbing all the comments, filter out the 'post' comments. - if ( 0 === $post_id ) { - - // Keep caching in place, note that on activation the plugin clears this cache. - // see class-disable-blog-activator.php. - $count = wp_cache_get( 'comments-0', 'counts' ); - if ( false !== $count ) { - return $count; - } - - $comments = $this->get_comment_counts(); - - $comments['moderated'] = $comments['awaiting_moderation']; - unset( $comments['awaiting_moderation'] ); - - $comments = (object) $comments; - wp_cache_set( 'comments-0', $comments, 'counts' ); - - } - - return $comments; - } - - /** - * Alter the comment counts on the admin comment table to remove comments associated with posts. - * - * @since 0.4.0 - * @param array $views all the views. - * @return array - */ - public function filter_admin_table_comment_count( $views ) { - - global $current_screen; - - if ( 'edit-comments' === $current_screen->id ) { - - $updated_counts = $this->get_comment_counts(); - foreach ( $views as $view => $text ) { - if ( isset( $updated_counts[ $view ] ) ) { - $views[ $view ] = preg_replace( '/\([^)]+\)/', '(' . $updated_counts[ $view ] . ')', $views[ $view ] ); - } - } - } - - return $views; - } - - /** - * Retrieve the comment counts without the 'post' comments. - * - * @since 0.4.0 - * @since 0.4.3 Removed Unused "count" function. - * @see get_comment_count() - * @see https://developer.wordpress.org/reference/functions/esc_sql/ - * @return array - */ - public function get_comment_counts() { - - global $wpdb; - - // Set up the counts, we'll be adding to this array. - $comment_count = array( - 'moderated' => 0, - 'approved' => 0, - 'awaiting_moderation' => 0, - 'spam' => 0, - 'trash' => 0, - 'post-trashed' => 0, - 'total_comments' => 0, - 'all' => 0, - ); - - // Get the post types that support comments. - $supported_post_types = dwpb_post_types_with_feature( 'comments' ); - - // Return an array of empty counts if there are no post types that support comments. - if ( empty( $supported_post_types ) || ! is_array( $supported_post_types ) ) { - return $comment_count; - } - - // Sanitizing the post type strings. - $sanitized_post_types = (array) array_map( 'esc_sql', $supported_post_types ); - - // Implode the post types into a string for the query. - $in_post_types = implode( "','", $sanitized_post_types ); - - // Grab the comments that are associated with supported post types only. - // @codingStandardsIgnoreStart -- The get_results function doesn't need a wpdb->prepare here because $in_post_types is sanitized above. - $totals = (array) $wpdb->get_results( - "SELECT comment_approved, COUNT( * ) AS total - FROM {$wpdb->comments} - WHERE comment_post_ID in ( - SELECT ID - FROM {$wpdb->posts} - WHERE post_type in ('{$in_post_types}') - AND post_status = 'publish') - GROUP BY comment_approved", - ARRAY_A - ); - // @codingStandardsIgnoreEnd - - foreach ( $totals as $row ) { - switch ( $row['comment_approved'] ) { - case 'trash': - $comment_count['trash'] = $row['total']; - break; - case 'post-trashed': - $comment_count['post-trashed'] = $row['total']; - break; - case 'spam': - $comment_count['spam'] = $row['total']; - $comment_count['total_comments'] += $row['total']; - break; - case '1': - $comment_count['approved'] = $row['total']; - $comment_count['total_comments'] += $row['total']; - $comment_count['all'] += $row['total']; - break; - case '0': - $comment_count['awaiting_moderation'] = $row['total']; - $comment_count['moderated'] = $comment_count['awaiting_moderation']; - $comment_count['total_comments'] += $row['total']; - $comment_count['all'] += $row['total']; - break; - default: - break; - } - } - - return array_map( 'intval', $comment_count ); - } - - /** - * Clear out the status of all post comments. - * - * @since 0.4.0 - * @param bool $open true if comments are open. - * @param int $post_id the post id. - * @return bool - */ - public function filter_comment_status( $open, $post_id ) { - - return ( 'post' === get_post_type( $post_id ) ) ? false : $open; - } - - /** - * Clear comments from 'post' post type. - * - * @since 0.4.0 - * @param array $comments the array of comments. - * @param int $post_id the post id. - * @return array - */ - public function filter_existing_comments( $comments, $post_id ) { - return ( 'post' === get_post_type( $post_id ) ) ? array() : $comments; - } - - /** - * Filters the default post display states used in the posts list table. - * - * @since 0.5.0 - * @param string[] $post_states An array of post display states. - * @param WP_Post $post The current post object. - * @return array - */ - public function page_post_states( $post_states, $post ) { - - if ( $this->has_front_page() && absint( get_option( 'page_for_posts' ) ) === $post->ID ) { - // translators: This string is used to indicate that the blog page is redirected to the homepage. - $post_states['dwpb-redirected'] = __( 'Redirected to the homepage', 'disable-blog' ); - } - - return $post_states; - } - - /** - * Removes the Site Health check for the post REST API. - * - * @uses dwpb_get_test_rest_availability() - * @since 0.5.0 - * @param array $tests the tests, of course. - * @return array - */ - public function site_status_tests( $tests ) { - - if ( isset( $tests['direct']['rest_availability'] ) ) { - $tests['direct']['rest_availability']['test'] = array( $this, 'get_test_rest_availability' ); - } - - return $tests; - } - - /** - * Replaces the core REST Availability Site Health check. - * - * Used by the site_status_tests filter in class-disable-blog-admin.php. - * - * Copied directly from https://developer.wordpress.org/reference/classes/wp_site_health/get_test_rest_availability/ but with the 'post' type updated to 'page' in the rest url. - * - * @see https://make.wordpress.org/core/2019/04/25/site-health-check-in-5-2/ - * @since 0.5.0 - * @return array - */ - public function get_test_rest_availability() { - - $result = array( - 'label' => __( 'The REST API is available' ), - 'status' => 'good', - 'badge' => array( - 'label' => __( 'Performance' ), - 'color' => 'blue', - ), - 'description' => sprintf( - '

%s

', - __( 'The REST API is one way WordPress, and other applications, communicate with the server. One example is the block editor screen, which relies on this to display, and save, your posts and pages.' ) - ), - 'actions' => '', - 'test' => 'rest_availability', - ); - - $cookies = wp_unslash( $_COOKIE ); - $timeout = 10; - $headers = array( - 'Cache-Control' => 'no-cache', - 'X-WP-Nonce' => wp_create_nonce( 'wp_rest' ), - ); - - /** This filter is documented in wp-includes/class-wp-http-streams.php */ - $sslverify = apply_filters( 'https_local_ssl_verify', false ); - - // Include Basic auth in loopback requests. - // @codingStandardsIgnoreStart - if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { - $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); - } - // @codingStandardsIgnoreEnd - - // -- here's the money, change this from 'post' to 'page'. - $url = rest_url( 'wp/v2/types/page' ); - - // The context for this is editing with the new block editor. - $url = add_query_arg( - array( - 'context' => 'edit', - ), - $url - ); - - $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) ); - - if ( is_wp_error( $r ) ) { - $result['status'] = 'critical'; - - $result['label'] = __( 'The REST API encountered an error' ); - - $result['description'] .= sprintf( - '

%s

', - sprintf( - '%s
%s', - __( 'The REST API request failed due to an error.' ), - sprintf( - /* translators: 1: The WordPress error message. 2: The WordPress error code. */ - __( 'Error: %1$s (%2$s)' ), - $r->get_error_message(), - $r->get_error_code() - ) - ) - ); - } elseif ( 200 !== wp_remote_retrieve_response_code( $r ) ) { - $result['status'] = 'recommended'; - - $result['label'] = __( 'The REST API encountered an unexpected result' ); - - $result['description'] .= sprintf( - '

%s

', - sprintf( - /* translators: 1: The HTTP error code. 2: The HTTP error message. */ - __( 'The REST API call gave the following unexpected result: (%1$d) %2$s.' ), - wp_remote_retrieve_response_code( $r ), - esc_html( wp_remote_retrieve_body( $r ) ) - ) - ); - } else { - $json = json_decode( wp_remote_retrieve_body( $r ), true ); - - if ( false !== $json && ! isset( $json['capabilities'] ) ) { - $result['status'] = 'recommended'; - - $result['label'] = __( 'The REST API did not behave correctly' ); - - $result['description'] .= sprintf( - '

%s

', - sprintf( - /* translators: %s: The name of the query parameter being tested. */ - __( 'The REST API did not process the %s query parameter correctly.' ), - 'context' - ) - ); - } - } - - return $result; - } - - /** - * Filter the taxonomy count on post_tag and category screens. - * - * Used on the post_tag and category screens for custom post types. - * - * @since 0.5.0 - * @param array $actions an array of actions. - * @param object $tag the current taxonomy object. - * @return array - */ - public function filter_taxonomy_count( $actions, $tag ) { - - if ( isset( $tag->taxonomy, $tag->count, $tag->term_id ) - && ( 'post_tag' === $tag->taxonomy || 'category' === $tag->taxonomy ) ) { - $screen = get_current_screen(); - $count = $this->get_term_post_count_by_type( $tag->term_id, $tag->taxonomy, $screen->post_type ); - $tag->count = $count; - } - - return $actions; - } - - /** - * Return the post count for a term based by post type. - * - * @since 0.5.0 - * @param int $term_id the current term id. - * @param string $taxonomy the taxonomy slug. - * @param string $post_type the post type slug. - * @return int - */ - public function get_term_post_count_by_type( $term_id, $taxonomy, $post_type ) { - - $args = array( - 'fields' => 'ids', - 'posts_per_page' => 500, - 'post_type' => $post_type, - 'no_found_rows' => true, - 'update_post_meta_cache' => false, - 'tax_query' => array( - array( - 'taxonomy' => $taxonomy, - 'field' => 'id', - 'terms' => intval( $term_id ), - ), - ), - ); - $query = new WP_Query( $args ); - - if ( count( $query->posts ) > 0 ) { - return count( $query->posts ); - } else { - return 0; - } - } - - /** - * Remove posts column form user table. - * - * @since 0.5.0 - * @param array $columns the column slugs => title array. - * @return array - */ - public function manage_users_columns( $columns ) { - - /** - * Disable the user post column. - * - * @since 0.5.0 - * @param bool $bool True to remove the column, defaults to true. - * @return bool - */ - $disable_user_posts_column = apply_filters( 'dpwb_disable_user_post_column', true ); - - if ( isset( $columns['posts'] ) && true === (bool) $disable_user_posts_column ) { - unset( $columns['posts'] ); - } - - // Get the current screen. - $screen = get_current_screen(); - - // cycle through the post types to be displayed. - $post_types = $this->user_column_post_types(); - foreach ( $post_types as $post_type ) { - /** - * Create a new column for 'pages' similar to the original 'post' column. - * - * @since 0.5.0 - * @param bool $bool True to remove the column, defaults to true. - * @return bool - */ - if ( apply_filters( "dpwb_create_user_{$post_type}_column", true ) - // Taken from core functions for users page, don't display the posts column on site-users-network core page. - // see wp-admin/includes/class-wp-users-list-table.php. - && isset( $screen->id ) && 'site-users-network' !== $screen->id ) { - - $post_type_obj = get_post_type_object( $post_type ); - if ( isset( $post_type_obj->labels->name ) ) { - $columns[ $post_type ] = $post_type_obj->labels->name; - } - } - } - - return $columns; - } - - /** - * Mange the new custom user columns. - * - * @since 0.5.0 - * @see wp-admin/includes/class-wp-users-list-table.php. - * @param string $output the column output. - * @param string $column_name the current column slug. - * @param int $user_id the current user's id. - * @return string - */ - public function manage_users_custom_column( $output, $column_name, $user_id ) { - - $post_types = $this->user_column_post_types(); - foreach ( $post_types as $post_type ) { - // Create new column output, mimicking core's 'post' column but for the 'page' and supported custom post types. - if ( $post_type === $column_name ) { - - // make sure we can grab valid post type labels before continuing. - $post_type_obj = get_post_type_object( $post_type ); - if ( ! isset( $post_type_obj->labels->name, $post_type_obj->labels->singular_name ) ) { - continue; - } - - // Get the couunts. - $page_counts = count_many_users_posts( array( $user_id ), $post_type ); - $page_count = absint( $page_counts[ $user_id ] ); - - // Note that we have to explicitly add the query variable for post type in order - // to avoid it being stripped by the admin redirect on the edit.php page. - // @codingStandardsIgnoreStart - phpcs doesn't like the mismatched placeholders, but it works. - $output = sprintf( - '%s', - "edit.php?post_type={$post_type}&author={$user_id}", - $page_count, - sprintf( - // translators: %1$s: Number of pieces of content by this author. %2$s and %3$s are the singular and plural names, respectively, for the content type. For example: "1 page by this author" and "2 pages by this author". - _n( '%1$s %2$s by this author', '%1$s %3$s by this author', $page_count, 'disable-blog' ), - number_format_i18n( $page_count ), - $post_type_obj->labels->singular_name, - $post_type_obj->labels->name - ) - ); - // @codingStandardsIgnoreEnd - } - } - - return $output; - } - - /** - * Grab the post types that - * - * @since 0.5.0 - * @return array - */ - private function user_column_post_types() { - - // Include any post types using author archives. - $post_types = $this->functions->author_archive_post_types(); - - // The author_archive_post_type function returns false if empty, but we need an array. - $post_types = empty( $post_types ) ? array() : $post_types; - - // Also include pages, which is not in the author archive by default, - // however we run array_unique in case the 'page' post type has been added - // to the author archives. - $post_types = array_unique( array_merge( array( 'page' ), $post_types ) ); - - /** - * Filter the post types that appear in the user table. - * - * @since 0.5.0 - * @param array $post_types an array of post type slugs. - * @return array - */ - return apply_filters( 'dwpb_admin_user_post_types', $post_types ); - } - - /** - * Remove the user's "view" link if we are not supporting author archives. - * - * @since 0.5.0 - * @param array $actions an array of actions in a key => output format. - * @param object $user_object the current user, WP_User object. - * @return array - */ - public function user_row_actions( $actions, $user_object ) { - - if ( true === $this->functions->disable_author_archives() - && isset( $actions['view'] ) ) { - unset( $actions['view'] ); - } - - return $actions; - } - - /** - * Alter the customizer view to mimic the reading settings. - * - * @since 0.5.0 - * @return void - */ - public function customizer_styles() { - - if ( $this->has_front_page() ) { - ?> - - plugin_name . '-customizer-scripts', DWPB_URL . 'assets/js/disable-blog-customizer.js', array( 'jquery', 'customize-controls' ), $this->version, true ); - - // Localize some information on the page. - $js_vars = array( - // translators: This text appears in the Customizer > Homepage Settings and provides the context for the homepage select field there. - 'homepageSettingsText' => __( 'You can choose what\'s displayed on the homepage of your site. To set a static homepage, create or select the page below.', 'disable-blog' ), - ); - wp_localize_script( $this->plugin_name . '-customizer-scripts', 'dwpbCustomizer', $js_vars ); - } - - /** - * Remove the default posts page notice and replace it with a new one. - * - * @since 0.5.0 - * @return void - */ - public function update_posts_page_notice() { - - if ( (int) get_option( 'page_for_posts' ) === get_the_ID() ) { - remove_action( 'edit_form_after_title', '_wp_posts_page_notice' ); - add_action( 'edit_form_after_title', array( $this, 'posts_page_notice' ) ); - } - } - - /** - * Replaces the default blog page posts notice. - * - * @since 0.5.0 - * @return void - */ - public function posts_page_notice() { - - // translators: this notice informs the user why the blog page editor is disabled and that it is redirected to the homepage. - echo '

' . __( 'You are currently editing the page that shows your latest posts, which is redirected to the homepage because the blog is disabled.', 'disable-blog' ) . '

'; // phpcs:ignore - } - - /** - * Filter the available tags in the permalink settings. - * - * @since 0.5.1 - * @param array $available_tags The available permalink tags. - * @return array - */ - public function available_permalink_structure_tags( $available_tags ) { - - // Remove the category permalink structure if categories are not supported. - if ( isset( $available_tags['category'] ) && ! dwpb_post_types_with_tax( 'category' ) ) { - unset( $available_tags['category'] ); - } - - // Remove the author tag if author archives are disabled. - if ( isset( $available_tags['author'] ) && $this->functions->disable_author_archives() ) { - unset( $available_tags['author'] ); - } - - return $available_tags; - } -} + + */ + +/** + * The admin-specific functionality of the plugin. + * + * Defines the plugin name, version, and contains all the admin functions. + * + * @since 0.4.0 + */ +class Disable_Blog_Admin { + + /** + * The ID of this plugin. + * + * @since 0.4.0 + * @access private + * @var string $plugin_name The ID of this plugin. + */ + private $plugin_name; + + /** + * The version of this plugin. + * + * @since 0.4.0 + * @access private + * @var string $version The current version of this plugin. + */ + private $version; + + /** + * Object with common utility functions. + * + * @since 0.5.0 + * @access private + * @var object + */ + private $functions; + + /** + * Initialize the class and set its properties. + * + * @since 0.4.0 + * @param string $plugin_name The name of this plugin. + * @param string $version The version of this plugin. + */ + public function __construct( $plugin_name, $version ) { + $this->plugin_name = $plugin_name; + $this->version = $version; + $this->functions = new Disable_Blog_Functions(); + } + + /** + * Add various links to plugin page + * + * @since 0.5.1 + * @param array $links the array of plugin links. + * @param string $file the current plugin file. + * @return array + */ + public function plugin_links( $links, $file ) { + + /** Capability Check */ + if ( ! current_user_can( 'install_plugins' ) ) { + return $links; + } + + if ( basename( dirname( $file ) ) === $this->plugin_name ) { + $meta = array( + 'support' => ' ' . __( 'Support', 'disable-blog' ) . '', + 'review' => ' ' . __( 'Review', 'disable-blog' ) . '', + 'donate' => ' ' . __( 'Donate', 'disable-blog' ) . '', + 'github' => ' ' . __( 'GitHub', 'disable-blog' ) . '', + ); + $links = array_merge( $links, $meta ); + } + + return $links; + } + + /** + * Disable public arguments of the 'post' post type. + * + * @since 0.4.2 + * @since 0.4.9 removed rest api specific filter and updated function + * for disabling all public-facing aspects of the 'post' post type. + * @return void + */ + public function modify_post_type_arguments() { + + global $wp_post_types; + + if ( isset( $wp_post_types['post'] ) ) { + $arguments_to_remove = array( + 'has_archive', + 'public', + 'publicly_queryable', + 'rewrite', + 'query_var', + 'show_ui', + 'show_in_admin_bar', + 'show_in_nav_menus', + 'show_in_menu', + 'show_in_rest', + ); + + foreach ( $arguments_to_remove as $arg ) { + if ( isset( $wp_post_types['post']->$arg ) ) { + // @codingStandardsIgnoreStart - phpcs doesn't like using variables like this. + $wp_post_types['post']->$arg = false; + // @codingStandardsIgnoreEnd + } + } + + // exclude from search. + $wp_post_types['post']->exclude_from_search = true; + + // remove supports. + $wp_post_types['post']->supports = array(); + } + } + + /** + * Disable public arguments of the 'category' and 'post_tag' taxonomies. + * + * Only disables these if the 'post' post type is the only post type using them. + * + * @since 0.4.9 + * @uses dwpb_post_types_with_tax() + * @return void + */ + public function modify_taxonomies_arguments() { + + global $wp_taxonomies; + $taxonomies = array( 'category', 'post_tag' ); + + foreach ( $taxonomies as $tax ) { + if ( isset( $wp_taxonomies[ $tax ] ) ) { + + // remove 'post' from object types. + if ( isset( $wp_taxonomies[ $tax ]->object_type ) ) { + if ( is_array( $wp_taxonomies[ $tax ]->object_type ) ) { + $key = array_search( 'post', $wp_taxonomies[ $tax ]->object_type, true ); + if ( false !== $key ) { + unset( $wp_taxonomies[ $tax ]->object_type[ $key ] ); + } + } + } + + // only modify the public arguments if 'post' is the only post type. + // using this taxonomy. + if ( ! dwpb_post_types_with_tax( $tax ) ) { + + // public arguments to remove. + $arguments_to_remove = array( + 'has_archive', + 'public', + 'publicly_queryable', + 'query_var', + 'show_ui', + 'show_tagcloud', + 'show_in_admin_bar', + 'show_in_quick_edit', + 'show_in_nav_menus', + 'show_admin_column', + 'show_in_menu', + 'show_in_rest', + ); + + foreach ( $arguments_to_remove as $arg ) { + if ( isset( $wp_taxonomies[ $tax ]->$arg ) ) { + // @codingStandardsIgnoreStart - phpcs doesn't like using variables like this. + $wp_taxonomies[ $tax ]->$arg = false; + // @codingStandardsIgnoreEnd + } + } + } + } + } + } + + /** + * Redirect blog-related admin pages + * + * @uses dwpb_post_types_with_tax() + * @since 0.1.0 + * @since 0.4.0 added single post edit screen redirect + * @since 0.5.0 condensed page rediects into a foreach loop with common structure + * for filters and functions used to check redirect conditions. + * @return void + */ + public function redirect_admin_pages() { + + global $pagenow; + + if ( ! isset( $pagenow ) ) { + return; + } + + $screen = get_current_screen(); + + // on multisite: Do not redirect if we are on a network page. + if ( is_multisite() && is_callable( array( $screen, 'in_admin' ) ) && $screen->in_admin( 'network' ) ) { + return; + } + + // setup false redirect url value for default/final check. + $dashboard_url = admin_url( 'index.php' ); + $redirect_url = false; + + // The admin page slugs to potentially be redirected. + $admin_redirects = array( + 'post', + 'edit', + 'post-new', + 'edit-tags', + 'term', + 'edit-comments', + 'options-discussion', + 'options-writing', + 'tools', + ); + + // cycle through each admin page, checking if we need to redirect. + foreach ( $admin_redirects as $pagename ) { + + // Filter names are all underscores. + $filternme = str_replace( '-', '_', $pagename ); + + // Custom function within this class used to check if the page needs to be redirected. + $function = 'redirect_admin_' . $filternme; + + // build the filter. + $filter = 'dwpb_' . $function; + + // If this is the right page, then setup the redirect url. + // make sure we're on that page right now. + // make sure the function used to check/provide the url is callable. + if ( $this->is_admin_page( $pagename ) + && is_callable( array( $this, $function ) ) ) { + + // Check the function for redirect clearance, or custom url. + $redirect = $this->$function(); + + // Set a redirect url variable to check against. + $potential_redirect_url = esc_url_raw( $redirect ); + + // If it's set to `true` then redirect to the dashboard, + // if it's set to a url, redirect to that url. + if ( true === $redirect || ! empty( $potential_redirect_url ) ) { + + // Either this is a custom redirect url or 'true', which defaults the url to the dashboard. + if ( ! empty( $potential_redirect_url ) ) { + $url = $potential_redirect_url; + } else { + $url = $dashboard_url; + } + + /** + * The redirect url used for this admin page. + * + * Example: use 'dwpb_redirect_admin_options_tools' to change the redirect url + * used for the options-tools.php page. Note `-` strings are converted to `_` + * in the filter name. + * + * @since 0.4.0 + * @since 0.5.0 combine common filters. + * @param string $url the url to redirct to, defaults to dashboard. + */ + $redirect_url = apply_filters( $filter, $url ); + + break; // no need to keep looping. + + } + } + } + + /** + * Global admin url redirect filter. + * + * @since 0.5.0 + * @param string $redirect_url The redirect url. + */ + $redirect_url = apply_filters( 'dwpb_admin_redirect_url', $redirect_url ); + + /** + * Redirect blog related admin pages. + * + * @since 0.4.0 + * @since 0.5.0 removed 3rd `$current_url` param. + * @param bool $bool True to enable, default is true. + * @param string $redirect_url The url to being used in the redirect. + */ + if ( $redirect_url && apply_filters( 'dwpb_redirect_admin', true, $redirect_url ) ) { + $this->functions->redirect( $redirect_url ); + } + } + + /** + * The admin redirect arguments checked to redirect the post.php screen. + * + * @since 0.5.0 + * @return bool + */ + public function redirect_admin_post() { + + // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. + return ( isset( $_GET['post'] ) && 'post' == get_post_type( $_GET['post'] ) ); + // @codingStandardsIgnoreEnd + } + + /** + * The admin redirect arguments checked to redirect the edit.php screen. + * + * @since 0.5.0 + * @return bool|string + */ + public function redirect_admin_edit() { + + // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. + if ( ! isset( $_GET['post_type'] ) || isset( $_GET['post_type'] ) && $_GET['post_type'] == 'post' ) { + // @codingStandardsIgnoreEnd + + return admin_url( 'edit.php?post_type=page' ); + + } + + return false; + } + + /** + * The admin redirect arguments checked to redirect the post-new.php screen. + * + * @since 0.5.0 + * @return bool|string + */ + public function redirect_admin_post_new() { + + // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. + if ( ( ! isset( $_GET['post_type'] ) || isset( $_GET['post_type'] ) && $_GET['post_type'] == 'post' ) ) { + // @codingStandardsIgnoreEnd + + return admin_url( 'post-new.php?post_type=page' ); + + } + + return false; + } + + /** + * The admin redirect arguments checked to redirect the term.php screen. + * + * @since 0.5.0 + * @return bool|string + */ + public function reidrect_admin_term() { + + // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. + return ( isset( $_GET['taxonomy'] ) && ! dwpb_post_types_with_tax( $_GET['taxonomy'] ) ); + // @codingStandardsIgnoreEnd + } + + /** + * The admin redirect arguments checked to redirect the edit-tags.php screen. + * + * @since 0.5.0 + * @return bool|string + */ + public function redirect_admin_edit_tags() { + + // @codingStandardsIgnoreStart - phpcs wants to sanitize this, but it's not necessary. + return ( isset( $_GET['taxonomy'] ) && ! dwpb_post_types_with_tax( $_GET['taxonomy'] ) ); + // @codingStandardsIgnoreEnd + } + + /** + * The admin redirect arguments checked to redirect the edit-comments.php screen. + * + * @uses dwpb_post_types_with_feature() + * @since 0.5.0 + * @return bool + */ + public function redirect_admin_edit_comments() { + + /** + * Will only work comments are only supported by 'post' type, + * note that pages and attachments support comments by default. + */ + return ! dwpb_post_types_with_feature( 'comments' ); + } + + /** + * The admin redirect arguments checked to redirect the options-discussion.php screen. + * + * The same checks are performed by the edit-comments check, so this is a wrapper function. + * + * @since 0.5.0 + * @return bool + */ + public function redirect_admin_options_discussion() { + return $this->redirect_admin_edit_comments(); + } + + /** + * The admin redirect arguments checked to redirect the options-writing.php screen. + * + * @since 0.5.0 + * @return bool|string + */ + public function redirect_admin_options_writing() { + + // Redirect writing options to general options. + if ( $this->remove_writing_options() ) { + return admin_url( 'options-general.php' ); + } + + return false; + } + + /** + * The admin redirect arguments checked to redirect the options-tools.php screen. + * + * @since 0.5.0 + * @return bool + */ + public function redirect_admin_options_tools() { + + /** + * The isset( $_GET['page'] ) check is to confirm the page + * isn't a 3rd party plugin's option page built into the tools page. + */ + // @codingStandardsIgnoreStart - phpcs wants to nounce this, but that's not needed. + return ! isset( $_GET['page'] ); + // @codingStandardsIgnoreEnd + } + + /** + * Remove Post Related Menus + * + * @uses dwpb_post_types_with_tax() + * @uses dwpb_post_types_with_feature() + * @link http://wordpress.stackexchange.com/questions/57464/remove-posts-from-admin-but-show-a-custom-post + * @since 0.1.0 + * @since 0.4.0 added tools and discussion subpages. + * @return void + */ + public function remove_menu_pages() { + + // Main pages to be removed. + $remove_pages = array( 'edit.php' ); + + // If no post type supports comments, then remove the edit-comments.php page from the admin. + if ( ! dwpb_post_types_with_feature( 'comments' ) ) { + $remove_pages[] = 'edit-comments.php'; + } + + /** + * Top level admin pages to remove. + * + * @since 0.4.0 + * @param array $remove_pages Array of page strings. + */ + $pages = apply_filters( 'dwpb_menu_pages_to_remove', $remove_pages ); + foreach ( $pages as $page ) { + remove_menu_page( $page ); + } + + // Submenu Pages. + $remove_subpages = array( + 'tools.php' => array( 'tools.php' ), + 'options-general.php' => array(), + ); + + // Remove the writings page, if the filter tells us so. + if ( $this->remove_writing_options() ) { + $remove_subpages['options-general.php'][] = 'options-writing.php'; + } + + // If there are no other post types supporting comments, remove the discussion page. + if ( ! dwpb_post_types_with_feature( 'comments' ) ) { + $remove_subpages['options-general.php'][] = 'options-discussion.php'; // Settings > Discussion. + } + + /** + * Admin subpages to be removed. + * + * @since 0.4.0 + * @since 0.5.0 in order to account for multiple subpages with a common parent + * the `subpages` are now in arrays + * @param array $remove_subpages Array of page => subpages where subpages is an array of strings. + */ + $subpages = apply_filters( 'dwpb_menu_subpages_to_remove', $remove_subpages ); + foreach ( $subpages as $page => $subpages ) { + if ( is_array( $subpages ) && ! empty( $subpages ) ) { + foreach ( $subpages as $subpage ) { + remove_submenu_page( $page, $subpage ); + } + } elseif ( is_string( $subpages ) ) { // for backwards compatibility. + remove_submenu_page( $page, $subpages ); + } + } + } + + /** + * Filter the body classes for admin screens to toggle on plugin specific styles. + * + * @since 0.4.7 + * @param string $classes the admin body classes, which is a string *not* an array. + * @return string + */ + public function admin_body_class( $classes ) { + + if ( $this->has_front_page() ) { + $classes .= ' disabled-blog'; + } + + return $classes; + } + + /** + * Filter for removing the writing options page. + * + * @since 0.4.5 + * @return bool + */ + public function remove_writing_options() { + + /** + * Toggle the options-writing page on/off. + * + * Defaults to false because other plugins often extend this page. + * Setting this to true will create a redirect for the page + * and remove it from the admin menu. + * + * See: https://wordpress.org/support/topic/disabling-writing-settings-panel-is-a-problem/ + * + * @since 0.4.5 + * @since 0.5.0 renamed from `dwpb_redirect_admin_options_writing` + * to `dwpb_remove_options_writing`. The old filter name + * is not used by the admin redirect function to filter + * the redirect url used for this page. + * @param bool $bool Defaults to false, keeping the writing page visible. + */ + return (bool) apply_filters( 'dwpb_remove_options_writing', false ); + } + + /** + * Remove blog-related admin bar links + * + * @uses dwpb_post_types_with_feature() + * @link http://www.paulund.co.uk/how-to-remove-links-from-wordpress-admin-bar + * @since 0.1.0 + * @return void + */ + public function remove_admin_bar_links() { + + global $wp_admin_bar; + + // If only posts support comments, then remove comment from admin bar. + if ( ! dwpb_post_types_with_feature( 'comments' ) ) { + $wp_admin_bar->remove_menu( 'comments' ); + } + + // Remove New Post from Content. + $wp_admin_bar->remove_node( 'new-post' ); + } + + /** + * Hide all comments from 'post' post type + * + * @uses dwpb_post_types_with_feature() + * @since 0.1.0 + * @param object $comments the comments query object. + * @return object + */ + public function comment_filter( $comments ) { + + global $pagenow; + + if ( ! isset( $pagenow ) ) { + return $comments; + } + + // Filter out comments from post. + $post_types = dwpb_post_types_with_feature( 'comments' ); + if ( $post_types && $this->is_admin_page( 'edit-comments' ) ) { + $comments->query_vars['post_type'] = $post_types; + } + + return $comments; + } + + /** + * Remove post-related dashboard widgets + * + * @uses dwpb_post_types_with_feature() + * @since 0.1.0 + * @since 0.4.1 dry out the code with a foreach loop + * @return void + */ + public function remove_dashboard_widgets() { + + // Remove post-specific widgets only, others obscured/modified elsewhere as necessary. + $metabox = array( + 'dashboard_quick_press' => 'side', // Quick Press. + 'dashboard_recent_drafts' => 'side', // Recent Drafts. + 'dashboard_incoming_links' => 'normal', // Incoming Links. + 'dashboard_activity' => 'normal', // Activity. + ); + + foreach ( $metabox as $metabox_id => $context ) { + + /** + * Filter to change the dashboard widgets beinre removed. + * + * Filter name based on the name of the widget above, + * For instance: `dwpb_disable_dashboard_quick_press` for the Quick Press widget. + * + * @since 0.4.1 + * @param bool $bool True to remove the dashboard widget. + */ + if ( apply_filters( "dwpb_disable_{$metabox_id}", true ) ) { + remove_meta_box( $metabox_id, 'dashboard', $context ); + } + } + } + + /** + * Throw an admin notice if settings are misconfigured. + * + * If there is not a homepage correctly set, then redirects don't work. + * The intention with this notice is to highlight what is needed for the plugin to function properly. + * + * @since 0.2.0 + * @since 0.4.7 Changed this to an error notice function. + * @return void + */ + public function admin_notices() { + + // only throwing this notice in the edit.php, plugins.php, and options-reading.php admin pages. + $current_screen = get_current_screen(); + $screens = array( + 'plugins', + 'options-reading', + 'edit', + ); + if ( ! ( isset( $current_screen->base ) && in_array( $current_screen->base, $screens, true ) ) ) { + return; + } + + // Throw a notice if the we don't have a front page. + if ( ! $this->has_front_page() ) { + + // The second part of the notice depends on which screen we're on. + if ( 'options-reading' === $current_screen->base ) { + + // translators: Direct the user to set a homepage in the current screen. + $message_link = ' ' . __( 'Select a page for your homepage below.', 'disable-blog' ); + + } else { // If we're not on the Reading Options page, then direct the user there. + + $reading_options_page = get_admin_url( null, 'options-reading.php' ); + + // translators: Direct the user to the Reading Settings admin page. + $message_link = ' ' . sprintf( __( 'Change in Reading Settings.', 'disable-blog' ), $reading_options_page ); + + } + + // translators: Prompt to configure the site for static homepage and posts page. + $message = __( 'Disable Blog is not fully active until a static page is selected for the site\'s homepage.', 'disable-blog' ) . $message_link; + + printf( '

%s

', 'notice notice-error', wp_kses_post( $message ) ); + + // If we have a front page set, but no posts page or they are the same,. + // Then let the user know the expected behavior of these two. + } elseif ( 'options-reading' === $current_screen->base + && get_option( 'page_for_posts' ) === get_option( 'page_on_front' ) ) { + + // translators: Warning that the homepage and blog page cannot be the same, the post page is redirected to the homepage. + $message = __( 'Disable Blog requires a homepage that is different from the post page. The "posts page" will be redirected to the homepage.', 'disable-blog' ); + + printf( '

%s

', 'notice notice-error', esc_attr( $message ) ); + + } + } + + /** + * Check that the site has a front page set in the Settings > Reading. + * + * @since 0.4.7 + * @return bool + */ + public function has_front_page() { + return 'page' === get_option( 'show_on_front' ) && absint( get_option( 'page_on_front' ) ); + } + + /** + * Kill the Press This functionality + * + * @since 0.2.0 + * @return void + */ + public function disable_press_this() { + wp_die( '"Press This" functionality has been disabled.' ); + } + + /** + * Remove post related widgets + * + * @since 0.2.0 + * @since 0.4.0 simplified unregistering and added dwpb_unregister_widgets filter. + * @return void + */ + public function remove_widgets() { + + // Unregister blog-related widgets. + $widgets = array( + 'WP_Widget_Recent_Comments', // Recent Comments. + 'WP_Widget_Tag_Cloud', // Tag Cloud. + 'WP_Widget_Categories', // Categories. + 'WP_Widget_Archives', // Archives. + 'WP_Widget_Calendar', // Calendar. + 'WP_Widget_Links', // Links. + 'WP_Widget_Recent_Posts', // Recent Posts. + 'WP_Widget_RSS', // RSS. + 'WP_Widget_Tag_Cloud', // Tag Cloud. + ); + foreach ( $widgets as $widget ) { + + /** + * The ability to stop the widget unregister. + * + * @since 0.4.0 + * + * @param bool $bool True to unregister the widget. + * @param string $widget The name of the widget to be unregistered. + */ + if ( apply_filters( 'dwpb_unregister_widgets', true, $widget ) ) { + unregister_widget( $widget ); + } + } + } + + /** + * Filter the widget removal & check for reasons to not remove specific widgets. + * + * If there are post types using the comments or built-in taxonomies outside of the default 'post' + * then we stop the plugin from removing the widget. + * + * @uses dwpb_post_types_with_feature() + * @since 0.4.0 + * @param bool $show true to show. + * @param string $widget the widget name. + * @return bool + */ + public function filter_widget_removal( $show, $widget ) { + + // Remove Categories Widget. + if ( 'WP_Widget_Categories' === $widget && dwpb_post_types_with_tax( 'category' ) ) { + $show = false; + } + + // Remove Recent Comments Widget if posts are the only type with comments. + if ( 'WP_Widget_Recent_Comments' === $widget && dwpb_post_types_with_feature( 'comments' ) ) { + $show = false; + } + + // Remove Tag Cloud. + if ( 'WP_Widget_Tag_Cloud' === $widget && dwpb_post_types_with_tax( 'post_tag' ) ) { + $show = false; + } + + return $show; + } + + /** + * Register the stylesheets for the admin area. + * + * @since 0.4.0 + * @return void + */ + public function enqueue_styles() { + wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . '../assets/css/disable-blog-admin.css', array(), $this->version, 'all' ); + } + + /** + * Register the scripts used in the admin area. + * + * @since 0.5.0 + * @return void + */ + public function enqueue_scripts() { + + wp_enqueue_script( $this->plugin_name, DWPB_URL . 'assets/js/disable-blog-admin.js', array(), $this->version, true ); + + // Localize some information on the page. + global $pagenow; + $js_vars = array( + 'page' => str_replace( '.php', '', $pagenow ), + 'categoriesSupported' => (bool) dwpb_post_types_with_tax( 'category' ), + 'tagsSupported' => (bool) dwpb_post_types_with_tax( 'post_tag' ), + 'commentsSupported' => (bool) dwpb_post_types_with_feature( 'comments' ), + ); + + wp_localize_script( $this->plugin_name, 'dwpb', $js_vars ); + } + + /** + * Check that we're on a specific admin page. + * + * @since 0.4.8 + * @param string $page the page slug. + * @return bool + */ + public function is_admin_page( $page ) { + global $pagenow; + return is_admin() && isset( $pagenow ) && is_string( $pagenow ) && $page . '.php' === $pagenow; + } + + /** + * Filter the comment counts to remove comments to 'post' post type. + * + * @since 0.4.0 + * @since 0.4.3 Moved everything into the post id check and updated caching functions to match wp_get_comments. + * + * @see wp_get_comments() + * @param object $comments the comment count object. + * @param int $post_id the post id. + * @return object + */ + public function filter_wp_count_comments( $comments, $post_id ) { + + // if this is grabbing all the comments, filter out the 'post' comments. + if ( 0 === $post_id ) { + + // Keep caching in place, note that on activation the plugin clears this cache. + // see class-disable-blog-activator.php. + $count = wp_cache_get( 'comments-0', 'counts' ); + if ( false !== $count ) { + return $count; + } + + $comments = $this->get_comment_counts(); + + $comments['moderated'] = $comments['awaiting_moderation']; + unset( $comments['awaiting_moderation'] ); + + $comments = (object) $comments; + wp_cache_set( 'comments-0', $comments, 'counts' ); + + } + + return $comments; + } + + /** + * Alter the comment counts on the admin comment table to remove comments associated with posts. + * + * @since 0.4.0 + * @param array $views all the views. + * @return array + */ + public function filter_admin_table_comment_count( $views ) { + + global $current_screen; + + if ( 'edit-comments' === $current_screen->id ) { + + $updated_counts = $this->get_comment_counts(); + foreach ( $views as $view => $text ) { + if ( isset( $updated_counts[ $view ] ) ) { + $views[ $view ] = preg_replace( '/\([^)]+\)/', '(' . $updated_counts[ $view ] . ')', $views[ $view ] ); + } + } + } + + return $views; + } + + /** + * Retrieve the comment counts without the 'post' comments. + * + * @since 0.4.0 + * @since 0.4.3 Removed Unused "count" function. + * @see get_comment_count() + * @see https://developer.wordpress.org/reference/functions/esc_sql/ + * @return array + */ + public function get_comment_counts() { + + global $wpdb; + + // Set up the counts, we'll be adding to this array. + $comment_count = array( + 'moderated' => 0, + 'approved' => 0, + 'awaiting_moderation' => 0, + 'spam' => 0, + 'trash' => 0, + 'post-trashed' => 0, + 'total_comments' => 0, + 'all' => 0, + ); + + // Get the post types that support comments. + $supported_post_types = dwpb_post_types_with_feature( 'comments' ); + + // Return an array of empty counts if there are no post types that support comments. + if ( empty( $supported_post_types ) || ! is_array( $supported_post_types ) ) { + return $comment_count; + } + + // Sanitizing the post type strings. + $sanitized_post_types = (array) array_map( 'esc_sql', $supported_post_types ); + + // Implode the post types into a string for the query. + $in_post_types = implode( "','", $sanitized_post_types ); + + // Grab the comments that are associated with supported post types only. + // @codingStandardsIgnoreStart -- The get_results function doesn't need a wpdb->prepare here because $in_post_types is sanitized above. + $totals = (array) $wpdb->get_results( + "SELECT comment_approved, COUNT( * ) AS total + FROM {$wpdb->comments} + WHERE comment_post_ID in ( + SELECT ID + FROM {$wpdb->posts} + WHERE post_type in ('{$in_post_types}') + AND post_status = 'publish') + GROUP BY comment_approved", + ARRAY_A + ); + // @codingStandardsIgnoreEnd + + foreach ( $totals as $row ) { + switch ( $row['comment_approved'] ) { + case 'trash': + $comment_count['trash'] = $row['total']; + break; + case 'post-trashed': + $comment_count['post-trashed'] = $row['total']; + break; + case 'spam': + $comment_count['spam'] = $row['total']; + $comment_count['total_comments'] += $row['total']; + break; + case '1': + $comment_count['approved'] = $row['total']; + $comment_count['total_comments'] += $row['total']; + $comment_count['all'] += $row['total']; + break; + case '0': + $comment_count['awaiting_moderation'] = $row['total']; + $comment_count['moderated'] = $comment_count['awaiting_moderation']; + $comment_count['total_comments'] += $row['total']; + $comment_count['all'] += $row['total']; + break; + default: + break; + } + } + + return array_map( 'intval', $comment_count ); + } + + /** + * Clear out the status of all post comments. + * + * @since 0.4.0 + * @param bool $open true if comments are open. + * @param int $post_id the post id. + * @return bool + */ + public function filter_comment_status( $open, $post_id ) { + + return ( 'post' === get_post_type( $post_id ) ) ? false : $open; + } + + /** + * Clear comments from 'post' post type. + * + * @since 0.4.0 + * @param array $comments the array of comments. + * @param int $post_id the post id. + * @return array + */ + public function filter_existing_comments( $comments, $post_id ) { + return ( 'post' === get_post_type( $post_id ) ) ? array() : $comments; + } + + /** + * Filters the default post display states used in the posts list table. + * + * @since 0.5.0 + * @param string[] $post_states An array of post display states. + * @param WP_Post $post The current post object. + * @return array + */ + public function page_post_states( $post_states, $post ) { + + if ( $this->has_front_page() && absint( get_option( 'page_for_posts' ) ) === $post->ID ) { + // translators: This string is used to indicate that the blog page is redirected to the homepage. + $post_states['dwpb-redirected'] = __( 'Redirected to the homepage', 'disable-blog' ); + } + + return $post_states; + } + + /** + * Removes the Site Health check for the post REST API. + * + * @uses dwpb_get_test_rest_availability() + * @since 0.5.0 + * @param array $tests the tests, of course. + * @return array + */ + public function site_status_tests( $tests ) { + + if ( isset( $tests['direct']['rest_availability'] ) ) { + $tests['direct']['rest_availability']['test'] = array( $this, 'get_test_rest_availability' ); + } + + return $tests; + } + + /** + * Replaces the core REST Availability Site Health check. + * + * Used by the site_status_tests filter in class-disable-blog-admin.php. + * + * Copied directly from https://developer.wordpress.org/reference/classes/wp_site_health/get_test_rest_availability/ but with the 'post' type updated to 'page' in the rest url. + * + * @see https://make.wordpress.org/core/2019/04/25/site-health-check-in-5-2/ + * @since 0.5.0 + * @return array + */ + public function get_test_rest_availability() { + + $result = array( + 'label' => __( 'The REST API is available' ), + 'status' => 'good', + 'badge' => array( + 'label' => __( 'Performance' ), + 'color' => 'blue', + ), + 'description' => sprintf( + '

%s

', + __( 'The REST API is one way WordPress, and other applications, communicate with the server. One example is the block editor screen, which relies on this to display, and save, your posts and pages.' ) + ), + 'actions' => '', + 'test' => 'rest_availability', + ); + + $cookies = wp_unslash( $_COOKIE ); + $timeout = 10; + $headers = array( + 'Cache-Control' => 'no-cache', + 'X-WP-Nonce' => wp_create_nonce( 'wp_rest' ), + ); + + /** This filter is documented in wp-includes/class-wp-http-streams.php */ + $sslverify = apply_filters( 'https_local_ssl_verify', false ); + + // Include Basic auth in loopback requests. + // @codingStandardsIgnoreStart + if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { + $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); + } + // @codingStandardsIgnoreEnd + + // -- here's the money, change this from 'post' to 'page'. + $url = rest_url( 'wp/v2/types/page' ); + + // The context for this is editing with the new block editor. + $url = add_query_arg( + array( + 'context' => 'edit', + ), + $url + ); + + $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) ); + + if ( is_wp_error( $r ) ) { + $result['status'] = 'critical'; + + $result['label'] = __( 'The REST API encountered an error' ); + + $result['description'] .= sprintf( + '

%s

', + sprintf( + '%s
%s', + __( 'The REST API request failed due to an error.' ), + sprintf( + /* translators: 1: The WordPress error message. 2: The WordPress error code. */ + __( 'Error: %1$s (%2$s)' ), + $r->get_error_message(), + $r->get_error_code() + ) + ) + ); + } elseif ( 200 !== wp_remote_retrieve_response_code( $r ) ) { + $result['status'] = 'recommended'; + + $result['label'] = __( 'The REST API encountered an unexpected result' ); + + $result['description'] .= sprintf( + '

%s

', + sprintf( + /* translators: 1: The HTTP error code. 2: The HTTP error message. */ + __( 'The REST API call gave the following unexpected result: (%1$d) %2$s.' ), + wp_remote_retrieve_response_code( $r ), + esc_html( wp_remote_retrieve_body( $r ) ) + ) + ); + } else { + $json = json_decode( wp_remote_retrieve_body( $r ), true ); + + if ( false !== $json && ! isset( $json['capabilities'] ) ) { + $result['status'] = 'recommended'; + + $result['label'] = __( 'The REST API did not behave correctly' ); + + $result['description'] .= sprintf( + '

%s

', + sprintf( + /* translators: %s: The name of the query parameter being tested. */ + __( 'The REST API did not process the %s query parameter correctly.' ), + 'context' + ) + ); + } + } + + return $result; + } + + /** + * Filter the taxonomy count on post_tag and category screens. + * + * Used on the post_tag and category screens for custom post types. + * + * @since 0.5.0 + * @param array $actions an array of actions. + * @param object $tag the current taxonomy object. + * @return array + */ + public function filter_taxonomy_count( $actions, $tag ) { + + if ( isset( $tag->taxonomy, $tag->count, $tag->term_id ) + && ( 'post_tag' === $tag->taxonomy || 'category' === $tag->taxonomy ) ) { + $screen = get_current_screen(); + $count = $this->get_term_post_count_by_type( $tag->term_id, $tag->taxonomy, $screen->post_type ); + $tag->count = $count; + } + + return $actions; + } + + /** + * Return the post count for a term based by post type. + * + * @since 0.5.0 + * @param int $term_id the current term id. + * @param string $taxonomy the taxonomy slug. + * @param string $post_type the post type slug. + * @return int + */ + public function get_term_post_count_by_type( $term_id, $taxonomy, $post_type ) { + + $args = array( + 'fields' => 'ids', + 'posts_per_page' => 500, + 'post_type' => $post_type, + 'no_found_rows' => true, + 'update_post_meta_cache' => false, + 'tax_query' => array( + array( + 'taxonomy' => $taxonomy, + 'field' => 'id', + 'terms' => intval( $term_id ), + ), + ), + ); + $query = new WP_Query( $args ); + + if ( count( $query->posts ) > 0 ) { + return count( $query->posts ); + } else { + return 0; + } + } + + /** + * Remove posts column form user table. + * + * @since 0.5.0 + * @param array $columns the column slugs => title array. + * @return array + */ + public function manage_users_columns( $columns ) { + + /** + * Disable the user post column. + * + * @since 0.5.0 + * @param bool $bool True to remove the column, defaults to true. + * @return bool + */ + $disable_user_posts_column = apply_filters( 'dpwb_disable_user_post_column', true ); + + if ( isset( $columns['posts'] ) && true === (bool) $disable_user_posts_column ) { + unset( $columns['posts'] ); + } + + // Get the current screen. + $screen = get_current_screen(); + + // cycle through the post types to be displayed. + $post_types = $this->user_column_post_types(); + foreach ( $post_types as $post_type ) { + /** + * Create a new column for 'pages' similar to the original 'post' column. + * + * @since 0.5.0 + * @param bool $bool True to remove the column, defaults to true. + * @return bool + */ + if ( apply_filters( "dpwb_create_user_{$post_type}_column", true ) + // Taken from core functions for users page, don't display the posts column on site-users-network core page. + // see wp-admin/includes/class-wp-users-list-table.php. + && isset( $screen->id ) && 'site-users-network' !== $screen->id ) { + + $post_type_obj = get_post_type_object( $post_type ); + if ( isset( $post_type_obj->labels->name ) ) { + $columns[ $post_type ] = $post_type_obj->labels->name; + } + } + } + + return $columns; + } + + /** + * Mange the new custom user columns. + * + * @since 0.5.0 + * @see wp-admin/includes/class-wp-users-list-table.php. + * @param string $output the column output. + * @param string $column_name the current column slug. + * @param int $user_id the current user's id. + * @return string + */ + public function manage_users_custom_column( $output, $column_name, $user_id ) { + + $post_types = $this->user_column_post_types(); + foreach ( $post_types as $post_type ) { + // Create new column output, mimicking core's 'post' column but for the 'page' and supported custom post types. + if ( $post_type === $column_name ) { + + // make sure we can grab valid post type labels before continuing. + $post_type_obj = get_post_type_object( $post_type ); + if ( ! isset( $post_type_obj->labels->name, $post_type_obj->labels->singular_name ) ) { + continue; + } + + // Get the couunts. + $page_counts = count_many_users_posts( array( $user_id ), $post_type ); + $page_count = absint( $page_counts[ $user_id ] ); + + // Note that we have to explicitly add the query variable for post type in order + // to avoid it being stripped by the admin redirect on the edit.php page. + // @codingStandardsIgnoreStart - phpcs doesn't like the mismatched placeholders, but it works. + $output = sprintf( + '%s', + "edit.php?post_type={$post_type}&author={$user_id}", + $page_count, + sprintf( + // translators: %1$s: Number of pieces of content by this author. %2$s and %3$s are the singular and plural names, respectively, for the content type. For example: "1 page by this author" and "2 pages by this author". + _n( '%1$s %2$s by this author', '%1$s %3$s by this author', $page_count, 'disable-blog' ), + number_format_i18n( $page_count ), + $post_type_obj->labels->singular_name, + $post_type_obj->labels->name + ) + ); + // @codingStandardsIgnoreEnd + } + } + + return $output; + } + + /** + * Grab the post types that + * + * @since 0.5.0 + * @return array + */ + private function user_column_post_types() { + + // Include any post types using author archives. + $post_types = $this->functions->author_archive_post_types(); + + // The author_archive_post_type function returns false if empty, but we need an array. + $post_types = empty( $post_types ) ? array() : $post_types; + + // Also include pages, which is not in the author archive by default, + // however we run array_unique in case the 'page' post type has been added + // to the author archives. + $post_types = array_unique( array_merge( array( 'page' ), $post_types ) ); + + /** + * Filter the post types that appear in the user table. + * + * @since 0.5.0 + * @param array $post_types an array of post type slugs. + * @return array + */ + return apply_filters( 'dwpb_admin_user_post_types', $post_types ); + } + + /** + * Remove the user's "view" link if we are not supporting author archives. + * + * @since 0.5.0 + * @param array $actions an array of actions in a key => output format. + * @param object $user_object the current user, WP_User object. + * @return array + */ + public function user_row_actions( $actions, $user_object ) { + + if ( true === $this->functions->disable_author_archives() + && isset( $actions['view'] ) ) { + unset( $actions['view'] ); + } + + return $actions; + } + + /** + * Alter the customizer view to mimic the reading settings. + * + * @since 0.5.0 + * @return void + */ + public function customizer_styles() { + + if ( $this->has_front_page() ) { + ?> + + plugin_name . '-customizer-scripts', DWPB_URL . 'assets/js/disable-blog-customizer.js', array( 'jquery', 'customize-controls' ), $this->version, true ); + + // Localize some information on the page. + $js_vars = array( + // translators: This text appears in the Customizer > Homepage Settings and provides the context for the homepage select field there. + 'homepageSettingsText' => __( 'You can choose what\'s displayed on the homepage of your site. To set a static homepage, create or select the page below.', 'disable-blog' ), + ); + wp_localize_script( $this->plugin_name . '-customizer-scripts', 'dwpbCustomizer', $js_vars ); + } + + /** + * Remove the default posts page notice and replace it with a new one. + * + * @since 0.5.0 + * @return void + */ + public function update_posts_page_notice() { + + if ( (int) get_option( 'page_for_posts' ) === get_the_ID() ) { + remove_action( 'edit_form_after_title', '_wp_posts_page_notice' ); + add_action( 'edit_form_after_title', array( $this, 'posts_page_notice' ) ); + } + } + + /** + * Replaces the default blog page posts notice. + * + * @since 0.5.0 + * @return void + */ + public function posts_page_notice() { + + // translators: this notice informs the user why the blog page editor is disabled and that it is redirected to the homepage. + echo '

' . __( 'You are currently editing the page that shows your latest posts, which is redirected to the homepage because the blog is disabled.', 'disable-blog' ) . '

'; // phpcs:ignore + } + + /** + * Filter the available tags in the permalink settings. + * + * @since 0.5.1 + * @param array $available_tags The available permalink tags. + * @return array + */ + public function available_permalink_structure_tags( $available_tags ) { + + // Remove the category permalink structure if categories are not supported. + if ( isset( $available_tags['category'] ) && ! dwpb_post_types_with_tax( 'category' ) ) { + unset( $available_tags['category'] ); + } + + // Remove the author tag if author archives are disabled. + if ( isset( $available_tags['author'] ) && $this->functions->disable_author_archives() ) { + unset( $available_tags['author'] ); + } + + return $available_tags; + } +} diff --git a/includes/class-disable-blog-deactivator.php b/includes/class-disable-blog-deactivator.php index b6f53d3..d75eae6 100644 --- a/includes/class-disable-blog-deactivator.php +++ b/includes/class-disable-blog-deactivator.php @@ -1,153 +1,153 @@ - - */ - -/** - * Fired during plugin deactivation. - * - * This class defines all code necessary to run during the plugin's deactivation. - * - * @since 0.4.3 - */ -class Disable_Blog_Deactivator { - - /** - * The $_REQUEST during plugin deactivation. - * - * @since 0.5.1 - * @access private - * @var array $request The $_REQUEST array during plugin deactivation. - */ - private static $request = array(); - - /** - * The $_REQUEST['plugin'] during plugin deactivation. - * - * @since 0.5.1 - * @access private - * @var string $plugin The $_REQUEST['plugin'] value during plugin deactivation. - */ - private static $plugin = 'disable-blog'; - - /** - * Deactivate the plugin. - * - * Checks if the plugin was (safely) deactivated. - * Place to add any custom action during plugin deactivation. - * - * @since 0.4.3 - * @since 0.4.9 add flush rewrite rules. - * @since 0.5.1 - */ - public static function deactivate() { - - if ( false === self::get_request() - || false === self::validate_request( self::$plugin ) - || false === self::check_caps() - ) { - if ( isset( $_REQUEST['plugin'] ) ) { - if ( ! check_admin_referer( 'deactivate-plugin_' . self::$request['plugin'] ) ) { - exit; - } - } elseif ( isset( $_REQUEST['checked'] ) ) { - if ( ! check_admin_referer( 'bulk-plugins' ) ) { - exit; - } - } - } - - /** - * The plugin is now safely deactivated. - */ - - wp_cache_delete( 'comments-0', 'counts' ); - delete_transient( 'wc_count_comments' ); - flush_rewrite_rules(); - } - - /** - * Get the request. - * - * Gets the $_REQUEST array and checks if necessary keys are set. - * Populates self::request with necessary and sanitized values. - * - * @since 0.5.1 - * @return bool|array false or self::$request array. - */ - private static function get_request() { - - if ( ! empty( $_REQUEST ) - && isset( $_REQUEST['_wpnonce'] ) - && isset( $_REQUEST['action'] ) - ) { - if ( isset( $_REQUEST['plugin'] ) ) { - if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'deactivate-plugin_' . sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ) ) ) { - - self::$request['plugin'] = sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ); - self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); - - return self::$request; - - } - } elseif ( isset( $_REQUEST['checked'] ) ) { - if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'bulk-plugins' ) ) { - - self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); - self::$request['plugins'] = array_map( 'sanitize_text_field', (array) wp_unslash( $_REQUEST['checked'] ) ); - - return self::$request; - - } - } - } - - return false; - } - - /** - * Validate the Request data. - * - * Validates the data in $_REQUEST is matching this plugin and action. - * - * @since 0.5.1 - * @param string $plugin The Plugin folder/name.php. - * @return bool false if either plugin or action does not match, else true. - */ - private static function validate_request( $plugin ) { - - if ( isset( self::$request['plugin'] ) - && $plugin === self::$request['plugin'] - && 'deactivate' === self::$request['action'] - ) { - - return true; - - } elseif ( isset( self::$request['plugins'] ) - && 'deactivate-selected' === self::$request['action'] - && in_array( $plugin, self::$request['plugins'], true ) - ) { - return true; - } - - return false; - } - - /** - * Check Capabilities. - * - * We want no one else but users with activate_plugins or above to be able to active this plugin. - * - * @since 0.5.1 - * @return bool false if no caps, else true. - */ - private static function check_caps(): bool { - return current_user_can( 'activate_plugins' ); - } -} + + */ + +/** + * Fired during plugin deactivation. + * + * This class defines all code necessary to run during the plugin's deactivation. + * + * @since 0.4.3 + */ +class Disable_Blog_Deactivator { + + /** + * The $_REQUEST during plugin deactivation. + * + * @since 0.5.1 + * @access private + * @var array $request The $_REQUEST array during plugin deactivation. + */ + private static $request = array(); + + /** + * The $_REQUEST['plugin'] during plugin deactivation. + * + * @since 0.5.1 + * @access private + * @var string $plugin The $_REQUEST['plugin'] value during plugin deactivation. + */ + private static $plugin = 'disable-blog'; + + /** + * Deactivate the plugin. + * + * Checks if the plugin was (safely) deactivated. + * Place to add any custom action during plugin deactivation. + * + * @since 0.4.3 + * @since 0.4.9 add flush rewrite rules. + * @since 0.5.1 + */ + public static function deactivate() { + + if ( false === self::get_request() + || false === self::validate_request( self::$plugin ) + || false === self::check_caps() + ) { + if ( isset( $_REQUEST['plugin'] ) ) { + if ( ! check_admin_referer( 'deactivate-plugin_' . self::$request['plugin'] ) ) { + exit; + } + } elseif ( isset( $_REQUEST['checked'] ) ) { + if ( ! check_admin_referer( 'bulk-plugins' ) ) { + exit; + } + } + } + + /** + * The plugin is now safely deactivated. + */ + + wp_cache_delete( 'comments-0', 'counts' ); + delete_transient( 'wc_count_comments' ); + flush_rewrite_rules(); + } + + /** + * Get the request. + * + * Gets the $_REQUEST array and checks if necessary keys are set. + * Populates self::request with necessary and sanitized values. + * + * @since 0.5.1 + * @return bool|array false or self::$request array. + */ + private static function get_request() { + + if ( ! empty( $_REQUEST ) + && isset( $_REQUEST['_wpnonce'] ) + && isset( $_REQUEST['action'] ) + ) { + if ( isset( $_REQUEST['plugin'] ) ) { + if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'deactivate-plugin_' . sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ) ) ) { + + self::$request['plugin'] = sanitize_text_field( wp_unslash( $_REQUEST['plugin'] ) ); + self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); + + return self::$request; + + } + } elseif ( isset( $_REQUEST['checked'] ) ) { + if ( false !== wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'bulk-plugins' ) ) { + + self::$request['action'] = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); + self::$request['plugins'] = array_map( 'sanitize_text_field', (array) wp_unslash( $_REQUEST['checked'] ) ); + + return self::$request; + + } + } + } + + return false; + } + + /** + * Validate the Request data. + * + * Validates the data in $_REQUEST is matching this plugin and action. + * + * @since 0.5.1 + * @param string $plugin The Plugin folder/name.php. + * @return bool false if either plugin or action does not match, else true. + */ + private static function validate_request( $plugin ) { + + if ( isset( self::$request['plugin'] ) + && $plugin === self::$request['plugin'] + && 'deactivate' === self::$request['action'] + ) { + + return true; + + } elseif ( isset( self::$request['plugins'] ) + && 'deactivate-selected' === self::$request['action'] + && in_array( $plugin, self::$request['plugins'], true ) + ) { + return true; + } + + return false; + } + + /** + * Check Capabilities. + * + * We want no one else but users with activate_plugins or above to be able to active this plugin. + * + * @since 0.5.1 + * @return bool false if no caps, else true. + */ + private static function check_caps(): bool { + return current_user_can( 'activate_plugins' ); + } +} diff --git a/includes/class-disable-blog-functions.php b/includes/class-disable-blog-functions.php index b314ece..747b774 100644 --- a/includes/class-disable-blog-functions.php +++ b/includes/class-disable-blog-functions.php @@ -1,241 +1,241 @@ -request ) ); - } else { - $current_url = home_url( add_query_arg( array(), $wp->request ) ); - - // Filter the safe redirect to avoid redirectin non-admin urls to the dashboard - // if the fallback is used by the core wp_redirect. - add_filter( 'wp_safe_redirect_fallback', array( $this, 'wp_safe_redirect_fallback' ), 9, 1 ); - } - - // Compare the current url to the redirect url, if they are the same, bail to avoid a loop. - // If there is no valid redirect url, then also bail. - if ( $redirect_url === $current_url || ! esc_url_raw( $redirect_url ) ) { - return; - } - - /** - * Should we pass url query string in the redirect? - * - * Default is false. - * - * @since 0.5.0 - * @param bool $bool true to allow query strings on redirect. - * @return bool - */ - if ( apply_filters( 'dwpb_pass_query_string_on_redirect', false ) ) { - $redirect_url = $this->parse_query_string( $redirect_url ); - } - - wp_safe_redirect( esc_url_raw( $redirect_url ), $this->get_redirect_status_code( $current_url, $redirect_url ) ); - exit; - } - - /** - * Parse the current query string and add it, clean and filter, to the url. - * - * @since 0.5.0 - * @param string $url the url any query string will be added to. - * @return string - */ - private function parse_query_string( $url ) { - - if ( ! isset( $_SERVER['QUERY_STRING'] ) || empty( $_SERVER['QUERY_STRING'] ) ) { - return $url; - } - - // Setup an array of the current query string variables. - $query_vars = array(); - wp_parse_str( $_SERVER['QUERY_STRING'], $query_vars ); // phpcs:ignore - - /** - * Filter for allowed queary string variables. - * - * @since 0.5.0 - * @param array $allowed_query_vars an array of the allowed query variable keys. - * @return array - */ - $allowed_query_vars = (array) apply_filters( 'dwpb_allowed_query_vars', array() ); - if ( ! empty( $allowed_query_vars ) ) { - $allowed_query_vars = array_filter( - $allowed_query_vars, - function ( $value ) { - $esc_value = esc_html( $value ); - return ! empty( $esc_value ); - } - ); - $query_vars = array_intersect_key( $query_vars, array_flip( $allowed_query_vars ) ); - } - - // Escaping and sanitization are important. - $query_vars = array_filter( - $query_vars, - function ( $value ) { - $esc_value = esc_html( $value ); - return ! empty( $esc_value ); - } - ); - $query_vars = array_filter( - $query_vars, - function ( $value ) { - $esc_value = esc_html( $value ); - return ! empty( $esc_value ); - }, - ARRAY_FILTER_USE_KEY - ); - - // if we have any query variables, add it to the url. - if ( ! empty( $query_vars ) ) { - $url = add_query_arg( $query_vars, $url ); - } - - return $url; - } - - /** - * Return the status code used in the main redirect function. - * - * @since 0.5.0 - * @param string $current_url the url being redirected FROM. - * @param string $redirect_url the url being redirected TO. - * @return int - */ - private function get_redirect_status_code( $current_url, $redirect_url ) { - - /** - * Filter the status code, must be a valid number to pass, defaults to 301. - * - * @since 0.5.0 - * @param int $status_code the status code returned with the redirect. - * @param string $current_url the url being redirected FROM. - * @param string $redirect_url the url being redirected TO. - * @return int $status_code - */ - $status_code = apply_filters( 'dwpb_redirect_status_code', 301, $current_url, $redirect_url ); - - // Make sure we have a valid redirect status code, if not we set to 301. - // helps to stop a wp_die in wp_redirect if the filtered value returns something invalid. - if ( ! absint( $status_code ) || 300 > $status_code || 399 < $status_code ) { - return 301; - } - - return absint( $status_code ); - } - - /** - * Filter the safe redirect fallback to prevent front-end users - * in public redirects from being redirected to the admin url - * which is the WP core default for safe redirects. - * - * @since 0.5.0 - * @param string $url the fallback url. - * @return string - */ - public function wp_safe_redirect_fallback( $url ) { - - if ( ! is_admin() ) { - return home_url(); - } - - return $url; - } - - /** - * Check what post types are supporting author archives. - * - * @since 0.5.0 - * @return bool|array $post_types Either an array of post types or false. - */ - public function author_archive_post_types() { - - /** - * The post types supported on author archives. - * - * By default only posts are shown on author archives, if other post types are to appear - * on the author archives, pass them with this filter. - * - * @since 0.5.0 - * @param array|bool $post_types an array of post type slugs for author archives, false to disable. - */ - $post_types = apply_filters( 'dwpb_author_archive_post_types', array() ); - - if ( ! empty( $post_types ) ) { - return $post_types; - } - - // Return false if there are no supported post types. - return false; - } - - /** - * Check if we are disabling author archives. - * - * Will only return true if the filter is actively set to true - * AND there are valid post types to show on the author archives. - * - * @since 0.5.0 - * @return bool - */ - public function disable_author_archives() { - - /** - * Disable the author archives. - * - * Because many plugins and themes use author archives for profile pages, - * the ability to disable the author archive defaults to false. - * - * @since 0.5.0 - * @param bool $bool True to disable author archives. - * @return bool - */ - return (bool) apply_filters( 'dwpb_disable_author_archives', false ); - } - - /** - * Toggle the disable feed via this filter. - * - * @since 0.5.1 - * @param object $post Global post object. - * @param bool $is_comment_feed True if the feed is a comment feed. - */ - public function disable_feeds( $post, $is_comment_feed = false ) { - - /** - * Toggle the disable feed via this filter. - * - * @since 0.4.0 - * @param bool $bool True to cancel the feed, assuming it's a post feed. - * @param object $post Global post object. - * @param bool $is_comment_feed True if the feed is a comment feed. - */ - return (bool) apply_filters( 'dwpb_disable_feed', true, $post, $is_comment_feed ); - } -} +request ) ); + } else { + $current_url = home_url( add_query_arg( array(), $wp->request ) ); + + // Filter the safe redirect to avoid redirectin non-admin urls to the dashboard + // if the fallback is used by the core wp_redirect. + add_filter( 'wp_safe_redirect_fallback', array( $this, 'wp_safe_redirect_fallback' ), 9, 1 ); + } + + // Compare the current url to the redirect url, if they are the same, bail to avoid a loop. + // If there is no valid redirect url, then also bail. + if ( $redirect_url === $current_url || ! esc_url_raw( $redirect_url ) ) { + return; + } + + /** + * Should we pass url query string in the redirect? + * + * Default is false. + * + * @since 0.5.0 + * @param bool $bool true to allow query strings on redirect. + * @return bool + */ + if ( apply_filters( 'dwpb_pass_query_string_on_redirect', false ) ) { + $redirect_url = $this->parse_query_string( $redirect_url ); + } + + wp_safe_redirect( esc_url_raw( $redirect_url ), $this->get_redirect_status_code( $current_url, $redirect_url ) ); + exit; + } + + /** + * Parse the current query string and add it, clean and filter, to the url. + * + * @since 0.5.0 + * @param string $url the url any query string will be added to. + * @return string + */ + private function parse_query_string( $url ) { + + if ( ! isset( $_SERVER['QUERY_STRING'] ) || empty( $_SERVER['QUERY_STRING'] ) ) { + return $url; + } + + // Setup an array of the current query string variables. + $query_vars = array(); + wp_parse_str( $_SERVER['QUERY_STRING'], $query_vars ); // phpcs:ignore + + /** + * Filter for allowed queary string variables. + * + * @since 0.5.0 + * @param array $allowed_query_vars an array of the allowed query variable keys. + * @return array + */ + $allowed_query_vars = (array) apply_filters( 'dwpb_allowed_query_vars', array() ); + if ( ! empty( $allowed_query_vars ) ) { + $allowed_query_vars = array_filter( + $allowed_query_vars, + function ( $value ) { + $esc_value = esc_html( $value ); + return ! empty( $esc_value ); + } + ); + $query_vars = array_intersect_key( $query_vars, array_flip( $allowed_query_vars ) ); + } + + // Escaping and sanitization are important. + $query_vars = array_filter( + $query_vars, + function ( $value ) { + $esc_value = esc_html( $value ); + return ! empty( $esc_value ); + } + ); + $query_vars = array_filter( + $query_vars, + function ( $value ) { + $esc_value = esc_html( $value ); + return ! empty( $esc_value ); + }, + ARRAY_FILTER_USE_KEY + ); + + // if we have any query variables, add it to the url. + if ( ! empty( $query_vars ) ) { + $url = add_query_arg( $query_vars, $url ); + } + + return $url; + } + + /** + * Return the status code used in the main redirect function. + * + * @since 0.5.0 + * @param string $current_url the url being redirected FROM. + * @param string $redirect_url the url being redirected TO. + * @return int + */ + private function get_redirect_status_code( $current_url, $redirect_url ) { + + /** + * Filter the status code, must be a valid number to pass, defaults to 301. + * + * @since 0.5.0 + * @param int $status_code the status code returned with the redirect. + * @param string $current_url the url being redirected FROM. + * @param string $redirect_url the url being redirected TO. + * @return int $status_code + */ + $status_code = apply_filters( 'dwpb_redirect_status_code', 301, $current_url, $redirect_url ); + + // Make sure we have a valid redirect status code, if not we set to 301. + // helps to stop a wp_die in wp_redirect if the filtered value returns something invalid. + if ( ! absint( $status_code ) || 300 > $status_code || 399 < $status_code ) { + return 301; + } + + return absint( $status_code ); + } + + /** + * Filter the safe redirect fallback to prevent front-end users + * in public redirects from being redirected to the admin url + * which is the WP core default for safe redirects. + * + * @since 0.5.0 + * @param string $url the fallback url. + * @return string + */ + public function wp_safe_redirect_fallback( $url ) { + + if ( ! is_admin() ) { + return home_url(); + } + + return $url; + } + + /** + * Check what post types are supporting author archives. + * + * @since 0.5.0 + * @return bool|array $post_types Either an array of post types or false. + */ + public function author_archive_post_types() { + + /** + * The post types supported on author archives. + * + * By default only posts are shown on author archives, if other post types are to appear + * on the author archives, pass them with this filter. + * + * @since 0.5.0 + * @param array|bool $post_types an array of post type slugs for author archives, false to disable. + */ + $post_types = apply_filters( 'dwpb_author_archive_post_types', array() ); + + if ( ! empty( $post_types ) ) { + return $post_types; + } + + // Return false if there are no supported post types. + return false; + } + + /** + * Check if we are disabling author archives. + * + * Will only return true if the filter is actively set to true + * AND there are valid post types to show on the author archives. + * + * @since 0.5.0 + * @return bool + */ + public function disable_author_archives() { + + /** + * Disable the author archives. + * + * Because many plugins and themes use author archives for profile pages, + * the ability to disable the author archive defaults to false. + * + * @since 0.5.0 + * @param bool $bool True to disable author archives. + * @return bool + */ + return (bool) apply_filters( 'dwpb_disable_author_archives', false ); + } + + /** + * Toggle the disable feed via this filter. + * + * @since 0.5.1 + * @param object $post Global post object. + * @param bool $is_comment_feed True if the feed is a comment feed. + */ + public function disable_feeds( $post, $is_comment_feed = false ) { + + /** + * Toggle the disable feed via this filter. + * + * @since 0.4.0 + * @param bool $bool True to cancel the feed, assuming it's a post feed. + * @param object $post Global post object. + * @param bool $is_comment_feed True if the feed is a comment feed. + */ + return (bool) apply_filters( 'dwpb_disable_feed', true, $post, $is_comment_feed ); + } +} diff --git a/includes/class-disable-blog-i18n.php b/includes/class-disable-blog-i18n.php index 7dffd1f..22472f3 100644 --- a/includes/class-disable-blog-i18n.php +++ b/includes/class-disable-blog-i18n.php @@ -1,37 +1,37 @@ - - */ - -/** - * Define the internationalization functionality. - * - * Loads and defines the internationalization files for this plugin - * so that it is ready for translation. - * - * @since 0.4.0 - */ -class Disable_Blog_I18n { - - /** - * Load the plugin text domain for translation. - * - * @since 0.4.0 - */ - public function load_plugin_textdomain() { - load_plugin_textdomain( - 'disable-blog', - false, - dirname( plugin_basename( __FILE__ ), 2 ) . '/languages/' - ); - } -} + + */ + +/** + * Define the internationalization functionality. + * + * Loads and defines the internationalization files for this plugin + * so that it is ready for translation. + * + * @since 0.4.0 + */ +class Disable_Blog_I18n { + + /** + * Load the plugin text domain for translation. + * + * @since 0.4.0 + */ + public function load_plugin_textdomain() { + load_plugin_textdomain( + 'disable-blog', + false, + dirname( plugin_basename( __FILE__ ), 2 ) . '/languages/' + ); + } +} diff --git a/includes/class-disable-blog-integrations.php b/includes/class-disable-blog-integrations.php index 5c020d3..6b6d383 100644 --- a/includes/class-disable-blog-integrations.php +++ b/includes/class-disable-blog-integrations.php @@ -1,108 +1,108 @@ -is_plugin_active( 'disable-comments/disable-comments.php' ) || class_exists( 'Disable_Comments' ) ); - } - - /** - * Check if WooCommerce is active. - * - * @since 0.5.3 - * @return bool - */ - public function is_woocommerce_active() { - return ( $this->is_plugin_active( 'woocommerce/woocommerce.php' ) || function_exists( 'WC' ) ); - } - - /** - * Turn the comments object back into an array if WooCommerce is active. - * - * This is only necessary for version of WooCommerce prior to 2.6.3, where it failed - * to check/convert the $comment object into an array. - * - * @since 0.4.3 - * @since 0.5.3 Moved to the Disable_Blog_Integrations class. - * @param object $comments the array of comments. - * @param int $post_id the post id. - * @return array - */ - public function filter_woocommerce_comment_count( $comments, $post_id ) { - - if ( 0 === $post_id && $this->woocommerce_version_check( '2.6.2' ) ) { - $comments = (array) $comments; - } - - return $comments; - } - - /** - * Check if the WooCommerce version is less than the checked version. - * - * @since 0.5.4 - * @param string $checked_version The version to check against. - * @param string $check The comparison operator. - * @return bool - */ - private function woocommerce_version_check( $checked_version, $check = '<=' ) { - - // Check if WooCommerce is active. - if ( $this->is_woocommerce_active() ) { - - // Figure out the version of WooCommerce. - if ( defined( 'WC_VERSION' ) ) { - $woo_version = WC_VERSION; - } elseif ( defined( 'WOOCOMMERCE_VERSION' ) ) { - $woo_version = WOOCOMMERCE_VERSION; - } else { - return false; - } - - // Check if the WooCommerce version is less than the checked version. - return version_compare( $woo_version, $checked_version, $check ); - } - - return false; - } -} +is_plugin_active( 'disable-comments/disable-comments.php' ) || class_exists( 'Disable_Comments' ) ); + } + + /** + * Check if WooCommerce is active. + * + * @since 0.5.3 + * @return bool + */ + public function is_woocommerce_active() { + return ( $this->is_plugin_active( 'woocommerce/woocommerce.php' ) || function_exists( 'WC' ) ); + } + + /** + * Turn the comments object back into an array if WooCommerce is active. + * + * This is only necessary for version of WooCommerce prior to 2.6.3, where it failed + * to check/convert the $comment object into an array. + * + * @since 0.4.3 + * @since 0.5.3 Moved to the Disable_Blog_Integrations class. + * @param object $comments the array of comments. + * @param int $post_id the post id. + * @return array + */ + public function filter_woocommerce_comment_count( $comments, $post_id ) { + + if ( 0 === $post_id && $this->woocommerce_version_check( '2.6.2' ) ) { + $comments = (array) $comments; + } + + return $comments; + } + + /** + * Check if the WooCommerce version is less than the checked version. + * + * @since 0.5.4 + * @param string $checked_version The version to check against. + * @param string $check The comparison operator. + * @return bool + */ + private function woocommerce_version_check( $checked_version, $check = '<=' ) { + + // Check if WooCommerce is active. + if ( $this->is_woocommerce_active() ) { + + // Figure out the version of WooCommerce. + if ( defined( 'WC_VERSION' ) ) { + $woo_version = WC_VERSION; + } elseif ( defined( 'WOOCOMMERCE_VERSION' ) ) { + $woo_version = WOOCOMMERCE_VERSION; + } else { + return false; + } + + // Check if the WooCommerce version is less than the checked version. + return version_compare( $woo_version, $checked_version, $check ); + } + + return false; + } +} diff --git a/includes/class-disable-blog-loader.php b/includes/class-disable-blog-loader.php index 9a82942..b436dcb 100644 --- a/includes/class-disable-blog-loader.php +++ b/includes/class-disable-blog-loader.php @@ -1,187 +1,187 @@ - - */ - -/** - * Register all actions and filters for the plugin. - * - * Maintain a list of all hooks that are registered throughout - * the plugin, and register them with the WordPress API. Call the - * run function to execute the list of actions and filters. - * - * @since 0.4.0 - */ -class Disable_Blog_Loader { - - /** - * The array of actions registered with WordPress. - * - * @since 0.4.0 - * @access protected - * @var array $actions The actions registered with WordPress to fire when the plugin loads. - */ - protected $actions; - - /** - * The array of filters registered with WordPress. - * - * @since 0.4.0 - * @access protected - * @var array $filters The filters registered with WordPress to fire when the plugin loads. - */ - protected $filters; - - /** - * Initialize the collections used to maintain the actions and filters. - * - * @since 0.4.0 - */ - public function __construct() { - $this->actions = array(); - $this->filters = array(); - } - - /** - * Simple Autoloader. - * - * @since 0.5.1 - * @param string $requested_class The Class to autoload. - */ - public function autoloader( $requested_class ) { - - $requested_class = 'class-' . str_replace( '_', '-', strtolower( $requested_class ) ) . '.php'; - $path = plugin_dir_path( __DIR__ ); - $sources = array( 'includes' ); - - foreach ( $sources as $source ) { - if ( file_exists( $path . $source . '/' . $requested_class ) ) { - include $path . $source . '/' . $requested_class; - } - } - } - - /** - * Add a new action to the collection to be registered with WordPress. - * - * @since 0.4.0 - * @param string $hook The name of the WordPress action that is being registered. - * @param object $component A reference to the instance of the object on which the action is defined. - * @param string $callback The name of the function definition on the $component. - * @param int $priority Optional. he priority at which the function should be fired. Default is 10. - * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1. - */ - public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { - $this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args ); - } - - /** - * Add a new filter to the collection to be registered with WordPress. - * - * @since 0.4.0 - * @param string $hook The name of the WordPress filter that is being registered. - * @param object $component A reference to the instance of the object on which the filter is defined. - * @param string $callback The name of the function definition on the $component. - * @param int $priority Optional. he priority at which the function should be fired. Default is 10. - * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1. - */ - public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { - $this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args ); - } - - /** - * A utility function that is used to register the actions and hooks into a single - * collection. - * - * @since 0.4.0 - * @access private - * @param array $hooks The collection of hooks that is being registered (that is, actions or filters). - * @param string $hook The name of the WordPress filter that is being registered. - * @param object $component A reference to the instance of the object on which the filter is defined. - * @param string $callback The name of the function definition on the $component. - * @param int $priority The priority at which the function should be fired. - * @param int $accepted_args The number of arguments that should be passed to the $callback. - * @return array The collection of actions and filters registered with WordPress. - */ - private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args ) { - - $hooks[] = array( - 'hook' => $hook, - 'component' => $component, - 'callback' => $callback, - 'priority' => $priority, - 'accepted_args' => $accepted_args, - ); - - return $hooks; - } - - /** - * Remove a filter from the collection registered with WordPress. - * - * @since 0.5.1 - * @param string $tag The filter hook to which the function to be removed is hooked. - * @param string $class_name Class name registering the filter callback. - * @param string $method_to_remove Method name for the filter's callback. - * @param int $priority The priority of the method (default 10). - * - * @return bool $removed Whether the function is removed. - */ - public function remove_filter( $tag, $class_name = '', $method_to_remove = '', $priority = 10 ) { - - global $wp_filter; - $removed = false; - - foreach ( $wp_filter[ $tag ]->callbacks as $filter_priority => $filters ) { - - if ( $filter_priority === $priority ) { - foreach ( $filters as $filter ) { - if ( $filter['function'][1] === $method_to_remove - && is_object( $filter['function'][0] ) // only WP 4.7 and above. This plugin is requiring at least WP 4.9. - && $filter['function'][0] instanceof $class_name ) { - $removed = $wp_filter[ $tag ]->remove_filter( $tag, array( $filter['function'][0], $method_to_remove ), $priority ); - } - } - } - } - - return $removed; - } - - /** - * Remove an action from the collection registered with WordPress. - * - * @since 0.5.1 - * @param string $tag The filter hook to which the function to be removed is hooked. - * @param string $class_name Class name registering the filter callback. - * @param string $method_to_remove Method name for the filter's callback. - * @param int $priority The priority of the method (default 10). - * - * @return bool $removed Whether the function is removed. - */ - public function remove_action( $tag, $class_name = '', $method_to_remove = '', $priority = 10 ) { - return $this->remove_filter( $tag, $class_name, $method_to_remove, $priority ); - } - - /** - * Register the filters and actions with WordPress. - * - * @since 0.4.0 - */ - public function run() { - - foreach ( $this->filters as $hook ) { - add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); - } - - foreach ( $this->actions as $hook ) { - add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); - } - } -} + + */ + +/** + * Register all actions and filters for the plugin. + * + * Maintain a list of all hooks that are registered throughout + * the plugin, and register them with the WordPress API. Call the + * run function to execute the list of actions and filters. + * + * @since 0.4.0 + */ +class Disable_Blog_Loader { + + /** + * The array of actions registered with WordPress. + * + * @since 0.4.0 + * @access protected + * @var array $actions The actions registered with WordPress to fire when the plugin loads. + */ + protected $actions; + + /** + * The array of filters registered with WordPress. + * + * @since 0.4.0 + * @access protected + * @var array $filters The filters registered with WordPress to fire when the plugin loads. + */ + protected $filters; + + /** + * Initialize the collections used to maintain the actions and filters. + * + * @since 0.4.0 + */ + public function __construct() { + $this->actions = array(); + $this->filters = array(); + } + + /** + * Simple Autoloader. + * + * @since 0.5.1 + * @param string $requested_class The Class to autoload. + */ + public function autoloader( $requested_class ) { + + $requested_class = 'class-' . str_replace( '_', '-', strtolower( $requested_class ) ) . '.php'; + $path = plugin_dir_path( __DIR__ ); + $sources = array( 'includes' ); + + foreach ( $sources as $source ) { + if ( file_exists( $path . $source . '/' . $requested_class ) ) { + include $path . $source . '/' . $requested_class; + } + } + } + + /** + * Add a new action to the collection to be registered with WordPress. + * + * @since 0.4.0 + * @param string $hook The name of the WordPress action that is being registered. + * @param object $component A reference to the instance of the object on which the action is defined. + * @param string $callback The name of the function definition on the $component. + * @param int $priority Optional. he priority at which the function should be fired. Default is 10. + * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1. + */ + public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { + $this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args ); + } + + /** + * Add a new filter to the collection to be registered with WordPress. + * + * @since 0.4.0 + * @param string $hook The name of the WordPress filter that is being registered. + * @param object $component A reference to the instance of the object on which the filter is defined. + * @param string $callback The name of the function definition on the $component. + * @param int $priority Optional. he priority at which the function should be fired. Default is 10. + * @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1. + */ + public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) { + $this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args ); + } + + /** + * A utility function that is used to register the actions and hooks into a single + * collection. + * + * @since 0.4.0 + * @access private + * @param array $hooks The collection of hooks that is being registered (that is, actions or filters). + * @param string $hook The name of the WordPress filter that is being registered. + * @param object $component A reference to the instance of the object on which the filter is defined. + * @param string $callback The name of the function definition on the $component. + * @param int $priority The priority at which the function should be fired. + * @param int $accepted_args The number of arguments that should be passed to the $callback. + * @return array The collection of actions and filters registered with WordPress. + */ + private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args ) { + + $hooks[] = array( + 'hook' => $hook, + 'component' => $component, + 'callback' => $callback, + 'priority' => $priority, + 'accepted_args' => $accepted_args, + ); + + return $hooks; + } + + /** + * Remove a filter from the collection registered with WordPress. + * + * @since 0.5.1 + * @param string $tag The filter hook to which the function to be removed is hooked. + * @param string $class_name Class name registering the filter callback. + * @param string $method_to_remove Method name for the filter's callback. + * @param int $priority The priority of the method (default 10). + * + * @return bool $removed Whether the function is removed. + */ + public function remove_filter( $tag, $class_name = '', $method_to_remove = '', $priority = 10 ) { + + global $wp_filter; + $removed = false; + + foreach ( $wp_filter[ $tag ]->callbacks as $filter_priority => $filters ) { + + if ( $filter_priority === $priority ) { + foreach ( $filters as $filter ) { + if ( $filter['function'][1] === $method_to_remove + && is_object( $filter['function'][0] ) // only WP 4.7 and above. This plugin is requiring at least WP 4.9. + && $filter['function'][0] instanceof $class_name ) { + $removed = $wp_filter[ $tag ]->remove_filter( $tag, array( $filter['function'][0], $method_to_remove ), $priority ); + } + } + } + } + + return $removed; + } + + /** + * Remove an action from the collection registered with WordPress. + * + * @since 0.5.1 + * @param string $tag The filter hook to which the function to be removed is hooked. + * @param string $class_name Class name registering the filter callback. + * @param string $method_to_remove Method name for the filter's callback. + * @param int $priority The priority of the method (default 10). + * + * @return bool $removed Whether the function is removed. + */ + public function remove_action( $tag, $class_name = '', $method_to_remove = '', $priority = 10 ) { + return $this->remove_filter( $tag, $class_name, $method_to_remove, $priority ); + } + + /** + * Register the filters and actions with WordPress. + * + * @since 0.4.0 + */ + public function run() { + + foreach ( $this->filters as $hook ) { + add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); + } + + foreach ( $this->actions as $hook ) { + add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] ); + } + } +} diff --git a/includes/class-disable-blog-public.php b/includes/class-disable-blog-public.php index 659db75..1f91375 100644 --- a/includes/class-disable-blog-public.php +++ b/includes/class-disable-blog-public.php @@ -1,566 +1,566 @@ -plugin_name = $plugin_name; - $this->version = $version; - $this->functions = new Disable_Blog_Functions(); - } - - /** - * Redirect single post pages - * - * @uses dwpb_post_types_with_tax() - * @link http://codex.wordpress.org/Plugin_API/Action_Reference/template_redirect - * @since 0.2.0 - * @since 0.4.9 added sitemap checks to avoid redirects on new sitemaps in WP v5.5. - * @since 0.5.0 renamed to redirect_public_pages - * @return void - */ - public function redirect_public_pages() { - - // Don't redirect on admin or sitemap, and only if there is a homepage to redirect to. - $sitemap = get_query_var( 'sitemap', false ); - $sitemap_styelsheet = get_query_var( 'sitemap-stylesheet', false ); - if ( is_admin() - || ! get_option( 'page_on_front' ) - || ! empty( $sitemap ) - || ! empty( $sitemap_styelsheet ) ) { - return; - } - - // Get the front page id and url. - $page_id = get_option( 'page_on_front' ); - $homepage_url = get_permalink( $page_id ); - $redirect_url = false; - - // The public pages to potentially be redirected. - global $post; - $public_redirects = array( - 'post' => ( $post instanceof WP_Post && is_singular( 'post' ) ), - 'post_tag_archive' => ( is_tag() && ! dwpb_post_types_with_tax( 'post_tag' ) ), - 'category_archive' => ( is_category() && ! dwpb_post_types_with_tax( 'category' ) ), - 'blog_page' => is_home(), - 'date_archive' => is_date(), - 'author_archive' => ( is_author() && true === $this->functions->disable_author_archives() ), - ); - - // cycle through each public page, checking if we need to redirect. - foreach ( $public_redirects as $filtername => $bool ) { - - // If this is the right page, then setup the redirect url. - if ( true === $bool ) { - - // Custom function within this class used to check if the page needs to be redirected. - $filter = 'dwpb_redirect_' . $filtername; - - /** - * The redirect url used for this public page. - * - * Example: use 'dwpb_redirect_post' to change the redirect url used - * on a post, or 'dwpb_redirect_post_tag_archive' to redirect tag archives. - * - * @since 0.4.0 - * @since 0.5.0 combine filters. - * @param string $url the url to redirect to, defaults to homepage. - */ - $redirect_url = apply_filters( $filter, $homepage_url ); - - break; // no need to keep looping. - - } // end if - } // end foreach - - // Only continue if we have a redirect url. - if ( ! $redirect_url ) { - return; - } - - /** - * Filter to toggle the plugin's front-end redirection. - * - * @since 0.2.0 - * @since 0.4.0 added the current_url param. - * @since 0.5.0 removed 'redirect_url' && 'current_url' params. - * @param bool $bool True to enable, false to disable. - */ - if ( apply_filters( 'dwpb_redirect_front_end', true ) ) { - - /** - * Global public url redirect filter. - * - * @since 0.5.0 - * @param string $redirect_url The redirect url. - */ - $redirect_url = apply_filters( 'dwpb_front_end_redirect_url', $redirect_url ); - - $this->functions->redirect( $redirect_url ); - } - } - - /** - * Modify query. - * - * Remove 'post' post type from any searches and archives. - * - * @uses $this->remove_post_from_array_in_query - * - * @link http://codex.wordpress.org/Plugin_API/Action_Reference/template_redirect - * @link http://stackoverflow.com/questions/7225070/php-array-delete-by-value-not-key#7225113 - * - * @since 0.2.0 - * @since 0.4.0 added remove_post_from_array_in_query function - * @since 0.4.9 remove 'post' from all archives. - * @since 0.4.10 update to just remove 'post' from built-in taxonomy archives, - * @since 0.5.0 remove 'post' type from author archives. - * @param object $query the query object. - * @return void - */ - public function modify_query( $query ) { - - // Bail if we're in the admin or not on the main query. - if ( is_admin() || ! $query->is_main_query() ) { - return; - } - - // Let's see if there are any post types supporting build-in taxonomies. - $tag_post_types = dwpb_post_types_with_tax( 'post_tag' ); - $category_post_types = dwpb_post_types_with_tax( 'category' ); - $author_post_types = $this->functions->author_archive_post_types(); - - // Remove existing posts from built-in taxonomy archives, if they are supported by another post type. - if ( $query->is_tag() && $tag_post_types ) { // tag archives. - - $this->set_post_types_in_query( $query, $tag_post_types, 'dwpb_tag_post_types' ); - - } elseif ( $query->is_category() && $category_post_types ) { // category archives. - - $this->set_post_types_in_query( $query, $category_post_types, 'dwpb_category_post_types' ); - - } elseif ( $query->is_author() && ! empty( $author_post_types ) ) { // author archives, if supported, have a filter for setting the post types. - - $this->set_post_types_in_query( $query, $author_post_types ); - - } - } - - /** - * Set post types for tag and category archive queries, excluding 'post' as the default type. - * - * Used in $this->modify_query to remove 'post' type from built-in archive queries. - * - * @since 0.4.0 - * @param object $query the main query object. - * @param array $post_types the array of post types. - * @param string $filter the filter to be applied. - * @return bool - */ - public function set_post_types_in_query( $query, $post_types = array(), $filter = '' ) { - - /** - * If there is a filter name passed, then a filter is applied on the array and query. - * - * Used for 'dwpb_tag_post_types' and 'dwpb_category_post_types' filters. - * Note that the 'dwpb_author_archive_post_types' filter is passed in another function, - * hence the reason $filter can be empty and not passed in this function. - * - * @see Disable_Blog_Public->modify_query - * @since 0.4.0 - * @since 0.4.10 fix bug in 0.4.9 causing cpt weirdness, now always using the filter. - * @since 0.5.0 made the filter part of this function optional, since the author - * post type filter is located in the functions class. - * @param array $post_types An array of post type slugs. - * @param object $query The query object being modified. - * @return array - */ - if ( ! empty( $filter ) ) { - $set_to = apply_filters( $filter, $post_types, $query ); - } else { - $set_to = $post_types; - } - if ( ! empty( $set_to ) && method_exists( $query, 'set' ) && is_array( $set_to ) ) { - $query->set( 'post_type', $set_to ); - return true; - } - - return false; - } - - /** - * Disable Blog feeds. - * - * @since 0.1.0 - * @since 0.4.0 add $is_comment_feed variable to feeds and check $is_comment_feed prior to redirect. - * @param bool $is_comment_feed true if a comment feed. - * @return void - */ - public function disable_feed( $is_comment_feed ) { - - // If this is a comment feed and comments are supported by other post types, bail. - if ( $is_comment_feed && dwpb_post_types_with_feature( 'comments' ) ) { - return; - } - - // Option to override this via filter and check to confirm post type. - global $post; - - // Check that we're disabling feeds and everything is good to go. - if ( $this->functions->disable_feeds( $post, $is_comment_feed ) && isset( $post->post_type ) && 'post' === $post->post_type ) { - - /** - * Filter the feed redirect url. - * - * @since 0.4.0 - * @param string $url The redirect url (defaults to homepage) - * @param object $post The global post object. - * @param bool $is_comment_feed True if the feed is a comment feed. - */ - $redirect_url = apply_filters( 'dwpb_redirect_feeds', home_url(), $post, $is_comment_feed ); - - /** - * Filter to toggle on a message instead of a redirect. - * - * Defaults to false, so a redirect is the expacted default behavior. - * - * @since 0.4.0 - * @since 0.4.9 updated variables passed to match other feed filters, - * previously only $is_comment_feed was passed and now - * the order is: bool, $post, $is_comment_feed. - * Note that if you used this filter before - * and relied on the $is_comment_feed, you'll need to update. - * @param bool $bool True to use a message, false to redirect. - * @param object $post Global post object. - * @param bool $is_comment_feed True if the feed is a comment feed. - */ - if ( apply_filters( 'dwpb_feed_message', false, $post, $is_comment_feed ) ) { - - // translators: This message appears when the feed is disabled instead of redirect, it should point to the homepage. - $message = sprintf( '%s: %s', __( 'No feed available, please visit our homepage:', 'disable-blog' ), esc_url_raw( $redirect_url ), esc_url_raw( $redirect_url ) ); - - /** - * Filter the feed die message. - * - * If the `dwpb_feed_message` is set to true, use this filter to set a custom message. - * - * @since 0.4.0 - * @param string $message - */ - $message = apply_filters( 'dwpb_feed_die_message', $message ); - $allowed_html = array( - 'a' => array( - 'href' => array(), - 'name' => array(), - 'id' => array(), - ), - ); - wp_die( wp_kses( $message, $allowed_html ) ); - - } else { // Default option: redirect to homepage. - - $this->functions->redirect( $redirect_url ); - - } - } - } - - /** - * Turn off the feed link. - * - * Only works for WordPress >= 4.4.0. - * - * @since 0.4.0 - * @param bool $show true to show the posts feed link. - * @return bool - */ - public function feed_links_show_posts_feed( $show ) { - return false; - } - - /** - * Turn off the comment's feed link. - * - * Only works for WordPress >= 4.4.0. - * - * @since 0.4.0 - * @param bool $show true to show the comments feed link. - * @return bool - */ - public function feed_links_show_comments_feed( $show ) { - - // If 'post' type is the only type supporting comments, then disable the comment feed link. - if ( ! dwpb_post_types_with_feature( 'comments' ) ) { - $show = false; - } - - return $show; - } - - /** - * Remove feed urls from head. - * - * @since 0.4.9 - * @return void - */ - public function header_feeds() { - - // Various feed links. - $feed = array( - 'feed_links' => 2, - 'feed_links_extra' => 3, - 'rsd_link' => 10, - 'wlwmanifest_link' => 10, - ); - - // Remove from head. - foreach ( $feed as $function => $priority ) { - remove_action( 'wp_head', $function, $priority ); - } - } - - /** - * Unset all post-related xmlrpc methods. - * - * @see wp-includes/class-wp-xmlrpc-server.php - * @since 0.4.9 - * @param array $methods the arrayve of xmlrpc methods. - * @return array - */ - public function xmlrpc_methods( $methods ) { - - $methods_to_remove = $this->get_disabled_xmlrpc_methods(); - - if ( ! empty( $methods_to_remove ) && is_array( $methods_to_remove ) ) { - foreach ( $methods_to_remove as $method ) { - if ( isset( $methods[ $method ] ) ) { - unset( $methods[ $method ] ); - } - } - } - - return $methods; - } - - /** - * Get the XML-RPC methods to disable. - * - * @since 0.5.0 - * @return array|bool - */ - private function get_disabled_xmlrpc_methods() { - - // The methods to remove. - $methods_to_remove = array( - 'wp.getUsersBlogs', - 'wp.newPost', - 'wp.editPost', - 'wp.deletePost', - 'wp.getPost', - 'wp.getPosts', - 'blogger.getPost', - 'blogger.getRecentPosts', - 'blogger.newPost', - 'blogger.editPost', - 'blogger.deletePost', - 'metaWeblog.newPost', - 'metaWeblog.editPost', - 'metaWeblog.getPost', - 'metaWeblog.getRecentPosts', - 'metaWeblog.deletePost', - 'mt.getRecentPostTitles', - 'mt.getTrackbackPings', - 'mt.publishPost', - 'pingback.ping', - 'pingback.extensions.getPingbacks', - 'system.multicall', - 'system.listMethods', - 'system.getCapabilities', - 'demo.sayHello', - 'demo.addTwoNumbers', - ); - - // Remove category / post tag terms, if not supported by another post type. - $taxonomy_methods = array(); - if ( ! dwpb_post_types_with_tax( 'category' ) ) { - $taxonomy_methods = array( - 'wp.newCategory', - 'wp.deleteeCategory', - 'mt.getCategoryList', - 'wp.suggestCategories', - 'mt.getPostCategories', - 'mt.setPostCategories', - 'metaWeblog.getCategories', - ); - } - if ( ! dwpb_post_types_with_tax( 'post_tag' ) ) { - $taxonomy_methods[] = 'wp.getTags'; - } - - $methods_to_remove = array_merge( $methods_to_remove, $taxonomy_methods ); - - /** - * Filter the methods being disabled by the plugin. - * - * Return false to disable this functionality entirely and keep all methods in place. - * - * @since 0.5.0 - * @param array $methods_to_remove an array of all the XMLRPC methods to disable. - * @return array|bool - */ - $methods_to_remove = apply_filters( 'dwpb_disabled_xmlrpc_methods', $methods_to_remove ); - - // filter any invalid entries out before returning the array. - return is_array( $methods_to_remove ) ? array_filter( $methods_to_remove, 'is_string' ) : false; // phpcs:ignore - } - - /** - * Remove the X-Pingback HTTP header. - * - * @since 0.4.0 - * @since 0.5.1 moved to the public class. - * @param array $headers the pingback headers. - * @return array - */ - public function filter_wp_headers( $headers ) { - - /** - * Toggle the disable pinback header feature. - * - * @since 0.4.0 - * @param bool $bool True to disable the header, false to keep it. - */ - if ( apply_filters( 'dwpb_remove_pingback_header', true ) && isset( $headers['X-Pingback'] ) ) { - unset( $headers['X-Pingback'] ); - } - - return $headers; - } - - /** - * Remove 'post' post type from sitemaps. - * - * @since 0.4.9 - * @param array $post_types an array of post type strings supported in sitemaps. - * @return array - */ - public function wp_sitemaps_post_types( $post_types ) { - - if ( isset( $post_types['post'] ) ) { - unset( $post_types['post'] ); - } - - return $post_types; - } - - /** - * Conditionally remove built-in taxonomies from sitemaps, if they are not being used by a custom post type. - * - * @since 0.4.9 - * @uses dwpb_post_types_with_tax() - * @param array $taxonomies an array of taxonomy strings supported in sitemaps. - * @return array - */ - public function wp_sitemaps_taxonomies( $taxonomies ) { - - $built_in_taxonomies = array( - 'post_tag', - 'category', - ); - foreach ( $built_in_taxonomies as $tax ) { - if ( isset( $taxonomies[ $tax ] ) && ! dwpb_post_types_with_tax( $tax ) ) { - unset( $taxonomies[ $tax ] ); - } - } - - return $taxonomies; - } - - /** - * Remove author sitemaps. - * - * @since 0.5.0 - * @link https://developer.wordpress.org/reference/hooks/wp_sitemaps_add_provider/ - * @param object $provider Instance of a WP_Sitemaps_Provider. - * @param string $name Name of the sitemap provider. - * @return object|bool Instance of a WP_Sitemaps_Provider or false. - */ - public function wp_author_sitemaps( $provider, $name ) { - - // If there are no author archives, then don't show the sitemap. - $disable_author_archives = $this->functions->disable_author_archives(); - if ( true === $disable_author_archives ) { - - $disable_sitemap = true; - - } else { // Otherwise, we may show it? - - // Check if we have any post types supporting author archives. - $author_archives_supported = $this->functions->author_archive_post_types(); - - // Only show the sitemap if there are post types support on the archives. - $disable_sitemap = empty( $author_archives_supported ); - } - - /** - * Turn off user/author sitemaps. - * - * @since 0.5.0 - * @param bool $bool True to disable, defaults to true. - * @return bool - */ - if ( 'users' === $name && apply_filters( 'dwpb_disable_user_sitemap', $disable_sitemap ) ) { - return false; - } - - return $provider; - } -} +plugin_name = $plugin_name; + $this->version = $version; + $this->functions = new Disable_Blog_Functions(); + } + + /** + * Redirect single post pages + * + * @uses dwpb_post_types_with_tax() + * @link http://codex.wordpress.org/Plugin_API/Action_Reference/template_redirect + * @since 0.2.0 + * @since 0.4.9 added sitemap checks to avoid redirects on new sitemaps in WP v5.5. + * @since 0.5.0 renamed to redirect_public_pages + * @return void + */ + public function redirect_public_pages() { + + // Don't redirect on admin or sitemap, and only if there is a homepage to redirect to. + $sitemap = get_query_var( 'sitemap', false ); + $sitemap_styelsheet = get_query_var( 'sitemap-stylesheet', false ); + if ( is_admin() + || ! get_option( 'page_on_front' ) + || ! empty( $sitemap ) + || ! empty( $sitemap_styelsheet ) ) { + return; + } + + // Get the front page id and url. + $page_id = get_option( 'page_on_front' ); + $homepage_url = get_permalink( $page_id ); + $redirect_url = false; + + // The public pages to potentially be redirected. + global $post; + $public_redirects = array( + 'post' => ( $post instanceof WP_Post && is_singular( 'post' ) ), + 'post_tag_archive' => ( is_tag() && ! dwpb_post_types_with_tax( 'post_tag' ) ), + 'category_archive' => ( is_category() && ! dwpb_post_types_with_tax( 'category' ) ), + 'blog_page' => is_home(), + 'date_archive' => is_date(), + 'author_archive' => ( is_author() && true === $this->functions->disable_author_archives() ), + ); + + // cycle through each public page, checking if we need to redirect. + foreach ( $public_redirects as $filtername => $bool ) { + + // If this is the right page, then setup the redirect url. + if ( true === $bool ) { + + // Custom function within this class used to check if the page needs to be redirected. + $filter = 'dwpb_redirect_' . $filtername; + + /** + * The redirect url used for this public page. + * + * Example: use 'dwpb_redirect_post' to change the redirect url used + * on a post, or 'dwpb_redirect_post_tag_archive' to redirect tag archives. + * + * @since 0.4.0 + * @since 0.5.0 combine filters. + * @param string $url the url to redirect to, defaults to homepage. + */ + $redirect_url = apply_filters( $filter, $homepage_url ); + + break; // no need to keep looping. + + } // end if + } // end foreach + + // Only continue if we have a redirect url. + if ( ! $redirect_url ) { + return; + } + + /** + * Filter to toggle the plugin's front-end redirection. + * + * @since 0.2.0 + * @since 0.4.0 added the current_url param. + * @since 0.5.0 removed 'redirect_url' && 'current_url' params. + * @param bool $bool True to enable, false to disable. + */ + if ( apply_filters( 'dwpb_redirect_front_end', true ) ) { + + /** + * Global public url redirect filter. + * + * @since 0.5.0 + * @param string $redirect_url The redirect url. + */ + $redirect_url = apply_filters( 'dwpb_front_end_redirect_url', $redirect_url ); + + $this->functions->redirect( $redirect_url ); + } + } + + /** + * Modify query. + * + * Remove 'post' post type from any searches and archives. + * + * @uses $this->remove_post_from_array_in_query + * + * @link http://codex.wordpress.org/Plugin_API/Action_Reference/template_redirect + * @link http://stackoverflow.com/questions/7225070/php-array-delete-by-value-not-key#7225113 + * + * @since 0.2.0 + * @since 0.4.0 added remove_post_from_array_in_query function + * @since 0.4.9 remove 'post' from all archives. + * @since 0.4.10 update to just remove 'post' from built-in taxonomy archives, + * @since 0.5.0 remove 'post' type from author archives. + * @param object $query the query object. + * @return void + */ + public function modify_query( $query ) { + + // Bail if we're in the admin or not on the main query. + if ( is_admin() || ! $query->is_main_query() ) { + return; + } + + // Let's see if there are any post types supporting build-in taxonomies. + $tag_post_types = dwpb_post_types_with_tax( 'post_tag' ); + $category_post_types = dwpb_post_types_with_tax( 'category' ); + $author_post_types = $this->functions->author_archive_post_types(); + + // Remove existing posts from built-in taxonomy archives, if they are supported by another post type. + if ( $query->is_tag() && $tag_post_types ) { // tag archives. + + $this->set_post_types_in_query( $query, $tag_post_types, 'dwpb_tag_post_types' ); + + } elseif ( $query->is_category() && $category_post_types ) { // category archives. + + $this->set_post_types_in_query( $query, $category_post_types, 'dwpb_category_post_types' ); + + } elseif ( $query->is_author() && ! empty( $author_post_types ) ) { // author archives, if supported, have a filter for setting the post types. + + $this->set_post_types_in_query( $query, $author_post_types ); + + } + } + + /** + * Set post types for tag and category archive queries, excluding 'post' as the default type. + * + * Used in $this->modify_query to remove 'post' type from built-in archive queries. + * + * @since 0.4.0 + * @param object $query the main query object. + * @param array $post_types the array of post types. + * @param string $filter the filter to be applied. + * @return bool + */ + public function set_post_types_in_query( $query, $post_types = array(), $filter = '' ) { + + /** + * If there is a filter name passed, then a filter is applied on the array and query. + * + * Used for 'dwpb_tag_post_types' and 'dwpb_category_post_types' filters. + * Note that the 'dwpb_author_archive_post_types' filter is passed in another function, + * hence the reason $filter can be empty and not passed in this function. + * + * @see Disable_Blog_Public->modify_query + * @since 0.4.0 + * @since 0.4.10 fix bug in 0.4.9 causing cpt weirdness, now always using the filter. + * @since 0.5.0 made the filter part of this function optional, since the author + * post type filter is located in the functions class. + * @param array $post_types An array of post type slugs. + * @param object $query The query object being modified. + * @return array + */ + if ( ! empty( $filter ) ) { + $set_to = apply_filters( $filter, $post_types, $query ); + } else { + $set_to = $post_types; + } + if ( ! empty( $set_to ) && method_exists( $query, 'set' ) && is_array( $set_to ) ) { + $query->set( 'post_type', $set_to ); + return true; + } + + return false; + } + + /** + * Disable Blog feeds. + * + * @since 0.1.0 + * @since 0.4.0 add $is_comment_feed variable to feeds and check $is_comment_feed prior to redirect. + * @param bool $is_comment_feed true if a comment feed. + * @return void + */ + public function disable_feed( $is_comment_feed ) { + + // If this is a comment feed and comments are supported by other post types, bail. + if ( $is_comment_feed && dwpb_post_types_with_feature( 'comments' ) ) { + return; + } + + // Option to override this via filter and check to confirm post type. + global $post; + + // Check that we're disabling feeds and everything is good to go. + if ( $this->functions->disable_feeds( $post, $is_comment_feed ) && isset( $post->post_type ) && 'post' === $post->post_type ) { + + /** + * Filter the feed redirect url. + * + * @since 0.4.0 + * @param string $url The redirect url (defaults to homepage) + * @param object $post The global post object. + * @param bool $is_comment_feed True if the feed is a comment feed. + */ + $redirect_url = apply_filters( 'dwpb_redirect_feeds', home_url(), $post, $is_comment_feed ); + + /** + * Filter to toggle on a message instead of a redirect. + * + * Defaults to false, so a redirect is the expacted default behavior. + * + * @since 0.4.0 + * @since 0.4.9 updated variables passed to match other feed filters, + * previously only $is_comment_feed was passed and now + * the order is: bool, $post, $is_comment_feed. + * Note that if you used this filter before + * and relied on the $is_comment_feed, you'll need to update. + * @param bool $bool True to use a message, false to redirect. + * @param object $post Global post object. + * @param bool $is_comment_feed True if the feed is a comment feed. + */ + if ( apply_filters( 'dwpb_feed_message', false, $post, $is_comment_feed ) ) { + + // translators: This message appears when the feed is disabled instead of redirect, it should point to the homepage. + $message = sprintf( '%s: %s', __( 'No feed available, please visit our homepage:', 'disable-blog' ), esc_url_raw( $redirect_url ), esc_url_raw( $redirect_url ) ); + + /** + * Filter the feed die message. + * + * If the `dwpb_feed_message` is set to true, use this filter to set a custom message. + * + * @since 0.4.0 + * @param string $message + */ + $message = apply_filters( 'dwpb_feed_die_message', $message ); + $allowed_html = array( + 'a' => array( + 'href' => array(), + 'name' => array(), + 'id' => array(), + ), + ); + wp_die( wp_kses( $message, $allowed_html ) ); + + } else { // Default option: redirect to homepage. + + $this->functions->redirect( $redirect_url ); + + } + } + } + + /** + * Turn off the feed link. + * + * Only works for WordPress >= 4.4.0. + * + * @since 0.4.0 + * @param bool $show true to show the posts feed link. + * @return bool + */ + public function feed_links_show_posts_feed( $show ) { + return false; + } + + /** + * Turn off the comment's feed link. + * + * Only works for WordPress >= 4.4.0. + * + * @since 0.4.0 + * @param bool $show true to show the comments feed link. + * @return bool + */ + public function feed_links_show_comments_feed( $show ) { + + // If 'post' type is the only type supporting comments, then disable the comment feed link. + if ( ! dwpb_post_types_with_feature( 'comments' ) ) { + $show = false; + } + + return $show; + } + + /** + * Remove feed urls from head. + * + * @since 0.4.9 + * @return void + */ + public function header_feeds() { + + // Various feed links. + $feed = array( + 'feed_links' => 2, + 'feed_links_extra' => 3, + 'rsd_link' => 10, + 'wlwmanifest_link' => 10, + ); + + // Remove from head. + foreach ( $feed as $function => $priority ) { + remove_action( 'wp_head', $function, $priority ); + } + } + + /** + * Unset all post-related xmlrpc methods. + * + * @see wp-includes/class-wp-xmlrpc-server.php + * @since 0.4.9 + * @param array $methods the arrayve of xmlrpc methods. + * @return array + */ + public function xmlrpc_methods( $methods ) { + + $methods_to_remove = $this->get_disabled_xmlrpc_methods(); + + if ( ! empty( $methods_to_remove ) && is_array( $methods_to_remove ) ) { + foreach ( $methods_to_remove as $method ) { + if ( isset( $methods[ $method ] ) ) { + unset( $methods[ $method ] ); + } + } + } + + return $methods; + } + + /** + * Get the XML-RPC methods to disable. + * + * @since 0.5.0 + * @return array|bool + */ + private function get_disabled_xmlrpc_methods() { + + // The methods to remove. + $methods_to_remove = array( + 'wp.getUsersBlogs', + 'wp.newPost', + 'wp.editPost', + 'wp.deletePost', + 'wp.getPost', + 'wp.getPosts', + 'blogger.getPost', + 'blogger.getRecentPosts', + 'blogger.newPost', + 'blogger.editPost', + 'blogger.deletePost', + 'metaWeblog.newPost', + 'metaWeblog.editPost', + 'metaWeblog.getPost', + 'metaWeblog.getRecentPosts', + 'metaWeblog.deletePost', + 'mt.getRecentPostTitles', + 'mt.getTrackbackPings', + 'mt.publishPost', + 'pingback.ping', + 'pingback.extensions.getPingbacks', + 'system.multicall', + 'system.listMethods', + 'system.getCapabilities', + 'demo.sayHello', + 'demo.addTwoNumbers', + ); + + // Remove category / post tag terms, if not supported by another post type. + $taxonomy_methods = array(); + if ( ! dwpb_post_types_with_tax( 'category' ) ) { + $taxonomy_methods = array( + 'wp.newCategory', + 'wp.deleteeCategory', + 'mt.getCategoryList', + 'wp.suggestCategories', + 'mt.getPostCategories', + 'mt.setPostCategories', + 'metaWeblog.getCategories', + ); + } + if ( ! dwpb_post_types_with_tax( 'post_tag' ) ) { + $taxonomy_methods[] = 'wp.getTags'; + } + + $methods_to_remove = array_merge( $methods_to_remove, $taxonomy_methods ); + + /** + * Filter the methods being disabled by the plugin. + * + * Return false to disable this functionality entirely and keep all methods in place. + * + * @since 0.5.0 + * @param array $methods_to_remove an array of all the XMLRPC methods to disable. + * @return array|bool + */ + $methods_to_remove = apply_filters( 'dwpb_disabled_xmlrpc_methods', $methods_to_remove ); + + // filter any invalid entries out before returning the array. + return is_array( $methods_to_remove ) ? array_filter( $methods_to_remove, 'is_string' ) : false; // phpcs:ignore + } + + /** + * Remove the X-Pingback HTTP header. + * + * @since 0.4.0 + * @since 0.5.1 moved to the public class. + * @param array $headers the pingback headers. + * @return array + */ + public function filter_wp_headers( $headers ) { + + /** + * Toggle the disable pinback header feature. + * + * @since 0.4.0 + * @param bool $bool True to disable the header, false to keep it. + */ + if ( apply_filters( 'dwpb_remove_pingback_header', true ) && isset( $headers['X-Pingback'] ) ) { + unset( $headers['X-Pingback'] ); + } + + return $headers; + } + + /** + * Remove 'post' post type from sitemaps. + * + * @since 0.4.9 + * @param array $post_types an array of post type strings supported in sitemaps. + * @return array + */ + public function wp_sitemaps_post_types( $post_types ) { + + if ( isset( $post_types['post'] ) ) { + unset( $post_types['post'] ); + } + + return $post_types; + } + + /** + * Conditionally remove built-in taxonomies from sitemaps, if they are not being used by a custom post type. + * + * @since 0.4.9 + * @uses dwpb_post_types_with_tax() + * @param array $taxonomies an array of taxonomy strings supported in sitemaps. + * @return array + */ + public function wp_sitemaps_taxonomies( $taxonomies ) { + + $built_in_taxonomies = array( + 'post_tag', + 'category', + ); + foreach ( $built_in_taxonomies as $tax ) { + if ( isset( $taxonomies[ $tax ] ) && ! dwpb_post_types_with_tax( $tax ) ) { + unset( $taxonomies[ $tax ] ); + } + } + + return $taxonomies; + } + + /** + * Remove author sitemaps. + * + * @since 0.5.0 + * @link https://developer.wordpress.org/reference/hooks/wp_sitemaps_add_provider/ + * @param object $provider Instance of a WP_Sitemaps_Provider. + * @param string $name Name of the sitemap provider. + * @return object|bool Instance of a WP_Sitemaps_Provider or false. + */ + public function wp_author_sitemaps( $provider, $name ) { + + // If there are no author archives, then don't show the sitemap. + $disable_author_archives = $this->functions->disable_author_archives(); + if ( true === $disable_author_archives ) { + + $disable_sitemap = true; + + } else { // Otherwise, we may show it? + + // Check if we have any post types supporting author archives. + $author_archives_supported = $this->functions->author_archive_post_types(); + + // Only show the sitemap if there are post types support on the archives. + $disable_sitemap = empty( $author_archives_supported ); + } + + /** + * Turn off user/author sitemaps. + * + * @since 0.5.0 + * @param bool $bool True to disable, defaults to true. + * @return bool + */ + if ( 'users' === $name && apply_filters( 'dwpb_disable_user_sitemap', $disable_sitemap ) ) { + return false; + } + + return $provider; + } +} diff --git a/includes/class-disable-blog.php b/includes/class-disable-blog.php index 17ae6ca..31e7f35 100644 --- a/includes/class-disable-blog.php +++ b/includes/class-disable-blog.php @@ -1,407 +1,407 @@ - - */ - -/** - * The core plugin class. - * - * This is used to define internationalization, admin-specific hooks, and - * public-facing site hooks. - * - * Also maintains the unique identifier of this plugin as well as the current - * version of the plugin. - * - * @since 0.4.0 - */ -class Disable_Blog { - - /** - * The loader that's responsible for maintaining and registering all hooks that power - * the plugin. - * - * @since 0.4.0 - * @access protected - * @var Disable_Blog_Loader $loader Maintains and registers all hooks for the plugin. - */ - protected $loader; - - /** - * The unique identifier of this plugin. - * - * @since 0.4.0 - * @access protected - * @var string $plugin_name The string used to uniquely identify this plugin. - */ - protected $plugin_name; - - /** - * The current version of the plugin. - * - * @since 0.4.0 - * @access protected - * @var string $version The current version of the plugin. - */ - protected $version; - - /** - * Define the core functionality of the plugin. - * - * Set the plugin name and the plugin version that can be used throughout the plugin. - * Load the dependencies, define the locale, and set the hooks for the admin area and - * the public-facing side of the site. - * - * @since 0.4.0 - * @access public - * @param string $plugin_name The name of this plugin. - * @param string $version The version of this plugin. - */ - public function __construct( $plugin_name, $version ) { - - $this->plugin_name = $plugin_name; - $this->version = $version; - - do_action( 'dwpb_init' ); - - $this->upgrade_check(); - $this->load_dependencies(); - $this->set_locale(); - $this->plugin_integrations(); - $this->define_admin_hooks(); - $this->define_public_hooks(); - } - - /** - * Upgrade check. - * - * @since 0.4.0 - * @access private - */ - private static function upgrade_check() { - - // let's only run these checks on the admin page load. - if ( ! is_admin() ) { - return; - } - - // Get the current version option. - $current_version = get_option( 'dwpb_version', false ); - - // Update the previous version if we're upgrading. - if ( $current_version && DWPB_VERSION !== $current_version ) { - update_option( 'dwpb_previous_version', $current_version, false ); - } - - // See if it's a previous version, which may not have set the version option. - if ( false === $current_version || DWPB_VERSION !== $current_version ) { - // do things on update. - - // Save current version. - update_option( 'dwpb_version', DWPB_VERSION, false ); - } - } - - /** - * Load the required dependencies for this plugin. - * - * Include the following files that make up the plugin: - * - * - Disable_Blog_Loader. Orchestrates the hooks of the plugin. - * - Disable_Blog_I18n. Defines internationalization functionality. - * - Disable_Blog_Admin. Defines all hooks for the admin area. - * - Disable_Blog_Public. Defines all hooks for the public side of the site. - * - * Create an instance of the loader which will be used to register the hooks - * with WordPress. - * - * @since 0.4.0 - * @since 0.5.3 Added Integrations class. - * @access private - */ - private function load_dependencies() { - - // Includes directory. - $includes_dir = plugin_dir_path( __DIR__ ) . 'includes'; - - /** - * The class responsible for orchestrating the actions and filters of the - * core plugin. - */ - require_once $includes_dir . '/class-disable-blog-loader.php'; - - /** - * File with common functions. - */ - require_once $includes_dir . '/functions.php'; - - /** - * Make it so. - */ - $this->loader = new Disable_Blog_Loader(); - - $classes = array( - 'Disable_Blog_I18n', - 'Disable_Blog_Functions', - 'Disable_Blog_Admin', - 'Disable_Blog_Public', - 'Disable_Blog_Integrations', - ); - foreach ( $classes as $class ) { - $this->loader->autoLoader( $class ); - } - } - - /** - * Define the locale for this plugin for internationalization. - * - * Uses the Disable_Blog_I18n class in order to set the domain and to register the hook - * with WordPress. - * - * @since 0.4.0 - * @access private - */ - private function set_locale() { - - $plugin_i18n = new Disable_Blog_I18n(); - - $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' ); - } - - /** - * Register all of the hooks related to the admin area functionality - * of the plugin. - * - * @since 0.4.0 - * @since 0.5.3 Separated comment functions to run only if comments are supported. - * @access private - */ - private function define_admin_hooks() { - - $plugin_admin = new Disable_Blog_Admin( $this->get_plugin_name(), $this->get_version() ); - - // Add Links to Plugin Bar. - $this->loader->add_filter( 'plugin_row_meta', $plugin_admin, 'plugin_links', 10, 2 ); - - // Hide items with CSS. - $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' ); - - // Hide items with JavaScript where CSS doesn't do the job as well. - $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts', 100 ); - - // Hide Blog Related Admin pages. - $this->loader->add_action( 'admin_menu', $plugin_admin, 'remove_menu_pages' ); - - // Modify the main 'post' post_type arguments to shut pubic things down. - $this->loader->add_action( 'init', $plugin_admin, 'modify_post_type_arguments', 25 ); - - // Modify the core taxonomy arguments to filter out posts and shut down public things. - $this->loader->add_action( 'init', $plugin_admin, 'modify_taxonomies_arguments', 25 ); - - // Redirect Blog-related Admin Pages. - $this->loader->add_action( 'current_screen', $plugin_admin, 'redirect_admin_pages' ); - - // Filter post open status for pings. - $this->loader->add_action( 'pings_open', $plugin_admin, 'filter_comment_status', 20, 2 ); - - // Remove Admin Bar Links. - $this->loader->add_action( 'wp_before_admin_bar_render', $plugin_admin, 'remove_admin_bar_links' ); - - // Disable Update Services configuration, no pingbacks. - add_filter( 'enable_update_services_configuration', '__return_false' ); - - // Remove Dashboard Widgets. - $this->loader->add_action( 'admin_init', $plugin_admin, 'remove_dashboard_widgets' ); - - // Admin notices. - $this->loader->add_action( 'admin_notices', $plugin_admin, 'admin_notices' ); - - // Add a class to the admin body for the reading options page. - $this->loader->add_filter( 'admin_body_class', $plugin_admin, 'admin_body_class', 10, 1 ); - - // Remove Post via Email Settings. - add_filter( 'enable_post_by_email_configuration', '__return_false' ); - - // Disable Press This Function. - $this->loader->add_action( 'load-press-this.php', $plugin_admin, 'disable_press_this' ); - - // Remove Post Related Widgets. - $this->loader->add_action( 'widgets_init', $plugin_admin, 'remove_widgets' ); - - // Filter removal of widgets for some checks. - $this->loader->add_filter( 'dwpb_unregister_widgets', $plugin_admin, 'filter_widget_removal', 10, 2 ); - - // Custom Post State for the Blog Page redirect. - $this->loader->add_filter( 'display_post_states', $plugin_admin, 'page_post_states', 10, 2 ); - - // Remove REST API site health check related to posts. - $this->loader->add_filter( 'site_status_tests', $plugin_admin, 'site_status_tests', 10, 1 ); - - // Replace 'post' column with 'page' column. - $this->loader->add_action( 'manage_users_columns', $plugin_admin, 'manage_users_columns', 10, 1 ); - $this->loader->add_filter( 'manage_users_custom_column', $plugin_admin, 'manage_users_custom_column', 10, 3 ); - - // Remove the "view" link from the user options if author archives are not supported. - $this->loader->add_filter( 'user_row_actions', $plugin_admin, 'user_row_actions', 10, 2 ); - - // Filter post counts on post-related taxonomy edit screens for custom post types. - $this->loader->add_filter( 'post_tag_row_actions', $plugin_admin, 'filter_taxonomy_count', 10, 2 ); - $this->loader->add_filter( 'category_row_actions', $plugin_admin, 'filter_taxonomy_count', 10, 2 ); - - // Update customizer homepage settings panel to match the Reading settings. - $this->loader->add_action( 'customize_controls_print_styles', $plugin_admin, 'customizer_styles', 999 ); - $this->loader->add_action( 'customize_controls_enqueue_scripts', $plugin_admin, 'customizer_scripts', 999 ); - - // Update Blog page notice. - $this->loader->add_action( 'post_edit_form_tag', $plugin_admin, 'update_posts_page_notice', 10, 1 ); - - // Remove and update available permalink structure tags. - $this->loader->add_filter( 'available_permalink_structure_tags', $plugin_admin, 'available_permalink_structure_tags', 10, 1 ); - - // Only run comment related functions if comments are supported. - if ( dwpb_post_types_with_feature( 'comments' ) ) { - - // Filter comment counts in admin table. - $this->loader->add_filter( 'views_edit-comments', $plugin_admin, 'filter_admin_table_comment_count', 20, 1 ); - - // Filter post open status for comments. - $this->loader->add_action( 'comments_open', $plugin_admin, 'filter_comment_status', 20, 2 ); - - // Filter wp_count_comments, which addresses comments in admin bar. - $this->loader->add_filter( 'wp_count_comments', $plugin_admin, 'filter_wp_count_comments', 10, 2 ); - - // Filter Comments off Admin Page. - $this->loader->add_action( 'pre_get_comments', $plugin_admin, 'comment_filter', 10, 1 ); - - // Clear comments from 'post' post type. - $this->loader->add_filter( 'comments_array', $plugin_admin, 'filter_existing_comments', 20, 2 ); - - } - } - - /** - * Register all of the hooks related to the public-facing functionality - * of the plugin. - * - * @since 0.4.0 - * @access private - */ - private function define_public_hooks() { - - $plugin_public = new Disable_Blog_Public( $this->get_plugin_name(), $this->get_version() ); - - // Redirect Public pages (single posts, archives, etc). - $this->loader->add_action( 'template_redirect', $plugin_public, 'redirect_public_pages' ); - - // Modify Query. Setting to priority 9 to allow default filter priority to override. - $this->loader->add_action( 'pre_get_posts', $plugin_public, 'modify_query', 9 ); - - // Disable Feed. - $this->loader->add_action( 'do_feed', $plugin_public, 'disable_feed', 1, 2 ); - $this->loader->add_action( 'do_feed_rdf', $plugin_public, 'disable_feed', 1, 2 ); - $this->loader->add_action( 'do_feed_rss', $plugin_public, 'disable_feed', 1, 2 ); - $this->loader->add_action( 'do_feed_rss2', $plugin_public, 'disable_feed', 1, 2 ); - $this->loader->add_action( 'do_feed_atom', $plugin_public, 'disable_feed', 1, 2 ); - - // Remove feed links from the header. - $this->loader->add_action( 'wp_loaded', $plugin_public, 'header_feeds', 1, 1 ); - - // Remove the X-Pingback HTTP header. - $this->loader->add_filter( 'wp_headers', $plugin_public, 'filter_wp_headers', 10, 1 ); - - // Hide Feed links. - $this->loader->add_filter( 'feed_links_show_posts_feed', $plugin_public, 'feed_links_show_posts_feed', 10, 1 ); - $this->loader->add_filter( 'feed_links_show_comments_feed', $plugin_public, 'feed_links_show_comments_feed', 10, 1 ); - - // Disable XML-RPC methods related to posts and built-in taxonomies. - $this->loader->add_filter( 'xmlrpc_methods', $plugin_public, 'xmlrpc_methods', 10, 1 ); - - // Remove posts from xml sitemaps. - $this->loader->add_filter( 'wp_sitemaps_post_types', $plugin_public, 'wp_sitemaps_post_types', 10, 1 ); - - // Conditionally remove built-in taxonomies from sitemaps, if they are not being used by a custom post type. - $this->loader->add_filter( 'wp_sitemaps_taxonomies', $plugin_public, 'wp_sitemaps_taxonomies', 10, 1 ); - - // Conditionally remove author sitemaps, if author archives are not being supported. - $this->loader->add_filter( 'wp_sitemaps_add_provider', $plugin_public, 'wp_author_sitemaps', 100, 2 ); - } - - /** - * Integrate with other plugins. - * - * @since 0.5.3 - * @return void - */ - public function plugin_integrations() { - - $plugin_integrations = new Disable_Blog_Integrations(); - - // Disable Comments. - if ( $plugin_integrations->is_disable_comments_active() ) { - - // If Disabled Comments is active, return false for post types supporting comments, - // and functionality in Disable Blog related to comments will be turned off, - // the assumption being that the Disable Comments plugin is handling it. - add_filter( 'dwpb_post_types_supporting_comments', '__return_false' ); - } - - // WooCommerce. - if ( $plugin_integrations->is_woocommerce_active() && dwpb_post_types_with_feature( 'comments' ) ) { - - // Convert the $comments object back into an array if older version of WooCommerce is active. - $this->loader->add_filter( 'wp_count_comments', $plugin_integrations, 'filter_woocommerce_comment_count', 15, 2 ); - } - } - - /** - * Run the loader to execute all of the hooks with WordPress. - * - * @since 0.4.0 - * @access public - */ - public function run() { - $this->loader->run(); - } - - /** - * The name of the plugin used to uniquely identify it within the context of - * WordPress and to define internationalization functionality. - * - * @since 0.4.0 - * @access public - * @return string The name of the plugin. - */ - public function get_plugin_name() { - return $this->plugin_name; - } - - /** - * The reference to the class that orchestrates the hooks with the plugin. - * - * @since 0.4.0 - * @access public - * @return Disable_Blog_Loader Orchestrates the hooks of the plugin. - */ - public function get_loader() { - return $this->loader; - } - - /** - * Retrieve the version number of the plugin. - * - * @since 0.4.0 - * @access public - * @return string The version number of the plugin. - */ - public function get_version() { - return $this->version; - } -} + + */ + +/** + * The core plugin class. + * + * This is used to define internationalization, admin-specific hooks, and + * public-facing site hooks. + * + * Also maintains the unique identifier of this plugin as well as the current + * version of the plugin. + * + * @since 0.4.0 + */ +class Disable_Blog { + + /** + * The loader that's responsible for maintaining and registering all hooks that power + * the plugin. + * + * @since 0.4.0 + * @access protected + * @var Disable_Blog_Loader $loader Maintains and registers all hooks for the plugin. + */ + protected $loader; + + /** + * The unique identifier of this plugin. + * + * @since 0.4.0 + * @access protected + * @var string $plugin_name The string used to uniquely identify this plugin. + */ + protected $plugin_name; + + /** + * The current version of the plugin. + * + * @since 0.4.0 + * @access protected + * @var string $version The current version of the plugin. + */ + protected $version; + + /** + * Define the core functionality of the plugin. + * + * Set the plugin name and the plugin version that can be used throughout the plugin. + * Load the dependencies, define the locale, and set the hooks for the admin area and + * the public-facing side of the site. + * + * @since 0.4.0 + * @access public + * @param string $plugin_name The name of this plugin. + * @param string $version The version of this plugin. + */ + public function __construct( $plugin_name, $version ) { + + $this->plugin_name = $plugin_name; + $this->version = $version; + + do_action( 'dwpb_init' ); + + $this->upgrade_check(); + $this->load_dependencies(); + $this->set_locale(); + $this->plugin_integrations(); + $this->define_admin_hooks(); + $this->define_public_hooks(); + } + + /** + * Upgrade check. + * + * @since 0.4.0 + * @access private + */ + private static function upgrade_check() { + + // let's only run these checks on the admin page load. + if ( ! is_admin() ) { + return; + } + + // Get the current version option. + $current_version = get_option( 'dwpb_version', false ); + + // Update the previous version if we're upgrading. + if ( $current_version && DWPB_VERSION !== $current_version ) { + update_option( 'dwpb_previous_version', $current_version, false ); + } + + // See if it's a previous version, which may not have set the version option. + if ( false === $current_version || DWPB_VERSION !== $current_version ) { + // do things on update. + + // Save current version. + update_option( 'dwpb_version', DWPB_VERSION, false ); + } + } + + /** + * Load the required dependencies for this plugin. + * + * Include the following files that make up the plugin: + * + * - Disable_Blog_Loader. Orchestrates the hooks of the plugin. + * - Disable_Blog_I18n. Defines internationalization functionality. + * - Disable_Blog_Admin. Defines all hooks for the admin area. + * - Disable_Blog_Public. Defines all hooks for the public side of the site. + * + * Create an instance of the loader which will be used to register the hooks + * with WordPress. + * + * @since 0.4.0 + * @since 0.5.3 Added Integrations class. + * @access private + */ + private function load_dependencies() { + + // Includes directory. + $includes_dir = plugin_dir_path( __DIR__ ) . 'includes'; + + /** + * The class responsible for orchestrating the actions and filters of the + * core plugin. + */ + require_once $includes_dir . '/class-disable-blog-loader.php'; + + /** + * File with common functions. + */ + require_once $includes_dir . '/functions.php'; + + /** + * Make it so. + */ + $this->loader = new Disable_Blog_Loader(); + + $classes = array( + 'Disable_Blog_I18n', + 'Disable_Blog_Functions', + 'Disable_Blog_Admin', + 'Disable_Blog_Public', + 'Disable_Blog_Integrations', + ); + foreach ( $classes as $class ) { + $this->loader->autoLoader( $class ); + } + } + + /** + * Define the locale for this plugin for internationalization. + * + * Uses the Disable_Blog_I18n class in order to set the domain and to register the hook + * with WordPress. + * + * @since 0.4.0 + * @access private + */ + private function set_locale() { + + $plugin_i18n = new Disable_Blog_I18n(); + + $this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' ); + } + + /** + * Register all of the hooks related to the admin area functionality + * of the plugin. + * + * @since 0.4.0 + * @since 0.5.3 Separated comment functions to run only if comments are supported. + * @access private + */ + private function define_admin_hooks() { + + $plugin_admin = new Disable_Blog_Admin( $this->get_plugin_name(), $this->get_version() ); + + // Add Links to Plugin Bar. + $this->loader->add_filter( 'plugin_row_meta', $plugin_admin, 'plugin_links', 10, 2 ); + + // Hide items with CSS. + $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' ); + + // Hide items with JavaScript where CSS doesn't do the job as well. + $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts', 100 ); + + // Hide Blog Related Admin pages. + $this->loader->add_action( 'admin_menu', $plugin_admin, 'remove_menu_pages' ); + + // Modify the main 'post' post_type arguments to shut pubic things down. + $this->loader->add_action( 'init', $plugin_admin, 'modify_post_type_arguments', 25 ); + + // Modify the core taxonomy arguments to filter out posts and shut down public things. + $this->loader->add_action( 'init', $plugin_admin, 'modify_taxonomies_arguments', 25 ); + + // Redirect Blog-related Admin Pages. + $this->loader->add_action( 'current_screen', $plugin_admin, 'redirect_admin_pages' ); + + // Filter post open status for pings. + $this->loader->add_action( 'pings_open', $plugin_admin, 'filter_comment_status', 20, 2 ); + + // Remove Admin Bar Links. + $this->loader->add_action( 'wp_before_admin_bar_render', $plugin_admin, 'remove_admin_bar_links' ); + + // Disable Update Services configuration, no pingbacks. + add_filter( 'enable_update_services_configuration', '__return_false' ); + + // Remove Dashboard Widgets. + $this->loader->add_action( 'admin_init', $plugin_admin, 'remove_dashboard_widgets' ); + + // Admin notices. + $this->loader->add_action( 'admin_notices', $plugin_admin, 'admin_notices' ); + + // Add a class to the admin body for the reading options page. + $this->loader->add_filter( 'admin_body_class', $plugin_admin, 'admin_body_class', 10, 1 ); + + // Remove Post via Email Settings. + add_filter( 'enable_post_by_email_configuration', '__return_false' ); + + // Disable Press This Function. + $this->loader->add_action( 'load-press-this.php', $plugin_admin, 'disable_press_this' ); + + // Remove Post Related Widgets. + $this->loader->add_action( 'widgets_init', $plugin_admin, 'remove_widgets' ); + + // Filter removal of widgets for some checks. + $this->loader->add_filter( 'dwpb_unregister_widgets', $plugin_admin, 'filter_widget_removal', 10, 2 ); + + // Custom Post State for the Blog Page redirect. + $this->loader->add_filter( 'display_post_states', $plugin_admin, 'page_post_states', 10, 2 ); + + // Remove REST API site health check related to posts. + $this->loader->add_filter( 'site_status_tests', $plugin_admin, 'site_status_tests', 10, 1 ); + + // Replace 'post' column with 'page' column. + $this->loader->add_action( 'manage_users_columns', $plugin_admin, 'manage_users_columns', 10, 1 ); + $this->loader->add_filter( 'manage_users_custom_column', $plugin_admin, 'manage_users_custom_column', 10, 3 ); + + // Remove the "view" link from the user options if author archives are not supported. + $this->loader->add_filter( 'user_row_actions', $plugin_admin, 'user_row_actions', 10, 2 ); + + // Filter post counts on post-related taxonomy edit screens for custom post types. + $this->loader->add_filter( 'post_tag_row_actions', $plugin_admin, 'filter_taxonomy_count', 10, 2 ); + $this->loader->add_filter( 'category_row_actions', $plugin_admin, 'filter_taxonomy_count', 10, 2 ); + + // Update customizer homepage settings panel to match the Reading settings. + $this->loader->add_action( 'customize_controls_print_styles', $plugin_admin, 'customizer_styles', 999 ); + $this->loader->add_action( 'customize_controls_enqueue_scripts', $plugin_admin, 'customizer_scripts', 999 ); + + // Update Blog page notice. + $this->loader->add_action( 'post_edit_form_tag', $plugin_admin, 'update_posts_page_notice', 10, 1 ); + + // Remove and update available permalink structure tags. + $this->loader->add_filter( 'available_permalink_structure_tags', $plugin_admin, 'available_permalink_structure_tags', 10, 1 ); + + // Only run comment related functions if comments are supported. + if ( dwpb_post_types_with_feature( 'comments' ) ) { + + // Filter comment counts in admin table. + $this->loader->add_filter( 'views_edit-comments', $plugin_admin, 'filter_admin_table_comment_count', 20, 1 ); + + // Filter post open status for comments. + $this->loader->add_action( 'comments_open', $plugin_admin, 'filter_comment_status', 20, 2 ); + + // Filter wp_count_comments, which addresses comments in admin bar. + $this->loader->add_filter( 'wp_count_comments', $plugin_admin, 'filter_wp_count_comments', 10, 2 ); + + // Filter Comments off Admin Page. + $this->loader->add_action( 'pre_get_comments', $plugin_admin, 'comment_filter', 10, 1 ); + + // Clear comments from 'post' post type. + $this->loader->add_filter( 'comments_array', $plugin_admin, 'filter_existing_comments', 20, 2 ); + + } + } + + /** + * Register all of the hooks related to the public-facing functionality + * of the plugin. + * + * @since 0.4.0 + * @access private + */ + private function define_public_hooks() { + + $plugin_public = new Disable_Blog_Public( $this->get_plugin_name(), $this->get_version() ); + + // Redirect Public pages (single posts, archives, etc). + $this->loader->add_action( 'template_redirect', $plugin_public, 'redirect_public_pages' ); + + // Modify Query. Setting to priority 9 to allow default filter priority to override. + $this->loader->add_action( 'pre_get_posts', $plugin_public, 'modify_query', 9 ); + + // Disable Feed. + $this->loader->add_action( 'do_feed', $plugin_public, 'disable_feed', 1, 2 ); + $this->loader->add_action( 'do_feed_rdf', $plugin_public, 'disable_feed', 1, 2 ); + $this->loader->add_action( 'do_feed_rss', $plugin_public, 'disable_feed', 1, 2 ); + $this->loader->add_action( 'do_feed_rss2', $plugin_public, 'disable_feed', 1, 2 ); + $this->loader->add_action( 'do_feed_atom', $plugin_public, 'disable_feed', 1, 2 ); + + // Remove feed links from the header. + $this->loader->add_action( 'wp_loaded', $plugin_public, 'header_feeds', 1, 1 ); + + // Remove the X-Pingback HTTP header. + $this->loader->add_filter( 'wp_headers', $plugin_public, 'filter_wp_headers', 10, 1 ); + + // Hide Feed links. + $this->loader->add_filter( 'feed_links_show_posts_feed', $plugin_public, 'feed_links_show_posts_feed', 10, 1 ); + $this->loader->add_filter( 'feed_links_show_comments_feed', $plugin_public, 'feed_links_show_comments_feed', 10, 1 ); + + // Disable XML-RPC methods related to posts and built-in taxonomies. + $this->loader->add_filter( 'xmlrpc_methods', $plugin_public, 'xmlrpc_methods', 10, 1 ); + + // Remove posts from xml sitemaps. + $this->loader->add_filter( 'wp_sitemaps_post_types', $plugin_public, 'wp_sitemaps_post_types', 10, 1 ); + + // Conditionally remove built-in taxonomies from sitemaps, if they are not being used by a custom post type. + $this->loader->add_filter( 'wp_sitemaps_taxonomies', $plugin_public, 'wp_sitemaps_taxonomies', 10, 1 ); + + // Conditionally remove author sitemaps, if author archives are not being supported. + $this->loader->add_filter( 'wp_sitemaps_add_provider', $plugin_public, 'wp_author_sitemaps', 100, 2 ); + } + + /** + * Integrate with other plugins. + * + * @since 0.5.3 + * @return void + */ + public function plugin_integrations() { + + $plugin_integrations = new Disable_Blog_Integrations(); + + // Disable Comments. + if ( $plugin_integrations->is_disable_comments_active() ) { + + // If Disabled Comments is active, return false for post types supporting comments, + // and functionality in Disable Blog related to comments will be turned off, + // the assumption being that the Disable Comments plugin is handling it. + add_filter( 'dwpb_post_types_supporting_comments', '__return_false' ); + } + + // WooCommerce. + if ( $plugin_integrations->is_woocommerce_active() && dwpb_post_types_with_feature( 'comments' ) ) { + + // Convert the $comments object back into an array if older version of WooCommerce is active. + $this->loader->add_filter( 'wp_count_comments', $plugin_integrations, 'filter_woocommerce_comment_count', 15, 2 ); + } + } + + /** + * Run the loader to execute all of the hooks with WordPress. + * + * @since 0.4.0 + * @access public + */ + public function run() { + $this->loader->run(); + } + + /** + * The name of the plugin used to uniquely identify it within the context of + * WordPress and to define internationalization functionality. + * + * @since 0.4.0 + * @access public + * @return string The name of the plugin. + */ + public function get_plugin_name() { + return $this->plugin_name; + } + + /** + * The reference to the class that orchestrates the hooks with the plugin. + * + * @since 0.4.0 + * @access public + * @return Disable_Blog_Loader Orchestrates the hooks of the plugin. + */ + public function get_loader() { + return $this->loader; + } + + /** + * Retrieve the version number of the plugin. + * + * @since 0.4.0 + * @access public + * @return string The version number of the plugin. + */ + public function get_version() { + return $this->version; + } +} diff --git a/includes/functions.php b/includes/functions.php index ceb9c61..0d38551 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -1,152 +1,152 @@ - - */ - -/** - * Get all the post types that support a featured (like 'comments') - * - * @link http://codex.wordpress.org/Function_Reference/register_post_type#Arguments - * - * @since 0.1.0 - * @since 0.4.0 pulled out of class, unique function. - * @since 0.5.0 added $args parameter for passing specific arguments to get_post_types. - * @since 0.5.3 added caching. - * @see register_post_types(), get_post_types(), get_object_taxonomies() - * @param string $feature the feature in question. - * @param array $args the arguments passed to get_post_types. - * @return array|bool A list of post type names that support the featured or false if nothing found. - */ -function dwpb_post_types_with_feature( $feature, $args = array() ) { - - // Bail if no feature is passed. - if ( ! $feature ) { - return false; - } - - // Check the cache. - $cache_name = 'post-types-supporting-' . esc_attr( $feature ); - $post_types_with_feature = wp_cache_get( $cache_name, 'post-types-by-feature' ); - - // If the cache is empty, then get the post types. - if ( false === $post_types_with_feature || ! is_array( $post_types_with_feature ) ) { - - $post_types = get_post_types( $args, 'names' ); - - $post_types_with_feature = array(); - foreach ( $post_types as $post_type ) { - if ( post_type_supports( $post_type, $feature ) && 'post' !== $post_type ) { - $post_types_with_feature[] = $post_type; - } - } - - // Keep the array if there are any, otherwise make it return false. - $post_types_with_feature = empty( $post_types_with_feature ) ? false : $post_types_with_feature; - - wp_cache_set( $cache_name, $post_types_with_feature, 'post-types-by-feature' ); - } - - /** - * Filter the returned "post types with feature". - * - * This function is used to determine if there are any post types with a specific - * feature, not including the `post` type. Often this toggles on/off options in - * the plugin. For instance, if comments are only support by posts, then they will - * be disabled and the options-discussion.php admin page redirected. - * - * @since 0.4.0 - * @since 0.5.0 Added the $args parameter. - * @param array|bool $post_types_with_feature an array of post types support this feature or false if none. - * @param array $args the arguments passed to get_post_types. - * @return array|bool A list of post type names that support the featured or false if nothing found. - */ - return apply_filters( "dwpb_post_types_supporting_{$feature}", $post_types_with_feature, $args ); -} - -/** - * Get post types that have a specific taxonomy - * (a combination of get_post_types and get_object_taxonomies) - * - * Basically, we need to know if there are post types, other than 'post' - * that support the taxonomy. - * - * @since 0.2.0 - * @since 0.4.0 pulled out of class, unique function. - * @see register_post_types(), get_post_types(), get_object_taxonomies() - * @uses get_post_types(), get_object_taxonomies(), apply_filters() - * @param string|object $taxonomy Required. The taxonomy object or taxonomy slug. - * @param array|string $args Optional. An array of key => value arguments to match against the post type objects. Default empty array. - * @param string $output Optional. The type of output to return. Accepts post type 'names' or 'objects'. Default 'names'. - * @return array|bool A list of post type names or objects that have the taxonomy or false if nothing found. - */ -function dwpb_post_types_with_tax( $taxonomy, $args = array(), $output = 'names' ) { - - if ( ! $taxonomy || ! is_object( $taxonomy ) ) { - return false; - } - - // We just need the taxonomy name. - if ( is_object( $taxonomy ) ) { // @phpstan-ignore function.alreadyNarrowedType - $taxonomy = $taxonomy->name; - } - - // Get all the post types. - $post_types = get_post_types( $args, $output ); - - // setup the finished product. - $post_types_with_tax = array(); - foreach ( $post_types as $post_type ) { - - // If post types are objects. - if ( is_object( $post_type ) ) { - $type = $post_type->name; - // If post types are strings. - } else { - $type = (string) $post_type; - } - - // is the post included in this post type, but not 'post' type. - if ( ! empty( $type ) && 'post' !== $type ) { - $taxonomies = get_object_taxonomies( $type, 'names' ); - if ( in_array( $taxonomy, $taxonomies, true ) ) { - $post_types_with_tax[] = $post_type; - } - } - } - - /** - * Filter the returned value of "post types with tax". - * - * This function is used to determine if there are any post types using a taxonomy, - * not including the `post` type. This is used to determine if there are custom - * post types using the built-in `post_tag` and `category` taxonomies and toggle - * off related features if they are not being used by anything other than built-in posts. - * - * @since 0.4.0 - * @param mixed $null Null for no override, otherwise pass an array of post type slugs. - * @param string|object $taxonomy The current taxonomy slug. - * @param array|bool $post_types_with_tax An array of post types use this taxonomy or false if none. - * @param array $args An array of key => value arguments to match against the post type objects. Default empty array. - * @param string $output The type of output to return, either 'names' or 'objects'. - * @return mixed A list of post type names that use this taxonomy or false if nothing found. - */ - $override = apply_filters( 'dwpb_taxonomy_support', null, $taxonomy, $post_types, $args, $output ); - if ( ! is_null( $override ) ) { - return $override; - } - - // If there aren't any results, return false. - if ( empty( $post_types_with_tax ) ) { - return false; - } else { - return $post_types_with_tax; - } -} + + */ + +/** + * Get all the post types that support a featured (like 'comments') + * + * @link http://codex.wordpress.org/Function_Reference/register_post_type#Arguments + * + * @since 0.1.0 + * @since 0.4.0 pulled out of class, unique function. + * @since 0.5.0 added $args parameter for passing specific arguments to get_post_types. + * @since 0.5.3 added caching. + * @see register_post_types(), get_post_types(), get_object_taxonomies() + * @param string $feature the feature in question. + * @param array $args the arguments passed to get_post_types. + * @return array|bool A list of post type names that support the featured or false if nothing found. + */ +function dwpb_post_types_with_feature( $feature, $args = array() ) { + + // Bail if no feature is passed. + if ( ! $feature ) { + return false; + } + + // Check the cache. + $cache_name = 'post-types-supporting-' . esc_attr( $feature ); + $post_types_with_feature = wp_cache_get( $cache_name, 'post-types-by-feature' ); + + // If the cache is empty, then get the post types. + if ( false === $post_types_with_feature || ! is_array( $post_types_with_feature ) ) { + + $post_types = get_post_types( $args, 'names' ); + + $post_types_with_feature = array(); + foreach ( $post_types as $post_type ) { + if ( post_type_supports( $post_type, $feature ) && 'post' !== $post_type ) { + $post_types_with_feature[] = $post_type; + } + } + + // Keep the array if there are any, otherwise make it return false. + $post_types_with_feature = empty( $post_types_with_feature ) ? false : $post_types_with_feature; + + wp_cache_set( $cache_name, $post_types_with_feature, 'post-types-by-feature' ); + } + + /** + * Filter the returned "post types with feature". + * + * This function is used to determine if there are any post types with a specific + * feature, not including the `post` type. Often this toggles on/off options in + * the plugin. For instance, if comments are only support by posts, then they will + * be disabled and the options-discussion.php admin page redirected. + * + * @since 0.4.0 + * @since 0.5.0 Added the $args parameter. + * @param array|bool $post_types_with_feature an array of post types support this feature or false if none. + * @param array $args the arguments passed to get_post_types. + * @return array|bool A list of post type names that support the featured or false if nothing found. + */ + return apply_filters( "dwpb_post_types_supporting_{$feature}", $post_types_with_feature, $args ); +} + +/** + * Get post types that have a specific taxonomy + * (a combination of get_post_types and get_object_taxonomies) + * + * Basically, we need to know if there are post types, other than 'post' + * that support the taxonomy. + * + * @since 0.2.0 + * @since 0.4.0 pulled out of class, unique function. + * @see register_post_types(), get_post_types(), get_object_taxonomies() + * @uses get_post_types(), get_object_taxonomies(), apply_filters() + * @param string|object $taxonomy Required. The taxonomy object or taxonomy slug. + * @param array|string $args Optional. An array of key => value arguments to match against the post type objects. Default empty array. + * @param string $output Optional. The type of output to return. Accepts post type 'names' or 'objects'. Default 'names'. + * @return array|bool A list of post type names or objects that have the taxonomy or false if nothing found. + */ +function dwpb_post_types_with_tax( $taxonomy, $args = array(), $output = 'names' ) { + + if ( ! $taxonomy || ! is_object( $taxonomy ) ) { + return false; + } + + // We just need the taxonomy name. + if ( is_object( $taxonomy ) ) { // @phpstan-ignore function.alreadyNarrowedType + $taxonomy = $taxonomy->name; + } + + // Get all the post types. + $post_types = get_post_types( $args, $output ); + + // setup the finished product. + $post_types_with_tax = array(); + foreach ( $post_types as $post_type ) { + + // If post types are objects. + if ( is_object( $post_type ) ) { + $type = $post_type->name; + // If post types are strings. + } else { + $type = (string) $post_type; + } + + // is the post included in this post type, but not 'post' type. + if ( ! empty( $type ) && 'post' !== $type ) { + $taxonomies = get_object_taxonomies( $type, 'names' ); + if ( in_array( $taxonomy, $taxonomies, true ) ) { + $post_types_with_tax[] = $post_type; + } + } + } + + /** + * Filter the returned value of "post types with tax". + * + * This function is used to determine if there are any post types using a taxonomy, + * not including the `post` type. This is used to determine if there are custom + * post types using the built-in `post_tag` and `category` taxonomies and toggle + * off related features if they are not being used by anything other than built-in posts. + * + * @since 0.4.0 + * @param mixed $null Null for no override, otherwise pass an array of post type slugs. + * @param string|object $taxonomy The current taxonomy slug. + * @param array|bool $post_types_with_tax An array of post types use this taxonomy or false if none. + * @param array $args An array of key => value arguments to match against the post type objects. Default empty array. + * @param string $output The type of output to return, either 'names' or 'objects'. + * @return mixed A list of post type names that use this taxonomy or false if nothing found. + */ + $override = apply_filters( 'dwpb_taxonomy_support', null, $taxonomy, $post_types, $args, $output ); + if ( ! is_null( $override ) ) { + return $override; + } + + // If there aren't any results, return false. + if ( empty( $post_types_with_tax ) ) { + return false; + } else { + return $post_types_with_tax; + } +} From 8c9bd3619be96830b12b70a4dd6349cfa7230550 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 6 Jan 2025 23:17:49 -0800 Subject: [PATCH 20/20] simplify the approach for allowed query vars on redirects --- includes/class-disable-blog-functions.php | 60 ++++++++++------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/includes/class-disable-blog-functions.php b/includes/class-disable-blog-functions.php index 747b774..36a2f32 100644 --- a/includes/class-disable-blog-functions.php +++ b/includes/class-disable-blog-functions.php @@ -76,6 +76,31 @@ private function parse_query_string( $url ) { $query_vars = array(); wp_parse_str( $_SERVER['QUERY_STRING'], $query_vars ); // phpcs:ignore + // Only allow specific query vars. + $allowed_query_vars = $this->get_allowed_query_vars(); + if ( ! empty( $allowed_query_vars ) ) { + $query_vars = array_intersect_key( $query_vars, array_flip( $allowed_query_vars ) ); + + // Escaping and sanitization are important. + $query_vars = array_filter( array_map( 'esc_html', $query_vars ) ); + + // if we have any query variables, add it to the url. + if ( ! empty( $query_vars ) ) { + $url = add_query_arg( $query_vars, $url ); + } + } + + return $url; + } + + /** + * Get the allowed query vars. + * + * @since 0.5.5 + * @return array + */ + private function get_allowed_query_vars() { + /** * Filter for allowed queary string variables. * @@ -84,40 +109,9 @@ private function parse_query_string( $url ) { * @return array */ $allowed_query_vars = (array) apply_filters( 'dwpb_allowed_query_vars', array() ); - if ( ! empty( $allowed_query_vars ) ) { - $allowed_query_vars = array_filter( - $allowed_query_vars, - function ( $value ) { - $esc_value = esc_html( $value ); - return ! empty( $esc_value ); - } - ); - $query_vars = array_intersect_key( $query_vars, array_flip( $allowed_query_vars ) ); - } - // Escaping and sanitization are important. - $query_vars = array_filter( - $query_vars, - function ( $value ) { - $esc_value = esc_html( $value ); - return ! empty( $esc_value ); - } - ); - $query_vars = array_filter( - $query_vars, - function ( $value ) { - $esc_value = esc_html( $value ); - return ! empty( $esc_value ); - }, - ARRAY_FILTER_USE_KEY - ); - - // if we have any query variables, add it to the url. - if ( ! empty( $query_vars ) ) { - $url = add_query_arg( $query_vars, $url ); - } - - return $url; + // Escaping and sanitization are important, insure we have only valid query vars. + return array_filter( array_map( 'esc_html', $allowed_query_vars ) ); } /**