true, 'div' => true, 'section' => true ); /** * Override default comments template with a Facebook-specific comments template. * * @since 1.3 * * @return string file path to plugin comments template PHP */ public static function comments_template() { return dirname( __FILE__ ) . '/comments.php'; } /** * Is Comments Box enabled for the current post type? * * @since 1.1.7 * * @param stdClass|WP_Post $post WordPress post object * @return bool true if Comments Box enabled */ public static function comments_enabled_for_post_type( $post = null ) { $post_type = get_post_type( $post ); if ( ! $post_type ) return false; $features = get_option( 'facebook_' . $post_type . '_features' ); if ( is_array( $features ) && isset( $features['comments'] ) ) return true; return false; } /** * Turn on comments open if Comments Box enabled for the post type. * * A Comments Box always solicits new comments * * @since 1.1.7 * * @see comments_open() * @param bool $open comment status == 'open' * @param int $post_id WordPress post identifier * @return bool are comments open? */ public static function comments_open_filter( $open, $post_id = null ) { if ( ! $open && $post_id ) { $_post = get_post( $post_id ); if ( $_post && self::comments_enabled_for_post_type( $_post ) ) return true; } return $open; } /** * Kill attempts to comment on a post managed by Facebook Comments Box. * * @since 1.3.1 * * @param int post_id post identifer * @return void */ public static function pre_comment_on_post( $post_id ) { if ( ! $post_id ) return; $_post = get_post( $post_id ); if ( $_post && self::comments_enabled_for_post_type( $_post ) ) { // match comments closed message and behavior do_action( 'comment_closed', $post_id ); wp_die( __('Sorry, comments are closed for this item.') ); } } /** * Add Facebook comment count to the built-in WordPress comment count. * * @since 1.4 * * @see get_comments_number() * @global \Facebook_Loader $facebook_loader test for app access token needed to request comments count from Facebook servers * @param int $count The number of WordPress comments for the post * @param int $post_id WordPress Post ID * @return int The number of WordPress comments + Facebook comments for the post */ public static function get_comments_number_filter( $count, $post_id ) { global $facebook_loader; if ( ! ( isset( $facebook_loader ) && $facebook_loader->app_access_token_exists() ) ) return $count; if ( ! $post_id ) return $count; $post = get_post( $post_id ); if ( ! ( $post && self::comments_enabled_for_post_type( $post ) ) ) return $count; $count = absint( $count ); $cache_key = 'fb_comments_count_' . $post_id; $fb_count = get_transient( $cache_key ); if ( $fb_count === false ) { // allow cached count of 0 $fb_count = 0; $url = apply_filters( 'facebook_rel_canonical', get_permalink( $post_id ) ); if ( ! $url ) return $count; if ( ! class_exists( 'Facebook_WP_Extend' ) ) require_once( $facebook_loader->plugin_directory . 'includes/facebook-php-sdk/class-facebook-wp.php' ); try { // request a summary of all comments for the object represented by the current post URL // minimize data response by limiting to one comment with a single field: id $comments = Facebook_WP_Extend::graph_api_with_app_access_token( 'comments', 'GET', array( 'id' => $url, 'filter' => 'stream', 'summary' => true, 'limit' => 1, 'fields' => 'id' ) ); } catch ( WP_FacebookApiException $e ) { return $count; } if ( isset( $comments['summary'] ) && isset( $comments['summary']['total_count'] ) ) { $fb_count = absint( $comments['summary']['total_count'] ); set_transient( $cache_key, $fb_count, 60*15 ); } } if ( $fb_count ) $count += $fb_count; return $count; } /** * Overrides text displayed with comments number, inserting Facebook XFBML to be replaced by a number with the Facebook SDK for JavaScript. * * @since 1.1 * * @global stdClass|WP_Post WordPress post object * @param string $output number of comments text * @param number of comments returned by get_comments_number() * @return string passed output or XFBML string */ public static function comments_number_filter( $output, $number = null ) { global $post; if ( isset( $post ) && self::comments_enabled_for_post_type( $post ) ) return self::comments_count_xfbml(); return $output; } /** * Build a XFBML string to place on the page for interpretation by the Facebook SDK for JavaScript at runtime. * * @since 1.1 * * @return string XFBML string or empty string if no post permalink context or publisher short-circuits via filter */ public static function comments_count_xfbml() { // duplicate_hook $url = esc_url( apply_filters( 'facebook_rel_canonical', get_permalink() ), array( 'http', 'https' ) ); if ( $url ) { // match comments_number() text builder, adding XFBML element instead of a number return str_replace( '%', '', apply_filters( 'facebook_comments_number_more', __( '% Comments' ) ) ); } return ''; } /** * Remove the WordPress comments menu bar item, replacing with a Facebook comments link. * * Check if Facebook comments enabled and if the current user might be able to view a comments edit screen on Facebook. * * @since 1.1 * * @see WP_Admin_Bar->add_menus() * @return void */ public static function admin_bar_menu() { global $facebook_loader; if ( is_network_admin() && is_user_admin() ) return; // use moderate_comments capability as a local proxy for accounts that might be granted moderate comments permissions for the Facebook application if the application administrator fully setup the app // technically the WordPress menu item is added for users with 'edit_posts' due to the permissions of the destination page but we'll check for the specific comments permission instead // TODO: check if Facebook data stored for current user, check if Facebook user is moderator if ( ! current_user_can( 'moderate_comments' ) ) return; if ( ! class_exists( 'Facebook_User' ) ) require_once( dirname( dirname( __FILE__ ) ) . '/facebook-user.php' ); $current_user = wp_get_current_user(); $facebook_user_data = Facebook_User::get_user_meta( $current_user->ID, 'fb_data', true ); if ( ! ( is_array( $facebook_user_data ) && isset( $facebook_user_data['fb_uid'] ) ) ) return; // swap only. don't add a menu item if none existed if ( remove_action( 'admin_bar_menu', 'wp_admin_bar_comments_menu', 60 ) ) { add_action( 'admin_bar_menu', array( 'Facebook_Comments', 'admin_bar_add_comments_menu' ), 60 ); } } /** * Add a Facebook Comments menu item to the WordPress admin bar * * @since 1.1 * * @global \Facebook_Loader $facebook_loader access Facebook application identifier * @see wp_admin_bar_comments_menu() * @param WP_Admin_Bar $wp_admin_bar WordPress admin bar to modify * @return void */ public static function admin_bar_add_comments_menu( $wp_admin_bar ) { global $facebook_loader; $wp_admin_bar->add_menu( array( 'id' => 'comments', 'title' => '', 'href' => 'https://developers.facebook.com/tools/comments?' . http_build_query( array( 'id' => $facebook_loader->credentials['app_id'] ) ), 'meta' => array( 'target' => '_blank', 'title' => __( 'Moderate Facebook comments', 'facebook' ) ) ) ); } /** * Retrieve a list of comments for the given URL from the Facebook Graph API. * * @since 1.1 * * @link https://developers.facebook.com/docs/reference/api/comment Individual Facebook Comment object * @param string $url absolute URL * @return array list of comments */ public static function get_comments_by_url( $url ) { global $facebook_loader; if ( ! ( isset( $facebook_loader ) && $facebook_loader->app_access_token_exists() && is_string( $url ) && $url ) ) return array(); if ( ! class_exists( 'Facebook_WP_Extend' ) ) require_once( $facebook_loader->plugin_directory . 'includes/facebook-php-sdk/class-facebook-wp.php' ); try { $comments = Facebook_WP_Extend::graph_api_with_app_access_token( 'comments', 'GET', array( 'id' => $url, 'filter' => 'toplevel', 'fields' => 'id,from,created_time,message,comments.fields(id,from,created_time,message)' ) ); } catch ( WP_FacebookApiException $e ) { return array(); } if ( is_array( $comments['data'] ) && ! empty( $comments['data'] ) ) return $comments['data']; else return array(); } /** * Remap Facebook comment fields to WordPress comment object. * * @since 1.1 * * @param array $comment comment array returned from Facebook Graph API * @param int $post_id optional. sets comment_post_ID if present * @return stdClass WordPress style comment object */ public static function to_wp_comment( $comment, $post_id = 0 ) { if ( ! ( is_array( $comment ) && ! empty( $comment ) ) ) return ''; $wp_comment = new stdClass(); $wp_comment->comment_approved = 1; // FB comments map best to pre-approved WP comment $wp_comment->comment_type = ''; // not a pingback or a trackback $wp_comment->user_id = 0; // not a WP user if ( is_int( $post_id ) && $post_id ) $wp_comment->comment_post_ID = $post_id; $wp_comment->comment_ID = $comment['id']; $wp_comment->comment_date = iso8601_to_datetime( $comment['created_time'] ); $wp_comment->comment_date_gmt = iso8601_to_datetime( $comment['created_time'], 'gmt' ); $wp_comment->comment_content = $comment['message']; if ( is_array( $comment['from'] ) ) { if ( isset( $comment['from']['name'] ) ) $wp_comment->comment_author = $comment['from']['name']; if ( isset( $comment['from']['id'] ) ) $wp_comment->comment_author_url = 'https://www.facebook.com/profile.php?' . http_build_query( array( 'id' => $comment['from']['id'] ) ); } return $wp_comment; } /** * Generate HTML for a single Facebook comment. * * @since 1.1 * * @param array $comment Facebook comment data * @param bool include Schema.org comment markup for semantic meaning * @return string comment HTML markup as a
  • */ public static function single_comment( $comment, $schema_org = true ) { if ( ! is_array( $comment ) ) return ''; if ( ! is_bool( $schema_org ) ) $schema_org = true; $li = '
  • $comment['from']['id'] ) ), array( 'http', 'https' ) ) ); if ( $author_url ) { $li .= '>' . esc_html( $author_name ) . ''; } else { if ( $schema_org ) $li .= ' itemprop="creator" itemscope itemtype="http://schema.org/Person"'; $li .= '>' . esc_html( $author_name ); } unset( $author_url ); $li .= ':

    '; } } if ( isset( $comment['created_time'] ) ) { $unix_timestamp = strtotime( $comment['created_time'] ); // strtotime could possibly fail to parse and return false if ( $unix_timestamp ) { $li .= '

    '; } unset( $unix_timestamp ); } $li .= '
    '; if ( isset( $comment['comments'] ) && is_array( $comment['comments']['data'] ) && ! empty( $comment['comments']['data'] ) ) { $children = array(); foreach( $comment['comments']['data'] as $comment_comment ) { $children[] = self::single_comment( $comment_comment, $schema_org ); } if ( ! empty( $children ) ) $li .= '
      ' . implode( '', $children ) . '
    '; unset( $children ); } $li .= '
  • '; return $li; } /** * Possibly generate a nested ol of comments from a comments array. * * @since 1.1 * * @param array $comments comments returned by Facebook * @return string ol.commentlist or empty string */ public static function list_comments( $comments ) { if ( ! is_array( $comments ) ) return ''; $comment_list = array(); /** * Mark up comments in Schema.org structured data * * @since 1.1 * * @param bool Default: true */ $schema_org = apply_filters( 'facebook_comment_schema_org', true ); if ( ! is_bool( $schema_org ) ) $schema_org = true; foreach( $comments as $comment ) { $comment_markup = self::single_comment( $comment, $schema_org ); if ( $comment_markup ) $comment_list[] = $comment_markup; unset( $comment_markup ); } if ( empty( $comment_list ) ) return ''; return '
      ' . implode( '', $comment_list ) . '
    '; } /** * Retrieve a list of comments for the current post permalink from Facebook servers. * * Generate threaded comment markup for the comments. Cache the generated HTML for 15 minutes for speed. * * @since 1.1 * * @global stdClass|WP_Post WordPress post object * @param string $wrapper_element the parent element of the list of comments, if any comments exist. noscript (default), div, or section * @return string list of comments possibly wrapped in a valid wrapper */ public static function comments_markup( $wrapper_element = 'noscript' ) { global $post; if ( ! ( isset( $post ) && empty( $post->post_password ) ) ) return ''; // duplicate_hook $url = apply_filters( 'facebook_rel_canonical', get_permalink( $post->ID ) ); if ( ! $url ) // could happen. kill it early return ''; $cache_key = 'fb_comments_html_' . $post->ID; $html = get_transient( $cache_key ); // false if not found if ( ! is_string( $html ) ) { $comments = self::get_comments_by_url( $url ); if ( empty( $comments ) ) { // store a non-empty result to prevent checking for Facebook comments with every pageload set_transient( $cache_key, ' ', 60*15 ); return ''; } $html = self::list_comments( $comments ); if ( is_string( $html ) && $html ) set_transient( $cache_key, $html, 60*15 ); else $html = ''; } $html = trim( $html ); if ( $html && is_string( $wrapper_element ) && isset( self::$allowed_comment_wrapper_elements[$wrapper_element] ) ) return '<' . $wrapper_element . '>' . $html . ''; return $html; } /** * Generate markup suitable for the Facebook SDK for JavaScript based on site options. * * @since 1.1 * * @param array $options site options for the comments box social plugin * @return string HTML5 div with data attributes for interpretation by the Facebook SDK for JavaScript at runtime */ public static function js_sdk_markup( $options ) { if ( ! is_array( $options ) ) return ''; if ( ! empty( $options['href'] ) ) $options['href'] = apply_filters( 'facebook_rel_canonical', get_permalink() ); if ( ! class_exists( 'Facebook_Comments_Box' ) ) require_once( dirname(__FILE__) . '/class-facebook-comments-box.php' ); $comments_box = Facebook_Comments_Box::fromArray( $options ); if ( $comments_box ) { $comments_box_attributes = array( 'class' => array( 'fb-social-plugin', 'comment-form' ) ); $wp_comment_form_args = apply_filters( 'comment_form_defaults', array( 'source' => 'facebook', 'id_form' => 'commentform' ) ); if ( is_array( $wp_comment_form_args ) && isset( $wp_comment_form_args['id_form'] ) ) $comments_box_attributes['id'] = $wp_comment_form_args['id_form']; unset( $wp_comment_form_args ); $comments_jssdk_div = $comments_box->asHTML( $comments_box_attributes ); if ( $comments_jssdk_div ) return "\n" . $comments_jssdk_div . "\n"; } return ''; } /** * Generate Facebook Comments Box social plugin markup for interpretation by the Facebook JavaScript SDK at runtime. * * Includes a list of existing plugins wrapped in a noscript by default to better reflect full page content to search engines and other noscript users. * * @since 1.1 * * @param array $options chosen options from the Facebook settings page * @param bool $with_noscript include a list of comments in your page markup wrapped in a noscript element. disable if you publish your posts in a non-dynamic environment such as a HTTP small object CDN and would like all content rendered dynamically at runtime * @return string Facebook Comments Box markup for interpretation by the Facebook JavaScript SDK + (optional) noscript markup of existing comments */ public static function render( $options = array(), $with_noscript = true ) { $html = self::js_sdk_markup( $options ); if ( $with_noscript === true ) { $comments_noscript = self::comments_markup( 'noscript' ); if ( $comments_noscript ) $html = "\n" . $comments_noscript . "\n" . $html; } return $html; } /** * Display comments and comments box. * * Existing Facebook comments are wrapped in a noscript for search engine indexing and styling. * * @since 1.3 * * @global stdClass|WP_Post WordPress post object * @return string noscript Facebook comments content and/or Comments Box markup to be interpreted by the Facebook JavaScript SDK */ public static function comments_box() { global $post; $content = ''; if ( ! ( isset( $post ) && isset( $post->ID ) ) ) return $content; $post_id = absint( $post->ID ); if ( ! $post_id ) return $content; $options = get_option( 'facebook_comments' ); if ( ! is_array( $options ) || empty( $options ) ) return $content; /** * Output existing Facebook comments as HTML wrapped in the given HTML element. * * Display a static HTML fallback version of existing comments stored on Facebook servers for SEO purposes. Wrap in a