public/class-subtitles.php
<?php/** * @package Subtitles */ /** * Do not load this file directly * * @since 1.0.0 */if ( ! defined( 'ABSPATH' ) ) { die( '-1' );} /** * Checks for the existence of Subtitles before defining it. * * @link http://www.php.net//manual/en/function.class-exists.php * * @since 1.0.0 */if ( ! class_exists( 'Subtitles' ) ) { /** * Define primary Subtitles class. * * Within classes constants and variables are called properties. * Within classes functions are called methods. * * @link http://php.net/manual/en/language.oop5.php * * @since 1.0.0 */ class Subtitles { /** * Constant used for plugin versioning and enqueues. * * Constants differ from normal variables in that you don't use the $ symbol * to declare or use them. The value must be a constant expression, not (for example) * a variable, a property, a result of a mathematical operation, or a function call. * * Semantic versioning is used in this plugin. * * @link http://semver.org/ * * @since 1.0.0 */ const VERSION = '4.0.0'; /** * Constant used when referencing the plugin in load text domain calls and other * functionality that relies on the slug of the plugin being present. * * @since 1.0.0 */ const PLUGIN_SLUG = 'subtitles'; /** * Constant used in subtitle support checks for custom post types. * * @link http://codex.wordpress.org/Post_Types * * @since 1.0.0 */ const SUBTITLE_FEATURE_SUPPORT = 'subtitles'; /** * Constant used in nonce checks when saving custom subtitles data. * * @link http://codex.wordpress.org/WordPress_Nonces * * @since 1.0.0 */ const SUBTITLE_NONCE_NAME = '_subtitle_data_nonce'; /** * Constant used for subtitle meta key. * * Note the underscore before the custom meta for subtitles. This ensures that * the meta key doesn't appear in the custom fields section on the new post * and page screens and is only used within our classes. This is considered * protected meta. * * @link http://codex.wordpress.org/Custom_Fields * * @since 1.0.0 */ const SUBTITLE_META_KEY = '_subtitle'; /** * If no instance of Subtitles has been made then let's make it. * * Declaring class properties or methods as static makes them accessible * without needing an instantiation of the class Subtitles. * * For example, this is how you would access a static property: * Subtitles::$instance * * And this is how you would access a static method: * Subtitles::getinstance() * * @link http://www.php.net/manual/en/language.oop5.late-static-bindings.php * @var object $instance * @access private * @static * * @since 1.0.0 */ private static $instance = null; /** * Kick off the class if it hasn't been instantiated. * * There is a lot of information about the Singleton design pattern * and singleton implementations for WordPress and plugins. I am not (at all!) * an expert on this and may very well be instantiating Subtitles in the * wrong way. If there's a better way to do this, or if it's not necessary * to use this design pattern with the plugin, please let me know. The reason * I've done it this way is because plugins should never be instantiated twice * in WordPress. In general, I assume that this won't happen under normal circumstances, * but using this design pattern ensures that if someone tries to instantiate * Subtitles twice, then it won't be possible to do so. * * For more reading, see the following links. * * @link http://en.wikipedia.org/wiki/Singleton_pattern * @link http://hardcorewp.com/2013/using-singleton-classes-for-wordpress-plugins/ * @link http://eamann.com/tech/the-case-for-singletons/ * @link http://www.toppa.com/2013/the-case-against-singletons-in-wordpress/ * @link http://eamann.com/tech/making-singletons-safe-in-php/ * * @staticvar Singleton $instance The Singleton instance of this class. * @return Singleton The Singleton instance of this class. * @access public * @static * * @since 1.0.0 */ public static function getinstance() { if ( ! self::$instance ) { self::$instance = new Subtitles; } return self::$instance; } // end method getinstance /** * Declare constructor methods for the class Subtitles. * * Classes which have a constructor method call this method on each newly-created object, * so it is suitable for any initialization that the object may need before it is used. * * Access is set to protected to prevent outside access to the method for anything other than * the class or a child class. Otherwise `new` could be used to kick off this constructor if * it were public. * * @access protected * * @since 1.0.0 */ protected function __construct() { /** * Add default support for subtitles on posts and pages. * * To find out the number and name of arguments for any action in WordPress, * search Core for the matching do_action call. For example, searching for * "do_action( 'save_post'" reveals that two arguments, $post_id and $post are used. * * add_action accepts: * * - $tag The name of the action to which the $function_to_add is hooked. * - callback function The name of the function you wish to be called. * - int $priority optional. Used to specify the order in which the functions * associated with a particular action are executed (default: 10). * Lower numbers correspond with earlier execution, and functions with the * same priority are executed in the order in which they were added to the action. * - int $accepted_args optional. The number of arguments the function accept (default: 1). * * Support for post types needs to be fired on `init`. * * @see add_action() * @link http://codex.wordpress.org/Function_Reference/add_action * * @since 1.0.0 */ add_action( 'init', array( &$this, 'add_subtitles_support' ) ); /** * Make Subtitles available for translation. * * Translations can be added into the /languages/ directory. * @link http://codex.wordpress.org/Translating_WordPress * * @since 1.0.0 */ add_action( 'init', array( &$this, 'load_subtitles_textdomain' ) ); /** * Output front-end styles for Subtitles via wp_head * * @see add_action() * @since 2.0.0 */ add_action( 'wp_head', array( &$this, 'subtitle_styling' ) ); /** * Filter post titles to display subtitles properly. * * add_filter accepts: * * - $tag The name of the filter to hook the $function_to_add callback to. * - $function_to_add The callback to be run when the filter is applied. * - $priority (optional) The order in which the functions associated with a * particular action are executed. Lower numbers correspond with * earlier execution, and functions with the same priority are * executed in the order in which they were added to the action. * Default: 10. * $accepted_args (optional) The number of arguments the function accepts. * Default: 1. * * @see add_filter() * @link http://codex.wordpress.org/Function_Reference/add_filter * * @since 1.0.0 */ if ( ! is_admin() ) { // Don't touch anything inside of the WordPress Dashboard, yet. add_filter( 'the_title', array( &$this, 'the_subtitle' ), 10, 2 ); /** * Let's also filter the dedicated function for single post titles * * @link https://github.com/wecobble/Subtitles/issues/2 * * @since 1.0.1 */ add_filter( 'single_post_title', array( &$this, 'the_subtitle' ), 10, 2 ); /** * Make sure that Subtitles plays nice with WordPress SEO plugin by Yoast * * @link https://wordpress.org/plugins/wordpress-seo/ * @link https://github.com/wecobble/Subtitles/issues/5 * * @since 1.0.1 * @since 4.0.0 - Deprecate the wp_seo_get_bc_title filter and use * wpseo_breadcrumb_single_link_info instead. * See https://yoast.com/yoast-seo-5-8/ */ add_filter( 'wpseo_breadcrumb_single_link_info', array( &$this, 'plugin_compat_wordpress_seo' ) ); } } // end method __construct /** * Make sure that Subtitles plays nice with WordPress SEO plugin by Yoast. * * The plugin features breadcrumb functionality, which users can add into their themes * by using functionality that's specific to the plugin. Because it's so popular and * I'm not sure where or how people will insert their breadcrumbs into their template * files, it's best avoid messing with the plugin altogether. We'll filter the output * and make sure that subtitles isn't included in any of the breadcrumb titles. * * @link https://wordpress.org/plugins/wordpress-seo/ * @link https://github.com/wecobble/Subtitles/issues/5 * @link http://us1.php.net//manual/en/function.strlen.php * @see get_the_subtitle() * * @since 1.0.1 */Function `plugin_compat_wordpress_seo` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring. public function plugin_compat_wordpress_seo( $breadcrumb_text ) { /** * This issue only arrises when breadcrumbs are placed inside of The Loop, * so we'll first check to see if we're in The Loop, and if not, just bail * on this altogether. * * @link http://codex.wordpress.org/Function_Reference/in_the_loop * * @since 1.0.1 */ $in_the_loop = (bool) in_the_loop(); if ( ! $in_the_loop ) { return $breadcrumb_text; } /** * Grab the post subtitle. * * Example: (string) "Subtitle" * * @see get_the_subtitle() * * @since 1.0.1 */ $post_subtitle = get_the_subtitle(); /** * Grab the length of the post subtitle. * * We're also using a negative value of the legnth of the subtitle. * * Example: (int) 8 * * @link http://us1.php.net//manual/en/function.strlen.php * * @since 1.0.1 */ $post_subtitle_length = (int) strlen( $post_subtitle ); $post_subtitle_length_neg = -1 * $post_subtitle_length; /** * Grab the already filtered post title. * * Example: (string) "Post TitleSubtitle" */ $post_title = (string) $breadcrumb_text['text']; /** * Grab the length of the filtered post title. * * Example: (int) 18 */ $post_title_length = (int) strlen( $post_title ); /** * Check for a few specific cases: * * 1. Does the subtitle of the current post equal the title of any of its ancestors? * 2. Is the post title that's being checked empty? * * If so, then bail out on this. * * Example: If the subtitle of the page is "Features" and one of the parent breadcrumbs also * has the name "Features", then we don't want to mess that up, so we'll make sure that we bail out * early on this function. * * @since 1.0.1 */ if ( ( $post_title == $post_subtitle ) || ( '' == $post_title ) ) { return $breadcrumb_text; } /** * Remove the subtitle from the "title" string so that it shows up properly. * * Example: If the post title that we've brought into the function is called "Post TitleSubtitle", * then all we really want is the the title to say "Post Title", so we'll use the length of the subtitle * to cut that many characters off of the end of the post title, and hope to end up with "Post Title". * * @see apply_filters() * @link http://us2.php.net//manual/en/function.substr.php * * @since 1.0.1 */ $post_title = substr( $post_title, 0, $post_subtitle_length_neg ); $post_title = apply_filters( 'compat_wordpress_seo', $post_title ); /** * Make sure that the new title and its subtitle is the right string that * we want to manipulate, by comparing the post title + subtitle against * the original title that we brought into this function. * * Example: The original title brought in was "Post TitleSubtitle", so we'll * make sure that "Post Title" + "Subtitle" is actually "Post TitleSubtitle". * * @since 1.0.1 */ $reconstructed_title = $post_title . $post_subtitle; if ( $reconstructed_title == $breadcrumb_text['text'] ) { $breadcrumb_text['text'] = $post_title; return $breadcrumb_text; } // else just return the title that was brought into the function return $breadcrumb_text; } // end plugin_compat_wordpress_seo() /** * Add default support for subtitles on posts and pages. * * @since 1.0.0 */ public function add_subtitles_support() { /** * Automatically enable subtitles support on posts. * * This can be overridden within themes. * * @see add_post_type_support() * @link http://codex.wordpress.org/Function_Reference/add_post_type_support * @see SUBTITLE_FEATURE_SUPPORT * * @since 1.0.0 */ add_post_type_support( 'post', self::SUBTITLE_FEATURE_SUPPORT ); /** * Automatically enable subtitles support on pages. * * This can be overridden within themes. * * @see add_post_type_support() * @link http://codex.wordpress.org/Function_Reference/add_post_type_support * @see SUBTITLE_FEATURE_SUPPORT * * @since 1.0.0 */ add_post_type_support( 'page', self::SUBTITLE_FEATURE_SUPPORT ); /** * Automatically enable subtitles support for Jetpack Portfolios * * This can be overridden within themes. * * @see add_post_type_support() * @link http://codex.wordpress.org/Function_Reference/add_post_type_support * @link https://jetpack.com/support/custom-content-types/ * @see SUBTITLE_FEATURE_SUPPORT * * @since 1.0.7 */ add_post_type_support( 'jetpack-portfolio', self::SUBTITLE_FEATURE_SUPPORT ); /** * Automatically enable subtitles support for Jetpack Testimonials * * This can be overridden within themes. * * @see add_post_type_support() * @link http://codex.wordpress.org/Function_Reference/add_post_type_support * @link https://jetpack.com/support/custom-content-types/ * @see SUBTITLE_FEATURE_SUPPORT * * @since 2.2.0 */ add_post_type_support( 'jetpack-testimonial', self::SUBTITLE_FEATURE_SUPPORT ); } // end add_subtitles_support() /** * Declare __clone as private to prevent cloning an instance of the class via `clone`. * * @link http://www.php.net/manual/en/language.oop5.cloning.php * @access protected * * @since 1.0.0 */ protected function __clone() { } // end method __clone() /** * Prevent unserializing. * * @link http://php.net/function.unserialize * @access public * * @since 1.0.0 */ public function __wakeup() { // i18n won't work on this exception so there's no need to add it into the language pack throw new Exception( 'This Singleton cannot be unserialized.' ); } // end method __wakeup() /** * Make Subtitles available for translation. * * @link https://ulrich.pogson.ch/load-theme-plugin-translations * @link http://ottopress.com/2013/language-packs-101-prepwork/ * * @since 1.0.0 */ public function load_subtitles_textdomain() { $domain = self::PLUGIN_SLUG; $locale = apply_filters( 'plugin_locale', get_locale(), $domain ); /** * Load a .mo file into the text domain $domain. * * If the text domain already exists, the translations will be merged. If both * sets have the same string, the translation from the original value will be taken. * * On success, the .mo file will be placed in the $l10n global by $domain * and will be a MO object. * * @param string $domain Text domain. Unique identifier for retrieving translated strings. * @param string $mofile Path to the .mo file. * @return bool True on success, false on failure. * * @since 1.0.0 */ load_textdomain( $domain, trailingslashit( WP_LANG_DIR ) . $domain . '/' . $domain . '-' . $locale . '.mo' ); /** * Load a plugin's translated strings. * * If the path is not given then it will be the root of the plugin directory. * * The .mo file should be named based on the text domain with a dash, and then the locale exactly. * * @param string $domain Unique identifier for retrieving translated strings * @param string $deprecated Use the $plugin_rel_path parameter instead. * @param string $plugin_rel_path Optional. Relative path to WP_PLUGIN_DIR where the .mo file resides. * * @since 1.0.0 */ load_plugin_textdomain( $domain, false, basename( plugin_dir_path( dirname( __FILE__ ) ) ) . '/languages/' ); } /** * Output front-end styles for Subtitles via wp_head * * @access public * @since 2.0.0 */ public function subtitle_styling() { ?> <style type="text/css" media="screen"> /** * Plugin Name: Subtitles * Plugin URI: http://wordpress.org/plugins/subtitles/ * Description: Easily add subtitles into your WordPress posts, pages, custom post types, and themes. * Author: <a href="https://philip.blog/">Philip Arthur Moore</a>, <a href="https://wecobble.com">We Cobble</a> * Author URI: https://wecobble.com/ * Version: 4.0.0 * License: GNU General Public License v2 or later * License URI: http://www.gnu.org/licenses/gpl-2.0.html */ /** * Be explicit about this styling only applying to spans, * since that's the default markup that's returned by * Subtitles. If a developer overrides the default subtitles * markup with another element or class, we don't want to stomp * on that. * * @since 1.0.0 */ span.entry-subtitle { display: block; /* Put subtitles on their own line by default. */ font-size: 0.53333333333333em; /* Sensible scaling. It's assumed that post titles will be wrapped in heading tags. */ } /** * If subtitles are shown in comment areas, we'll hide them by default. * * @since 1.0.5 */ #comments .comments-title span.entry-subtitle { display: none; } </style><?php } // end function subtitle_styling /** * Output the subtitle * * @since 1.0.0 */Function `the_subtitle` has a Cognitive Complexity of 19 (exceeds 5 allowed). Consider refactoring.
Method `the_subtitle` has 46 lines of code (exceeds 25 allowed). Consider refactoring. public function the_subtitle( $title, $id = null ) { /** * Which globals will we need? * * @since 1.0.0 */ global $post; /** * Check if $post is set. There's a chance that this can * be NULL on search results pages with zero results. * * @link https://github.com/wecobble/Subtitles/issues/12 * * @since 1.0.2 */ if ( ! isset( $post ) ) { return $title; } /** * Make sure we're not touching any of the titles in the Dashboard * This filtering should only happen on the front end of the site. * * @since 1.0.0 */ if ( is_admin() ) { return $title; } /** * Do not show subtitles in RSS feeds, but give devs. the option * to turn this off. * * @since 2.0.1 */ $is_feed = apply_filters( 'subtitles_is_feed', is_feed() ); if ( $is_feed ) { return $title; } /** * Bail early if no subtitle has been set for the post. * * @since 1.0.0 */ $post_id = (int) absint( $post->ID ); // post ID should always be a non-negative integer $subtitle = (string) html_entity_decode( wp_unslash( esc_html( get_post_meta( $post_id, self::SUBTITLE_META_KEY, true ) ) ), ENT_QUOTES ); /** * Allow theme and plugin authors to override the early return if no subtitle exists. * * @see https://github.com/wecobble/Subtitles/issues/79 * @since 2.2.0 */ $subtitle_exists = ! empty( $subtitle ); $subtitle_exists = apply_filters( 'subtitle_exists', $subtitle_exists ); if ( ! $subtitle_exists ) { return $title; } /** * Bail if the title being filtered isn't the actual primary post title. * * This can happen when something is used within the loop that outputs titles, * like navigation between single posts on a blog. * * $id should not be an object, and if it is then the single_post_title() filter is likely * being invoked, so we'll need a check against that. * * Here's a more detailed explanation: * * 1. the_title() can be filtered with apply_filters( 'the_title', $title, $id ). * This means that the second argument needs to be an integer, which will be the ID of a post. * 2. single_post_title() can be filtered with apply_filters( 'single_post_title', $_post->post_title, $_post ). * This means that the second argument needs to be the entire post object. * * So if we're checking $id and it's an object, then there's a good chance that single_post_title() * is being used and we need to reassign $id to be ID of the object WP_Post. * * @see the_title() * @see single_post_title() * @see get_the_ID() * @see is_object() * * @since 1.0.0 */ if ( isset( $id ) && is_object( $id ) ) { // single_post_title() is being used. $id = $id->ID; // grab the ID from the post object. } if ( isset( $post->post_title ) && get_the_ID() != $id ) {Avoid too many `return` statements within this method. return $title; } /** * Make sure we're in The Loop. If a theme maker wants to create * a custom loop via WP_Query, then he can filter subtitle_view_supported. * * @see in_the_loop() * @link http://codex.wordpress.org/Function_Reference/in_the_loop * * @since 1.0.0 */ $subtitle_view_supported = true; $in_the_loop = (bool) in_the_loop(); if ( ! $in_the_loop ) { $subtitle_view_supported = false; } /** * Allow subtitle views to be filtered by theme developers. * * @see apply_filters() * @link http://codex.wordpress.org/Function_Reference/apply_filters * * @since 1.0.0 */ $subtitle_view_supported = (bool) apply_filters( 'subtitle_view_supported', $subtitle_view_supported ); /** * If no subtitle support is active for the given view * then simply return the post title. * * @since 1.0.0 */ if ( ! $subtitle_view_supported ) {Avoid too many `return` statements within this method. return $title; } /** * Bail if the current post type doesn't support Subtitles. * * The good news here is that if Subtitles have been entered in for a user, * but support for a specific post type has been removed, then the subtitles * won't be displayed on the front-end of the site but will still be retained * in the database. This is useful for child themes and such who want to override * the look and feel of a theme that features subtitles. * * @since 1.0.0 */ if ( ! post_type_supports( $post->post_type, self::SUBTITLE_FEATURE_SUPPORT ) ) {Avoid too many `return` statements within this method. return $title; } /** * Let theme authors modify the subtitle markup, in case spans aren't appropriate * for what they are trying to do with their themes. * * The reason that spans are being used is because HTML does not have a dedicated * mechanism for marking up subheadings, alternative titles, or taglines. There * are suggested alternatives from the World Wide Web Consortium (W3C); among them * are spans, which work well for what we're trying to do with titles in WordPress. * See the linked documentation for more information. * * @link http://www.w3.org/html/wg/drafts/html/master/common-idioms.html#sub-head * * @since 1.0.0 */ $subtitle_markup = apply_filters( 'subtitle_markup', array( 'before' => '<span class="entry-subtitle">', 'after' => '</span>', ) ); $subtitle = $subtitle_markup['before'] . $subtitle . $subtitle_markup['after']; /** * Put together the final title and subtitle set * * @since 1.0.0 */ $title = '<span class="entry-title-primary">' . $title . '</span> ' . $subtitle; /** * Filter the post subtitle, if necessary. * * @param string $title The post title. * @param int $id The post ID. * * @since 1.0.0 */Avoid too many `return` statements within this method. return apply_filters( 'the_subtitle', $title ); } /** * Get the Subtitle. * * This is a helper method used to grab the subtitle for any given post, * which theme authors can use with the template tag get_the_subtitle(). * * @since 1.0.0 */ public static function get_the_subtitle( $post = 0 ) { /** * Bail early if no subtitle has been set for the post. * * @since 1.0.0 */ $post = get_post( $post ); // this will be returned as an object or NULL $post_id = ( isset( $post ) ) ? $post->ID : 0; // post ID should always be a non-negative integer $subtitle = (string) html_entity_decode( wp_unslash( esc_html( get_post_meta( $post_id, self::SUBTITLE_META_KEY, true ) ) ), ENT_QUOTES ); if ( '' === $subtitle ) { return $subtitle; } /** * Filter the post subtitle. * * @since 1.0.0 * * @param string $subtitle The post subtitle. * @param int $id The post ID. */ return apply_filters( 'the_subtitle', $subtitle ); } // end get_the_subtitle() } // end class Subtitles} // End if().