Skip to content
Permalink
Browse files

Comments: Add `rel="nofollow ugc"` attribute to links in comments.

UGC stands for User Generated Content, and the `ugc` attribute value is recommended for links within user generated content, such as comments and forum posts.

See https://webmasters.googleblog.com/2019/09/evolving-nofollow-new-ways-to-identify.html.

Props audrasjb, joostdevalk, dkarfa, SergeyBiryukov.
Fixes #48022.

git-svn-id: https://develop.svn.wordpress.org/trunk@46349 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information...
SergeyBiryukov committed Sep 30, 2019
1 parent 1504804 commit 0e26adbaeaf801305332cb54252665042262c3c8
@@ -224,7 +224,7 @@ function get_comment_author_link( $comment_ID = 0 ) {
if ( empty( $url ) || 'http://' == $url ) {
$return = $author;
} else {
$return = "<a href='$url' rel='external nofollow' class='url'>$author</a>";
$return = "<a href='$url' rel='external nofollow ugc' class='url'>$author</a>";
}
/**
@@ -246,7 +246,7 @@
add_filter( 'sanitize_title', 'sanitize_title_with_dashes', 10, 3 );
add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 );
add_filter( 'comment_flood_filter', 'wp_throttle_comment_flood', 10, 3 );
add_filter( 'pre_comment_content', 'wp_rel_nofollow', 15 );
add_filter( 'pre_comment_content', 'wp_rel_ugc', 15 );
add_filter( 'comment_email', 'antispambot' );
add_filter( 'option_tag_base', '_wp_filter_taxonomy_base' );
add_filter( 'option_category_base', '_wp_filter_taxonomy_base' );
@@ -3012,35 +3012,19 @@ function _split_str_by_whitespace( $string, $goal ) {
}
/**
* Adds rel nofollow string to all HTML A elements in content.
* Callback to add a rel attribute to HTML A element.
*
* @since 1.5.0
*
* @param string $text Content that may contain HTML A elements.
* @return string Converted content.
*/
function wp_rel_nofollow( $text ) {
// This is a pre save filter, so text is already escaped.
$text = stripslashes( $text );
$text = preg_replace_callback( '|<a (.+?)>|i', 'wp_rel_nofollow_callback', $text );
return wp_slash( $text );
}
/**
* Callback to add rel=nofollow string to HTML A element.
*
* Will remove already existing rel="nofollow" and rel='nofollow' from the
* string to prevent from invalidating (X)HTML.
* Will remove already existing string before adding to prevent invalidating (X)HTML.
*
* @since 2.3.0
* @since 5.3.0
*
* @param array $matches Single Match
* @return string HTML A Element with rel nofollow.
* @param array $matches Single match.
* @param string $rel The rel attribute to add.
* @return string HTML A element with the added rel attribute.
*/
function wp_rel_nofollow_callback( $matches ) {
function wp_rel_callback( $matches, $rel ) {
$text = $matches[1];
$atts = wp_kses_hair( $matches[1], wp_allowed_protocols() );
$rel = 'nofollow';
if ( ! empty( $atts['href'] ) ) {
if ( in_array( strtolower( wp_parse_url( $atts['href']['value'], PHP_URL_SCHEME ) ), array( 'http', 'https' ), true ) ) {
@@ -3051,11 +3035,10 @@ function wp_rel_nofollow_callback( $matches ) {
}
if ( ! empty( $atts['rel'] ) ) {
$parts = array_map( 'trim', explode( ' ', $atts['rel']['value'] ) );
if ( false === array_search( 'nofollow', $parts ) ) {
$parts[] = 'nofollow';
}
$rel = implode( ' ', $parts );
$parts = array_map( 'trim', explode( ' ', $atts['rel']['value'] ) );
$rel_array = array_map( 'trim', explode( ' ', $rel ) );
$parts = array_unique( array_merge( $parts, $rel_array ) );
$rel = implode( ' ', $parts );
unset( $atts['rel'] );
$html = '';
@@ -3071,6 +3054,63 @@ function wp_rel_nofollow_callback( $matches ) {
return "<a $text rel=\"" . esc_attr( $rel ) . '">';
}
/**
* Adds `rel="nofollow"` string to all HTML A elements in content.
*
* @since 1.5.0
*
* @param string $text Content that may contain HTML A elements.
* @return string Converted content.
*/
function wp_rel_nofollow( $text ) {
// This is a pre save filter, so text is already escaped.
$text = stripslashes( $text );
$rel = 'nofollow';
$text = preg_replace_callback(
'|<a (.+?)>|i',
function( $matches ) use ( $rel ) {
return wp_rel_callback( $matches, $rel );
},
$text
);
return wp_slash( $text );
}
/**
* Callback to add `rel="nofollow"` string to HTML A element.
*
* @since 2.3.0
* @deprecated 5.3.0 Use wp_rel_callback()
*
* @param array $matches Single match.
* @return string HTML A Element with `rel="nofollow"`.
*/
function wp_rel_nofollow_callback( $matches ) {
return wp_rel_callback( $matches, 'nofollow' );
}
/**
* Adds `rel="nofollow ugc"` string to all HTML A elements in content.
*
* @since 5.3.0
*
* @param string $text Content that may contain HTML A elements.
* @return string Converted content.
*/
function wp_rel_ugc( $text ) {
// This is a pre save filter, so text is already escaped.
$text = stripslashes( $text );
$rel = 'nofollow ugc';
$text = preg_replace_callback(
'|<a (.+?)>|i',
function( $matches ) use ( $rel ) {
return wp_rel_callback( $matches, $rel );
},
$text
);
return wp_slash( $text );
}
/**
* Adds rel noreferrer and noopener to all HTML A elements that have a target.
*
@@ -0,0 +1,83 @@
<?php
/**
* @group formatting
*/
class Tests_Rel_Ugc extends WP_UnitTestCase {
/**
* @ticket 48022
*/
public function test_add_ugc() {
$content = '<p>This is some cool <a href="/">Code</a></p>';
$expected = '<p>This is some cool <a href=\"/\" rel=\"nofollow ugc\">Code</a></p>';
$this->assertEquals( $expected, wp_rel_ugc( $content ) );
}
/**
* @ticket 48022
*/
public function test_convert_ugc() {
$content = '<p>This is some cool <a href="/" rel="weird">Code</a></p>';
$expected = '<p>This is some cool <a href=\"/\" rel=\"weird nofollow ugc\">Code</a></p>';
$this->assertEquals( $expected, wp_rel_ugc( $content ) );
}
/**
* @ticket 48022
* @dataProvider data_wp_rel_ugc
*/
public function test_wp_rel_ugc( $input, $output ) {
return $this->assertEquals( wp_slash( $output ), wp_rel_ugc( $input ) );
}
public function data_wp_rel_ugc() {
$home_url_http = set_url_scheme( home_url(), 'http' );
$home_url_https = set_url_scheme( home_url(), 'https' );
return array(
array(
'<a href="">Double Quotes</a>',
'<a href="" rel="nofollow ugc">Double Quotes</a>',
),
array(
'<a href="https://wordpress.org">Double Quotes</a>',
'<a href="https://wordpress.org" rel="nofollow ugc">Double Quotes</a>',
),
array(
"<a href='https://wordpress.org'>Single Quotes</a>",
"<a href='https://wordpress.org' rel=\"nofollow ugc\">Single Quotes</a>",
),
array(
'<a href="https://wordpress.org" title="Title">Multiple attributes</a>',
'<a href="https://wordpress.org" title="Title" rel="nofollow ugc">Multiple attributes</a>',
),
array(
'<a title="Title" href="https://wordpress.org">Multiple attributes</a>',
'<a title="Title" href="https://wordpress.org" rel="nofollow ugc">Multiple attributes</a>',
),
array(
'<a data-someflag href="https://wordpress.org">Multiple attributes</a>',
'<a data-someflag href="https://wordpress.org" rel="nofollow ugc">Multiple attributes</a>',
),
array(
'<a data-someflag title="Title" href="https://wordpress.org" onclick="" >Everything at once</a>',
'<a data-someflag title="Title" href="https://wordpress.org" onclick="" rel="nofollow ugc">Everything at once</a>',
),
array(
'<a href="' . $home_url_http . '/some-url">Home URL (http)</a>',
'<a href="' . $home_url_http . '/some-url">Home URL (http)</a>',
),
array(
'<a href="' . $home_url_https . '/some-url">Home URL (https)</a>',
'<a href="' . $home_url_https . '/some-url">Home URL (https)</a>',
),
);
}
public function test_append_ugc_with_valueless_attribute() {
$content = '<p>This is some cool <a href="demo.com" download rel="hola">Code</a></p>';
$expected = '<p>This is some cool <a href=\"demo.com\" download rel=\"hola nofollow ugc\">Code</a></p>';
$this->assertEquals( $expected, wp_rel_ugc( $content ) );
}
}

0 comments on commit 0e26adb

Please sign in to comment.
You can’t perform that action at this time.