XF 2.3 Censor Help

Ozzy47

Well-known member
I have an addon that will allow those with permission to view censored words, https://xenforo.com/community/resources/ozzmodz-censor-permissions.9660/

It uses the Formatter.php file to adjust it. It works as it should. However, I'd like to add it for a single node only. I have an option set up for my addon using PHP callback with \XF\Option\Forum::renderSelect which allows you to select one forum, the option is called ozzmodz_words_monitor_node

In my formatter file, how would I make it so it only applies in the selected node?

PHP:
<?php

namespace OzzModz\CensorPerms\XF\Str;

use XF\Template\Templater;

class Formatter extends XFCP_Formatter
{
    public function censorText($string, $censorChar = null)
    {
        // Get Parent       
        $parent = parent::censorText($string, $censorChar = null);
        
        // Get Visitor
        $visitor = \XF::visitor();

        if ($visitor->hasPermission('ozzmodzCensorPerms', 'view'))
        {
            // Show Censored Words
            return $string;
        }
        else
        {
           // Return To Parent
            return $parent;
        }
    }
}
 
I tried this, but it does not disable the censor in my selected node.

PHP:
<?php

namespace OzzModz\ConversationMonitor\XF\Str;

use XF;
use XF\Str\Formatter as XFFormatter;

class Formatter extends XFFormatter
{
    public function censorText($string, $censorChar = null)
    {
        // Get Parent
        $parent = parent::censorText($string, $censorChar);
   
        // Get Options
        $options = \XF::options();
       
        // Get Our Forum
         $excludeForum = $options->ozzmodz_email_convo_monitor_node;
   
        // Get Visitor
        $visitor = \XF::visitor();
   
        // Get current forum ID
        $forumId = \XF::app()->request()->get('node_id'); 
        $forum = \XF::em()->find('XF:Forum', $forumId);
   
        // Check if current forum is the excluded forum
        if ($forum == $excludeForum)
        {
            // Show Censored Words
            return $string;
        } else {
            // Return to Parent
            return $parent;
        }
    }
}
 
Last edited:
In my formatter file, how would I make it so it only applies in the selected node?

Replace the method in the above post with this:

PHP:
public function censorText($string, $censorChar = null)
{
    $app = \XF::app();
    $options = \XF::options();
  
     if ( $excludeForumId = $options->ozzmodz_email_convo_monitor_node )
     {
        $routeMatch = $app->router('public')->routeToController($app->request->getRoutePath());
        $parameterBag = $routeMatch->getParameterBag();
   
        if ( $excludeForumId == $parameterBag->node_id )
        {
            return $string;
        }
     }
    
    return parent::censorText($string, $censorChar);
}

This should take care of comparing the current node_id (if any) to the option value. You only included the visitor permisssions in the OP so you may want to fit it into the new function as well.
 
Thanks for posting that. unfortunately that still does not bypass the censor, the text is still showing the replacement.

For this use case, I didn't need the visitor permissions.
 
On your installation, can you debug and check the value of $parameterBag->node_id on the relevant page? I tested it here and it returns the proper node_id. It could be some other issue rather than the node_id detection..
 
node_id is available on the page:
Code:
    #_values: array:31 [▼
      "node_id" => 185
      "discussion_count" => 93
      "message_count" => 217
      "last_post_id" => 35324
      "last_post_date" => 1739752231
      "last_post_user_id" => 769
      "last_post_username" => "Ozzy47"
      "last_thread_id" => 6709
      "last_thread_title" => "Conversation/DM Monitor Keywords Triggered - Yeo"
      "last_thread_prefix_id" => 0
      "moderate_replies" => 0
      "allow_posting" => 1
      "count_messages" => 1
      "auto_feature" => 0
      "find_new" => 1
      "allow_index" => "allow"
      "index_criteria" => ""
      "field_cache" => "[]"
      "prefix_cache" => "[]"
      "default_prefix_id" => 0
      "require_prefix" => 0
      "allowed_watch_notifications" => "all"
      "default_sort_order" => "last_post_date"
      "default_sort_direction" => "desc"
      "moderate_threads" => 0
      "list_date_limit_days" => 0
      "min_tags" => 0
      "prompt_cache" => "[]"
      "forum_type_id" => "discussion"
      "type_config" => "{"allowed_thread_types":["poll"],"allow_answer_voting":false,"allow_answer_downvote":false}"
      "xfmg_media_mirror_category_id" => 0
    ]
 
It's available on the page but it's not available to the censor method which is 'page agnostic', which is why we forcefully fetch the parameter bag. I can't debug on the actual add-on since it's behind a paywall :)
 
You can do the node check in the controllers instead (which is the proper way to do it) and then the node ID is readily available, but that obviously requires more code extensions.
 
I looked into it again, I mentioned controllers but that's inaccurate.

There are 2 elements to handle for censoring: the thread title and the thread posts content.

For the title, in the thread entity structure, the 'title' column has censor set to true. You can conditionally set it to false based on the node_id, but that would require fetching the node_id with $routeMatch again.

For the posts, that's more complex, the content is displayed with the bb_code() or bb_code_snippet() template functions. So now the node_id needs to pass from the templates all the way to XF\BbCode\Renderer\Html\filterString(), which needs to be modified to censor the text conditionally based on the node.

Considering the amount of changes and also that we're using $routeMatch again, it's pretty pointless. The original censorText() modification seems much more practical, assuming the issue there is debugged.
 
Not sure if this is helpful but for a similar add-on I wrote ages ago I just conditionally overwrote the option value itself:

PHP:
\XF::options()->censorWords = [];
 
Yes that works :) by far the easiest solution.

Should be used within the node_id conditional while extending:
XF\Pub\Controller\ForumController\actionForum()
XF\Pub\Controller\ThreadController\actionIndex()
 
So I extended XF/Pub/Controller/Thread.php but I still get no node_id, dumping $parameterBag->node_id shows null. If I remove the conditional, the censored text shows up uncensored as expected.

Code:
    public function actionIndex(ParameterBag $params)
    {
        $app = \XF::app();
        $options = \XF::options();
 
         if ( $excludeForumId = $options->ozzmodz_email_convo_monitor_node )
         {
            $routeMatch = $app->router('public')->routeToController($app->request->getRoutePath());
            $parameterBag = $routeMatch->getParameterBag(); 

            if ( $excludeForumId == $parameterBag->node_id )
            {
                \XF::options()->censorWords = [];
            }
         }
//dump($parameterBag->node_id);             
            return parent::actionIndex($params);
    }
 
I got it, was over thinking it.

PHP:
    public function actionIndex(ParameterBag $params)
    {
        $options = \XF::options();

        $thread = $this->assertViewableThread($params->thread_id);
       
         if ( $excludeForumId = $options->ozzmodz_email_convo_monitor_node )
         {
            if ($thread->node_id == $excludeForumId)
            {
                \XF::options()->censorWords = [];
            }
         }
       
        return parent::actionIndex($params);
    }
 
Last edited:
Back
Top Bottom