activity_stream-1.0.x-dev/src/ActivityFactory.php

src/ActivityFactory.php
<?php

namespace Drupal\activity_stream;

use Drupal\activity_stream\Entity\Activity;
use Drupal\activity_stream\Plugin\ActivityDestinationManager;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Utility\Token;
use Drupal\language\ConfigurableLanguageManagerInterface;
use Drupal\message\Entity\Message;
use Drupal\message\MessageInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\group\Entity\GroupInterface;
use Drupal\activity_stream\ActivityConfigInterface;


/**
 * Class ActivityFactory to create Activity items based on ActivityLogs.
 *
 * @package Drupal\activity_stream
 */
class ActivityFactory extends ControllerBase {

  /**
   * Activity destination manager.
   *
   * @var \Drupal\activity_stream\Plugin\ActivityDestinationManager
   */
  protected $activityDestinationManager;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The connection to the database.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $database;

  /**
   * The language manager.
   *
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  protected $languageManager;

  /**
   * The token replacement instance.
   *
   * @var \Drupal\Core\Utility\Token
   */
  protected $token;

  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * ActivityFactory constructor.
   *
   * @param \Drupal\activity_stream\Plugin\ActivityDestinationManager $activityDestinationManager
   *   The activity destination manager.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Database\Connection $database
   *   The connection to the database.
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   The new language manager.
   * @param \Drupal\Core\Utility\Token $token
   *   The token replacement instance.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler.
   */
  public function __construct(
    ActivityDestinationManager $activityDestinationManager,
    EntityTypeManagerInterface $entity_type_manager,
    Connection $database,
    LanguageManagerInterface $language_manager,
    Token $token,
    ModuleHandlerInterface $module_handler
  ) {
    $this->activityDestinationManager = $activityDestinationManager;
    $this->entityTypeManager = $entity_type_manager;
    $this->database = $database;
    $this->languageManager = $language_manager;
    $this->token = $token;
    $this->moduleHandler = $module_handler;
  }

  /**
   * Create the activities based on a data array.
   *
   * @param array $data
   *   An array of data to create activity from.
   *
   * @return array
   *   An array of created activities.
   */
  public function createActivities(array $data) {
    $activities = $this->buildActivities($data);

    return $activities;
  }

  /**
   * Build the activities based on a data array.
   *
   * @param array $data
   *   An array of data to create activity from.
   *
   * @return array
   *   An array of created activities.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  protected function buildActivities(array $data) {
    $activities = [];

    // Load message from message id only if available
    if (isset($data['mid']) && !empty($data['mid'])) {
      $message = $this->entityTypeManager
        ->getStorage('message')
        ->load($data['mid']);
    }

    // Load Activity config from activity config id
    $activity_config = $this->entityTypeManager
      ->getStorage('activity_config')
      ->load($data['activity_config']);
    
    // Activity config not needed in data array
    unset($data['activity_config']);

    if (!$activity_config instanceof ActivityConfigInterface) {     
      return $activities;
    }

    $activity_direct = $activity_config->get('activity_direct');
    $activity_aggregate = $activity_config->get('activity_aggregate');
    $activity_date = $activity_config->get('activity_date');  


    // Initialize fields for new activity entity.
    $activity_fields = [
      'field_activity_destinations' => $this->getFieldDestinations($data),
      'field_activity_entity' => $this->getFieldEntity($data),
    ];

    // In case we have a message we want to attach it to our activity
    if (isset($message) && $message instanceof MessageInterface) {
      $activity_fields['field_activity_message'] = $this->getFieldMessage($data);
    }  
    
    // In case we have an activity data
    if (isset($data['activity_date']) && is_numeric($data['activity_date'])) {
      $activity_fields['field_activity_date'] = $data['activity_date'];
    }
    else {
      $activity_fields['field_activity_date'] = time();
    }

    // Check if aggregation is enabled for this activity type.
    // @todo Consider if we should put aggregation to separate service.
    if ($activity_aggregate) {

      // First let's get the bundle
      $activity_entity = $this->entityTypeManager->getStorage('activity_stream_activity');

      $bundle = $this->getBundleFromRelatedEntity($data);

      $target_type = $this->getTypeFromFieldEntity($data);

      $entity_id = $this->getEntityIdFromFieldEntity($data);

      $destinations = $this->getConditionDestination($data); 

      $storage = $this->entityTypeManager->getStorage('activity_stream_activity');
      $entity_ids = $storage->getQuery()
        ->condition('field_activity_entity.target_type', $target_type)
        ->condition('field_activity_entity.target_id', $entity_id)
        ->condition('uid', $this->getActor($data))
        ->condition('field_activity_destinations', $destinations, 'IN')
        ->condition('activity_entity_bundle', $bundle, '=')
        ->accessCheck(FALSE)
        ->execute();

      if (!empty($entity_ids)) {
        $existing_activities = $storage->loadMultiple($entity_ids);
        foreach ($existing_activities as $existing_activity) {       
          $existing_activity->delete();
        }
      }        
    }
    
    $activities = $this->createActivitiesFromRecipients($data, $activity_fields); 

    return $activities;
  
  }

  /**
   * Create activities based on recipients
   *
   * @param array $data
   * @param array $activity_fields
   * @return array 
   *   Empty array or array of created activities.
   */
  protected function createActivitiesFromRecipients(array $data, array $activity_fields): array {

    $user_recipients = [];
    $activities = [];
    $actor = FALSE;

    // Get the recipients
    $recipients = $this->getFieldRecipientUser($data);

    \Drupal::logger('debug')->warning('<pre><code>' . print_r($recipients, TRUE) . '</code></pre>');
    

    // Should we use the recipients as actors
    if (isset($recipients['actors'])) {
      $recipients = $recipients['actors']; 
      unset($recipients['actors']);
      $actor = TRUE;      
    }

    if (!empty($recipients)) {
      $activity_by_type = array_column($recipients, 'target_type');
      foreach ($activity_by_type as $recipients_key => $target_type) {
        // For all one to one target entity types we create an activity.
        if ($target_type !== 'user') {          
          $activity_fields['field_activity_recipient_group'] = $recipients[$recipients_key];
          $activity = $this->entityTypeManager
            ->getStorage('activity_stream_activity')
            ->create($activity_fields);
          $activities[] = $activity->save();          
        }

        if ($target_type === 'user' ) {
          $user_recipients[] = $recipients[$recipients_key];
        }
   
      }
    
      if (!empty($user_recipients) && !$actor) {
        $activity_fields['field_activity_recipient_user'] = $user_recipients;
        $activity = $this->entityTypeManager
          ->getStorage('activity_stream_activity')
          ->create($activity_fields);
        $activities[] = $activity->save();
      }

      if (!empty($user_recipients) && $actor) {
        // We have to know if we have converted the
        // user recipients to actors.
        $activity_fields['activity_recipient_actor'] = TRUE;
        if ($related_group = $this->getGroupFromGroupRelationship($data)) {
          $activity_fields['field_activity_recipient_group'] = $related_group->id();
        }         
        foreach ($user_recipients as $user) {
          $activity_fields['uid'] = $user['target_id'];
          $activity = $this->entityTypeManager
          ->getStorage('activity_stream_activity')
          ->create($activity_fields);
          $activities[] = $activity->save();
        }
      } 
    }
    else {
      // If we do not have any recipients
      // we have to set the owner!
      if ($owner = $this->getActor($data)) {
        $activity_fields['uid'] = $owner;
      }
      $activity = $this->entityTypeManager
        ->getStorage('activity_stream_activity')
        ->create($activity_fields);
      $activities[] = $activity->save();
    }

    return $activities;

  }

  protected function isValidTimestamp($timestamp) {
    return ((string) (int) $timestamp === $timestamp) 
      && ($timestamp <= PHP_INT_MAX)
      && ($timestamp >= ~PHP_INT_MAX);
  }  

  /**
   * Get field value for 'destination' field from data array.
   */
  protected function getFieldDestinations(array $data, $allowed_destinations = []) {
    $value = NULL;
    if (isset($data['destination'])) {
      $value = $data['destination'];
      if (!empty($allowed_destinations)) {
        foreach ($value as $key => $destination) {
          if (!in_array($destination['value'], $allowed_destinations)) {
            unset($value[$key]);
          }
        }
      }
    }
    return $value;
  }

  protected function getGroupFromGroupRelationship($data) {
    $related_group = FALSE;
    if (isset($data['related_object']) && isset($data['related_object']['target_type'])) {
      if ($data['related_object']['target_type'] === 'group_relationship') {
        $target_id = $data['related_object']['target_id'];
        $group_relationship = $this->entityTypeManager->getStorage('group_relationship')->load($target_id);
        $group = $group_relationship->getGroup();
        if ($group instanceof GroupInterface) {
          $related_group = $group;
        }
      }
    }
    return $related_group;
  }

  /**
   * Get field value for 'entity' field from data array.
   */
  protected function getFieldEntity($data) {    

    // Check if we have a group relationship
    $value = NULL;
    if (isset($data['related_object']) && isset($data['related_object']['target_type'])) {
      if ($data['related_object']['target_type'] === 'group_relationship') {
        $target_id = $data['related_object']['target_id'];
        $group_relationship = $this->entityTypeManager->getStorage('group_relationship')->load($target_id);
        $related_entity = $group_relationship->getEntity();
        $related_entity_target_type = $related_entity->getEntityTypeId();
        $related_entity_target_id = $related_entity->id();       

        $value = [
          'target_type' => $related_entity_target_type,
          'target_id' => $related_entity_target_id           
        ];

      }
      else {
        $value = $data['related_object'];
      }
    }
    return $value;
  }

  protected function getBundleFromFieldEntity($data) {
    $bundle = NULL;
    if (isset($data['related_object'])) {
      $value = $data['related_object'];
      $storage = $this->entityTypeManager->getStorage($value['target_type']);
      $entity = $storage->load($value['target_id']);
      if ($entity instanceof ContentEntityInterface) {
        $bundle = $entity->bundle();        
      }
    }

    return $bundle;

  }

  protected function getBundleFromRelatedEntity($data) {
    $bundle = NULL;
    if (isset($data['related_object'])) {
      $value = $data['related_object'];
      $storage = $this->entityTypeManager->getStorage($value['target_type']);
      $entity = $storage->load($value['target_id']);

      if ($value['target_type'] === 'group_relationship') {        
        $related_entity = $entity->getEntity();
      }
      else {
        $related_entity = $entity;
      }

      if ($related_entity instanceof ContentEntityInterface) {        
        $bundle = $related_entity->bundle();        
      }

    }

    return $bundle;

  }  

  protected function getTypeFromFieldEntity($data) {

    $related_entity = $this->getFieldEntity($data);


    $type = NULL;
    if (isset($related_entity)) {
      $type = $related_entity['target_type'];
    }

    return $type;

  }  

  protected function getEntityIdFromFieldEntity($data) {

    $related_entity = $this->getFieldEntity($data);

    $id = NULL;
    if (isset($related_entity)) {
      $id = $related_entity['target_id'];
    }

    return $id;

  }  

  protected function getConditionDestination($data) {
    $destinations = NULL;
    if (isset($data['destination'])) {
      $value = $data['destination'];
      if (!empty($allowed_destinations)) {
        foreach ($value as $key => $destination) {
          if (!in_array($destination['value'], $allowed_destinations)) {
            unset($value[$key]);
          }
        }
      }
    }

    foreach($value as $value_value)  {
      $destinations[] = $value_value['target_id'];      
    }

    return $destinations;

  }

  /**
   * Get field value for 'message' field from data array.
   */
  protected function getFieldMessage($data) {
    $value = NULL;
    if (isset($data['mid'])) {
      $value = [];
      $value[] = [
        'target_id' => $data['mid'],
      ];
    }
    return $value;
  }

  /**
   * Get field value for 'created' field from data array.
   */
  protected function getCreated(Message $message) {
    return $message->getCreatedTime();
  }

  /**
   * Build the aggregated activities based on a data array.
   */
  protected function buildAggregatedActivites($data, $activity_fields) {
    $activities = [];
    $common_destinations = $this->activityDestinationManager->getListByProperties('isCommon', TRUE);
    $personal_destinations = $this->activityDestinationManager->getListByProperties('isCommon', FALSE);

    // Get related activities.
    $related_activities = $this->getAggregationRelatedActivities($data);
    if (!empty($related_activities)) {
      // Update related activities.
      foreach ($related_activities as $related_activity) {
        $destination = $related_activity->field_activity_destinations->value;
        // If user already have related activity we remove it and create new.
        // And we also remove related activities from common streams.
        if ($related_activity->getOwnerId() == $this->getActor($data) || in_array($destination, $common_destinations)) {
          // @todo Consider if need to delete or unpublish old activites.
          $related_activity->delete();
        }
      }
    }
    
    return $activities;
  }

  /**
   * Get related activities for activity aggregation.
   */
  protected function getAggregationRelatedActivities($data) {
    $activities = [];
    $related_object = $data['related_object'];
    if (!empty($related_object['target_id']) && !empty($related_object['target_type'])) {
      if ($related_object['target_type'] === 'comment') {
        // Get commented entity.
        $comment_storage = $this->entityTypeManager->getStorage('comment');
        $comment = $comment_storage->load($related_object['target_id']);
        // This can happen if the comment was removed before the activity was
        // processed.
        if ($comment === NULL) {
          $comment_ids = NULL;
        }
        else {
          $commented_entity = $comment->getCommentedEntity();
          // Get all comments of commented entity.
          $comment_query = $this->entityTypeManager->getStorage('comment')
            ->getQuery();
          $comment_query->condition('entity_id', $commented_entity->id(), '=');
          $comment_query->condition('entity_type', $commented_entity->getEntityTypeId(), '=');
          $comment_query->accessCheck();
          $comment_ids = $comment_query->execute();
        }
        // Get all activities provided by comments of commented entity.
        if (!empty($comment_ids)) {
          $activity_query = $this->entityTypeManager->getStorage('activity')->getQuery();
          $activity_query->condition('field_activity_entity.target_id', $comment_ids, 'IN');
          $activity_query->condition('field_activity_entity.target_type', $related_object['target_type'], '=');
          // We exclude activities with email, platform_email and notifications
          // destinations from aggregation.
          $aggregatable_destinations = $this->activityDestinationManager->getListByProperties('isAggregatable', TRUE);
          $activity_query->condition('field_activity_destinations.value', $aggregatable_destinations, 'IN');
          $activity_query->accessCheck();
          $activity_ids = $activity_query->execute();
          if (!empty($activity_ids)) {
            $activities = Activity::loadMultiple($activity_ids);
          }
        }
      }
    }
    return $activities;
  }

  /**
   * Get related entity for activity aggregation.
   */
  public function getActivityRelatedEntity($data) {
    $related_object = $data['related_object'];
    $this->moduleHandler->alter('activity_stream_related_entity_object', $related_object, $data);
    return $related_object;
  }

  /**
   * Get unique authors number for activity aggregation.
   */
  protected function getAggregationAuthorsCount(array $data) {
    $count = 0;
    $related_object = $data['related_object'];
    if (isset($related_object['target_type']) && $related_object['target_type'] === 'comment') {
      // Get related entity.
      $related_entity = $this->getActivityRelatedEntity($data);
      if (!empty($related_entity['target_id']) && !empty($related_entity['target_type'])) {
        $query = $this->database->select('comment_field_data', 'cfd');
        $query->addExpression('COUNT(DISTINCT cfd.uid)');
        $query->condition('cfd.status', 1);
        $query->condition('cfd.entity_type', $related_entity['target_type']);
        $query->condition('cfd.entity_id', $related_entity['target_id']);
        $count = $query->execute()->fetchField();
      }
    }
    return $count;
  }

  /**
   * Get field value for 'recipient_group' field from data array.
   */
  protected function getFieldRecipientGroup($data) {

  
    $value = NULL;
    if (isset($data['recipient']['target_type']) && $data['recipient']['target_type'] === 'group') {    
      // Should be in an array for the field.
      $value = [$data['recipient']];
    }
    return $value;
  }

  /**
   * Get field value for 'recipient_user' field from data array.
   */
  protected function getFieldRecipientUser($data) {

    $recipients = [];

    $context_plugin_manager = \Drupal::service('plugin.manager.activity_context.processor');

    if ($context_plugin_manager->hasDefinition($data['context'])) {
      $plugin = $context_plugin_manager->createInstance($data['context']);      
      if ($plugin->setRecipientsToActor()) {
        $recipients['actors'] = $plugin->getRecipients($data, $data['last_uid'], 0);
      }
      else {
        $recipients = $plugin->getRecipients($data, $data['last_uid'], 0);
      }
    }

    return $recipients;

  }

  /**
   * Return the actor uid.
   *
   * @param array $data
   *   Array of data.
   *
   * @return int
   *   Value uid integer.
   */
  protected function getActor(array $data) {
    $value = 0;
    if (isset($data['actor'])) {
      $value = $data['actor'];
    }
    return $value;
  }

  /**
   * Process the message given the arguments saved with it.
   *
   * @param array $arguments
   *   Array with the arguments.
   * @param array $output
   *   Array with the templated text saved in the message template.
   * @param \Drupal\message\Entity\Message $message
   *   Message object.
   *
   * @return array
   *   The templated text, with the placeholders replaced with the actual value,
   *   if there are indeed arguments.
   */
  protected function processArguments(array $arguments, array $output, Message $message) {
    // Check if we have arguments saved along with the message.
    if (empty($arguments)) {
      return $output;
    }

    foreach ($arguments as $key => $value) {
      if (is_array($value) && !empty($value['callback']) && is_callable($value['callback'])) {

        // A replacement via callback function.
        $value += ['pass message' => FALSE];

        if ($value['pass message']) {
          // Pass the message object as-well.
          $value['arguments']['message'] = $message;
        }

        $arguments[$key] = call_user_func_array($value['callback'], $value['arguments']);
      }
    }

    foreach ($output as $key => $value) {
      $output[$key] = new FormattableMarkup($value, $arguments);
    }

    return $output;
  }

  /**
   * Replace placeholders with tokens.
   *
   * @param array $output
   *   The templated text to be replaced.
   * @param array $options
   *   A keyed array of settings and flags to control the token
   *    replacement process. Supported options are:
   *    - langcode: A language code to be used when generating locale-sensitive
   *      tokens.
   *    - callback: A callback function that will be used to post-process the
   *      array of token replacements after they are generated.
   *    - clear: A boolean flag indicating that tokens should be removed from
   *      the final text if no replacement value can be generated.
   *   This will be passed to \Drupal\Core\Utility\Token::replace().
   * @param \Drupal\message\Entity\Message $message
   *   Message object.
   *
   * @return array
   *   The output with placeholders replaced with the token value,
   *   if there are indeed tokens.
   */
  protected function processTokens(array $output, array $options, Message $message) {
    $bubbleable_metadata = new BubbleableMetadata();
    foreach ($output as $key => $value) {
      if (is_string($value)) {
        $output[$key] = $this->token
          ->replace($value, ['message' => $message], $options, $bubbleable_metadata);
      }
      else {
        if (isset($value['value'])) {
          $output[$key] = $this->token
            ->replace($value['value'], ['message' => $message], $options, $bubbleable_metadata);
        }
      }
    }
    $bubbleable_metadata->applyTo($output);

    return $output;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc