p =& $plugin; if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $this->p->util->add_plugin_filters( $this, array( 'plugin_image_sizes' => 1, ) ); } public function filter_plugin_image_sizes( array $sizes ) { $sizes[ 'tc_sum' ] = array( // Option prefix. 'name' => 'tc-summary', 'label_transl' => _x( 'X (Twitter) Summary Card', 'option label', 'wpsso' ), ); $sizes[ 'tc_lrg' ] = array( // Option prefix. 'name' => 'tc-lrgimg', 'label_transl' => _x( 'X (Twitter) Summary Card Large Image', 'option label', 'wpsso' ), ); return $sizes; } /* * Use reference for $mt_og argument to allow unset of existing twitter meta tags. */ public function get_array( array $mod, array $mt_og = array(), $author_id = false ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $mt_tc = apply_filters( 'wpsso_tc_seed', array(), $mod ); /* * The twitter:domain is used in place of the 'view on web' text. */ if ( ! isset( $mt_tc[ 'twitter:domain' ] ) && ! empty( $mt_og[ 'og:url' ] ) ) { $mt_tc[ 'twitter:domain' ] = preg_replace( '/^.*\/\/([^\/]+).*$/', '$1', $mt_og[ 'og:url' ] ); } if ( ! isset( $mt_tc[ 'twitter:site' ] ) ) { $mt_tc[ 'twitter:site' ] = SucomUtilOptions::get_key_value( 'tc_site', $this->p->options, $mod ); } if ( ! isset( $mt_tc[ 'twitter:title' ] ) ) { $mt_tc[ 'twitter:title' ] = $this->p->page->get_title( $mod, $md_key = 'tc_title', $max_len = 'tc_title' ); } if ( ! isset( $mt_tc[ 'twitter:description' ] ) ) { $mt_tc[ 'twitter:description' ] = $this->p->page->get_description( $mod, $md_key = 'tc_desc', $max_len = 'tc_desc', $num_hashtags = true ); } if ( ! isset( $mt_tc[ 'twitter:creator' ] ) ) { if ( $author_id ) { $mt_tc[ 'twitter:creator' ] = get_the_author_meta( $this->p->options[ 'plugin_cm_twitter_name' ], $author_id ); $mt_tc[ 'twitter:creator' ] = SucomUtil::sanitize_twitter_name( $mt_tc[ 'twitter:creator' ] ); // Just in case. } } /* * Player video card. */ $this->maybe_add_player_card( $mt_tc, $mod, $mt_og, $author_id ); /* * Post summary or large image summary card. */ $this->maybe_add_post_card( $mt_tc, $mod, $mt_og, $author_id ); /* * Default card. */ $this->maybe_add_default_card( $mt_tc, $mod, $mt_og, $author_id ); /* * Additional article and product data. */ $this->maybe_add_extra_data( $mt_tc, $mod, $mt_og, $author_id ); return apply_filters( 'wpsso_tc', $mt_tc, $mod ); } /* * $mixed = 'singular' | 'default' | $mod. * * Example return: * * array( * 'summary_large_image', * 'X (Twitter) Summary Card Large Image', * 'wpsso-tc-lrgimg', * 'tc_lrg', * ) */ public function get_card_info( $mixed, array $head_tags = array(), $use_md_opts = true ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $card_type = 'summary'; $size_label = ''; $size_name = 'thumbnail'; // Just in case. $md_pre = ''; if ( ! empty( $head_tags[ 'twitter:card' ] ) ) { $card_type = $head_tags[ 'twitter:card' ]; } elseif ( is_string( $mixed ) ) { // $mixed is 'singular' or 'default'. if ( ! empty( $this->p->options[ 'tc_type_' . $mixed ] ) ) { $card_type = $this->p->options[ 'tc_type_' . $mixed ]; } } elseif ( is_array( $mixed ) ) { // $mixed is $mod. /* * Get the default card type for this post type. */ if ( ! empty( $mixed[ 'is_post' ] ) ) { if ( ! empty( $mixed[ 'post_type' ] ) && is_string( $mixed[ 'post_type' ] ) && ! empty( $this->p->options[ 'tc_type_for_' . $mixed[ 'post_type' ] ] ) ) { $card_type = $this->p->options[ 'tc_type_for_' . $mixed[ 'post_type' ] ]; } elseif ( ! empty( $this->p->options[ 'tc_type_singular' ] ) ) { $card_type = $this->p->options[ 'tc_type_singular' ]; } } /* * Maybe override the default with a custom card type. */ if ( ! empty( $mixed[ 'obj' ] ) && $use_md_opts ) { $md_card_type = $mixed[ 'obj' ]->get_options( $mixed[ 'id' ], 'tc_type' ); // Returns null if index key not found. if ( ! empty( $md_card_type ) ) { $card_type = $md_card_type; } } } switch ( $card_type ) { case 'app': $card_label = _x( 'X (Twitter) App Card', 'metabox title', 'wpsso' ); $size_name = ''; $md_pre = 'tc_app'; break; case 'player': $card_label = _x( 'X (Twitter) Player Card', 'metabox title', 'wpsso' ); $size_name = ''; $md_pre = 'tc_play'; break; case 'summary': $card_label = _x( 'X (Twitter) Summary Card', 'metabox title', 'wpsso' ); $size_name = 'wpsso-tc-summary'; $md_pre = 'tc_sum'; break; case 'summary_large_image': $card_label = _x( 'X (Twitter) Summary Card Large Image', 'metabox title', 'wpsso' ); $size_name = 'wpsso-tc-lrgimg'; $md_pre = 'tc_lrg'; break; } /* * Example return: * * array( * 'summary_large_image', * 'X (Twitter) Summary Card Large Image', * 'wpsso-tc-lrgimg', * 'tc_lrg', * ) */ $card_info = array( $card_type, $card_label, $size_name, $md_pre ); if ( $this->p->debug->enabled ) { $this->p->debug->log_arr( 'card_info', $card_info ); } return $card_info; } /* * Player video card. * * The twitter:player:stream meta tags are used for self-hosted MP4 videos. The videos provided by * YouTube, Vimeo, Wistia, etc. are application/x-shockwave-flash or text/html. * * twitter:player:stream * This is a URL to the video file itself (not a video embed). The video must be an mp4 file. The * supported codecs within the file are: H.264 video, Baseline Profile Level 3.0, up to 640 x 480 at * 30 fps and AAC Low Complexity Profile (LC) audio. This property is optional. * * twitter:player:stream:content_type * The MIME type for your video file (video/mp4). This property is only required if you have set a * twitter:player:stream meta tag. */ private function maybe_add_player_card( &$mt_tc, $mod, $mt_og, $author_id ) { // Pass by reference is OK. if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } if ( isset( $mt_tc[ 'twitter:card' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: twitter card is set' ); } return; } if ( empty( $mt_og[ 'og:video' ] ) || ! count( $mt_og[ 'og:video' ] ) > 1 ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: no videos for player card' ); } return; } foreach ( $mt_og[ 'og:video' ] as $mt_single_video ) { $player_embed_url = ''; $player_stream_url = ''; /* * Check for internal meta tag embed_url or stream_url. */ if ( ! empty( $mt_single_video[ 'og:video:embed_url' ] ) ) { $player_embed_url = $mt_single_video[ 'og:video:embed_url' ]; if ( $this->p->debug->enabled ) { $this->p->debug->log( 'player card: embed url = ' . $player_embed_url ); } } if ( ! empty( $mt_single_video[ 'og:video:stream_url' ] ) ) { $player_stream_url = $mt_single_video[ 'og:video:stream_url' ]; if ( $this->p->debug->enabled ) { $this->p->debug->log( 'player card: stream url = ' . $player_stream_url ); } } /* * Check for a video mime-type meta tag. */ if ( isset( $mt_single_video[ 'og:video:type' ] ) ) { switch ( $mt_single_video[ 'og:video:type' ] ) { /* * twitter:player * * HTTPS URL to iFrame player. This must be a HTTPS URL which does not generate active * mixed content warnings in a web browser. The audio or video player must not require * plugins such as Adobe Flash. */ case 'text/html': if ( empty( $player_embed_url ) ) { $player_embed_url = SucomUtil::get_first_mt_media_url( $mt_single_video, $media_pre = 'og:video' ); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'player card: ' . $mt_single_video[ 'og:video:type' ] . ' url = ' . $player_embed_url ); } } break; /* * twitter:player:stream */ case 'video/mp4': if ( empty( $player_stream_url ) ) { $player_stream_url = SucomUtil::get_first_mt_media_url( $mt_single_video, $media_pre = 'og:video' ); if ( $this->p->debug->enabled ) { $this->p->debug->log( 'player card: ' . $mt_single_video[ 'og:video:type' ] . ' url = ' . $player_stream_url ); } } break; default: if ( $this->p->debug->enabled ) { $this->p->debug->log( 'player card: video type "' . $mt_single_video[ 'og:video:type' ] . '" is unknown' ); } break; } } /* * Set the twitter:player meta tag value(s). */ if ( ! empty( $player_embed_url ) ) { $mt_tc[ 'twitter:card' ] = 'player'; $mt_tc[ 'twitter:player' ] = $player_embed_url; } if ( ! empty( $player_stream_url ) ) { $mt_tc[ 'twitter:card' ] = 'player'; if ( empty( $mt_tc[ 'twitter:player' ] ) ) { $mt_tc[ 'twitter:player' ] = $player_stream_url; // Fallback to video/mp4. } $mt_tc[ 'twitter:player:stream' ] = $player_stream_url; $mt_tc[ 'twitter:player:stream:content_type' ] = $mt_single_video[ 'og:video:type' ]; } /* * Set twitter:player related values (player width, height, mobile apps, etc.) */ if ( ! empty( $mt_tc[ 'twitter:card' ] ) ) { foreach ( array( 'og:video:width' => 'twitter:player:width', 'og:video:height' => 'twitter:player:height', 'og:video:iphone_name' => 'twitter:app:name:iphone', 'og:video:iphone_id' => 'twitter:app:id:iphone', 'og:video:iphone_url' => 'twitter:app:url:iphone', 'og:video:ipad_name' => 'twitter:app:name:ipad', 'og:video:ipad_id' => 'twitter:app:id:ipad', 'og:video:ipad_url' => 'twitter:app:url:ipad', 'og:video:googleplay_name' => 'twitter:app:name:googleplay', 'og:video:googleplay_id' => 'twitter:app:id:googleplay', 'og:video:googleplay_url' => 'twitter:app:url:googleplay', ) as $og_name => $tc_name ) { if ( ! empty( $mt_single_video[ $og_name ] ) ) { $mt_tc[ $tc_name ] = $mt_single_video[ $og_name ]; } } /* * Get the video preview image (if one is available). */ $mt_tc[ 'twitter:image' ] = SucomUtil::get_first_mt_media_url( $mt_single_video ); if ( ! empty( $mt_single_video[ 'og:image:alt' ] ) ) { $mt_tc[ 'twitter:image:alt' ] = $mt_single_video[ 'og:image:alt' ]; } /* * Fallback to the open graph image. */ if ( empty( $mt_tc[ 'twitter:image' ] ) && ! empty( $mt_og[ 'og:image' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'player card: no video image - using og:image instead' ); } $mt_tc[ 'twitter:image' ] = SucomUtil::get_first_mt_media_url( $mt_og[ 'og:image' ] ); } } return; // Use only the first video. } } /* * Post summary or large image summary card. */ private function maybe_add_post_card( &$mt_tc, $mod, $mt_og, $author_id ) { // Pass by reference is OK. if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } if ( isset( $mt_tc[ 'twitter:card' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: twitter card is set' ); } return; } if ( empty( $mod[ 'is_post' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: module is not post object' ); } return; } list( $card_type, $card_label, $size_name, $md_pre ) = $this->get_card_info( $mod ); /* * Post image. */ if ( $this->p->debug->enabled ) { $this->p->debug->log( $card_type . ' card: checking for post image (meta, featured, attached)' ); } $mt_images = $this->p->media->get_post_images( 1, $size_name, $mod[ 'id' ], $md_pre ); if ( count( $mt_images ) > 0 ) { $mt_single_image = reset( $mt_images ); $image_url = SucomUtil::get_first_mt_media_url( $mt_single_image ); /* * 'summary_large_image' webpages cannot have the same image URLs, so add the post ID to all * 'summary_large_image' images. */ if ( 'summary_large_image' === $card_type ) { $image_url = add_query_arg( 'p', $mod[ 'id' ], $image_url ); } $mt_tc[ 'twitter:card' ] = $card_type; $mt_tc[ 'twitter:image' ] = $image_url; if ( ! empty( $mt_single_image[ 'og:image:alt' ] ) ) { $mt_tc[ 'twitter:image:alt' ] = $mt_single_image[ 'og:image:alt' ]; } } elseif ( $this->p->debug->enabled ) { $this->p->debug->log( 'no post image found' ); } } /* * Default card. */ private function maybe_add_default_card( &$mt_tc, $mod, $mt_og, $author_id ) { // Pass by reference is OK. if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } if ( isset( $mt_tc[ 'twitter:card' ] ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'exiting early: twitter card is set' ); } return; } list( $card_type, $card_label, $size_name, $md_pre ) = $this->get_card_info( 'default' ); if ( $this->p->debug->enabled ) { $this->p->debug->log( $card_type . ' card: using default card type' ); } $mt_tc[ 'twitter:card' ] = $card_type; if ( $this->p->debug->enabled ) { $this->p->debug->log( $card_type . ' card: checking other images' ); } $mt_images = $this->p->media->get_all_images( $num = 1, $size_name, $mod, $md_pre ); if ( count( $mt_images ) > 0 ) { $mt_single_image = reset( $mt_images ); $image_url = SucomUtil::get_first_mt_media_url( $mt_single_image ); $mt_tc[ 'twitter:image' ] = $image_url; if ( ! empty( $mt_single_image[ 'og:image:alt' ] ) ) { $mt_tc[ 'twitter:image:alt' ] = $mt_single_image[ 'og:image:alt' ]; } } elseif ( $this->p->debug->enabled ) { $this->p->debug->log( 'no other images found' ); } } /* * Additional article and product data. */ private function maybe_add_extra_data( &$mt_tc, $mod, $mt_og, $author_id ) { // Pass by reference is OK. if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } if ( isset( $mt_og[ 'og:type' ] ) ) { switch ( $mt_og[ 'og:type' ] ) { case 'article': if ( ! empty( $mt_og[ 'article:author:name' ] ) ) { $mt_tc[ 'twitter:label1' ] = __( 'Written by', 'wpsso' ); $mt_tc[ 'twitter:data1' ] = $mt_og[ 'article:author:name' ]; } if ( ! empty( $mt_og[ 'article:reading_mins' ] ) ) { $mt_tc[ 'twitter:label2' ] = __( 'Est. reading time', 'wpsso' ); $mt_tc[ 'twitter:data2' ] = $this->p->page->fmt_reading_mins( $mt_og[ 'article:reading_mins' ] ); } break; case 'product': if ( isset( $mt_og[ 'product:price:amount' ] ) && isset( $mt_og[ 'product:price:currency' ] ) ) { $mt_tc[ 'twitter:label1' ] = __( 'Price', 'wpsso' ); $mt_tc[ 'twitter:data1' ] = $mt_og[ 'product:price:amount' ] . ' ' . $mt_og[ 'product:price:currency' ]; } if ( isset( $mt_og[ 'product:availability' ] ) ) { $mt_name = 'product:availability'; $map_key = $mt_og[ $mt_name ]; /* * Map 'https://schema.org/InStock' to 'in stock', for example. */ if ( ! empty( $this->p->cf[ 'head' ][ 'og_content_map' ][ $mt_name ][ $map_key ] ) ) { $map_key = $this->p->cf[ 'head' ][ 'og_content_map' ][ $mt_name ][ $map_key ]; } $mt_tc[ 'twitter:label2' ] = __( 'Availability', 'wpsso' ); $mt_tc[ 'twitter:data2' ] = ucwords( $map_key ); } break; } } } } }