You are here

social_event_managers.module in Open Social 10.0.x

File

modules/social_features/social_event/modules/social_event_managers/social_event_managers.module
View source
<?php

/**
 * @file
 * Contains social_event_managers.module.
 */
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultAllowed;
use Drupal\Core\Access\AccessResultForbidden;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\block\Entity\Block;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\social_event\EventEnrollmentInterface;
use Drupal\social_event_managers\SocialEventManagersAccessHelper;
use Drupal\user\Entity\User;
use Drupal\views\ViewExecutable;
use Drupal\social_event\Entity\EventEnrollment;
use Drupal\views_bulk_operations\ViewsBulkOperationsBatch;

/**
 * Implements hook_preprocess_block().
 */
function social_event_managers_preprocess_block(&$variables) {

  /** @var \Drupal\node\Entity\Node $node */
  $node = \Drupal::routeMatch()
    ->getParameter('node');

  // Add variables to sidebar blocks.
  switch ($variables['elements']['#derivative_plugin_id']) {
    case 'managers-event_managers':

      // Show "All" button only when there are more than 10 organisers.
      if ($node->field_event_managers
        ->count() > 10) {
        $variables['view_all_path'] = Url::fromUserInput('/node/' . $node
          ->id() . '/organisers');
        $variables['button_text'] = t('All @label', [
          '@label' => $variables['label']['#markup'],
        ]);
      }
      break;
  }
}

/**
 * Implements hook_views_data_alter().
 */
function social_event_managers_views_data_alter(array &$data) {

  // Create our own views VBO field for enrollments.
  $data['views']['social_views_bulk_operations_bulk_form_enrollments'] = [
    'title' => t('Social Views bulk operations for Enrollments'),
    'help' => t("Process enrollments returned by the view with Views Bulk Operations' actions."),
    'field' => [
      'id' => 'social_views_bulk_operations_bulk_form_enrollments',
    ],
  ];
}

/**
 * Implements hook_activity_send_email_notifications_alter().
 */
function social_event_managers_activity_send_email_notifications_alter(array &$items, array $email_message_templates) {

  // If a member_added_by_event_organiser template is enabled then we add it in
  // the "Message to Me" section.
  if (isset($email_message_templates['member_added_by_event_organiser'])) {
    $items['message_to_me']['templates'][] = 'member_added_by_event_organiser';
  }
}

/**
 * Implements hook_block_access().
 */
function social_event_managers_block_access(Block $block, $operation, AccountInterface $account) {
  if ($operation == 'view' && $block
    ->getPluginId() == 'views_block:managers-event_managers') {

    // Exclude block form edit node page.
    $route_name = \Drupal::routeMatch()
      ->getRouteName();
    if ($route_name == 'entity.node.edit_form') {
      return AccessResult::forbidden();
    }
  }
  $route_name = \Drupal::routeMatch()
    ->getRouteName();
  $excluded_routes = [
    'social_event_managers.vbo.confirm',
    'social_event_managers.vbo.execute_configurable',
  ];
  if (in_array($route_name, $excluded_routes)) {
    if ($operation === 'view' && ($block
      ->getPluginId() === 'social_page_title_block' || $block
      ->getPluginId() === 'views_block:event_enrollments-event_enrollments')) {
      return AccessResult::forbidden();
    }
  }

  // No opinion for other situations really.
  return AccessResult::neutral();
}

/**
 * Implements hook_link_alter().
 */
function social_event_managers_link_alter(&$variables) {

  /** @var \Drupal\Core\Url $url */
  $url = $variables['url'];

  // Let's make sure we reroute the view more link.
  if ($url instanceof Url && !$url
    ->isExternal() && $url
    ->isRouted() && $url
    ->getRouteName() === 'view.event_enrollments.view_enrollments' && stripos(strtolower($variables['text']), 'all enrollments') !== FALSE) {
    $params = $url
      ->getRouteParameters();
    $variables['url'] = Url::fromRoute('view.event_manage_enrollments.page_manage_enrollments', $params);
  }
}

/**
 * Implements hook_entity_access().
 */
function social_event_managers_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {

  // Event Manager & Organizers can view/delete/edit enrollments for events
  // they are organizing.
  if ($entity instanceof EventEnrollmentInterface) {
    if (social_event_manager_or_organizer()) {
      return AccessResult::allowedIf($entity instanceof EventEnrollmentInterface);
    }
  }
}

/**
 * Implements hook_form_alter().
 */
function social_event_managers_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Make sure we pass along the ccorrect view id, display id and node
  // parameter to our custom views bulk forms and redirects.
  if (isset($form['views_bulk_operations_bulk_form']) && isset($form['output'][0]['#view'])) {
    $view =& $form['output'][0]['#view'];
    if ($view instanceof ViewExecutable) {
      $view_id = $view
        ->id();
      $display_id = $view->current_display;
    }
  }
  elseif (strpos($form_id, 'views_form_event_manage_enrollments_page_manage_enrollments') !== FALSE && isset($form['output'][0]['#view'])) {
    $view =& $form['output'][0]['#view'];
    if ($view instanceof ViewExecutable) {
      $view_id = $view
        ->id();
      $display_id = $view->current_display;
    }
  }
  elseif (isset($form['social_views_bulk_operations_bulk_form_enrollments_1']) && isset($form['output'][0]['#view'])) {
    $view =& $form['output'][0]['#view'];
    if ($view instanceof ViewExecutable) {
      $view_id = $view
        ->id();
      $display_id = $view->current_display;
    }
  }
  elseif ($form_id === 'views_bulk_operations_configure_action') {
    $data = $form_state
      ->get('views_bulk_operations');
    $view_id = $data['view_id'];
    $display_id = $data['display_id'];
  }
  if (isset($view_id) && $view_id === 'event_manage_enrollments' && $display_id === 'page_manage_enrollments') {
    $form_state
      ->set('node', \Drupal::routeMatch()
      ->getRawParameter('node'));
    if ($form_id === 'views_bulk_operations_configure_action') {
      $callbacks =& $form['actions']['submit']['#submit'];
    }
    else {
      $callbacks =& $form['#submit'];
    }
    $callbacks[] = '_social_event_managers_views_bulk_operations_bulk_form_submit';
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function social_event_managers_form_node_event_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // On event event edit node form we check if users can alter author.
  $node = \Drupal::routeMatch()
    ->getParameter('node');

  // Not on newly created nodes so we check if there is a route match for a node
  // object.
  if ($node) {

    // Get the current user.
    $user = \Drupal::currentUser();

    // Remove authoring information for everybody on node event edit form.
    $form['author']['#access'] = FALSE;

    // Check for permission. Otherwise you can't change the author.
    // Unless you are the author / have the right permissions.
    if ($user
      ->hasPermission('administer nodes') || $user
      ->id() == $node
      ->getOwnerId()) {
      $form['author']['#access'] = TRUE;
    }
  }
}

/**
 * Implements hook_node_access_records().
 */
function social_event_managers_node_access_records(NodeInterface $node) {
  $grants = [];

  // Only for events.
  if ($node
    ->getType() === 'event' && ($event_managers_ids = array_column($node
    ->get('field_event_managers')
    ->getValue(), 'target_id'))) {

    // Event organizers should be granted access.
    // Load the event managers accounts.
    $users = User::loadMultiple($event_managers_ids);
    foreach ($users as $event_manager) {

      // Event organizers must have access
      // to view the record in the first place.
      if ($node
        ->access('view', $event_manager)) {

        // Add grant.
        $grants[] = [
          'realm' => 'social_event_managers:' . $node
            ->id(),
          'gid' => $event_manager
            ->id(),
          'grant_view' => 1,
          'grant_update' => 1,
          'grant_delete' => 0,
        ];
      }
    }
  }
  return $grants;
}

/**
 * Implements hook_node_grants().
 */
function social_event_managers_node_grants(AccountInterface $account, $op) {
  $grants = [];

  // @todo Fetch all nodes this user has access to
  // and add a grant for each of those.
  $query = \Drupal::database()
    ->select('node__field_event_managers', 'em');
  $query
    ->fields('em', [
    'entity_id',
  ]);
  $query
    ->condition('em.field_event_managers_target_id', $account
    ->id());

  // Add grants.
  foreach ($query
    ->execute()
    ->fetchAllAssoc('entity_id') as $nid) {
    $grants['social_event_managers:' . $nid->entity_id][] = $account
      ->id();
  }

  // Tell Drupal about users grants.
  return $grants;
}

/**
 * Implements hook_module_implements_alter().
 */
function social_event_managers_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'node_access') {

    // Remove the gnode implementation, we have a fallback in our hook.
    if (isset($implementations['gnode']) && function_exists('gnode_node_access')) {
      unset($implementations['gnode']);
    }
  }
}

/**
 * Implements hook_node_access().
 *
 * Remember: if any module returns forbidden and denies access to certain node
 * and operation it will not allow the user to do the operation on the node.
 *
 * We need this implementation because we also want to give edit access to event
 * manager regardless these scenarios thought of in gnode_node_access:
 * - is a member in the group and:
 * - has edit own or edit any permission in the group
 *
 * The gnode module specifically returns allowed if any of the above scenarios
 * are met, but forbidden in all the other scenarios. Our code ensures that if
 * we are in operation update and if gnode already returns forbidden we are able
 * to return an allowed if user is an event manager.
 */
function social_event_managers_node_access(NodeInterface $node, $op, AccountInterface $account) {

  // Only continue if the gnode module is enabled.
  if (function_exists('gnode_node_access')) {
    $gnode_access = gnode_node_access($node, $op, $account);
    if ($op === 'update') {
      if ($gnode_access instanceof AccessResultForbidden) {
        $social_event_managers_access = SocialEventManagersAccessHelper::getEntityAccessResult($node, $op, $account);

        // Only return the result of SocialEventManagersAccessHelper
        // if it is allowed.
        if ($social_event_managers_access instanceof AccessResultAllowed) {
          return $social_event_managers_access;
        }
      }
      return $gnode_access;
    }
    return $gnode_access;
  }
  return SocialEventManagersAccessHelper::getEntityAccessResult($node, $op, $account);
}

/**
 * Implements hook_menu_local_tasks_alter().
 */
function social_event_managers_menu_local_tasks_alter(&$data, $route_name) {
  $can_show_managers_link = FALSE;
  $routes_to_check = [
    'view.event_enrollments.view_enrollments',
    'entity.node.canonical',
    'view.managers.view_managers',
    'view.manage_enrollments.page',
    'view.event_manage_enrollments.page_manage_enrollments',
  ];
  if (in_array($route_name, $routes_to_check)) {
    $node = \Drupal::service('current_route_match')
      ->getParameter('node');
    if (!is_null($node) && !$node instanceof Node) {
      $node = Node::load($node);
    }
    if ($node instanceof Node && $node
      ->getType() === 'event') {
      $can_show_managers_link = TRUE;

      // Should we minimise the amount of tabs? If so we remove enrollees as we
      // show it in the management tab.
      if (!empty($data['tabs'][0]['views_view:view.event_enrollments.view_enrollments'])) {
        unset($data['tabs'][0]['views_view:view.event_enrollments.view_enrollments']);
      }

      // For Guest Enrollments alone we hide them as well and place them in the
      // manage tab.
      if (!empty($data['tabs'][0]['views_view:view.manage_enrollments.page'])) {
        unset($data['tabs'][0]['views_view:view.manage_enrollments.page']);
      }
      if (!social_event_manager_or_organizer()) {
        $data['tabs'][0]['views_view:view.event_manage_enrollments.page_manage_enrollments']['#link']['title'] = t('Enrollments');
      }
    }
  }

  // PLace this here, since hiding it should happen
  // always and not only on the mentioned routes.
  if (!$can_show_managers_link) {
    unset($data['tabs'][0]['views_view:view.managers.view_managers']);
    unset($data['tabs'][0]['views_view:view.event_manage_enrollments.page_manage_enrollments']);
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function social_event_managers_form_node_event_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Set author of event as event organiser automatically.
  $config = \Drupal::configFactory()
    ->getEditable('social_event_managers.settings');
  if ($config
    ->get('author_as_manager')) {
    if ($form_state
      ->getTriggeringElement() === NULL) {
      $account = \Drupal::currentUser();
      $user = \Drupal::entityTypeManager()
        ->getStorage('user')
        ->load($account
        ->id());
      $last_key = $form['field_event_managers']['widget']['#max_delta'];
      $form['field_event_managers']['widget'][$last_key]['target_id']['#default_value'] = $user;
    }
  }

  // Update the field event managers widget form.
  if (isset($form['field_event_managers']['widget']['add_more'])) {
    $form['field_event_managers']['widget']['add_more']['#value'] = t('Add another organizer');
  }
}

/**
 * Implements hook_activity_recipient_organizer_alter().
 */
function social_event_managers_activity_recipient_organizer_alter(array &$recipients, Node $event, $data) {
  $receiver = '';
  $organizers = $event
    ->get('field_event_managers')
    ->getValue();
  if ($data['target_type'] === 'event_enrollment' && !empty($data['target_id'])) {
    $enrollment = EventEnrollment::load($data['target_id']);
    $receiver = $enrollment
      ->getAccount();
  }

  // If there are more organizers we want them to receive a notification too
  // so we add them to the array of recipients.
  if (!empty($organizers)) {
    foreach ($organizers as $organizer) {

      // We don't want Organizers to receive activity_on_events_im_organizing.
      // It will already receive it as part of a different context.
      if (!empty($receiver) && $organizer['target_id'] === $receiver) {
        continue;
      }

      // Make sure we don't add the people twice.
      if (!in_array($organizer['target_id'], array_column($recipients, 'target_id'))) {
        $recipients[] = [
          'target_type' => 'user',
          'target_id' => $organizer['target_id'],
        ];
      }
    }
  }
}

/**
 * Implements hook_batch_alter().
 */
function social_event_managers_batch_alter(&$batch) {
  if (!isset($batch['source_url'])) {
    return;
  }
  $actions = [
    'social_event_enrolments_export_enrollments_action',
    'social_event_managers_send_email_action',
    'social_event_managers_delete_event_enrollment_action',
    'social_event_an_enroll_enrolments_export_action',
    'social_event_an_enroll_send_email_action',
  ];

  /** @var \Drupal\Core\Url $url */
  $url =& $batch['source_url'];
  if ($url
    ->getRouteName() === 'social_event_managers.vbo.confirm' || $url
    ->getRouteName() === 'views_bulk_operations.confirm' || $url
    ->getRouteName() === 'views_bulk_operations.execute_batch') {

    // Get the action ID.
    $action_id = _social_event_managers_get_action_id($batch);
    $batch['sets'][0]['results']['action'] = $action_id;
    if (in_array($action_id, $actions, TRUE)) {
      $batch['sets'][0]['finished'] = '_social_event_managers_action_batch_finish';
    }
  }
}

/**
 * Action batch finished callback.
 *
 * @param bool $success
 *   Was the process successfull?
 * @param array $results
 *   Batch process results array.
 * @param array $operations
 *   Performed operations array.
 */
function _social_event_managers_action_batch_finish($success, array $results, array $operations) {

  // When we do a bulk action on all the items in a view, across multiple pages,
  // the saveList function needs to be called. So after pre-populating the list
  // the actual action is performed on the entities.
  if (!empty($results['view_id']) && !empty($results['display_id'])) {
    ViewsBulkOperationsBatch::saveList(TRUE, $results, $operations);
    return;
  }
  $operations = array_count_values($results['operations']);
  $results_count = 0;
  foreach ($operations as $count) {
    $results_count += $count;
  }
  $hook = 'social_event_managers_action_' . $results['action'] . '_finish';
  foreach (\Drupal::moduleHandler()
    ->getImplementations($hook) as $module) {
    $function = $module . '_' . $hook;
    $messages = $function($success);
    if (is_array($messages)) {
      $fields = 0;
      foreach ($messages as $type => $message) {
        if (($type === 'singular' || $type === 'plural') && !empty($message) && is_string($message)) {
          $fields++;
        }
      }
      if ($fields === 2) {

        // @todo source strings will never be translatable when we use
        // variables. This should be refactored.
        $message = \Drupal::translation()
          ->formatPlural($results_count, $messages['singular'], $messages['plural']);
        $type = $success ? MessengerInterface::TYPE_STATUS : MessengerInterface::TYPE_WARNING;
        \Drupal::messenger()
          ->addMessage($message, $type);
      }
    }
  }
}

/**
 * Function to get the action id of a batch.
 *
 * @param array $batch
 *   The batch array.
 *
 * @return string
 *   Returns the batch action id.
 */
function _social_event_managers_get_action_id(array &$batch) {

  /** @var \Drupal\Core\Form\FormStateInterface $form_state */
  $form_state =& $batch['form_state'];
  $action_id = '';
  if ($form_state instanceof FormStateInterface) {
    $data = $form_state
      ->get('views_bulk_operations');
    $action_id = $data['action_id'];
  }
  else {
    foreach ($batch['sets'][0]['operations'] as $operations) {
      if (empty($operations) || !is_array($operations)) {
        break;
      }
      foreach ($operations as $operation) {
        if (empty($operation) || !is_array($operation)) {
          break;
        }
        foreach ($operation as $items) {
          if (empty($items) || !is_array($items)) {
            break;
          }
          if (!empty($items['action_id'])) {
            $action_id = $items['action_id'];
            break;
          }
        }
      }
    }
  }
  return $action_id;
}

/**
 * Implements hook_social_event_action_ACTION_ID_finish().
 */
function social_event_managers_social_event_managers_action_social_event_managers_send_email_action_finish($success) {
  if ($success) {

    // When the queue storage module is enabled the email is send in the
    // background.
    if (\Drupal::moduleHandler()
      ->moduleExists('social_queue_storage')) {
      return [
        'singular' => 'Your email will be send to 1 selected enrollee',
        'plural' => 'Your email will be send to @count selected enrollees',
      ];
    }
    return [
      'singular' => 'Your email has been sent to 1 selected enrollee successfully',
      'plural' => 'Your email has been sent to @count selected enrollees successfully',
    ];
  }
  return [
    'singular' => 'Your email has not been sent to 1 selected enrollee successfully',
    'plural' => 'Your email has not been sent to @count selected enrollees successfully',
  ];
}

/**
 * Implements hook_social_event_action_ACTION_ID_finish().
 */
function social_event_managers_social_event_managers_action_social_event_managers_delete_event_enrollment_action_finish($success) {
  if ($success) {
    return [
      'singular' => '1 selected enrollee has been removed from the event successfully',
      'plural' => '@count selected enrollees have been removed from the event successfully',
    ];
  }
  return [
    'singular' => '1 selected enrollee has not been removed from the event successfully',
    'plural' => '@count selected enrollees have not been removed from the event successfully',
  ];
}

/**
 * Add node ID to the route of action confirmation step.
 */
function _social_event_managers_views_bulk_operations_bulk_form_submit($form, FormStateInterface $form_state) {

  /** @var \Drupal\Core\Url $url */
  $url = $form_state
    ->getRedirect();
  $node = '';
  if ($form_state
    ->get('node')) {
    $node = $form_state
      ->get('node');
  }
  if (empty($node)) {
    $route = \Drupal::routeMatch()
      ->getParameter('node');
    $node = $route
      ->id();
  }
  $route_parameters = [
    'node' => $node,
  ];
  if (!empty($node)) {
    if ($url
      ->getRouteName() === 'views_bulk_operations.execute_configurable') {
      $url = Url::fromRoute('social_event_managers.vbo.execute_configurable', $route_parameters);
    }
    if ($url
      ->getRouteName() === 'social_event_managers.vbo.confirm') {
      $url = Url::fromRoute('social_event_managers.vbo.confirm', $route_parameters);
    }
  }
  $form_state
    ->setRedirectUrl($url);
}

/**
 * Implements hook_preprocess_HOOK().
 */
function social_event_managers_preprocess_views_view(&$variables) {
  if (!\Drupal::moduleHandler()
    ->moduleExists('social_event_invite')) {

    /** @var \Drupal\views\ViewExecutable $view */
    $view =& $variables['view'];

    // Remove header & VBO actions from the Enrollment Management tab if the
    // user is not a manager or organiser.
    if ($view
      ->id() === 'event_manage_enrollments') {
      if (!social_event_manager_or_organizer()) {
        unset($variables['rows']['social_views_bulk_operations_bulk_form_enrollments_1']);
        unset($variables['rows']['header']);
        unset($variables['rows']['actions']);
      }
      $block = \Drupal::entityTypeManager()
        ->getStorage('block')
        ->load('socialblue_local_actions');
      $variables['header']['actions'] = \Drupal::entityTypeManager()
        ->getViewBuilder('block')
        ->view($block);
    }
  }
}

/**
 * Implements hook_views_pre_view().
 */
function social_event_managers_views_pre_view(ViewExecutable $view, $display_id, array &$args) {

  // Remove fields from the Enrollment Management tab if the user is not a
  // manager or organiser.
  if ($view
    ->id() === 'event_manage_enrollments') {
    if (!social_event_manager_or_organizer()) {
      $fields = $view->display_handler
        ->getOption('fields');
      $fields['operations_1']['exclude'] = TRUE;
      $fields['social_views_bulk_operations_bulk_form_enrollments_1']['exclude'] = TRUE;
      $view->display_handler
        ->overrideOption('fields', $fields);
    }
  }
}

Functions

Namesort descending Description
social_event_managers_activity_recipient_organizer_alter Implements hook_activity_recipient_organizer_alter().
social_event_managers_activity_send_email_notifications_alter Implements hook_activity_send_email_notifications_alter().
social_event_managers_batch_alter Implements hook_batch_alter().
social_event_managers_block_access Implements hook_block_access().
social_event_managers_entity_access Implements hook_entity_access().
social_event_managers_form_alter Implements hook_form_alter().
social_event_managers_form_node_event_edit_form_alter Implements hook_form_FORM_ID_alter().
social_event_managers_form_node_event_form_alter Implements hook_form_FORM_ID_alter().
social_event_managers_link_alter Implements hook_link_alter().
social_event_managers_menu_local_tasks_alter Implements hook_menu_local_tasks_alter().
social_event_managers_module_implements_alter Implements hook_module_implements_alter().
social_event_managers_node_access Implements hook_node_access().
social_event_managers_node_access_records Implements hook_node_access_records().
social_event_managers_node_grants Implements hook_node_grants().
social_event_managers_preprocess_block Implements hook_preprocess_block().
social_event_managers_preprocess_views_view Implements hook_preprocess_HOOK().
social_event_managers_social_event_managers_action_social_event_managers_delete_event_enrollment_action_finish Implements hook_social_event_action_ACTION_ID_finish().
social_event_managers_social_event_managers_action_social_event_managers_send_email_action_finish Implements hook_social_event_action_ACTION_ID_finish().
social_event_managers_views_data_alter Implements hook_views_data_alter().
social_event_managers_views_pre_view Implements hook_views_pre_view().
_social_event_managers_action_batch_finish Action batch finished callback.
_social_event_managers_get_action_id Function to get the action id of a batch.
_social_event_managers_views_bulk_operations_bulk_form_submit Add node ID to the route of action confirmation step.