XF 2.2 Xenforo Approval Query

Mbx

Member
How do i get infos like "Spam phrase matched (XX)" or some kind of reason into the Approval Queue information ? Thanks.
 
Went on to making a plugin for this ... but somewhere is a bug. Any idea, help ? Must be something very basic.

src/
└── addons/
└── Fanverse/
└── ApprovalReason/
├── Listener.php
├── Setup/
│ └── Setup.php
├── XF/
│ └── Entity/
│ └── ApprovalQueue.php
├── _data/
│ └── code_event_listeners.xml
│ └── template_modifications.xml
├── addon.json



addon.json

{
"legacy_addon_id": "Fanverse_ApprovalReason",
"title": "Approval Queue Reason",
"description": "Extends the approval queue listings to include a reason.",
"version_id": 1000010,
"version_string": "1.0.0",
"dev": "Fanverse",
"dev_url": "https://fanverse.com",
"require": {
"XF": [2020070, "XenForo 2.2.0+"]
},
"class_extensions": {
"XF\\Entity\\ApprovalQueue": "Fanverse\\ApprovalReason\\XF\\Entity\\ApprovalQueue"
},
"listeners": [
{
"event": "templater_macro_pre_render",
"callback_class": "Fanverse\\ApprovalReason\\Listener",
"callback_method": "templaterMacroPreRender"
}
],
"setup": "Fanverse\\ApprovalReason\\Setup"
}

Listener.php

<?php

namespace Fanverse\ApprovalReason;

use XF\Template\Templater;

class Listener
{
public static function templaterMacroPreRender(Templater $templater, &$type, &$template, &$name, array &$params)
{
error_log("Listener called: name = $name, type = $type");

if ($name == 'item_message_type' && $type == 'public')
{
error_log("Matching item_message_type and public");
if (isset($params['unapprovedItem']['approval_reason'])) {
error_log("Approval Reason: " . $params['unapprovedItem']['approval_reason']);
$params['approval_reason'] = $params['unapprovedItem']['approval_reason'];
} else {
error_log("Approval reason not set in unapprovedItem");
}
} else {
error_log("Name or type did not match");
}
}
}

code_event_listeners.xml

<?xml version="1.0" encoding="utf-8"?>
<code_event_listeners>
<listener event="templater_macro_pre_render" callback_class="Fanverse\ApprovalReason\Listener" callback_method="templaterMacroPreRender" active="true" event_id="templater_macro_pre_render" />
</code_event_listeners>

template_modifications.xml

<?xml version="1.0" encoding="utf-8"?>
<template_modifications>
<modification template="public:approval_queue_macros" modification_key="fanverse_approvalreason" description="Show approval reason in approval queue" version_id="1000010" version_string="1.0.0">
<find><![CDATA[<xf:contentcheck>]]></find>
<replace><![CDATA[
<xf:contentcheck>
<xf:if is="{$approval_reason}">
<div class="messageNotice messageNotice--moderated">
<span>Reason: {$approval_reason}</span>
</div>
</xf:if>
]]></replace>
</modification>
</template_modifications>

Setup.php

<?php

namespace Fanverse\ApprovalReason\Setup;

use XF\AddOn\AbstractSetup;
use XF\Db\Schema\Alter;

class Setup extends AbstractSetup
{
public function install(array $stepParams = [])
{
error_log("Running install");

$this->schemaManager()->alterTable('xf_approval_queue', function(Alter $table)
{
error_log("Altering table xf_approval_queue");

$table->addColumn('approval_reason', 'varchar', 255)->setDefault('');
});
}

public function upgrade(array $stepParams = [])
{
// Handle upgrades
}

public function uninstall(array $stepParams = [])
{
error_log("Running uninstall");

$this->schemaManager()->alterTable('xf_approval_queue', function(Alter $table)
{
error_log("Dropping column approval_reason from xf_approval_queue");

$table->dropColumns(['approval_reason']);
});
}
}

ApprovalQueue.php

<?php

namespace Fanverse\ApprovalReason\XF\Entity;

use XF\Mvc\Entity\Entity;
use XF\Mvc\Entity\Structure;

class ApprovalQueue extends XFCP_ApprovalQueue
{
protected function _setupDefaults()
{
parent::_setupDefaults();
$this->approval_reason = '';
}

protected function _preSave()
{
if ($this->isInsert())
{
$this->set('approval_reason', $this->get('approval_reason'));
}
}

public static function getStructure(Structure $structure)
{
$structure = parent::getStructure($structure);
$structure->columns['approval_reason'] = ['type' => self::STR, 'default' => ''];
return $structure;
}
}
 
Last edited:
You're more likely to get useful feedback when posting code in [CODE] tags so that it is not difficult to read, and when asking a pointed question about a specific problem you're trying to solve. I'm not sure if this code was AI generated or not but, doing my best to skim over it, it doesn't seem like it would actually do much of anything.

Unless I'm misunderstanding XF already does what you want in out of the box, so it's not obvious what you're trying to accomplish:

1725400161479.webp
 
Hi there. I just want to add a Reason field in ApprovalQueue, which will be displayed within the approval queue (so something very basic, tiny extension), so I can do AI moderation that can add content to approval queue with specific reasons/explanations. Only "spam" filtering ^ is quite limited. I want to display a custom data field, ... It must be a very silly mistake.


addon.json

Code:
{
"legacy_addon_id": "Fanverse_ApprovalReason",
"title": "Approval Queue Reason",
"description": "Extends the approval queue listings to include a reason.",
"version_id": 1000010,
"version_string": "1.0.0",
"dev": "Fanverse",
"dev_url": "https://fanverse.com",
"require": {
"XF": [2020070, "XenForo 2.2.0+"]
},
"class_extensions": {
"XF\\Entity\\ApprovalQueue": "Fanverse\\ApprovalReason\\XF\\Entity\\ApprovalQueue"
},
"listeners": [
{
"event": "templater_macro_pre_render",
"callback_class": "Fanverse\\ApprovalReason\\Listener",
"callback_method": "templaterMacroPreRender"
}
],
"setup": "Fanverse\\ApprovalReason\\Setup"
}

Listener.php

Code:
<?php

namespace Fanverse\ApprovalReason;

use XF\Template\Templater;

class Listener
{
public static function templaterMacroPreRender(Templater $templater, &$type, &$template, &$name, array &$params)
{
error_log("Listener called: name = $name, type = $type");

if ($name == 'item_message_type' && $type == 'public')
{
error_log("Matching item_message_type and public");
if (isset($params['unapprovedItem']['approval_reason'])) {
error_log("Approval Reason: " . $params['unapprovedItem']['approval_reason']);
$params['approval_reason'] = $params['unapprovedItem']['approval_reason'];
} else {
error_log("Approval reason not set in unapprovedItem");
}
} else {
error_log("Name or type did not match");
}
}
}

code_event_listeners.xml

Code:
<?xml version="1.0" encoding="utf-8"?>
<code_event_listeners>
<listener event="templater_macro_pre_render" callback_class="Fanverse\ApprovalReason\Listener" callback_method="templaterMacroPreRender" active="true" event_id="templater_macro_pre_render" />
</code_event_listeners>

template_modifications.xml

Code:
<?xml version="1.0" encoding="utf-8"?>
<template_modifications>
<modification template="public:approval_queue_macros" modification_key="fanverse_approvalreason" description="Show approval reason in approval queue" version_id="1000010" version_string="1.0.0">
<find><![CDATA[<xf:contentcheck>]]></find>
<replace><![CDATA[
<xf:contentcheck>
<xf:if is="{$approval_reason}">
<div class="messageNotice messageNotice--moderated">
<span>Reason: {$approval_reason}</span>
</div>
</xf:if>
]]></replace>
</modification>
</template_modifications>

Setup.php

Code:
<?php

namespace Fanverse\ApprovalReason\Setup;

use XF\AddOn\AbstractSetup;
use XF\Db\Schema\Alter;

class Setup extends AbstractSetup
{
public function install(array $stepParams = [])
{
error_log("Running install");

$this->schemaManager()->alterTable('xf_approval_queue', function(Alter $table)
{
error_log("Altering table xf_approval_queue");

$table->addColumn('approval_reason', 'varchar', 255)->setDefault('');
});
}

public function upgrade(array $stepParams = [])
{
// Handle upgrades
}

public function uninstall(array $stepParams = [])
{
error_log("Running uninstall");

$this->schemaManager()->alterTable('xf_approval_queue', function(Alter $table)
{
error_log("Dropping column approval_reason from xf_approval_queue");

$table->dropColumns(['approval_reason']);
});
}
}

ApprovalQueue.php

Code:
<?php

namespace Fanverse\ApprovalReason\XF\Entity;

use XF\Mvc\Entity\Entity;
use XF\Mvc\Entity\Structure;

class ApprovalQueue extends XFCP_ApprovalQueue
{
protected function _setupDefaults()
{
parent::_setupDefaults();
$this->approval_reason = '';
}

protected function _preSave()
{
if ($this->isInsert())
{
$this->set('approval_reason', $this->get('approval_reason'));
}
}

public static function getStructure(Structure $structure)
{
$structure = parent::getStructure($structure);
$structure->columns['approval_reason'] = ['type' => self::STR, 'default' => ''];
return $structure;
}
}
 
Last edited:
You may wish to follow the add-on tutorial or building with XenForo series to learn more about add-on development. Am I wrong to guess the code was AI generated? I don't mean to be harsh, but it contains several issues that seem like LLM hallucinations:
  • Specifies a legacy_addon_id unnecessarily (only required for add-ons that existed for XF 1)
  • Specifies class extensions and listeners in addon.json (they should be defined via the control panel)
  • Several calls to error_log for unclear reasons
  • Listener sets a parameter that was already available in a macro (you can access $unapprovedItem.approval_reason in a template modification)
  • Entity class extension is missing column constraints, does not need _setupDefaults, and _preSave sets approval_reason to its already existing value, essentially doing nothing at all
Even if all of those issues were addressed you'd need to set the approval reason from somewhere, but this record is managed by the content type entities so it's likely better to approach it differently. The existing spam checkers write to a dedicated xf_spam_trigger_log table for example, which is available via a getter on the approval queue entity that is hydrated by \XF\Repository\ApprovalQueueRepository::addContentToUnapprovedItems.
 
You may wish to follow the add-on tutorial or building with XenForo series to learn more about add-on development. Am I wrong to guess the code was AI generated? I don't mean to be harsh, but it contains several issues that seem like LLM hallucinations:
  • Specifies a legacy_addon_id unnecessarily (only required for add-ons that existed for XF 1)
  • Specifies class extensions and listeners in addon.json (they should be defined via the control panel)
  • Several calls to error_log for unclear reasons
  • Listener sets a parameter that was already available in a macro (you can access $unapprovedItem.approval_reason in a template modification)
  • Entity class extension is missing column constraints, does not need _setupDefaults, and _preSave sets approval_reason to its already existing value, essentially doing nothing at all
I tested a lot of things and added a lot - maybe this is why. Ok, I will remove this, and test it. Thanks.

But these are not real bugs then!?

The image: thats what I get.

Even if all of those issues were addressed you'd need to set the approval reason from somewhere, but this relation is managed by the content type entities so it's likely better to approach it differently. The existing spam checkers write to a dedicated xf_spam_trigger_log table for example, which is available via a getter on the approval queue entity that is hydrated by \XF\Repository\ApprovalQueueRepository::addContentToUnapprovedItems.

This is a bot that has been developed and works. My idea is not just to report content, but to put it in the approval queue for moderation to check. But moderation needs to understand why ... so I need the extension to show a custom reason.
 

Attachments

  • Screenshot from 2024-08-30 02-24-11.webp
    Screenshot from 2024-08-30 02-24-11.webp
    7 KB · Views: 5
The image: thats what I get.
When doing what...?

This is a bot that has been developed and works. My idea is not just to report content, but to put it in the approval queue for moderation to check. But moderation needs to understand why ... so I need the extension to show a custom reason.
I understand the concept, though my suggestion would likely be to write these details to a dedicated table, set up a getter on the approval queue entity, and then hydrate it by extending the repository method. Even if you wanted to write to a custom column you would need to do that when setting up the approval queue record (which is typically managed by each content type entity without an ideal extension point) or otherwise setting the content state.
 
When doing what...?

Trying to install the plugin. Debug does not help. So, it must be something very simple. Most likely template_modifications, I think.

I understand the concept, though my suggestion would likely be to write these details to a dedicated table, set up a getter on the approval queue entity, and then hydrate it by extending the repository method. Even if you wanted to write to a custom column you would need to do that when setting up the approval queue record (which is typically managed by each content type entity without an ideal extension point) or otherwise setting the content state.
Sure, but since I wanted to keep it simple ... In general, the Queue should allow custom information, maybe as suggestion.
 
Likely something bizarre in the addon.json or XML files. My advice would be to create a new add-on from scratch (via php cmd.php xf-addon:create) to get a proper addon.json, copy the PHP files over to the new add-on directory and set up the class extensions and stuff from the control panel.

Sure, but since I wanted to keep it simple ... In general, the Queue should allow custom information, maybe as suggestion.
XF allows for custom spam checkers and includes logging facilities that display in the approval queue, in case that is a better architecture for what you're trying to do. You can still use a custom column here though if that allows you to progress your idea further, it's just probably not an ideal approach.
 
I fixed now after trying. I was correct. It was template_modifications -- but I am not sure, why it cased the issue in principle. I made now the changes within

template="approval_item_user"

and that worked.

It seems I have to add the var within the args, so I can add them in the template, or so ... It shows the coding was not wrong, but you need to understand the structure of Xenforo (which I figured out now by looking over the templates and making some guesses). Through, I am not sure if that actually the right way todo it. But it works -- fine enough :)
 
Last edited:
Back
Top Bottom