You are here

social_group_invite.module in Open Social 8.9

The Social Invite group module.

File

modules/social_features/social_group/modules/social_group_invite/social_group_invite.module
View source
<?php

/**
 * @file
 * The Social Invite group module.
 */
use Drupal\block\Entity\Block;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TypedData\Exception\MissingDataException;
use Drupal\Core\Url;
use Drupal\ginvite\GroupInvitation as GroupInvitationWrapper;
use Drupal\ginvite\Plugin\GroupContentEnabler\GroupInvitation;
use Drupal\group\Entity\GroupContent;
use Drupal\group\Entity\GroupInterface;
use Drupal\group\Entity\GroupRoleInterface;
use Drupal\group\Entity\GroupTypeInterface;
use Drupal\social_group\Entity\Group;

/**
 * Implements hook_menu_local_actions_alter().
 */
function social_group_invite_menu_local_actions_alter(&$local_actions) {

  // Remove "Invite members" button.
  if (isset($local_actions['ginvite.invitation.bulk'])) {
    unset($local_actions['ginvite.invitation.bulk']);
  }

  // Remove "Invite member" button.
  if (isset($local_actions['ginvite.invite_member'])) {
    unset($local_actions['ginvite.invite_member']);
  }
}

/**
 * Implements hook_menu_local_tasks_alter().
 */
function social_group_invite_menu_local_tasks_alter(&$data, $route_name, RefinableCacheableDependencyInterface $cacheability) {

  // Add tasks on these route for invite Groups/Events.
  if ($route_name === 'view.social_group_user_invitations.page_1') {
    $tabs_to_remove = [
      'social_user.groups',
      'social_user.stream',
      'social_user.topics',
      'social_user.events',
      'social_profile.information',
      'profile.user_page:profile',
      'entity.user.canonical',
      'entity.user.edit_form',
    ];
    foreach ($tabs_to_remove as $task_name) {
      if (!empty($data['tabs'][0][$task_name])) {
        unset($data['tabs'][0][$task_name]);
      }
    }
  }

  // Else we remove our custom group task.
  // This will render for example on the user profile etc.
  if ($route_name !== 'view.social_group_user_invitations.page_1' && $route_name !== 'view.user_event_invites.page_user_event_invites') {
    if (!empty($data['tabs'][0]['social_group_invite.user_groups'])) {
      unset($data['tabs'][0]['social_group_invite.user_groups']);
    }
  }
}

/**
 * Implements hook_block_access().
 */
function social_group_invite_block_access(Block $block, $operation, AccountInterface $account) {
  $route_name = \Drupal::routeMatch()
    ->getRouteName();
  $routes_to_check = [
    'entity.group_content.add_form',
    'entity.group_content.delete_form',
  ];

  // Only when on the confirm page of removing or adding invites
  // we remove the block for tasks and heros,the cancel button
  // will allow users to go back.
  if (in_array($route_name, $routes_to_check)) {
    $block_id = $block
      ->getPluginId();

    /** @var \Drupal\group\Entity\GroupContent $group_content */
    $group_content = \Drupal::routeMatch()
      ->getParameter('group_content');

    // Only if we are sure it's a group invitation.
    if (NULL !== $group_content && NULL !== $group_content
      ->getGroupContentType() && $group_content
      ->getGroupContentType()
      ->getContentPluginId() === 'group_invitation') {

      // This is a list of the blocks that this function cares about,
      // if we're being called for a different block we exit early.
      $hide_blocks = [
        'group_hero_block',
        'local_tasks_block',
      ];
      if (!in_array($block_id, $hide_blocks)) {
        return AccessResult::neutral();
      }

      // Remove the blocks.
      return AccessResult::forbidden();
    }
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function social_group_invite_preprocess_views_view(&$variables) {

  /** @var \Drupal\views\ViewExecutable $view */
  $view =& $variables['view'];
  if ($view
    ->id() === 'group_manage_members') {
    $entity = \Drupal::entityTypeManager()
      ->getStorage('block')
      ->load('socialblue_local_actions');
    $variables['header']['actions'] = \Drupal::entityTypeManager()
      ->getViewBuilder('block')
      ->view($entity);
  }

  // See function social_group_preprocess_views_view(&$variables).
  // We have to override the local actions block.
  // and render our own block instance in the view for placement.
  // hook_theme_registry_alter will ensure our hooks is invoked later.
  // That is also why hook_menu_local_actions_alter won't work.
  // Get current group so we can build correct links.
  if ($view
    ->id() === 'group_manage_members') {
    if (_social_group_invite_current_type_enabled_invites()) {
      $entity = \Drupal::entityTypeManager()
        ->getStorage('block')
        ->load('socialinviteactionsblock');
      if (NULL !== $entity) {
        $block_content = \Drupal::entityTypeManager()
          ->getViewBuilder('block')
          ->view($entity);
        if (!empty($block_content)) {
          $variables['header']['actions'] = $block_content;
        }
      }
    }
    else {
      $entity = \Drupal::entityTypeManager()
        ->getStorage('block')
        ->load('socialblue_local_actions');
      $variables['header']['actions'] = \Drupal::entityTypeManager()
        ->getViewBuilder('block')
        ->view($entity);
    }
  }

  // Implement button to go back to the group for our custom view.
  if ($variables['view']
    ->id() === 'social_group_invitations') {
    $group = _social_group_get_current_group();
    $variables['more'] = [
      '#title' => t('Back to group'),
      '#type' => 'link',
      '#url' => Url::fromRoute('entity.group.canonical', [
        'group' => $group
          ->id(),
      ]),
      '#attributes' => [
        'class' => [
          'btn',
          'btn-default',
          'btn-raised',
          'waves-effect',
        ],
      ],
    ];
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function social_group_invite_preprocess_views_view_field(&$variables) {
  $view = $variables['view'];

  // Alter some group invite fields for better representation in the
  // custom social overview.
  if ($view
    ->id() === 'social_group_invitations') {

    // For invite status we want to show the readable name.
    if ($variables['field']->field === 'invitation_status' && !empty($variables['row']->_entity)) {

      /** @var \Drupal\group\Entity\GroupContent $entity */
      $entity = $variables['row']->_entity;

      // If we have a invite we check it's status.
      $field_value = _social_group_get_group_invite_value($entity, 'invitation_status');
      if (NULL !== $field_value) {

        // Field values are always returned as string,
        // so lets typecast them for now.
        $current_status = (int) $field_value;
        switch ($current_status) {
          case GroupInvitation::INVITATION_PENDING:
            $output = t('Pending reply');
            break;
          case GroupInvitation::INVITATION_ACCEPTED:
            $output = t('Accepted and joined');
            break;
          case GroupInvitation::INVITATION_REJECTED:
            $output = t('Declined');
            break;
          default:
            $output = $variables['output'];
            break;
        }
        $variables['output'] = $output;
      }
    }

    // If user accepted the invite, don't render the actions.
    if ($variables['field']->field === 'dropbutton' && !empty($variables['row']->_entity)) {
      $entity = $variables['row']->_entity;
      $field_value = _social_group_get_group_invite_value($entity, 'invitation_status');

      // Field values are always returned as string,
      // so lets typecast them for now.
      if (NULL !== $field_value) {
        $current_status = (int) $field_value;
        if ($current_status === GroupInvitation::INVITATION_ACCEPTED) {
          $variables['output'] = '';
        }
      }
    }
  }
}

/**
 * Return a group invites field value if it exists.
 *
 * @param \Drupal\group\Entity\GroupContent $entity
 *   Group invitation.
 * @param string $field_name
 *   The field name.
 *
 * @return string|null
 *   The field value or NULL if there isn't any.
 */
function _social_group_get_group_invite_value(GroupContent $entity, $field_name) {
  $field_value = NULL;
  if ($entity
    ->hasField($field_name)) {
    try {

      /** @var \Drupal\Core\TypedData\ListInterface $field */
      $field = $entity
        ->get($field_name)
        ->first();
      if (NULL !== $field && !empty($field
        ->getValue())) {
        $field_value = $field
          ->getValue()['value'];
      }
    } catch (MissingDataException $e) {
    }
  }
  return $field_value;
}

/**
 * Check if current group it's group_type has invites enabled.
 *
 * @return bool
 *   TRUE if it's enabled.
 */
function _social_group_invite_current_type_enabled_invites() {
  $enabled = FALSE;
  $group = _social_group_get_current_group();

  // Check if group type has content plugin enabled.
  if ($group instanceof GroupInterface) {
    $group_type = $group
      ->getGroupType();
    if ($group_type
      ->hasContentPlugin('group_invitation')) {
      $enabled = TRUE;
    }
  }
  return $enabled;
}

/**
 * Implements hook_theme_registry_alter().
 */
function social_group_invite_theme_registry_alter(&$theme_registry) {

  // Unfortunately the preprocess functions aren't ordered by module weight.
  // Changing module weight doesn't work, also with dependency set to
  // social_group this should be dealt with but isnt.
  // So we enforce our preprocess after social_group.
  if (!empty($theme_registry['views_view']['preprocess functions'])) {
    $current_key = array_search('social_group_invite_preprocess_views_view', $theme_registry['views_view']['preprocess functions'], FALSE);
    unset($theme_registry['views_view']['preprocess functions'][$current_key]);

    // Give it a new key all the way at the end.
    $theme_registry['views_view']['preprocess functions'][] = 'social_group_invite_preprocess_views_view';
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function social_group_invite_preprocess_page_title(&$variables) {

  // Add count of pending invites to the page title for a group.
  if (\Drupal::routeMatch()
    ->getParameter('view_id') === 'social_group_invitations' && !empty(\Drupal::routeMatch()
    ->getParameter('group'))) {
    $group = _social_group_get_current_group();
    if (!empty($group
      ->label())) {
      $loader = \Drupal::service('ginvite.invitation_loader');
      $count = count($loader
        ->loadByProperties([
        'gid' => $group
          ->id(),
      ]));
      $title = \Drupal::translation()
        ->formatPlural($count, '1 membership invite for group: @group_name', '@count membership invites for group: @group_name', [
        '@group_name' => $group
          ->label(),
      ]);
      $variables['title'] = $title;
      $variables['#cache']['tags'][] = 'group_content_list:group:' . $group
        ->id();
      $variables['#cache']['tags'][] = 'group_content_list:plugin:group_invitation:group:' . $group
        ->id();
    }
  }

  // Add count of pending invites to the page title for a user.
  if (\Drupal::routeMatch()
    ->getParameter('view_id') === 'social_group_user_invitations' && !empty(\Drupal::routeMatch()
    ->getParameter('user'))) {
    $loader = \Drupal::service('ginvite.invitation_loader');
    $count = count($loader
      ->loadByUser());
    $translation = \Drupal::translation()
      ->formatPlural($count, '1 group invite', '@count group invites');
    $variables['title'] = $translation;
    $user = \Drupal::routeMatch()
      ->getParameter('user');
    if (is_string($user)) {
      $variables['#cache']['tags'][] = 'group_content_list:entity:' . $user;
      $variables['#cache']['tags'][] = 'group_content_list:plugin:group_invitation:entity:' . $user;
    }
  }
}

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

  // We need to write a custom form alter, since the ginvite module
  // has it's own routes, with no group context that loads the
  // join form. Decline is done differently. Due to the fact on joining
  // users need to fill in their membership fields.
  $social_group_types = [
    'open_group',
    'closed_group',
    'public_group',
  ];

  // Also include any added optional group types.
  \Drupal::moduleHandler()
    ->alter('social_group_types', $social_group_types);
  $join_forms = [];
  foreach ($social_group_types as $social_group_type) {
    $join_forms[] = "group_content_{$social_group_type}-group_membership_group-join_form";
  }

  // Perform alterations on joining / leaving groups.
  if (in_array($form_id, $join_forms)) {

    // Add custom redirect form submit.
    $form['actions']['submit']['#submit'][] = '_social_group_invite_action_form_submit';
  }
}

/**
 * Form submit for group invite join form.
 */
function _social_group_invite_action_form_submit($form, FormStateInterface $form_state) {
  $invite_content = \Drupal::routeMatch()
    ->getParameter('group_content');
  $group = '';
  if ($invite_content instanceof GroupContent) {
    $group = $invite_content
      ->getGroup();
  }
  if (is_string($invite_content)) {
    $group_content = GroupContent::load($invite_content);
    $group = $group_content
      ->getGroup();
  }

  // Redirect user back to the group they joined.
  if ($group instanceof Group) {
    $form_state
      ->setRedirect('entity.group.canonical', [
      'group' => $group
        ->id(),
      [],
    ]);
  }
}

/**
 * A specific Group Type Role, will get default invite permissions.
 *
 * @param \Drupal\group\Entity\GroupRoleInterface $role
 *   The role that we will add the default permissions to.
 *
 * @throws \Drupal\Core\Entity\EntityStorageException
 */
function social_group_invite_set_default_permissions_for_role_on_group_type(GroupRoleInterface $role) {
  $role
    ->grantPermissions([
    'delete any invitation',
    'delete own invitations',
    'invite users to group',
    'view group invitations',
  ]);
  $role
    ->save();
}

/**
 * A specific Group Type, where will assign invite permissions.
 *
 * @param \Drupal\group\Entity\GroupTypeInterface $group_type
 *   The Group type role that we will add the default permissions to.
 *
 * @throws \Drupal\Core\Entity\EntityStorageException
 */
function social_group_invite_set_default_permissions_for_group_type(GroupTypeInterface $group_type) {

  /** @var \Drupal\group\Entity\GroupRoleInterface $roles */
  $roles = $group_type
    ->getRoles();

  // Make sure this is done for managers and admins.
  if (!empty($roles[$group_type
    ->id() . '-group_manager'])) {
    $role = $roles[$group_type
      ->id() . '-group_manager'];
    social_group_invite_set_default_permissions_for_role_on_group_type($role);
  }
  if (!empty($roles[$group_type
    ->id() . '-group_admin'])) {
    $role = $roles[$group_type
      ->id() . '-group_admin'];
    social_group_invite_set_default_permissions_for_role_on_group_type($role);
  }
}

/**
 * Implements hook_preprocess_activity().
 */
function social_group_invite_preprocess_activity(&$variables) {
  $activity = $variables['elements']['#activity'];
  $related_entity = $activity
    ->getRelatedEntity();

  // If the related entity is a piece of GroupContent and coming from the
  // ginvite group_invitation plugin, lets link the entity to the group itself.
  if (!empty($related_entity) && $related_entity instanceof GroupContent && $related_entity
    ->getGroupContentType()
    ->getContentPluginId() === 'group_invitation') {
    $group = $related_entity
      ->getGroup();
    if ($group instanceof Group) {
      $variables['full_url'] = $group
        ->urlInfo('canonical');
    }
  }
}

/**
 * Implements hook_preprocess_group().
 */
function social_group_invite_preprocess_group(&$variables) {

  /** @var \Drupal\group\Entity\GroupInterface $group */
  $group = $variables['group'];
  $group_type = $group
    ->getGroupType();
  $variables['user_is_invited'] = FALSE;

  // Only for groups that have invites enabled.
  if (!$group_type
    ->hasContentPlugin('group_invitation')) {
    return;
  }

  // Only for LU.
  $account = \Drupal::currentUser();
  if ($account
    ->isAnonymous()) {
    return;
  }

  // Check if the user (entity_id) has a pending invite for the group.
  $properties = [
    'entity_id' => $account
      ->id(),
    'gid' => $group
      ->id(),
    'invitation_status' => GroupInvitation::INVITATION_PENDING,
  ];

  /** @var \Drupal\ginvite\GroupInvitationLoaderInterface $loader */
  $loader = \Drupal::service('ginvite.invitation_loader');
  $invitations = $loader
    ->loadByProperties($properties);

  // We have pending invites, let's build a button to accept or decline one.
  if ($invitations > 0) {

    // Build routes.
    $invitation = reset($invitations);
    if ($invitation instanceof GroupInvitationWrapper) {

      // Lets grab the group content so we can build the URL.
      $group_content = $invitation
        ->getGroupContent();
      if ($group_content instanceof GroupContent) {
        $variables['user_is_invited'] = TRUE;

        // It will become two links with a dropdown button.
        $variables['group_invite_accept_operations_url'] = Url::fromRoute('ginvite.invitation.accept', [
          'group_content' => $group_content
            ->id(),
        ]);
        $variables['group_invite_decline_operations_url'] = Url::fromRoute('ginvite.invitation.decline', [
          'group_content' => $group_content
            ->id(),
        ]);
        $variables['#cache']['tags'][] = 'group_content_list:entity:' . $account
          ->id();
        $variables['#cache']['tags'][] = 'group_content_list:plugin:group_invitation:entity:' . $account
          ->id();
        $variables['#cache']['contexts'][] = 'user';
      }
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function social_group_invite_form_social_group_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $invite_config = \Drupal::config('social_group.settings')
    ->get('group_invite');
  $form['group_invite'] = [
    '#type' => 'details',
    '#title' => t('Group invite settings'),
  ];
  $default_subject = '[current-user:display-name] has invited you to join a group on [site:name].';
  $form['group_invite']['invite_subject'] = [
    '#type' => 'textfield',
    '#title' => t('Subject'),
    '#default_value' => $invite_config['invite_subject'] ?? $default_subject,
    '#required' => TRUE,
  ];
  $default_message = "Hi,<br /><br /> I would like to invite you to join my group [group:title] on [site:name].<br /><br />Kind regards,<br />[current-user:display-name]<br /><br />\r\n<table class=\"btn-wrapp\">\r\n\t<tbody>\r\n\t\t<tr>\r\n\t\t\t<td class=\"align-center\"><a class=\"btn-link btn-link-bg btn-link-one\" href=\"[group_content:register_link]\">View group</a></td>\r\n\t\t\t<td class=\"align-center\"><a class=\"btn-link btn-link-bg btn-link-one\" href=\"[site:url]\">About [site:name]</a></td>\r\n\t\t</tr>\r\n\t</tbody>\r\n</table>";
  $form['group_invite']['invite_message'] = [
    '#type' => 'textarea',
    '#title' => t('Message'),
    '#default_value' => $invite_config['invite_message'] ?? $default_message,
    '#required' => TRUE,
  ];
  $form['actions']['submit']['#submit'][] = '_social_group_invite_settings_submit';
}

/**
 * Sets settings for invite mail message.
 */
function _social_group_invite_settings_submit($form, FormStateInterface $form_state) {
  \Drupal::configFactory()
    ->getEditable('social_group.settings')
    ->set('group_invite.invite_subject', $form_state
    ->getValue('invite_subject'))
    ->set('group_invite.invite_message', $form_state
    ->getValue('invite_message'))
    ->save();
}

/**
 * Implements hook_mail_alter().
 */
function social_group_invite_mail_alter(&$message) {
  if ($message['id'] === 'ginvite_invite') {
    $params = $message['params'];
    $token_service = \Drupal::token();
    $language_manager = \Drupal::languageManager();
    $langcode = $message['langcode'];
    $language = $language_manager
      ->getLanguage($langcode);
    $original_language = $language_manager
      ->getConfigOverrideLanguage();
    $language_manager
      ->setConfigOverrideLanguage($language);

    // Load group invite configuration.
    $group_config = \Drupal::config('social_group.settings');
    $invite_settings = $group_config
      ->get('group_invite');

    // Alter message and subject if it configured.
    if (!is_null($invite_settings) && isset($invite_settings['invite_subject'], $invite_settings['invite_message'])) {
      $invitation_subject = $invite_settings['invite_subject'];
      $invitation_body = $invite_settings['invite_message'];
      unset($params['existing_user']);
      $body = $token_service
        ->replace($invitation_body, $params);
      $subject = $token_service
        ->replace($invitation_subject, $params);
      $message['subject'] = $subject;
      $message['body'][0] = $body;
    }
    $language_manager
      ->setConfigOverrideLanguage($original_language);
  }
}

Functions

Namesort descending Description
social_group_invite_block_access Implements hook_block_access().
social_group_invite_form_alter Implements hook_form_alter().
social_group_invite_form_social_group_form_alter Implements hook_form_FORM_ID_alter().
social_group_invite_mail_alter Implements hook_mail_alter().
social_group_invite_menu_local_actions_alter Implements hook_menu_local_actions_alter().
social_group_invite_menu_local_tasks_alter Implements hook_menu_local_tasks_alter().
social_group_invite_preprocess_activity Implements hook_preprocess_activity().
social_group_invite_preprocess_group Implements hook_preprocess_group().
social_group_invite_preprocess_page_title Implements hook_preprocess_HOOK().
social_group_invite_preprocess_views_view Implements hook_preprocess_HOOK().
social_group_invite_preprocess_views_view_field Implements hook_preprocess_HOOK().
social_group_invite_set_default_permissions_for_group_type A specific Group Type, where will assign invite permissions.
social_group_invite_set_default_permissions_for_role_on_group_type A specific Group Type Role, will get default invite permissions.
social_group_invite_theme_registry_alter Implements hook_theme_registry_alter().
_social_group_get_group_invite_value Return a group invites field value if it exists.
_social_group_invite_action_form_submit Form submit for group invite join form.
_social_group_invite_current_type_enabled_invites Check if current group it's group_type has invites enabled.
_social_group_invite_settings_submit Sets settings for invite mail message.