You are here

social_group_flexible_group.module in Open Social 10.1.x

The Social Group Flexible Group module.

File

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

/**
 * @file
 * The Social Group Flexible Group module.
 */
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\block\Entity\Block;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\group\Entity\Group;
use Drupal\group\Entity\GroupInterface;
use Drupal\social_group_flexible_group\FlexibleGroupContentVisibilityUpdate;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\Plugin\views\row\EntityRow;
use Drupal\views\ViewExecutable;
use Drupal\views\Views;

/**
 * Provide a method to alter array of group types used in open social.
 *
 * @param array $social_group_types
 *   List of group types used in open social.
 *
 * @ingroup social_group_api
 */
function social_group_flexible_group_social_group_types_alter(array &$social_group_types) {
  $social_group_types[] = 'flexible_group';
}

/**
 * Implements hook_views_data_alter().
 */
function social_group_flexible_group_views_data_alter(array &$data) {
  $data['node_access']['flexible_group_node_access'] = [
    'title' => t('Filter nodes, with visibility group, placed in group you are not a member of.'),
    'filter' => [
      'title' => t('Filter nodes, with visibility group, placed in group you are not a member of.'),
      'help' => t('Filter nodes, with visibility group, placed in group you are not a member of.'),
      'id' => 'flexible_group_node_access',
    ],
  ];
}

/**
 * Implements hook_form_FORM_ID_form_alter().
 */
function social_group_flexible_group_form_group_flexible_group_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  // Lets remove group type from flexible group.
  // Editing is useless since the options are all in there.
  if ($form['group_type']['#disabled']) {

    // Remove all the options.
    // @see social_group_form_alter().
    unset($form['group_type']);
    unset($form['#fieldgroups']['group_settings']->children['group_type']);
    unset($form['#group_children']['group_type']);
  }
  $form['field_flexible_group_visibility']['#prefix'] = t('Altering the visibility options could result in changes in access for content within this group and the group itself.');
  $form['actions']['submit']['#submit'][] = '_social_flexible_group_edit_submit';
}

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

  // Exposed Filter block on the all-groups overview and search.
  if ($form['#id'] === 'views-exposed-form-newest-groups-page-all-groups' || $form['#id'] === 'views-exposed-form-search-groups-page-no-value' || $form['#id'] === 'views-exposed-form-search-groups-page') {

    // Update filter values so it matches the join methods in the popover.
    if (!empty($form['field_group_allowed_join_method'])) {
      if (array_key_exists('added', $form['field_group_allowed_join_method']['#options'])) {
        $form['field_group_allowed_join_method']['#options']['added'] = t('Invite only');
      }
      if (array_key_exists('direct', $form['field_group_allowed_join_method']['#options'])) {
        $form['field_group_allowed_join_method']['#options']['direct'] = t('Open to join');
      }
      if (array_key_exists('request', $form['field_group_allowed_join_method']['#options'])) {
        $form['field_group_allowed_join_method']['#options']['request'] = t('Request to join');
      }
    }

    // Add states so this is only available when flexible groups is checked.
    // Could be hidden when only flexible groups is enabled, so check that.
    // @todo remove this once everything is migrated to flexible groups.
    if (!empty($form['field_group_allowed_join_method']) && !empty($form['type']['#options']) && $form['type']['#type'] !== 'hidden') {
      $form['field_group_allowed_join_method']['#states'] = [
        'visible' => [
          ':input[name="type"]' => [
            'value' => 'flexible_group',
          ],
        ],
      ];
    }
  }

  // For adding or editing a flexible group, we alter the visibility fields.
  if ($form['#id'] === 'group-flexible-group-add-form' || $form['#id'] === 'group-flexible-group-edit-form') {

    // Change the group visibility on flexible groups.
    if (!empty($form['field_group_allowed_visibility'])) {
      if (!empty($form['field_group_allowed_visibility']['widget']['#title'])) {
        $form['field_group_allowed_visibility']['widget']['#title'] = t('Group content visibility options');
      }
      if (!empty($form['field_group_allowed_visibility']['widget']['#description'])) {
        $form['field_group_allowed_visibility']['widget']['#description'] = t('Choose the visibility options allowed for the group content.');
      }

      // When referencing select lists and radio buttons in remote conditions,
      // a 'value' condition must be used.
      $form['field_group_allowed_visibility']['#states'] = [
        'visible' => [
          ':input[name="field_flexible_group_visibility"]' => [
            [
              'value' => 'public',
            ],
            [
              'value' => 'community',
            ],
            [
              'value' => 'members',
            ],
          ],
        ],
      ];
    }

    // Change the allowed join method on flexible groups.
    if (!empty($form['field_group_allowed_join_method'])) {

      // First we reorder the elmements, if invite only is part of it
      // we always want to show this last.
      if (!empty($form['field_group_allowed_join_method']['widget']['#options'])) {
        if (array_key_exists('added', $form['field_group_allowed_join_method']['widget']['#options'])) {
          $option = $form['field_group_allowed_join_method']['widget']['#options']['added'];

          // Unset it.
          unset($form['field_group_allowed_join_method']['widget']['#options']['added']);

          // Add it at the end.
          $form['field_group_allowed_join_method']['widget']['#options']['added'] = $option;
        }
      }
      if (!empty($form['field_group_allowed_join_method']['widget']['#title'])) {
        $form['field_group_allowed_join_method']['widget']['#title'] = t('Join methods');
      }
      if (!empty($form['field_group_allowed_join_method']['widget']['#description'])) {
        $form['field_group_allowed_join_method']['widget']['#description'] = t('How can people join this group. Group managers can always add members directly, regardless of the chosen join method.');
      }

      // When referencing select lists and radio buttons in remote conditions,
      // a 'value' condition must be used.
      $form['field_group_allowed_join_method']['#states'] = [
        'visible' => [
          ':input[name="field_flexible_group_visibility"]' => [
            [
              'value' => 'public',
            ],
            [
              'value' => 'community',
            ],
            [
              'value' => 'members',
            ],
          ],
        ],
      ];
    }

    // Only when adding a flexible group, we need to predefine the
    // content visibility and join method based on the group visibility.
    if ($form['#id'] === 'group-flexible-group-add-form') {

      // Reset default_values on the add form only.
      // So we can use state management to check / select.
      if (!empty($form['field_group_allowed_join_method']['widget']['#default_value'])) {
        $form['field_group_allowed_join_method']['widget']['#default_value'] = [];
      }
      if (!empty($form['field_group_allowed_visibility']['widget']['#default_value'])) {
        $form['field_group_allowed_visibility']['widget']['#default_value'] = [];
      }
    }

    // On the edit form, if there is no value set for the group_visibility
    // it means it was an already existing flexible group and we need to add a
    // default value so people can save it.
    if ($form['#id'] === 'group-flexible-group-edit-form' && empty($form['field_flexible_group_visibility']['widget']['#default_value'])) {

      // The default is always community because we used to only have closed
      // groups (which is visible for the community) or public if the
      // content visibility has public as an option.
      $form['field_flexible_group_visibility']['widget']['#default_value'] = 'community';
      if (in_array('public', $form['field_group_allowed_visibility']['widget']['#default_value'])) {
        $form['field_flexible_group_visibility']['widget']['#default_value'] = 'public';
      }
    }

    // Disable type field if there are no terms added in 'Group type' vocab.
    // Also, check for settings by SM for enabling this field.
    $group_type_settings = \Drupal::config('social_group.settings')
      ->get('social_group_type_required');
    if ($group_type_settings && isset($form['field_group_type']) && !empty(\Drupal::entityTypeManager()
      ->getStorage('taxonomy_term')
      ->loadTree('group_type'))) {
      $form['field_group_type']['widget']['#required'] = TRUE;

      // Unset the group type label for none.
      if (isset($form['field_group_type']['widget']['#options']['_none'])) {
        unset($form['field_group_type']['widget']['#options']['_none']);
      }
    }
    else {
      $form['field_group_type']['#access'] = FALSE;
    }
    $form['#after_build'][] = 'social_group_flexible_group_flexible_group_add_after_build';
    $form['#attached']['library'][] = 'social_group_flexible_group/admin';
  }
}

/**
 * After_build function for the flexible group add form to add default states.
 *
 * @param array $form
 *   The form element.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The form state.
 *
 * @return mixed
 *   The form array containing the default values & states.
 */
function social_group_flexible_group_flexible_group_add_after_build(array $form, FormStateInterface $form_state) {
  $group_types = [
    'flexible_group',
  ];
  \Drupal::moduleHandler()
    ->alter('social_group_settings', $group_types);
  foreach ($group_types as $group_type) {
    $form_ids[] = 'group-' . str_replace('_', '-', $group_type) . '-add-form';
  }

  // Add states for content visibility based on group visibility.
  // Only on the add page. On edit we want users to be able to consciously
  // decide what allowed visibility to select.
  if (in_array($form['#id'], $form_ids ?? []) && isset($form['field_flexible_group_visibility']['widget']['#options'], $form['field_group_allowed_visibility']['widget']['#options'])) {

    // If group visibility is public. All content visibility is selected.
    $form['field_group_allowed_visibility']['widget']['public']['#states'] = [
      'checked' => [
        ':input[name="field_flexible_group_visibility"]' => [
          [
            'value' => 'public',
          ],
        ],
      ],
      'unchecked' => [
        ':input[name="field_flexible_group_visibility"]' => [
          [
            'value' => 'community',
          ],
          [
            'value' => 'members',
          ],
        ],
      ],
    ];

    // If group visibility is community. Communit & Group members are selected.
    $form['field_group_allowed_visibility']['widget']['community']['#states'] = [
      'checked' => [
        ':input[name="field_flexible_group_visibility"]' => [
          [
            'value' => 'public',
          ],
          [
            'value' => 'community',
          ],
        ],
      ],
      'unchecked' => [
        ':input[name="field_flexible_group_visibility"]' => [
          [
            'value' => 'members',
          ],
        ],
      ],
    ];

    // If group visibility is group. Only group members are selected.
    $form['field_group_allowed_visibility']['widget']['group']['#states'] = [
      'checked' => [
        ':input[name="field_flexible_group_visibility"]' => [
          [
            'value' => 'public',
          ],
          [
            'value' => 'community',
          ],
          [
            'value' => 'members',
          ],
        ],
      ],
    ];
  }

  // Add states for join method based on group visibility.
  // We do this for add and edit, we want to make sure if users make the
  // decision to choose to only show a group to it's Members, the
  // join method is selected to Invite only. Because there is no way
  // for users to join or request to join in that case.
  if (isset($form['field_flexible_group_visibility']['widget']['#options'], $form['field_group_allowed_join_method']['widget']['#options'])) {

    // If group visibility is members. Select invite-only.
    if (!empty($form['field_group_allowed_join_method']['widget']['added'])) {
      $form['field_group_allowed_join_method']['widget']['added']['#states'] = [
        'checked' => [
          ':input[name="field_flexible_group_visibility"]' => [
            [
              'value' => 'members',
            ],
          ],
        ],
      ];
    }

    // If group visibility is members. Disable and uncheck open to join.
    if (!empty($form['field_group_allowed_join_method']['widget']['direct'])) {
      $form['field_group_allowed_join_method']['widget']['direct']['#states'] = [
        'disabled' => [
          ':input[name="field_flexible_group_visibility"]' => [
            [
              'value' => 'members',
            ],
          ],
        ],
        'unchecked' => [
          ':input[name="field_flexible_group_visibility"]' => [
            [
              'value' => 'members',
            ],
          ],
        ],
      ];
    }

    // If group visibility is members. Disable and uncheck open to join.
    if (!empty($form['field_group_allowed_join_method']['widget']['request'])) {
      $form['field_group_allowed_join_method']['widget']['request']['#states'] = [
        'disabled' => [
          ':input[name="field_flexible_group_visibility"]' => [
            [
              'value' => 'members',
            ],
          ],
        ],
        'unchecked' => [
          ':input[name="field_flexible_group_visibility"]' => [
            [
              'value' => 'members',
            ],
          ],
        ],
      ];
    }
  }
  return $form;
}

/**
 * Implements hook_ENTITY_TYPE_access().
 */
function social_group_flexible_group_group_access(EntityInterface $entity, $operation, AccountInterface $account) {
  $result = AccessResult::neutral();

  // Write custom access checks based on the new group visibility field.
  // If group visibility doesn't exist we can skip this.

  /** @var \Drupal\group\Entity\GroupInterface $entity */
  if ($operation !== 'view' || !$entity
    ->hasField('field_flexible_group_visibility')) {
    return $result;
  }

  // If user has administer groups access we can skip this.
  if ($account
    ->hasPermission('manage all groups')) {
    return $result;
  }

  // If group visibility value doesn't exist we can skip.
  if (empty($entity
    ->getFieldValue('field_flexible_group_visibility', 'value'))) {
    return $result;
  }

  // If group visibility exists and public is selected, we can skip.
  $group_visibility = $entity
    ->getFieldValue('field_flexible_group_visibility', 'value');
  if ($group_visibility === 'public') {
    return $result;
  }

  // If group visibility exists and community or members is selected, check
  // if user is logged.
  if ($group_visibility === 'community') {
    return AccessResult::forbiddenIf($account
      ->isAnonymous())
      ->cachePerUser()
      ->addCacheableDependency($entity);
  }

  // If group visibility exists and members only is selected, we need to check
  // if user is logged in and is a member of the group.
  if ($group_visibility === 'members') {
    $not_a_member = !$entity
      ->getMember($account) || $account
      ->isAnonymous();
    return AccessResult::forbiddenIf($not_a_member)
      ->cachePerPermissions()
      ->cachePerUser()
      ->addCacheableDependency($entity)
      ->addCacheableDependency($account);
  }
  return $result;
}

/**
 * Implements template_preprocess_form_element().
 */
function social_group_flexible_group_preprocess_form_element(&$variables) {

  // Make sure our flexible group visibility field renders icons in the radio
  // labels. We add a new property to the form element label render array.
  // This is used in FormElementLabel.php to add data for templating.
  if (!isset($variables['element']['#type'])) {
    return;
  }
  if ($variables['element']['#type'] === 'radio' && !empty($variables['name']) && $variables['name'] === 'field_flexible_group_visibility') {
    $variables['label']['#render_icon'] = TRUE;
    $variables['attributes']['class'][] = 'inline-item';
  }
  if ($variables['element']['#type'] === 'checkbox' && !empty($variables['name']) && strpos($variables['name'], 'field_group_allowed_visibility') !== FALSE) {
    $variables['label']['#render_icon'] = TRUE;
    $variables['attributes']['class'][] = 'inline-item';
  }
}

/**
 * Implements template_preprocess_form_element().
 */
function social_group_flexible_group_preprocess_fieldset(&$variables) {

  // Make sure our flexible group visibility field renders a tooltip, since
  // this field is rendered as fieldset with legend and radios as children
  // we need to do it in this preprocess.
  $element = $variables['element'];
  if (!empty($element['#field_name'])) {
    if ($element['#field_name'] === 'field_flexible_group_visibility') {
      $description = '';
      foreach ($element['#options'] as $key => $label) {
        $description .= social_group_group_visibility_description($key);
      }

      // Render a specific tooltip based on a field name and description.
      // This is done in the fieldset, next to the <legend>.
      $variables['popover'] = social_group_render_tooltip('field_flexible_group_visibility', t('Group Visibility'), $description);
    }
    if ($element['#field_name'] === 'field_group_allowed_visibility') {
      $description = '';
      foreach ($element['#options'] as $key => $label) {
        $description .= social_group_allowed_visibility_description($key);
      }

      // Render a specific tooltip based on a field name and description.
      // This is done in the fieldset, next to the <legend>.
      $variables['popover'] = social_group_render_tooltip('field_group_allowed_visibility', t('Group content visibility'), $description);
    }
    if ($element['#field_name'] === 'field_group_allowed_join_method') {
      $description = '';
      foreach ($element['#options'] as $key => $label) {
        $description .= social_group_allowed_join_method_description($key);
      }

      // Render a specific tooltip based on a field name and description.
      // This is done in the fieldset, next to the <legend>.
      $variables['popover'] = social_group_render_tooltip('field_group_allowed_join_method', t('Join methods'), $description);
    }
  }
}

/**
 * Custom form submit handler for editing a flexible group.
 *
 * @param array $form
 *   The form array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   Form state.
 *
 * @throws \Drupal\Core\Entity\EntityStorageException
 */
function _social_flexible_group_edit_submit(array $form, FormStateInterface $form_state) {

  // Check if the visibility changed.
  $default_visibility = $form['field_group_allowed_visibility']['widget']['#default_value'];
  $new_visibility = $form_state
    ->getValue('field_group_allowed_visibility');
  $changed_visibility = [];

  // If there was a visibility that we don't have anymore after editting
  // all the content that was inside the group with this visibility
  // will get the lowest visibility that is still checked.
  foreach ($default_visibility as $key => $option) {
    if (array_search($option, array_column($new_visibility, 'value')) === FALSE) {
      $changed_visibility[] = $option;
    }
  }

  // So there is now a visibility setting we don't support anymore
  // after editing. Make sure we update all the content that has this
  // to the next best optin.
  if (!empty($changed_visibility)) {
    $group = _social_group_get_current_group();
    if ($group instanceof GroupInterface) {

      // Update the default visibility of all the content.
      FlexibleGroupContentVisibilityUpdate::batchUpdateGroupContentVisibility($group, $changed_visibility, $new_visibility);

      // Make sure we clear cache tags accordingly.
      $cache_tags = _social_group_cache_tags($group);
      foreach ($cache_tags as $cache_tag) {
        Cache::invalidateTags([
          $cache_tag,
        ]);
      }
    }
  }
}

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

  // Rearrange all the options so flexible is last
  // we will need the space for the configuration.
  $type_options = $form['group_settings']['group_type']['#options'];
  if (!empty($type_options['flexible_group'])) {
    $flexible = $type_options['flexible_group'];
    unset($form['group_settings']['group_type']['#options']['flexible_group']);
    $form['group_settings']['group_type']['#options']['flexible_group'] = $flexible;
  }
}

/**
 * Check if a user can join the group directly.
 *
 * @param \Drupal\group\Entity\GroupInterface $group
 *   The group we are checking.
 *
 * @return bool
 *   TRUE when users can join.
 */
function social_group_flexible_group_can_join_directly(GroupInterface $group) {
  $join_methods = $group
    ->get('field_group_allowed_join_method')
    ->getValue();
  if (!in_array('direct', array_column($join_methods, 'value'), FALSE)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Check if a user can be added to a group.
 *
 * @param \Drupal\group\Entity\Group $group
 *   The group we are checking.
 *
 * @return bool
 *   TRUE when users can join.
 */
function social_group_flexible_group_can_be_added(Group $group) {
  $join_methods = $group
    ->get('field_group_allowed_join_method')
    ->getValue();
  if (!in_array('added', array_column($join_methods, 'value'), FALSE)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Check if public is enabled as visibility options.
 *
 * @param \Drupal\group\Entity\GroupInterface $group
 *   The group we are checking.
 *
 * @return bool
 *   TRUE when users can join.
 */
function social_group_flexible_group_public_enabled(GroupInterface $group) {
  $visibility_options = $group
    ->get('field_group_allowed_visibility')
    ->getValue();
  if (!in_array('public', array_column($visibility_options, 'value'), FALSE)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Check if community is enabled as visibility options.
 *
 * @param \Drupal\group\Entity\GroupInterface $group
 *   The group we are checking.
 *
 * @return bool
 *   TRUE when users can join.
 */
function social_group_flexible_group_community_enabled(GroupInterface $group) {
  $visibility_options = $group
    ->get('field_group_allowed_visibility')
    ->getValue();
  if (!in_array('community', array_column($visibility_options, 'value'), FALSE)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Check if members is enabled as visibility options.
 *
 * @param \Drupal\group\Entity\Group $group
 *   The group we are checking.
 *
 * @return bool
 *   TRUE when users can join.
 */
function social_group_flexible_group_members_enabled(Group $group) {
  $visibility_options = $group
    ->get('field_group_allowed_visibility')
    ->getValue();
  if (!in_array('group', array_column($visibility_options, 'value'), FALSE)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Implements hook_menu_local_actions_alter().
 */
function social_group_flexible_group_menu_local_actions_alter(&$local_actions) {
  $group = _social_group_get_current_group();
  $user = \Drupal::currentUser();

  // Remove the social_group add member action on the
  // membership overview if we can't add members directly.
  // SM+ can still add members though.
  if ($group instanceof GroupInterface && $group
    ->getGroupType()
    ->id() === 'flexible_group' && !social_group_flexible_group_can_be_added($group) && !$user
    ->hasPermission('manage all groups') && !$group
    ->hasPermission('administer members', $user)) {
    unset($local_actions['social_group.add_member']);
  }
}

/**
 * Determine whether a user can see flexible groups as outsider.
 *
 * @param \Drupal\group\Entity\Group $group
 *   The group we are checking.
 * @param \Drupal\Core\Session\AccountInterface $account
 *   The user to check for.
 *
 * @return bool
 *   Whether the user is allowed to view this flexible groups.
 */
function social_group_flexible_group_can_view_flexible_groups(Group $group, AccountInterface $account) : bool {

  // Users who can manage all can manage everything.
  if ($account
    ->hasPermission('manage all groups')) {
    return TRUE;
  }

  // If User is a member it can see it.
  if ($group
    ->getMember($account) !== FALSE) {
    return TRUE;
  }

  // Outsiders can only see groups that have
  // public / community enabled as visibility.
  if ($account
    ->isAuthenticated() && !social_group_flexible_group_community_enabled($group) && !social_group_flexible_group_public_enabled($group)) {
    return FALSE;
  }
  return TRUE;
}

/**
 * Implements hook_views_query_alter().
 *
 * Hide flexible groups everywhere when the current user cant see it.
 */
function social_group_flexible_group_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
  if (empty($view->rowPlugin) || !$view->rowPlugin instanceof EntityRow || $view->rowPlugin
    ->getEntityTypeId() !== 'group') {
    return;
  }
  $account = \Drupal::currentUser();
  if (!$account
    ->isAnonymous()) {

    // Don't trigger page cache, this will cache it for AN
    // but with LU data.
    // Dynamic page cache handles this.
    \Drupal::service('page_cache_kill_switch')
      ->trigger();
  }

  // Don't check, they can see it all.
  if ($account
    ->hasPermission('manage all groups')) {
    return;
  }

  // Let's build our join with the allowed visibility data.
  $configuration = [
    'type' => 'LEFT',
    'table' => 'group__field_flexible_group_visibility',
    'field' => 'entity_id',
    'left_table' => 'groups_field_data',
    'left_field' => 'id',
    'operator' => '=',
  ];
  $alias = 'groups_field_flexible_group_visibility';

  /** @var \Drupal\views\Plugin\views\query\Sql $query */
  $join = Views::pluginManager('join')
    ->createInstance('standard', $configuration);
  $rel = $query
    ->addRelationship($alias, $join, 'groups_field_data');
  $query
    ->addTable('group__field_flexible_group_visibility', $rel, $join, $alias);

  /** @var \Drupal\views\Plugin\views\query\Sql $query */
  $current_where = count($query->where);

  // Make sure we add one new group with a where clause.
  $new_where = $current_where + 1;

  // We need to add our group by using a query tag.
  // Otherwise views doesn't accept it.
  $query
    ->addTag('flexible_group_by');

  // Get all LU groups.
  $my_groups = \Drupal::service('social_group.helper_service')
    ->getAllGroupsForUser($account
    ->id());

  // Get all hidden groups.
  $hidden_groups = \Drupal::entityTypeManager()
    ->getStorage('group')
    ->loadByProperties([
    'field_flexible_group_visibility' => 'members',
  ]);

  // Get all hidden groups that the current user is not a member of
  // and remove them from showing in the view.
  $ids = array_diff(array_keys($hidden_groups), $my_groups);
  if ($ids) {

    // Anonymous user should only see 'public' groups.
    if ($account
      ->isAnonymous()) {
      $community_groups = \Drupal::entityTypeManager()
        ->getStorage('group')
        ->loadByProperties([
        'field_flexible_group_visibility' => 'community',
      ]);
      $ids = array_merge($ids, array_keys($community_groups));

      // Add context so for AN it will have a different cache.
      $view->element['#cache']['contexts'][] = 'user.roles:anonymous';
    }
    $query
      ->addWhere($new_where, 'groups_field_data.id', $ids, 'NOT IN');
  }

  // Make sure this block gets refreshed for a user if the group membership
  // changed for this user.
  $view->element['#cache']['contexts'][] = 'user';
  $view->element['#cache']['tags'][] = 'group_content_list:plugin:group_membership:entity:' . $account
    ->id();
}

/**
 * Implements hook_query_TAG_alter().
 */
function social_group_flexible_group_query_flexible_group_by_alter(AlterableInterface $query) {
  $query
    ->distinct();
}

/**
 * Implements hook_block_access().
 */
function social_group_flexible_group_block_access(Block $block, $operation, AccountInterface $account) {

  // This is a list of the blocks that this function cares about, if we're being
  // called for a different block we exit early.
  $block_id = $block
    ->getPluginId();
  $managed_blocks = [
    'views_exposed_filter_block:newest_groups-page_all_groups',
    'views_block:groups-block_user_groups',
    'views_block:upcoming_events-upcoming_events_group',
    'views_block:latest_topics-group_topics_block',
    'views_block:group_managers-block_list_managers',
  ];

  // We don't care for other blocks.
  if (!in_array($block_id, $managed_blocks, FALSE)) {
    return AccessResult::neutral();
  }
  $group = _social_group_get_current_group();

  // We don't care about other group types in here.
  if ($group && $group
    ->getGroupType()
    ->id() === 'flexible_group') {

    // Only when users cant join directly, add the managers block
    // so they know who to contact.
    if ($operation === 'view' && social_group_flexible_group_can_join_directly($group) && $block
      ->getPluginId() === 'views_block:group_managers-block_list_managers') {
      return AccessResult::forbidden();
    }

    // All users with permissions can see the rest.
    if ($account
      ->hasPermission('manage all groups')) {
      return AccessResult::neutral();
    }
    if (!$group
      ->getMember($account) && !social_group_flexible_group_community_enabled($group) && !social_group_flexible_group_public_enabled($group)) {

      // If it is flexible and the current user is not an member of this group,
      // and content visibility is not public and also not community
      // hide it.
      $forbidden_blocks = [
        'views_block:upcoming_events-upcoming_events_group',
        'views_block:latest_topics-group_topics_block',
      ];
      foreach ($forbidden_blocks as $forbidden_block) {
        if ($operation === 'view' && $block
          ->getPluginId() === $forbidden_block) {
          return AccessResult::forbidden();
        }
      }
    }
  }
  return AccessResult::neutral();
}

/**
 * Sets dynamic allowed values for the group type icon field.
 */
function social_group_flexible_group_type_allowed_values_function(FieldStorageConfig $definition, ContentEntityInterface $entity = NULL, $cacheable) {
  $theme_path = drupal_get_path('theme', 'socialbase');
  $icon_files = \Drupal::service('file_system')
    ->scanDirectory(DRUPAL_ROOT . '/' . $theme_path . '/components/06-libraries/icons/source', '/^icon-.*\\.svg$/', [
    'recurse' => FALSE,
  ]);
  $options = [];
  foreach ($icon_files as $file) {
    $options[$file->name] = $file->name;
  }
  return $options;
}

/**
 * Sets the default value for the group type icon field.
 */
function social_group_flexible_group_type_default_value_function(ContentEntityInterface $entity, FieldDefinitionInterface $definition) {
  return [
    [
      'value' => 'icon-event',
    ],
  ];
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function social_group_flexible_group_form_taxonomy_term_group_type_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if (!isset($form['field_group_type_icon'])) {
    return;
  }
  $form['#attached']['library'][] = 'social_group_flexible_group/admin';
  $default_icon = $form['field_group_type_icon']['widget']['#default_value'][0];
  $form['field_group_type_icon']['preview'] = [
    '#type' => 'inline_template',
    '#template' => '<div class="icon-svg-wrapper" data-href="#' . $default_icon . '"><svg class="selected-svg"><use xlink:href="#' . $default_icon . '"></use></svg>{{ svg_icons|raw }}</div>',
    '#context' => [
      'svg_icons' => file_get_contents(drupal_get_path('theme', 'socialbase') . '/assets/icons/icons.svg'),
    ],
  ];
  $form['field_group_type_icon']['widget']['#ajax'] = [
    'callback' => '_social_group_flexible_group_type_change_topic_icon_ajax',
    'event' => 'change',
  ];
}

/**
 * Ajax callback to change group type icon.
 */
function _social_group_flexible_group_type_change_topic_icon_ajax(array $form, FormStateInterface $form_state) {
  $response = new AjaxResponse();
  $icon = $form_state
    ->getValue('field_group_type_icon')[0]['value'];
  $replaced = [
    '#type' => 'inline_template',
    '#template' => '<div class="icon-svg-wrapper" data-href="#' . $icon . '"><svg class="selected-svg"><use xlink:href="#' . $icon . '"></use></svg>{{ svg_icons|raw }}</div>',
    '#context' => [
      'svg_icons' => file_get_contents(drupal_get_path('theme', 'socialbase') . '/assets/icons/icons.svg'),
    ],
  ];
  $response
    ->addCommand(new ReplaceCommand('.icon-svg-wrapper', $replaced));
  return $response;
}

Functions

Namesort descending Description
social_group_flexible_group_block_access Implements hook_block_access().
social_group_flexible_group_can_be_added Check if a user can be added to a group.
social_group_flexible_group_can_join_directly Check if a user can join the group directly.
social_group_flexible_group_can_view_flexible_groups Determine whether a user can see flexible groups as outsider.
social_group_flexible_group_community_enabled Check if community is enabled as visibility options.
social_group_flexible_group_flexible_group_add_after_build After_build function for the flexible group add form to add default states.
social_group_flexible_group_form_alter Implements hook_form_alter().
social_group_flexible_group_form_group_flexible_group_edit_form_alter Implements hook_form_FORM_ID_form_alter().
social_group_flexible_group_form_social_group_add_alter Implements hook_form_FORM_ID_alter().
social_group_flexible_group_form_taxonomy_term_group_type_form_alter Implements hook_form_FORM_ID_alter().
social_group_flexible_group_group_access Implements hook_ENTITY_TYPE_access().
social_group_flexible_group_members_enabled Check if members is enabled as visibility options.
social_group_flexible_group_menu_local_actions_alter Implements hook_menu_local_actions_alter().
social_group_flexible_group_preprocess_fieldset Implements template_preprocess_form_element().
social_group_flexible_group_preprocess_form_element Implements template_preprocess_form_element().
social_group_flexible_group_public_enabled Check if public is enabled as visibility options.
social_group_flexible_group_query_flexible_group_by_alter Implements hook_query_TAG_alter().
social_group_flexible_group_social_group_types_alter Provide a method to alter array of group types used in open social.
social_group_flexible_group_type_allowed_values_function Sets dynamic allowed values for the group type icon field.
social_group_flexible_group_type_default_value_function Sets the default value for the group type icon field.
social_group_flexible_group_views_data_alter Implements hook_views_data_alter().
social_group_flexible_group_views_query_alter Implements hook_views_query_alter().
_social_flexible_group_edit_submit Custom form submit handler for editing a flexible group.
_social_group_flexible_group_type_change_topic_icon_ajax Ajax callback to change group type icon.