gotem_content_moderation-1.1.6-alpha1/src/EventSubscriber/ContentModerationSubscriber.php
src/EventSubscriber/ContentModerationSubscriber.php
<?php
namespace Drupal\gotem\EventSubscriber;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\TranslationManager;
use Drupal\gotem\Event\ContentModerationEvent;
use Drupal\gotem\Moderation\ModerationInterface;
use Drupal\gotem\Service\ThresholdService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\node\NodeInterface;
use Drupal\node\Entity\Node;
/**
* Class ContentModerationSubscriber.
* Handles content moderation events.
*/
class ContentModerationSubscriber implements EventSubscriberInterface {
/**
* The moderation service.
*
* @var \Drupal\gotem\Moderation\ModerationInterface
*/
protected $moderationService;
/**
* The threshold service.
*
* @var \Drupal\gotem\Service\ThresholdService
*/
protected $thresholdService;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountProxyInterface
*/
protected $currentUser;
/**
* The messenger service.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* The logger channel.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* The string translation service.
*
* @var \Drupal\Core\StringTranslation\TranslationManager
*/
protected $stringTranslation;
/**
* Static flag to track if moderation is in progress.
*
* @var bool
*/
protected static $moderationInProgress = FALSE;
/**
* Constructs a new ContentModerationSubscriber.
*
* @param \Drupal\gotem\Moderation\ModerationInterface $moderation_service
* The moderation service.
* @param \Drupal\gotem\Service\ThresholdService $threshold_service
* The threshold service.
* @param \Drupal\Core\Session\AccountProxyInterface $current_user
* The current user.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger
* The logger service.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
* @param \Drupal\Core\StringTranslation\TranslationManager $string_translation
* The string translation service.
*/
public function __construct(
ModerationInterface $moderation_service,
ThresholdService $threshold_service,
AccountProxyInterface $current_user,
MessengerInterface $messenger,
LoggerChannelFactoryInterface $logger,
StateInterface $state,
TranslationManager $string_translation
) {
$this->moderationService = $moderation_service;
$this->thresholdService = $threshold_service;
$this->currentUser = $current_user;
$this->messenger = $messenger;
$this->logger = $logger->get('gotem');
$this->state = $state;
$this->stringTranslation = $string_translation;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return [
ContentModerationEvent::EVENT_NAME => 'onContentModeration',
];
}
/**
* Responds to content moderation event.
*
* @param \Drupal\gotem\Event\ContentModerationEvent $event
* The content moderation event.
*/
public function onContentModeration(ContentModerationEvent $event) {
// Exit if moderation is already in progress to prevent loops.
if (self::$moderationInProgress) {
$this->logger->info('Moderation already in progress, skipping for this request.');
return;
}
$entity = $event->getEntity();
// Check if moderation is enabled.
if (!$this->state->get('gotem.settings.moderation_enabled', TRUE)) {
$this->logger->info('Moderation is disabled for node ID @id.', ['@id' => $entity->id()]);
return;
}
// Only moderate if the node is published.
if ($entity instanceof NodeInterface && $entity->isPublished()) {
$text_to_check = $this->extractTextFromFields($entity, ['title', 'body']);
if (empty(trim($text_to_check))) {
return;
}
// Mark moderation as in progress to prevent re-triggering.
self::$moderationInProgress = TRUE;
// Perform the moderation.
$result = $this->moderationService->moderateText($text_to_check);
if ($result && !empty($result['flagged'])) {
// Process moderation result.
$this->processModerationResult($entity, $result);
}
// Reset the flag after processing is done.
self::$moderationInProgress = FALSE;
}
}
/**
* Processes the moderation result.
*
* @param \Drupal\node\NodeInterface $entity
* The node entity.
* @param array $result
* The moderation result.
*/
protected function processModerationResult(NodeInterface $entity, array $result) {
$categories = ['hate', 'violence', 'sexual', 'self-harm'];
// Check moderation score for each category.
foreach ($categories as $category) {
$moderation_score = $result['category_scores'][$category] ?? 0;
// Flag content if it exceeds the threshold for any category.
if ($this->thresholdService->shouldFlagContent($moderation_score, $category)) {
$this->handleFlaggedContent($entity, $result, $category);
break;
}
}
}
/**
* Handles flagged content.
*
* @param \Drupal\node\NodeInterface $entity
* The node entity that was flagged.
* @param array $result
* The moderation result.
* @param string $category
* The category that triggered the flag.
*/
protected function handleFlaggedContent(NodeInterface $entity, array $result, string $category) {
// Check if the node is already unpublished to prevent looping.
if (!$entity->isPublished()) {
$this->logger->info('Node ID @id is already unpublished, skipping moderation.', ['@id' => $entity->id()]);
return;
}
// Create a moderation flag node.
$moderation_flag = Node::create([
'type' => 'moderation_flag',
'title' => 'Flagged content: ' . $entity->id(),
]);
$moderation_flag->save();
// Store the node ID in the State API for redirection after save.
$this->state->set('gotem_flagged_node_id', $entity->id());
// Add warnings to the user.
$this->messenger->addWarning($this->stringTranslation->translate('This content has been flagged for %category and is under review.', ['%category' => ucfirst($category)]));
}
/**
* Extracts text from the given fields of the entity.
*
* @param \Drupal\node\NodeInterface $entity
* The node entity.
* @param array $fields
* The fields to extract text from.
*
* @return string
* The extracted text.
*/
protected function extractTextFromFields(NodeInterface $entity, array $fields) {
$text = '';
foreach ($fields as $field) {
if ($entity->hasField($field) && !$entity->get($field)->isEmpty()) {
$text .= $entity->get($field)->value . ' ';
}
}
return trim($text);
}
}
