, CodeTRAX.org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * The NOTICE file contains additional licensing and copyright information. */ /** * Basic Metadata * * Module containing functions related to Basic Metadata */ // Prevent direct access to this file. if ( ! defined( 'ABSPATH' ) ) { header( 'HTTP/1.0 403 Forbidden' ); echo 'This file should not be accessed directly!'; exit; // Exit if accessed directly } /** * Generates basic metadata for the head area. * */ function amt_add_basic_metadata_head( $post, $attachments, $embedded_media, $options ) { if ( apply_filters('amt_exclude_basic_metadata', false) ) { return array(); } $do_description = (($options["auto_description"] == "1") ? true : false ); $do_keywords = (($options["auto_keywords"] == "1") ? true : false ); $do_noodp_description = (($options["noodp_description"] == "1") ? true : false ); // Array to store metadata $metadata_arr = array(); // Pre-processing // Store hreflang links in array $hreflang_links_arr = array(); // Store base robots options $robots_options = array(); if ( $do_noodp_description && ( is_front_page() || is_singular() || is_category() || is_tag() || is_tax() || is_author() ) ) { // Add NOODP on posts and pages $robots_options[] = 'noodp'; $robots_options[] = 'noydir'; } // Store full meta tags (site wide and post specific) // Add site wide meta tags $full_metatags_as_string = ''; if ( ! empty( $options['site_wide_meta'] ) ) { $full_metatags_for_site = html_entity_decode( stripslashes( $options['site_wide_meta'] ) ); $full_metatags_as_string .= apply_filters('amt_full_metatags_site', $full_metatags_for_site); $full_metatags_as_string .= PHP_EOL; } // Full meta tags if ( is_singular() || amt_is_static_front_page() || amt_is_static_home() ) { // per post full meta tags $full_metatags_for_content = amt_get_post_meta_full_metatags( $post->ID ); $full_metatags_for_content = html_entity_decode( stripslashes( $full_metatags_for_content ) ); $full_metatags_as_string .= apply_filters('amt_full_metatags_post', $full_metatags_for_content); } elseif ( is_category() || is_tag() || is_tax() ) { // Term specific full meta tags ($post is a term object) $full_metatags_for_term = amt_get_term_meta_full_metatags( $post->term_id ); $full_metatags_for_term = html_entity_decode( stripslashes( $full_metatags_for_term ) ); $full_metatags_as_string .= apply_filters('amt_full_metatags_term', $full_metatags_for_term); } elseif ( is_author() ) { // User specific full meta tags ($post is a user object) $full_metatags_for_user = amt_get_user_meta_full_metatags( $post->ID ); $full_metatags_for_user = html_entity_decode( stripslashes( $full_metatags_for_user ) ); $full_metatags_as_string .= apply_filters('amt_full_metatags_term', $full_metatags_for_user); } // Sanitize //$full_metatags_as_string = esc_textarea( wp_kses( $full_metatags_as_string, amt_get_allowed_html_kses() ) ); $full_metatags_as_string = wp_kses( $full_metatags_as_string, amt_get_allowed_html_kses() ); // Make array of full meta tags $full_meta_tags = preg_split('#\R#', $full_metatags_as_string, NULL, PREG_SPLIT_NO_EMPTY); // Process if ( apply_filters('amt_full_metatags_processor_enable', true) ) { // Store processed meta tags here $processed_full_meta_tags = array(); // Field substitutions currently take place only on content pages. // TODO: See if this can be expanded to terms, authors. // Store the post's custom fields $custom_fields = null; // Store the post object's custom fields. // if ( is_singular() || amt_is_static_front_page() || amt_is_static_home() ) { // Get an array of all custom fields names of the post object. $custom_fields = get_post_custom_keys( $post->ID ); } // Iterate over full meta tags foreach ($full_meta_tags as $single_meta_tag) { // Note: Field value substitutions take place first, outside the elseif clauses. // Process substitutions of special notation with data from Custom Fields // Supported special notation: // [field=Field Name] // Notes: // - 'Field Name' is the name of custom field. // - If the custom field with name 'Field Name' does not exist, the meta tag // that contains it is omitted. // - If the value of the field is an empty string, then the substitution // takes place normally. // // The regex pattern fo our special notation. $special_notation_pattern = '#(?:\[field\=)([^\]]+)(?:\])#'; // The following covers content pages, as $custom_fields is only set on content pages. See above. if ( ! empty( $custom_fields ) && isset($post->ID) ) { // This also assumes that we have a post object since custom fields // are set only on content pages, otherwise it is null. // Check for special notation if ( preg_match($special_notation_pattern, $single_meta_tag, $matches) ) { //var_dump($matches); // If the field name of the special notation does not match // any custom field name, omit the meta tag as per the rules above. if ( ! in_array($matches[1], $custom_fields) ) { continue; } // Since there is special notation and the field name from the special // notation exists in the $custom_fields array, iterate over the available // custom fields and perform the substitutions. foreach ( $custom_fields as $custom_field ) { // Check if it matches the field name of the special notation if ( $custom_field == $matches[1] ) { // Fetch the custom field's value $field_value = get_post_meta( $post->ID, $custom_field, true ); // Sanitize value // TODO: this can be a problem depending on the value and the used sanitization function. $field_value = esc_attr( sanitize_text_field( $field_value ) ); // Perform the substitution even if the the value is an empty string as per the rules above $single_meta_tag = str_replace( sprintf('[field=%s]', $custom_field), $field_value, $single_meta_tag); } } } } else { // In any other case, just remove the meta tags which contain the special notation. if ( preg_match($special_notation_pattern, $single_meta_tag, $tmp) ) { continue; } } // Process the PAGEINFO variable. // If the current page is the 1st page of any archive or of multipage content, // PAGEINFO is just stripped. For subsequent pages of archives or multipage // content, PAGEINFO is replaced with page based path (page/N/ for archives or N/ for multipage content) // // For paginated archives or paginated main page with latest posts. $has_paging_info = false; if ( is_paged() ) { $paged = get_query_var( 'paged' ); // paged if ( $paged && $paged >= 2 ) { $single_meta_tag = str_replace('PAGEINFO', 'page/' . $paged . '/', $single_meta_tag); $has_paging_info = true; } // For a Post or Page that has been divided into pages using the QuickTag } else { $paged = get_query_var( 'page' ); // page if ( $paged && $paged >= 2 ) { $single_meta_tag = str_replace('PAGEINFO', $paged . '/', $single_meta_tag); $has_paging_info = true; } } // If this is not paged, strip PAGEINFO if ( $has_paging_info === false ) { $single_meta_tag = str_replace('PAGEINFO', '', $single_meta_tag); } // Process custom canonical link // If a rel="canonical" meta tags exists, we deactivate WordPress' 'rel_canonical' action, // Since it is assumed that a custom canonical link has been added. //if ( preg_match( '# rel="canonical" #', $post_full_meta_tags, $tmp ) ) { if ( strpos($single_meta_tag, ' rel="canonical" ') !== false ) { // Remove default WordPress action remove_action('wp_head', 'rel_canonical'); } // Process robots meta tags. // Multiple robots meta tags may exist. Here we collect the options. elseif ( strpos($single_meta_tag, ' name="robots" ') !== false ) { if ( preg_match( '# content="([^"]+)" #', $single_meta_tag, $matches ) ) { $tmp_robots_opts = explode(',', $matches[1]); foreach ($tmp_robots_opts as $single_robots_option) { $single_robots_option_cleaned = strtolower(trim($single_robots_option)); if ( ! empty($single_robots_option_cleaned) ) { $robots_options[] = $single_robots_option_cleaned; } } } // We simply collect options. Do not add any robots meta tags to the processed meta tags array. continue; } // Process hreflang links. // Here we just collect them and let them be process later below at the special section. elseif ( strpos($single_meta_tag, ' hreflang="') !== false ) { // Simply add to the hreflang links array for later processing $hreflang_links_arr[] = $single_meta_tag; // We simply collect hreflang links for later processing. Do not add them to the processed meta tags array. continue; } // If we have reached here, add the meta tags to the array with processed meta tags. $processed_full_meta_tags[] = $single_meta_tag; } } else { // Full meta tags processor not enabled $processed_full_meta_tags = $full_meta_tags; } //var_dump($full_meta_tags); //var_dump($processed_full_meta_tags); // Add Meta Tags // Add a robots meta tag if robots options exist. // Backwards compatible filter. TODO: This is deprecated. Needs to be deleted after a while. $old_options_as_string = apply_filters( 'amt_robots_data', '' ); if ( ! empty($old_options_as_string) ) { foreach ( explode(',', $old_options_as_string) as $single_robots_option) { $single_robots_option_cleaned = strtolower(trim($single_robots_option)); if ( ! empty($single_robots_option_cleaned) ) { $robots_options[] = $single_robots_option_cleaned; } } } // Add robot_options filtering $robots_options = apply_filters( 'amt_robots_options', $robots_options ); if ( version_compare( PHP_VERSION, '5.3', '<' ) ) { // The flag is not supported $robots_options = array_unique( $robots_options ); } else { $robots_options = array_unique( $robots_options, SORT_STRING ); } if ( ! empty( $robots_options ) ) { $metadata_arr['basic:robots'] = ''; } // Add full meta tags // Merge meta tags $processed_full_meta_tags = apply_filters('amt_full_metatags_processed', $processed_full_meta_tags); if ( ! empty($processed_full_meta_tags) ) { $metadata_arr = array_merge($metadata_arr, $processed_full_meta_tags); } // Add copyright link // On every page print the copyright head link $copyright_url = amt_get_site_copyright_url($options); //if ( empty($copyright_url)) { // $copyright_url = trailingslashit( get_bloginfo('url') ); //} if ( ! empty($copyright_url) ) { $metadata_arr['basic:copyright'] = ''; } // hreflang link element // This section also expects an array of extra hreflang links that may have // been collected from the full meta tags boxes. if ( $options['generate_hreflang_links'] == '1' ) { if ( is_singular() ) { $locale = amt_get_language_content($options, $post); $hreflang = amt_get_the_hreflang($locale, $options); $hreflang_url = amt_get_permalink_for_multipage($post); } else { $locale = amt_get_language_site($options); $hreflang = amt_get_the_hreflang($locale, $options); $hreflang_url = ''; if ( amt_is_default_front_page() ) { $hreflang_url = trailingslashit( get_bloginfo('url') ); } elseif ( is_category() || is_tag() || is_tax() ) { // $post is a term object $hreflang_url = get_term_link($post); } elseif ( is_author() ) { // $post is an author object $hreflang_url = get_author_posts_url( $post->ID ); } elseif ( is_year() ) { $archive_year = get_the_time('Y'); $hreflang_url = get_year_link($archive_year); } elseif ( is_month() ) { $archive_year = get_the_time('Y'); $archive_month = get_the_time('m'); $hreflang_url = get_month_link($archive_year, $archive_month); } elseif ( is_day() ) { $archive_year = get_the_time('Y'); $archive_month = get_the_time('m'); $archive_day = get_the_time('d'); $hreflang_url = get_day_link($archive_year, $archive_month, $archive_day); } // If paged information is available if ( is_paged() ) { //$hreflang_url = trailingslashit( $hreflang_url ) . get_query_var('paged') . '/'; $hreflang_url = get_pagenum_link( get_query_var('paged') ); } } // hreflang links array $hreflang_arr = array(); // Add link element if ( ! empty($hreflang) && ! empty($hreflang_url) ) { $hreflang_arr[] = ''; } // Add extra hreflang links that have been collected from the full meta tags boxes if ( ! empty($hreflang_links_arr) ) { $hreflang_arr = array_merge($hreflang_arr, $hreflang_links_arr); } // Allow filtering of the hreflang array $hreflang_arr = apply_filters( 'amt_hreflang_links', $hreflang_arr ); // Add to to metadata array foreach ( $hreflang_arr as $hreflang_link ) { if ( preg_match('# hreflang="([^"]+)" #', $hreflang_link, $matches) ) { $metadata_arr['basic:hreflang:' . $matches[1]] = $hreflang_link; } } } // Basic Meta Tags // Custom content override if ( amt_is_custom($post, $options) ) { // Return metadata with: // add_filter( 'amt_custom_metadata_basic', 'my_function', 10, 5 ); // Return an array of meta tags. Array item format: ['key_can_be_whatever'] = '' $metadata_arr = apply_filters( 'amt_custom_metadata_basic', $metadata_arr, $post, $options, $attachments, $embedded_media ); // Default front page displaying latest posts } elseif ( amt_is_default_front_page() ) { // Description and Keywords from the Add-Meta-Tags settings override // default behaviour. // Description if ($do_description) { // Use the site description from the Add-Meta-Tags settings. // Fall back to the blog description. $site_description = amt_get_site_description($options); if ( empty($site_description ) ) { // Alternatively, use the blog description // Here we sanitize the provided description for safety $site_description = sanitize_text_field( amt_sanitize_description( get_bloginfo('description') ) ); } // If we have a description, use it in the description meta-tag of the front page if ( ! empty( $site_description ) ) { // Note: Contains multipage information through amt_process_paged() $metadata_arr['basic:description'] = ''; } } // Keywords if ($do_keywords) { // Use the site keywords from the Add-Meta-Tags settings. // Fall back to the blog categories. $site_keywords = amt_get_site_keywords($options); if ( empty( $site_keywords ) ) { // Alternatively, use the blog categories // Here we sanitize the provided keywords for safety $site_keywords = sanitize_text_field( amt_sanitize_keywords( amt_get_all_categories() ) ); } // If we have keywords, use them in the keywords meta-tag of the front page if ( ! empty( $site_keywords ) ) { $metadata_arr['basic:keywords'] = ''; } } // Attachments } elseif ( is_attachment() ) { // has to be before is_singular() since is_singular() is true for attachments. // Description if ($do_description) { $description = amt_get_content_description($post, $auto=$do_description); if ( ! empty($description ) ) { // Note: Contains multipage information through amt_process_paged() $metadata_arr['basic:description'] = ''; } } // No keywords // Content pages and static pages used as "front page" and "posts page" // This also supports products via is_singular() } elseif ( is_singular() || amt_is_static_front_page() || amt_is_static_home() ) { // Description if ($do_description) { $description = amt_get_content_description($post, $auto=$do_description); if ( ! empty( $description ) ) { // Note: Contains multipage information through amt_process_paged() $metadata_arr['basic:description'] = ''; } } // Keywords if ($do_keywords) { $keywords = amt_get_content_keywords($post, $auto=$do_keywords); if ( ! empty( $keywords ) ) { $metadata_arr['basic:keywords'] = ''; // Static Posts Index Page // If no keywords have been set in the metabox and this is the static page, // which displayes the latest posts, use the categories of the posts in the loop. } elseif ( amt_is_static_home() ) { // Here we sanitize the provided keywords for safety $cats_from_loop = sanitize_text_field( amt_sanitize_keywords( implode( ', ', amt_get_categories_from_loop() ) ) ); if ( ! empty( $cats_from_loop ) ) { $metadata_arr['basic:keywords'] = ''; } } } // 'news_keywords' $newskeywords = amt_get_post_meta_newskeywords( $post->ID ); if ( ! empty( $newskeywords ) ) { $metadata_arr['basic:news_keywords'] = ''; } // Category based archives } elseif ( is_category() ) { if ($do_description) { // If set, the description of the category is used in the 'description' metatag. // Otherwise, a generic description is used. // Here we sanitize the provided description for safety $description_content = sanitize_text_field( amt_sanitize_description( category_description() ) ); // Note: Contains multipage information through amt_process_paged() if ( empty( $description_content ) ) { // Add a filtered generic description. $generic_description = apply_filters( 'amt_generic_description_category_archive', __('Content filed under the %s category.', 'add-meta-tags') ); $generic_description = sprintf( $generic_description, single_cat_title( $prefix='', $display=false ) ); $metadata_arr['basic:description'] = ''; } else { $metadata_arr['basic:description'] = ''; } } if ($do_keywords) { // The category name alone is included in the 'keywords' metatag // Here we sanitize the provided keywords for safety $cur_cat_name = sanitize_text_field( amt_sanitize_keywords( single_cat_title($prefix = '', $display = false ) ) ); if ( ! empty($cur_cat_name) ) { $metadata_arr['basic:keywords'] = ''; } } } elseif ( is_tag() ) { if ($do_description) { // If set, the description of the tag is used in the 'description' metatag. // Otherwise, a generic description is used. // Here we sanitize the provided description for safety $description_content = sanitize_text_field( amt_sanitize_description( tag_description() ) ); // Note: Contains multipage information through amt_process_paged() if ( empty( $description_content ) ) { // Add a filtered generic description. $generic_description = apply_filters( 'amt_generic_description_tag_archive', __('Content tagged with %s.', 'add-meta-tags') ); $generic_description = sprintf( $generic_description, single_tag_title( $prefix='', $display=false ) ); $metadata_arr['basic:description'] = ''; } else { $metadata_arr['basic:description'] = ''; } } if ($do_keywords) { // The tag name alone is included in the 'keywords' metatag // Here we sanitize the provided keywords for safety $cur_tag_name = sanitize_text_field( amt_sanitize_keywords( single_tag_title($prefix = '', $display = false ) ) ); if ( ! empty($cur_tag_name) ) { $metadata_arr['basic:keywords'] = ''; } } // Custom taxonomies - Should be after is_category() and is_tag(), as it would catch those taxonomies as well. // This also supports product groups via is_tax(). Only product groups that are WordPress custom taxonomies are supported. } elseif ( is_tax() ) { // Taxonomy term object. // When viewing taxonomy archives, the $post object is the taxonomy term object. Check with: var_dump($post); $tax_term_object = $post; //var_dump($tax_term_object); if ($do_description) { // If set, the description of the custom taxonomy term is used in the 'description' metatag. // Otherwise, a generic description is used. // Here we sanitize the provided description for safety $description_content = sanitize_text_field( amt_sanitize_description( term_description( $tax_term_object->term_id, $tax_term_object->taxonomy ) ) ); // Note: Contains multipage information through amt_process_paged() if ( empty( $description_content ) ) { // Add a filtered generic description. // Construct the filter name. Template: ``amt_generic_description_TAXONOMYSLUG_archive`` $taxonomy_description_filter_name = sprintf( 'amt_generic_description_%s_archive', $tax_term_object->taxonomy); // var_dump($taxonomy_description_filter_name); $generic_description = apply_filters( $taxonomy_description_filter_name, __('Content filed under the %s taxonomy.', 'add-meta-tags') ); $generic_description = sprintf( $generic_description, single_term_title( $prefix='', $display=false ) ); $metadata_arr['basic:description'] = ''; } else { $metadata_arr['basic:description'] = ''; } } if ($do_keywords) { // The taxonomy term name alone is included in the 'keywords' metatag. // Here we sanitize the provided keywords for safety. $cur_tax_term_name = sanitize_text_field( amt_sanitize_keywords( single_term_title( $prefix = '', $display = false ) ) ); if ( ! empty($cur_tax_term_name) ) { $metadata_arr['basic:keywords'] = ''; } } } elseif ( is_author() ) { // Author object // NOTE: Inside the author archives `$post->post_author` does not contain the author object. // In this case the $post (get_queried_object()) contains the author object itself. // We also can get the author object with the following code. Slug is what WP uses to construct urls. // $author = get_user_by( 'slug', get_query_var( 'author_name' ) ); // Also, ``get_the_author_meta('....', $author)`` returns nothing under author archives. // Access user meta with: $author->description, $author->user_email, etc // $author = get_queried_object(); $author = $post; // If a bio has been set in the user profile, use it in the description metatag of the // first page of the author archive *ONLY*. The other pages of the author archive use a generic description. // This happens because the 1st page of the author archive is considered the profile page // by the other metadata modules. // Otherwise use a generic meta tag. if ($do_description) { // Here we sanitize the provided description for safety $author_description = sanitize_text_field( amt_sanitize_description( $author->description ) ); if ( empty( $author_description ) || is_paged() ) { // Note: Contains multipage information through amt_process_paged() // Add a filtered generic description. $generic_description = apply_filters( 'amt_generic_description_author_archive', __('Content published by %s.', 'add-meta-tags') ); $generic_description = sprintf( $generic_description, $author->display_name ); $metadata_arr['basic:description'] = ''; } else { $metadata_arr['basic:description'] = ''; } } // For the keywords metatag use the categories of the posts the author has written and are displayed in the current page. if ($do_keywords) { // Here we sanitize the provided keywords for safety $cats_from_loop = sanitize_text_field( amt_sanitize_keywords( implode( ', ', amt_get_categories_from_loop() ) ) ); if ( ! empty( $cats_from_loop ) ) { $metadata_arr['basic:keywords'] = ''; } } // Custom Post Type Archive } elseif ( is_post_type_archive() ) { // Custom post type object. // When viewing custom post type archives, the $post object is the custom post type object. Check with: var_dump($post); $post_type_object = $post; //var_dump($post_type_object); if ($do_description) { // Description // Note: Contains multipage information through amt_process_paged() // Add a filtered generic description. // Construct the filter name. Template: ``amt_generic_description_posttype_POSTTYPESLUG_archive`` $custom_post_type_description_filter_name = sprintf( 'amt_generic_description_posttype_%s_archive', $post_type_object->name); // var_dump($custom_post_type_description_filter_name); // Generic description $generic_description = apply_filters( $custom_post_type_description_filter_name, __('%s archive.', 'add-meta-tags') ); // Final generic description $generic_description = sprintf( $generic_description, post_type_archive_title( $prefix='', $display=false ) ); $metadata_arr['basic:description'] = ''; } // For the keywords metatag use the categories of the posts that are listed in the current page. if ($do_keywords) { // Here we sanitize the provided keywords for safety $cats_from_loop = sanitize_text_field( amt_sanitize_keywords( implode( ', ', amt_get_categories_from_loop() ) ) ); if ( ! empty( $cats_from_loop ) ) { $metadata_arr['basic:keywords'] = ''; } } } // Filtering of the generated basic metadata $metadata_arr = apply_filters( 'amt_basic_metadata_head', $metadata_arr ); return $metadata_arr; }