Instantly share code, notes, and snippets.
Last active
September 22, 2022 13:37
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save zanvidmar/cf36baff499fb12f0a7d03ccb4deecf2 to your computer and use it in GitHub Desktop.
social-re-use-group-join-methods-3254715-29-zan.patch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/composer.json b/composer.json | |
| index dd0736126..e0c52f12f 100644 | |
| --- a/composer.json | |
| +++ b/composer.json | |
| @@ -140,6 +140,12 @@ | |
| }, | |
| "drupal/redirect": { | |
| "Redirection issue when interface language is different from content language": "https://www.drupal.org/files/issues/2020-06-01/redirect-interface_language_different_from_content_language_2991423-13.patch" | |
| + }, | |
| + "drupal/socialbase": { | |
| + "Make join methods for groups re-usable": "https://www.drupal.org/files/issues/2021-12-29/re-use-group-join-methods-3256324-3.patch" | |
| + }, | |
| + "drupal/socialblue": { | |
| + "Make join methods for groups re-usable": "https://www.drupal.org/files/issues/2021-12-29/re-use-group-join-methods-3256328-3.patch" | |
| } | |
| } | |
| }, | |
| diff --git a/modules/social_features/social_album/src/Controller/SocialAlbumController.php b/modules/social_features/social_album/src/Controller/SocialAlbumController.php | |
| index 13aaadb61..b7352a5dc 100644 | |
| --- a/modules/social_features/social_album/src/Controller/SocialAlbumController.php | |
| +++ b/modules/social_features/social_album/src/Controller/SocialAlbumController.php | |
| @@ -231,10 +231,10 @@ class SocialAlbumController extends ControllerBase { | |
| $storage = $this->entityTypeManager()->getStorage('group_content'); | |
| if ($group_content = $storage->loadByEntity($node)) { | |
| - /** @var \Drupal\group\Entity\GroupInterface $group */ | |
| + /** @var \Drupal\social_group\SocialGroupInterface $group */ | |
| $group = reset($group_content)->getGroup(); | |
| - return AccessResult::allowedIf($group->getMember($account) !== FALSE); | |
| + return AccessResult::allowedIf($group->hasMember($account)); | |
| } | |
| } | |
| } | |
| diff --git a/modules/social_features/social_event/src/Plugin/Block/EventRequestEnrollmentNotification.php b/modules/social_features/social_event/src/Plugin/Block/EventRequestEnrollmentNotification.php | |
| index 3b5c16043..82ca2e29f 100644 | |
| --- a/modules/social_features/social_event/src/Plugin/Block/EventRequestEnrollmentNotification.php | |
| +++ b/modules/social_features/social_event/src/Plugin/Block/EventRequestEnrollmentNotification.php | |
| @@ -65,7 +65,7 @@ class EventRequestEnrollmentNotification extends BlockBase implements ContainerF | |
| protected $loggerFactory; | |
| /** | |
| - * Constructs SocialGroupRequestMembershipNotification. | |
| + * Constructs EventRequestEnrollmentNotification. | |
| * | |
| * @param array $configuration | |
| * Configuration array. | |
| diff --git a/modules/social_features/social_group/modules/social_group_default_route/src/EventSubscriber/RedirectSubscriber.php b/modules/social_features/social_group/modules/social_group_default_route/src/EventSubscriber/RedirectSubscriber.php | |
| index bc7404e81..6b7e08417 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_default_route/src/EventSubscriber/RedirectSubscriber.php | |
| +++ b/modules/social_features/social_group/modules/social_group_default_route/src/EventSubscriber/RedirectSubscriber.php | |
| @@ -5,8 +5,7 @@ namespace Drupal\social_group_default_route\EventSubscriber; | |
| use Drupal\Core\Routing\CurrentRouteMatch; | |
| use Drupal\Core\Session\AccountProxy; | |
| use Drupal\Core\Url; | |
| -use Drupal\group\Entity\Group; | |
| -use Drupal\user\Entity\User; | |
| +use Drupal\social_group\SocialGroupInterface; | |
| use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |
| use Symfony\Component\HttpFoundation\RedirectResponse; | |
| use Symfony\Component\HttpKernel\Event\RequestEvent; | |
| @@ -19,6 +18,26 @@ use Symfony\Component\HttpKernel\KernelEvents; | |
| */ | |
| class RedirectSubscriber implements EventSubscriberInterface { | |
| + /** | |
| + * The route name of the default page of any group type except closed groups. | |
| + */ | |
| + private const DEFAULT_ROUTE = 'social_group.stream'; | |
| + | |
| + /** | |
| + * The route name of the group default page is provided by the current module. | |
| + */ | |
| + private const ALTERNATIVE_ROUTE = 'social_group_default.group_home'; | |
| + | |
| + /** | |
| + * The route name of the default page of any group. | |
| + */ | |
| + private const DEFAULT_GROUP_ROUTE = 'entity.group.canonical'; | |
| + | |
| + /** | |
| + * The route name of the default page of closed groups. | |
| + */ | |
| + private const DEFAULT_CLOSED_ROUTE = 'view.group_information.page_group_about'; | |
| + | |
| /** | |
| * The current route. | |
| * | |
| @@ -34,7 +53,7 @@ class RedirectSubscriber implements EventSubscriberInterface { | |
| protected $currentUser; | |
| /** | |
| - * Redirectsubscriber construct. | |
| + * RedirectSubscriber constructor. | |
| * | |
| * @param \Drupal\Core\Routing\CurrentRouteMatch $route_match | |
| * The current route. | |
| @@ -47,10 +66,7 @@ class RedirectSubscriber implements EventSubscriberInterface { | |
| } | |
| /** | |
| - * Get the request events. | |
| - * | |
| - * @return mixed | |
| - * Returns request events. | |
| + * {@inheritdoc} | |
| */ | |
| public static function getSubscribedEvents() { | |
| $events[KernelEvents::REQUEST][] = ['groupLandingPage']; | |
| @@ -66,52 +82,54 @@ class RedirectSubscriber implements EventSubscriberInterface { | |
| public function groupLandingPage(RequestEvent $event) { | |
| // First check if the current route is the group canonical. | |
| - $routeMatch = $this->currentRoute->getRouteName(); | |
| + $route_name = $this->currentRoute->getRouteName(); | |
| // Not group canonical, then we leave. | |
| if ( | |
| - $routeMatch !== 'entity.group.canonical' && | |
| - $routeMatch !== 'social_group_default.group_home' | |
| + $route_name !== self::DEFAULT_GROUP_ROUTE && | |
| + $route_name !== self::ALTERNATIVE_ROUTE | |
| ) { | |
| return; | |
| } | |
| // Fetch the group parameter and check if's an actual group. | |
| $group = $this->currentRoute->getParameter('group'); | |
| + | |
| // Not group, then we leave. | |
| - if (!$group instanceof Group) { | |
| + if (!$group instanceof SocialGroupInterface) { | |
| return; | |
| } | |
| - // Set the already default redirect route. | |
| - $defaultRoute = 'social_group.stream'; | |
| - $defaultClosedRoute = 'view.group_information.page_group_about'; | |
| - | |
| - // Check if this group has a custom route set. | |
| - $route = $group->getFieldValue('default_route', 'value'); | |
| - | |
| // Check if current user is a member. | |
| - if ($group->getMember(User::load($this->currentUser->id())) === FALSE) { | |
| - $route = $group->getFieldValue('default_route_an', 'value'); | |
| + if (!$group->getMember($this->currentUser)) { | |
| + /** @var string|null $route */ | |
| + $route = $group->default_route_an->value; | |
| + | |
| // If you're not a member and the group type is closed. | |
| if ($route === NULL) { | |
| - $route = ($group->getGroupType()->id() === 'closed_group') ? $defaultClosedRoute : $defaultRoute; | |
| + $route = $group->getGroupType()->id() === 'closed_group' | |
| + ? self::DEFAULT_CLOSED_ROUTE : self::DEFAULT_ROUTE; | |
| } | |
| } | |
| + else { | |
| + /** @var string|null $route */ | |
| + $route = $group->default_route->value; | |
| - // Still no route here? Then we use the normal default. | |
| - if ($route === NULL) { | |
| - $route = $defaultRoute; | |
| + // Still no route here? Then we use the normal default. | |
| + if ($route === NULL) { | |
| + $route = self::DEFAULT_ROUTE; | |
| + } | |
| } | |
| // Determine the URL we want to redirect to. | |
| $url = Url::fromRoute($route, ['group' => $group->id()]); | |
| // If it's not set, set to canonical, or the current user has no access. | |
| - if (!isset($route) || ($route === $routeMatch) || $url->access($this->currentUser) === FALSE) { | |
| + if ($route === $route_name || $url->access($this->currentUser) === FALSE) { | |
| // This basically means that the normal flow remains intact. | |
| return; | |
| } | |
| + | |
| // Redirect. | |
| $event->setResponse(new RedirectResponse($url->toString())); | |
| } | |
| diff --git a/modules/social_features/social_group/modules/social_group_flexible_group/config/install/field.storage.group.field_group_allowed_join_method.yml b/modules/social_features/social_group/modules/social_group_flexible_group/config/install/field.storage.group.field_group_allowed_join_method.yml | |
| index 5aee3aa4a..9b49ffa3a 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_flexible_group/config/install/field.storage.group.field_group_allowed_join_method.yml | |
| +++ b/modules/social_features/social_group/modules/social_group_flexible_group/config/install/field.storage.group.field_group_allowed_join_method.yml | |
| @@ -9,14 +9,8 @@ field_name: field_group_allowed_join_method | |
| entity_type: group | |
| type: list_string | |
| settings: | |
| - allowed_values: | |
| - - | |
| - value: direct | |
| - label: 'Join directly' | |
| - - | |
| - value: added | |
| - label: 'Be added' | |
| - allowed_values_function: '' | |
| + allowed_values: { } | |
| + allowed_values_function: '_social_group_allowed_values_callback' | |
| module: options | |
| locked: false | |
| cardinality: -1 | |
| diff --git a/modules/social_features/social_group/modules/social_group_flexible_group/config/update/social_group_flexible_group_update_11204.yml b/modules/social_features/social_group/modules/social_group_flexible_group/config/update/social_group_flexible_group_update_11204.yml | |
| new file mode 100644 | |
| index 000000000..c0ba3f366 | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/modules/social_group_flexible_group/config/update/social_group_flexible_group_update_11204.yml | |
| @@ -0,0 +1,16 @@ | |
| +field.storage.group.field_group_allowed_join_method: | |
| + expected_config: | |
| + settings: | |
| + allowed_values: | |
| + - | |
| + value: direct | |
| + label: 'Join directly' | |
| + - | |
| + value: added | |
| + label: 'Be added' | |
| + allowed_values_function: '' | |
| + update_actions: | |
| + change: | |
| + settings: | |
| + allowed_values: { } | |
| + allowed_values_function: '_social_group_allowed_values_callback' | |
| diff --git a/modules/social_features/social_group/modules/social_group_flexible_group/social_group_flexible_group.install b/modules/social_features/social_group/modules/social_group_flexible_group/social_group_flexible_group.install | |
| index 68d5b6325..d8b885796 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_flexible_group/social_group_flexible_group.install | |
| +++ b/modules/social_features/social_group/modules/social_group_flexible_group/social_group_flexible_group.install | |
| @@ -502,3 +502,17 @@ function social_group_flexible_group_update_11203(): void { | |
| $config_storage->write('language.content_settings.taxonomy_term.group_type', (array) $source->read('language.content_settings.taxonomy_term.group_type_11203')); | |
| } | |
| + | |
| +/** | |
| + * Allow extending list of allowed join methods dynamically. | |
| + */ | |
| +function social_group_flexible_group_update_11204(): string { | |
| + /** @var \Drupal\update_helper\UpdaterInterface $update_helper */ | |
| + $update_helper = \Drupal::service('update_helper.updater'); | |
| + | |
| + // Execute configuration update definitions with logging of success. | |
| + $update_helper->executeUpdate('social_group_flexible_group', __FUNCTION__); | |
| + | |
| + // Output logged messages to related channel of update execution. | |
| + return $update_helper->logger()->output(); | |
| +} | |
| diff --git a/modules/social_features/social_group/modules/social_group_flexible_group/social_group_flexible_group.module b/modules/social_features/social_group/modules/social_group_flexible_group/social_group_flexible_group.module | |
| index 65ad3dde9..0bcff1257 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_flexible_group/social_group_flexible_group.module | |
| +++ b/modules/social_features/social_group/modules/social_group_flexible_group/social_group_flexible_group.module | |
| @@ -19,6 +19,7 @@ use Drupal\block\Entity\Block; | |
| use Drupal\field\Entity\FieldStorageConfig; | |
| use Drupal\group\Entity\Group; | |
| use Drupal\group\Entity\GroupInterface; | |
| +use Drupal\social_group\SocialGroupInterface; | |
| use Drupal\social_group_flexible_group\FlexibleGroupContentVisibilityUpdate; | |
| use Drupal\views\Plugin\views\query\QueryPluginBase; | |
| use Drupal\views\Plugin\views\row\EntityRow; | |
| @@ -78,20 +79,6 @@ function social_group_flexible_group_form_alter(&$form, FormStateInterface $form | |
| 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. | |
| @@ -130,18 +117,6 @@ function social_group_flexible_group_form_alter(&$form, FormStateInterface $form | |
| } | |
| // 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'); | |
| } | |
| @@ -284,7 +259,7 @@ function social_group_flexible_group_group_access(EntityInterface $entity, $oper | |
| $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 */ | |
| + /** @var \Drupal\social_group\SocialGroupInterface $entity */ | |
| if ($operation !== 'view' || !$entity->hasField('field_flexible_group_visibility')) { | |
| return $result; | |
| } | |
| @@ -294,32 +269,32 @@ function social_group_flexible_group_group_access(EntityInterface $entity, $oper | |
| } | |
| // If group visibility value doesn't exist we can skip. | |
| - if (empty($entity->getFieldValue('field_flexible_group_visibility', 'value'))) { | |
| + if ($entity->field_flexible_group_visibility->isEmpty()) { | |
| 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; | |
| - } | |
| + switch ($entity->field_flexible_group_visibility->value) { | |
| + // If group visibility exists and public is selected, we can skip. | |
| + case '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); | |
| + // If group visibility exists and community or members is selected, check | |
| + // if user is logged. | |
| + case '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. | |
| + case 'members': | |
| + $not_a_member = !$entity->hasMember($account) || $account->isAnonymous(); | |
| + | |
| + return AccessResult::forbiddenIf($not_a_member) | |
| + ->cachePerPermissions() | |
| + ->cachePerUser() | |
| + ->addCacheableDependency($entity) | |
| + ->addCacheableDependency($account); | |
| } | |
| return $result; | |
| @@ -357,45 +332,53 @@ function social_group_flexible_group_preprocess_form_element(&$variables) { | |
| } | |
| /** | |
| - * Implements template_preprocess_form_element(). | |
| + * Implements hook_preprocess_HOOK(). | |
| */ | |
| -function social_group_flexible_group_preprocess_fieldset(&$variables) { | |
| +function social_group_flexible_group_preprocess_fieldset(array &$variables): void { | |
| // 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); | |
| - } | |
| + if (empty($element['#field_name'])) { | |
| + return; | |
| + } | |
| - // 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); | |
| - } | |
| + $fields = [ | |
| + 'field_flexible_group_visibility' => t('Group Visibility'), | |
| + 'field_group_allowed_visibility' => t('Group content visibility'), | |
| + ]; | |
| - // 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); | |
| - } | |
| + if (!isset($fields[$element['#field_name']])) { | |
| + return; | |
| + } | |
| + | |
| + $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( | |
| + $element['#field_name'], | |
| + $fields[$element['#field_name']], | |
| + $description, | |
| + ); | |
| +} | |
| + | |
| +/** | |
| + * Implements hook_social_group_join_method_usage(). | |
| + */ | |
| +function social_group_flexible_group_social_group_join_method_usage(): array { | |
| + return [ | |
| + [ | |
| + 'entity_type' => 'group', | |
| + 'bundle' => 'flexible_group', | |
| + 'field' => 'field_group_allowed_join_method', | |
| + ], | |
| + ]; | |
| } | |
| /** | |
| @@ -478,20 +461,20 @@ function social_group_flexible_group_can_join_directly(GroupInterface $group) { | |
| /** | |
| * Check if a user can be added to a group. | |
| * | |
| - * @param \Drupal\group\Entity\Group $group | |
| + * @param \Drupal\social_group\SocialGroupInterface $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; | |
| +function social_group_flexible_group_can_be_added(SocialGroupInterface $group): bool { | |
| + return in_array( | |
| + 'added', | |
| + array_column( | |
| + $group->get('field_group_allowed_join_method')->getValue(), | |
| + 'value', | |
| + ), | |
| + ); | |
| } | |
| /** | |
| @@ -554,18 +537,17 @@ function social_group_flexible_group_members_enabled(Group $group) { | |
| /** | |
| * 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) | |
| +function social_group_flexible_group_menu_local_actions_alter(array &$local_actions): void { | |
| + $account = \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 = _social_group_get_current_group()) !== NULL && | |
| + $group->getGroupType()->id() === 'flexible_group' && | |
| + !social_group_flexible_group_can_be_added($group) && | |
| + !$account->hasPermission('manage all groups') && | |
| + !$group->hasPermission('administer members', $account) | |
| ) { | |
| unset($local_actions['social_group.add_member']); | |
| } | |
| @@ -574,7 +556,7 @@ function social_group_flexible_group_menu_local_actions_alter(&$local_actions) { | |
| /** | |
| * Determine whether a user can see flexible groups as outsider. | |
| * | |
| - * @param \Drupal\group\Entity\Group $group | |
| + * @param \Drupal\social_group\SocialGroupInterface $group | |
| * The group we are checking. | |
| * @param \Drupal\Core\Session\AccountInterface $account | |
| * The user to check for. | |
| @@ -582,14 +564,17 @@ function social_group_flexible_group_menu_local_actions_alter(&$local_actions) { | |
| * @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 { | |
| +function social_group_flexible_group_can_view_flexible_groups( | |
| + SocialGroupInterface $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) { | |
| + if ($group->hasMember($account)) { | |
| return TRUE; | |
| } | |
| @@ -730,9 +715,11 @@ function social_group_flexible_group_block_access(Block $block, $operation, Acco | |
| return AccessResult::neutral(); | |
| } | |
| - if (!$group->getMember($account) && | |
| + if ( | |
| + !$group->hasMember($account) && | |
| !social_group_flexible_group_community_enabled($group) && | |
| - !social_group_flexible_group_public_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. | |
| diff --git a/modules/social_features/social_group/modules/social_group_flexible_group/src/Access/FlexibleGroupContentAccessCheck.php b/modules/social_features/social_group/modules/social_group_flexible_group/src/Access/FlexibleGroupContentAccessCheck.php | |
| index d6f8d858f..e1fd15ecf 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_flexible_group/src/Access/FlexibleGroupContentAccessCheck.php | |
| +++ b/modules/social_features/social_group/modules/social_group_flexible_group/src/Access/FlexibleGroupContentAccessCheck.php | |
| @@ -2,13 +2,12 @@ | |
| namespace Drupal\social_group_flexible_group\Access; | |
| -use Drupal\group\Entity\Group; | |
| use Drupal\Core\Access\AccessResult; | |
| use Drupal\Core\Routing\Access\AccessInterface; | |
| use Drupal\Core\Routing\RouteMatchInterface; | |
| use Drupal\Core\Session\AccountInterface; | |
| use Drupal\group\Entity\GroupTypeInterface; | |
| -use Drupal\group\GroupMembership; | |
| +use Drupal\social_group\SocialGroupInterface; | |
| use Symfony\Component\Routing\Route; | |
| /** | |
| @@ -45,7 +44,7 @@ class FlexibleGroupContentAccessCheck implements AccessInterface { | |
| // Don't interfere if the group isn't a real group. | |
| $group = $parameters->get('group'); | |
| - if (!$group instanceof Group) { | |
| + if (!$group instanceof SocialGroupInterface) { | |
| return AccessResult::allowed(); | |
| } | |
| @@ -54,12 +53,11 @@ class FlexibleGroupContentAccessCheck implements AccessInterface { | |
| return AccessResult::allowed(); | |
| } | |
| + $is_member = $group->hasMember($account); | |
| + | |
| // Handling the visibility of a group. | |
| if ($group->hasField('field_flexible_group_visibility')) { | |
| - $group_visibility_value = $group->getFieldValue('field_flexible_group_visibility', 'value'); | |
| - $is_member = $group->getMember($account) instanceof GroupMembership; | |
| - | |
| - switch ($group_visibility_value) { | |
| + switch ($group->field_flexible_group_visibility->value) { | |
| case 'members': | |
| if (!$is_member) { | |
| return AccessResult::forbidden(); | |
| @@ -86,7 +84,7 @@ class FlexibleGroupContentAccessCheck implements AccessInterface { | |
| } | |
| // If User is a member we can also rely on Group to take permissions. | |
| - if ($group->getMember($account) !== FALSE) { | |
| + if ($is_member) { | |
| return AccessResult::allowed()->addCacheableDependency($group); | |
| } | |
| diff --git a/modules/social_features/social_group/modules/social_group_flexible_group/src/Access/FlexibleGroupJoinPermissionAccessCheck.php b/modules/social_features/social_group/modules/social_group_flexible_group/src/Access/FlexibleGroupJoinPermissionAccessCheck.php | |
| index ae4c62bea..6e799e214 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_flexible_group/src/Access/FlexibleGroupJoinPermissionAccessCheck.php | |
| +++ b/modules/social_features/social_group/modules/social_group_flexible_group/src/Access/FlexibleGroupJoinPermissionAccessCheck.php | |
| @@ -3,13 +3,12 @@ | |
| namespace Drupal\social_group_flexible_group\Access; | |
| use Drupal\group\Access\GroupAccessResult; | |
| -use Drupal\group\Entity\Group; | |
| -use Drupal\group\Entity\GroupInterface; | |
| use Drupal\Core\Access\AccessResult; | |
| use Drupal\Core\Routing\Access\AccessInterface; | |
| use Drupal\Core\Routing\RouteMatchInterface; | |
| use Drupal\Core\Session\AccountInterface; | |
| use Drupal\group\Entity\GroupTypeInterface; | |
| +use Drupal\social_group\SocialGroupInterface; | |
| use Symfony\Component\Routing\Route; | |
| /** | |
| @@ -47,9 +46,11 @@ class FlexibleGroupJoinPermissionAccessCheck implements AccessInterface { | |
| // Don't interfere if the group isn't a real group. | |
| $group = $parameters->get('group'); | |
| - if (!$group instanceof GroupInterface) { | |
| + | |
| + if (!$group instanceof SocialGroupInterface) { | |
| $group = _social_group_get_current_group(); | |
| - if (!$group instanceof GroupInterface) { | |
| + | |
| + if (!$group instanceof SocialGroupInterface) { | |
| return AccessResult::neutral(); | |
| } | |
| } | |
| @@ -105,7 +106,7 @@ class FlexibleGroupJoinPermissionAccessCheck implements AccessInterface { | |
| * | |
| * @param string $permission | |
| * The permission we need to check access for. | |
| - * @param \Drupal\group\Entity\Group $group | |
| + * @param \Drupal\social_group\SocialGroupInterface $group | |
| * The Group we are on. | |
| * @param \Drupal\Core\Session\AccountInterface $account | |
| * The account to check access for. | |
| @@ -113,9 +114,14 @@ class FlexibleGroupJoinPermissionAccessCheck implements AccessInterface { | |
| * The parametrized route. | |
| * | |
| * @return bool | |
| - * FALSE if its not allowed. | |
| + * FALSE if it's not allowed. | |
| */ | |
| - private function calculateJoinPermission($permission, Group $group, AccountInterface $account, RouteMatchInterface $route_match) { | |
| + private function calculateJoinPermission( | |
| + string $permission, | |
| + SocialGroupInterface $group, | |
| + AccountInterface $account, | |
| + RouteMatchInterface $route_match | |
| + ): bool { | |
| $direct_option = social_group_flexible_group_can_join_directly($group); | |
| $added_option = social_group_flexible_group_can_be_added($group); | |
| @@ -129,13 +135,13 @@ class FlexibleGroupJoinPermissionAccessCheck implements AccessInterface { | |
| if (!$direct_option && | |
| $route_match->getRouteName() === 'view.group_manage_members.page_group_manage_members' && | |
| $account->isAuthenticated() && | |
| - !$group->getMember($account) && | |
| + !$group->hasMember($account) && | |
| !$group->hasPermission('administer members', $account)) { | |
| return FALSE; | |
| } | |
| // There is no direct join method so it's not allowed to go to /join. | |
| - if ($permission === 'join direct' && !$direct_option && !$group->getMember($account)) { | |
| + if ($permission === 'join direct' && !$direct_option && !$group->hasMember($account)) { | |
| return FALSE; | |
| } | |
| diff --git a/modules/social_features/social_group/modules/social_group_flexible_group/src/EventSubscriber/RedirectSubscriber.php b/modules/social_features/social_group/modules/social_group_flexible_group/src/EventSubscriber/RedirectSubscriber.php | |
| index 128a72e0c..f3bafa6cc 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_flexible_group/src/EventSubscriber/RedirectSubscriber.php | |
| +++ b/modules/social_features/social_group/modules/social_group_flexible_group/src/EventSubscriber/RedirectSubscriber.php | |
| @@ -79,7 +79,7 @@ class RedirectSubscriber implements EventSubscriberInterface { | |
| // If the user can manage groups or the user is a member. | |
| if ( | |
| $this->currentUser->hasPermission('manage all groups') || | |
| - $group->getMember($this->currentUser) | |
| + $group->hasMember($this->currentUser) | |
| ) { | |
| return; | |
| } | |
| diff --git a/modules/social_features/social_group/modules/social_group_gvbo/src/Form/SocialGroupViewsBulkOperationsConfigureAction.php b/modules/social_features/social_group/modules/social_group_gvbo/src/Form/SocialGroupViewsBulkOperationsConfigureAction.php | |
| index c0624d8f8..faced972b 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_gvbo/src/Form/SocialGroupViewsBulkOperationsConfigureAction.php | |
| +++ b/modules/social_features/social_group/modules/social_group_gvbo/src/Form/SocialGroupViewsBulkOperationsConfigureAction.php | |
| @@ -33,8 +33,10 @@ class SocialGroupViewsBulkOperationsConfigureAction extends GroupViewsBulkOperat | |
| if ($url->getRouteName() === 'views_bulk_operations.confirm') { | |
| $parameters = $url->getRouteParameters(); | |
| - if (empty($parameters['group'])) { | |
| - $group = _social_group_get_current_group(); | |
| + if ( | |
| + empty($parameters['group']) && | |
| + ($group = _social_group_get_current_group()) !== NULL | |
| + ) { | |
| $parameters['group'] = $group->id(); | |
| } | |
| diff --git a/modules/social_features/social_group/modules/social_group_invite/social_group_invite.module b/modules/social_features/social_group/modules/social_group_invite/social_group_invite.module | |
| index a905e5ce1..ce56ef7f7 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_invite/social_group_invite.module | |
| +++ b/modules/social_features/social_group/modules/social_group_invite/social_group_invite.module | |
| @@ -16,7 +16,6 @@ use Drupal\Core\Render\BubbleableMetadata; | |
| 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\GroupContentInterface; | |
| @@ -166,8 +165,10 @@ function social_group_invite_preprocess_views_view(&$variables) { | |
| } | |
| } | |
| // 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(); | |
| + elseif ( | |
| + $view->id() === 'social_group_invitations' && | |
| + ($group = _social_group_get_current_group()) !== NULL | |
| + ) { | |
| $variables['more'] = [ | |
| '#title' => t('Back to group'), | |
| '#type' => 'link', | |
| @@ -308,17 +309,17 @@ function social_group_invite_theme_registry_alter(&$theme_registry) { | |
| */ | |
| 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(); | |
| - } | |
| + if ( | |
| + \Drupal::routeMatch()->getParameter('view_id') === 'social_group_invitations' && | |
| + !empty(\Drupal::routeMatch()->getParameter('group')) && | |
| + ($group = _social_group_get_current_group()) !== NULL | |
| + ) { | |
| + $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' && | |
| @@ -487,86 +488,58 @@ function social_group_invite_preprocess_activity(&$variables) { | |
| } | |
| /** | |
| - * Implements hook_preprocess_group(). | |
| + * Implements hook_preprocess_join(). | |
| */ | |
| -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; | |
| - | |
| - if ($variables['view_mode'] === 'statistic') { | |
| - $url = Url::fromRoute('ginvite.invitation.bulk', ['group' => $group->id()]); | |
| - $group_url = Url::fromRoute('view.group_information.page_group_about', ['group' => $group->id()], ['absolute' => TRUE]); | |
| - // For groups that only group members have access to, we should not show the | |
| - // share link functionality. | |
| - if ( | |
| - ($group->hasField('field_flexible_group_visibility') && | |
| - !$group->get('field_flexible_group_visibility')->isEmpty() && | |
| - $group->get('field_flexible_group_visibility')->getValue()[0]['value'] === 'members') || | |
| - in_array($group->bundle(), ['closed_group', 'secret_group']) | |
| - ) { | |
| - $group_url = NULL; | |
| - } | |
| +function social_group_invite_preprocess_join(array &$variables): void { | |
| + $entity = $variables['entity']; | |
| - $group_settings = \Drupal::config('social_group.settings'); | |
| - // Show share/invite button only if member is allowed to do that. | |
| - if ( | |
| - (bool) $group_settings->get('group_invite.invite_by_members') && | |
| - $url->access(\Drupal::currentUser()) | |
| - ) { | |
| - $variables['members_allowed_to_invite'] = TRUE; | |
| - $variables['invite_content'] = [ | |
| - '#theme' => 'invite_to_group_by_member', | |
| - '#url' => $group_url, | |
| - '#link' => Link::fromTextAndUrl(t('Invite via email'), $url) | |
| - ->toRenderable(), | |
| - ]; | |
| - } | |
| + if (!$entity instanceof GroupInterface) { | |
| + return; | |
| } | |
| - // Only for groups that have invites enabled. | |
| - if (!$group_type->hasContentPlugin('group_invitation')) { | |
| + $url = Url::fromRoute('ginvite.invitation.bulk', [ | |
| + 'group' => $entity->id(), | |
| + ]); | |
| + | |
| + if (!$url->access()) { | |
| return; | |
| } | |
| - // Only for LU. | |
| - $account = \Drupal::currentUser(); | |
| - if ($account->isAnonymous()) { | |
| + $config = \Drupal::config('social_group.settings'); | |
| + | |
| + if (!$config->get('group_invite.invite_by_members')) { | |
| 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'; | |
| - } | |
| - } | |
| + // For groups that only group members have access to, we should not show the | |
| + // share link functionality. | |
| + if ( | |
| + $entity->hasField('field_flexible_group_visibility') && | |
| + !$entity->field_flexible_group_visibility->isEmpty() && | |
| + $entity->field_flexible_group_visibility->value === 'members' || | |
| + in_array($entity->bundle(), ['closed_group', 'secret_group']) | |
| + ) { | |
| + $group_url = NULL; | |
| + } | |
| + else { | |
| + $group_url = Url::fromRoute('view.group_information.page_group_about', [ | |
| + 'group' => $entity->id(), | |
| + ], [ | |
| + 'absolute' => TRUE, | |
| + ]); | |
| } | |
| + | |
| + // Show share/invite button only if member is allowed to do that. | |
| + $variables['invite_widget'] = [ | |
| + '#theme' => 'invite_to_group_by_member', | |
| + '#url' => $group_url, | |
| + '#link' => Link::fromTextAndUrl(t('Invite via email'), $url) | |
| + ->toRenderable(), | |
| + ]; | |
| } | |
| /** | |
| - * Implements hook_form_FORM_ID_alter() for "social_group_form". | |
| + * 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') | |
| diff --git a/modules/social_features/social_group/modules/social_group_invite/src/Plugin/Block/SocialGroupInviteNotificationBlock.php b/modules/social_features/social_group/modules/social_group_invite/src/Plugin/Block/SocialGroupInviteNotificationBlock.php | |
| index a313d0172..7c0183ac5 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_invite/src/Plugin/Block/SocialGroupInviteNotificationBlock.php | |
| +++ b/modules/social_features/social_group/modules/social_group_invite/src/Plugin/Block/SocialGroupInviteNotificationBlock.php | |
| @@ -10,6 +10,7 @@ use Drupal\Core\Session\AccountInterface; | |
| use Drupal\Core\StringTranslation\TranslationManager; | |
| use Drupal\ginvite\GroupInvitationLoaderInterface; | |
| use Drupal\ginvite\Plugin\GroupContentEnabler\GroupInvitation; | |
| +use Drupal\social_group\SocialGroupInterface; | |
| use Symfony\Component\DependencyInjection\ContainerInterface; | |
| /** | |
| @@ -30,11 +31,9 @@ class SocialGroupInviteNotificationBlock extends BlockBase implements ContainerF | |
| protected $account; | |
| /** | |
| - * Group entity. | |
| - * | |
| - * @var \Drupal\group\Entity\GroupInterface | |
| + * The group entity object. | |
| */ | |
| - protected $group; | |
| + protected ?SocialGroupInterface $group; | |
| /** | |
| * Translation manager. | |
| @@ -100,7 +99,10 @@ class SocialGroupInviteNotificationBlock extends BlockBase implements ContainerF | |
| */ | |
| public function build() { | |
| // Only when group invite is installed. | |
| - if (!$this->group->getGroupType()->hasContentPlugin('group_invitation')) { | |
| + if ( | |
| + $this->group === NULL || | |
| + !$this->group->getGroupType()->hasContentPlugin('group_invitation') | |
| + ) { | |
| return []; | |
| } | |
| @@ -157,11 +159,16 @@ class SocialGroupInviteNotificationBlock extends BlockBase implements ContainerF | |
| * {@inheritdoc} | |
| */ | |
| public function getCacheTags() { | |
| - return Cache::mergeTags(parent::getCacheTags(), [ | |
| + $tags = [ | |
| 'group_content_list:entity:' . $this->account->id(), | |
| 'group_content_list:plugin:group_invitation:entity:' . $this->account->id(), | |
| - 'group:' . $this->group->id(), | |
| - ]); | |
| + ]; | |
| + | |
| + if ($this->group !== NULL) { | |
| + $tags[] = 'group:' . $this->group->id(); | |
| + } | |
| + | |
| + return Cache::mergeTags(parent::getCacheTags(), $tags); | |
| } | |
| } | |
| diff --git a/modules/social_features/social_group/modules/social_group_invite/src/Plugin/Block/SocialInviteLocalActionsBlock.php b/modules/social_features/social_group/modules/social_group_invite/src/Plugin/Block/SocialInviteLocalActionsBlock.php | |
| index 2e1313327..828e45861 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_invite/src/Plugin/Block/SocialInviteLocalActionsBlock.php | |
| +++ b/modules/social_features/social_group/modules/social_group_invite/src/Plugin/Block/SocialInviteLocalActionsBlock.php | |
| @@ -124,10 +124,10 @@ class SocialInviteLocalActionsBlock extends BlockBase implements ContainerFactor | |
| $build = []; | |
| // Get current group so we can build correct links. | |
| - if (_social_group_invite_current_type_enabled_invites()) { | |
| - /** @var \Drupal\group\Entity\GroupInterface $group */ | |
| - $group = _social_group_get_current_group(); | |
| - | |
| + if ( | |
| + _social_group_invite_current_type_enabled_invites() && | |
| + ($group = _social_group_get_current_group()) !== NULL | |
| + ) { | |
| $button = [ | |
| '#type' => 'link', | |
| '#title' => $this->t('View invites'), | |
| diff --git a/modules/social_features/social_group/modules/social_group_invite/src/Plugin/Join/SocialGroupInviteJoin.php b/modules/social_features/social_group/modules/social_group_invite/src/Plugin/Join/SocialGroupInviteJoin.php | |
| new file mode 100644 | |
| index 000000000..8b73d6a9c | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/modules/social_group_invite/src/Plugin/Join/SocialGroupInviteJoin.php | |
| @@ -0,0 +1,128 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\social_group_invite\Plugin\Join; | |
| + | |
| +use Drupal\Core\Link; | |
| +use Drupal\Core\Url; | |
| +use Drupal\ginvite\GroupInvitation as GroupInvitationWrapper; | |
| +use Drupal\ginvite\GroupInvitationLoaderInterface; | |
| +use Drupal\ginvite\Plugin\GroupContentEnabler\GroupInvitation as GroupInvitationEnabler; | |
| +use Drupal\group\Entity\GroupContentInterface; | |
| +use Drupal\social_group\EntityMemberInterface; | |
| +use Drupal\social_group\Plugin\Join\SocialGroupDirectJoin; | |
| +use Symfony\Component\DependencyInjection\ContainerInterface; | |
| + | |
| +/** | |
| + * Provides a join plugin instance for joining after invitation. | |
| + * | |
| + * @Join( | |
| + * id = "social_group_invite_join", | |
| + * entityTypeId = "group", | |
| + * method = "added", | |
| + * weight = 30, | |
| + * ) | |
| + */ | |
| +class SocialGroupInviteJoin extends SocialGroupDirectJoin { | |
| + | |
| + /** | |
| + * The group invitation loader. | |
| + */ | |
| + private GroupInvitationLoaderInterface $loader; | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public static function create( | |
| + ContainerInterface $container, | |
| + array $configuration, | |
| + $plugin_id, | |
| + $plugin_definition | |
| + ): self { | |
| + /** @var self $instance */ | |
| + $instance = parent::create( | |
| + $container, | |
| + $configuration, | |
| + $plugin_id, | |
| + $plugin_definition, | |
| + ); | |
| + | |
| + $instance->loader = $container->get('ginvite.invitation_loader'); | |
| + | |
| + return $instance; | |
| + } | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public function actions(EntityMemberInterface $entity, array &$variables): array { | |
| + $items = []; | |
| + $invited = FALSE; | |
| + | |
| + // Only for groups that have invites enabled. | |
| + /** @var \Drupal\social_group\SocialGroupInterface $entity */ | |
| + if ( | |
| + $entity->getGroupType()->hasContentPlugin('group_invitation') && | |
| + $this->currentUser->isAuthenticated() | |
| + ) { | |
| + // Check if the user has a pending invite for the group. | |
| + $invitations = $this->loader->loadByProperties([ | |
| + 'entity_id' => $this->currentUser->id(), | |
| + 'gid' => $entity->id(), | |
| + 'invitation_status' => GroupInvitationEnabler::INVITATION_PENDING, | |
| + ]); | |
| + | |
| + // We have pending invites, let's build a button to accept or decline one. | |
| + if (count($invitations) > 0) { | |
| + $invitation = reset($invitations); | |
| + | |
| + if ($invitation instanceof GroupInvitationWrapper) { | |
| + // Let's grab the group content, so we can build the URL. | |
| + $group_content = $invitation->getGroupContent(); | |
| + | |
| + if ($group_content instanceof GroupContentInterface) { | |
| + $invited = TRUE; | |
| + } | |
| + } | |
| + } | |
| + } | |
| + | |
| + if ($invited && isset($group_content)) { | |
| + $items[] = [ | |
| + 'label' => $this->t('Accept'), | |
| + 'url' => Url::fromRoute('ginvite.invitation.accept', [ | |
| + 'group_content' => $group_content->id(), | |
| + ]), | |
| + ]; | |
| + | |
| + $items[] = Link::createFromRoute( | |
| + $this->t('Decline'), | |
| + 'ginvite.invitation.decline', | |
| + ['group_content' => $group_content->id()], | |
| + ); | |
| + | |
| + $variables['user_is_invited'] = TRUE; | |
| + $variables['#cache']['contexts'][] = 'user'; | |
| + $variables['#cache']['tags'][] = 'group_content_list:entity:' . $this->currentUser->id(); | |
| + $variables['#cache']['tags'][] = 'group_content_list:plugin:group_invitation:entity:' . $this->currentUser->id(); | |
| + } | |
| + elseif ( | |
| + count($items = parent::actions($entity, $variables)) === 1 && | |
| + $entity->bundle() === 'flexible_group' || | |
| + $entity->bundle() === 'closed_group' && | |
| + !$entity->hasPermission('manage all groups', $this->currentUser) | |
| + ) { | |
| + if (count($items) === 0) { | |
| + $items[] = ['attributes' => ['class' => ['btn-accent']]]; | |
| + } | |
| + else { | |
| + unset($items[0]['url']); | |
| + } | |
| + | |
| + $items[0]['label'] = $variables['cta'] = $this->t('Invitation only'); | |
| + $variables['closed_group'] = TRUE; | |
| + } | |
| + | |
| + return $items; | |
| + } | |
| + | |
| +} | |
| diff --git a/modules/social_features/social_group/modules/social_group_quickjoin/src/Controller/SocialQuickJoinController.php b/modules/social_features/social_group/modules/social_group_quickjoin/src/Controller/SocialQuickJoinController.php | |
| index 3c899c9ac..2803c00e1 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_quickjoin/src/Controller/SocialQuickJoinController.php | |
| +++ b/modules/social_features/social_group/modules/social_group_quickjoin/src/Controller/SocialQuickJoinController.php | |
| @@ -6,8 +6,7 @@ use Drupal\Core\Config\ConfigFactoryInterface; | |
| use Drupal\Core\Controller\ControllerBase; | |
| use Drupal\Core\Messenger\MessengerTrait; | |
| use Drupal\Core\Routing\CurrentRouteMatch; | |
| -use Drupal\Core\Url; | |
| -use Drupal\group\Entity\GroupInterface; | |
| +use Drupal\social_group\SocialGroupInterface; | |
| use Drupal\user\Entity\User; | |
| use Drupal\user\UserInterface; | |
| use Symfony\Component\DependencyInjection\ContainerInterface; | |
| @@ -62,21 +61,12 @@ class SocialQuickJoinController extends ControllerBase { | |
| /** | |
| * Function that add the current user to a group without confirmation step. | |
| * | |
| - * @param \Drupal\group\Entity\GroupInterface $group | |
| + * @param \Drupal\social_group\SocialGroupInterface $group | |
| * The group you want to join. | |
| * | |
| - * @return \Symfony\Component\HttpFoundation\RedirectResponse | |
| - * Where to redirect to. | |
| - * | |
| * @throws \Drupal\Core\Entity\EntityMalformedException | |
| */ | |
| - public function quickJoin(GroupInterface $group) { | |
| - | |
| - // No group, so go home. | |
| - if (!$group instanceof GroupInterface) { | |
| - return new RedirectResponse(Url::fromRoute('<front>')->toString()); | |
| - } | |
| - | |
| + public function quickJoin(SocialGroupInterface $group): RedirectResponse { | |
| // It's a group, so determine the path for redirection. | |
| $groupRedirect = $group->toUrl()->toString(); | |
| @@ -90,7 +80,7 @@ class SocialQuickJoinController extends ControllerBase { | |
| } | |
| // Already a member. | |
| - if ($group->getMember($this->currentUser())) { | |
| + if ($group->hasMember($this->currentUser())) { | |
| // Set a message. | |
| $this->messenger()->addMessage($this->t("You're already a member of this group.")); | |
| // Redirect to the group. | |
| diff --git a/modules/social_features/social_group/modules/social_group_request/social_group_request.api.php b/modules/social_features/social_group/modules/social_group_request/social_group_request.api.php | |
| index 9b3d33e67..a8f2de032 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_request/social_group_request.api.php | |
| +++ b/modules/social_features/social_group/modules/social_group_request/social_group_request.api.php | |
| @@ -16,6 +16,11 @@ | |
| * @param array $group_types | |
| * List of group types used in open social. | |
| * | |
| + * @deprecated in social:11.2.0 and is removed from social:12.0.0. Use | |
| + * hook_social_group_join_method_usage instead. | |
| + * | |
| + * @see https://www.drupal.org/node/3254715 | |
| + * | |
| * @ingroup social_group_api | |
| */ | |
| function hook_social_group_request_alter(array &$group_types) { | |
| diff --git a/modules/social_features/social_group/modules/social_group_request/social_group_request.module b/modules/social_features/social_group/modules/social_group_request/social_group_request.module | |
| index 5cf5c1d35..16cbe1dc0 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_request/social_group_request.module | |
| +++ b/modules/social_features/social_group/modules/social_group_request/social_group_request.module | |
| @@ -7,9 +7,9 @@ | |
| use Drupal\block\Entity\Block; | |
| use Drupal\Core\Access\AccessResult; | |
| -use Drupal\Core\Entity\ContentEntityInterface; | |
| use Drupal\Core\Entity\EntityTypeInterface; | |
| use Drupal\Core\Field\BaseFieldDefinition; | |
| +use Drupal\Core\Form\FormStateInterface; | |
| use Drupal\Core\Link; | |
| use Drupal\Core\Session\AccountInterface; | |
| use Drupal\Core\Url; | |
| @@ -18,7 +18,6 @@ use Drupal\field\Entity\FieldStorageConfig; | |
| use Drupal\grequest\Plugin\GroupContentEnabler\GroupMembershipRequest; | |
| use Drupal\group\Entity\GroupContentTypeInterface; | |
| use Drupal\group\Entity\GroupContentInterface; | |
| -use Drupal\Core\Form\FormStateInterface; | |
| use Drupal\views\ViewExecutable; | |
| use Drupal\message\Entity\Message; | |
| @@ -65,6 +64,83 @@ function social_group_request_entity_base_field_info(EntityTypeInterface $entity | |
| return $fields; | |
| } | |
| +/** | |
| + * Implements hook_social_group_join_method_usage_alter(). | |
| + */ | |
| +function social_group_request_social_group_join_method_usage_alter(array &$items): void { | |
| + /** @var array $all_bundles */ | |
| + $all_bundles = \Drupal::entityQuery('group_type') | |
| + ->accessCheck(FALSE) | |
| + ->execute(); | |
| + | |
| + $bundles = []; | |
| + | |
| + foreach ($items as $item) { | |
| + if ($item['entity_type'] === 'group') { | |
| + $add = FALSE; | |
| + | |
| + if (isset($item['field'])) { | |
| + if (isset($item['method'])) { | |
| + if (in_array('request', (array) $item['method'])) { | |
| + $add = TRUE; | |
| + } | |
| + } | |
| + else { | |
| + $add = TRUE; | |
| + } | |
| + } | |
| + elseif (in_array('request', (array) $item['method'])) { | |
| + $add = TRUE; | |
| + } | |
| + | |
| + if ($add) { | |
| + $bundles = array_merge( | |
| + $bundles, | |
| + isset($item['bundle']) ? (array) $item['bundle'] : $all_bundles, | |
| + ); | |
| + } | |
| + } | |
| + } | |
| + | |
| + $query = \Drupal::entityQuery('group_type')->accessCheck(FALSE); | |
| + | |
| + if (!empty($bundles)) { | |
| + $query->condition('id', $bundles, 'NOT IN'); | |
| + } | |
| + | |
| + /** @var array $bundles */ | |
| + $bundles = $query->execute(); | |
| + | |
| + if (!empty($bundles)) { | |
| + $item = [ | |
| + 'entity_type' => 'group', | |
| + 'field' => 'allow_request', | |
| + 'method' => 'request', | |
| + ]; | |
| + | |
| + if (count($bundles) < count($all_bundles)) { | |
| + $item['bundle'] = array_values($bundles); | |
| + } | |
| + | |
| + $items[] = $item; | |
| + } | |
| +} | |
| + | |
| +/** | |
| + * Implements hook_social_group_join_method_info(). | |
| + */ | |
| +function social_group_request_social_group_join_method_info(): array { | |
| + return [ | |
| + 'request' => [ | |
| + 'title' => t('Request to join'), | |
| + 'description' => t('users can "request to join" this @entity_type_id which | |
| +@entity_type_id managers approve/decline.'), | |
| + 'icon' => 'join_close', | |
| + 'weight' => 15, | |
| + ], | |
| + ]; | |
| +} | |
| + | |
| /** | |
| * Implements hook_ENTITY_TYPE_insert(). | |
| */ | |
| @@ -128,96 +204,37 @@ function social_group_request_theme_registry_alter(&$theme_registry) { | |
| } | |
| /** | |
| - * Implements hook_preprocess_group(). | |
| + * Implements hook_preprocess_HOOK(). | |
| */ | |
| -function social_group_request_preprocess_group(&$variables) { | |
| - /** @var \Drupal\group\Entity\GroupInterface $group */ | |
| - $group = $variables['group']; | |
| - $group_type = $group->getGroupType(); | |
| - | |
| - $account = \Drupal::currentUser(); | |
| - // If the user is already a member we don't bother processing any further. | |
| - if ($group->getMember($account)) { | |
| +function social_group_request_preprocess_group(array &$variables): void { | |
| + if (!\Drupal::config('social_group.settings')->get('social_group_type_required')) { | |
| return; | |
| } | |
| - if (!$group_type->hasContentPlugin('group_membership_request')) { | |
| - return; | |
| - } | |
| - | |
| - // If user has a pending invite we should skip the request button. | |
| - if (\Drupal::hasService('ginvite.invitation_loader')) { | |
| - /** @var \Drupal\ginvite\GroupInvitationLoader $loader */ | |
| - $loader = \Drupal::service('ginvite.invitation_loader'); | |
| - $group_invites = count($loader->loadByProperties(['gid' => $group->id(), 'uid' => $account->id()])); | |
| - if (NULL !== $group_invites && $group_invites > 0) { | |
| - return; | |
| - } | |
| - } | |
| - | |
| - $group_types = ['flexible_group']; | |
| - \Drupal::moduleHandler()->alter('social_group_request', $group_types); | |
| - | |
| - if (in_array($group_type->id(), $group_types)) { | |
| - $join_methods = $group->get('field_group_allowed_join_method')->getValue(); | |
| - | |
| - $request_option = in_array('request', array_column($join_methods, 'value'), FALSE); | |
| - if (!$request_option) { | |
| - $variables['allow_request'] = FALSE; | |
| - return; | |
| - } | |
| - } | |
| - else { | |
| - $allow_request = $group->get('allow_request'); | |
| - if ($allow_request->isEmpty() || $allow_request->value == 0) { | |
| - $variables['allow_request'] = FALSE; | |
| - return; | |
| - } | |
| - } | |
| + /** @var \Drupal\social_group\SocialGroupInterface $group */ | |
| + $group = $variables['group']; | |
| - if ($account->isAnonymous()) { | |
| - $variables['anonymous_request'] = TRUE; | |
| - $variables['group_operations_url'] = Url::fromRoute('social_group_request.anonymous_request_membership', ['group' => $group->id()]); | |
| - $variables['#attached']['library'][] = 'core/drupal.dialog.ajax'; | |
| - $variables['#attached']['library'][] = 'social_group_request/social_group_popup'; | |
| + if (!$group->hasField('field_group_type')) { | |
| return; | |
| } | |
| - if ( | |
| - !$group->hasPermission('request group membership', $account) || | |
| - !$group->hasField('allow_request') | |
| - ) { | |
| - $variables['allow_request'] = FALSE; | |
| - return; | |
| - } | |
| + /** @var \Drupal\social_group\JoinManagerInterface $manager */ | |
| + $manager = \Drupal::service('plugin.manager.social_group.join'); | |
| - $variables['closed_group'] = TRUE; | |
| - $variables['allow_request'] = TRUE; | |
| - $variables['group_operations_url'] = Url::fromRoute('grequest.request_membership', ['group' => $group->id()]); | |
| - $variables['cta'] = t('Request to join'); | |
| + /** @var string $bundle */ | |
| + $bundle = $group->getGroupType()->id(); | |
| - $contentTypeConfigId = $group | |
| - ->getGroupType() | |
| - ->getContentPlugin('group_membership_request') | |
| - ->getContentTypeConfigId(); | |
| + if ($manager->hasMethod($bundle, 'request')) { | |
| + $field = $group->field_group_type; | |
| - $request = \Drupal::entityQuery('group_content') | |
| - ->condition('type', $contentTypeConfigId) | |
| - ->condition('gid', $group->id()) | |
| - ->condition('entity_id', $account->id()) | |
| - ->condition('grequest_status', GroupMembershipRequest::REQUEST_PENDING) | |
| - ->count() | |
| - ->execute(); | |
| + if (!$field->isEmpty()) { | |
| + /** @var \Drupal\taxonomy\TermInterface $term */ | |
| + $term = $field->entity; | |
| - if ($request > 0) { | |
| - $variables['requested'] = TRUE; | |
| - $variables['group_operations_url'] = Url::fromRoute('social_group_request.cancel_request', ['group' => $group->id()]); | |
| + if ($term instanceof \Drupal\taxonomy\Entity\Term) { $variables['group_type'] = $term->getName(); | |
| + $variables['group_type_icon'] = $term->field_group_type_icon->value; } | |
| + } | |
| } | |
| - | |
| - $variables['#attached']['library'][] = 'social_group_request/social_group_popup'; | |
| - $variables['#attached']['library'][] = 'social_group_request/social_group_request_popup'; | |
| - $variables['#attached']['library'][] = 'core/drupal.dialog.ajax'; | |
| - $variables['#cache']['tags'][] = 'request-membership:' . $group->id(); | |
| } | |
| /** | |
| @@ -236,7 +253,7 @@ function social_group_request_activity_send_email_notifications_alter(array &$it | |
| /** | |
| * Implements hook_form_alter(). | |
| */ | |
| -function social_group_request_form_alter(array &$form, FormStateInterface $form_state, $form_id) { | |
| +function social_group_request_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void { | |
| $social_group_types = [ | |
| 'open_group', | |
| 'closed_group', | |
| @@ -245,19 +262,21 @@ function social_group_request_form_alter(array &$form, FormStateInterface $form_ | |
| \Drupal::moduleHandler()->alter('social_group_types', $social_group_types); | |
| - $group_membership_add_forms = []; | |
| - foreach ($social_group_types as $social_group_type) { | |
| - $group_membership_add_forms[] = "group_content_{$social_group_type}-group_membership_add_form"; | |
| - } | |
| - | |
| - $route = \Drupal::routeMatch()->getRouteName(); | |
| - if (in_array($form_id, $group_membership_add_forms) && $route === 'grequest.group_request_membership_approve') { | |
| + if ( | |
| + \Drupal::routeMatch()->getRouteName() === 'grequest.group_request_membership_approve' && | |
| + preg_match('/^group_content_(' . implode('|', $social_group_types) . ')-group_membership_add_form$/', $form_id) | |
| + ) { | |
| // Name of user which we're adding to the group. | |
| - $user_name = $form['entity_id']['widget'][0]['target_id']['#default_value']->getDisplayName(); | |
| + $user_name = $form['entity_id']['widget'][0]['target_id']['#default_value'] | |
| + ->getDisplayName(); | |
| + | |
| $form['question'] = [ | |
| '#type' => 'html_tag', | |
| '#tag' => 'p', | |
| - '#value' => t('Are you sure you want to approve the membership request for @name?', ['@name' => $user_name]), | |
| + '#value' => t( | |
| + 'Are you sure you want to approve the membership request for @name?', | |
| + ['@name' => $user_name], | |
| + ), | |
| '#weight' => 1, | |
| ]; | |
| @@ -272,21 +291,35 @@ function social_group_request_form_alter(array &$form, FormStateInterface $form_ | |
| $form['actions']['submit']['#value'] = t('Yes'); | |
| } | |
| + elseif (preg_match( | |
| + '/^group_(' . implode('|', $social_group_types) . ')_(add|edit)_form$/', | |
| + $form_id, | |
| + )) { | |
| + /** @var \Drupal\Core\Entity\EntityFormInterface $form_object */ | |
| + $form_object = $form_state->getFormObject(); | |
| - $group_forms = []; | |
| - foreach ($social_group_types as $social_group_type) { | |
| - $group_forms[] = "group_{$social_group_type}_edit_form"; | |
| - $group_forms[] = "group_{$social_group_type}_add_form"; | |
| - } | |
| + /** @var \Drupal\group\Entity\GroupInterface $group */ | |
| + $group = $form_object->getEntity(); | |
| - if (in_array($form_id, $group_forms)) { | |
| - /** @var \Drupal\group\Entity\GroupTypeInterface $group_type */ | |
| - $group_type = $form_state->getFormObject()->getEntity()->getGroupType(); | |
| + $group_type = $group->getGroupType(); | |
| + $prohibit = FALSE; | |
| - $group_types = ['flexible_group']; | |
| - \Drupal::moduleHandler()->alter('social_group_request', $group_types); | |
| + if ($group_type->hasContentPlugin('group_membership_request')) { | |
| + /** @var \Drupal\social_group\JoinManagerInterface $manager */ | |
| + $manager = \Drupal::service('plugin.manager.social_group.join'); | |
| - if (in_array($group_type->id(), $group_types) || !$group_type->hasContentPlugin('group_membership_request')) { | |
| + /** @var string $bundle */ | |
| + $bundle = $group_type->id(); | |
| + | |
| + if (!$manager->hasMethod($bundle, 'request')) { | |
| + $prohibit = TRUE; | |
| + } | |
| + } | |
| + else { | |
| + $prohibit = TRUE; | |
| + } | |
| + | |
| + if ($prohibit) { | |
| unset($form['allow_request']); | |
| } | |
| } | |
| @@ -367,15 +400,6 @@ function social_group_request_preprocess_page_title(&$variables) { | |
| $variables['#cache']['tags'][] = 'request-membership:' . $group->id(); | |
| } | |
| -/** | |
| - * Allowed join method values. | |
| - */ | |
| -function social_group_request_allowed_join_method_values(FieldStorageConfig $definition, ContentEntityInterface $entity = NULL, $cacheable) { | |
| - $allowed_values = $definition->getSetting('allowed_values'); | |
| - $allowed_values['request'] = t('Request to join'); | |
| - return $allowed_values; | |
| -} | |
| - | |
| /** | |
| * Implements hook_views_pre_view(). | |
| */ | |
| diff --git a/modules/social_features/social_group/modules/social_group_request/src/Plugin/Block/SocialGroupRequestMembershipNotification.php b/modules/social_features/social_group/modules/social_group_request/src/Plugin/Block/SocialGroupRequestMembershipNotification.php | |
| index e2ba82c30..231b0b5e2 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_request/src/Plugin/Block/SocialGroupRequestMembershipNotification.php | |
| +++ b/modules/social_features/social_group/modules/social_group_request/src/Plugin/Block/SocialGroupRequestMembershipNotification.php | |
| @@ -6,14 +6,14 @@ use Drupal\Core\Access\AccessResult; | |
| use Drupal\Core\Block\BlockBase; | |
| use Drupal\Core\Cache\Cache; | |
| use Drupal\Core\Entity\EntityTypeManagerInterface; | |
| -use Drupal\Core\Extension\ModuleHandlerInterface; | |
| use Drupal\Core\Link; | |
| use Drupal\Core\Plugin\ContainerFactoryPluginInterface; | |
| use Drupal\Core\Session\AccountInterface; | |
| use Drupal\Core\StringTranslation\TranslationManager; | |
| use Drupal\Core\Url; | |
| use Drupal\grequest\Plugin\GroupContentEnabler\GroupMembershipRequest; | |
| -use Drupal\social_group\Entity\Group; | |
| +use Drupal\social_group\JoinManagerInterface; | |
| +use Drupal\social_group\SocialGroupInterface; | |
| use Symfony\Component\DependencyInjection\ContainerInterface; | |
| /** | |
| @@ -34,11 +34,9 @@ class SocialGroupRequestMembershipNotification extends BlockBase implements Cont | |
| protected $account; | |
| /** | |
| - * Group entity. | |
| - * | |
| - * @var \Drupal\group\Entity\GroupInterface | |
| + * The group entity object. | |
| */ | |
| - protected $group; | |
| + protected ?SocialGroupInterface $group; | |
| /** | |
| * Entity type manger. | |
| @@ -48,18 +46,9 @@ class SocialGroupRequestMembershipNotification extends BlockBase implements Cont | |
| protected $entityTypeManager; | |
| /** | |
| - * Translation manager. | |
| - * | |
| - * @var \Drupal\Core\StringTranslation\TranslationManager | |
| + * The join manager. | |
| */ | |
| - protected $translation; | |
| - | |
| - /** | |
| - * The module handler. | |
| - * | |
| - * @var \Drupal\Core\Extension\ModuleHandlerInterface | |
| - */ | |
| - protected $moduleHandler; | |
| + private JoinManagerInterface $joinManager; | |
| /** | |
| * Constructs SocialGroupRequestMembershipNotification. | |
| @@ -76,8 +65,8 @@ class SocialGroupRequestMembershipNotification extends BlockBase implements Cont | |
| * The entity type manager. | |
| * @param \Drupal\Core\StringTranslation\TranslationManager $translation | |
| * The translation manager. | |
| - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | |
| - * The module handler. | |
| + * @param \Drupal\social_group\JoinManagerInterface $join_manager | |
| + * The join manager. | |
| */ | |
| public function __construct( | |
| array $configuration, | |
| @@ -86,14 +75,14 @@ class SocialGroupRequestMembershipNotification extends BlockBase implements Cont | |
| AccountInterface $account, | |
| EntityTypeManagerInterface $entity_type_manager, | |
| TranslationManager $translation, | |
| - ModuleHandlerInterface $module_handler | |
| + JoinManagerInterface $join_manager | |
| ) { | |
| parent::__construct($configuration, $plugin_id, $plugin_definition); | |
| $this->account = $account; | |
| $this->group = _social_group_get_current_group(); | |
| $this->entityTypeManager = $entity_type_manager; | |
| - $this->translation = $translation; | |
| - $this->moduleHandler = $module_handler; | |
| + $this->setStringTranslation($translation); | |
| + $this->joinManager = $join_manager; | |
| } | |
| /** | |
| @@ -107,7 +96,7 @@ class SocialGroupRequestMembershipNotification extends BlockBase implements Cont | |
| $container->get('current_user'), | |
| $container->get('entity_type.manager'), | |
| $container->get('string_translation'), | |
| - $container->get('module_handler') | |
| + $container->get('plugin.manager.social_group.join'), | |
| ); | |
| } | |
| @@ -115,40 +104,50 @@ class SocialGroupRequestMembershipNotification extends BlockBase implements Cont | |
| * {@inheritdoc} | |
| */ | |
| public function build() { | |
| - if (!$this->group->getGroupType()->hasContentPlugin('group_membership_request')) { | |
| + if ($this->group === NULL) { | |
| + return []; | |
| + } | |
| + | |
| + $group_type = $this->group->getGroupType(); | |
| + | |
| + if (!$group_type->hasContentPlugin('group_membership_request')) { | |
| return []; | |
| } | |
| - $group_types = ['flexible_group']; | |
| - $this->moduleHandler->alter('social_group_request', $group_types); | |
| + /** @var string $bundle */ | |
| + $bundle = $group_type->id(); | |
| + | |
| + if ( | |
| + $this->joinManager->hasMethod($bundle, 'request') && | |
| + $this->group->hasField('field_group_allowed_join_method') | |
| + ) { | |
| + $join_methods = $this->group->field_group_allowed_join_method->getValue(); | |
| - if (in_array($this->group->getGroupType()->id(), $group_types)) { | |
| - $join_methods = $this->group->get('field_group_allowed_join_method')->getValue(); | |
| - $request_option = in_array('request', array_column($join_methods, 'value'), FALSE); | |
| - if (!$request_option) { | |
| + if (!in_array('request', array_column($join_methods, 'value'))) { | |
| return []; | |
| } | |
| } | |
| else { | |
| - $allow_request = $this->group->get('allow_request'); | |
| + $allow_request = $this->group->allow_request; | |
| + | |
| if ($allow_request->isEmpty() || $allow_request->value == 0) { | |
| return []; | |
| } | |
| } | |
| - $contentTypeConfigId = $this->group | |
| - ->getGroupType() | |
| + $content_type_config_id = $group_type | |
| ->getContentPlugin('group_membership_request') | |
| ->getContentTypeConfigId(); | |
| - $requests = $this->entityTypeManager->getStorage('group_content')->getQuery() | |
| - ->condition('type', $contentTypeConfigId) | |
| + $requests = (int) $this->entityTypeManager->getStorage('group_content') | |
| + ->getQuery() | |
| + ->condition('type', $content_type_config_id) | |
| ->condition('gid', $this->group->id()) | |
| ->condition('grequest_status', GroupMembershipRequest::REQUEST_PENDING) | |
| ->count() | |
| ->execute(); | |
| - if (!$requests) { | |
| + if ($requests === 0) { | |
| return []; | |
| } | |
| @@ -157,8 +156,15 @@ class SocialGroupRequestMembershipNotification extends BlockBase implements Cont | |
| '#tag' => 'div', | |
| '#value' => $this->t('There @link to join this group.', [ | |
| '@link' => Link::fromTextAndUrl( | |
| - $this->translation->formatPlural($requests, 'is (1) new request', 'are (@count) new requests'), | |
| - Url::fromRoute('view.group_pending_members.membership_requests', ['arg_0' => $this->group->id()]) | |
| + $this->getStringTranslation()->formatPlural( | |
| + $requests, | |
| + 'is (1) new request', | |
| + 'are (@count) new requests', | |
| + ), | |
| + Url::fromRoute( | |
| + 'view.group_pending_members.membership_requests', | |
| + ['arg_0' => $this->group->id()], | |
| + ), | |
| )->toString(), | |
| ]), | |
| '#attributes' => [ | |
| @@ -174,38 +180,45 @@ class SocialGroupRequestMembershipNotification extends BlockBase implements Cont | |
| * {@inheritdoc} | |
| */ | |
| public function access(AccountInterface $account, $return_as_object = FALSE) { | |
| - $is_group_page = isset($this->group); | |
| - if ($this->group instanceof Group) { | |
| - $is_group_manager = $this->group->hasPermission('administer members', $account); | |
| - return AccessResult::allowedIf($is_group_page && $is_group_manager); | |
| + if ($this->group === NULL) { | |
| + $access = AccessResult::forbidden(); | |
| + } | |
| + else { | |
| + $access = AccessResult::allowedIf( | |
| + $this->group->hasPermission('administer members', $account), | |
| + ); | |
| } | |
| - return AccessResult::forbidden(); | |
| + return $return_as_object ? $access : $access->isAllowed(); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getCacheContexts() { | |
| - $contexts = parent::getCacheContexts(); | |
| - // Ensure the context keeps track of the URL | |
| - // so we don't see the message on every group. | |
| - $contexts = Cache::mergeContexts($contexts, [ | |
| + // Ensure the context keeps track of the URL, so we don't see the message on | |
| + // every group. | |
| + return Cache::mergeContexts(parent::getCacheContexts(), [ | |
| 'url', | |
| 'user.permissions', | |
| 'route.group', | |
| ]); | |
| - return $contexts; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getCacheTags() { | |
| - return Cache::mergeTags(parent::getCacheTags(), [ | |
| - 'request-membership:' . $this->group->id(), | |
| - 'group:' . $this->group->id(), | |
| - ]); | |
| + $tags = parent::getCacheTags(); | |
| + | |
| + if ($this->group !== NULL) { | |
| + $tags = Cache::mergeTags($tags, [ | |
| + 'request-membership:' . $this->group->id(), | |
| + 'group:' . $this->group->id(), | |
| + ]); | |
| + } | |
| + | |
| + return $tags; | |
| } | |
| } | |
| diff --git a/modules/social_features/social_group/modules/social_group_request/src/Plugin/Join/SocialGroupRequestJoin.php b/modules/social_features/social_group/modules/social_group_request/src/Plugin/Join/SocialGroupRequestJoin.php | |
| new file mode 100644 | |
| index 000000000..f826b19ec | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/modules/social_group_request/src/Plugin/Join/SocialGroupRequestJoin.php | |
| @@ -0,0 +1,160 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\social_group_request\Plugin\Join; | |
| + | |
| +use Drupal\Core\Entity\EntityTypeManagerInterface; | |
| +use Drupal\Core\Link; | |
| +use Drupal\Core\Url; | |
| +use Drupal\ginvite\GroupInvitationLoaderInterface; | |
| +use Drupal\grequest\Plugin\GroupContentEnabler\GroupMembershipRequest; | |
| +use Drupal\social_group\EntityMemberInterface; | |
| +use Drupal\social_group\JoinBase; | |
| +use Symfony\Component\DependencyInjection\ContainerInterface; | |
| + | |
| +/** | |
| + * Provides a join plugin instance for joining after sending a request. | |
| + * | |
| + * @Join( | |
| + * id = "social_group_request_join", | |
| + * entityTypeId = "group", | |
| + * method = "request", | |
| + * weight = 20, | |
| + * ) | |
| + */ | |
| +class SocialGroupRequestJoin extends JoinBase { | |
| + | |
| + /** | |
| + * The group invitation loader. | |
| + */ | |
| + private ?GroupInvitationLoaderInterface $loader = NULL; | |
| + | |
| + /** | |
| + * The entity type manager. | |
| + */ | |
| + private EntityTypeManagerInterface $entityTypeManager; | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public static function create( | |
| + ContainerInterface $container, | |
| + array $configuration, | |
| + $plugin_id, | |
| + $plugin_definition | |
| + ): self { | |
| + /** @var self $instance */ | |
| + $instance = parent::create( | |
| + $container, | |
| + $configuration, | |
| + $plugin_id, | |
| + $plugin_definition, | |
| + ); | |
| + | |
| + if ($container->has($id = 'ginvite.invitation_loader')) { | |
| + /** @var \Drupal\ginvite\GroupInvitationLoaderInterface $loader */ | |
| + $loader = $container->get($id); | |
| + | |
| + $instance->loader = $loader; | |
| + } | |
| + | |
| + $instance->entityTypeManager = $container->get('entity_type.manager'); | |
| + | |
| + return $instance; | |
| + } | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public function actions(EntityMemberInterface $entity, array &$variables): array { | |
| + $items = []; | |
| + | |
| + /** @var \Drupal\social_group\SocialGroupInterface $group */ | |
| + $group = $entity; | |
| + | |
| + $group_type = $group->getGroupType(); | |
| + | |
| + if (!$group_type->hasContentPlugin('group_membership_request')) { | |
| + return $items; | |
| + } | |
| + | |
| + // If user has a pending invite we should skip the request button. | |
| + if ($this->loader !== NULL) { | |
| + $group_invites = $this->loader->loadByProperties([ | |
| + 'gid' => $group->id(), | |
| + 'uid' => $this->currentUser->id(), | |
| + ]); | |
| + | |
| + if ($group_invites !== []) { | |
| + return $items; | |
| + } | |
| + } | |
| + | |
| + $variables['#attached']['library'][] = 'core/drupal.dialog.ajax'; | |
| + $variables['#attached']['library'][] = 'social_group_request/social_group_popup'; | |
| + | |
| + if ($this->currentUser->isAnonymous()) { | |
| + $items[] = [ | |
| + 'label' => $this->t('Request to join'), | |
| + 'url' => Url::fromRoute( | |
| + 'social_group_request.anonymous_request_membership', | |
| + ['group' => $group->id()], | |
| + ), | |
| + 'attributes' => [ | |
| + 'class' => ['btn-accent', 'use-ajax'], | |
| + ], | |
| + ]; | |
| + | |
| + return $items; | |
| + } | |
| + | |
| + if ( | |
| + !$group->hasPermission('request group membership', $this->currentUser) || | |
| + !$group->hasField('allow_request') | |
| + ) { | |
| + return $items; | |
| + } | |
| + | |
| + $types = $group | |
| + ->getGroupType() | |
| + ->getContentPlugin('group_membership_request') | |
| + ->getContentTypeConfigId(); | |
| + | |
| + $count = $this->entityTypeManager->getStorage('group_content') | |
| + ->getQuery() | |
| + ->condition('type', $types) | |
| + ->condition('gid', $group->id()) | |
| + ->condition('entity_id', $this->currentUser->id()) | |
| + ->condition('grequest_status', GroupMembershipRequest::REQUEST_PENDING) | |
| + ->range(0, 1) | |
| + ->count() | |
| + ->execute(); | |
| + | |
| + if ($count > 0) { | |
| + $items[] = $this->t('Request sent'); | |
| + | |
| + $items[] = Link::createFromRoute( | |
| + $this->t('Cancel request'), | |
| + 'social_group_request.cancel_request', | |
| + ['group' => $group->id()], | |
| + ); | |
| + } | |
| + else { | |
| + $items[] = [ | |
| + 'label' => $this->t('Request to join'), | |
| + 'url' => Url::fromRoute( | |
| + 'grequest.request_membership', | |
| + ['group' => $group->id()], | |
| + ), | |
| + 'attributes' => [ | |
| + 'class' => ['btn-accent', 'use-ajax'], | |
| + ], | |
| + ]; | |
| + } | |
| + | |
| + $variables['#attached']['library'][] = 'social_group_request/social_group_request_popup'; | |
| + $variables['#cache']['tags'][] = 'request-membership:' . $group->id(); | |
| + | |
| + return $items; | |
| + } | |
| + | |
| +} | |
| diff --git a/modules/social_features/social_group/modules/social_group_request/src/SocialGroupRequestConfigOverride.php b/modules/social_features/social_group/modules/social_group_request/src/SocialGroupRequestConfigOverride.php | |
| index 0b76035c8..dd00dee57 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_request/src/SocialGroupRequestConfigOverride.php | |
| +++ b/modules/social_features/social_group/modules/social_group_request/src/SocialGroupRequestConfigOverride.php | |
| @@ -124,15 +124,6 @@ class SocialGroupRequestConfigOverride implements ConfigFactoryOverrideInterface | |
| } | |
| } | |
| - $config_name = 'field.storage.group.field_group_allowed_join_method'; | |
| - if (in_array($config_name, $names)) { | |
| - $overrides[$config_name] = [ | |
| - 'settings' => [ | |
| - 'allowed_values_function' => 'social_group_request_allowed_join_method_values', | |
| - ], | |
| - ]; | |
| - } | |
| - | |
| $config_name = 'views.view.group_pending_members'; | |
| if (in_array($config_name, $names)) { | |
| $overrides[$config_name] = [ | |
| diff --git a/modules/social_features/social_group/modules/social_group_secret/social_group_secret.module b/modules/social_features/social_group/modules/social_group_secret/social_group_secret.module | |
| index bf527ea63..fd5e311f6 100644 | |
| --- a/modules/social_features/social_group/modules/social_group_secret/social_group_secret.module | |
| +++ b/modules/social_features/social_group/modules/social_group_secret/social_group_secret.module | |
| @@ -215,3 +215,16 @@ function social_group_secret_form_node_form_alter(&$form, FormStateInterface $fo | |
| } | |
| } | |
| } | |
| + | |
| +/** | |
| + * Implements hook_social_group_join_method_usage(). | |
| + */ | |
| +function social_group_secret_social_group_join_method_usage(): array { | |
| + return [ | |
| + [ | |
| + 'entity_type' => 'group', | |
| + 'bundle' => 'secret_group', | |
| + 'method' => 'added', | |
| + ], | |
| + ]; | |
| +} | |
| diff --git a/modules/social_features/social_group/social_group.api.php b/modules/social_features/social_group/social_group.api.php | |
| index 3e2aeef5a..294a35fd0 100644 | |
| --- a/modules/social_features/social_group/social_group.api.php | |
| +++ b/modules/social_features/social_group/social_group.api.php | |
| @@ -169,6 +169,111 @@ function hook_social_group_group_visibility_description_alter($key, &$descriptio | |
| } | |
| } | |
| +/** | |
| + * Alter the list of join plugin definitions. | |
| + * | |
| + * @param array $info | |
| + * The join plugin definitions to be altered. | |
| + * | |
| + * @see \Drupal\social_group\Annotation\Join | |
| + * @see \Drupal\social_group\JoinManager | |
| + */ | |
| +function hook_social_group_join_info_alter(array &$info) { | |
| + if (isset($info['social_group_request_join'])) { | |
| + unset($info['social_group_request_join']['entityTypeId']); | |
| + } | |
| +} | |
| + | |
| +/** | |
| + * Define join methods. | |
| + * | |
| + * @return array | |
| + * An associative array of join method definitions. The keys are the | |
| + * identifiers. The values are associative arrays that should contain the | |
| + * following elements: | |
| + * - title: The human-readable name of the join method. If this should be | |
| + * translated, create a \Drupal\Core\StringTranslation\TranslatableMarkup | |
| + * object. | |
| + * - description: The description of the join method. The "@entity_type_id" | |
| + * placeholder will be replaced automatically. If this should be translated, | |
| + * create a \Drupal\Core\StringTranslation\TranslatableMarkup object. | |
| + * - icon: The join method unique icon. | |
| + * - weight: Integer weight used for sorting join methods. | |
| + * | |
| + * @see _social_group_allowed_values_callback() | |
| + * @see social_group_allowed_join_method_description() | |
| + * @see social_group_form_alter() | |
| + * | |
| + * @ingroup social_group_api | |
| + */ | |
| +function hook_social_group_join_method_info() { | |
| + return [ | |
| + 'direct' => [ | |
| + 'title' => t('Open to join'), | |
| + 'description' => t('users can join this @entity_type_id without approval.'), | |
| + 'icon' => 'join_open', | |
| + 'weight' => 10, | |
| + ], | |
| + 'added' => [ | |
| + 'title' => t('Invite only'), | |
| + 'description' => t('users can only join this @entity_type_id if they are | |
| +added/invited by @entity_type_id managers.'), | |
| + 'icon' => 'invite', | |
| + 'weight' => 20, | |
| + ], | |
| + ]; | |
| +} | |
| + | |
| +/** | |
| + * Define entity type bundles that support join methods. | |
| + * | |
| + * @return array | |
| + * An array of entity types/bundles definitions that support join methods. The | |
| + * values are associative arrays that should contain the following elements: | |
| + * - entity_type: The entity type ID. | |
| + * - bundle: (optional) The bundle(s). | |
| + * - field: (optional) The field contains a list of supported join methods | |
| + * when the "method" item isn't defined. Otherwise, the field indicates if | |
| + * an entity can use the join method defined in the "method" item. | |
| + * - method: (optional) The join method(s). | |
| + * | |
| + * @see \Drupal\social_group\JoinManager::relations() | |
| + * | |
| + * @ingroup social_group_api | |
| + */ | |
| +function hook_social_group_join_method_usage() { | |
| + return [ | |
| + [ | |
| + 'entity_type' => 'group', | |
| + 'bundle' => 'flexible_group', | |
| + 'field' => 'field_group_allowed_join_method', | |
| + ], | |
| + ]; | |
| +} | |
| + | |
| +/** | |
| + * Alter the list of relations between entity type bundles and join methods. | |
| + * | |
| + * @param array $items | |
| + * The join method fields to be altered. | |
| + * | |
| + * @see \Drupal\social_group\JoinManager::relations() | |
| + */ | |
| +function hook_social_group_join_method_usage_alter(array &$items) { | |
| + foreach ($items as &$item) { | |
| + if ( | |
| + $item['entity_type'] === 'group' && | |
| + isset($item['bundle']) && | |
| + $item['bundle'] === 'closed_group' && | |
| + !isset($item['field']) | |
| + ) { | |
| + $item['field'] = 'field_group_allowed_join_method'; | |
| + | |
| + break; | |
| + } | |
| + } | |
| +} | |
| + | |
| /** | |
| * Provide a description for a given key from the content visibility #options. | |
| * | |
| diff --git a/modules/social_features/social_group/social_group.module b/modules/social_features/social_group/social_group.module | |
| index 750ee4144..02311f459 100644 | |
| --- a/modules/social_features/social_group/social_group.module | |
| +++ b/modules/social_features/social_group/social_group.module | |
| @@ -8,22 +8,32 @@ | |
| use Drupal\block\Entity\Block; | |
| use Drupal\bootstrap\Bootstrap; | |
| use Drupal\Component\Utility\Html; | |
| +use Drupal\Component\Utility\SortArray; | |
| use Drupal\Core\Access\AccessResult; | |
| use Drupal\Core\Access\AccessResultNeutral; | |
| use Drupal\Core\Ajax\AjaxResponse; | |
| use Drupal\Core\Ajax\HtmlCommand; | |
| +use Drupal\Core\Block\BlockPluginInterface; | |
| +use Drupal\Core\Cache\Cache; | |
| use Drupal\Core\Entity\Display\EntityViewDisplayInterface; | |
| +use Drupal\Core\Entity\EntityFormInterface; | |
| use Drupal\Core\Entity\EntityInterface; | |
| +use Drupal\Core\Entity\EntityTypeInterface; | |
| +use Drupal\Core\Entity\FieldableEntityInterface; | |
| use Drupal\Core\Field\FieldStorageDefinitionInterface; | |
| +use Drupal\Core\Form\FormStateInterface; | |
| +use Drupal\Core\Messenger\MessengerInterface; | |
| use Drupal\Core\Render\Element; | |
| use Drupal\Core\Session\AccountInterface; | |
| -use Drupal\Core\Form\FormStateInterface; | |
| +use Drupal\Core\StringTranslation\TranslatableMarkup; | |
| +use Drupal\Core\Url; | |
| +use Drupal\group\Entity\GroupContent; | |
| use Drupal\group\Entity\GroupContentInterface; | |
| use Drupal\group\Entity\GroupContentType; | |
| use Drupal\group\Entity\GroupInterface; | |
| -use Drupal\group\Entity\GroupContent; | |
| use Drupal\group\Entity\GroupType; | |
| use Drupal\group\GroupMembership; | |
| +use Drupal\image\Entity\ImageStyle; | |
| use Drupal\menu_link_content\MenuLinkContentInterface; | |
| use Drupal\node\Entity\Node; | |
| use Drupal\node\NodeInterface; | |
| @@ -33,20 +43,14 @@ use Drupal\social_group\Element\SocialGroupEntityAutocomplete; | |
| use Drupal\social_group\Entity\Access\SocialGroupAccessControlHandler; | |
| use Drupal\social_group\Entity\Group; | |
| use Drupal\social_group\Form\SocialGroupAddForm; | |
| -use Drupal\views\ViewExecutable; | |
| +use Drupal\social_group\GroupContentVisibilityUpdate; | |
| +use Drupal\social_group\SocialGroupInterface; | |
| +use Drupal\user\Entity\Role; | |
| +use Drupal\user\Entity\User; | |
| use Drupal\views\Plugin\views\cache\CachePluginBase; | |
| use Drupal\views\Plugin\views\query\QueryPluginBase; | |
| use Drupal\views\Plugin\views\row\EntityRow; | |
| -use Drupal\Core\Url; | |
| -use Drupal\Core\Cache\Cache; | |
| -use Drupal\image\Entity\ImageStyle; | |
| -use Drupal\Core\Block\BlockPluginInterface; | |
| -use Drupal\user\Entity\Role; | |
| -use Drupal\user\Entity\User; | |
| -use Drupal\Core\Entity\EntityTypeInterface; | |
| -use Drupal\Core\StringTranslation\TranslatableMarkup; | |
| -use Drupal\social_group\GroupContentVisibilityUpdate; | |
| -use Drupal\Core\Messenger\MessengerInterface; | |
| +use Drupal\views\ViewExecutable; | |
| use Drupal\views_bulk_operations\ViewsBulkOperationsBatch; | |
| /** | |
| @@ -64,6 +68,15 @@ function social_group_theme() { | |
| 'flag__mute_group_notifications' => [ | |
| 'base hook' => 'flag', | |
| ], | |
| + 'join' => [ | |
| + 'variables' => [ | |
| + 'primary' => NULL, | |
| + 'secondaries' => NULL, | |
| + 'attributes' => [], | |
| + 'entity' => NULL, | |
| + ], | |
| + 'file' => 'social_group.theme.inc', | |
| + ], | |
| ]; | |
| } | |
| @@ -101,9 +114,10 @@ function template_preprocess_group_settings_help(array &$variables) { | |
| * TRUE if a user may edit a existings groups group type. | |
| */ | |
| function social_group_group_type_permission_check() { | |
| - $user = \Drupal::currentUser(); | |
| // Get the Group object from the route. | |
| - $group = _social_group_get_current_group(); | |
| + if (($group = _social_group_get_current_group()) === NULL) { | |
| + return FALSE; | |
| + } | |
| // Check if we have a default visibility, if we don't it must be a custom | |
| // group type, we need to have a visibility in order to update group content. | |
| @@ -113,8 +127,8 @@ function social_group_group_type_permission_check() { | |
| return FALSE; | |
| } | |
| - // Otherwise return true when we are able to edit the current group type. | |
| - return ($group instanceof GroupInterface && $user->hasPermission('edit group types')); | |
| + // Otherwise, return true when we are able to edit the current group type. | |
| + return \Drupal::currentUser()->hasPermission('edit group types'); | |
| } | |
| /** | |
| @@ -212,6 +226,24 @@ function _social_group_get_group_labels() { | |
| } | |
| +/** | |
| + * Implements hook_preprocess(). | |
| + */ | |
| +function social_group_preprocess(array &$variables, string $hook): void { | |
| + if ( | |
| + isset( | |
| + $variables['elements']['#view_mode'], | |
| + $variables['elements']['#' . $hook], | |
| + ) && | |
| + in_array($variables['elements']['#view_mode'], ['hero', 'statistic']) | |
| + ) { | |
| + /** @var \Drupal\social_group\JoinManagerInterface $manager */ | |
| + $manager = \Drupal::service('plugin.manager.social_group.join'); | |
| + | |
| + $manager->preprocess($variables, $hook); | |
| + } | |
| +} | |
| + | |
| /** | |
| * Prepares variables for profile templates. | |
| * | |
| @@ -226,8 +258,10 @@ function _social_group_get_group_labels() { | |
| function social_group_preprocess_group(array &$variables) { | |
| /** @var \Drupal\social_group\GroupStatistics $group_statistics */ | |
| $group_statistics = \Drupal::service('social_group.group_statistics'); | |
| - /** @var \Drupal\group\Entity\GroupInterface $group */ | |
| + | |
| + /** @var \Drupal\social_group\SocialGroupInterface $group */ | |
| $group = $variables['group']; | |
| + | |
| $variables['title'] = $group->label(); | |
| $variables['joined'] = FALSE; | |
| $variables['closed_group'] = FALSE; | |
| @@ -241,68 +275,30 @@ function social_group_preprocess_group(array &$variables) { | |
| $variables['group_type_id'] = $group_type_id; | |
| $variables['group_type'] = $group->getGroupType()->label(); | |
| - $group_types = ['flexible_group']; | |
| - \Drupal::moduleHandler()->alter('social_group_request', $group_types); | |
| - | |
| - if (in_array($group_type_id, $group_types) | |
| - && $group->hasField('field_group_type') | |
| - && !empty($term = $group->get('field_group_type')->entity) | |
| - && \Drupal::config('social_group.settings') | |
| - ->get('social_group_type_required')) { | |
| - $variables['group_type'] = $term->getName(); | |
| - $variables['group_type_icon'] = $term->get('field_group_type_icon')->value; | |
| - } | |
| + /** @var \Drupal\Core\Render\RendererInterface $renderer */ | |
| + $renderer = \Drupal::service('renderer'); | |
| // Render the group settings help, gear icon with popover. | |
| $group_settings_help = _social_group_render_group_settings_hero($group); | |
| - $variables['group_settings_help'] = \Drupal::service('renderer') | |
| - ->renderPlain($group_settings_help); | |
| + $variables['group_settings_help'] = $renderer->renderPlain($group_settings_help); | |
| $account = \Drupal::currentUser(); | |
| - // Prevent access to mute group notifications if a user is not a group member. | |
| - if (!in_array($variables['view_mode'], [ | |
| - 'statistic', | |
| - 'hero', | |
| - ]) || !$group->getMember($account)) { | |
| - unset($variables['content']['flag_mute_group_notifications']); | |
| - } | |
| - // Set joined to true for teaser when current logged in | |
| - // user is member of the group. | |
| - if ($group->getMember($account)) { | |
| - $variables['joined'] = TRUE; | |
| - if ($group->hasPermission('leave group', $account)) { | |
| - $variables['group_operations_url'] = Url::fromRoute('entity.group.leave', ['group' => $group->id()]); | |
| - } | |
| - } | |
| - elseif ($group->hasPermission('join group', $account)) { | |
| - // @todo switch this to get URL from routes correctly. | |
| - $variables['group_operations_url'] = Url::fromRoute('entity.group.join', ['group' => $group->id()]); | |
| + if ( | |
| + in_array($variables['view_mode'], ['statistic', 'hero']) && | |
| + $group->hasMember($account) | |
| + ) { | |
| + if ($account->isAuthenticated()) { | |
| + $content = $renderer->render($variables['content']['flag_mute_group_notifications']); | |
| - if ( | |
| - in_array($group_type_id, $group_types) && | |
| - !social_group_flexible_group_can_join_directly($group) | |
| - ) { | |
| - $variables['group_operations_url'] = Url::fromRoute('entity.group.join', ['group' => $group->id()]); | |
| - $variables['closed_group'] = TRUE; | |
| - $variables['cta'] = t('Invitation only'); | |
| + if ((string) $content) { | |
| + $variables['join']['#secondaries'][] = $content; | |
| + } | |
| } | |
| } | |
| - // If the group type is a closed_group. | |
| - elseif ($group_type_id == 'closed_group' && !$group->hasPermission('manage all groups', $account)) { | |
| - // Users can only be invited. | |
| - $variables['group_operations_url'] = Url::fromRoute('entity.group.join', ['group' => $group->id()]); | |
| - $variables['closed_group'] = TRUE; | |
| - $variables['cta'] = t('Invitation only'); | |
| - } | |
| - // Non logged in users should still see "join" button on a group page. | |
| - if ( | |
| - $account->isAnonymous() && | |
| - (in_array($group_type_id, $group_types) && | |
| - social_group_flexible_group_can_join_directly($group) || | |
| - in_array($group_type_id, ['public_group'])) | |
| - ) { | |
| - $variables['group_operations_url'] = Url::fromRoute('entity.group.join', ['group' => $group->id()]); | |
| + // Prevent access to mute group notifications if a user is not a group member. | |
| + else { | |
| + unset($variables['content']['flag_mute_group_notifications']); | |
| } | |
| // Add the hero styled image. | |
| @@ -653,15 +649,85 @@ function social_group_group_visibility_description($key) { | |
| } | |
| /** | |
| - * Returns a description array for the field_group_allowed_join_method options. | |
| + * Sets dynamic allowed values for the join method field. | |
| + * | |
| + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $definition | |
| + * The field storage definition. | |
| + * @param \Drupal\Core\Entity\FieldableEntityInterface|null $entity | |
| + * (optional) The entity object. | |
| + */ | |
| +function _social_group_allowed_values_callback( | |
| + FieldStorageDefinitionInterface $definition, | |
| + FieldableEntityInterface $entity = NULL | |
| +): array { | |
| + $values = []; | |
| + $hook = 'social_group_join_method_info'; | |
| + | |
| + foreach (\Drupal::moduleHandler()->getImplementations($hook) as $module) { | |
| + /** @var callable $function */ | |
| + $function = $module . '_' . $hook; | |
| + | |
| + foreach ($function() as $id => $item) { | |
| + $values[] = ['id' => $id] + $item; | |
| + } | |
| + } | |
| + | |
| + usort($values, [SortArray::class, 'sortByWeightElement']); | |
| + | |
| + return array_column($values, 'title', 'id'); | |
| +} | |
| + | |
| +/** | |
| + * Implements hook_social_group_join_method_usage(). | |
| + */ | |
| +function social_group_social_group_join_method_usage(): array { | |
| + return [ | |
| + [ | |
| + 'entity_type' => 'group', | |
| + 'bundle' => 'closed_group', | |
| + 'method' => 'added', | |
| + ], | |
| + [ | |
| + 'entity_type' => 'group', | |
| + 'bundle' => ['open_group', 'public_group'], | |
| + 'method' => ['direct', 'added'], | |
| + ], | |
| + ]; | |
| +} | |
| + | |
| +/** | |
| + * Implements hook_social_group_join_method_info(). | |
| + */ | |
| +function social_group_social_group_join_method_info(): array { | |
| + return [ | |
| + 'direct' => [ | |
| + 'title' => t('Open to join'), | |
| + 'description' => t('users can join this @entity_type_id without approval.'), | |
| + 'icon' => 'join_open', | |
| + 'weight' => 10, | |
| + ], | |
| + 'added' => [ | |
| + 'title' => t('Invite only'), | |
| + 'description' => t('users can only join this @entity_type_id if they are | |
| +added/invited by @entity_type_id managers.'), | |
| + 'icon' => 'invite', | |
| + 'weight' => 20, | |
| + ], | |
| + ]; | |
| +} | |
| + | |
| +/** | |
| + * Returns a description for the field_group_allowed_join_method options. | |
| * | |
| * @param string $key | |
| * The join method key. | |
| - * | |
| - * @return string | |
| - * The render array containing the description. | |
| + * @param string $entity_type_id | |
| + * (optional) The entity type ID. Defaults to 'group'. | |
| */ | |
| -function social_group_allowed_join_method_description($key) { | |
| +function social_group_allowed_join_method_description( | |
| + string $key, | |
| + string $entity_type_id = 'group' | |
| +): string { | |
| $description = ''; | |
| // We need it to be specified otherwise we can't build the markup. | |
| @@ -669,37 +735,62 @@ function social_group_allowed_join_method_description($key) { | |
| return $description; | |
| } | |
| - // Add explanatory descriptive text after the icon. | |
| - switch ($key) { | |
| - case 'direct': | |
| - $description = '<p>'; | |
| - $description .= '<p><strong><svg class="icon-small"><use xlink:href="#icon-join_open"></use></svg></strong>'; | |
| - $description .= '<strong>' . t('Open to join')->render() . '</strong>'; | |
| - $description .= ' - ' . t('users can join this group without approval.')->render(); | |
| - $description .= '</p>'; | |
| - break; | |
| + $hook = 'social_group_join_method_info'; | |
| - case 'request': | |
| - $description = '<p>'; | |
| - $description .= '<p><strong><svg class="icon-small"><use xlink:href="#icon-join_close"></use></svg></strong>'; | |
| - $description .= '<strong>' . t('Request to join')->render() . '</strong>'; | |
| - $description .= ' - ' . t('users can "request to join" this group which group managers approve/decline.')->render(); | |
| - $description .= '</p>'; | |
| - break; | |
| + if (!($modules = \Drupal::moduleHandler()->getImplementations($hook))) { | |
| + return $description; | |
| + } | |
| - case 'added': | |
| - $description = '<p>'; | |
| - $description .= '<p><strong><svg class="icon-small"><use xlink:href="#icon-invite"></use></svg></strong>'; | |
| - $description .= '<strong>' . t('Invite only')->render() . '</strong>'; | |
| - $description .= ' - ' . t('users can only join this group if they are added/invited by group managers.')->render(); | |
| - $description .= '</p>'; | |
| - break; | |
| + $found = FALSE; | |
| + | |
| + foreach ($modules as $module) { | |
| + /** @var callable $function */ | |
| + $function = $module . '_' . $hook; | |
| + | |
| + foreach ($function() as $id => $item) { | |
| + if ($id === $key) { | |
| + $found = TRUE; | |
| + break 2; | |
| + } | |
| + } | |
| + } | |
| + | |
| + if (!$found || !isset($item)) { | |
| + return $description; | |
| + } | |
| + | |
| + /** @var \Drupal\Core\StringTranslation\TranslatableMarkup $text */ | |
| + $text = $item['description']; | |
| + | |
| + $definition = \Drupal::entityTypeManager()->getDefinition($entity_type_id); | |
| + | |
| + if ($definition === NULL) { | |
| + return $description; | |
| } | |
| + // @codingStandardsIgnoreStart | |
| + $text = t( | |
| + $text->getUntranslatedString(), | |
| + $text->getArguments() + [ | |
| + '@entity_type_id' => $definition->getSingularLabel(), | |
| + ], | |
| + ); | |
| + // @codingStandardsIgnoreEnd | |
| + | |
| + // Add explanatory descriptive text after the icon. | |
| + $description = '<p>'; | |
| + $description .= '<p><strong><svg class="icon-small"><use xlink:href="#icon-' . $item['icon'] . '"></use></svg></strong>'; | |
| + $description .= '<strong>' . $item['title']->render() . '</strong>'; | |
| + $description .= ' - ' . $text->render(); | |
| + $description .= '</p>'; | |
| + | |
| // Allow modules to provide their own markup for a given key in the | |
| // join method #options array. | |
| - \Drupal::moduleHandler() | |
| - ->alter('social_group_allowed_join_method_description', $key, $description); | |
| + \Drupal::moduleHandler()->alter( | |
| + 'social_group_allowed_join_method_description', | |
| + $key, | |
| + $description, | |
| + ); | |
| return $description; | |
| } | |
| @@ -827,8 +918,7 @@ function social_group_form_alter(&$form, FormStateInterface $form_state, $form_i | |
| // Change the form when adding members directly in groups. | |
| if ( | |
| in_array($form_id, $membership_add_forms, TRUE) && | |
| - \Drupal::routeMatch() | |
| - ->getRouteName() !== 'grequest.group_request_membership_approve' | |
| + \Drupal::routeMatch()->getRouteName() !== 'grequest.group_request_membership_approve' | |
| ) { | |
| // Lets add the new select 2 widget to add members to a group. | |
| $form['entity_id']['widget'][0]['target_id'] = $helper->addMemberFormField(); | |
| @@ -841,6 +931,12 @@ function social_group_form_alter(&$form, FormStateInterface $form_state, $form_i | |
| } | |
| } | |
| + $form_object = $form_state->getFormObject(); | |
| + | |
| + if ($form_object instanceof EntityFormInterface) { | |
| + $entity = $form_object->getEntity(); | |
| + } | |
| + | |
| // Check if form is group content create form. | |
| if (isset($form['#entity_type']) && $form['#entity_type'] === 'node') { | |
| // Add custom validate handler to check if groups field values in node. | |
| @@ -853,17 +949,14 @@ function social_group_form_alter(&$form, FormStateInterface $form_state, $form_i | |
| // We don't want to override the form::save in group. | |
| $form['actions']['submit']['#submit'][] = '_social_group_node_form_submit'; | |
| } | |
| - else { | |
| + elseif (isset($entity)) { | |
| // If node don't belong to any group as a group content plugin | |
| // we don't need group visibility field. | |
| - if (method_exists($form_state->getFormObject(), 'getEntity')) { | |
| - /** @var \Drupal\Core\Entity\EntityInterface $node */ | |
| - $node = $form_state->getFormObject()->getEntity(); | |
| - /** @var \Drupal\group\Plugin\GroupContentEnablerManagerInterface $gc_manager */ | |
| - $gc_manager = \Drupal::service('plugin.manager.group_content_enabler'); | |
| - if (!$gc_manager->getGroupContentTypeIds('group_node:' . $node->bundle())) { | |
| - unset($form['groups']); | |
| - } | |
| + /** @var \Drupal\group\Plugin\GroupContentEnablerManagerInterface $gc_manager */ | |
| + $gc_manager = \Drupal::service('plugin.manager.group_content_enabler'); | |
| + | |
| + if (!$gc_manager->getGroupContentTypeIds('group_node:' . $entity->bundle())) { | |
| + unset($form['groups']); | |
| } | |
| } | |
| } | |
| @@ -884,11 +977,13 @@ function social_group_form_alter(&$form, FormStateInterface $form_state, $form_i | |
| if (in_array($form_id, $group_forms['edit'])) { | |
| $social_group_form = SocialGroupAddForm::create(\Drupal::getContainer()); | |
| $group_type_element = $social_group_form->getGroupTypeElement(TRUE); | |
| + | |
| // Get the current group. | |
| - $group = _social_group_get_current_group(); | |
| - // Set the default value in the form. | |
| - $group_type_element['widget']['#default_value'] = $group->getGroupType() | |
| - ->id(); | |
| + if (($group = _social_group_get_current_group()) !== NULL) { | |
| + // Set the default value in the form. | |
| + $group_type_element['widget']['#default_value'] = $group->getGroupType() | |
| + ->id(); | |
| + } | |
| // If user doesn't have permission to change group types disable it. | |
| // Or if group types can't be edited due to visibility issues. | |
| @@ -923,9 +1018,11 @@ function social_group_form_alter(&$form, FormStateInterface $form_state, $form_i | |
| $form['actions']['submit']['#submit'][] = '_social_group_type_edit_submit'; | |
| } | |
| - if (in_array($form_id, $group_forms['delete'])) { | |
| + if ( | |
| + in_array($form_id, $group_forms['delete']) && | |
| + ($group = _social_group_get_current_group()) !== NULL | |
| + ) { | |
| // Add custom submit handler to delete all content of the group. | |
| - $group = _social_group_get_current_group(); | |
| $form['description']['#markup'] = t('Are you sure you want to delete your group "@group" along with all of the posts, events and topics inside this group? This action cannot be undone.', ['@group' => $group->label()]); | |
| $form['actions']['cancel'] = [ | |
| '#type' => 'submit', | |
| @@ -960,33 +1057,78 @@ function social_group_form_alter(&$form, FormStateInterface $form_state, $form_i | |
| } | |
| } | |
| - $group_types = ['flexible_group']; | |
| - \Drupal::moduleHandler()->alter('social_group_request', $group_types); | |
| + if ( | |
| + isset($entity) && | |
| + $form_object instanceof EntityFormInterface && | |
| + in_array($form_object->getOperation(), ['default', 'add', 'edit']) | |
| + ) { | |
| + /** @var \Drupal\social_group\JoinManagerInterface $manager */ | |
| + $manager = \Drupal::service('plugin.manager.social_group.join'); | |
| + | |
| + $found = FALSE; | |
| + $entity_type_id = $entity->getEntityTypeId(); | |
| + | |
| + $bundle = $entity->getEntityType()->getBundleEntityType() === NULL | |
| + ? NULL : $entity->bundle(); | |
| + | |
| + foreach ($manager->relations() as $data) { | |
| + if ( | |
| + isset($data['field'], $form[$data['field']]) && | |
| + !isset($data['method']) && | |
| + $data['entity_type'] === $entity_type_id && | |
| + ( | |
| + !isset($data['bundle']) && | |
| + $bundle === NULL || | |
| + in_array($bundle, (array) $data['bundle']) | |
| + ) | |
| + ) { | |
| + $found = TRUE; | |
| + break; | |
| + } | |
| + } | |
| - $group_form_ids = []; | |
| - foreach ($group_types as $group_type) { | |
| - $group_form_ids[] = 'group_' . $group_type . '_edit_form'; | |
| - $group_form_ids[] = 'group_' . $group_type . '_add_form'; | |
| - } | |
| + if ($found && isset($data)) { | |
| + $field = &$form[$data['field']]['widget']; | |
| - if (isset($form['field_group_allowed_join_method']) && in_array($form_id, $group_form_ids)) { | |
| - $join_method_default_value = 'added'; | |
| - // Ensure we have a better descriptive label. | |
| - if (array_key_exists('added', $form['field_group_allowed_join_method']['widget']['#options'])) { | |
| - $form['field_group_allowed_join_method']['widget']['#options']['added'] = t('Invite only'); | |
| - } | |
| - if (array_key_exists('direct', $form['field_group_allowed_join_method']['widget']['#options'])) { | |
| - $form['field_group_allowed_join_method']['widget']['#options']['direct'] = t('Open to join'); | |
| - } | |
| - // If directly exists it's becoming the default. | |
| - if (in_array('direct', $form['field_group_allowed_join_method']['widget']['#default_value'])) { | |
| - $join_method_default_value = 'direct'; | |
| - } | |
| - elseif (in_array('request', $form['field_group_allowed_join_method']['widget']['#default_value'])) { | |
| - $join_method_default_value = 'request'; | |
| + if ($field['#type'] === 'checkboxes') { | |
| + $field['#type'] = 'radios'; | |
| + } | |
| + | |
| + if ($entity->isNew()) { | |
| + $hook = 'social_group_join_method_info'; | |
| + $join_methods = []; | |
| + | |
| + foreach (\Drupal::moduleHandler()->getImplementations($hook) as $module) { | |
| + /** @var callable $function */ | |
| + $function = $module . '_' . $hook; | |
| + | |
| + foreach ($function() as $id => $join_method) { | |
| + $join_methods[] = [ | |
| + 'id' => $id, | |
| + 'weight' => $join_method['weight'], | |
| + ]; | |
| + } | |
| + } | |
| + | |
| + usort($join_methods, [SortArray::class, 'sortByWeightElement']); | |
| + | |
| + $join_methods = array_column($join_methods, 'id'); | |
| + $join_method = array_pop($join_methods); | |
| + $join_methods = array_reverse($join_methods); | |
| + | |
| + foreach ($join_methods as $current_join_method) { | |
| + if (in_array($current_join_method, $field['#default_value'])) { | |
| + $default_join_method = $current_join_method; | |
| + break; | |
| + } | |
| + } | |
| + | |
| + $field['#default_value'] = $default_join_method ?? reset($join_method); | |
| + } | |
| + elseif (isset($field['#default_value']) && is_array($field['#default_value'])) { | |
| + $field['#default_value'] = reset($field['#default_value']); | |
| + } | |
| } | |
| - $form['field_group_allowed_join_method']['widget']['#type'] = 'radios'; | |
| - $form['field_group_allowed_join_method']['widget']['#default_value'] = $join_method_default_value; | |
| } | |
| // Exposed Filter block on the all-groups overview. | |
| @@ -1049,10 +1191,31 @@ function _social_group_cross_posting_group_type_validate(array $element, FormSta | |
| /** | |
| * Function to validate entries against group members. | |
| + * | |
| + * @param array $element | |
| + * The element being processed. | |
| + * @param \Drupal\Core\Form\FormStateInterface $form_state | |
| + * The current state of the form. | |
| + * @param array $complete_form | |
| + * The complete form structure. | |
| + * | |
| + * @deprecated in social:11.1.0 and is removed from social:12.0.0. Use | |
| + * \Drupal\social_group\Element\SocialGroupEntityAutocomplete::validateEntityAutocompleteSelect2() | |
| + * instead. | |
| + * | |
| + * @see https://www.drupal.org/node/3254715 | |
| */ | |
| -function _social_group_unique_members($element, &$form_state, $complete_form) { | |
| +function _social_group_unique_members( | |
| + array &$element, | |
| + FormStateInterface $form_state, | |
| + array $complete_form | |
| +): void { | |
| // Call the autocomplete function to make sure enrollees are unique. | |
| - SocialGroupEntityAutocomplete::validateEntityAutocomplete($element, $form_state, $complete_form, TRUE); | |
| + SocialGroupEntityAutocomplete::validateEntityAutocompleteSelect2( | |
| + $element, | |
| + $form_state, | |
| + $complete_form, | |
| + ); | |
| } | |
| /** | |
| @@ -1508,33 +1671,36 @@ function social_group_menu_local_tasks_alter(&$data, $route_name) { | |
| unset($data['tabs'][0]['group.edit_form']); | |
| } | |
| - $user = \Drupal::currentUser(); | |
| + $account = \Drupal::currentUser(); | |
| + | |
| // Get the Group object from the route. | |
| $group = Drupal::routeMatch()->getParameter('group'); | |
| - if ($group instanceof GroupInterface) { | |
| - /** @var \Drupal\group\Entity\GroupTypeInterface $group_type */ | |
| - $group_type = $group->getGroupType()->id(); | |
| + if ( | |
| + $group instanceof SocialGroupInterface && | |
| // Check if it's a closed group. | |
| - if ($group_type === 'closed_group') { | |
| - // And if the user is not user 1. | |
| - if ($user->id() !== 1) { | |
| - if ($user->hasPermission('manage all groups')) { | |
| - return; | |
| - } | |
| - // If the user is not an member of this group. | |
| - if (!$group->getMember($user)) { | |
| - // Disable these local tasks. | |
| - $data['tabs'][0]['group.view'] = []; | |
| - if (!$group->hasPermission('administer members', $user)) { | |
| - $data['tabs'][0]['group.content'] = []; | |
| - } | |
| - $data['tabs'][0]['social_group.events'] = []; | |
| - $data['tabs'][0]['social_group.topics'] = []; | |
| - } | |
| + $group->getGroupType()->id() === 'closed_group' && | |
| + // And if the user is not user 1. | |
| + $account->id() !== 1 | |
| + ) { | |
| + if ($account->hasPermission('manage all groups')) { | |
| + return; | |
| + } | |
| + | |
| + // If the user is not an member of this group. | |
| + if (!$group->getMember($account)) { | |
| + // Disable these local tasks. | |
| + $data['tabs'][0]['group.view'] = []; | |
| + | |
| + if (!$group->hasPermission('administer members', $account)) { | |
| + $data['tabs'][0]['group.content'] = []; | |
| } | |
| + | |
| + $data['tabs'][0]['social_group.events'] = []; | |
| + $data['tabs'][0]['social_group.topics'] = []; | |
| } | |
| } | |
| + | |
| // We don't want to display the "Nodes" tab on groups anymore. Before, we | |
| // just disabled the view on hook update, but sometimes it is back to us, | |
| // this is the reason why we want to hide the tab. Also, we don't want to | |
| @@ -1598,12 +1764,9 @@ function _social_group_get_member_profile(GroupContent $group_content) { | |
| * Gets current Group entity from the route. | |
| * | |
| * @param \Drupal\node\NodeInterface|null $node | |
| - * (optional) The node object or NULL. | |
| - * | |
| - * @return \Drupal\group\Entity\GroupInterface|null | |
| - * Returns the group object. | |
| + * (optional) The node entity object. Defaults to NULL. | |
| */ | |
| -function _social_group_get_current_group($node = NULL) { | |
| +function _social_group_get_current_group($node = NULL): ?SocialGroupInterface { | |
| $cache = &drupal_static(__FUNCTION__, []); | |
| // For the same $node input, within the same request the return is always | |
| @@ -1630,8 +1793,10 @@ function _social_group_get_current_group($node = NULL) { | |
| ->load($group); | |
| } | |
| else { | |
| - $node = is_object($node) ? $node : \Drupal::routeMatch() | |
| - ->getParameter('node'); | |
| + if (!is_object($node)) { | |
| + $node = \Drupal::routeMatch()->getParameter('node'); | |
| + } | |
| + | |
| if (is_object($node)) { | |
| $node_entity = [ | |
| 'target_type' => 'node', | |
| @@ -1653,6 +1818,7 @@ function _social_group_get_current_group($node = NULL) { | |
| $cache[$nid] = $group ?? FALSE; | |
| } | |
| + /** @var \Drupal\social_group\SocialGroupInterface|null $group */ | |
| return $group; | |
| } | |
| @@ -1824,7 +1990,7 @@ function social_group_block_access(Block $block, $operation, AccountInterface $a | |
| if ($account->hasPermission('manage all groups')) { | |
| return AccessResult::neutral(); | |
| } | |
| - elseif (!$group->getMember($user)) { | |
| + elseif (!$group->hasMember($user)) { | |
| // If it is closed and the current user is not an member of this group, | |
| // then it is not allowed to see these blocks. | |
| $forbidden_blocks = [ | |
| @@ -2669,11 +2835,11 @@ function social_group_preprocess_field(&$variables) { | |
| $formatter = $variables['element']['#formatter']; | |
| if (in_array($formatter, ['address_plain', 'address_default'])) { | |
| $entity = $variables['element']['#object']; | |
| - if ($entity && $entity instanceof GroupInterface) { | |
| + if ($entity instanceof SocialGroupInterface) { | |
| $social_group_settings = \Drupal::config('social_group.settings'); | |
| $address_visibility_settings = $social_group_settings->get('address_visibility_settings'); | |
| if (isset($address_visibility_settings['street_code_private']) && !empty($address_visibility_settings['street_code_private'])) { | |
| - if (!$entity->getMember(\Drupal::currentUser())) { | |
| + if (!$entity->hasMember(\Drupal::currentUser())) { | |
| switch ($formatter) { | |
| case 'address_plain': | |
| if (isset($variables['items'][0]['content']['#address_line1'])) { | |
| @@ -2777,29 +2943,113 @@ function social_group_render_tooltip($field_name, TranslatableMarkup $data_title | |
| ], | |
| ]; | |
| - $variables['popover'] = $build; | |
| - | |
| return $build; | |
| } | |
| /** | |
| - * Implements template_preprocess_form_element(). | |
| + * Implements hook_preprocess_HOOK(). | |
| */ | |
| -function social_group_preprocess_fieldset(&$variables) { | |
| +function social_group_preprocess_fieldset(array &$variables): void { | |
| // 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']) && $element['#field_name'] === 'field_content_visibility') { | |
| + | |
| + if (empty($element['#field_name'])) { | |
| + return; | |
| + } | |
| + | |
| + if ($element['#field_name'] === 'field_content_visibility') { | |
| $description = ''; | |
| - foreach ($element['#options'] as $key => $label) { | |
| + | |
| + /** @var string $key */ | |
| + foreach (array_keys($element['#options']) as $key) { | |
| $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_content_visibility', t('Visibility'), $description); | |
| + $variables['popover'] = social_group_render_tooltip( | |
| + 'field_content_visibility', | |
| + t('Visibility'), | |
| + $description, | |
| + ); | |
| + | |
| + return; | |
| + } | |
| + | |
| + $parameters = \Drupal::routeMatch()->getParameters(); | |
| + | |
| + if ($parameters->has('entity_type_id')) { | |
| + $entity_type_id = $parameters->get('entity_type_id'); | |
| + | |
| + if ($parameters->has('bundle_parameter')) { | |
| + /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $type */ | |
| + $type = $parameters->get($parameters->get('bundle_parameter')); | |
| + | |
| + $bundle = $type->id(); | |
| + } | |
| + } | |
| + else { | |
| + $found = FALSE; | |
| + | |
| + foreach ($parameters->all() as $parameter) { | |
| + if ($parameter instanceof EntityInterface) { | |
| + $entity_type_id = $parameter->getEntityTypeId(); | |
| + | |
| + if ($parameter->getEntityType()->getBundleEntityType() !== NULL) { | |
| + $bundle = $parameter->bundle(); | |
| + } | |
| + | |
| + $found = TRUE; | |
| + break; | |
| + } | |
| + } | |
| + | |
| + if (!$found || !isset($entity_type_id)) { | |
| + return; | |
| + } | |
| + } | |
| + | |
| + /** @var \Drupal\social_group\JoinManagerInterface $manager */ | |
| + $manager = \Drupal::service('plugin.manager.social_group.join'); | |
| + | |
| + $found = FALSE; | |
| + | |
| + foreach ($manager->relations() as $data) { | |
| + if ( | |
| + !isset($data['method']) && | |
| + $data['entity_type'] === $entity_type_id && | |
| + isset($bundle) === isset($data['bundle']) && | |
| + (!isset($bundle) || $data['bundle'] === $bundle) && | |
| + $data['field'] === $element['#field_name'] | |
| + ) { | |
| + $found = TRUE; | |
| + break; | |
| + } | |
| + } | |
| + | |
| + if (!$found || !isset($data)) { | |
| + return; | |
| } | |
| + | |
| + $description = ''; | |
| + | |
| + /** @var string $key */ | |
| + foreach (array_keys($element['#options']) as $key) { | |
| + $description .= social_group_allowed_join_method_description( | |
| + $key, | |
| + $entity_type_id, | |
| + ); | |
| + } | |
| + | |
| + // 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( | |
| + $data['field'], | |
| + t('Join methods'), | |
| + $description, | |
| + ); | |
| } | |
| /** | |
| diff --git a/modules/social_features/social_group/social_group.services.yml b/modules/social_features/social_group/social_group.services.yml | |
| index e2490097b..620ac2947 100644 | |
| --- a/modules/social_features/social_group/social_group.services.yml | |
| +++ b/modules/social_features/social_group/social_group.services.yml | |
| @@ -1,4 +1,8 @@ | |
| services: | |
| + plugin.manager.social_group.join: | |
| + class: Drupal\social_group\JoinManager | |
| + parent: default_plugin_manager | |
| + | |
| social_group.route_subscriber: | |
| class: Drupal\social_group\Routing\RouteSubscriber | |
| tags: | |
| diff --git a/modules/social_features/social_group/social_group.theme.inc b/modules/social_features/social_group/social_group.theme.inc | |
| new file mode 100644 | |
| index 000000000..f556672ec | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/social_group.theme.inc | |
| @@ -0,0 +1,23 @@ | |
| +<?php | |
| + | |
| +/** | |
| + * @file | |
| + * Preprocess functions for the Social Group module. | |
| + */ | |
| + | |
| +use Drupal\Core\Template\Attribute; | |
| + | |
| +/** | |
| + * Prepares variables for join templates. | |
| + * | |
| + * Default template: join.html.twig. | |
| + * | |
| + * @param array $variables | |
| + * An associative array containing: | |
| + * - attributes: An array of HTML attributes to apply to the wrapper. | |
| + * - primary: The primary element. | |
| + * - secondaries: The secondary elements. | |
| + */ | |
| +function template_preprocess_join(array &$variables): void { | |
| + $variables['attributes'] = new Attribute($variables['attributes']); | |
| +} | |
| diff --git a/modules/social_features/social_group/src/Annotation/Join.php b/modules/social_features/social_group/src/Annotation/Join.php | |
| new file mode 100644 | |
| index 000000000..d5d52241b | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/src/Annotation/Join.php | |
| @@ -0,0 +1,36 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\social_group\Annotation; | |
| + | |
| +use Drupal\Component\Annotation\Plugin; | |
| + | |
| +/** | |
| + * Defines a Join annotation object. | |
| + * | |
| + * @ingroup social_group_api | |
| + * | |
| + * @Annotation | |
| + */ | |
| +class Join extends Plugin { | |
| + | |
| + /** | |
| + * The plugin ID. | |
| + */ | |
| + public string $id; | |
| + | |
| + /** | |
| + * The entity type ID. | |
| + */ | |
| + public ?string $entityTypeId = NULL; | |
| + | |
| + /** | |
| + * The join method. | |
| + */ | |
| + public ?string $method = NULL; | |
| + | |
| + /** | |
| + * The weight. | |
| + */ | |
| + public int $weight = 0; | |
| + | |
| +} | |
| diff --git a/modules/social_features/social_group/src/Element/SocialGroupEntityAutocomplete.php b/modules/social_features/social_group/src/Element/SocialGroupEntityAutocomplete.php | |
| index 2bfdce2b5..266dee54a 100644 | |
| --- a/modules/social_features/social_group/src/Element/SocialGroupEntityAutocomplete.php | |
| +++ b/modules/social_features/social_group/src/Element/SocialGroupEntityAutocomplete.php | |
| @@ -2,15 +2,16 @@ | |
| namespace Drupal\social_group\Element; | |
| +use Drupal\Component\Utility\Tags; | |
| use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface; | |
| use Drupal\Core\Form\FormStateInterface; | |
| use Drupal\group\Entity\GroupContentInterface; | |
| use Drupal\social_core\Entity\Element\EntityAutocomplete; | |
| -use Drupal\Component\Utility\Tags; | |
| -use Drupal\user\Entity\User; | |
| +use Drupal\social_group\EntityMemberInterface; | |
| +use Drupal\user\UserInterface; | |
| /** | |
| - * Provides an Group member autocomplete form element. | |
| + * Provides a Group member autocomplete form element. | |
| * | |
| * The #default_value accepted by this element is either an entity object or an | |
| * array of entity objects. | |
| @@ -21,6 +22,15 @@ class SocialGroupEntityAutocomplete extends EntityAutocomplete { | |
| /** | |
| * Form element validation handler for entity_autocomplete elements. | |
| + * | |
| + * @param array $element | |
| + * The element being processed. | |
| + * @param \Drupal\Core\Form\FormStateInterface $form_state | |
| + * The current state of the form. | |
| + * @param array $complete_form | |
| + * The complete form structure. | |
| + * @param bool $select2 | |
| + * (optional) TRUE if the Select2 widget is used. Defaults to FALSE. | |
| */ | |
| public static function validateEntityAutocomplete( | |
| array &$element, | |
| @@ -28,8 +38,6 @@ class SocialGroupEntityAutocomplete extends EntityAutocomplete { | |
| array &$complete_form, | |
| bool $select2 = FALSE | |
| ): void { | |
| - $duplicated_values = $value = []; | |
| - | |
| /** @var \Drupal\Core\Entity\ContentEntityFormInterface $form_object */ | |
| $form_object = $form_state->getFormObject(); | |
| @@ -38,7 +46,13 @@ class SocialGroupEntityAutocomplete extends EntityAutocomplete { | |
| $entity = $entity->getGroup(); | |
| } | |
| - if (!method_exists($entity, 'getMember')) { | |
| + // We need set "validate_reference" for element to prevent receive notice | |
| + // Undefined index #validate_reference. | |
| + if (!isset($element['#validate_reference'])) { | |
| + $element['#validate_reference'] = FALSE; | |
| + } | |
| + | |
| + if (!$entity instanceof EntityMemberInterface) { | |
| parent::validateEntityAutocomplete($element, $form_state, $complete_form); | |
| return; | |
| @@ -51,21 +65,27 @@ class SocialGroupEntityAutocomplete extends EntityAutocomplete { | |
| $input_values = $element['#value']; | |
| } | |
| + $duplicated_values = $value = []; | |
| + $storage = \Drupal::entityTypeManager()->getStorage('user'); | |
| + | |
| + /** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface $manager */ | |
| + $manager = \Drupal::service('plugin.manager.entity_reference_selection'); | |
| + | |
| foreach ($input_values as $input) { | |
| - $match = static::extractEntityIdFromAutocompleteInput($input); | |
| // If we use the select 2 widget then we already got a nice array. | |
| - if ($select2 === TRUE) { | |
| - $match = $input; | |
| - } | |
| + $match = $select2 ? $input : static::extractEntityIdFromAutocompleteInput($input); | |
| + | |
| if ($match === NULL) { | |
| $options = $element['#selection_settings'] + [ | |
| 'target_type' => $element['#target_type'], | |
| 'handler' => $element['#selection_handler'], | |
| ]; | |
| - /** @var /Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface $handler */ | |
| - $handler = \Drupal::service('plugin.manager.entity_reference_selection')->getInstance($options); | |
| - $autocreate = (bool) $element['#autocreate'] && $handler instanceof SelectionWithAutocreateInterface; | |
| + /** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface $handler */ | |
| + $handler = $manager->getInstance($options); | |
| + | |
| + $autocreate = $element['#autocreate'] && $handler instanceof SelectionWithAutocreateInterface; | |
| + | |
| // Try to get a match from the input string when the user didn't use | |
| // the autocomplete but filled in a value manually. | |
| // Got this from the parent::validateEntityAutocomplete. | |
| @@ -73,21 +93,15 @@ class SocialGroupEntityAutocomplete extends EntityAutocomplete { | |
| } | |
| if ($match !== NULL) { | |
| - $value[$match] = [ | |
| - 'target_id' => $match, | |
| - ]; | |
| + $value[$match] = ['target_id' => $match]; | |
| + $account = $storage->load($match); | |
| - $account = User::load($match); | |
| // User is already a member, add it to an array for the Form element | |
| // to render an error after all checks are gone. | |
| - if ($entity->getMember($account)) { | |
| + if ($account instanceof UserInterface && $entity->hasMember($account)) { | |
| $duplicated_values[] = $account->getDisplayName(); | |
| } | |
| - // We need set "validate_reference" for element to prevent | |
| - // receive notice Undefined index #validate_reference. | |
| - if (!isset($element['#validate_reference'])) { | |
| - $element['#validate_reference'] = FALSE; | |
| - } | |
| + | |
| // Validate input for every single user. This way we make sure that | |
| // The element validates one, or more users added in the autocomplete. | |
| // This is because Group doesn't allow adding multiple users at once, | |
| @@ -99,21 +113,20 @@ class SocialGroupEntityAutocomplete extends EntityAutocomplete { | |
| // If we have duplicates, provide an error message. | |
| if (!empty($duplicated_values)) { | |
| - $usernames = implode(', ', $duplicated_values); | |
| - | |
| $message = \Drupal::translation()->formatPlural(count($duplicated_values), | |
| "@usernames is already member of the @type, you can't add them again", | |
| "@usernames are already members of the @type, you can't add them again", | |
| [ | |
| - '@usernames' => $usernames, | |
| + '@usernames' => implode(', ', $duplicated_values), | |
| '@type' => $entity->getEntityType()->getSingularLabel(), | |
| - ] | |
| + ], | |
| ); | |
| // We have to kick in a form set error here, or else the | |
| // GroupContentCardinalityValidator will kick in and show a faulty | |
| // error message. Alter this later when Group supports multiple members. | |
| $form_state->setError($element, $message); | |
| + | |
| return; | |
| } | |
| @@ -122,10 +135,29 @@ class SocialGroupEntityAutocomplete extends EntityAutocomplete { | |
| // don't use it to perform the action, but we should mimic the behaviour | |
| // as it would be without Select2. | |
| if ($select2 === TRUE) { | |
| - $form_state->setValue($element['#parents'], $match); | |
| + $form_state->setValue($element['#parents'], $match ?? NULL); | |
| } | |
| + | |
| $form_state->setValue('entity_id_new', $value); | |
| } | |
| } | |
| + /** | |
| + * Form element validation handler for entity_autocomplete elements. | |
| + * | |
| + * @param array $element | |
| + * The element being processed. | |
| + * @param \Drupal\Core\Form\FormStateInterface $form_state | |
| + * The current state of the form. | |
| + * @param array $complete_form | |
| + * The complete form structure. | |
| + */ | |
| + public static function validateEntityAutocompleteSelect2( | |
| + array &$element, | |
| + FormStateInterface $form_state, | |
| + array &$complete_form | |
| + ): void { | |
| + static::validateEntityAutocomplete($element, $form_state, $complete_form, TRUE); | |
| + } | |
| + | |
| } | |
| diff --git a/modules/social_features/social_group/src/Entity/Group.php b/modules/social_features/social_group/src/Entity/Group.php | |
| index e16d1cb3e..406dbfe09 100644 | |
| --- a/modules/social_features/social_group/src/Entity/Group.php | |
| +++ b/modules/social_features/social_group/src/Entity/Group.php | |
| @@ -2,13 +2,23 @@ | |
| namespace Drupal\social_group\Entity; | |
| -use Drupal\social_core\EntityUrlLanguageTrait; | |
| +use Drupal\Core\Session\AccountInterface; | |
| use Drupal\group\Entity\Group as GroupBase; | |
| +use Drupal\social_core\EntityUrlLanguageTrait; | |
| +use Drupal\social_group\SocialGroupInterface; | |
| /** | |
| - * Provides a Node entity that has links that work with different languages. | |
| + * Provides a Group entity that has links that work with different languages. | |
| */ | |
| -class Group extends GroupBase { | |
| +class Group extends GroupBase implements SocialGroupInterface { | |
| + | |
| use EntityUrlLanguageTrait; | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public function hasMember(AccountInterface $account): bool { | |
| + return $this->getMember($account) !== FALSE; | |
| + } | |
| + | |
| } | |
| diff --git a/modules/social_features/social_group/src/EntityMemberInterface.php b/modules/social_features/social_group/src/EntityMemberInterface.php | |
| new file mode 100644 | |
| index 000000000..5b84400fa | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/src/EntityMemberInterface.php | |
| @@ -0,0 +1,21 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\social_group; | |
| + | |
| +use Drupal\Core\Entity\ContentEntityInterface; | |
| +use Drupal\Core\Session\AccountInterface; | |
| + | |
| +/** | |
| + * Defines a common interface for entities that support memberships. | |
| + */ | |
| +interface EntityMemberInterface extends ContentEntityInterface { | |
| + | |
| + /** | |
| + * Checks if a user is a member. | |
| + * | |
| + * @param \Drupal\Core\Session\AccountInterface $account | |
| + * The account object. | |
| + */ | |
| + public function hasMember(AccountInterface $account): bool; | |
| + | |
| +} | |
| diff --git a/modules/social_features/social_group/src/EventSubscriber/RedirectSubscriber.php b/modules/social_features/social_group/src/EventSubscriber/RedirectSubscriber.php | |
| index cc08d0e2b..85d2429bf 100644 | |
| --- a/modules/social_features/social_group/src/EventSubscriber/RedirectSubscriber.php | |
| +++ b/modules/social_features/social_group/src/EventSubscriber/RedirectSubscriber.php | |
| @@ -34,7 +34,9 @@ class RedirectSubscriber implements EventSubscriberInterface { | |
| */ | |
| public function checkForRedirection(RequestEvent $event) { | |
| // Check if there is a group object on the current route. | |
| - $group = _social_group_get_current_group(); | |
| + if (($group = _social_group_get_current_group()) === NULL) { | |
| + return; | |
| + } | |
| // Get the current route name for the checks being performed below. | |
| $routeMatch = \Drupal::routeMatch()->getRouteName(); | |
| @@ -55,13 +57,13 @@ class RedirectSubscriber implements EventSubscriberInterface { | |
| 'view.group_topics.page_group_topics', | |
| ]; | |
| // If a group is set, and the type is closed_group. | |
| - if ($group && $group->getGroupType()->id() == 'closed_group') { | |
| + if ($group->getGroupType()->id() === 'closed_group') { | |
| if ($user->id() != 1) { | |
| if ($user->hasPermission('manage all groups')) { | |
| return; | |
| } | |
| // If the user is not an member of this group. | |
| - elseif (!$group->getMember($user) && in_array($routeMatch, $routes)) { | |
| + elseif (!$group->hasMember($user) && in_array($routeMatch, $routes)) { | |
| $event->setResponse(new RedirectResponse(Url::fromRoute('view.group_information.page_group_about', ['group' => $group->id()]) | |
| ->toString())); | |
| } | |
| diff --git a/modules/social_features/social_group/src/JoinBase.php b/modules/social_features/social_group/src/JoinBase.php | |
| new file mode 100644 | |
| index 000000000..5b10779e3 | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/src/JoinBase.php | |
| @@ -0,0 +1,59 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\social_group; | |
| + | |
| +use Drupal\Component\Plugin\PluginBase; | |
| +use Drupal\Core\Session\AccountProxyInterface; | |
| +use Drupal\Core\StringTranslation\StringTranslationTrait; | |
| +use Drupal\Core\StringTranslation\TranslationInterface; | |
| +use Symfony\Component\DependencyInjection\ContainerInterface; | |
| + | |
| +/** | |
| + * Defines a base join implementation. | |
| + * | |
| + * @ingroup social_group_api | |
| + */ | |
| +abstract class JoinBase extends PluginBase implements JoinPluginInterface { | |
| + | |
| + use StringTranslationTrait; | |
| + | |
| + /** | |
| + * The current active user. | |
| + */ | |
| + protected AccountProxyInterface $currentUser; | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public function __construct( | |
| + array $configuration, | |
| + string $plugin_id, | |
| + $plugin_definition, | |
| + TranslationInterface $translation, | |
| + AccountProxyInterface $current_user | |
| + ) { | |
| + parent::__construct($configuration, $plugin_id, $plugin_definition); | |
| + | |
| + $this->setStringTranslation($translation); | |
| + $this->currentUser = $current_user; | |
| + } | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public static function create( | |
| + ContainerInterface $container, | |
| + array $configuration, | |
| + $plugin_id, | |
| + $plugin_definition | |
| + ): self { | |
| + return new static( | |
| + $configuration, | |
| + $plugin_id, | |
| + $plugin_definition, | |
| + $container->get('string_translation'), | |
| + $container->get('current_user'), | |
| + ); | |
| + } | |
| + | |
| +} | |
| diff --git a/modules/social_features/social_group/src/JoinManager.php b/modules/social_features/social_group/src/JoinManager.php | |
| new file mode 100644 | |
| index 000000000..ff0660c72 | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/src/JoinManager.php | |
| @@ -0,0 +1,250 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\social_group; | |
| + | |
| +use Drupal\Component\Utility\SortArray; | |
| +use Drupal\Core\Cache\CacheBackendInterface; | |
| +use Drupal\Core\Extension\ModuleHandlerInterface; | |
| +use Drupal\Core\Plugin\Context\ContextAwarePluginManagerTrait; | |
| +use Drupal\Core\Plugin\DefaultPluginManager; | |
| +use Drupal\Core\Render\RenderableInterface; | |
| +use Drupal\Core\Template\Attribute; | |
| +use Drupal\Core\Url; | |
| +use Drupal\social_group\Annotation\Join; | |
| + | |
| +/** | |
| + * Defines the join manager. | |
| + */ | |
| +class JoinManager extends DefaultPluginManager implements JoinManagerInterface { | |
| + | |
| + use ContextAwarePluginManagerTrait; | |
| + | |
| + private const HOOK_JOIN_METHOD_USAGE = 'social_group_join_method_usage'; | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public function __construct( | |
| + \Traversable $namespaces, | |
| + CacheBackendInterface $cache_backend, | |
| + ModuleHandlerInterface $module_handler | |
| + ) { | |
| + parent::__construct( | |
| + 'Plugin/Join', | |
| + $namespaces, | |
| + $module_handler, | |
| + JoinPluginInterface::class, | |
| + Join::class, | |
| + ); | |
| + | |
| + $this->alterInfo('social_group_join_info'); | |
| + $this->setCacheBackend($cache_backend, 'join_plugins'); | |
| + } | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public function relations(): array { | |
| + $items = $this->moduleHandler->invokeAll(self::HOOK_JOIN_METHOD_USAGE); | |
| + | |
| + $this->moduleHandler->alter(self::HOOK_JOIN_METHOD_USAGE, $items); | |
| + | |
| + if ($this->moduleHandler->moduleExists('social_group_request')) { | |
| + $old_types = []; | |
| + | |
| + foreach ($items as $item) { | |
| + if ($item['entity_type'] === 'group' && isset($item['bundle'])) { | |
| + $old_types = array_merge($old_types, (array) $item['bundle']); | |
| + } | |
| + } | |
| + | |
| + $old_types = $new_types = array_unique($old_types); | |
| + | |
| + $this->moduleHandler->alterDeprecated( | |
| + 'Deprecated in social:11.2.0 and is removed from social:12.0.0. Use hook_social_group_join_method_usage instead. See https://www.drupal.org/node/3254715', | |
| + 'social_group_request', | |
| + $new_types, | |
| + ); | |
| + | |
| + $added_types = $removed_types = []; | |
| + | |
| + foreach (array_unique(array_merge($old_types, $new_types)) as $type) { | |
| + $is_new = in_array($type, $new_types); | |
| + | |
| + if (in_array($type, $old_types) !== $is_new) { | |
| + if ($is_new) { | |
| + $added_types[] = $type; | |
| + } | |
| + else { | |
| + $removed_types[] = $type; | |
| + } | |
| + } | |
| + } | |
| + | |
| + if (!empty($removed_types)) { | |
| + foreach ($items as $item_delta => &$item) { | |
| + if ($item['entity_type'] === 'group' && isset($item['bundle'])) { | |
| + if (is_array($item['bundle'])) { | |
| + foreach ($removed_types as $type) { | |
| + $bundle_delta = array_search($type, $item['bundle']); | |
| + | |
| + if ($bundle_delta !== FALSE) { | |
| + unset($item['bundle'][$bundle_delta]); | |
| + } | |
| + } | |
| + | |
| + if (empty($item['bundle'])) { | |
| + unset($items[$item_delta]); | |
| + } | |
| + } | |
| + elseif (in_array($item['bundle'], $removed_types)) { | |
| + unset($items[$item_delta]); | |
| + } | |
| + } | |
| + } | |
| + } | |
| + | |
| + if (!empty($added_types)) { | |
| + $items[] = [ | |
| + 'entity_type' => 'group', | |
| + 'bundle' => $added_types, | |
| + ]; | |
| + } | |
| + } | |
| + | |
| + return $items; | |
| + } | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public function preprocess(array &$variables, string $hook): void { | |
| + $entity = $variables['elements']['#' . $hook]; | |
| + | |
| + if (!$entity instanceof EntityMemberInterface) { | |
| + return; | |
| + } | |
| + | |
| + $entity_type = $entity->getEntityType(); | |
| + $entity_type_id = $entity->getEntityTypeId(); | |
| + $methods = []; | |
| + | |
| + foreach ($this->relations() as $data) { | |
| + if ( | |
| + $data['entity_type'] === $entity_type_id && | |
| + ( | |
| + !isset($data['bundle']) && | |
| + $entity_type->getBundleEntityType() === NULL || | |
| + in_array($entity->bundle(), (array) $data['bundle']) | |
| + ) | |
| + ) { | |
| + if (isset($data['field'])) { | |
| + if (isset($data['method'])) { | |
| + $field = $entity->get($data['field']); | |
| + | |
| + if (!$field->isEmpty() && !empty($field->getValue()[0]['value'])) { | |
| + $relation_methods = (array) $data['method']; | |
| + } | |
| + else { | |
| + continue; | |
| + } | |
| + } | |
| + else { | |
| + $relation_methods = array_column( | |
| + $entity->get($data['field'])->getValue(), | |
| + 'value', | |
| + ); | |
| + } | |
| + } | |
| + else { | |
| + $relation_methods = (array) $data['method']; | |
| + } | |
| + | |
| + $methods = array_merge($methods, $relation_methods); | |
| + } | |
| + } | |
| + | |
| + if (empty($methods)) { | |
| + return; | |
| + } | |
| + | |
| + $definitions = array_filter( | |
| + $this->getDefinitions(), | |
| + fn (array $definition): bool => | |
| + ( | |
| + !isset($definition['entityTypeId']) || | |
| + $definition['entityTypeId'] === $entity_type_id | |
| + ) && | |
| + ( | |
| + !isset($definition['method']) || | |
| + in_array($definition['method'], $methods) | |
| + ), | |
| + ); | |
| + | |
| + usort($definitions, [SortArray::class, 'sortByWeightElement']); | |
| + | |
| + foreach ($definitions as $definition) { | |
| + /** @var \Drupal\social_group\JoinPluginInterface $plugin */ | |
| + $plugin = $this->createInstance($definition['id']); | |
| + | |
| + $items = array_map(function ($item) { | |
| + if ($item instanceof RenderableInterface) { | |
| + return $item->toRenderable(); | |
| + } | |
| + elseif (is_array($item)) { | |
| + $attributes = $item['attributes'] ?? []; | |
| + | |
| + /** @var \Drupal\Core\Url $url */ | |
| + $url = $item['url'] ?? Url::fromRoute('<none>'); | |
| + | |
| + $attributes['href'] = $url->toString(); | |
| + | |
| + if (!isset($item['title'])) { | |
| + $attributes['title'] = $item['label']; | |
| + } | |
| + | |
| + if (!isset($item['url'])) { | |
| + $attributes['class'][] = 'disabled'; | |
| + } | |
| + | |
| + $item['attributes'] = new Attribute($attributes); | |
| + } | |
| + | |
| + return $item; | |
| + }, $plugin->actions($entity, $variables)); | |
| + | |
| + if ($items) { | |
| + $variables['join'] = [ | |
| + '#theme' => 'join', | |
| + '#primary' => array_shift($items), | |
| + '#secondaries' => $items, | |
| + '#entity' => $entity, | |
| + ]; | |
| + | |
| + break; | |
| + } | |
| + } | |
| + } | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public function hasMethod(string $bundle, string $method): bool { | |
| + foreach ($this->relations() as $relation) { | |
| + if ( | |
| + $relation['entity_type'] === 'group' && | |
| + isset($relation['bundle']) && | |
| + in_array($bundle, (array) $relation['bundle']) && | |
| + ( | |
| + !isset($relation['method']) || | |
| + in_array($method, (array) $relation['method']) | |
| + ) | |
| + ) { | |
| + return TRUE; | |
| + } | |
| + } | |
| + | |
| + return FALSE; | |
| + } | |
| + | |
| +} | |
| diff --git a/modules/social_features/social_group/src/JoinManagerInterface.php b/modules/social_features/social_group/src/JoinManagerInterface.php | |
| new file mode 100644 | |
| index 000000000..f8024e80b | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/src/JoinManagerInterface.php | |
| @@ -0,0 +1,37 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\social_group; | |
| + | |
| +use Drupal\Core\Plugin\Context\ContextAwarePluginManagerInterface; | |
| + | |
| +/** | |
| + * Defines the join manager interface. | |
| + */ | |
| +interface JoinManagerInterface extends ContextAwarePluginManagerInterface { | |
| + | |
| + /** | |
| + * Returns list of entity types and their bundles that support join methods. | |
| + */ | |
| + public function relations(): array; | |
| + | |
| + /** | |
| + * Preprocess theme variables for templates. | |
| + * | |
| + * @param array $variables | |
| + * The variables array (modify in place). | |
| + * @param string $hook | |
| + * The name of the theme hook. | |
| + */ | |
| + public function preprocess(array &$variables, string $hook): void; | |
| + | |
| + /** | |
| + * Check if specific bundle supports selected the join method. | |
| + * | |
| + * @param string $bundle | |
| + * The bundle. | |
| + * @param string $method | |
| + * The join method. | |
| + */ | |
| + public function hasMethod(string $bundle, string $method): bool; | |
| + | |
| +} | |
| diff --git a/modules/social_features/social_group/src/JoinPluginInterface.php b/modules/social_features/social_group/src/JoinPluginInterface.php | |
| new file mode 100644 | |
| index 000000000..7b3c349df | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/src/JoinPluginInterface.php | |
| @@ -0,0 +1,46 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\social_group; | |
| + | |
| +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; | |
| +use Drupal\Core\Session\AccountProxyInterface; | |
| +use Drupal\Core\StringTranslation\TranslationInterface; | |
| + | |
| +/** | |
| + * Provides an interface for join plugins. | |
| + */ | |
| +interface JoinPluginInterface extends ContainerFactoryPluginInterface { | |
| + | |
| + /** | |
| + * JoinBase constructor. | |
| + * | |
| + * @param array $configuration | |
| + * A configuration array containing information about the plugin instance. | |
| + * @param string $plugin_id | |
| + * The plugin_id for the plugin instance. | |
| + * @param mixed $plugin_definition | |
| + * The plugin implementation definition. | |
| + * @param \Drupal\Core\StringTranslation\TranslationInterface $translation | |
| + * The string translation service. | |
| + * @param \Drupal\Core\Session\AccountProxyInterface $current_user | |
| + * The current active user. | |
| + */ | |
| + public function __construct( | |
| + array $configuration, | |
| + string $plugin_id, | |
| + $plugin_definition, | |
| + TranslationInterface $translation, | |
| + AccountProxyInterface $current_user | |
| + ); | |
| + | |
| + /** | |
| + * Gets a list of clickable elements. | |
| + * | |
| + * @param \Drupal\social_group\EntityMemberInterface $entity | |
| + * The membership entity object. | |
| + * @param array $variables | |
| + * The variables. | |
| + */ | |
| + public function actions(EntityMemberInterface $entity, array &$variables): array; | |
| + | |
| +} | |
| diff --git a/modules/social_features/social_group/src/Plugin/Action/AddMembersToGroup.php b/modules/social_features/social_group/src/Plugin/Action/AddMembersToGroup.php | |
| index 66eac960f..9171a714c 100644 | |
| --- a/modules/social_features/social_group/src/Plugin/Action/AddMembersToGroup.php | |
| +++ b/modules/social_features/social_group/src/Plugin/Action/AddMembersToGroup.php | |
| @@ -9,6 +9,7 @@ use Drupal\Core\Plugin\ContainerFactoryPluginInterface; | |
| use Drupal\Core\Plugin\PluginFormInterface; | |
| use Drupal\Core\Session\AccountInterface; | |
| use Drupal\group\Entity\Group; | |
| +use Drupal\social_group\SocialGroupInterface; | |
| use Drupal\views_bulk_operations\Action\ViewsBulkOperationsActionBase; | |
| use Symfony\Component\DependencyInjection\ContainerInterface; | |
| use Drupal\user\Entity\User; | |
| @@ -72,14 +73,11 @@ class AddMembersToGroup extends ViewsBulkOperationsActionBase implements Contain | |
| */ | |
| public function execute($entity = NULL) { | |
| // Load the Group. | |
| - $group = Group::load($this->configuration['groups']); | |
| - | |
| - if (NULL !== $group) { | |
| - // Check if user already is a member. | |
| - $is_member = $group->getMember($entity); | |
| + $group = $this->storage->load($this->configuration['groups']); | |
| + if ($group instanceof SocialGroupInterface) { | |
| // If that is not the case we can add it to the group. | |
| - if (!$is_member) { | |
| + if (!$group->hasMember($entity)) { | |
| $group->addMember($entity); | |
| return $this->t('Amount of users added to group'); | |
| diff --git a/modules/social_features/social_group/src/Plugin/Join/SocialGroupAlreadyJoin.php b/modules/social_features/social_group/src/Plugin/Join/SocialGroupAlreadyJoin.php | |
| new file mode 100644 | |
| index 000000000..a9c196911 | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/src/Plugin/Join/SocialGroupAlreadyJoin.php | |
| @@ -0,0 +1,46 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\social_group\Plugin\Join; | |
| + | |
| +use Drupal\Core\Link; | |
| +use Drupal\social_group\EntityMemberInterface; | |
| +use Drupal\social_group\JoinBase; | |
| + | |
| +/** | |
| + * Provides a join plugin instance for members. | |
| + * | |
| + * @Join( | |
| + * id = "social_group_already_join", | |
| + * ) | |
| + */ | |
| +class SocialGroupAlreadyJoin extends JoinBase { | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public function actions(EntityMemberInterface $entity, array &$variables): array { | |
| + $items = []; | |
| + | |
| + if (!$entity->hasMember($this->currentUser)) { | |
| + return $items; | |
| + } | |
| + | |
| + $items[] = $this->t('Joined', [], ['context' => 'Is a member']); | |
| + | |
| + $entity_type_id = $entity->getEntityTypeId(); | |
| + | |
| + $items[] = Link::createFromRoute( | |
| + $this->t( | |
| + 'Leave @entity_type_id', | |
| + ['@entity_type_id' => $entity->getEntityType()->getSingularLabel()], | |
| + ), | |
| + 'entity.' . $entity_type_id . '.leave', | |
| + [$entity_type_id => $entity->id()], | |
| + ); | |
| + | |
| + $variables['joined'] = TRUE; | |
| + | |
| + return $items; | |
| + } | |
| + | |
| +} | |
| diff --git a/modules/social_features/social_group/src/Plugin/Join/SocialGroupDirectJoin.php b/modules/social_features/social_group/src/Plugin/Join/SocialGroupDirectJoin.php | |
| new file mode 100644 | |
| index 000000000..a97771e3f | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/src/Plugin/Join/SocialGroupDirectJoin.php | |
| @@ -0,0 +1,64 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\social_group\Plugin\Join; | |
| + | |
| +use Drupal\Core\Url; | |
| +use Drupal\social_group\EntityMemberInterface; | |
| +use Drupal\social_group\JoinBase; | |
| + | |
| +/** | |
| + * Provides a join plugin instance for joining directly. | |
| + * | |
| + * @Join( | |
| + * id = "social_group_direct_join", | |
| + * entityTypeId = "group", | |
| + * method = "direct", | |
| + * weight = 10, | |
| + * ) | |
| + */ | |
| +class SocialGroupDirectJoin extends JoinBase { | |
| + | |
| + /** | |
| + * {@inheritdoc} | |
| + */ | |
| + public function actions(EntityMemberInterface $entity, array &$variables): array { | |
| + $items = []; | |
| + | |
| + if (!$this->access($entity)) { | |
| + return $items; | |
| + } | |
| + | |
| + $entity_type_id = $entity->getEntityTypeId(); | |
| + | |
| + $url = Url::fromRoute( | |
| + 'entity.' . $entity_type_id . '.join', | |
| + [$entity_type_id => $entity->id()], | |
| + ); | |
| + | |
| + $items[] = [ | |
| + 'label' => $this->t('Join'), | |
| + 'url' => $url, | |
| + 'attributes' => [ | |
| + 'class' => ['btn-accent'], | |
| + ], | |
| + ]; | |
| + | |
| + $variables['group_operations_url'] = $url; | |
| + | |
| + return $items; | |
| + } | |
| + | |
| + /** | |
| + * Check if a user can join directly. | |
| + * | |
| + * @param \Drupal\social_group\EntityMemberInterface $entity | |
| + * The membership entity object. | |
| + */ | |
| + protected function access(EntityMemberInterface $entity): bool { | |
| + /** @var \Drupal\social_group\SocialGroupInterface $entity */ | |
| + return $entity->hasPermission('join group', $this->currentUser) || | |
| + $this->currentUser->isAnonymous() && | |
| + in_array($entity->bundle(), ['flexible_group', 'public_group']); | |
| + } | |
| + | |
| +} | |
| diff --git a/modules/social_features/social_group/src/Plugin/views/field/SocialGroupViewsBulkOperationsBulkForm.php b/modules/social_features/social_group/src/Plugin/views/field/SocialGroupViewsBulkOperationsBulkForm.php | |
| index e0ec29939..b8f9a8f71 100644 | |
| --- a/modules/social_features/social_group/src/Plugin/views/field/SocialGroupViewsBulkOperationsBulkForm.php | |
| +++ b/modules/social_features/social_group/src/Plugin/views/field/SocialGroupViewsBulkOperationsBulkForm.php | |
| @@ -268,8 +268,10 @@ class SocialGroupViewsBulkOperationsBulkForm extends ViewsBulkOperationsBulkForm | |
| if ($url->getRouteName() === 'views_bulk_operations.execute_configurable') { | |
| $parameters = $url->getRouteParameters(); | |
| - if (empty($parameters['group'])) { | |
| - $group = _social_group_get_current_group(); | |
| + if ( | |
| + empty($parameters['group']) && | |
| + ($group = _social_group_get_current_group()) !== NULL | |
| + ) { | |
| $parameters['group'] = $group->id(); | |
| } | |
| diff --git a/modules/social_features/social_group/src/SocialGroupHelperService.php b/modules/social_features/social_group/src/SocialGroupHelperService.php | |
| index 99bb6c2a9..1681d5864 100644 | |
| --- a/modules/social_features/social_group/src/SocialGroupHelperService.php | |
| +++ b/modules/social_features/social_group/src/SocialGroupHelperService.php | |
| @@ -15,6 +15,7 @@ use Drupal\group\Entity\GroupContent; | |
| use Drupal\group\Entity\GroupContentInterface; | |
| use Drupal\group\Entity\GroupContentType; | |
| use Drupal\group\Entity\GroupInterface; | |
| +use Drupal\social_group\Element\SocialGroupEntityAutocomplete; | |
| use Drupal\social_post\Entity\PostInterface; | |
| /** | |
| @@ -307,7 +308,7 @@ class SocialGroupHelperService implements SocialGroupHelperServiceInterface { | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| - public function addMemberFormField() { | |
| + public function addMemberFormField(): array { | |
| return [ | |
| '#title' => $this->t('Find people by name or email address'), | |
| '#type' => 'select2', | |
| @@ -320,7 +321,12 @@ class SocialGroupHelperService implements SocialGroupHelperServiceInterface { | |
| ], | |
| '#selection_handler' => 'social', | |
| '#target_type' => 'user', | |
| - '#element_validate' => ['_social_group_unique_members'], | |
| + '#element_validate' => [ | |
| + [ | |
| + SocialGroupEntityAutocomplete::class, | |
| + 'validateEntityAutocompleteSelect2', | |
| + ], | |
| + ], | |
| ]; | |
| } | |
| diff --git a/modules/social_features/social_group/src/SocialGroupHelperServiceInterface.php b/modules/social_features/social_group/src/SocialGroupHelperServiceInterface.php | |
| index 87dd057a0..44f28c61d 100644 | |
| --- a/modules/social_features/social_group/src/SocialGroupHelperServiceInterface.php | |
| +++ b/modules/social_features/social_group/src/SocialGroupHelperServiceInterface.php | |
| @@ -108,10 +108,7 @@ interface SocialGroupHelperServiceInterface { | |
| /** | |
| * Provides a field for potential members. | |
| - * | |
| - * @return array | |
| - * The renderable field. | |
| */ | |
| - public function addMemberFormField(); | |
| + public function addMemberFormField(): array; | |
| } | |
| diff --git a/modules/social_features/social_group/src/SocialGroupInterface.php b/modules/social_features/social_group/src/SocialGroupInterface.php | |
| new file mode 100644 | |
| index 000000000..47469b612 | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/src/SocialGroupInterface.php | |
| @@ -0,0 +1,10 @@ | |
| +<?php | |
| + | |
| +namespace Drupal\social_group; | |
| + | |
| +use Drupal\group\Entity\GroupInterface; | |
| + | |
| +/** | |
| + * Provides an interface defining a Group entity. | |
| + */ | |
| +interface SocialGroupInterface extends EntityMemberInterface, GroupInterface {} | |
| diff --git a/modules/social_features/social_group/templates/join.html.twig b/modules/social_features/social_group/templates/join.html.twig | |
| new file mode 100644 | |
| index 000000000..5f61655d3 | |
| --- /dev/null | |
| +++ b/modules/social_features/social_group/templates/join.html.twig | |
| @@ -0,0 +1,47 @@ | |
| +{# | |
| +/** | |
| + * @file | |
| + * Default theme implementation for join actions. | |
| + * | |
| + * Available variables: | |
| + * - attributes: HTML attributes to apply to the wrapper. | |
| + * - primary: The primary element. | |
| + * - secondaries: The secondary elements. | |
| + * | |
| + * @see template_preprocess_join() | |
| + * | |
| + * @ingroup themeable | |
| + */ | |
| +#} | |
| +{% if primary %} | |
| + <div class="hero-footer__cta"> | |
| + <div{{ attributes }}> | |
| + {% if secondaries %} | |
| + {% if primary.label %} | |
| + <a{{ primary.attributes.addClass('form-item', 'btn', 'btn-accent', 'btn-lg', 'btn-raised') }}>{{ primary.label }}</a> | |
| + <button type="button" autocomplete="off" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-accent btn-lg btn-raised dropdown-toggle waves-effect waves-btn waves-light margin-left-m"><span class="caret"></span></button> | |
| + {% else %} | |
| + <button type="button" autocomplete="off" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-accent btn-lg btn-raised dropdown-toggle btn-block">{{ primary }}<span class="caret"></span></button> | |
| + {% endif %} | |
| + <ul class="dropdown-menu dropdown-menu-right"> | |
| + {% for secondary in secondaries %} | |
| + <li>{{ secondary }}</li> | |
| + {% endfor %} | |
| + </ul> | |
| + {% else %} | |
| + <a{{ primary.attributes.addClass('btn', 'btn-block') }}>{{ primary.label }}</a> | |
| + {% endif %} | |
| + </div> | |
| + | |
| + {% if invite_widget %} | |
| + <div class="btn-group"> | |
| + <button type="button" autocomplete="off" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-default btn-block dropdown-toggle"> | |
| + {% trans %}Invite{% endtrans %}<span class="caret"></span> | |
| + </button> | |
| + <div class="dropdown-menu dropdown-menu--invite"> | |
| + {{ invite_widget }} | |
| + </div> | |
| + </div> | |
| + {% endif %} | |
| + </div> | |
| +{% endif %} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment