Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Try Vanilla Forums Cloud product
Vanilla 2.6 is here! It includes security fixes and requires PHP 7.0. We have therefore ALSO released Vanilla 2.5.2 with security patches if you are still on PHP 5.6 to give you additional time to upgrade.

DoFollow certain domains

This discussion is related to the Vanilla addon.
GermontGermont New
edited January 12 in Vanilla 2.5 Help

I was happy that some domains linked from the forum increased traffic due to the new version of Vanilla.
Well, it couldn't be. I found in the source page that every external link is nofollowed, even without pop-up link.

Is there any solution to exclude nofollow for certain external domains?
Thank you!


  • rel="nofollow" does not exist in the database! Meaning it's added on the fly, at every page request :)

  • R_JR_J Cheerleader & Troubleshooter Munich Moderator

    I don't think it would be possible to do that without touching core files.

    Look at the Format class, especially those two blocks:

         * @var bool Flag which allows plugins to decide if the output should include rel="nofollow" on any <a> links.
         * @example a plugin can run on "BeforeCommentBody" to check the current users role and decide if his/her post
         * should contain rel="nofollow" links. The default setting is true, meaning all links will contain
         * the rel="nofollow" attribute.
        public static $DisplayNoFollow = true;


         * Formats the anchor tags around the links in text.
         * @param mixed $mixed An object, array, or string to be formatted.
         * @return string
        public static function links($mixed) {
            if (!c('Garden.Format.Links', true)) {
                return $mixed;
            if (!is_string($mixed)) {
                return self::to($mixed, 'Links');
            $linksCallback = function($matches) {
                static $inTag = 0;
                static $inAnchor = false;
                $inOut = $matches[1];
                $tag = strtolower($matches[2]);
                if ($inOut == '<') {
                    if ($tag == 'a') {
                        $inAnchor = true;
                } elseif ($inOut == '</') {
                    if ($tag == 'a') {
                        $inAnchor = false;
                } elseif ($matches[3]) {
                if (c('Garden.Format.WarnLeaving', false) && isset($matches[4]) && $inAnchor) {
                    // This is a the href url value in an anchor tag.
                    $url = $matches[4];
                    $domain = parse_url($url, PHP_URL_HOST);
                    if (!isTrustedDomain($domain)) {
                        return url('/home/leaving?target='.urlencode($url)).'" class="Popup';
                if (!isset($matches[4]) || $inTag || $inAnchor) {
                    return $matches[0];
                // We are not in a tag and what we matched starts with //
                if (preg_match('#^//#', $matches[4])) {
                    return $matches[0];
                $url = $matches[4];
                $embeddedResult = self::embedReplacement($url);
                if ($embeddedResult !== '') {
                    return $embeddedResult;
                // Unformatted links
                if (!self::$FormatLinks) {
                    return $url;
                // Strip punctuation off of the end of the url.
                $punc = '';
                // Special case where &nbsp; is right after an url and is not part of it!
                // This can happen in WYSIWYG format if the url is the last text of the body.
                while (stringEndsWith($url, '&nbsp;')) {
                    $url = substr($url, 0, -6);
                    $punc .= '&nbsp;';
                if (preg_match('`^(.+)([.?,;:])$`', $url, $matches)) {
                    $url = $matches[1];
                    $punc = $matches[2].$punc;
                // Get human-readable text from url.
                $text = $url;
                if (strpos($text, '%') !== false) {
                    $text = rawurldecode($text);
                    $text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
                $nofollow = (self::$DisplayNoFollow) ? ' rel="nofollow"' : '';
                if (c('Garden.Format.WarnLeaving', false)) {
                    // This is a plaintext url we're converting into an anchor.
                    $domain = parse_url($url, PHP_URL_HOST);
                    if (!isTrustedDomain($domain)) {
                        return '<a href="'.url('/home/leaving?target='.urlencode($url)).'" class="Popup">'.$text.'</a>'.$punc;
                return '<a href="'.$url.'"'.$nofollow.'>'.$text.'</a>'.$punc;
            if (unicodeRegexSupport()) {
                $regex = "`(?:(</?)([!a-z]+))|(/?\s*>)|((?:(?:https?|ftp):)?//[@\p{L}\p{N}\x21\x23-\x27\x2a-\x2e\x3a\x3b\/\x3f-\x7a\x7e\x3d]+)`iu";
            } else {
                $regex = "`(?:(</?)([!a-z]+))|(/?\s*>)|((?:(?:https?|ftp):)?//[@a-z0-9\x21\x23-\x27\x2a-\x2e\x3a\x3b\/\x3f-\x7a\x7e\x3d]+)`i";
            $mixed = Gdn_Format::replaceButProtectCodeBlocks(
            Gdn::pluginManager()->fireAs('Format')->fireEvent('Links', ['Mixed' => &$mixed]);
            return $mixed;

    A plugin could change the way links are tagged, but only on a per-body basis, not on a per link basis.

    You could insert a fireEvent call before $nofollow = (self::$DisplayNoFollow) ? ' rel="nofollow"' : '';, passing in the link url so tha you can hook into it.

    It might be more simple to work with JavaScript in this case and this plugin would be a good starting point.

  • /\

    there is an event there

    Gdn::pluginManager()->fireAs('Format')->fireEvent('Links', ['Mixed' => &$mixed]);

    that mean you could use the hook format_links_handler it is after the fact, but you could still do a preg_replace on $mixed

    grep is your friend.

  • Unfortunately my php skills are limited to changing values from 'true' to 'false', or searching for a missing semicolon.

    I suppose Vanilla premium clients use a version with more basic features like opening links in new tab without configuring a plugin, or favoring your sites on your own forum. Or at least I hope they do.

  • I changed public static $DisplayNoFollow = true; to false, removed the line
    $nofollow = (self::$DisplayNoFollow) ? ' rel="nofollow"' : '';and the attribute it's still there.

  • R_JR_J Cheerleader & Troubleshooter Munich Moderator

    Oh please don't do it like that! Get the [example plugin]( and start with that.
    1. Read the little bit that is needed to name it correctly
    2. In the example plugin file delete everything that is in the curly braces from the class
    3. Add the following code between the braces:

    public function format_links_handler($sender, $args) {
        // This will print out the text in a way that only admins can see it    
        // Now we do some transformation
        $args['Mixed'] = str_replace('nofollow', 'dofollow', $args['Mixed']);

    That's the start. You would have to find out about regular expressions in order to change the behaviour the way you like it and that is not too easy but you can alwaays ask...

Sign In or Register to comment.