diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..dea5525 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "includes/twitter-cards-php"] + path = includes/twitter-cards-php + url = https://github.com/niallkennedy/twitter-cards-php.git diff --git a/class-twitter-card-wp.php b/class-twitter-card-wp.php new file mode 100644 index 0000000..0f904de --- /dev/null +++ b/class-twitter-card-wp.php @@ -0,0 +1,110 @@ + element from a name and value + * + * @param string $name name attribute value + * @param string|int $value value attribute value + * @param bool $xml include a trailing slash for XML. encode attributes for XHTML in PHP 5.4+ + * @return meta element or empty string if name or value not valid + */ + public static function build_meta_element( $name, $value, $xml = false ) { + if ( ! ( is_string( $name ) && $name && ( is_string( $value ) || ( is_int( $value ) && $value > 0 ) ) ) ) + return ''; + return '' : '>' ) . "\n"; + } + + /** + * Pass all URLs through esc_url_raw. Unset the property if URL rejected + * + * @since 1.0 + * @uses esc_url_raw() + */ + private function filter_urls() { + if ( isset( $this->url ) ) { + $this->url = esc_url_raw( $this->url ); + if ( ! $this->url ) + unset( $this->url ); + } + + if ( isset( $this->image ) && isset( $this->image->url ) ) { + $this->image->url = esc_url_raw( $this->image->url ); + if ( ! $this->image->url ) + unset( $this->image ); + } + + if ( isset( $this->video ) && isset( $this->video->url ) ) { + $this->video->url = esc_url_raw( $this->video->url ); + if ( $this->video->url ) { + if ( isset( $this->video->stream ) && isset( $this->video->stream->url ) ) + $this->video->stream->url = esc_url_raw( $this->video->stream->url ); + if ( ! $this->video->stream->url ) + unset( $this->video->stream ); + } else { + unset( $this->video ); + } + } + } + + /** + * Build a string of elements representing the object + * Pass URLs through esc_url_raw to preserve site preferences + * + * @since 1.0 + * @param string $style markup style. "xml" adds a trailing slash to the meta void element + * @return string elements or empty string if minimum requirements not met + */ + private function generate_markup( $style = 'html' ) { + $xml = false; + if ( $style === 'xml' ) + $xml = true; + $this->filter_urls(); + $t = apply_filters( 'twitter_cards_properties', $this->toArray() ); + if ( ! is_array( $t ) || empty( $t ) ) + return ''; + $s = ''; + foreach ( $t as $name => $value ) { + $s .= self::build_meta_element( $name, $value, $xml ); + } + return $s; + } + + /** + * Output object properties as HTML meta elements with name and value attributes + * + * @return string HTML elements or empty string if minimum requirements not met for card type + */ + public function asHTML() { + return $this->generate_markup(); + } + + /** + * Output object properties as XML meta elements with name and value attributes + * + * @since 1.0 + * @return string XML elements or empty string if minimum requirements not met for card type + */ + public function asXML() { + return $this->generate_markup( 'xml' ); + } +} +?> \ No newline at end of file diff --git a/includes/twitter-cards-php b/includes/twitter-cards-php new file mode 160000 index 0000000..0a2f64e --- /dev/null +++ b/includes/twitter-cards-php @@ -0,0 +1 @@ +Subproject commit 0a2f64e8e536799f314ceab356882a64b745e340 diff --git a/twitter-cards.php b/twitter-cards.php new file mode 100644 index 0000000..bb27be2 --- /dev/null +++ b/twitter-cards.php @@ -0,0 +1,79 @@ + + * + * @since 1.0 + * @version 1.0 + */ +class Twitter_Cards { + /** + * Attach Twitter cards markup to wp_head if single post view + * + * @since 1.0 + */ + public static function init() { + if ( is_single() ) + add_action( 'wp_head', 'Twitter_Cards::markup' ); + } + + /** + * Build a Twitter Card object. Possibly output markup + * + * @since 1.0 + */ + public static function markup() { + global $post; + + if ( ! class_exists( 'Twitter_Card_WP' ) ) + require_once( dirname(__FILE__) . '/class-twitter-card-wp.php' ); + + $card = new Twitter_Card_WP(); + $card->setURL( apply_filters( 'rel_canonical', get_permalink() ) ); + $post_type = get_post_type(); + if ( post_type_supports( $post_type, 'title' ) ) + $card->setTitle( get_the_title() ); + if ( post_type_supports( $post_type, 'excerpt' ) ) { + // one line, no HTML + $card->setDescription( self::clean_description( apply_filters( 'the_excerpt', get_the_excerpt() ) ) ); + } + // does current post type and the current theme support post thumbnails? + if ( post_type_supports( $post_type, 'thumbnail' ) && function_exists( 'has_post_thumbnail' ) && has_post_thumbnail() ) { + list( $post_thumbnail_url, $post_thumbnail_width, $post_thumbnail_height ) = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'full' ); + $card->setImage( $post_thumbnail_url, $post_thumbnail_width, $post_thumbnail_height ); + } + + if ( apply_filters( 'twitter_cards_htmlxml', 'html' ) === 'xml' ) + echo $card->asXML(); + else + echo $card->asHTML(); + } + + public static function clean_description( $description ) { + if ( ! ( is_string( $description ) && $description ) ) + return ''; + + $description = wp_strip_all_tags( strip_shortcodes( $description ) ); + $description = trim( str_replace( array( "\r\n", "\r", "\n" ), ' ', $description ) ); + $excerpt_more = trim( wp_strip_all_tags( apply_filters('excerpt_more', '[...]') ) ); + if ( $excerpt_more ) { + $excerpt_more_length = strlen( $excerpt_more ); + if ( strlen( $description ) > $excerpt_more_length && substr_compare( $description, $excerpt_more, $excerpt_more_length * -1, $excerpt_more_length ) === 0 ) { + $description = trim( substr( $description, 0, $excerpt_more_length * -1 ) ); + } + } + return $description; + } +} +add_action( 'wp', 'Twitter_Cards::init' ); +endif; +?> \ No newline at end of file