ID;
}
self::$posts = array_unique( self::$posts );
}
return $posts;
}
/**
* Get tags from current post views
*
* @return boolean
*/
public static function get_tags_from_current_posts() {
if ( is_array( self::$posts ) && count( self::$posts ) > 0 ) {
// Generate SQL from post id
$postlist = implode( "', '", self::$posts );
// Generate key cache
$key = md5( maybe_serialize( $postlist ) );
$results = array();
// Get cache if exist
$cache = wp_cache_get( 'generate_keywords', 'simpletags' );
if ( $cache === false ) {
foreach ( self::$posts as $object_id ) {
// Get terms
$terms = get_object_term_cache( $object_id, 'post_tag' );
if ( false === $terms ) {
$terms = wp_get_object_terms( $object_id, 'post_tag' );
}
if ( $terms != false ) {
$results = array_merge( $results, $terms );
}
}
$cache[ $key ] = $results;
wp_cache_set( 'generate_keywords', $cache, 'simpletags' );
} else {
if ( isset( $cache[ $key ] ) ) {
return $cache[ $key ];
}
}
return $results;
}
return array();
}
/**
* Get links for each tag for auto link feature
*
*/
public static function prepare_auto_link_tags() {
$auto_link_min = (int) SimpleTags_Plugin::get_option_value( 'auto_link_min' );
if ( $auto_link_min == 0 ) {
$auto_link_min = 1;
}
foreach ( (array) self::get_tags_from_current_posts() as $term ) {
if ( $term->count >= $auto_link_min ) {
self::$link_tags[ $term->name ] = esc_url( get_term_link( $term, $term->taxonomy ) );
}
}
return true;
}
/**
* Replace text by link to tag
*
* @param string $content
*
* @return string
*/
public static function the_content( $content = '' ) {
global $post;
// Show only on singular view ? Check context
if ( SimpleTags_Plugin::get_option_value( 'auto_link_views' ) == 'singular' && ! is_singular() ) {
return $content;
}
// user preference for this post ?
$meta_value = get_post_meta( $post->ID, '_exclude_autolinks', true );
if ( ! empty( $meta_value ) ) {
return $content;
}
// Get currents tags if no exists
self::prepare_auto_link_tags();
// Shuffle array
SimpleTags_Client::random_array( self::$link_tags );
// HTML Rel (tag/no-follow)
$rel = SimpleTags_Client::get_rel_attribut();
// only continue if the database actually returned any links
if ( ! isset( self::$link_tags ) || ! is_array( self::$link_tags ) || empty( self::$link_tags ) ) {
return $content;
}
// Case option ?
$case = ( (int) SimpleTags_Plugin::get_option_value( 'auto_link_case' ) == 1 ) ? 'i' : '';
$strpos_fnc = ( $case == 'i' ) ? 'stripos' : 'strpos';
// Prepare exclude terms array
$excludes_terms = explode( ',', SimpleTags_Plugin::get_option_value( 'auto_link_exclude' ) );
if ( $excludes_terms == false ) {
$excludes_terms = array();
} else {
$excludes_terms = array_filter( $excludes_terms, '_delete_empty_element' );
$excludes_terms = array_unique( $excludes_terms );
}
$z = 0;
foreach ( (array) self::$link_tags as $term_name => $term_link ) {
// Force string for tags "number"
$term_name = (string) $term_name;
// Exclude terms ? next...
if ( in_array( $term_name, (array) $excludes_terms ) ) {
continue;
}
// Make a first test with PHP function, economize CPU with regexp
if ( $strpos_fnc( $content, $term_name ) === false ) {
continue;
}
if ( (int) SimpleTags_Plugin::get_option_value( 'auto_link_dom' ) == 1 && class_exists( 'DOMDocument' ) && class_exists( 'DOMXPath' ) ) {
self::_replace_by_links_dom( $content, $term_name, $term_link, $case, $rel );
} else {
self::_replace_by_links_regexp( $content, $term_name, $term_link, $case, $rel );
}
$z ++;
if ( $z > (int) SimpleTags_Plugin::get_option_value( 'auto_link_max_by_post' ) ) {
break;
}
}
return $content;
}
/**
* Replace text by link, except HTML tag, and already text into link, use DOMdocument.
* Code take from : http://stackoverflow.com/questions/4044812/regex-domdocument-match-and-replace-text-not-in-a-link
*
* @param string $content
* @param string $search
* @param string $replace
* @param string $case
* @param string $rel
*/
private static function _replace_by_links_dom( &$content, $search = '', $replace = '', $case = '', $rel = '' ) {
$dom = new DOMDocument();
// loadXml needs properly formatted documents, so it's better to use loadHtml, but it needs a hack to properly handle UTF-8 encoding
$result = $dom->loadHtml( mb_convert_encoding( $content, 'HTML-ENTITIES', "UTF-8" ) );
if ( $result === false ) {
return false;
}
$xpath = new DOMXPath( $dom );
foreach ( $xpath->query( '//text()[not(ancestor::a)]' ) as $node ) {
$substitute = '$search";
if ( $case == 'i' ) {
$replaced = str_ireplace( $search, $substitute, $node->wholeText );
} else {
$replaced = str_replace( $search, $substitute, $node->wholeText );
}
$newNode = $dom->createDocumentFragment();
$newNode->appendXML( $replaced );
$node->parentNode->replaceChild( $newNode, $node );
}
// get only the body tag with its contents, then trim the body tag itself to get only the original content
$content = mb_substr( $dom->saveHTML( $xpath->query( '//body' )->item( 0 ) ), 6, - 7, "UTF-8" );
}
/**
* Replace text by link, except HTML tag, and already text into link, use PregEXP.
*
* @param string $content
* @param string $search
* @param string $replace
* @param string $case
* @param string $rel
*/
private static function _replace_by_links_regexp( &$content, $search = '', $replace = '', $case = '', $rel = '' ) {
$must_tokenize = true; // will perform basic tokenization
$tokens = null; // two kinds of tokens: markup and text
$j = 0;
$filtered = ''; // will filter text token by token
$match = '/(\PL|\A)(' . preg_quote( $search, "/" ) . ')(\PL|\Z)/u' . $case;
$substitute = '$1$2$3";
//$match = "/\b" . preg_quote($search, "/") . "\b/".$case;
//$substitute = '$0";
// for efficiency only tokenize if forced to do so
if ( $must_tokenize ) {
// this regexp is taken from PHP Markdown by Michel Fortin: http://www.michelf.com/projects/php-markdown/
$comment = '(?s:)|';
$processing_instruction = '(?s:<\?.*?\?>)|';
$tag = '(?:<[/!$]?[-a-zA-Z0-9:]+\b(?>[^"\'>]+|"[^"]*"|\'[^\']*\')*>)';
$markup = $comment . $processing_instruction . $tag;
$flags = PREG_SPLIT_DELIM_CAPTURE;
$tokens = preg_split( "{($markup)}", $content, - 1, $flags );
$must_tokenize = false;
}
// there should always be at least one token, but check just in case
$anchor_level = 0;
if ( isset( $tokens ) && is_array( $tokens ) && count( $tokens ) > 0 ) {
$i = 0;
foreach ( $tokens as $token ) {
if ( ++ $i % 2 && $token != '' ) { // this token is (non-markup) text
if ( $anchor_level == 0 ) { // linkify if not inside anchor tags
if ( preg_match( $match, $token ) ) { // use preg_match for compatibility with PHP 4
$j ++;
if ( $j <= SimpleTags_Plugin::get_option_value( 'auto_link_max_by_tag' ) || SimpleTags_Plugin::get_option_value( 'auto_link_max_by_tag' ) == 0 ) {// Limit replacement at 1 by default, or options value !
$token = preg_replace( $match, $substitute, $token ); // only PHP 5 supports calling preg_replace with 5 arguments
}
$must_tokenize = true; // re-tokenize next time around
}
}
} else { // this token is markup
if ( preg_match( "#<\s*a\s+[^>]*>#i", $token ) ) { // found
$anchor_level ++;
} elseif ( preg_match( "#<\s*/\s*a\s*>#i", $token ) ) { // found
$anchor_level --;
}
}
$filtered .= $token; // this token has now been filtered
}
$content = $filtered; // filtering completed for this link
}
}
}