activity_stream-1.0.x-dev/src/ActivityMessageFactory.php
src/ActivityMessageFactory.php
<?php
namespace Drupal\activity_stream;
use Drupal\activity_stream\Plugin\ActivityContextManager;
use Drupal\activity_stream\Plugin\ActivityEntityConditionManager;
use Drupal\activity_stream\Entity\NotificationConfigEntityInterface;
use Drupal\Core\Entity\EntityBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\field\FieldConfigInterface;
use Drupal\message\Entity\Message;
use Drupal\user\EntityOwnerInterface;
use Drupal\activity_stream\Entity\Activity;
/**
* Class ActivityMessageFactory.
*
* @package Drupal\activity_stream\Service
* Service that determines which actions need to be performed.
*/
class ActivityMessageFactory {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The condition manager.
*
* @var \Drupal\activity_stream\Plugin\ActivityEntityConditionManager
*/
protected $activityEntityConditionManager;
/**
* The context manager.
*
* @var \Drupal\activity_stream\Plugin\ActivityContextManager
*/
protected $activityContextManager;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* ActivityLoggerFactory constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager.
* @param \Drupal\activity_stream\Plugin\ActivityEntityConditionManager $activityEntityConditionManager
* The condition manager.
* @param \Drupal\activity_stream\Plugin\ActivityContextManager $activityContextManager
* The context manager.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
* The module handler.
*/
public function __construct(
EntityTypeManagerInterface $entityTypeManager,
ActivityEntityConditionManager $activityEntityConditionManager,
ActivityContextManager $activityContextManager,
ModuleHandlerInterface $moduleHandler) {
$this->entityTypeManager = $entityTypeManager;
$this->activityEntityConditionManager = $activityEntityConditionManager;
$this->activityContextManager = $activityContextManager;
$this->moduleHandler = $moduleHandler;
}
/**
* Create message entities.
*
* @param \Drupal\Core\Entity\EntityBase $entity
* Entity object to create a message for.
* @param string $action
* Action string. Defaults to 'create'.
*/
public function createMessages(EntityBase $entity, $action): void {
$helper_service = \Drupal::service('activity_stream.helper');
// Get all activity configs that are responsible for creating items.
$activity_types = $this->getActivtyConfig($action, $entity);
// Loop through those message types and create messages.
foreach ($activity_types as $activity_type => $values) {
$activity_date_value = $values['activity_date'];
if (isset($activity_date_value) && !empty($activity_date_value)) {
// Check if that field would be available in the given entity.
// And check if group relationship present.
if ($entity->getEntityTypeId() === 'group_relationship') {
$related_entity = $entity->getEntity();
if ($related_entity->hasField($activity_date_value)) {
$activity_date = $related_entity->$activity_date_value->value;
}
}
else {
if ($entity->hasField($activity_date_value)) {
$activity_date = $entity->$activity_date_value->value;
}
}
}
// Create the ones applicable for this bundle.
// Determine destinations.
$destinations = [];
if (!empty($values['destinations']) && is_array($values['destinations'])) {
foreach ($values['destinations'] as $destination) {
$destinations[] = ['target_id' => $destination];
}
}
// Get context
$activity_context = $values['context'];
// Get message template
if (isset($values['message_template']) && !empty($values['message_template'])) {
// Set the values.
$new_message['template'] = $values['message_template'];
// Get the owner or default to anonymous.
if ($entity instanceof EntityOwnerInterface && $entity->getOwner() !== NULL) {
$new_message['uid'] = (string) $entity->getOwner()->id();
}
else {
$new_message['uid'] = '0';
}
if ($entity instanceof NotificationConfigEntityInterface) {
$new_message['field_message_related_object'] = [
'target_type' => $entity->getEntityTypeId(),
'target_id' => $entity->getUniqueId(),
];
$new_message['created'] = $entity->getCreatedTime();
}
else {
$new_message['field_message_related_object'] = [
'target_type' => $entity->getEntityTypeId(),
'target_id' => $entity->id(),
];
// The flagging entity does not implement getCreatedTime().
if ($entity->getEntityTypeId() === 'flagging') {
$new_message['created'] = $entity->get('created')->value;
}
elseif ($entity->getEntityTypeId() === 'organizer') {
$new_message['created'] = $entity->get('created')->value;
}
else {
$new_message['created'] = $entity->getCreatedTime();
}
}
$new_message['field_message_context'] = $activity_context;
$new_message['field_message_destination'] = $destinations;
// Create the message only if it doesn't exist.
if (!$this->checkIfMessageExist($new_message['template'], $new_message['field_message_context'], $new_message['field_message_destination'], $new_message['field_message_related_object'], $new_message['uid'])) {
$message = Message::create($new_message);
$message->save();
if ($message instanceof Message) {
$data = [
'activity_config' => $activity_type,
'mid' => $message->id(),
'message_template' => $message->getTemplate()->id(),
'actor' => $message->getOwner()->id(),
'context' => $activity_context,
'destination' => $destinations,
'related_object' => $new_message['field_message_related_object'],
'last_uid' => 0,
'status' => NULL,
];
if (isset($activity_date)) {
$data['activity_date'] = $activity_date;
}
if ($helper_service->isActivityDirect($activity_type)) {
$activities = \Drupal::service('activity_stream.activity_factory')->createActivities($data);
}
else {
$queue = \Drupal::queue('activity_stream_activities');
$queue->createItem($data);
}
}
}
}
else {
$data = [
'activity_config' => $activity_type,
'actor' => $entity->getOwner()->id(),
'context' => $activity_context,
'destination' => $destinations,
'related_object' => [
'target_type' => $entity->getEntityTypeId(),
'target_id' => $entity->id(),
],
'last_uid' => 0,
'status' => NULL,
];
if (isset($activity_date)) {
$data['activity_date'] = $activity_date;
}
if ($helper_service->isActivityDirect($activity_type)) {
$activities = \Drupal::service('activity_stream.activity_factory')->createActivities($data);
}
else {
$queue = \Drupal::queue('activity_stream_activities');
$queue->createItem($data);
}
}
}
}
/**
* Get message templates for action and entity.
*
* @param string $action
* Action string, e.g. 'create'.
* @param \Drupal\Core\Entity\EntityBase $entity
* Entity object.
*
* @return array
* Array of message types.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Component\Plugin\Exception\PluginException
*/
public function getActivtyConfig($action, EntityBase $entity) {
// Init.
$activity_types = [];
// Message type storage.
$activity_config_storage = $this->entityTypeManager->getStorage('activity_config');
// Check all enabled messages.
foreach ($activity_config_storage->loadByProperties(['status' => '1']) as $key => $activity_type) {
$activity_entity_bundles = $activity_type->get('activity_bundle_entities');
$activity_entity_action = $activity_type->get('activity_entity_action');
$activity_context = $activity_type->get('activity_context');
$activity_destinations = $activity_type->get('activity_destinations');
$activity_entity_condition = $activity_type->get('activity_entity_condition');
$activity_message_template = $activity_type->get('activity_message_template');
$activity_direct = $activity_type->get('activity_direct');
$activity_aggregate = $activity_type->get('activity_aggregate');
$activity_date = $activity_type->get('activity_date');
if ($activity_message_template === '_none') {
$activity_message_template = NULL;
}
if ($activity_entity_action !== $action) {
continue;
}
$entity_bundle_name = $entity->getEntityTypeId() . '-' . $entity->bundle();
if (!in_array($entity_bundle_name, $activity_entity_bundles, TRUE)) {
continue;
}
if (!$this->activityContextManager->hasDefinition($activity_context)) {
continue;
}
if (
!empty($activity_entity_condition)
&& $this->activityEntityConditionManager->hasDefinition($activity_entity_condition)
&& !$this->activityEntityConditionManager->createInstance($activity_entity_condition)->isValidEntityCondition($entity)
) {
continue;
}
$context_plugin = $this->activityContextManager->createInstance($activity_context);
if ($context_plugin->isValidEntity($entity)) {
$activity_types[$key] = [
'activity_type' => $activity_type,
'bundle' => $entity_bundle_name,
'destinations' => $activity_destinations,
'context' => $activity_context,
'message_template' => $activity_message_template,
'activity_direct' => $activity_direct,
'activity_aggregate' => $activity_aggregate,
'activity_date' => $activity_date,
];
}
}
// Return the message types that belong to the requested action.
return $activity_types;
}
/**
* Checks that a message template is compatible with the activity system.
*
* To support activities, various fields need to exist on a message template,
* with the right configuration. Below are the most important configurations
* for a field that are checked, though these are not complete examples of
* a field config.
*
* ```yaml
* message.<message_type>.field_message_context:
* langcode: en
* status: TRUE
* id: message.<message_type>.field_message_context
* field_type: list_string
* module: ['options']
* field_name: field_message_context
* required: FALSE
* translatable: FALSE
*
* message.<message_type>.field_message_destination:
* langcode: en
* status: TRUE
* id: message.<message_type>.field_message_destination
* field_type: list_string
* module: ['options']
* field_name: field_message_destination
* required: FALSE
* translatable: FALSE
*
* message.<message_type>.field_message_related_object:
* langcode: en
* status: TRUE
* id: message.<message_type>.field_message_related_object
* field_type: dynamic_entity_reference
* module: ['dynamic_entity_reference']
* field_name: field_message_related_object
* required: FALSE
* translatable: FALSE
* ```
*
* @param string $message_type
* The ID of the message type to check for.
*
* @return bool
* Whether the message type is compatible.
*/
private function debugMessageTemplateIsCompatible(string $message_type) : bool {
$config_storage = $this->entityTypeManager
->getStorage('field_config');
$field_message_context = $config_storage->load("message.$message_type.field_message_context");
if (!$field_message_context instanceof FieldConfigInterface) {
return FALSE;
}
$field_message_destination = $config_storage->load("message.$message_type.field_message_destination");
if (!$field_message_destination instanceof FieldConfigInterface) {
return FALSE;
}
$field_message_related_object = $config_storage->load("message.$message_type.field_message_related_object");
if (!$field_message_related_object instanceof FieldConfigInterface) {
return FALSE;
}
return TRUE;
}
/**
* Checks if a message already exists.
*
* @param string $message_type
* The message type.
* @param string $context
* The context of the message.
* @param array $destination
* The array of destinations to check for, include delta as well.
* @param array $related_object
* The related object, include target_type and target_id in array.
* @param string $uid
* The uid of the message.
*
* @return bool
* Returns true if the message exists.
*/
public function checkIfMessageExist(string $message_type, string $context, array $destination, array $related_object, string $uid): bool {
$exists = FALSE;
$query = $this->entityTypeManager->getStorage('message')->getQuery();
$query->condition('template', $message_type);
$query->condition('field_message_related_object.target_id', $related_object['target_id']);
$query->condition('field_message_related_object.target_type', $related_object['target_type']);
$query->condition('field_message_context', $context);
$query->condition('uid', $uid);
foreach ($destination as $delta => $dest_value) {
$query->condition('field_message_destination.target_id', $delta);
}
$query->accessCheck(FALSE);
$ids = $query->execute();
$allowed_duplicates = ['moved_content_between_groups'];
$this->moduleHandler->alter('activity_allowed_duplicates', $allowed_duplicates);
if (!empty($ids) && !in_array($message_type, $allowed_duplicates)) {
$exists = TRUE;
}
return $exists;
}
}
