p =& $plugin; if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $this->charset = get_bloginfo( $show = 'charset', $filter = 'raw' ); /* * Add information about the page $mod to the body tag class. */ add_filter( 'body_class', array( $this, 'add_body_class' ), 1000, 1 ); /* * Maybe add the Validators toolbar menu. */ $add_toolbar_validate = empty( $this->p->options[ 'plugin_add_toolbar_validate' ] ) ? false : true; $add_toolbar_validate = apply_filters( 'wpsso_add_toolbar_validate', $add_toolbar_validate ); if ( $add_toolbar_validate ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'adding validators toolbar' ); } add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_menu_validate' ), WPSSO_TB_VALIDATE_MENU_ORDER, 1 ); } elseif ( $this->p->debug->enabled ) { $this->p->debug->log( 'validators toolbar is disabled' ); } /* * Maybe add title tag filters. * * Since WordPress v4.4. * * See wordpress/wp-includes/general-template.php. */ if ( ! $this->p->util->is_title_tag_disabled() ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'adding title tag filters' ); } add_filter( 'pre_get_document_title', array( $this, 'pre_get_document_title' ), WPSSO_TITLE_TAG_PRIORITY, 1 ); add_filter( 'document_title_separator', array( $this, 'document_title_separator' ), WPSSO_TITLE_TAG_PRIORITY, 1 ); add_filter( 'document_title_parts', array( $this, 'document_title_parts' ), WPSSO_TITLE_TAG_PRIORITY, 1 ); add_filter( 'document_title', array( $this, 'document_title' ), WPSSO_TITLE_TAG_PRIORITY, 1 ); add_filter( 'get_wp_title_rss', array( $this, 'get_wp_title_rss' ), WPSSO_TITLE_TAG_PRIORITY, 1 ); add_filter( 'get_bloginfo_rss', array( $this, 'get_bloginfo_rss' ), WPSSO_TITLE_TAG_PRIORITY, 2 ); } elseif ( $this->p->debug->enabled ) { $this->p->debug->log( 'title tag is disabled' ); } } /* * Add information about the page $mod to the body tag class. * * $classes = An array of body class names. */ public function add_body_class( array $classes ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'required call to WpssoPage->get_mod()' ); } $mod = $this->get_mod(); $classes[] = 'wpsso-' . SucomUtil::get_mod_css_id( $mod ); return (array) apply_filters( 'wpsso_body_class', $classes, $mod ); } /* * This method is hooked to the 'admin_bar_menu' action and receives a reference to the $wp_admin_bar variable. * * WpssoPost->ajax_get_validate_submenu() also calls this method directly, supplying the post ID in $post_id. */ public function add_admin_bar_menu_validate( &$wp_admin_bar, $post_id = false ) { // Pass by reference OK. if ( ! $user_id = get_current_user_id() ) { // Just in case. return; } if ( $this->p->debug->enabled ) { $this->p->debug->log( 'required call to WpssoPage->get_mod()' ); } $mod = $this->get_mod( $post_id ); if ( $mod[ 'is_comment' ] ) { $capability = 'edit_comment'; } elseif ( $mod[ 'is_post' ] ) { $capability = 'page' === $mod[ 'post_type' ] ? 'edit_page' : 'edit_post'; } elseif ( $mod[ 'is_term' ] ) { $tax_obj = get_taxonomy( $mod[ 'tax_slug' ] ); $capability = $tax_obj->cap->edit_terms; } elseif ( $mod[ 'is_user' ] ) { $capability = 'edit_user'; } else return false; // Validators are only provided for known modules (post, term, and user). if ( ! current_user_can( $capability, $mod[ 'id' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: cannot ' . $capability . ' for ' . $mod[ 'name' ] . ' id ' . $mod[ 'id' ] ); } return false; // Stop here. } /* * We do not want to validate settings pages in the back-end, so validators are only provided for known * modules (post, term, and user). If we're on the front-end, validating the current webpage URL is fine. */ $validators = $this->p->util->get_validators( $mod, $form = null ); if ( ! empty( $validators ) ) { $parent_id = 'wpsso-validate'; $menu_icon = ''; $menu_title = _x( 'Validators', 'toolbar menu title', 'wpsso' ); $menu_items = array(); foreach ( $validators as $key => $el ) { if ( empty( $el[ 'type' ] ) ) { continue; } $menu_items[ $el[ 'type' ] ] = array( 'id' => $parent_id . '-' . $key, 'title' => $el[ 'type' ], 'parent' => $parent_id, 'href' => $el[ 'url' ], 'group' => false, 'meta' => array( 'class' => empty( $el[ 'url' ] ) ? 'disabled' : '', 'target' => '_blank', 'title' => $el[ 'title' ], ), ); } SucomUtil::natksort( $menu_items ); $wp_admin_bar->add_node( array( 'id' => $parent_id, 'title' => $menu_icon . $menu_title, 'parent' => false, 'href' => false, 'group' => false, 'meta' => array( 'html' => '', ), ) ); foreach ( $menu_items as $menu_item ) { $wp_admin_bar->add_node( $menu_item ); } return $parent_id; } return false; } /* * Since WordPress v4.4. * * Filters the WordPress document title before it is generated. Returning a non-empty value skips the * 'document_title_separator', 'document_title_parts', and 'document_title' filters. * * See wordpress/wp-includes/general-template.php. */ public function pre_get_document_title( $pre_title = '' ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'received pre_title value (' . gettype( $pre_title ) . ') = ' . $pre_title ); } /* * If $pre_title is not an empty string, then maybe force an empty string to make sure the * 'document_title_separator', 'document_title_parts', and 'document_title' filters are applied. */ if ( '' !== $pre_title ) { if ( 'wp_title' !== $this->p->options[ 'plugin_title_tag' ] ) { $pre_title = ''; if ( $this->p->debug->enabled ) { $this->p->debug->log( 'returning an empty string to use document_title filters' ); } } } return $pre_title; } /* * Since WordPress v4.4. * * Filters the separator for the document title. * * See wordpress/wp-includes/general-template.php. */ public function document_title_separator( $title_sep = '-' ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $title_sep = $this->maybe_get_title_sep(); // Returns default title separator (decoded). return $title_sep; } /* * Since WordPress v4.4. * * Filters the parts of the document title. * * Array ( * [title] => A Title * [page] => Page 2 * [site] => Site Name * [tagline] => * ) * * The final array is imploded into a string using the $title_sep value. * * $title = implode( ' ' . $title_sep . ' ', array_filter( $title ) ); * * See wordpress/wp-includes/general-template.php. */ public function document_title_parts( $title_parts ) { if ( $this->p->debug->enabled ) { $this->p->debug->log_arr( 'title_parts (argument)', $title_parts ); $this->p->debug->log( 'required call to WpssoPage->get_mod()' ); } /* * Note that in_the_loop() can be true in both archive and singular pages. */ $use_post = apply_filters( 'wpsso_use_post', in_the_loop() ? true : false ); $mod = $this->get_mod( $use_post ); if ( $this->p->debug->enabled ) { $this->p->debug->log_arr( 'mod', $mod ); $this->p->debug->log( 'getting title for ' . $this->p->options[ 'plugin_title_tag' ] ); } if ( 'wp_title' === $this->p->options[ 'plugin_title_tag' ] ) { if ( ! empty( $title_parts[ 'site' ] ) && empty( $title_parts[ 'title' ] ) ) { $title_parts[ 'tagline' ] = true; // Add the tagline before returning the array. } } else { $md_key = $this->p->options[ 'plugin_title_tag' ]; $max_len = $this->p->options[ 'plugin_title_tag' ]; /* * Return a decoded title, which may be used in the RSS XML tag. */ $title_parts[ 'title' ] = $this->get_title( $mod, $md_key, $max_len, $title_sep = null, $num_hashtags = false, $do_encode = false ); if ( $mod[ 'is_home' ] ) { // Home page (static or blog archive). unset( $title_parts[ 'site' ] ); // The title from WPSSO will be the site name. $title_parts[ 'tagline' ] = true; // Add the tagline before returning the array. } unset( $title_parts[ 'page' ] ); // The title from WPSSO includes the page number. } if ( ! empty( $title_parts[ 'site' ] ) ) { $title_parts[ 'site' ] = $this->p->opt->get_text( 'plugin_title_part_site' ); } if ( ! empty( $title_parts[ 'tagline' ] ) ) { $title_parts[ 'tagline' ] = $this->p->opt->get_text( 'plugin_title_part_tagline' ); } /* * Make sure the parts are ordered properly (just in case). */ $title_parts = array_merge( array( 'title' => null, 'page' => null, 'site' => null, 'tagline' => null ), $title_parts ); if ( $this->p->debug->enabled ) { $this->p->debug->log_arr( 'title_parts (returned)', $title_parts ); } return $title_parts; } /* * Since WordPress v4.4. * * Filters the document title. * * See wordpress/wp-includes/general-template.php. */ public function document_title( $title ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'title = ' . $title ); } if ( false !== strpos( $title, '%%' ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'required call to WpssoPage->get_mod()' ); } /* * Note that in_the_loop() can be true in both archive and singular pages. */ $use_post = apply_filters( 'wpsso_use_post', in_the_loop() ? true : false ); $mod = $this->get_mod( $use_post ); $title = $this->p->util->inline->replace_variables( $title, $mod ); } return SucomUtil::encode_html_emoji( $title ); // Does not double-encode. } /* * Since WPSSO Core v15.20.0. * * Filters the string from wp_get_document_title() for get_wp_title_rss() and wp_title_rss() to convert HTML named * entities into numbered entities. * * See wordpress/wp-includes/feed.php: * return apply_filters( 'get_wp_title_rss', wp_get_document_title(), $deprecated ); * * See wordpress/wp-includes/feed-rdf.php: * <title><?php wp_title_rss(); ?> * * See wordpress/wp-includes/feed-rss2.php: * <?php wp_title_rss(); ?> * * See wordpress/wp-includes/feed-rss2-comments.php: * <?php printf( ent2ncr( __( 'Comments for %s' ) ), get_wp_title_rss() ); ?> * * See wordpress/wp-includes/feed-rss.php: * <?php wp_title_rss(); ?> */ public function get_wp_title_rss( $wp_title ) { return ent2ncr( $wp_title ); // Converts named entities into numbered entities. } /* * Since WPSSO Core v15.20.0. * * Filters the string from get_bloginfo() for get_bloginfo_rss() and bloginfo_rss() to encode special characters * and convert HTML named entities into numbered entities. * * See https://developer.wordpress.org/reference/functions/get_bloginfo/. */ public function get_bloginfo_rss( $bloginfo, $show ) { $bloginfo = SucomUtil::encode_html_emoji( $bloginfo ); // Does not double-encode. return ent2ncr( $bloginfo ); // Converts named entities into numbered entities. } /* * Public method to sanitize arguments or modify values for get_title(), get_description(), etc. * * The $mod array argument is preferred but not required. * * $mod = true | false | post_id | $mod array */ public function maybe_get_mod( $mod ) { if ( ! is_array( $mod ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'optional call to WpssoPage->get_mod()' ); } $mod = $this->get_mod( $mod ); } return $mod; } /* * Determine and return the post/user/term module array. * * If any custom modifications are required to the WP_Query 'query_vars', they should be done before the 'wp_head' * action is triggered. The WpssoHead->show_head() method calls WpssoPage->get_mod() to determine the current * WordPress object (comment, post, term, or user), if any, and saves the 'query_vars' value for WordPress archive * queries. */ public function get_mod( $use_post = false, $mod = false, $wp_obj = false ) { if ( ! is_array( $mod ) ) { $mod = array(); } elseif ( isset( $mod[ 'obj' ] ) && is_object( $mod[ 'obj' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: module object is already defined' ); } return $mod; } if ( $this->p->debug->enabled ) { $this->p->debug->log( 'use_post is ' . SucomUtil::get_use_post_string( $use_post ) ); } /* * Check for known WP objects and set the object module name and its object ID. */ if ( is_object( $wp_obj ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'wp_obj argument is ' . get_class( $wp_obj ) . ' object' ); } switch ( get_class( $wp_obj ) ) { case 'WP_Comment': $mod[ 'name' ] = 'comment'; $mod[ 'id' ] = $wp_obj->comment_ID; break; case 'WP_Post': $mod[ 'name' ] = 'post'; $mod[ 'id' ] = $wp_obj->ID; break; case 'WP_Term': $mod[ 'name' ] = 'term'; $mod[ 'id' ] = $wp_obj->term_id; break; case 'WP_User': $mod[ 'name' ] = 'user'; $mod[ 'id' ] = $wp_obj->ID; break; default: if ( $this->p->debug->enabled ) { $this->p->debug->log( 'unknown wp object class = ' . get_class( $wp_obj ) ); } break; } } elseif ( $this->p->debug->enabled ) { $this->p->debug->log( 'wp_obj argument is not an object' ); } unset( $wp_obj ); // Done with $wp_obj. if ( empty( $mod[ 'name' ] ) ) { if ( SucomUtilWP::is_post_page( $use_post ) ) { // $use_post = true | false | post ID. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'is_post_page is true' ); } $mod[ 'name' ] = 'post'; } elseif ( SucomUtilWP::is_term_page() ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'is_term_page is true' ); } $mod[ 'name' ] = 'term'; } elseif ( SucomUtilWP::is_user_page() ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'is_user_page is true' ); } $mod[ 'name' ] = 'user'; } else $mod[ 'name' ] = false; } if ( empty( $mod[ 'id' ] ) ) { switch ( $mod[ 'name' ] ) { case 'comment': $mod[ 'id' ] = SucomUtilWP::get_comment_object( false, 'id' ); break; case 'post': $mod[ 'id' ] = SucomUtilWP::get_post_object( $use_post, 'id' ); // $use_post = true | false | post_id break; case 'term': $mod[ 'id' ] = SucomUtilWP::get_term_object( false, '', 'id' ); break; case 'user': $mod[ 'id' ] = SucomUtilWP::get_user_object( false, 'id' ); break; default: $mod[ 'id' ] = false; break; } } if ( ! empty( $mod[ 'name' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'getting $mod array from ' . $mod[ 'name' ] . ' module object' ); } } switch ( $mod[ 'name' ] ) { case 'comment': $mod = $this->p->comment->get_mod( $mod[ 'id' ] ); break; case 'post': $mod = $this->p->post->get_mod( $mod[ 'id' ] ); break; case 'term': $mod = $this->p->term->get_mod( $mod[ 'id' ] ); break; case 'user': $mod = $this->p->user->get_mod( $mod[ 'id' ] ); break; default: if ( $this->p->debug->enabled ) { $this->p->debug->log( 'unknown module object - merging $mod defaults array' ); } $mod = array_merge( WpssoAbstractWpMeta::get_mod_defaults(), $mod ); break; } /* * WpssoPage elements. */ global $wp_query; $mod[ 'query_vars' ] = $wp_query->query_vars; if ( empty( $mod[ 'paged' ] ) ) { // False by default. if ( ! empty( $mod[ 'query_vars' ][ 'page' ] ) ) { $mod[ 'paged' ] = $mod[ 'query_vars' ][ 'page' ]; } elseif ( ! empty( $mod[ 'query_vars' ][ 'paged' ] ) ) { $mod[ 'paged' ] = $mod[ 'query_vars' ][ 'paged' ]; } } /* * Note that 'paged_total' can be pre-defined by WpssoPost->get_mod() for posts with content (ie. singular) * and paging in their content. */ if ( empty( $mod[ 'paged_total' ] ) ) { // False by default. if ( ! empty( $wp_query->max_num_pages ) ) { $mod[ 'paged_total' ] = $wp_query->max_num_pages; } } if ( $mod[ 'paged' ] && $mod[ 'paged_total' ] && $mod[ 'paged' ] > $mod[ 'paged_total' ] ) { // Just in case. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'paged greater than paged_total - adjusting paged value' ); } $mod[ 'paged' ] = $mod[ 'paged_total' ]; } if ( empty( $mod[ 'comment_paged' ] ) ) { // False by default. if ( ! empty( $mod[ 'query_vars' ][ 'cpage' ] ) ) { $mod[ 'comment_paged' ] = $mod[ 'query_vars' ][ 'cpage' ]; } } $mod[ 'use_post' ] = $use_post; if ( empty( $mod[ 'name' ] ) ) { // Not a comment, post, term, or user object. if ( is_home() ) { $mod[ 'is_home' ] = true; // Home page (static or blog archive). $mod[ 'is_home_posts' ] = true; // Static posts page or blog archive page. } elseif ( is_feed() ) { $mod[ 'is_feed' ] = true; } elseif ( is_404() ) { $mod[ 'is_404' ] = true; } elseif ( is_search() ) { $mod[ 'is_search' ] = true; } elseif ( is_archive() ) { $mod[ 'is_archive' ] = true; if ( is_date() ) { $mod[ 'is_date' ] = true; if ( is_year() ) { $mod[ 'is_year' ] = true; } elseif ( is_month() ) { $mod[ 'is_month' ] = true; } elseif ( is_day() ) { $mod[ 'is_day' ] = true; } } } } elseif ( empty( $mod[ 'id' ] ) ) { switch ( $mod[ 'name' ] ) { case 'post': /* * If the query is a post with an ID of 0, then it may be a post type archive page * that was setup incorrectly (ie. a post object without an ID or slug). */ if ( ! empty( $mod[ 'query_vars' ][ 'post_type' ] ) ) { $mod[ 'post_type' ] = $mod[ 'query_vars' ][ 'post_type' ]; $post_type_obj = get_post_type_object( $mod[ 'post_type' ] ); if ( is_object( $post_type_obj ) ) { // Just in case. if ( isset( $post_type_obj->labels->name ) ) { $mod[ 'post_type_label_plural' ] = $post_type_obj->labels->name; } if ( isset( $post_type_obj->labels->singular_name ) ) { $mod[ 'post_type_label_single' ] = $post_type_obj->labels->singular_name; } if ( isset( $post_type_obj->public ) ) { $mod[ 'is_public' ] = $post_type_obj->public ? true : false; } if ( is_post_type_archive() ) { $mod[ 'is_post_type_archive' ] = true; $mod[ 'is_archive' ] = $mod[ 'is_post_type_archive' ]; } } unset( $post_type_obj ); // Done with $post_type_obj. } break; } } return $mod; } /* * See WpssoFaqShortcodeFaq->do_shortcode(). * See WpssoSchema->add_itemlist_data(). * See WpssoSchema->add_posts_data(). */ public function get_posts_mods( array $mod ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $page_posts_mods = array(); if ( ! empty( $mod[ 'query_vars' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'using WP_Query query_vars to get post mods' ); } global $wp_query; $saved_wp_query = $wp_query; $wp_query = new WP_Query( $mod[ 'query_vars' ] ); if ( $mod[ 'is_home_posts' ] ) { // Static posts page or blog archive page. $wp_query->is_home = true; } if ( have_posts() ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'looping through posts' ); } $have_num = 0; while ( have_posts() ) { $have_num++; the_post(); // Defines the $post global. global $post; if ( $this->p->debug->enabled ) { $this->p->debug->log( '#' . $have_num . ': getting mod for post ID ' . $post->ID ); } $page_posts_mods[] = $this->p->post->get_mod( $post->ID ); } if ( $this->p->debug->enabled ) { $this->p->debug->log( 'retrieved ' . $have_num . ' post mods' ); } rewind_posts(); } elseif ( $this->p->debug->enabled ) { $this->p->debug->log( 'no posts to add' ); } $wp_query = $saved_wp_query; } elseif ( ! empty( $mod[ 'obj' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'using module object to get post mods' ); } $page_posts_mods = $mod[ 'obj' ]->get_posts_mods( $mod ); } elseif ( $this->p->debug->enabled ) { $this->p->debug->log( 'no source to get post mods' ); } $page_posts_mods = apply_filters( 'wpsso_page_posts_mods', $page_posts_mods, $mod ); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'returning ' . count( $page_posts_mods ) . ' post mods' ); } return $page_posts_mods; } /* * $mod = true | false | post_id | array * * $md_key = true | false | string | array * * $caption_type = 'title' | 'excerpt' | 'both' * * See WpssoRrssbFiltersEdit->filter_post_edit_share_rows(). */ public function get_caption( $mod = false, $md_key = null, $caption_type = 'title', $caption_max_len = 0, $num_hashtags = false, $do_encode = true ) { if ( $this->p->debug->enabled ) { $this->p->debug->log_args( array( 'mod' => $mod, 'md_key' => $md_key, 'caption_type' => $caption_type, 'caption_max_len' => $caption_max_len, 'num_hashtags' => $num_hashtags, // True, false, or numeric. 'do_encode' => $do_encode, ) ); } switch ( $caption_type ) { case 'title': $md_key = $this->sanitize_md_key( $md_key, $def_key = 'og_title' ); // Returns an array of metadata keys (can be empty). break; case 'excerpt': $md_key = $this->sanitize_md_key( $md_key, $def_key = 'og_desc' ); // Returns an array of metadata keys (can be empty). break; case 'both': default: $md_key = $this->sanitize_md_key( $md_key, $def_key = 'og_caption' ); // Returns an array of metadata keys (can be empty). break; } $caption_max_len = $this->sanitize_max_len( $caption_max_len ); // Returns max integer for numeric, string, or array value. $mod = $this->maybe_get_mod( $mod ); // Returns $mod array if not provided. $caption_text = $this->maybe_get_opt_multi( $mod, $md_key ); // Returns null or custom value. $is_custom = empty( $caption_text ) ? false : true; /* * If there's no custom caption text, then go ahead and generate the caption text value. */ if ( empty( $caption_text ) ) { // No custom value found. /* * Request all values un-encoded, then encode once we have the complete caption text. */ switch ( $caption_type ) { case 'title': $caption_text = $this->get_title( $mod, $md_key_title = 'og_title', $caption_max_len, $title_sep = null, $num_hashtags, $do_encode_title = false ); break; case 'excerpt': $caption_text = $this->get_description( $mod, $md_key_desc = 'og_desc', $caption_max_len, $num_hashtags, $do_encode_desc = false ); break; case 'both': $title_sep = $this->maybe_get_title_sep(); // Returns default title separator (decoded). $caption_text = $this->get_title( $mod, $md_key_title = 'og_title', $max_len = 'og_title', $title_sep, $num_hashtags_title = false, $do_encode_title = false ); $caption_text_len = strlen( trim( $caption_text . ' ' . $title_sep ) . ' ' ); $adj_max_len = $caption_max_len - $caption_text_len; $caption_desc = $this->get_description( $mod, $md_key_desc = 'og_desc', $adj_max_len, $num_hashtags, $do_encode_desc = false ); $this->add_title_part( $caption_text, $title_sep, $caption_desc ); break; } } if ( true === $do_encode ) { $caption_text = SucomUtil::encode_html_emoji( $caption_text ); // Does not double-encode. } else $caption_text = html_entity_decode( SucomUtil::decode_utf8( $caption_text ), ENT_QUOTES, $this->charset ); return apply_filters( 'wpsso_caption', $caption_text, $mod, $num_hashtags, $md_key ); } /* * $mod = true | false | post_id | array * * $md_key = true | false | string | array * * Use $title_sep = false to avoid adding term parent names in the term title. * * WpssoUtilInline->replace_variables() is applied to the final title text. * * The WPSSO BC add-on uses $title_sep = false to avoid prefixing term parents in the term titles. */ public function get_title( $mod = false, $md_key = null, $max_len = 0, $title_sep = null, $num_hashtags = false, $do_encode = true ) { if ( $this->p->debug->enabled ) { $this->p->debug->log_args( array( 'mod' => $mod, 'md_key' => $md_key, 'max_len' => $max_len, 'title_sep' => $title_sep, 'num_hashtags' => $num_hashtags, // True, false, or numeric. 'do_encode' => $do_encode, ) ); } $mod = $this->maybe_get_mod( $mod ); // Returns $mod array if not provided. $md_key = $this->sanitize_md_key( $md_key, $def_key = 'seo_title' ); // Returns an array of metadata keys (can be empty). $max_len = $this->sanitize_max_len( $max_len ); // Returns max integer for numeric, string, or array value. $dots = $this->maybe_get_ellipsis(); // Returns default ellipsis (decoded). $title_sep = $this->maybe_get_title_sep( $title_sep ); // Returns default title separator (decoded) if not provided. $title_text = $this->maybe_get_opt_multi( $mod, $md_key ); // Returns null or custom value. $is_custom = empty( $title_text ) ? false : true; $hashtags = ''; /* * Get seed if no custom meta title. */ if ( empty( $title_text ) ) { $title_text = apply_filters( 'wpsso_title_seed', '', $mod, $num_hashtags, $md_key, $title_sep ); if ( ! empty( $title_text ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'title seed = ' . $title_text ); } } } /* * If there's no custom title, and no pre-seed, then go ahead and generate the title value. * * A custom or pre-seed title is expected to provide any hashtags, so get hashtags only when generating the * title value. */ if ( empty( $title_text ) ) { $title_text = $this->get_the_title( $mod, $title_sep ); $hashtags = $this->get_hashtags( $mod, $num_hashtags ); } /* * Add the page number if it's greater than 1 and we don't already have a '%%page%%' or '%%pagenumber%%' * inline variable in the title. */ $page_number_transl = ''; if ( $mod[ 'paged' ] > 1 ) { if ( false === strpos( $title_text, '%%page%%' ) && false === strpos( $title_text, '%%pagenumber%%' ) ) { if ( $mod[ 'paged_total' ] > 1 ) { $page_number_transl = sprintf( __( 'Page %1$d of %2$d', 'wpsso' ), $mod[ 'paged' ], $mod[ 'paged_total' ] ); } else { $page_number_transl = sprintf( __( 'Page %1$d', 'wpsso' ), $mod[ 'paged' ] ); } } } /* * Titles comprised entirely of HTML content will be empty after running cleanup_html_tags(), so remove the * HTML tags before maybe falling back to the generic title. */ $title_text = $this->p->util->cleanup_html_tags( $title_text, $strip_tags = true, $use_img_alt = true ); /* * If there's still no title, then fallback to a generic version. */ if ( empty( $title_text ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'title is empty - using generic title text' ); } $title_text = $this->p->opt->get_text( 'plugin_no_title_text' ); // No Title Text. } /* * Replace any inline variables in the string before checking/adjusting its length. */ if ( false !== strpos( $title_text, '%%' ) ) { /* * Override the default 'title_sep' value. */ $title_text = $this->p->util->inline->replace_variables( $title_text, $mod, $atts = array( 'title_sep' => $title_sep ) ); } /* * Check title against string length limits. */ if ( $max_len > 0 ) { /* * If we have a page number, reduce the max length by the separator, page number, and two spaces. */ $adj_max_len = empty( $page_number_transl ) ? $max_len : $max_len - strlen ( $title_sep ) - strlen( $page_number_transl ) - 2; /* * If we have any hashtags, further reduce the max title length by the hashtags and one space. */ $adj_max_len = empty( $hashtags ) ? $adj_max_len : $adj_max_len - strlen( $hashtags ) - 1; $title_text = $this->p->util->limit_text_length( $title_text, $adj_max_len, $dots, $cleanup_html = false ); } /* * Once the title length has been adjusted, we can add the page number and hashtags. */ if ( ! empty( $page_number_transl ) ) { $this->add_title_part( $title_text, $title_sep, $page_number_transl ); } if ( ! empty( $hashtags ) ) { $this->add_title_part( $title_text, '', $hashtags ); } /* * Maybe return the values encoded (true by default). */ if ( $do_encode ) { $title_text = SucomUtil::encode_html_emoji( $title_text ); // Does not double-encode. $title_sep = SucomUtil::encode_html_emoji( $title_sep ); // Does not double-encode. } return apply_filters( 'wpsso_title', $title_text, $mod, $num_hashtags, $md_key, $title_sep, $is_custom ); } /* * $mod = true | false | post_id | array. * * $md_key = true | false | string | array. * * WpssoUtilInline->replace_variables() is applied to the final description text. */ public function get_description( $mod = false, $md_key = null, $max_len = 0, $num_hashtags = false, $do_encode = true ) { if ( $this->p->debug->enabled ) { $this->p->debug->log_args( array( 'mod' => $mod, 'md_key' => $md_key, 'max_len' => $max_len, 'num_hashtags' => $num_hashtags, // True, false, or numeric. 'do_encode' => $do_encode, ) ); } $mod = $this->maybe_get_mod( $mod ); // Returns $mod array if not provided. $md_key = $this->sanitize_md_key( $md_key, $def_key = 'seo_desc' ); // Returns an array of metadata keys (can be empty). $max_len = $this->sanitize_max_len( $max_len ); // Returns max integer for numeric, string, or array value. $dots = $this->maybe_get_ellipsis(); // Returns default ellipsis (decoded). $desc_text = $this->maybe_get_opt_multi( $mod, $md_key ); // Returns null or custom value. $is_custom = empty( $desc_text ) ? false : true; $hashtags = ''; /* * Get seed if no custom meta description. */ if ( empty( $desc_text ) ) { $desc_text = apply_filters( 'wpsso_description_seed', '', $mod, $num_hashtags, $md_key ); if ( ! empty( $desc_text ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'description seed = ' . $desc_text ); } } } /* * If there's no custom description, and no pre-seed, then go ahead and generate the description value. * * A custom or pre-seed description is expected to provide any hashtags, so get hashtags only when * generating the description value. */ if ( empty( $desc_text ) ) { $desc_text = $this->get_the_description( $mod ); $hashtags = $this->get_hashtags( $mod, $num_hashtags ); } /* * Descriptions comprised entirely of HTML content will be empty after running cleanup_html_tags(), so * remove the HTML tags before maybe falling back to the generic description. */ $desc_text = $this->p->util->cleanup_html_tags( $desc_text, $strip_tags = true, $use_img_alt = true ); /* * If there's still no description, then fallback to a generic version. */ if ( empty( $desc_text ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'description is empty - using generic description text' ); } $desc_text = $this->p->opt->get_text( 'plugin_no_desc_text' ); // No Description Text. } /* * Replace any inline variables in the string before checking/adjusting its length. */ if ( false !== strpos( $desc_text, '%%' ) ) { $desc_text = $this->p->util->inline->replace_variables( $desc_text, $mod ); } /* * Check description against string length limits. */ if ( $max_len > 0 ) { /* * If we have any hashtags, reduce the max length by the hashtags and one space. */ $adj_max_len = empty( $hashtags ) ? $max_len : $max_len - strlen( $hashtags ) - 1; $desc_text = $this->p->util->limit_text_length( $desc_text, $adj_max_len, $dots, $cleanup_html = false ); } /* * Once the description length has been adjusted, we can add the hashtags. */ if ( ! empty( $hashtags ) ) { $this->add_title_part( $desc_text, '', $hashtags ); } /* * Maybe return the values encoded (true by default). */ if ( $do_encode ) { $desc_text = SucomUtil::encode_html_emoji( $desc_text ); // Does not double-encode. } return apply_filters( 'wpsso_description', $desc_text, $mod, $num_hashtags, $md_key, $is_custom ); } public function get_text( $mod = false, $md_key = null, $max_len = 0, $num_hashtags = false, $do_encode = true ) { if ( $this->p->debug->enabled ) { $this->p->debug->log_args( array( 'mod' => $mod, 'md_key' => $md_key, 'max_len' => $max_len, 'num_hashtags' => $num_hashtags, // True, false, or numeric. 'do_encode' => $do_encode, ) ); } $mod = $this->maybe_get_mod( $mod ); // Returns $mod array if not provided. $md_key = $this->sanitize_md_key( $md_key, $def_key = 'schema_text' ); // Returns an array of metadata keys (can be empty). $max_len = $this->sanitize_max_len( $max_len ); // Returns max integer for numeric, string, or array value. $dots = $this->maybe_get_ellipsis(); // Returns default ellipsis (decoded). $text = $this->maybe_get_opt_multi( $mod, $md_key ); // Returns null or custom value. $is_custom = empty( $text ) ? false : true; /* * If there's no custom text, then go ahead and generate the text value. */ if ( empty( $title_text ) ) { $text = $this->get_the_text( $mod ); $hashtags = $this->get_hashtags( $mod, $num_hashtags ); } /* * Check text against string length limits. */ if ( $max_len > 0 ) { /* * If we have any hashtags, reduce the max length by the hashtags and one space. */ $adj_max_len = empty( $hashtags ) ? $max_len : $max_len - strlen( $hashtags ) - 1; $text = $this->p->util->limit_text_length( $text, $adj_max_len, $dots, $cleanup_html = false ); } /* * Once the text length has been adjusted, we can add the hashtags. */ if ( ! empty( $hashtags ) ) { $this->add_title_part( $text, '', $hashtags ); } /* * Maybe return the values encoded (true by default). */ if ( $do_encode ) { $text = SucomUtil::encode_html_emoji( $text ); // Does not double-encode. } return apply_filters( 'wpsso_text', $text, $mod, $num_hashtags, $md_key, $is_custom ); } /* * Use $title_sep = false to avoid adding term parent names in the term title. * * WpssoUtilInline->replace_variables() is called from the WpssoPage->get_title() method. Do not use * WpssoUtilInline->replace_variables() in this method as WpssoPage->get_title() is also called from * WpssoUtilInline->replace_variables(), and this would create a circular loop. * * See WpssoBcBreadcrumb->add_breadcrumblist_data(). * See WpssoPage->get_title(). * See WpssoUtilInline->replace_callback(). */ public function get_the_title( array $mod, $title_sep = null ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $title_sep = $this->maybe_get_title_sep( $title_sep ); // Returns default title separator (decoded) if not provided. $title_text = ''; /* * Similar module type logic can be found in the following methods: * * See WpssoOpenGraph->get_mod_og_type(). * See WpssoPage->get_the_title(). * See WpssoPage->get_the_description(). * See WpssoSchema->get_mod_schema_type(). * See WpssoUtil->get_canonical_url(). */ if ( $mod[ 'is_home' ] ) { // Home page (static or blog archive). $title_text = SucomUtilWP::get_site_name( $this->p->options ); } elseif ( $mod[ 'is_comment' ] ) { if ( is_numeric( $mod[ 'comment_rating' ] ) ) { $title_text = $this->p->opt->get_text( 'plugin_comment_review_title' ); } elseif ( $mod[ 'comment_parent' ] ) { $title_text = $this->p->opt->get_text( 'plugin_comment_reply_title' ); } else $title_text = $this->p->opt->get_text( 'plugin_comment_title' ); } elseif ( $mod[ 'is_post' ] ) { if ( $mod[ 'post_type' ] && is_string( $mod[ 'post_type' ] ) ) { // Just in case. if ( $mod[ 'is_post_type_archive' ] ) { // The post ID may be 0. $title_text = $this->p->opt->get_text( 'plugin_pta_' . $mod[ 'post_type' ] . '_title' ); if ( empty( $title_text ) ) { // Just in case. $post_type_obj = get_post_type_object( $mod[ 'post_type' ] ); if ( isset( $post_type_obj->label ) ) { // Just in case. $title_text = $post_type_obj->label; } } } elseif ( $mod[ 'wp_obj' ] instanceof WP_Post ) { /* * Note that get_the_title() applies the 'the_title' filter, not the 'wp_title' filter. */ $title_text = get_the_title( $mod[ 'wp_obj' ] ); } elseif ( $mod[ 'id' ] ) { /* * Note that get_the_title() applies the 'the_title' filter, not the 'wp_title' filter. */ $title_text = get_the_title( $mod[ 'id' ] ); } elseif ( $this->p->debug->enabled ) { $this->p->debug->log( 'no post object or id' ); } $title_text = html_entity_decode( $title_text, ENT_QUOTES, $this->charset ); } elseif ( $this->p->debug->enabled ) { $this->p->debug->log( 'no post type' ); } } elseif ( $mod[ 'is_term' ] ) { /* * The default 'plugin_term_page_title' value is '%%term_hierarchy%%'. * * If $title_sep is false, then use '%%term_title%%' instead. */ if ( false === $title_sep ) { $title_text = '%%term_title%%'; } else $title_text = $this->p->opt->get_text( 'plugin_term_page_title' ); } elseif ( $mod[ 'is_user' ] ) { $title_text = $this->p->opt->get_text( 'plugin_author_page_title' ); } elseif ( $mod[ 'is_feed' ] ) { $title_text = $this->p->opt->get_text( 'plugin_feed_title' ); } elseif ( $mod[ 'is_404' ] ) { $title_text = $this->p->opt->get_text( 'plugin_404_page_title' ); } elseif ( $mod[ 'is_search' ] ) { $title_text = $this->p->opt->get_text( 'plugin_search_page_title' ); } elseif ( $mod[ 'is_archive' ] ) { if ( $mod[ 'is_date' ] ) { if ( $mod[ 'is_year' ] ) { $title_text = $this->p->opt->get_text( 'plugin_year_page_title' ); } elseif ( $mod[ 'is_month' ] ) { $title_text = $this->p->opt->get_text( 'plugin_month_page_title' ); } elseif ( $mod[ 'is_day' ] ) { $title_text = $this->p->opt->get_text( 'plugin_day_page_title' ); } } } return apply_filters( 'wpsso_the_title', $title_text, $mod, $title_sep ); } /* * WpssoUtilInline->replace_variables() is called from the WpssoPage->get_description() method. Do not use * WpssoUtilInline->replace_variables() in this method as WpssoPage->get_description() is also called from * WpssoUtilInline->replace_variables(), and this would create a circular loop. * * See WpssoPage->get_description(). * See WpssoUtilInline->replace_callback(). */ public function get_the_description( array $mod ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $desc_text = ''; /* * Similar module type logic can be found in the following methods: * * See WpssoOpenGraph->get_mod_og_type(). * See WpssoPage->get_the_title(). * See WpssoPage->get_the_description(). * See WpssoSchema->get_mod_schema_type(). * See WpssoUtil->get_canonical_url(). */ if ( $mod[ 'is_home' ] ) { // Home page (static or blog archive). if ( $mod[ 'is_post' ] && $mod[ 'id' ] ) { $desc_text = $this->get_the_excerpt( $mod ); } if ( empty( $desc_text ) ) { $desc_text = SucomUtilWP::get_site_description( $this->p->options ); } } elseif ( $mod[ 'is_comment' ] ) { if ( $mod[ 'wp_obj' ] instanceof WP_Comment ) { $desc_text = get_comment_excerpt( $mod[ 'wp_obj' ] ); } elseif ( $mod[ 'id' ] ) { // Just in case. $desc_text = get_comment_excerpt( $mod[ 'id' ] ); } } elseif ( $mod[ 'is_post' ] ) { if ( $mod[ 'post_type' ] && is_string( $mod[ 'post_type' ] ) ) { // Just in case. if ( $mod[ 'is_post_type_archive' ] ) { // The post ID may be 0. $desc_text = $this->p->opt->get_text( 'plugin_pta_' . $mod[ 'post_type' ] . '_desc' ); if ( empty( $desc_text ) ) { // Just in case. $post_type_obj = get_post_type_object( $mod[ 'post_type' ] ); if ( ! empty( $post_type_obj->description ) ) { $desc_text = $post_type_obj->description; } } } else { $desc_text = $this->get_the_excerpt( $mod ); /* * If there's no excerpt, then fallback to the content. */ if ( empty( $desc_text ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'getting the content for post ID ' . $mod[ 'id' ] ); } $desc_text = $this->get_the_content( $mod ); /* * Ignore everything before the first paragraph. */ if ( ! empty( $desc_text ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'removing text before the first paragraph' ); } /* * U = Inverts the "greediness" of quantifiers so that they are not greedy by default. * i = Letters in the pattern match both upper and lower case letters. * * See http://php.net/manual/en/reference.pcre.pattern.modifiers.php. */ $desc_text = preg_replace( '/^.*]*>/Usi', '', $desc_text ); } } /* * Fallback to the image alt value. */ if ( empty( $desc_text ) ) { if ( $mod[ 'is_attachment' ] && 'image' === $mod[ 'post_mime_group' ] ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'falling back to attachment image alt text' ); } $desc_text = get_metadata( 'post', $mod[ 'id' ], '_wp_attachment_image_alt', $single = true ); } } } } elseif ( $this->p->debug->enabled ) { $this->p->debug->log( 'no post type' ); } } elseif ( $mod[ 'is_term' ] ) { if ( $mod[ 'wp_obj' ] instanceof WP_Term ) { if ( isset( $mod[ 'wp_obj' ]->description ) ) { $desc_text = $mod[ 'wp_obj' ]->description; } } elseif ( $mod[ 'id' ] ) { $term_obj = SucomUtilWP::get_term_object( $mod[ 'id' ], $mod[ 'tax_slug' ] ); if ( isset( $term_obj->description ) ) { $desc_text = $term_obj->description; } } if ( '' === $desc_text ) { $desc_text = $this->p->opt->get_text( 'plugin_term_page_desc' ); } } elseif ( $mod[ 'is_user' ] ) { if ( $mod[ 'wp_obj' ] instanceof WP_User ) { if ( isset( $mod[ 'wp_obj' ]->description ) ) { $desc_text = $mod[ 'wp_obj' ]->description; } } elseif ( $mod[ 'id' ] ) { // Just in case. $user_obj = SucomUtilWP::get_user_object( $mod[ 'id' ] ); if ( isset( $user_obj->description ) ) { $desc_text = $user_obj->description; } } if ( '' === $desc_text ) { $desc_text = $this->p->opt->get_text( 'plugin_author_page_desc' ); } } elseif ( $mod[ 'is_404' ] ) { $desc_text = $this->p->opt->get_text( 'plugin_404_page_desc' ); } elseif ( $mod[ 'is_search' ] ) { $desc_text = $this->p->opt->get_text( 'plugin_search_page_desc' ); } elseif ( $mod[ 'is_archive' ] ) { if ( $mod[ 'is_date' ] ) { if ( $mod[ 'is_year' ] ) { $desc_text = $this->p->opt->get_text( 'plugin_year_page_desc' ); } elseif ( $mod[ 'is_month' ] ) { $desc_text = $this->p->opt->get_text( 'plugin_month_page_desc' ); } elseif ( $mod[ 'is_day' ] ) { $desc_text = $this->p->opt->get_text( 'plugin_day_page_desc' ); } } } return apply_filters( 'wpsso_the_description', $desc_text, $mod ); } public function get_the_excerpt( array $mod ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $excerpt_text = ''; if ( $mod[ 'is_post' ] ) { // Only post objects have excerpts. if ( $mod[ 'wp_obj' ] instanceof WP_Post ) { $excerpt_text = get_post_field( 'post_excerpt', $mod[ 'wp_obj' ] ); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'get_post_field() for WP_Post = ' . $excerpt_text ); } } elseif ( $mod[ 'id' ] ) { $excerpt_text = get_post_field( 'post_excerpt', $mod[ 'id' ] ); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'get_post_field() for ' . $mod[ 'name' ] . ' id ' . $mod[ 'id' ] . ' = ' . $excerpt_text ); } } $filter_excerpt = empty( $this->p->options[ 'plugin_filter_excerpt' ] ) ? false : true; $filter_excerpt = apply_filters( 'wpsso_the_excerpt_filter_excerpt', $filter_excerpt ); if ( $filter_excerpt ) { $excerpt_text = $this->p->util->safe_apply_filters( array( 'get_the_excerpt', $excerpt_text, $mod[ 'wp_obj' ] ), $mod ); if ( ! is_string( $excerpt_text ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'excerpt_text from "get_the_excerpt" is ' . gettype( $content ) ); } $excerpt_text = ''; } } } return apply_filters( 'wpsso_the_excerpt', $excerpt_text, $mod ); } /* * The cache is cleared by WpssoAbstractWpMeta->clear_mod_cache(). */ public function clear_the_content( array $mod ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } /* * Note that the sort order, page number, locale, amp and embed checks are provided by * WpssoHead->get_head_cache_index() and not SucomUtil::get_mod_salt(). */ $canonical_url = $this->p->util->get_canonical_url( $mod ); $cache_md5_pre = 'wpsso_c_'; $cache_salt = __CLASS__ . '::the_content(' . SucomUtil::get_mod_salt( $mod, $canonical_url ) . ')'; $cache_id = $cache_md5_pre . md5( $cache_salt ); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'canonical url = ' . $canonical_url ); $this->p->debug->log( 'wp cache salt = ' . $cache_salt ); $this->p->debug->log( 'wp cache id = ' . $cache_id ); } wp_cache_delete( $cache_id ); return; } /* * The cache is cleared by WpssoAbstractWpMeta->clear_mod_cache(). */ public function get_the_content( array $mod, $flatten = true ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); // Log execution time and memory usage. $this->p->debug->log_args( array( 'mod' => $mod, ) ); } /* * Note that the sort order, page number, locale, amp and embed checks are provided by * WpssoHead->get_head_cache_index() and not SucomUtil::get_mod_salt(). */ $filter_content = empty( $this->p->options[ 'plugin_filter_content' ] ) ? false : true; $filter_content = apply_filters( 'wpsso_the_content_filter_content', $filter_content ); $filter_blocks = apply_filters( 'wpsso_the_content_filter_blocks', true ); $canonical_url = $this->p->util->get_canonical_url( $mod ); $cache_md5_pre = 'wpsso_c_'; $cache_exp_secs = $this->p->util->get_cache_exp_secs( $cache_md5_pre, $cache_type = 'wp_cache' ); $cache_salt = __CLASS__ . '::the_content(' . SucomUtil::get_mod_salt( $mod, $canonical_url ) . ')'; $cache_id = $cache_md5_pre . md5( $cache_salt ); $cache_index = 'locale:' . SucomUtilWP::get_locale( $mod ) . '_filter:' . ( $filter_content ? 'true' : 'false' ); $cache_array = array(); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'canonical url = ' . $canonical_url ); $this->p->debug->log( 'filter content = ' . ( $filter_content ? 'true' : 'false' ) ); $this->p->debug->log( 'wp cache expire = ' . $cache_exp_secs ); $this->p->debug->log( 'wp cache salt = ' . $cache_salt ); $this->p->debug->log( 'wp cache id = ' . $cache_id ); $this->p->debug->log( 'wp cache index = ' . $cache_index ); } if ( $cache_exp_secs > 0 ) { $cache_array = wp_cache_get( $cache_id, __METHOD__ ); if ( isset( $cache_array[ $cache_index ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: cache index found in wp_cache' ); } $content =& $cache_array[ $cache_index ]; /* * Maybe put everything on one line but do not cache the re-formatted content. */ return $flatten ? preg_replace( '/[\s\r\n]+/s', ' ', $content ) : $content; } else { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'cache index not in wp_cache' ); } if ( ! is_array( $cache_array ) ) { $cache_array = array(); } } } if ( $this->p->debug->enabled ) { $this->p->debug->log( 'initializing new wp cache element' ); } $cache_array[ $cache_index ] = ''; // Initialize the cache element. $content =& $cache_array[ $cache_index ]; // Reference the cache element. /* * Apply the seed filter. * * Return false to prevent the comment or post content from being used. * * See WpssoIntegEcomWooCommerce->filter_the_content_seed(). */ $content = apply_filters( 'wpsso_the_content_seed', '', $mod ); /* * Make sure that we have a string (empty or not) to work with. */ if ( ! is_string( $content ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'content from "wpsso_the_content_seed" is ' . gettype( $content ) ); } $content = ''; } elseif ( '' !== $content ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'content seed = ' . $content ); } } elseif ( $mod[ 'is_post' ] && $mod[ 'id' ] ) { $content = (string) $mod[ 'wp_obj' ]->post_content; } elseif ( $mod[ 'is_comment' ] && $mod[ 'id' ] ) { $content = (string) $mod[ 'wp_obj' ]->comment_content; } /* * Remove singlepics, which we detect and use before-hand. */ $count = null; $content = preg_replace( '/\[singlepic[^\]]+\]/', '', $content, $limit = -1, $count ); if ( $count ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( $count . ' singlepic shortcode(s) removed from content' ); } } /* * Maybe apply 'the_content' filter to expand shortcodes and blocks. */ if ( $filter_content ) { $use_bfo = SucomUtil::get_const( 'WPSSO_CONTENT_BLOCK_FILTER_OUTPUT', true ); $mtime_max = SucomUtil::get_const( 'WPSSO_CONTENT_FILTERS_MAX_TIME', 1.50 ); if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'applying filters "the_content"' ); // Begin timer. } $content = $this->p->util->safe_apply_filters( array( 'the_content', $content ), $mod, $mtime_max, $use_bfo ); if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'applying filters "the_content"' ); // End timer. } if ( ! is_string( $content ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'content from "the_content" is ' . gettype( $content ) ); } $content = ''; } } else { /* * Maybe fallback and apply only the 'do_blocks' filters. */ if ( $filter_blocks ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'calling do_blocks()' ); // Begin timer. } $content = do_blocks( $content ); if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'calling do_blocks()' ); // End timer. } if ( ! is_string( $content ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'content from do_blocks() is ' . gettype( $content ) ); } $content = ''; } } /* * When the content filter is disabled, fallback and apply our own shortcode filter. */ if ( false !== strpos( $content, '[' ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'applying filters "wpsso_do_shortcode"' ); // Begin timer. } $content = apply_filters( 'wpsso_do_shortcode', $content ); if ( $this->p->debug->enabled ) { $this->p->debug->mark( 'applying filters "wpsso_do_shortcode"' ); // End timer. } if ( ! is_string( $content ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'content from "wpsso_do_shortcode" is ' . gettype( $content ) ); } $content = ''; } } } /* * Maybe use only a certain part of the content. */ if ( false !== strpos( $content, 'wpsso-content' ) ) { $content = preg_replace( '/^.*(.*).*$/Us', '$1', $content ); } /* * Maybe remove text between ignore markers. */ if ( false !== strpos( $content, 'wpsso-ignore' ) ) { $content = preg_replace( '/.*/Us', ' ', $content ); } /* * Remove "Google+" link and text. */ if ( false !== strpos( $content, '>Google+<' ) ) { $content = preg_replace( '/Google\+<\/a>/', ' ', $content ); } /* * Prefix caption text. */ if ( false !== strpos( $content, '

' ) ) { $caption_prefix = $this->p->opt->get_text( 'plugin_p_cap_prefix' ); if ( ! empty( $caption_prefix ) ) { $content = preg_replace( '/

/', '$0' . $caption_prefix . ' ', $content ); } } /* * Apply the filter. */ $content = apply_filters( 'wpsso_the_content', $content, $mod ); if ( ! is_string( $content ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'content from "wpsso_the_content" is ' . gettype( $content ) ); } $content = ''; } /* * Save content to non-persistant cache. */ if ( $cache_exp_secs > 0 ) { /* * Adds a group or set of groups to the list of non-persistent groups. * * Note that only a few caching plugins support this feature. */ wp_cache_add_non_persistent_groups( array( __METHOD__ ) ); wp_cache_set( $cache_id, $cache_array, __METHOD__, $cache_exp_secs ); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'content array saved to wp_cache for ' . $cache_exp_secs . ' seconds'); } } /* * Maybe put everything on one line but do not cache the re-formatted content. */ return $flatten ? preg_replace( '/[\s\r\n]+/s', ' ', $content ) : $content; } /* * Returns the content text, stripped of all HTML tags. */ public function get_the_text( array $mod ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $text = $this->get_the_content( $mod ); $text = preg_replace( '//Us', '', $text ); $text = preg_replace( '/]*>.*<\/pre>/Us', '', $text ); $text = $this->p->util->cleanup_html_tags( $text, $strip_tags = true, $use_img_alt = true ); return apply_filters( 'wpsso_the_text', $text, $mod ); } /* * Returns a comma delimited text string of keywords (ie. post tag names). */ public function get_keywords_csv( array $mod, $md_key = null ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $keywords = ''; $is_custom = false; /* * Check for custom keywords if a metadata index key is provided. */ if ( ! empty( $md_key ) && 'none' !== $md_key ) { // $md_key can be a string or array. if ( ! empty( $mod[ 'obj' ] ) && $mod[ 'id' ] ) { // Just in case. $keywords = $mod[ 'obj' ]->get_options_multi( $mod[ 'id' ], $md_key ); if ( ! empty( $keywords ) ) { $is_custom = false; if ( $this->p->debug->enabled ) { $this->p->debug->log( 'custom keywords = ' . $keywords ); } } } } /* * If there's no custom keywords, then go ahead and generate the keywords value. */ if ( empty( $keywords ) ) { $tags = $this->get_tag_names( $mod ); if ( ! empty( $tags ) ) { $keywords = SucomUtil::array_to_keywords( $tags ); // Returns a comma delimited text string. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'keywords = ' . $keywords ); } } } return apply_filters( 'wpsso_keywords', $keywords, $mod, $md_key ); } /* * Returns a space delimited text string of hashtags. */ public function get_hashtags( array $mod, $num_hashtags = false ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $hashtags = apply_filters( 'wpsso_hashtags_seed', '', $mod, $num_hashtags ); if ( ! empty( $hashtags ) ) { // Seed hashtags returned. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'hashtags seed = ' . $hashtags ); } } elseif ( false !== $num_hashtags ) { if ( true === $num_hashtags ) { $num_hashtags = $this->p->options[ 'og_desc_hashtags' ]; } if ( is_numeric( $num_hashtags ) && $num_hashtags >= 1 ) { $tags = $this->get_tag_names( $mod ); $tags = array_slice( $tags, 0, $num_hashtags ); if ( ! empty( $tags ) ) { $hashtags = SucomUtil::array_to_hashtags( $tags ); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'hashtags = ' . $hashtags ); } } } } return apply_filters( 'wpsso_hashtags', $hashtags, $mod, $num_hashtags ); } /* * Returns an array of post tags. */ public function get_tag_names( array $mod ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } /* * See WpssoIntegEcomWooCommerce->filter_tag_names_seed(). */ $tags = apply_filters( 'wpsso_tag_names_seed', null, $mod ); if ( ! is_array( $tags ) ) { if ( $mod[ 'is_post' ] ) { if ( 'post' === $mod[ 'post_type' ] ) { $taxonomy = 'post_tag'; } elseif ( 'page' === $mod[ 'post_type' ] && ! empty( $this->p->options[ 'plugin_page_tags' ] ) ) { $taxonomy = SucomUtil::get_const( 'WPSSO_PAGE_TAG_TAXONOMY' ); } else $taxonomy = ''; if ( ! empty( $mod[ 'post_type' ] ) && is_string( $mod[ 'post_type' ] ) ) { // Not empty string. $filter_name = SucomUtil::sanitize_hookname( 'wpsso_' . $mod[ 'post_type' ] . '_tag_taxonomy' ); $taxonomy = apply_filters( $filter_name, $taxonomy, $mod ); if ( ! empty( $taxonomy ) ) { $tags = wp_get_post_terms( $mod[ 'id' ], $taxonomy, $args = array( 'fields' => 'names' ) ); } unset( $filter_name, $taxonomy ); } } } $tags = is_array( $tags ) ? array_unique( $tags ) : array(); $tags = apply_filters( 'wpsso_tag_names', $tags, $mod ); if ( $this->p->debug->enabled ) { $this->p->debug->log_arr( 'tags', $tags ); } return $tags; } /* * Includes parent names in the term title by default, unless the $title_sep value is false. * * See WpssoUtilInline->replace_callback(). * See WpssoFaqShortcodeFaq->do_shortcode(). */ public function get_term_title( $term_id = 0, $title_sep = null ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $term_obj = SucomUtilWP::get_term_object( $term_id ); $term_id = isset( $term_obj->term_id ) ? $term_obj->term_id : 0; $title_text = isset( $term_obj->name ) ? $term_obj->name : ''; $title_sep = $this->maybe_get_title_sep( $title_sep ); // Returns default title separator (decoded) if not provided. /* * If we have a title separator and a parent, then redefine the title text with the parent list. */ if ( ! empty( $title_sep ) ) { if ( ! empty( $term_obj->parent ) ) { $term_parents = get_term_parents_list( $term_id, $term_obj->taxonomy, $args = array( 'format' => 'name', // Use term names or slugs for display. 'separator' => ' ' . $title_sep . ' ', // Separator for between the terms. 'link' => false, // Whether to format as a link. 'inclusive' => true, // Include the term to get the parents for. ) ); if ( $term_parents && ! is_wp_error( $term_parents ) ) { /* * Trim excess separator. */ $title_text = preg_replace( '/ *' . preg_quote( $title_sep, '/' ) . ' *$/', '', $term_parents ); } } } return apply_filters( 'wpsso_term_title', $title_text, $term_id, $title_sep ); } /* * Returns an empty or formatted string (number with minutes). */ public function get_reading_time( array $mod ) { $reading_mins = $this->get_reading_mins( $mod ); return $this->fmt_reading_mins( $reading_mins ); } public function get_reading_mins( array $mod ) { $content = $this->get_the_content( $mod ); $words_per_min = WPSSO_READING_WORDS_PER_MIN; $reading_mins = null; if ( $mod[ 'obj' ] && $mod[ 'id' ] ) { // Just in case. $reading_mins = $mod[ 'obj' ]->get_options( $mod[ 'id' ], 'schema_reading_mins' ); } if ( null === $reading_mins ) { // Default value or no custom value. $reading_mins = round( str_word_count( wp_strip_all_tags( $content ) ) / $words_per_min ); } return $reading_mins; } public function fmt_reading_mins( $reading_mins ) { return $reading_mins ? sprintf( _n( '%s minute', '%s minutes', $reading_mins, 'wpsso' ), $reading_mins ) : ''; } /* * Add a separator and a value to the left/right hand side of the title. */ private function add_title_part( &$title, $title_sep, $part, $hand = 'right' ) { if ( $part ) { // Adding an empty $part would leave a hanging separator. if ( $title ) { if ( 'left' === $hand ) { $title = $part . ' ' . trim( $title_sep . ' ' . $title ); } else $title = trim( $title . ' ' . $title_sep ) . ' ' . $part; } else $title = $part; // We have a part, but no title. } return trim( $title ); } /* * Public method to sanitize arguments or modify values for get_title(), get_description(), etc. * * Returns default ellipsis (decoded) if not provided (ie. $ellipsis = null). */ private function maybe_get_ellipsis( $ellipsis = null ) { if ( null === $ellipsis ) { $ellipsis = html_entity_decode( $this->p->options[ 'og_ellipsis' ], ENT_QUOTES, $this->charset ); } return $ellipsis; } /* * Public method to sanitize arguments or modify values for get_title(), get_description(), etc. * * Returns default title separator (decoded) if not provided (ie. $title_sep = null). */ private function maybe_get_title_sep( $title_sep = null ) { if ( null === $title_sep ) { $title_sep = html_entity_decode( $this->p->options[ 'og_title_sep' ], ENT_QUOTES, $this->charset ); } return $title_sep; } /* * Public method to sanitize arguments or modify values for get_title(), get_description(), etc. */ private function maybe_get_opt_multi( $mod, $md_key ) { if ( ! empty( $md_key ) && 'none' !== $md_key ) { // Make sure we have something to work with. if ( ! empty( $mod[ 'obj' ] ) && $mod[ 'id' ] ) { // Just in case. return $mod[ 'obj' ]->get_options_multi( $mod[ 'id' ], $md_key ); } } return null; } /* * Private method to sanitize arguments or modify values for get_title(), get_description(), etc. * * Returns an array of metadata keys (can be empty). * * $md_key = true | false | string | array */ private function sanitize_md_key( $md_key, $def_key = '' ) { if ( false === $md_key || 'none' === $md_key || '' === $md_key ) { // Nothing to do. return array(); } elseif ( true === $md_key || null === $md_key || $md_key === $def_key ) { // Return the default key array. return WpssoConfig::get_md_keys_fallback( $def_key ); } if ( ! is_array( $md_key ) ) { $md_key = WpssoConfig::get_md_keys_fallback( $md_key ); } foreach ( WpssoConfig::get_md_keys_fallback( $def_key ) as $key ) { $md_key[] = $key; } $md_key = array_unique( $md_key ); // Just in case. return $md_key; } /* * Private method to sanitize arguments or modify values for get_title(), get_description(), etc. * * Return 0 by default. */ private function sanitize_max_len( $max_len ) { if ( is_numeric( $max_len ) ) { return (int) $max_len; } elseif ( is_array( $max_len ) && isset( $max_len[ 'max' ] ) ) { return (int) $max_len[ 'max' ]; } elseif ( is_string( $max_len ) ) { $input_limits = WpssoConfig::get_input_limits( $max_len ); // Uses a local cache. if ( ! empty( $input_limits[ 'max' ] ) ) { return (int) $input_limits[ 'max' ]; } } return 0; } } }