Skip to content

Commit

Permalink
Setting terms from comments, display archive links, see #8 and #9
Browse files Browse the repository at this point in the history
  • Loading branch information
neverything committed Sep 5, 2019
1 parent 3d9b7b6 commit c59055a
Show file tree
Hide file tree
Showing 7 changed files with 307 additions and 7 deletions.
88 changes: 88 additions & 0 deletions classes/Comment_Tags.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,93 @@
* @since 0.7.0
*/
class Comment_Tags extends Hashtag_Parser {
/**
* @var string $taxonomy the multisite taxonomy used.
*
* @since 0.8.0
*/
static $taxonomy;

/**
* Comment_Tags constructor.
*
* @param string $taxonomy the multisite taxonomy used.
*
* @since 0.8.0
*/
public function __construct( $taxonomy = 'global_comment_tag' ) {

self::$taxonomy = $taxonomy;

self::register();
}

/**
* Register with WordPress hooks.
*
* @since 0.8.0
*/
public function register() {

add_action( 'wp_insert_comment', [ $this, 'update_comment' ] );
add_action( 'edit_comment', [ $this, 'update_comment' ] );

/**
* When displaying a tag, update the markup with a link to the tag.
*/
add_filter( 'comment_text', [ '\Spaces_Global_Tags\Comment_Tags', 'tag_comment_links'], 15 );

}

/**
* Markup tags in comments with links to the archive page.
*
* @param string $content Comment content.
*
* @since 0.8.0
*
* @return string|void
*/
static function tag_comment_links( $content ) {
$taxonomy = self::$taxonomy;
return parent::tag_links( $content, $taxonomy );
}

/**
* Update new or existing comment.
*
* @param int $comment_id ID of a comment.
*
* @since 0.8.0
*/
public function update_comment( $comment_id ) {
/**
* Get the comment object.
*/
$comment = get_comment( $comment_id );

/**
* Find raw tags in the comment_content.
*/
$tags = self::find_tags( $comment->comment_content );

$this->update_post( $comment->comment_post_ID, $tags );

}

/**
* Update post with tags from comment.
*
* @param int $post_id ID of a post.
* @param array $terms Array of terms for $this->taxonomy.
*/
public function update_post( $post_id, $terms ) {

/**
* Append the comment tags on the associated post.
*/
set_post_multisite_terms( $post_id, $terms, self::$taxonomy, get_current_blog_id(), true );

}

}
129 changes: 127 additions & 2 deletions classes/Hashtag_Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ abstract class Hashtag_Parser {
*/
const TAGS_REGEX = '/(?:^|\s|>|\()#(?!\d{1,2}(?:$|\s|<|\)|\p{P}{1}\s))([\p{L}\p{N}\_\-\.]*[\p{L}\p{N}]+)(?:$|\b|\s|<|\))/iu';

/**
* Find tags in a string.
*
* @param $content
*
* @return mixed|void
*/
static function find_tags( $content ) {
/**
* Placeholder for all tags found.
Expand All @@ -30,13 +37,13 @@ static function find_tags( $content ) {
$content = wp_pre_kses_less_than( $content );
$content = wp_kses_normalize_entities( $content );

$dom = new DOMDocument;
$dom = new \DOMDocument;

libxml_use_internal_errors( true );
$dom->loadHTML( '<?xml encoding="UTF-8">' . $content );
libxml_use_internal_errors( false );

$xpath = new DOMXPath( $dom );
$xpath = new \DOMXPath( $dom );
$textNodes = $xpath->query( '//text()' );

foreach ( $textNodes as $textNode ) {
Expand All @@ -59,4 +66,122 @@ static function find_tags( $content ) {
return apply_filters( 'spaces_global_tags_found_tags', $tags, $content );

}

/**
* Parses and links tags within a string.
* Run on the_content and comment_text.
*
* @param string $content The content.
* @param string $taxonomy Taxonomy name.
*
* @return string The linked content.
*/
static function tag_links( $content, $taxonomy ) {
if ( empty( $content ) ) {
return $content;
}

$tags = self::find_tags( $content );

$tags = array_unique( $tags );

usort( $tags, [ '\Spaces_Global_Tags\Hashtag_Parser', '_sortByLength' ] );

static $tag_links = [];

static $tag_info = [];

foreach ( $tags as $tag ) {
if ( isset( $tag_info[ $tag ] ) ) {
continue;
}
$info = get_multisite_term_by( 'slug', $tag, $taxonomy );
if ( ! $info ) {
$info = get_multisite_term_by( 'name', $tag, $taxonomy );
}
$tag_info[ $tag ] = $info;
}
$content = wp_pre_kses_less_than( $content );
$content = wp_kses_normalize_entities( $content );

$dom = new \DOMDocument;

libxml_use_internal_errors( true );
@$dom->loadHTML( '<?xml encoding="UTF-8">' . $content );
libxml_use_internal_errors( false );

$xpath = new \DOMXPath( $dom );

$textNodes = $xpath->query( '//text()' );

foreach( $textNodes as $textNode ) {
if ( ! $textNode->parentNode ) {
continue;
}
$parent = $textNode;
while( $parent ) {
if ( ! empty( $parent->tagName ) && in_array( strtolower( $parent->tagName ), array( 'pre', 'code', 'a', 'script', 'style', 'head' ) ) ) {
continue 2;
}
$parent = $parent->parentNode;
}
$text = $textNode->nodeValue;
$totalCount = 0;
foreach ( $tags as $tag ) {
if ( empty( $tag_info[ $tag ] ) ) {
continue;
}
if ( empty( $tag_links[ $tag ] ) ) {
$tag_url = get_multisite_term_link( $tag_info[ $tag ], $taxonomy );
$replacement = "<a href='" . esc_url( $tag_url ) . "' class='tag'><span class='tag-prefix'>#</span>" . htmlentities( $tag ) . "</a>";
$replacement = apply_filters( 'spaces_global_tags_tag_link', $replacement, $tag );
$tag_links[ $tag ] = $replacement;
} else {
$replacement = $tag_links[ $tag ];
}
$count = 0;
$text = preg_replace( "/(^|\s|>|\()#$tag(($|\b|\s|<|\)))/", '$1' . $replacement . '$2', $text, -1, $count );
$totalCount += $count;
}
if ( ! $totalCount ) {
continue;
}
$text = wp_pre_kses_less_than( $text );
$text = wp_kses_normalize_entities( $text );

$newNodes = new \DOMDocument;

libxml_use_internal_errors( true );
@$newNodes->loadHTML( '<?xml encoding="UTF-8"><div>' . $text . '</div>' );
libxml_use_internal_errors( false );

foreach( $newNodes->getElementsByTagName( 'body' )->item( 0 )->childNodes->item( 0 )->childNodes as $newNode ) {
$cloneNode = $dom->importNode( $newNode, true );
if ( ! $cloneNode ) {
continue 2;
}
$textNode->parentNode->insertBefore( $cloneNode, $textNode );
}
$textNode->parentNode->removeChild( $textNode );
}
$html = '';
// Sometime, DOMDocument will put things in the head instead of the body.
// We still need to keep them in our output.
$search_tags = array( 'head', 'body' );
foreach ( $search_tags as $tag ) {
$list = $dom->getElementsByTagName( $tag );
if ( 0 === $list->length ) {
continue;
}
foreach ( $list->item( 0 )->childNodes as $node ) {
$html .= $dom->saveHTML( $node );
}
}
return $html;
}

static function _sortByLength( $a, $b ) {
return strlen( $b ) - strlen( $a );
}

}
73 changes: 73 additions & 0 deletions classes/Post_Tags.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,77 @@
*/
class Post_Tags extends Hashtag_Parser {

/**
* @var string $taxonomy the multisite taxonomy used.
*
* @since 0.8.0
*/
static $taxonomy;

/**
* Comment_Tags constructor.
*
* @param string $taxonomy the multisite taxonomy used.
*
* @since 0.8.0
*/
public function __construct( $taxonomy = 'global_post_tag' ) {

self::$taxonomy = $taxonomy;

self::register();
}

/**
* Register with WordPress hooks.
*
* @since 0.8.0
*/
public function register() {

add_action( 'transition_post_status', [ $this, 'process_tags' ], 12, 3 );

/**
* When displaying a tag, update the markup with a link to the tag.
*/
add_filter( 'the_content', [ '\Spaces_Global_Tags\Post_Tags', 'tag_post_links'], 15 );

}

/**
* Markup tags in posts with links to the archive page.
*
* @param string $content The Content of the post.
*
* @since 0.8.0
*
* @return string|void
*/
static function tag_post_links( $content ) {
$taxonomy = self::$taxonomy;
return parent::tag_links( $content, $taxonomy );
}

/**
* Fires when the post is published or edited and
* sets the tags accordingly.
*
* @param boolean $new Status being switched to
* @param boolean $old Status being switched from
* @param object $post The full Post object
*
* @since 0.8.0
*
* @return void
*/
public function process_tags( $new, $old, $post ) {

if ( 'publish' !== $new ) {
return;
}
$tags = self::find_tags( $post->post_content );
// TODO: Needs fixing, creates the tags, but not yet adds them to the post.
set_post_multisite_terms( $post->ID, $tags, self::$taxonomy, get_current_blog_id(), true );
}

}
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "spaces-global-tags",
"version": "0.7.0",
"version": "0.9.0",
"main": "Gruntfile.js",
"author": "Silvan Hagen",
"devDependencies": {
Expand Down
10 changes: 8 additions & 2 deletions readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Contributors: neverything
Tags: multisite, wpmu, taxonomies
Requires at least: 4.5
Tested up to: 5.2.2
Stable tag: 0.7.0
Stable tag: 0.8.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Expand All @@ -27,7 +27,7 @@ Clone it, love it, hate it.

= Does this replace the default tags in a WordPress multisite =

No.
No. But it does disable them on posts.

= Is there a global search available =

Expand All @@ -37,6 +37,12 @@ No.

== Changelog ==

= 0.8.0 =
Add composer.json support.
Implement PSR4 autoloader for classes.
Add method to filter displayed comment/content with links to the tag archive pages.
Implement logic for parsing and settings terms for posts in comments.

= 0.7.0 =
Implement abstract class Hashtag_Parser.
Setting up Post_Tags and Comment_Tags classes.
Expand Down
Loading

0 comments on commit c59055a

Please sign in to comment.