XF 2.0 external links forbidden if...

Idhae

Active member
hello,
i hope someone could help.
I have an option field where i could set a number of min. posts per user to allow him posting external links. For example if he or she has 10 posts made, external link posting allowed.

What i've done so far:

i have extend the checkValidity function in XF\Service\Message\Preparer:
PHP:
//check for externalLink settings
        $visitor = \XF::visitor();
        $postCountSetting = \XF::options()->external_link_forbidden;

        if($postCountSetting > 0 && !($visitor['is_admin'] || $visitor['is_moderator']))
        {
            if($visitor['message_count'] < $postCountSetting)
            {
                /* error !!! */
                $externalLink = new \MyAddon\Dir\XF\BbCode\Renderer\Html;
                $externalLink = $externalLink->getExternalUrlCount();
                /* error !!! */

                if($externalLink > 0)
                {
                    $this->errors[] = 'Posting not allowed with less then ' . $postCountSetting . ' posts';
                }
            }
        }

Also extend XF\BbCode\Renderer\Html ->renderTagUrl:
PHP:
//check externalLink
if ($url)
{
    $link = $this->formatter->getLinkClassTarget($url);

    if ($link['type'] != 'internal')
    {
        $this->_externalUrlCount ++;
    }

}

// and add a new public function
public function getExternalUrlCount()
{
     return $this->_externalUrlCount;
}


ERROR:
MyAddon\Dir\XF\BbCode\Renderer\XFCP_Html' not found
 
i got it.
for anybody who are interested how it works for me:

Extended Classes
PHP:
<?php

namespace MyAddon\DIR\XF\Service\Message;

class Preparer extends XFCP_Preparer
{
    public function checkValidity($message)
    {
        $validity = parent::checkValidity($message);

        if ($validity)
        {
            $bbCodeContainer = $this->app->bbCode();
            $parser = $bbCodeContainer->parser();
            $rules = $bbCodeContainer->rules($this->context);

            $renderer = $bbCodeContainer->renderer('html');
            $renderer->render($message, $parser, $rules);

            $externalUrlCount = $renderer->getExternalUrlCount();
            $visitor = \XF::visitor();
            $postCountSetting = \XF::options()->external_link_forbidden;

            if($postCountSetting > 0 && !($visitor['is_admin'] || $visitor['is_moderator'])) {
                if ($visitor['message_count'] < $postCountSetting) {
                    if ($externalUrlCount > 0) {
                        $this->errors[] = 'Posting external urls are not allowed with less then '. $postCountSetting . ' posts.';
                    }
                }
            }

            $this->isValid = (count($this->errors) == 0);
        }

        return $this->isValid;
    }
}

PHP:
<?php

namespace MyAddon\Dir\XF\BbCode\Renderer;

class Html extends XFCP_Html
{
    protected $_externalUrlCount = 0;

    public function renderTagUrl(array $children, $option, array $tag, array $options)
    {
        if ($option !== null)
        {
            $url = $option;
            $text = $this->renderSubTree($children, $options);
        }
        else
        {
            $url = $this->renderSubTreePlain($children);
            $text = rawurldecode($url);
            if (!preg_match('/./u', $text))
            {
                $text = $url;
            }
            $text = $this->formatter->censorText($text);

            if (!empty($options['shortenUrl']))
            {
                $length = utf8_strlen($text);
                if ($length > 100)
                {
                    $text = utf8_substr_replace($text, '...', 35, $length - 35 - 45);
                }
            }

            $text = htmlspecialchars($text);
        }

        $url = $this->getValidUrl($url);
        if (!$url)
        {
            return $text;
        }
        //check externalLink
        if ($url)
        {
            $link = $this->formatter->getLinkClassTarget($url);

            if ($link['type'] != 'internal')
            {
                $this->_externalUrlCount ++;
            }

        }

        $url = $this->formatter->censorText($url);


        return $this->getRenderedLink($text, $url, $options);
    }

    public function getExternalUrlCount()
    {
        return $this->_externalUrlCount;
    }
}

Now it works ...
 
Last edited:
Very nice, thank you for this.
I optimised the first extension slightly to skip the processing the user is allowed to post external links and then simplified the second class extension (and removed the duplicated code from the base class which would need to be maintained over upgrades)..

PHP:
<?php

namespace links\XF\Service\Message;

class Preparer extends XFCP_Preparer
{
    public function checkValidity($message)
    {
        $validity = parent::checkValidity($message);

        if ($validity)
        {
            $postCountSetting = \XF::options()->external_link_forbidden;
            $visitor = \XF::visitor();

            if (!$postCountSetting || $visitor['is_admin'] ||
                $visitor['message_count'] >= $postCountSetting)
                        return $validity;

            $bbCodeContainer = $this->app->bbCode();
            $parser = $bbCodeContainer->parser();
            $rules = $bbCodeContainer->rules($this->context);

            $renderer = $bbCodeContainer->renderer('html');
            $renderer->render($message, $parser, $rules);

            $externalUrlCount = $renderer->getExternalUrlCount();

            if ($externalUrlCount > 0)
                $this->errors[] = \XF::phraseDeferred('no_links_allowed');
        }

        return ($this->isValid = (count($this->errors) == 0));
    }
}
PHP:
<?php

namespace links\XF\BbCode\Renderer;

class Html extends XFCP_Html
{
    protected $_externalUrlCount = 0;

    protected function getRenderedLink($text, $url, array $options)
    {
        if ($url)
        {
            $link = $this->formatter->getLinkClassTarget($url);

            if ($link['type'] != 'internal')
                $this->_externalUrlCount++;
        }

        return parent::getRenderedLink($text, $url, $options);
    }

    public function getExternalUrlCount()
    {
        return $this->_externalUrlCount;
    }
}
 
I know I'm coming a bit late into this thread, but was wondering if I/we could get direction on how to put this stuff to use...? I'm assuming these are completely separate files, but where do I need to place them -- I'm assuming in a separate directory. How do they get plugged into the rest of Xen? Perhaps they're added at the head of existing code? Sorry for the stupid questions..
 
I know I'm coming a bit late into this thread, but was wondering if I/we could get direction on how to put this stuff to use...? I'm assuming these are completely separate files, but where do I need to place them -- I'm assuming in a separate directory. How do they get plugged into the rest of Xen? Perhaps they're added at the head of existing code? Sorry for the stupid questions..
You need to go into your root installation folder on into /src/XF/Service/Message and edit the Preparer file as above.
 
I was wondering the same thing -- the other way, while easy, makes for upgrade nightmares.. Perhaps I'll go down this path in the future, but for now I'm happy.. Thanks!!
 
Hello,
i dont know why but if someone quote, the external link checker throws an error:

  • ErrorException: Template error: Cannot call method isIgnoring on a non-object (NULL)
  • src/XF/Template/Templater.php:989

@af123 have you an idea?
 
You're running the BB code renderer in a context where the templater global variables aren't set up yet, so it'll throw that error when trying to access the visitor when rendering a quote template.
 
You're running the BB code renderer in a context where the templater global variables aren't set up yet, so it'll throw that error when trying to access the visitor when rendering a quote template.
Thank you for the quick answer
you have an idea to check this otherwise?
 
Ok, i don't unterstand how i achieve this with xenforo code so i do it with preg_match_all on $message to search for urls.

only one extended class needed:
PHP:
<?php

namespace MyAddon\DIR\XF\Service\Message;


class Preparer extends XFCP_Preparer
{
    protected $_externalUrlCount = 0;

    public function checkValidity($message)
    {
        $validity = parent::checkValidity($message);

        if ($validity)
        {
            $postCountSetting = \XF::options()->external_link_forbidden;
            $phraseVar['postCountSetting'] = $postCountSetting;
            $visitor = \XF::visitor();

            if (!$postCountSetting || $visitor['is_admin'] ||
                $visitor['message_count'] >= $postCountSetting){
                return $validity;
            }

            $urls = preg_match_all('/\[url=\'(.*)\'\]/i', $message, $matches);
            if($urls){
                foreach ($matches as $urlMatch){
                    $link = $this->app->stringFormatter()->getLinkClassTarget($urlMatch);

                    if ($link['type'] != 'internal'){
                        $this->_externalUrlCount++;
                    }
                }
            }

            if ($this->_externalUrlCount > 0){
                $this->errors[] = \XF::phraseDeferred('no_external_links_allowed', $phraseVar);
            }
        }
        return ($this->isValid = (count($this->errors) == 0));
    }
}
 
Last edited:
Back
Top Bottom