diff --git a/.travis.yml b/.travis.yml
index f121c28ab..a54414a7e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,6 @@ language: php
sudo: true
php:
- - 5.6
- 7.0
- 7.1
- 7.2
@@ -13,11 +12,18 @@ env:
matrix:
- TEST_SUITE=8.5.x
- TEST_SUITE=8.6.x
+ - TEST_SUITE=8.7.x
- TEST_SUITE=PHP_CodeSniffer
# Only run the coding standards check once.
matrix:
exclude:
+ - php: 5.6
+ env: TEST_SUITE=8.7.x
+ - php: 7.0
+ env: TEST_SUITE=8.7.x
+ - php: 7.1
+ env: TEST_SUITE=8.7.x
- php: 5.6
env: TEST_SUITE=PHP_CodeSniffer
- php: 7.0
@@ -25,7 +31,7 @@ matrix:
- php: 7.1
env: TEST_SUITE=PHP_CodeSniffer
allow_failures:
- - env: TEST_SUITE=8.6.x
+ - env: TEST_SUITE=8.7.x
mysql:
database: og
@@ -44,9 +50,6 @@ before_script:
# Remember the current directory for later use in the Drupal installation.
- MODULE_DIR=$(pwd)
- # Install Composer dependencies for OG.
- - composer install
-
# Navigate out of module directory to prevent blown stack by recursive module
# lookup.
- cd ..
diff --git a/config/install/core.entity_form_display.og_membership.default.default.yml b/config/install/core.entity_form_display.og_membership.default.default.yml
index 08b77f3bf..0332b58f8 100644
--- a/config/install/core.entity_form_display.og_membership.default.default.yml
+++ b/config/install/core.entity_form_display.og_membership.default.default.yml
@@ -18,4 +18,17 @@ content:
rows: 2
placeholder: ''
third_party_settings: { }
+ roles:
+ type: options_buttons
+ weight: 0
+ settings: { }
+ third_party_settings: { }
+ uid:
+ type: og_autocomplete
+ weight: -1
+ settings:
+ match_operator: CONTAINS
+ size: 60
+ placeholder: ''
+ third_party_settings: { }
hidden: { }
diff --git a/config/install/core.entity_view_display.og_membership.default.default.yml b/config/install/core.entity_view_display.og_membership.default.default.yml
index 4d19f8b4e..468f4dd18 100644
--- a/config/install/core.entity_view_display.og_membership.default.default.yml
+++ b/config/install/core.entity_view_display.og_membership.default.default.yml
@@ -14,7 +14,21 @@ content:
og_membership_request:
label: above
type: basic_string
- weight: 0
+ weight: 2
settings: { }
third_party_settings: { }
+ roles:
+ type: entity_reference_label
+ weight: 1
+ label: above
+ settings:
+ link: false
+ third_party_settings: { }
+ uid:
+ type: entity_reference_label
+ weight: 0
+ label: above
+ settings:
+ link: true
+ third_party_settings: { }
hidden: { }
diff --git a/config/optional/views.view.og_members_overview.yml b/config/optional/views.view.og_members_overview.yml
index ad531e3ab..93c434969 100644
--- a/config/optional/views.view.og_members_overview.yml
+++ b/config/optional/views.view.og_members_overview.yml
@@ -421,6 +421,57 @@ display:
entity_type: og_membership
entity_field: roles
plugin_id: field
+ operations:
+ id: operations
+ table: og_membership
+ field: operations
+ relationship: none
+ group_type: group
+ admin_label: ''
+ label: 'Operations links'
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: true
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ destination: true
+ entity_type: og_membership
+ plugin_id: entity_operations
filters: { }
sorts: { }
header: { }
diff --git a/config/schema/og.schema.yml b/config/schema/og.schema.yml
index 4f08e369b..aa919cd2b 100644
--- a/config/schema/og.schema.yml
+++ b/config/schema/og.schema.yml
@@ -193,3 +193,18 @@ condition.plugin.og_group_type:
type: sequence
sequence:
type: string
+
+# Copied and adapted from core.entity.schema.yml
+field.widget.settings.og_autocomplete:
+ type: mapping
+ label: 'OG context based entity reference autocomplete with display format settings'
+ mapping:
+ match_operator:
+ type: string
+ label: 'Autocomplete matching'
+ size:
+ type: integer
+ label: 'Size of textfield'
+ placeholder:
+ type: label
+ label: 'Placeholder'
diff --git a/og.links.action.yml b/og.links.action.yml
new file mode 100644
index 000000000..276999d49
--- /dev/null
+++ b/og.links.action.yml
@@ -0,0 +1,11 @@
+og_membership.type_add:
+ route_name: og_membership.type_add
+ title: 'Add membership type'
+ appears_on:
+ - entity.og_membership_type.collection
+
+og_admin.members.add:
+ route_name: og_admin.members.add
+ title: 'Add member'
+ appears_on:
+ - og_admin.members
diff --git a/og.links.menu.yml b/og.links.menu.yml
new file mode 100644
index 000000000..b91d6607f
--- /dev/null
+++ b/og.links.menu.yml
@@ -0,0 +1,5 @@
+entity.og_membership_type.collection:
+ title: 'Membership types'
+ parent: system.admin_structure
+ description: 'Create and manage fields, forms, and display settings for OG memberships.'
+ route_name: entity.og_membership_type.collection
diff --git a/og.links.task.yml b/og.links.task.yml
index 854c7df83..bedb52d58 100644
--- a/og.links.task.yml
+++ b/og.links.task.yml
@@ -1,2 +1,15 @@
-og.og_admin_routes:
+entity.og_membership.canonical:
+ route_name: entity.og_membership.canonical
+ base_route: entity.og_membership.canonical
+ title: View
+entity.og_membership.edit_form:
+ route_name: entity.og_membership.edit_form
+ base_route: entity.og_membership.canonical
+ title: Edit
+entity.og_membership.delete_form:
+ route_name: entity.og_membership.delete_form
+ base_route: entity.og_membership.canonical
+ title: Delete
+ weight: 10
+og_admin.local_tasks:
deriver: \Drupal\og\Plugin\Derivative\OgLocalTask
diff --git a/og.module b/og.module
index d4bf89737..61e8f2ab8 100755
--- a/og.module
+++ b/og.module
@@ -17,6 +17,7 @@ use Drupal\og\Entity\OgRole;
use Drupal\og\Og;
use Drupal\og\OgGroupAudienceHelperInterface;
use Drupal\og\OgMembershipInterface;
+use Drupal\og\OgMembershipTypeInterface;
use Drupal\og\OgRoleInterface;
use Drupal\system\Entity\Action;
use Drupal\user\EntityOwnerInterface;
@@ -177,6 +178,47 @@ function og_entity_access(EntityInterface $entity, $operation, AccountInterface
return AccessResult::forbidden();
}
+/**
+ * Implements hook_ENTITY_TYPE_access().
+ */
+function og_og_membership_type_access(OgMembershipTypeInterface $entity, $operation, AccountInterface $account) {
+ // Do not allow deleting the default membership type.
+ if ($operation === 'delete' && $entity->id() === OgMembershipInterface::TYPE_DEFAULT) {
+ return AccessResult::forbidden();
+ }
+
+ // If the user has permission to administer all groups, allow access.
+ if ($account->hasPermission('administer group')) {
+ return AccessResult::allowed();
+ }
+
+ return AccessResult::forbidden();
+}
+
+/**
+ * Implements hook_ENTITY_TYPE_access().
+ */
+function og_og_membership_access(OgMembershipInterface $entity, $operation, AccountInterface $account) {
+ $group = $entity->getGroup();
+
+ // If there's a group owner, don't let them leave.
+ if (
+ isset($group_fields['uid'])
+ && $operation === 'delete'
+ && $group_fields['uid']->entity->id() === $entity->getOwner()->id()
+ ) {
+ return AccessResult::forbidden();
+ }
+
+ // Ensure that there's at least one member in the group.
+ if ($operation === 'delete' && \Drupal::service('og.membership_manager')->getGroupMembershipCount($group) === 1) {
+ return AccessResult::forbidden();
+ }
+
+ return \Drupal::service('og.access')
+ ->userAccess($entity->getGroup(), 'manage members');
+}
+
/**
* Implements hook_entity_create_access().
*/
@@ -242,7 +284,7 @@ function og_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle,
$fields = [];
$fields['og_group'] = BaseFieldDefinition::create('og_group')
- ->setLabel(t('OG Group'))
+ ->setLabel('OG Group')
->setComputed(TRUE)
->setTranslatable(FALSE)
->setDefaultValue(TRUE)
diff --git a/og.routing.yml b/og.routing.yml
index 06a78558e..4fdeb8a54 100644
--- a/og.routing.yml
+++ b/og.routing.yml
@@ -1,11 +1,11 @@
# Routes for Organic groups.
og.subscribe:
- path: 'group/{entity_type_id}/{group}/subscribe/{membership_type}'
+ path: 'group/{entity_type_id}/{group}/subscribe/{og_membership_type}'
defaults:
_controller: '\Drupal\og\Controller\SubscriptionController::subscribe'
_title: 'Join Group'
- membership_type: default
+ og_membership_type: default
requirements:
# Only authenticated users can subscribe to group, but we do allow anonymous
# users to reach this route. They will be redirect to login page or be given
@@ -15,8 +15,6 @@ og.subscribe:
parameters:
group:
type: entity:{entity_type_id}
- membership_type:
- type: entity:og_membership_type
og.unsubscribe:
path: 'group/{entity_type_id}/{group}/unsubscribe'
@@ -43,3 +41,125 @@ og.remove_multiple_roles_confirm:
_form: '\Drupal\og\Form\OgRemoveMultipleRolesForm'
requirements:
_custom_access: '\Drupal\og\Form\OgRemoveMultipleRolesForm::access'
+
+og.entity_autocomplete:
+ path: '/group/{entity_type_id}/{group}/autocomplete/{target_type}/{selection_handler}/{selection_settings_key}'
+ defaults:
+ _controller: '\Drupal\og\Controller\OgAutocompleteController:handleAutocomplete'
+ requirements:
+ _access: 'TRUE'
+ options:
+ parameters:
+ group:
+ type: entity:{entity_type_id}
+
+# OG Membership entity routes
+entity.og_membership.add_form:
+ path: 'group/{entity_type_id}/{group}/admin/members/add/{og_membership_type}'
+ defaults:
+ _controller: '\Drupal\og\Controller\OgAdminMembersController::addForm'
+ _title: 'Add member'
+ requirements:
+ _og_membership_add_access: 'TRUE'
+ options:
+ _admin_route: 'TRUE'
+ parameters:
+ group:
+ type: entity:{entity_type_id}
+
+# The canonical route is the same as the edit-form route because we need a
+# canonical route for various functionality to work properly, but a standard
+# entity view for OG memberships tends to feel quite stub-like.
+entity.og_membership.canonical:
+ path: 'group/{entity_type_id}/{group}/admin/members/{og_membership}/edit'
+ defaults:
+ _entity_form: 'og_membership.edit'
+ options:
+ _admin_route: 'TRUE'
+ parameters:
+ group:
+ type: entity:{entity_type_id}
+ requirements:
+ _entity_access: 'og_membership.edit'
+
+entity.og_membership.edit_form:
+ path: 'group/{entity_type_id}/{group}/admin/members/{og_membership}/edit'
+ defaults:
+ _entity_form: 'og_membership.edit'
+ options:
+ _admin_route: 'TRUE'
+ parameters:
+ group:
+ type: entity:{entity_type_id}
+ requirements:
+ _entity_access: 'og_membership.edit'
+
+entity.og_membership.delete_form:
+ path: 'group/{entity_type_id}/{group}/admin/members/{og_membership}/delete'
+ defaults:
+ _entity_form: 'og_membership.delete'
+ options:
+ _admin_route: 'TRUE'
+ parameters:
+ group:
+ type: entity:{entity_type_id}
+ requirements:
+ _entity_access: 'og_membership.delete'
+
+# OG Membership type entity routes
+entity.og_membership_type.collection:
+ path: '/admin/structure/membership-types'
+ defaults:
+ _entity_list: 'og_membership_type'
+ _title: 'Membership types'
+ requirements:
+ _permission: 'administer group'
+
+entity.og_membership_type.edit_form:
+ path: '/admin/structure/membership-types/manage/{og_membership_type}'
+ defaults:
+ _entity_form: 'og_membership_type.edit'
+ requirements:
+ _permission: 'administer group'
+
+entity.og_membership_type.delete_form:
+ path: '/admin/structure/membership-types/manage/{og_membership_type}/delete'
+ defaults:
+ _entity_form: 'og_membership_type.delete'
+ _title: 'Delete'
+ requirements:
+ _permission: 'og_membership_type.delete'
+
+og_membership.type_add:
+ path: '/admin/structure/membership-types/add'
+ defaults:
+ _entity_form: 'og_membership_type.add'
+ _title: 'Add membership type'
+ requirements:
+ _permission: 'administer group'
+
+og_admin.members:
+ path: '/group/{entity_type_id}/{group}/admin/members'
+ defaults:
+ _controller: '\Drupal\og\Controller\OgAdminMembersController::membersList'
+ _title: 'Members'
+ requirements:
+ _permission: 'administer group'
+ options:
+ _admin_route: 'TRUE'
+ parameters:
+ group:
+ type: entity:{entity_type_id}
+
+og_admin.members.add:
+ path: '/group/{entity_type_id}/{group}/admin/members/add'
+ defaults:
+ _controller: '\Drupal\og\Controller\OgAdminMembersController::addPage'
+ _title: 'Add member'
+ requirements:
+ _permission: 'administer group'
+ options:
+ _admin_route: 'TRUE'
+ parameters:
+ group:
+ type: entity:{entity_type_id}
diff --git a/og.services.yml b/og.services.yml
index 34a31b286..c7f5a1736 100644
--- a/og.services.yml
+++ b/og.services.yml
@@ -4,6 +4,11 @@ services:
arguments: ['@entity_type.manager', '@og.access']
tags:
- { name: access_check, applies_to: _og_user_access_group }
+ access_check.og.membership.add:
+ class: Drupal\og\Access\OgMembershipAddAccessCheck
+ arguments: ['@entity_type.manager', '@og.access']
+ tags:
+ - { name: access_check, applies_to: _og_membership_add_access }
cache_context.og_group_context:
class: 'Drupal\og\Cache\Context\OgGroupContextCacheContext'
arguments: ['@og.context']
diff --git a/og_ui/og_ui.module b/og_ui/og_ui.module
index f319c4ed0..f09da9234 100644
--- a/og_ui/og_ui.module
+++ b/og_ui/og_ui.module
@@ -19,10 +19,16 @@ use Drupal\og_ui\BundleFormAlter;
* Implements hook_form_alter().
*/
function og_ui_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
- if ($form_state->getFormObject() instanceof BundleEntityFormBase) {
- (new BundleFormAlter($form_state->getFormObject()->getEntity()))
- ->formAlter($form, $form_state);
+ if (!$form_state->getFormObject() instanceof BundleEntityFormBase) {
+ return;
}
+
+ $entity_type = $form_state->getFormObject()->getEntity();
+ if ($entity_type->getEntityTypeId() === 'og_membership_type') {
+ return;
+ }
+
+ (new BundleFormAlter($entity_type))->formAlter($form, $form_state);
}
/**
diff --git a/og_ui/tests/src/Functional/BundleFormAlterTest.php b/og_ui/tests/src/Functional/BundleFormAlterTest.php
index 8f326fcdf..1427f0cbf 100644
--- a/og_ui/tests/src/Functional/BundleFormAlterTest.php
+++ b/og_ui/tests/src/Functional/BundleFormAlterTest.php
@@ -18,7 +18,16 @@ class BundleFormAlterTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
- public static $modules = ['block_content', 'entity_test', 'node', 'og_ui'];
+ public static $modules = [
+ 'block_content',
+ 'entity_test',
+ 'node',
+ 'og_ui',
+ 'system',
+ 'og',
+ 'options',
+ 'field',
+ ];
/**
* An administrator user.
diff --git a/scripts/travis-ci/run-test.sh b/scripts/travis-ci/run-test.sh
index aafaab4a7..07797a6a9 100755
--- a/scripts/travis-ci/run-test.sh
+++ b/scripts/travis-ci/run-test.sh
@@ -11,7 +11,7 @@ mysql_to_ramdisk() {
sudo service mysql start
}
-TEST_DIRS=($MODULE_DIR/tests $MODULE_DIR/og_ui/tests)
+TEST_DIRS=($DRUPAL_DIR/modules/og/tests $DRUPAL_DIR/modules/og/og_ui/tests)
case "$1" in
PHP_CodeSniffer)
diff --git a/src/Access/GroupCheck.php b/src/Access/GroupCheck.php
index 002797b84..5bb177bac 100644
--- a/src/Access/GroupCheck.php
+++ b/src/Access/GroupCheck.php
@@ -73,18 +73,18 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, OgA
public function access(AccountInterface $user, Route $route, RouteMatchInterface $route_match, $entity_type_id = NULL, $entity_id = NULL) {
$group = NULL;
if (!$entity_type_id) {
- $parameter_name = $route_match->getRouteObject()->getOption('_og_entity_type_id');
+ $entity_type_id = $route_match->getRouteObject()->getOption('_og_entity_type_id');
- if (!$parameter_name) {
+ if (!$entity_type_id) {
throw new \BadMethodCallException('Group definition is missing from the router. Did you define $route->setOption(\'_og_entity_type_id\', $entity_type_id) in your route declaration?');
}
/** @var \Drupal\Core\Entity\EntityInterface $group */
- if (!$group = $route_match->getParameter($parameter_name)) {
+ $group = $route_match->getParameter($entity_type_id);
+ if (!$group) {
return AccessResult::forbidden();
}
- $entity_type_id = $group->getEntityTypeId();
}
// No access if the entity type doesn't exist.
@@ -93,7 +93,7 @@ public function access(AccountInterface $user, Route $route, RouteMatchInterface
}
$entity_storage = $this->entityTypeManager->getStorage($entity_type_id);
- $group = $group ?: $entity_storage->load($entity_id);
+ $group = is_numeric($group) && !is_object($group) && is_null($entity_id) ? $entity_storage->load($group) : $entity_storage->load($entity_id);
// No access if no entity was loaded or it's not a group.
if (!$group || !Og::isGroup($entity_type_id, $group->bundle())) {
diff --git a/src/Access/OgMembershipAddAccessCheck.php b/src/Access/OgMembershipAddAccessCheck.php
new file mode 100644
index 000000000..922543e5b
--- /dev/null
+++ b/src/Access/OgMembershipAddAccessCheck.php
@@ -0,0 +1,77 @@
+entityTypeManager = $entity_type_manager;
+ }
+
+ /**
+ * Checks access to create the entity type and bundle for the given route.
+ *
+ * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+ * The parametrized route.
+ * @param \Drupal\Core\Session\AccountInterface $account
+ * The currently logged in account.
+ * @param \Drupal\Core\Entity\EntityInterface $group
+ * The group entity.
+ * @param \Drupal\og\OgMembershipTypeInterface $og_membership_type
+ * The membership type entity.
+ *
+ * @return \Drupal\Core\Access\AccessResultInterface
+ * The access result.
+ */
+ public function access(RouteMatchInterface $route_match, AccountInterface $account, EntityInterface $group = NULL, OgMembershipTypeInterface $og_membership_type = NULL) {
+ // The $group param will be null if it is from the
+ // Drupal\og\Event\OgAdminRoutesEvent rather than the routing.yml version.
+ if (is_null($group)) {
+ $entity_type_id = $route_match->getRouteObject()
+ ->getOption('_og_entity_type_id');
+ $group = $route_match->getParameter($entity_type_id);
+ }
+
+ if (!Og::isGroup($group->getEntityTypeId(), $group->bundle())) {
+ return AccessResult::forbidden();
+ }
+
+ $membership_type_id = OgMembershipInterface::TYPE_DEFAULT;
+ if (!is_null($og_membership_type)) {
+ $membership_type_id = $og_membership_type->id();
+ }
+
+ $context = ['group' => $group];
+
+ return $this->entityTypeManager
+ ->getAccessControlHandler('og_membership')
+ ->createAccess($membership_type_id, $account, $context, TRUE);
+ }
+
+}
diff --git a/src/Controller/OgAdminMembersController.php b/src/Controller/OgAdminMembersController.php
index 9eb723aea..6018f3834 100644
--- a/src/Controller/OgAdminMembersController.php
+++ b/src/Controller/OgAdminMembersController.php
@@ -3,14 +3,47 @@
namespace Drupal\og\Controller;
use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\og\Entity\OgMembership;
+use Drupal\og\OgMembershipInterface;
+use Drupal\og\OgMembershipTypeInterface;
use Drupal\views\Views;
+use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* OgAdminMembersController class.
*/
class OgAdminMembersController extends ControllerBase {
+ /**
+ * The entity manager.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+ */
+ protected $entityTypeManager;
+
+ /**
+ * Constructs a new EntityController.
+ *
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+ * The entity type manager.
+ */
+ public function __construct(EntityTypeManagerInterface $entity_type_manager) {
+ $this->entityTypeManager = $entity_type_manager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('entity_type.manager')
+ );
+ }
+
/**
* Display list of members that belong to the group.
*
@@ -21,13 +54,78 @@ class OgAdminMembersController extends ControllerBase {
* The members overview View.
*/
public function membersList(RouteMatchInterface $route_match) {
- $parameter_name = $route_match->getRouteObject()->getOption('_og_entity_type_id');
-
- /** @var \Drupal\Core\Entity\EntityInterface $group */
- $group = $route_match->getParameter($parameter_name);
-
+ $group = $route_match->getParameter('group');
$arguments = [$group->getEntityTypeId(), $group->id()];
return Views::getView('og_members_overview')->executeDisplay('default', $arguments);
}
+ /**
+ * Displays add membership links for available membership types.
+ *
+ * Returns default membership type if that's all that exists.
+ *
+ * @return array
+ * A render array for a list of the og membership types that can be added;
+ * however, if there is only one og membership type defined for the site,
+ * the function will return the default add member form.
+ */
+ public function addPage(RouteMatchInterface $route_match) {
+ $group = $route_match->getParameter('group');
+
+ $membership_types = $this->entityTypeManager
+ ->getStorage('og_membership_type')
+ ->loadMultiple();
+
+ if ($membership_types && count($membership_types) == 1) {
+ return $this->addForm($group, $membership_types[OgMembershipInterface::TYPE_DEFAULT]);
+ }
+
+ $build = [
+ '#theme' => 'entity_add_list',
+ '#bundles' => [],
+ ];
+
+ $build['#cache']['tags'] = $this->entityTypeManager
+ ->getDefinition('og_membership_type')
+ ->getListCacheTags();
+
+ $add_link_params = [
+ 'group' => $group->id(),
+ 'entity_type_id' => $group->getEntityType()->id(),
+ ];
+
+ foreach ($membership_types as $membership_type_id => $og_membership_type) {
+ $add_link_params['og_membership_type'] = $membership_type_id;
+ $build['#bundles'][$membership_type_id] = [
+ 'label' => $og_membership_type->label(),
+ 'description' => NULL,
+ 'add_link' => Link::createFromRoute($og_membership_type->label(), 'entity.og_membership.add_form', $add_link_params),
+ ];
+ }
+
+ return $build;
+ }
+
+ /**
+ * Provides the add member submission form.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $group
+ * The group entity.
+ * @param \Drupal\og\OgMembershipTypeInterface $og_membership_type
+ * The membership type entity.
+ *
+ * @return array
+ * The member add form.
+ */
+ public function addForm(EntityInterface $group, OgMembershipTypeInterface $og_membership_type) {
+ /** @var \Drupal\og\Entity\OgMembership $og_membership */
+ $og_membership = OgMembership::create([
+ 'type' => $og_membership_type->id(),
+ 'entity_type' => $group->getEntityType()->id(),
+ 'entity_id' => $group->id(),
+ ]);
+
+ return $this->entityFormBuilder()->getForm($og_membership, 'add');
+ }
+
}
diff --git a/src/Controller/OgAdminRoutesController.php b/src/Controller/OgAdminRoutesController.php
index 071b53f35..4da0f73cf 100644
--- a/src/Controller/OgAdminRoutesController.php
+++ b/src/Controller/OgAdminRoutesController.php
@@ -2,13 +2,10 @@
namespace Drupal\og\Controller;
-use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
use Drupal\Core\Access\AccessManagerInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
-use Drupal\og\Event\OgAdminRoutesEvent;
-use Drupal\og\Event\OgAdminRoutesEventInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -16,13 +13,6 @@
*/
class OgAdminRoutesController extends ControllerBase {
- /**
- * The event dispatcher service.
- *
- * @var \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher
- */
- protected $eventDispatcher;
-
/**
* The access manager service.
*
@@ -33,13 +23,10 @@ class OgAdminRoutesController extends ControllerBase {
/**
* Constructs an OgAdminController object.
*
- * @param \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher $event_dispatcher
- * The event dispatcher service.
* @param \Drupal\Core\Access\AccessManagerInterface $access_manager
* The access manager service.
*/
- public function __construct(ContainerAwareEventDispatcher $event_dispatcher, AccessManagerInterface $access_manager) {
- $this->eventDispatcher = $event_dispatcher;
+ public function __construct(AccessManagerInterface $access_manager) {
$this->accessManager = $access_manager;
}
@@ -48,7 +35,6 @@ public function __construct(ContainerAwareEventDispatcher $event_dispatcher, Acc
*/
public static function create(ContainerInterface $container) {
return new static(
- $container->get('event_dispatcher'),
$container->get('access_manager')
);
}
@@ -63,39 +49,32 @@ public static function create(ContainerInterface $container) {
* List of available admin routes for the current group.
*/
public function overview(RouteMatchInterface $route_match) {
- $parameter_name = $route_match->getRouteObject()->getOption('_og_entity_type_id');
-
/** @var \Drupal\Core\Entity\EntityInterface $group */
- $group = $route_match->getParameter($parameter_name);
-
- $entity_type_id = $group->getEntityTypeId();
-
+ $group = $route_match->getParameter('group');
+ $entity_type_id = NULL;
+ if (!is_object($group)) {
+ $entity_type_id = $route_match->getRouteObject()->getOption('_og_entity_type_id');
+ $group = $route_match->getParameter($entity_type_id);
+ }
// Get list from routes.
$content = [];
- $event = new OgAdminRoutesEvent();
- $event = $this->eventDispatcher->dispatch(OgAdminRoutesEventInterface::EVENT_NAME, $event);
-
- foreach ($event->getRoutes($entity_type_id) as $name => $info) {
- $route_name = "entity.$entity_type_id.og_admin_routes.$name";
- $parameters = [$entity_type_id => $group->id()];
+ $route_name = "og_admin.members";
+ $parameters = ['entity_type_id' => $group->getEntityTypeId(), 'group' => $group->id()];
- // We don't use Url::fromRoute() here for the access check, as it will
- // prevent us from unit testing this method.
- if (!$this->accessManager->checkNamedRoute($route_name, $parameters)) {
- // User doesn't have access to the route.
- continue;
- }
-
- $content[$name]['title'] = $info['title'];
- $content[$name]['description'] = $info['description'];
- $content[$name]['url'] = Url::fromRoute($route_name, $parameters);
- }
-
- if (!$content) {
+ // We don't use Url::fromRoute() here for the access check, as it will
+ // prevent us from unit testing this method.
+ if (!$this->accessManager->checkNamedRoute($route_name, $parameters)) {
+ // User doesn't have access to the route.
return ['#markup' => $this->t('You do not have any administrative items.')];
}
+ $content[] = [
+ 'title' => 'Members',
+ 'description' => 'Manage members',
+ 'url' => Url::fromRoute($route_name, $parameters),
+ ];
+
return [
'og_admin_routes' => [
'#theme' => 'admin_block_content',
diff --git a/src/Controller/OgAutocompleteController.php b/src/Controller/OgAutocompleteController.php
new file mode 100644
index 000000000..7f976976e
--- /dev/null
+++ b/src/Controller/OgAutocompleteController.php
@@ -0,0 +1,113 @@
+matcher = $matcher;
+ $this->keyValue = $key_value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('entity.autocomplete_matcher'),
+ $container->get('keyvalue')->get('entity_autocomplete')
+ );
+ }
+
+ /**
+ * Autocomplete the label of an entity.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * The request object that contains the typed tags.
+ * @param \Drupal\Core\Entity\EntityInterface $group
+ * The group context for this autocomplete.
+ * @param string $target_type
+ * The ID of the target entity type.
+ * @param string $selection_handler
+ * The plugin ID of the entity reference selection handler.
+ * @param string $selection_settings_key
+ * The hashed key of the key/value entry that holds the selection handler
+ * settings.
+ *
+ * @return \Symfony\Component\HttpFoundation\JsonResponse
+ * The matched entity labels as a JSON response.
+ *
+ * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+ * Thrown if the selection settings key is not found in the key/value store
+ * or if it does not match the stored data.
+ */
+ public function handleAutocomplete(Request $request, EntityInterface $group, $target_type, $selection_handler, $selection_settings_key) {
+ $matches = [];
+ // Get the typed string from the URL, if it exists.
+ if ($input = $request->query->get('q')) {
+ $typed_string = Tags::explode($input);
+ $typed_string = Unicode::strtolower(array_pop($typed_string));
+
+ // Selection settings are passed in as a hashed key of a serialized array
+ // stored in the key/value store.
+ $selection_settings = $this->keyValue->get($selection_settings_key, FALSE);
+ if ($selection_settings !== FALSE) {
+ $selection_settings_hash = Crypt::hmacBase64(serialize($selection_settings) . $target_type . $selection_handler, Settings::getHashSalt());
+ if ($selection_settings_hash !== $selection_settings_key) {
+ // Disallow access when the selection settings hash does not match the
+ // passed-in key.
+ throw new AccessDeniedHttpException('Invalid selection settings key.');
+ }
+ }
+ else {
+ // Disallow access when the selection settings key is not found in the
+ // key/value store.
+ throw new AccessDeniedHttpException();
+ }
+
+ $selection_settings['group'] = $group;
+ $matches = $this->matcher->getMatches($target_type, $selection_handler, $selection_settings, $typed_string);
+ }
+
+ return new JsonResponse($matches);
+ }
+
+}
diff --git a/src/Controller/SubscriptionController.php b/src/Controller/SubscriptionController.php
index a320562d9..8c5b904fc 100644
--- a/src/Controller/SubscriptionController.php
+++ b/src/Controller/SubscriptionController.php
@@ -15,6 +15,7 @@
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Drupal\og\Og;
+use Drupal\Core\Messenger\MessengerInterface;
/**
* Controller for OG subscription routes.
@@ -29,14 +30,24 @@ class SubscriptionController extends ControllerBase {
*/
protected $ogAccess;
+ /**
+ * The messenger.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
/**
* Constructs a SubscriptionController object.
*
* @param \Drupal\og\OgAccessInterface $og_access
* The OG access service.
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger.
*/
- public function __construct(OgAccessInterface $og_access) {
+ public function __construct(OgAccessInterface $og_access, MessengerInterface $messenger) {
$this->ogAccess = $og_access;
+ $this->messenger = $messenger;
}
/**
@@ -44,7 +55,8 @@ public function __construct(OgAccessInterface $og_access) {
*/
public static function create(ContainerInterface $container) {
return new static(
- $container->get('og.access')
+ $container->get('og.access'),
+ $container->get('messenger')
);
}
@@ -55,14 +67,14 @@ public static function create(ContainerInterface $container) {
* The entity type of the group entity.
* @param \Drupal\Core\Entity\EntityInterface $group
* The entity ID of the group entity.
- * @param \Drupal\og\OgMembershipTypeInterface $membership_type
+ * @param \Drupal\og\OgMembershipTypeInterface $og_membership_type
* The membership type to be used for creating the membership.
*
* @return mixed
* Redirect user or show access denied if they are not allowed to subscribe,
* otherwise provide a subscribe confirmation form.
*/
- public function subscribe($entity_type_id, EntityInterface $group, OgMembershipTypeInterface $membership_type) {
+ public function subscribe($entity_type_id, EntityInterface $group, OgMembershipTypeInterface $og_membership_type) {
if (!$group instanceof ContentEntityInterface) {
// Not a valid entity.
throw new AccessDeniedHttpException();
@@ -83,12 +95,12 @@ public function subscribe($entity_type_id, EntityInterface $group, OgMembershipT
if ($this->config('user.settings')->get('register') === USER_REGISTER_ADMINISTRATORS_ONLY) {
$params = [':login' => $user_login_url];
- drupal_set_message($this->t('In order to join any group, you must login. After you have successfully done so, you will need to request membership again.', $params));
+ $this->messenger->addMessage($this->t('In order to join any group, you must login. After you have successfully done so, you will need to request membership again.', $params));
}
else {
$user_register_url = Url::fromRoute('user.register', [], $destination)->toString();
$params = [':register' => $user_register_url, ':login' => $user_login_url];
- drupal_set_message($this->t('In order to join any group, you must login or register a new account. After you have successfully done so, you will need to request membership again.', $params));
+ $this->messenger->addMessage($this->t('In order to join any group, you must login or register a new account. After you have successfully done so, you will need to request membership again.', $params));
}
return new RedirectResponse(Url::fromRoute('user.page')->setAbsolute(TRUE)->toString());
@@ -119,7 +131,7 @@ public function subscribe($entity_type_id, EntityInterface $group, OgMembershipT
}
if ($redirect) {
- drupal_set_message($message, 'warning');
+ $this->messenger->addWarning($message);
return new RedirectResponse($group->toUrl()->setAbsolute(TRUE)->toString());
}
@@ -127,7 +139,7 @@ public function subscribe($entity_type_id, EntityInterface $group, OgMembershipT
throw new AccessDeniedHttpException();
}
- $membership = Og::createMembership($group, $user, $membership_type->id());
+ $membership = Og::createMembership($group, $user, $og_membership_type->id());
$form = $this->entityFormBuilder()->getForm($membership, 'subscribe');
return $form;
@@ -164,7 +176,7 @@ public function unsubscribe(ContentEntityInterface $group) {
if ($group instanceof EntityOwnerInterface && $group->getOwnerId() == $user->id()) {
// The user is the manager of the group.
- drupal_set_message($this->t('As the manager of %group, you can not leave the group.', ['%group' => $group->label()]));
+ $this->messenger->addMessage($this->t('As the manager of %group, you can not leave the group.', ['%group' => $group->label()]));
return new RedirectResponse($group->toUrl()
->setAbsolute()
diff --git a/src/Element/OgAutocomplete.php b/src/Element/OgAutocomplete.php
new file mode 100644
index 000000000..9047564b8
--- /dev/null
+++ b/src/Element/OgAutocomplete.php
@@ -0,0 +1,61 @@
+id();
+ }
+
+ // Store the selection settings in the key/value store and pass a hashed key
+ // in the route parameters.
+ $selection_settings = isset($element['#selection_settings']) ? $element['#selection_settings'] : [];
+ $data = serialize($selection_settings) . $element['#target_type'] . $element['#selection_handler'];
+ $selection_settings_key = Crypt::hmacBase64($data, Settings::getHashSalt());
+
+ $key_value_storage = \Drupal::keyValue('entity_autocomplete');
+ if (!$key_value_storage->has($selection_settings_key)) {
+ $key_value_storage->set($selection_settings_key, $selection_settings);
+ }
+
+ $element['#autocomplete_route_name'] = 'og.entity_autocomplete';
+ $element['#autocomplete_route_parameters'] = [
+ 'target_type' => $element['#target_type'],
+ 'selection_handler' => $element['#selection_handler'],
+ 'selection_settings_key' => $selection_settings_key,
+ 'entity_type_id' => $element['#og_group']->getEntityTypeId(),
+ 'group' => $element['#og_group']->id(),
+ ];
+
+ return $element;
+ }
+
+}
diff --git a/src/Entity/OgMembership.php b/src/Entity/OgMembership.php
index c9ccf9788..4d11b2897 100644
--- a/src/Entity/OgMembership.php
+++ b/src/Entity/OgMembership.php
@@ -59,6 +59,7 @@
* fieldable = TRUE,
* bundle_entity_type = "og_membership_type",
* entity_keys = {
+ * "uuid" = "uuid",
* "id" = "id",
* "bundle" = "type",
* },
@@ -66,16 +67,38 @@
* "bundle" = "type",
* },
* handlers = {
+ * "access" = "Drupal\og\OgMembershipAccessControlHandler",
* "views_data" = "Drupal\og\OgMembershipViewsData",
+ * "list_builder" = "Drupal\Core\Entity\EntityListBuilder",
+ * "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
* "form" = {
* "subscribe" = "Drupal\og\Form\GroupSubscribeForm",
* "unsubscribe" = "Drupal\og\Form\GroupUnsubscribeConfirmForm",
+ * "add" = "Drupal\og\Form\OgMembershipForm",
+ * "edit" = "Drupal\og\Form\OgMembershipForm",
+ * "delete" = "Drupal\og\Form\OgMembershipDeleteForm",
* },
- * }
+ * },
+ * links = {
+ * "edit-form" = "/group/{entity_type_id}/{group}/admin/members/{og_membership}/edit",
+ * "delete-form" = "/group/{entity_type_id}/{group}/admin/members/{og_membership}/delete",
+ * "canonical" = "/group/{entity_type_id}/{group}/admin/members/{og_membership}/edit"
+ * },
+ * field_ui_base_route = "entity.og_membership_type.edit_form"
* )
*/
class OgMembership extends ContentEntityBase implements OgMembershipInterface {
+ /**
+ * {@inheritdoc}
+ */
+ protected function urlRouteParameters($rel) {
+ $uri_route_parameters = parent::urlRouteParameters($rel);
+ $uri_route_parameters['entity_type_id'] = $this->getGroupEntityType();
+ $uri_route_parameters['group'] = $this->getGroupId();
+ return $uri_route_parameters;
+ }
+
/**
* {@inheritdoc}
*/
@@ -356,9 +379,23 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setSetting('target_type', 'og_membership_type');
$fields['uid'] = BaseFieldDefinition::create('entity_reference')
- ->setLabel(t('Member User ID'))
+ ->setLabel(t('Username'))
->setDescription(t('The user ID of the member.'))
- ->setSetting('target_type', 'user');
+ ->setSetting('target_type', 'user')
+ ->setSetting('handler', 'og:user')
+ ->setConstraints(['UniqueOgMembership' => []])
+ ->setDisplayOptions('form', [
+ 'type' => 'og_autocomplete',
+ 'weight' => -1,
+ 'settings' => [
+ 'match_operator' => 'CONTAINS',
+ 'size' => 60,
+ 'placeholder' => '',
+ ],
+ ])
+ ->setDisplayConfigurable('view', TRUE)
+ ->setDisplayConfigurable('form', TRUE)
+ ->setRequired(TRUE);
$fields['entity_type'] = BaseFieldDefinition::create('string')
->setLabel(t('Group entity type'))
@@ -372,21 +409,38 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setLabel(t('Group entity ID'))
->setDescription(t('The entity ID of the group.'));
- $fields['state'] = BaseFieldDefinition::create('string')
+ $fields['state'] = BaseFieldDefinition::create('list_string')
->setLabel(t('State'))
->setDescription(t('The user membership state: active, pending, or blocked.'))
- ->setDefaultValue(OgMembershipInterface::STATE_ACTIVE);
+ ->setDefaultValue(OgMembershipInterface::STATE_ACTIVE)
+ ->setSettings([
+ 'allowed_values' => [
+ OgMembershipInterface::STATE_ACTIVE => t('Active'),
+ OgMembershipInterface::STATE_PENDING => t('Pending'),
+ OgMembershipInterface::STATE_BLOCKED => t('Blocked'),
+ ],
+ ])
+ ->setDisplayOptions('form', [
+ 'type' => 'options_buttons',
+ 'weight' => 0,
+ ])
+ ->setDisplayConfigurable('view', TRUE)
+ ->setDisplayConfigurable('form', TRUE)
+ ->setRequired(TRUE);
$fields['roles'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Roles'))
->setDescription(t('The OG roles related to an OG membership entity.'))
->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)
- ->setDisplayOptions('view', [
- 'label' => 'hidden',
- 'type' => 'entity_reference_label',
+ ->setSetting('target_type', 'og_role')
+ ->setSetting('handler', 'og:og_role')
+ ->setConstraints(['ValidOgRole' => []])
+ ->setDisplayOptions('form', [
+ 'type' => 'options_buttons',
'weight' => 0,
])
- ->setSetting('target_type', 'og_role');
+ ->setDisplayConfigurable('view', TRUE)
+ ->setDisplayConfigurable('form', TRUE);
$fields['created'] = BaseFieldDefinition::create('created')
->setLabel(t('Create'))
diff --git a/src/Entity/OgMembershipType.php b/src/Entity/OgMembershipType.php
index 821b29966..acac6fd54 100644
--- a/src/Entity/OgMembershipType.php
+++ b/src/Entity/OgMembershipType.php
@@ -3,6 +3,8 @@
namespace Drupal\og\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\og\OgMembershipInterface;
use Drupal\og\OgMembershipTypeInterface;
/**
@@ -20,11 +22,26 @@
* @ConfigEntityType(
* id = "og_membership_type",
* label = @Translation("OG membership type"),
+ * handlers = {
+ * "access" = "Drupal\Core\Entity\EntityAccessControlHandler",
+ * "form" = {
+ * "add" = "Drupal\og\Form\OgMembershipTypeForm",
+ * "edit" = "Drupal\og\Form\OgMembershipTypeForm",
+ * "delete" = "Drupal\Core\Entity\EntityDeleteForm"
+ * },
+ * "list_builder" = "Drupal\og\OgMembershipTypeListBuilder"
+ * },
+ * admin_permission = "administer group",
* config_prefix = "og_membership_type",
* bundle_of = "og_membership",
* entity_keys = {
* "id" = "type",
* "label" = "name"
+ * },
+ * links = {
+ * "edit-form" = "/admin/structure/membership-types/manage/{membership_type}",
+ * "delete-form" = "/admin/structure/membership-types/manage/{membership_type}/delete",
+ * "collection" = "/admin/structure/membership-types",
* }
* )
*/
@@ -42,4 +59,35 @@ public function id() {
return $this->type;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function save() {
+ $status = parent::save();
+
+ if ($status === SAVED_NEW) {
+ FieldConfig::create([
+ 'field_name' => 'og_membership_request',
+ 'entity_type' => 'og_membership',
+ 'bundle' => $this->id(),
+ 'label' => 'Request Membership',
+ 'description' => 'Explain the motivation for your request to join this group.',
+ 'translatable' => TRUE,
+ 'settings' => [],
+ ])->save();
+ }
+
+ return $status;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function delete() {
+ if ($this->id() === OgMembershipInterface::TYPE_DEFAULT) {
+ throw \Exception("The default OG membership type cannot be deleted.");
+ }
+ parent::delete();
+ }
+
}
diff --git a/src/Event/OgAdminRoutesEvent.php b/src/Event/OgAdminRoutesEvent.php
deleted file mode 100644
index 575ccf90c..000000000
--- a/src/Event/OgAdminRoutesEvent.php
+++ /dev/null
@@ -1,72 +0,0 @@
-routesInfo = $routes_info;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getRoutesInfo() {
- return $this->routesInfo;
- }
-
- /**
- * {@inheritdoc}
- */
- public function getRoutes($entity_type_id) {
- $routes_info = [];
-
- foreach ($this->routesInfo as $name => $route_info) {
-
- $routes_info[$name] = $route_info;
-
- // Add default values.
- $routes_info[$name] += [
- 'description' => '',
-
- 'requirements' => [
- '_og_user_access_group' => 'administer group',
- ],
-
- 'options' => [
- 'parameters' => [
- $entity_type_id => ['type' => 'entity:' . $entity_type_id],
- ],
- // The above parameters doesn't send the entity,
- // so we will have to use the Route matcher to extract it.
- '_og_entity_type_id' => $entity_type_id,
- '_admin_route' => TRUE,
- ],
-
- // Move the title and controller under the "defaults" key.
- 'defaults' => [
- '_controller' => $route_info['controller'],
- '_title' => $route_info['title'],
- ],
- ];
- }
-
- return $routes_info;
- }
-
-}
diff --git a/src/Event/OgAdminRoutesEventInterface.php b/src/Event/OgAdminRoutesEventInterface.php
deleted file mode 100644
index 5ad4bd851..000000000
--- a/src/Event/OgAdminRoutesEventInterface.php
+++ /dev/null
@@ -1,49 +0,0 @@
- [['provideDefaultRoles']],
- OgAdminRoutesEventInterface::EVENT_NAME => [['provideOgAdminRoutes']],
];
}
@@ -342,28 +340,4 @@ protected function generateEntityOperationPermissionList($group_content_entity_t
return $permissions;
}
- /**
- * Provide OG admin routes.
- *
- * @param \Drupal\og\Event\OgAdminRoutesEventInterface $event
- * The OG admin routes event object.
- */
- public function provideOgAdminRoutes(OgAdminRoutesEventInterface $event) {
- $routes_info = $event->getRoutesInfo();
-
- $routes_info['members'] = [
- 'controller' => '\Drupal\og\Controller\OgAdminMembersController::membersList',
- 'title' => 'Members',
- 'description' => 'Manage members',
- 'path' => 'members',
- 'requirements' => [
- '_og_user_access_group' => 'administer group',
- // Views module must be enabled.
- '_module_dependencies' => 'views',
- ],
- ];
-
- $event->setRoutesInfo($routes_info);
- }
-
}
diff --git a/src/Form/GroupSubscribeForm.php b/src/Form/GroupSubscribeForm.php
index d77fc69a4..39a715e4c 100644
--- a/src/Form/GroupSubscribeForm.php
+++ b/src/Form/GroupSubscribeForm.php
@@ -11,6 +11,7 @@
use Drupal\og\OgAccessInterface;
use Drupal\og\OgMembershipInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Messenger\MessengerInterface;
/**
* Provides a form for subscribing to a group.
@@ -30,6 +31,13 @@ class GroupSubscribeForm extends ContentEntityForm {
*/
protected $ogAccess;
+ /**
+ * The messenger.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
/**
* Constructs a GroupSubscribeForm.
*
@@ -41,6 +49,8 @@ class GroupSubscribeForm extends ContentEntityForm {
* The entity type bundle service.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger.
*
* @todo Set the `EntityRepositoryInterface` type hint on the second argument
* once Drupal 8.6.0 is released. It is currently omitted to preserve
@@ -48,9 +58,16 @@ class GroupSubscribeForm extends ContentEntityForm {
*
* @see https://github.com/Gizra/og/issues/397
*/
- public function __construct(OgAccessInterface $og_access, $entity_repository, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) {
+ public function __construct(
+ OgAccessInterface $og_access,
+ EntityRepositoryInterface $entity_repository,
+ EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL,
+ TimeInterface $time = NULL,
+ MessengerInterface $messenger
+ ) {
parent::__construct($entity_repository, $entity_type_bundle_info, $time);
$this->ogAccess = $og_access;
+ $this->messenger = $messenger;
}
/**
@@ -72,7 +89,8 @@ public static function create(ContainerInterface $container) {
$container->get('og.access'),
$container->get($entity_repository_service),
$container->get('entity_type.bundle.info'),
- $container->get('datetime.time')
+ $container->get('datetime.time'),
+ $container->get('messenger')
);
}
@@ -235,7 +253,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
$group = $membership->getGroup();
$message = $membership->isActive() ? $this->t('Your are now subscribed to the group.') : $this->t('Your subscription request was sent.');
- drupal_set_message($message);
+ $this->messenger->addMessage($message);
// User doesn't have access to the group entity, so redirect to front page,
// otherwise back to the group entity.
diff --git a/src/Form/GroupUnsubscribeConfirmForm.php b/src/Form/GroupUnsubscribeConfirmForm.php
index 08f4fa0aa..d224ae42e 100644
--- a/src/Form/GroupUnsubscribeConfirmForm.php
+++ b/src/Form/GroupUnsubscribeConfirmForm.php
@@ -5,12 +5,39 @@
use Drupal\Core\Entity\ContentEntityDeleteForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
+use Drupal\Core\Messenger\MessengerInterface;
/**
* Provides a confirmation form for unsubscribing form a group.
*/
class GroupUnsubscribeConfirmForm extends ContentEntityDeleteForm {
+ /**
+ * The messenger.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
+ /**
+ * Constructs the GroupUnsubscribeConfirmForm object.
+ *
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger.
+ */
+ public function __construct(MessengerInterface $messenger) {
+ $this->messenger = $messenger;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('messenger')
+ );
+ }
+
/**
* {@inheritdoc}
*/
@@ -62,7 +89,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
$form_state->setRedirectUrl($redirect);
$membership->delete();
- drupal_set_message($this->t('You have unsubscribed from the group.'));
+ $this->messenger->addMessage($this->t('You have unsubscribed from the group.'));
}
}
diff --git a/src/Form/OgMembershipDeleteForm.php b/src/Form/OgMembershipDeleteForm.php
new file mode 100644
index 000000000..eaa049d8c
--- /dev/null
+++ b/src/Form/OgMembershipDeleteForm.php
@@ -0,0 +1,53 @@
+getEntity();
+
+ return $this->t("%user has been unsubscribed from %group.", [
+ '%user' => $membership->getOwner()->getDisplayName(),
+ '%group' => $membership->getGroup()->label(),
+ ]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function logDeletionMessage() {
+ /** @var \Drupal\og\Entity\OgMembership $entity */
+ $membership = $this->getEntity();
+
+ $this->logger('og')->notice("OG Membership: deleted the @membership_type membership for the user uid: @uid to the group of the entity-type @group_type and ID: @gid", [
+ '@membership_type' => $membership->getType(),
+ '@uid' => $membership->getOwner()->id(),
+ '@group_type' => $membership->getGroupEntityType(),
+ '@gid' => $membership->getGroupId(),
+ ]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getQuestion() {
+ /** @var \Drupal\og\Entity\OgMembership $entity */
+ $membership = $this->getEntity();
+
+ return $this->t("Are you sure you want to unsubscribe %user from %group?", [
+ '%user' => $membership->getOwner()->getDisplayName(),
+ '%group' => $membership->getGroup()->label(),
+ ]);
+ }
+
+}
diff --git a/src/Form/OgMembershipForm.php b/src/Form/OgMembershipForm.php
new file mode 100644
index 000000000..73dcd959c
--- /dev/null
+++ b/src/Form/OgMembershipForm.php
@@ -0,0 +1,150 @@
+ogAccess = $og_access;
+ $this->messenger = $messenger;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('entity.manager'),
+ $container->get('entity_type.bundle.info'),
+ $container->get('datetime.time'),
+ $container->get('og.access'),
+ $container->get('messenger')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function form(array $form, FormStateInterface $form_state) {
+ /** @var \Drupal\og\Entity\OgMembership $entity */
+ $entity = $this->getEntity();
+ /** @var \Drupal\Core\Entity\ContentEntityInterface $group */
+ $group = $entity->getGroup();
+
+ $form = parent::form($form, $form_state);
+ $form['#title'] = $this->t('Add member to %group', ['%group' => $group->label()]);
+ $form['entity_type'] = ['#value' => $entity->getEntityType()->id()];
+ $form['entity_id'] = ['#value' => $group->id()];
+
+ if ($entity->getType() != OgMembershipInterface::TYPE_DEFAULT) {
+ $form['membership_type'] = [
+ '#title' => $this->t('Membership type'),
+ '#type' => 'item',
+ '#plain_text' => $entity->type->entity->label(),
+ '#weight' => -2,
+ ];
+ }
+
+ if ($this->operation == 'edit') {
+ $form['#title'] = $this->t('Edit membership in %group', ['%group' => $group->label()]);
+ $form['uid']['#access'] = FALSE;
+ $form['member'] = [
+ '#title' => $this->t('Member name'),
+ '#type' => 'item',
+ '#markup' => $entity->getOwner()->getDisplayName(),
+ '#weight' => -10,
+ ];
+ }
+
+ // Require the 'manage members' permission to be able to edit roles.
+ $form['roles']['#access'] = $this->ogAccess
+ ->userAccess($group, 'manage members')
+ ->isAllowed();
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save(array $form, FormStateInterface $form_state) {
+ $membership = $this->entity;
+ $insert = $membership->isNew();
+ $membership->save();
+
+ $membership_link = $membership->link($this->t('View'));
+
+ $context = [
+ '@membership_type' => $membership->getType(),
+ '@uid' => $membership->getOwner()->id(),
+ '@group_type' => $membership->getGroupEntityType(),
+ '@gid' => $membership->getGroupId(),
+ 'link' => $membership_link,
+ ];
+
+ $t_args = [
+ '%user' => $membership->getOwner()->link(),
+ '%group' => $membership->getGroup()->link(),
+ ];
+
+ if ($insert) {
+ $this->logger('og')->notice('OG Membership: added the @membership_type membership for the use uid @uid to the group of the entity-type @group_type and ID @gid.', $context);
+ $this->messenger->addMessage($this->t('Added %user to %group.', $t_args));
+ return;
+ }
+
+ $this->logger('og')->notice('OG Membership: updated the @membership_type membership for the use uid @uid to the group of the entity-type @group_type and ID @gid.', $context);
+ $this->messenger->addMessage($this->t('Updated the membership for %user to %group.', $t_args));
+ }
+
+}
diff --git a/src/Form/OgMembershipTypeForm.php b/src/Form/OgMembershipTypeForm.php
new file mode 100644
index 000000000..540970ec6
--- /dev/null
+++ b/src/Form/OgMembershipTypeForm.php
@@ -0,0 +1,138 @@
+entityTypeManager = $entity_manager;
+ $this->messenger = $messenger;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('entity_type.manager'),
+ $container->get('messenger')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function form(array $form, FormStateInterface $form_state) {
+ $form = parent::form($form, $form_state);
+
+ $type = $this->entity;
+ if ($this->operation == 'add') {
+ $form['#title'] = $this->t('Add membership type');
+ }
+ else {
+ $form['#title'] = $this->t('Edit %label membership type', ['%label' => $type->label()]);
+ }
+
+ $form['name'] = [
+ '#title' => $this->t('Name'),
+ '#type' => 'textfield',
+ '#default_value' => $type->label(),
+ '#description' => $this->t('The human-readable name of this membership type.'),
+ '#required' => TRUE,
+ '#size' => 30,
+ ];
+
+ $form['type'] = [
+ '#type' => 'machine_name',
+ '#default_value' => $type->id(),
+ '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
+ '#machine_name' => [
+ 'exists' => ['Drupal\og\Entity\OgMembershipType', 'load'],
+ 'source' => ['name'],
+ ],
+ '#description' => $this->t('A unique machine-readable name for this membership type. It must only contain lowercase letters, numbers, and underscores.'),
+ ];
+ return $this->protectBundleIdElement($form);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function actions(array $form, FormStateInterface $form_state) {
+ $actions = parent::actions($form, $form_state);
+ $actions['submit']['#value'] = $this->t('Save membership type');
+ $actions['delete']['#value'] = $this->t('Delete membership type');
+ return $actions;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateForm(array &$form, FormStateInterface $form_state) {
+ parent::validateForm($form, $form_state);
+
+ $id = trim($form_state->getValue('type'));
+ // '0' is invalid, since elsewhere we check it using empty().
+ if ($id == '0') {
+ $form_state->setErrorByName('type', $this->t("Invalid machine-readable name. Enter a name other than %invalid.", ['%invalid' => $id]));
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save(array $form, FormStateInterface $form_state) {
+ $type = $this->entity;
+ $type->set('type', trim($type->id()));
+ $type->set('name', trim($type->label()));
+
+ $status = $type->save();
+
+ $t_args = ['%name' => $type->label()];
+
+ if ($status == SAVED_UPDATED) {
+ $this->messenger->addMessage($this->t('The membership type %name has been updated.', $t_args));
+ }
+ elseif ($status == SAVED_NEW) {
+ $this->messenger->addMessage($this->t('The membership type %name has been added.', $t_args));
+ $context = array_merge($t_args, ['link' => $type->link($this->t('View'), 'collection')]);
+ $this->logger('og')->notice('Added membership type %name.', $context);
+ }
+
+ $this->entityTypeManager->clearCachedFieldDefinitions();
+ $form_state->setRedirectUrl($type->urlInfo('collection'));
+ }
+
+}
diff --git a/src/MembershipManager.php b/src/MembershipManager.php
index 7677d35f4..27a9d2e42 100644
--- a/src/MembershipManager.php
+++ b/src/MembershipManager.php
@@ -131,6 +131,23 @@ public function getMembership(EntityInterface $group, AccountInterface $user, ar
return NULL;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getGroupMembershipCount(EntityInterface $group, array $states = [OgMembershipInterface::STATE_ACTIVE]) {
+ $query = $this->entityTypeManager
+ ->getStorage('og_membership')
+ ->getQuery()
+ ->condition('entity_id', $group->id());
+
+ if ($states) {
+ $query->condition('state', $states, 'IN');
+ }
+
+ $query->count();
+ return $query->execute();
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/src/MembershipManagerInterface.php b/src/MembershipManagerInterface.php
index d117645bb..bbe6db710 100644
--- a/src/MembershipManagerInterface.php
+++ b/src/MembershipManagerInterface.php
@@ -68,6 +68,19 @@ public function getUserGroups(AccountInterface $user, array $states = [OgMembers
*/
public function getMemberships(AccountInterface $user, array $states = [OgMembershipInterface::STATE_ACTIVE]);
+ /**
+ * Returns the number of group memberships for a given group.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $group
+ * The group to get the membership for.
+ * @param array $states
+ * (optional) Array with the state to return. Defaults to active.
+ *
+ * @return int
+ * The number of memberships for the group.
+ */
+ public function getGroupMembershipCount(EntityInterface $group, array $states = [OgMembershipInterface::STATE_ACTIVE]);
+
/**
* Returns the group membership for a given user and group.
*
diff --git a/src/Og.php b/src/Og.php
index 1aa2361e5..0ae939c1f 100644
--- a/src/Og.php
+++ b/src/Og.php
@@ -164,6 +164,23 @@ public static function getMembership(EntityInterface $group, AccountInterface $u
return $membership_manager->getMembership($group, $user, $states);
}
+ /**
+ * Returns the group memberships for a given group.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $group
+ * The group to get the membership for.
+ * @param array $states
+ * (optional) Array with the state to return. Defaults to active.
+ *
+ * @return \Drupal\og\OgMembershipInterface[]
+ * An array of OgMembership entities, keyed by ID.
+ */
+ public static function getGroupMemberships(EntityInterface $group, array $states = [OgMembershipInterface::STATE_ACTIVE]) {
+ /** @var \Drupal\og\MembershipManagerInterface $membership_manager */
+ $membership_manager = \Drupal::service('og.membership_manager');
+ return $membership_manager->getGroupMemberships($group, $states);
+ }
+
/**
* Creates an OG membership.
*
diff --git a/src/OgMembershipAccessControlHandler.php b/src/OgMembershipAccessControlHandler.php
new file mode 100644
index 000000000..d9b352dc1
--- /dev/null
+++ b/src/OgMembershipAccessControlHandler.php
@@ -0,0 +1,155 @@
+entityTypeId = $entity_type->id();
+ $this->entityType = $entity_type;
+ $this->ogAccess = $og_access;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+ return new static(
+ $entity_type,
+ $container->get('og.access')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
+ $group = $entity->getGroup();
+
+ // Do not allow deleting the group owner's membership.
+ if (($operation === 'delete') && ($group instanceof EntityOwnerInterface) && ($group->getOwnerId() == $entity->getOwner()->id())) {
+ return AccessResult::forbidden();
+ }
+
+ // If the user has permission to administer all groups, allow access.
+ if ($account->hasPermission('administer group')) {
+ return AccessResult::allowed();
+ }
+
+ $permissions = [OgAccess::ADMINISTER_GROUP_PERMISSION, 'manager members'];
+ foreach ($permissions as $permission) {
+ $result = $this->ogAccess->userAccess($group, $permission, $account);
+ if ($result->isAllowed()) {
+ return $result;
+ }
+ }
+
+ return AccessResult::neutral();
+
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createAccess($entity_bundle = NULL, AccountInterface $account = NULL, array $context = [], $return_as_object = FALSE) {
+ $account = $this->prepareUser($account);
+ $context += [
+ 'entity_type_id' => $this->entityTypeId,
+ 'langcode' => LanguageInterface::LANGCODE_DEFAULT,
+ ];
+
+ $cid = 'create:' . $context['group']->getEntityTypeId() . ':' . $context['group']->id();
+ if ($entity_bundle) {
+ $cid .= ':' . $entity_bundle;
+ }
+
+ if (($access = $this->getCache($cid, 'create', $context['langcode'], $account)) !== NULL) {
+ // Cache hit, no work necessary.
+ return $return_as_object ? $access : $access->isAllowed();
+ }
+
+ // Invoke hook_entity_create_access() and hook_ENTITY_TYPE_create_access().
+ // Hook results take precedence over overridden implementations of
+ // EntityAccessControlHandler::checkCreateAccess(). Entities that have
+ // checks that need to be done before the hook is invoked should do so by
+ // overriding this method.
+ // We grant access to the entity if both of these conditions are met:
+ // - No modules say to deny access.
+ // - At least one module says to grant access.
+ $args = [$account, $context, $entity_bundle];
+ $access = array_merge(
+ $this->moduleHandler()->invokeAll('entity_create_access', $args),
+ $this->moduleHandler()->invokeAll($this->entityTypeId . '_create_access', $args)
+ );
+
+ $return = $this->processAccessHookResults($access);
+
+ // Also execute the default access check except when the access result is
+ // already forbidden, as in that case, it can not be anything else.
+ if (!$return->isForbidden()) {
+ $return = $return->orIf($this->checkCreateAccess($account, $context, $entity_bundle));
+ }
+ $result = $this->setCache($return, $cid, 'create', $context['langcode'], $account);
+ return $return_as_object ? $result : $result->isAllowed();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
+ // If the user has permission to administer all groups, allow access.
+ if ($account->hasPermission('administer group')) {
+ return AccessResult::allowed();
+ }
+
+ $group = $context['group'];
+
+ // If we don't have a group, we can't really determine access other than
+ // checking global account permissions.
+ if ($group === NULL) {
+ return AccessResult::neutral();
+ }
+
+ $permissions = [
+ OgAccess::ADMINISTER_GROUP_PERMISSION,
+ 'add user',
+ 'manager members',
+ ];
+ foreach ($permissions as $permission) {
+ $result = $this->ogAccess->userAccess($group, $permission, $account);
+ if ($result->isAllowed()) {
+ return $result;
+ }
+ }
+
+ return AccessResult::neutral();
+ }
+
+}
diff --git a/src/OgMembershipTypeListBuilder.php b/src/OgMembershipTypeListBuilder.php
new file mode 100644
index 000000000..47f992ab4
--- /dev/null
+++ b/src/OgMembershipTypeListBuilder.php
@@ -0,0 +1,47 @@
+ $entity->label(),
+ 'class' => ['menu-label'],
+ ];
+ return $row + parent::buildRow($entity);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDefaultOperations(EntityInterface $entity) {
+ $operations = parent::getDefaultOperations($entity);
+ // Place the edit operation after the operations added by field_ui.module
+ // which have the weights 15, 20, 25.
+ if (isset($operations['edit'])) {
+ $operations['edit']['weight'] = 30;
+ }
+ return $operations;
+ }
+
+}
diff --git a/src/Plugin/Derivative/OgLocalTask.php b/src/Plugin/Derivative/OgLocalTask.php
index c861065c4..65cf6f769 100644
--- a/src/Plugin/Derivative/OgLocalTask.php
+++ b/src/Plugin/Derivative/OgLocalTask.php
@@ -28,7 +28,7 @@ class OgLocalTask extends DeriverBase implements ContainerDeriverInterface {
*
* @var \Drupal\Core\Routing\RouteProvider
*/
- protected $routProvider;
+ protected $routeProvider;
/**
* Creates an OgLocalTask object.
@@ -40,7 +40,7 @@ class OgLocalTask extends DeriverBase implements ContainerDeriverInterface {
*/
public function __construct(GroupTypeManagerInterface $group_type_manager, RouteProvider $route_provider) {
$this->groupTypeManager = $group_type_manager;
- $this->routProvider = $route_provider;
+ $this->routeProvider = $route_provider;
}
/**
@@ -60,14 +60,14 @@ public function getDerivativeDefinitions($base_plugin_definition) {
$derivatives = [];
foreach (array_keys($this->groupTypeManager->getGroupMap()) as $entity_type_id) {
- $route_name = "entity.$entity_type_id.og_admin_routes";
+ $route_name = "entity.$entity_type_id.og_admin";
- if (!$this->routProvider->getRoutesByNames([$route_name])) {
+ if (!$this->routeProvider->getRoutesByNames([$route_name])) {
// Route not found.
continue;
}
- $derivatives[$entity_type_id . '.og_admin_routes'] = [
+ $derivatives[$route_name] = [
'route_name' => $route_name,
'title' => $this->t('Group'),
'base_route' => 'entity.' . $entity_type_id . '.canonical',
diff --git a/src/Plugin/EntityReferenceSelection/OgRoleSelection.php b/src/Plugin/EntityReferenceSelection/OgRoleSelection.php
new file mode 100644
index 000000000..c6f2a52f8
--- /dev/null
+++ b/src/Plugin/EntityReferenceSelection/OgRoleSelection.php
@@ -0,0 +1,64 @@
+ 'og_role',
+ ];
+ return \Drupal::service('plugin.manager.entity_reference_selection')->getInstance($options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
+ $query = parent::buildEntityQuery($match, $match_operator);
+
+ // @todo implement an easier, more consistent way to get the group type. At
+ // the moment, this works either for checkboxes or OG Autocomplete widget
+ // types on entities that have a getGroup() method. It also does not work
+ // properly every time; for example during validation.
+ $group = NULL;
+ if (isset($this->configuration['entity'])) {
+ $entity = $this->configuration['entity'];
+ $group = is_callable([$entity, 'getGroup']) ? $entity->getGroup() : NULL;
+ }
+
+ if (isset($this->configuration['handler_settings']['group'])) {
+ $group = $this->configuration['handler_settings']['group'];
+ }
+
+ if ($group === NULL) {
+ return $query;
+ }
+
+ $query->condition('group_type', $group->getEntityTypeId(), '=');
+ $query->condition('group_bundle', $group->bundle(), '=');
+ $query->condition($query->orConditionGroup()
+ ->condition('role_type', NULL, 'IS NULL')
+ ->condition('role_type', 'required', '<>'));
+ return $query;
+ }
+
+}
diff --git a/src/Plugin/EntityReferenceSelection/OgUserSelection.php b/src/Plugin/EntityReferenceSelection/OgUserSelection.php
new file mode 100644
index 000000000..e66edd5d6
--- /dev/null
+++ b/src/Plugin/EntityReferenceSelection/OgUserSelection.php
@@ -0,0 +1,194 @@
+connection = $connection;
+ $this->userStorage = $entity_manager->getStorage('user');
+ $this->membershipManager = $membership_manager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ return new static(
+ $configuration,
+ $plugin_id,
+ $plugin_definition,
+ $container->get('entity.manager'),
+ $container->get('module_handler'),
+ $container->get('current_user'),
+ $container->get('database'),
+ $container->get('og.membership_manager')
+ );
+ }
+
+ /**
+ * Get the selection handler of the field.
+ *
+ * @return Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection
+ * Returns the selection handler.
+ */
+ public function getSelectionHandler() {
+ $options = [
+ 'target_type' => 'user',
+ ];
+ return \Drupal::service('plugin.manager.entity_reference_selection')->getInstance($options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
+ $query = parent::buildEntityQuery($match, $match_operator);
+
+ // Anon can't be a group member.
+ $query->condition('uid', 0, '<>');
+
+ // The user entity doesn't have a label column.
+ if (isset($match)) {
+ $query->condition('name', $match, $match_operator);
+ }
+
+ // Adding the permission check is sadly insufficient for users: core
+ // requires us to also know about the concept of 'blocked' and 'active'.
+ if (!$this->currentUser->hasPermission('administer users')) {
+ $query->condition('status', 1);
+ }
+
+ return $query;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function entityQueryAlter(SelectInterface $query) {
+ // Exclude users who are already in the current group.
+ // This has to be done on the SQL query rather than the entity query,
+ // because a reverse relationship to the OG membership entity is needed.
+ // @todo implement an easier, more consistent way to get the group type. At
+ // the moment, this works either for checkboxes or OG Autocomplete widget
+ // types on entities that have a getGroup() method. It also does not work
+ // properly every time; for example during validation.
+ $group = NULL;
+ if (isset($this->configuration['entity'])) {
+ $entity = $this->configuration['entity'];
+ $group = is_callable([$entity, 'getGroup']) ? $entity->getGroup() : NULL;
+ }
+
+ if (isset($this->configuration['handler_settings']['group'])) {
+ $group = $this->configuration['handler_settings']['group'];
+ }
+
+ if ($group === NULL) {
+ return $query;
+ }
+
+ // Left join to the OG membership base table.
+ $query->leftJoin('og_membership', 'ogm', "base_table.uid = ogm.uid AND ogm.entity_type = :entity_type AND ogm.entity_id = :entity_id", [
+ ':entity_type' => $group->getEntityTypeId(),
+ ':entity_id' => $group->id(),
+ ]);
+
+ // Exclude any users who are in the current group.
+ $query->isNull('ogm.id');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createNewEntity($entity_type_id, $bundle, $label, $uid) {
+ $user = parent::createNewEntity($entity_type_id, $bundle, $label, $uid);
+
+ // In order to create a referenceable user, it needs to be active.
+ if (!$this->currentUser->hasPermission('administer users')) {
+ /** @var \Drupal\user\UserInterface $user */
+ $user->activate();
+ }
+
+ return $user;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateReferenceableNewEntities(array $entities) {
+ $entities = parent::validateReferenceableNewEntities($entities);
+ // Mirror the conditions checked in buildEntityQuery().
+ if (!$this->currentUser->hasPermission('administer users')) {
+ $entities = array_filter($entities, function ($user) {
+ /** @var \Drupal\user\UserInterface $user */
+ return $user->isActive();
+ });
+ }
+ return $entities;
+ }
+
+}
diff --git a/src/Plugin/Field/FieldFormatter/GroupSubscribeFormatter.php b/src/Plugin/Field/FieldFormatter/GroupSubscribeFormatter.php
index ab3431c3d..ca19e00d4 100644
--- a/src/Plugin/Field/FieldFormatter/GroupSubscribeFormatter.php
+++ b/src/Plugin/Field/FieldFormatter/GroupSubscribeFormatter.php
@@ -141,6 +141,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) {
$parameters = [
'entity_type_id' => $group->getEntityTypeId(),
'group' => $group->id(),
+ 'og_membership_type' => OgMembershipInterface::TYPE_DEFAULT,
];
$url = Url::fromRoute('og.subscribe', $parameters);
diff --git a/src/Plugin/Field/FieldWidget/OgAutocomplete.php b/src/Plugin/Field/FieldWidget/OgAutocomplete.php
new file mode 100644
index 000000000..0a2aa4042
--- /dev/null
+++ b/src/Plugin/Field/FieldWidget/OgAutocomplete.php
@@ -0,0 +1,48 @@
+getEntity();
+ if (!is_callable([$entity, 'getGroup'])) {
+ return $element;
+ }
+
+ $element['target_id']['#type'] = 'og_autocomplete';
+ $element['target_id']['#og_group'] = $entity->getGroup();
+
+ return $element;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function errorElement(array $element, ConstraintViolationInterface $error, array $form, FormStateInterface $form_state) {
+ return $element;
+ }
+
+}
diff --git a/src/Plugin/Validation/Constraint/UniqueOgMembershipConstraint.php b/src/Plugin/Validation/Constraint/UniqueOgMembershipConstraint.php
new file mode 100644
index 000000000..905bc6a28
--- /dev/null
+++ b/src/Plugin/Validation/Constraint/UniqueOgMembershipConstraint.php
@@ -0,0 +1,26 @@
+getEntity();
+
+ // Only applicable to new memberships.
+ if (!$entity->isNew()) {
+ return;
+ }
+
+ // The default entity reference constraint adds a violation in this case.
+ $value = $value->getValue();
+ if (!isset($value[0]) || !isset($value[0]['target_id'])) {
+ return;
+ }
+
+ $new_member_uid = $value[0]['target_id'];
+
+ $query = \Drupal::service('entity_type.manager')
+ ->getStorage('og_membership')
+ ->getQuery()
+ ->condition('entity_type', $entity->getGroupEntityType())
+ ->condition('entity_id', $entity->getGroupId())
+ ->condition('uid', $new_member_uid);
+ $membership_ids = $query->execute();
+
+ if ($membership_ids) {
+ $user = \Drupal::service('entity_type.manager')->getStorage('user')->load($new_member_uid);
+ $this->context->addViolation($constraint->NotUniqueMembership, ['%user' => $user->getDisplayName()]);
+ return;
+ }
+ }
+
+}
diff --git a/src/Plugin/Validation/Constraint/ValidOgRoleConstraint.php b/src/Plugin/Validation/Constraint/ValidOgRoleConstraint.php
new file mode 100644
index 000000000..6fdbc8bdf
--- /dev/null
+++ b/src/Plugin/Validation/Constraint/ValidOgRoleConstraint.php
@@ -0,0 +1,24 @@
+getEntity();
+ if (!$entity) {
+ // Entity with that entity ID does not exists. This could happen if a
+ // stale entity is passed for validation.
+ return;
+ }
+
+ $group_type = $entity->getGroup()->getEntityTypeId();
+ $group_bundle = $entity->getGroup()->bundle();
+
+ foreach ($value->referencedEntities() as $og_role) {
+ if ($og_role->getGroupType() !== $group_type || $og_role->getGroupBundle() !== $group_bundle) {
+ $this->context->addViolation($constraint->NotValidRole);
+ }
+ }
+
+ }
+
+}
diff --git a/src/Routing/RouteSubscriber.php b/src/Routing/RouteSubscriber.php
index 2ae800d96..24c6b095d 100644
--- a/src/Routing/RouteSubscriber.php
+++ b/src/Routing/RouteSubscriber.php
@@ -7,8 +7,6 @@
use Drupal\Core\Routing\RouteSubscriberBase;
use Drupal\Core\Routing\RoutingEvents;
use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Drupal\og\Event\OgAdminRoutesEvent;
-use Drupal\og\Event\OgAdminRoutesEventInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
@@ -74,7 +72,7 @@ protected function alterRoutes(RouteCollection $collection) {
}
$entity_type_id = $entity_type->id();
- $route_name = "entity.$entity_type_id.og_admin_routes";
+ $route_name = "entity.$entity_type_id.og_admin";
$route = new Route($og_admin_path);
$route
@@ -86,7 +84,7 @@ protected function alterRoutes(RouteCollection $collection) {
'_og_user_access_group' => 'administer group',
])
->setOption('parameters', [
- $entity_type_id => ['type' => 'entity:' . $entity_type_id],
+ 'group' => ['type' => 'entity:' . $entity_type_id],
])
// As the above parameters doesn't send the entity,
// so we will have to use the Route matcher to extract it.
@@ -95,36 +93,10 @@ protected function alterRoutes(RouteCollection $collection) {
$collection->add($route_name, $route);
- // Add the routes defined in the event subscribers.
- $this->createRoutesFromEventSubscribers($og_admin_path, $entity_type_id, $collection);
-
}
}
- /**
- * Add all the OG admin items to the route collection.
- *
- * @param string $og_admin_path
- * The OG admin path.
- * @param string $entity_type_id
- * The entity type ID.
- * @param \Symfony\Component\Routing\RouteCollection $collection
- * The route collection object.
- */
- protected function createRoutesFromEventSubscribers($og_admin_path, $entity_type_id, RouteCollection $collection) {
- $event = new OgAdminRoutesEvent();
- $this->eventDispatcher->dispatch(OgAdminRoutesEventInterface::EVENT_NAME, $event);
-
- foreach ($event->getRoutes($entity_type_id) as $name => $route_info) {
- // Add the parent route.
- $parent_route_name = "entity.$entity_type_id.og_admin_routes.$name";
- $parent_path = $og_admin_path . '/' . $route_info['path'];
-
- $this->addRoute($collection, $parent_route_name, $parent_path, $route_info);
- }
- }
-
/**
* Helper method to add route to collection.
*
diff --git a/tests/src/Functional/GroupSubscribeFormatterTest.php b/tests/src/Functional/GroupSubscribeFormatterTest.php
index 04cccc0ca..11a092880 100644
--- a/tests/src/Functional/GroupSubscribeFormatterTest.php
+++ b/tests/src/Functional/GroupSubscribeFormatterTest.php
@@ -19,7 +19,7 @@ class GroupSubscribeFormatterTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
- public static $modules = ['node', 'og'];
+ public static $modules = ['node', 'og', 'options'];
/**
* Test entity group.
diff --git a/tests/src/Functional/GroupTabTest.php b/tests/src/Functional/GroupTabTest.php
deleted file mode 100644
index 2bd2e6912..000000000
--- a/tests/src/Functional/GroupTabTest.php
+++ /dev/null
@@ -1,103 +0,0 @@
-bundle1 = mb_strtolower($this->randomMachineName());
- $this->bundle2 = mb_strtolower($this->randomMachineName());
-
- // Create node types.
- $node_type1 = NodeType::create(['type' => $this->bundle1, 'name' => $this->bundle1]);
- $node_type1->save();
-
- $node_type2 = NodeType::create(['type' => $this->bundle2, 'name' => $this->bundle2]);
- $node_type2->save();
-
- // Define the first bundle as group.
- Og::groupTypeManager()->addGroup('node', $this->bundle1);
-
- // Create node author user.
- $user = $this->createUser();
-
- // Create nodes.
- $this->group = Node::create([
- 'type' => $this->bundle1,
- 'title' => $this->randomString(),
- 'uid' => $user->id(),
- ]);
- $this->group->save();
-
- $this->nonGroup = Node::create([
- 'type' => $this->bundle2,
- 'title' => $this->randomString(),
- 'uid' => $user->id(),
- ]);
- $this->nonGroup->save();
-
- $this->user1 = $this->drupalCreateUser(['administer group']);
- }
-
- /**
- * Tests the formatter changes by user and membership.
- */
- public function testGroupTab() {
- $this->drupalLogin($this->user1);
- $this->drupalGet('group/node/' . $this->group->id() . '/admin');
- $this->assertResponse(200);
-
- $this->drupalGet('group/node/' . $this->nonGroup->id() . '/admin');
- $this->assertResponse(403);
- }
-
-}
diff --git a/tests/src/Kernel/Access/AccessByOgMembershipTest.php b/tests/src/Kernel/Access/AccessByOgMembershipTest.php
index 37c50ead3..0594d0a07 100644
--- a/tests/src/Kernel/Access/AccessByOgMembershipTest.php
+++ b/tests/src/Kernel/Access/AccessByOgMembershipTest.php
@@ -36,6 +36,7 @@ class AccessByOgMembershipTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/Access/OgAccessHookTest.php b/tests/src/Kernel/Access/OgAccessHookTest.php
index 275770da7..799e05088 100644
--- a/tests/src/Kernel/Access/OgAccessHookTest.php
+++ b/tests/src/Kernel/Access/OgAccessHookTest.php
@@ -33,6 +33,7 @@ class OgAccessHookTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/Access/OgEntityAccessTest.php b/tests/src/Kernel/Access/OgEntityAccessTest.php
index b26be94a0..ddd86d6e7 100644
--- a/tests/src/Kernel/Access/OgEntityAccessTest.php
+++ b/tests/src/Kernel/Access/OgEntityAccessTest.php
@@ -25,6 +25,7 @@ class OgEntityAccessTest extends KernelTestBase {
'user',
'field',
'og',
+ 'options',
'entity_test',
];
diff --git a/tests/src/Kernel/Access/OgGroupContentOperationAccessTest.php b/tests/src/Kernel/Access/OgGroupContentOperationAccessTest.php
index 5a8573817..d25ec5135 100644
--- a/tests/src/Kernel/Access/OgGroupContentOperationAccessTest.php
+++ b/tests/src/Kernel/Access/OgGroupContentOperationAccessTest.php
@@ -32,6 +32,7 @@ class OgGroupContentOperationAccessTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/Action/ActionTestBase.php b/tests/src/Kernel/Action/ActionTestBase.php
index adc59c38a..1190f71b7 100644
--- a/tests/src/Kernel/Action/ActionTestBase.php
+++ b/tests/src/Kernel/Action/ActionTestBase.php
@@ -31,7 +31,7 @@ abstract class ActionTestBase extends KernelTestBase {
/**
* {@inheritdoc}
*/
- public static $modules = ['node', 'og', 'system', 'user'];
+ public static $modules = ['node', 'og', 'system', 'user', 'options'];
/**
* An array of test users.
diff --git a/tests/src/Kernel/Console/DrupalConsoleAddFieldTest.php b/tests/src/Kernel/Console/DrupalConsoleAddFieldTest.php
index 99ae6e631..de0471227 100644
--- a/tests/src/Kernel/Console/DrupalConsoleAddFieldTest.php
+++ b/tests/src/Kernel/Console/DrupalConsoleAddFieldTest.php
@@ -24,6 +24,7 @@ class DrupalConsoleAddFieldTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/DefaultRoleEventIntegrationTest.php b/tests/src/Kernel/DefaultRoleEventIntegrationTest.php
index 5f316e0e8..545de2ebf 100644
--- a/tests/src/Kernel/DefaultRoleEventIntegrationTest.php
+++ b/tests/src/Kernel/DefaultRoleEventIntegrationTest.php
@@ -18,7 +18,14 @@ class DefaultRoleEventIntegrationTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
- public static $modules = ['entity_test', 'og', 'system', 'user', 'field'];
+ public static $modules = [
+ 'entity_test',
+ 'og',
+ 'system',
+ 'user',
+ 'field',
+ 'options',
+ ];
/**
* The Symfony event dispatcher.
diff --git a/tests/src/Kernel/Entity/CacheInvalidationOnGroupChangeTest.php b/tests/src/Kernel/Entity/CacheInvalidationOnGroupChangeTest.php
index 2546c5f97..307c4a733 100644
--- a/tests/src/Kernel/Entity/CacheInvalidationOnGroupChangeTest.php
+++ b/tests/src/Kernel/Entity/CacheInvalidationOnGroupChangeTest.php
@@ -24,6 +24,7 @@ class CacheInvalidationOnGroupChangeTest extends KernelTestBase {
'og',
'system',
'user',
+ 'options',
];
/**
diff --git a/tests/src/Kernel/Entity/EntityCreateAccessTest.php b/tests/src/Kernel/Entity/EntityCreateAccessTest.php
index 0c919b11a..38f4ce611 100644
--- a/tests/src/Kernel/Entity/EntityCreateAccessTest.php
+++ b/tests/src/Kernel/Entity/EntityCreateAccessTest.php
@@ -31,6 +31,7 @@ class EntityCreateAccessTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/Entity/GetBundleByBundleTest.php b/tests/src/Kernel/Entity/GetBundleByBundleTest.php
index 24ed030ed..d89e83614 100644
--- a/tests/src/Kernel/Entity/GetBundleByBundleTest.php
+++ b/tests/src/Kernel/Entity/GetBundleByBundleTest.php
@@ -24,6 +24,7 @@ class GetBundleByBundleTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/Entity/GetGroupContentTest.php b/tests/src/Kernel/Entity/GetGroupContentTest.php
index c360b7440..b35cd0f58 100644
--- a/tests/src/Kernel/Entity/GetGroupContentTest.php
+++ b/tests/src/Kernel/Entity/GetGroupContentTest.php
@@ -26,6 +26,7 @@ class GetGroupContentTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/Entity/GetMembershipsTest.php b/tests/src/Kernel/Entity/GetMembershipsTest.php
index b8e813337..58088897d 100644
--- a/tests/src/Kernel/Entity/GetMembershipsTest.php
+++ b/tests/src/Kernel/Entity/GetMembershipsTest.php
@@ -28,6 +28,7 @@ class GetMembershipsTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/Entity/GetUserGroupsTest.php b/tests/src/Kernel/Entity/GetUserGroupsTest.php
index d183fdb74..543c35791 100644
--- a/tests/src/Kernel/Entity/GetUserGroupsTest.php
+++ b/tests/src/Kernel/Entity/GetUserGroupsTest.php
@@ -26,6 +26,7 @@ class GetUserGroupsTest extends KernelTestBase {
'user',
'field',
'og',
+ 'options',
'entity_test',
];
diff --git a/tests/src/Kernel/Entity/GroupAudienceTest.php b/tests/src/Kernel/Entity/GroupAudienceTest.php
index d1132d337..de907ca91 100644
--- a/tests/src/Kernel/Entity/GroupAudienceTest.php
+++ b/tests/src/Kernel/Entity/GroupAudienceTest.php
@@ -29,6 +29,7 @@ class GroupAudienceTest extends KernelTestBase {
'entity_test',
'field',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/Entity/GroupMembershipManagerTest.php b/tests/src/Kernel/Entity/GroupMembershipManagerTest.php
index 705b81918..fb0bc4a9f 100644
--- a/tests/src/Kernel/Entity/GroupMembershipManagerTest.php
+++ b/tests/src/Kernel/Entity/GroupMembershipManagerTest.php
@@ -29,6 +29,7 @@ class GroupMembershipManagerTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/Entity/GroupTypeTest.php b/tests/src/Kernel/Entity/GroupTypeTest.php
index c3f16144e..eed80eb73 100644
--- a/tests/src/Kernel/Entity/GroupTypeTest.php
+++ b/tests/src/Kernel/Entity/GroupTypeTest.php
@@ -15,7 +15,7 @@ class GroupTypeTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
- public static $modules = ['field', 'node', 'og', 'system', 'user'];
+ public static $modules = ['field', 'node', 'og', 'options', 'system', 'user'];
/**
* The group type manager.
diff --git a/tests/src/Kernel/Entity/OgMembershipRoleReferenceTest.php b/tests/src/Kernel/Entity/OgMembershipRoleReferenceTest.php
index 98a669fc0..019c70cdd 100644
--- a/tests/src/Kernel/Entity/OgMembershipRoleReferenceTest.php
+++ b/tests/src/Kernel/Entity/OgMembershipRoleReferenceTest.php
@@ -20,11 +20,12 @@ class OgMembershipRoleReferenceTest extends KernelTestBase {
* {@inheritdoc}
*/
public static $modules = [
- 'og',
'field',
'node',
- 'user',
+ 'og',
+ 'options',
'system',
+ 'user',
];
/**
diff --git a/tests/src/Kernel/Entity/OgMembershipTest.php b/tests/src/Kernel/Entity/OgMembershipTest.php
index e238d2528..e97725faf 100644
--- a/tests/src/Kernel/Entity/OgMembershipTest.php
+++ b/tests/src/Kernel/Entity/OgMembershipTest.php
@@ -30,6 +30,7 @@ class OgMembershipTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/Entity/OgRoleTest.php b/tests/src/Kernel/Entity/OgRoleTest.php
index 728e90444..932495d20 100644
--- a/tests/src/Kernel/Entity/OgRoleTest.php
+++ b/tests/src/Kernel/Entity/OgRoleTest.php
@@ -24,6 +24,7 @@ class OgRoleTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/Entity/OgStandardReferenceItemTest.php b/tests/src/Kernel/Entity/OgStandardReferenceItemTest.php
index 7e336ad35..3ed486df0 100644
--- a/tests/src/Kernel/Entity/OgStandardReferenceItemTest.php
+++ b/tests/src/Kernel/Entity/OgStandardReferenceItemTest.php
@@ -18,7 +18,14 @@ class OgStandardReferenceItemTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
- public static $modules = ['user', 'entity_test', 'field', 'og', 'system'];
+ public static $modules = [
+ 'user',
+ 'entity_test',
+ 'field',
+ 'og',
+ 'options',
+ 'system',
+ ];
protected $bundles;
protected $fieldName;
diff --git a/tests/src/Kernel/Entity/ReferenceStringIdTest.php b/tests/src/Kernel/Entity/ReferenceStringIdTest.php
index d7575dd0e..3ea0c4b5e 100644
--- a/tests/src/Kernel/Entity/ReferenceStringIdTest.php
+++ b/tests/src/Kernel/Entity/ReferenceStringIdTest.php
@@ -17,7 +17,14 @@ class ReferenceStringIdTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
- public static $modules = ['user', 'entity_test', 'field', 'og', 'system'];
+ public static $modules = [
+ 'user',
+ 'entity_test',
+ 'field',
+ 'og',
+ 'options',
+ 'system',
+ ];
/**
* Array of test bundles. The first is a group, the second group content.
diff --git a/tests/src/Kernel/Entity/SelectionHandlerTest.php b/tests/src/Kernel/Entity/SelectionHandlerTest.php
index 5e84de5d4..76cbc7035 100644
--- a/tests/src/Kernel/Entity/SelectionHandlerTest.php
+++ b/tests/src/Kernel/Entity/SelectionHandlerTest.php
@@ -34,6 +34,7 @@ class SelectionHandlerTest extends KernelTestBase {
'entity_reference',
'node',
'og',
+ 'options',
];
/**
diff --git a/tests/src/Kernel/EntityReference/Views/OgStandardReferenceRelationshipTest.php b/tests/src/Kernel/EntityReference/Views/OgStandardReferenceRelationshipTest.php
index 03beeacfa..1a12e55d3 100644
--- a/tests/src/Kernel/EntityReference/Views/OgStandardReferenceRelationshipTest.php
+++ b/tests/src/Kernel/EntityReference/Views/OgStandardReferenceRelationshipTest.php
@@ -43,6 +43,7 @@ class OgStandardReferenceRelationshipTest extends ViewsKernelTestBase {
'views',
'og_standard_reference_test_views',
'og',
+ 'options',
];
/**
diff --git a/tests/src/Kernel/Field/AudienceFieldFormatterTest.php b/tests/src/Kernel/Field/AudienceFieldFormatterTest.php
index 1b95a78db..d485441e8 100644
--- a/tests/src/Kernel/Field/AudienceFieldFormatterTest.php
+++ b/tests/src/Kernel/Field/AudienceFieldFormatterTest.php
@@ -15,7 +15,7 @@ class AudienceFieldFormatterTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
- public static $modules = ['field', 'og'];
+ public static $modules = ['field', 'og', 'options'];
/**
* Testing og_field_formatter_info_alter().
diff --git a/tests/src/Kernel/Form/GroupSubscribeFormTest.php b/tests/src/Kernel/Form/GroupSubscribeFormTest.php
index 6b0fccafd..5b85cde8e 100644
--- a/tests/src/Kernel/Form/GroupSubscribeFormTest.php
+++ b/tests/src/Kernel/Form/GroupSubscribeFormTest.php
@@ -29,6 +29,7 @@ class GroupSubscribeFormTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
];
/**
diff --git a/tests/src/Kernel/GroupManagerSubscriptionTest.php b/tests/src/Kernel/GroupManagerSubscriptionTest.php
index aa932451d..24949d904 100644
--- a/tests/src/Kernel/GroupManagerSubscriptionTest.php
+++ b/tests/src/Kernel/GroupManagerSubscriptionTest.php
@@ -26,6 +26,7 @@ class GroupManagerSubscriptionTest extends KernelTestBase {
'og_test',
'system',
'user',
+ 'options',
];
/**
diff --git a/tests/src/Kernel/GroupTypeConditionTest.php b/tests/src/Kernel/GroupTypeConditionTest.php
index ff2c52320..8306f394a 100644
--- a/tests/src/Kernel/GroupTypeConditionTest.php
+++ b/tests/src/Kernel/GroupTypeConditionTest.php
@@ -22,6 +22,7 @@ class GroupTypeConditionTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
'system',
'user',
];
diff --git a/tests/src/Kernel/OgDeleteOrphansTest.php b/tests/src/Kernel/OgDeleteOrphansTest.php
index 17fa75f3c..0ba6342f6 100644
--- a/tests/src/Kernel/OgDeleteOrphansTest.php
+++ b/tests/src/Kernel/OgDeleteOrphansTest.php
@@ -26,6 +26,7 @@ class OgDeleteOrphansTest extends KernelTestBase {
'entity_reference',
'node',
'og',
+ 'options',
];
/**
diff --git a/tests/src/Kernel/OgRoleManagerTest.php b/tests/src/Kernel/OgRoleManagerTest.php
index f226fe7a4..2d8378b94 100644
--- a/tests/src/Kernel/OgRoleManagerTest.php
+++ b/tests/src/Kernel/OgRoleManagerTest.php
@@ -23,6 +23,7 @@ class OgRoleManagerTest extends KernelTestBase {
'field',
'node',
'og',
+ 'options',
];
/**
diff --git a/tests/src/Kernel/PermissionEventTest.php b/tests/src/Kernel/PermissionEventTest.php
index c9a0ea94c..05089d1ec 100644
--- a/tests/src/Kernel/PermissionEventTest.php
+++ b/tests/src/Kernel/PermissionEventTest.php
@@ -28,6 +28,7 @@ class PermissionEventTest extends KernelTestBase {
'og',
'system',
'user',
+ 'options',
];
/**
diff --git a/tests/src/Kernel/SelectionHandlerSettingsSchemaTest.php b/tests/src/Kernel/SelectionHandlerSettingsSchemaTest.php
index c1effe04c..8d1e38da3 100644
--- a/tests/src/Kernel/SelectionHandlerSettingsSchemaTest.php
+++ b/tests/src/Kernel/SelectionHandlerSettingsSchemaTest.php
@@ -22,6 +22,7 @@ class SelectionHandlerSettingsSchemaTest extends KernelTestBase {
'og_ui',
'system',
'user',
+ 'options',
];
/**
diff --git a/tests/src/Kernel/Views/OgAdminMembersViewTest.php b/tests/src/Kernel/Views/OgAdminMembersViewTest.php
index 0a41fbc16..277848675 100644
--- a/tests/src/Kernel/Views/OgAdminMembersViewTest.php
+++ b/tests/src/Kernel/Views/OgAdminMembersViewTest.php
@@ -27,6 +27,7 @@ class OgAdminMembersViewTest extends ViewsKernelTestBase {
'field',
'node',
'og',
+ 'options',
'views',
];
diff --git a/tests/src/Unit/OgAdminRoutesControllerTest.php b/tests/src/Unit/OgAdminRoutesControllerTest.php
index ddd303058..7768a7b60 100644
--- a/tests/src/Unit/OgAdminRoutesControllerTest.php
+++ b/tests/src/Unit/OgAdminRoutesControllerTest.php
@@ -2,17 +2,13 @@
namespace Drupal\Tests\og\Unit;
-use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
use Drupal\Core\Access\AccessManagerInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\og\Controller\OgAdminRoutesController;
-use Drupal\og\Event\OgAdminRoutesEvent;
-use Drupal\og\Event\OgAdminRoutesEventInterface;
use Drupal\Tests\UnitTestCase;
-use Prophecy\Argument;
use Symfony\Component\Routing\Route;
/**
@@ -51,13 +47,6 @@ class OgAdminRoutesControllerTest extends UnitTestCase {
*/
protected $routeMatch;
- /**
- * The event dispatcher service.
- *
- * @var \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher|\Prophecy\Prophecy\ObjectProphecy
- */
- protected $eventDispatcher;
-
/**
* The group entity.
*
@@ -65,13 +54,6 @@ class OgAdminRoutesControllerTest extends UnitTestCase {
*/
protected $group;
- /**
- * The OG admin route event.
- *
- * @var \Drupal\og\Event\OgAdminRoutesEvent
- */
- protected $event;
-
/**
* The entity type ID of the group entity.
*
@@ -79,13 +61,6 @@ class OgAdminRoutesControllerTest extends UnitTestCase {
*/
protected $entityTypeId;
- /**
- * The routes info as returned from the event subscribers.
- *
- * @var array
- */
- protected $routesInfo;
-
/**
* The Url object.
*
@@ -109,25 +84,11 @@ public function setUp() {
$this->routeMatch = $this->prophesize(RouteMatchInterface::class);
$this->group = $this->prophesize(EntityInterface::class);
- $this->event = $this->prophesize(OgAdminRoutesEvent::class);
- $this->eventDispatcher = $this->prophesize(ContainerAwareEventDispatcher::class);
$this->route = $this->prophesize(Route::class);
$this->entityTypeId = $this->randomMachineName();
$this->entityId = rand(20, 30);
$this->url = $this->prophesize(Url::class);
- $this->routesInfo = [
- $this->randomMachineName() => [
- 'title' => $this->randomMachineName(),
- 'description' => $this->randomMachineName(),
- ],
-
- $this->randomMachineName() => [
- 'title' => $this->randomMachineName(),
- 'description' => $this->randomMachineName(),
- ],
- ];
-
$this
->routeMatch
->getRouteObject()
@@ -142,7 +103,7 @@ public function setUp() {
$this
->routeMatch
- ->getParameter($parameter_name)
+ ->getParameter('group')
->willReturn($this->group->reveal());
$this
@@ -155,18 +116,6 @@ public function setUp() {
->id()
->willReturn($this->entityId);
- $this
- ->eventDispatcher
- ->dispatch(OgAdminRoutesEventInterface::EVENT_NAME, Argument::type(OgAdminRoutesEvent::class))
- ->willReturn($this->event->reveal())
- ->shouldBeCalled();
-
- $this
- ->event
- ->getRoutes($this->entityTypeId)
- ->willReturn($this->routesInfo)
- ->shouldBeCalled();
-
// Set the container for the string translation service.
$translation = $this->getStringTranslationStub();
$container = new ContainerBuilder();
@@ -194,8 +143,8 @@ public function testRoutesWithAccess() {
$result = $this->getRenderElementResult(TRUE);
foreach ($result['og_admin_routes']['#content'] as $key => $value) {
- $this->assertEquals($this->routesInfo[$key]['title'], $value['title']);
- $this->assertEquals($this->routesInfo[$key]['description'], $value['description']);
+ $this->assertEquals('Members', $value['title']);
+ $this->assertEquals('Manage members', $value['description']);
}
}
@@ -210,16 +159,15 @@ public function testRoutesWithAccess() {
* The render array.
*/
protected function getRenderElementResult($allow_access) {
- $parameters = [$this->entityTypeId => $this->entityId];
- foreach (array_keys($this->routesInfo) as $name) {
- $route_name = "entity.{$this->entityTypeId}.og_admin_routes.$name";
- $this
- ->accessManager
- ->checkNamedRoute($route_name, $parameters)
- ->willReturn($allow_access);
- }
+ $parameters = ['entity_type_id' => $this->entityTypeId, 'group' => $this->entityId];
+
+ $route_name = "og_admin.members";
+ $this
+ ->accessManager
+ ->checkNamedRoute($route_name, $parameters)
+ ->willReturn($allow_access);
- $og_admin_routes_controller = new OgAdminRoutesController($this->eventDispatcher->reveal(), $this->accessManager->reveal());
+ $og_admin_routes_controller = new OgAdminRoutesController($this->accessManager->reveal());
return $og_admin_routes_controller->overview($this->routeMatch->reveal());
}
diff --git a/tests/src/Unit/OgLocalTaskTest.php b/tests/src/Unit/OgLocalTaskTest.php
index e7dd53142..f5d130152 100644
--- a/tests/src/Unit/OgLocalTaskTest.php
+++ b/tests/src/Unit/OgLocalTaskTest.php
@@ -75,7 +75,7 @@ public function testGetDerivativeDefinitions() {
->willReturn($group_map);
foreach (array_keys($group_map) as $entity_type_id) {
- $route_name = "entity.$entity_type_id.og_admin_routes";
+ $route_name = "entity.$entity_type_id.og_admin";
$this
->routeProvider
diff --git a/tests/src/Unit/SubscriptionControllerTest.php b/tests/src/Unit/SubscriptionControllerTest.php
index db2e2e67f..9688a11e8 100644
--- a/tests/src/Unit/SubscriptionControllerTest.php
+++ b/tests/src/Unit/SubscriptionControllerTest.php
@@ -13,6 +13,7 @@
use Drupal\og\OgMembershipInterface;
use Drupal\Tests\UnitTestCase;
use Drupal\user\EntityOwnerInterface;
+use Drupal\Core\Messenger\MessengerInterface;
/**
* Tests the subscription controller.
@@ -71,6 +72,13 @@ class SubscriptionControllerTest extends UnitTestCase {
*/
protected $user;
+ /**
+ * The messenger.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
/**
* {@inheritdoc}
*/
@@ -82,13 +90,14 @@ public function setUp() {
$this->ogMembership = $this->prophesize(OgMembershipInterface::class);
$this->url = $this->prophesize(Url::class);
$this->user = $this->prophesize(AccountInterface::class);
-
+ $this->messenger = $this->prophesize(MessengerInterface::class);
// Set the container for the string translation service.
$container = new ContainerBuilder();
$container->set('current_user', $this->user->reveal());
$container->set('entity.form_builder', $this->entityFormBuilder->reveal());
$container->set('og.membership_manager', $this->membershipManager->reveal());
$container->set('string_translation', $this->getStringTranslationStub());
+ $container->set('messenger', $this->messenger->reveal());
\Drupal::setContainer($container);
}
@@ -255,21 +264,8 @@ public function testGroupManager($state) {
* Invoke the unsubscribe method.
*/
protected function unsubscribe() {
- $controller = new SubscriptionController($this->ogAccess->reveal());
+ $controller = new SubscriptionController($this->ogAccess->reveal(), $this->messenger->reveal());
$controller->unsubscribe($this->group->reveal());
}
}
-
-// @todo Delete after https://www.drupal.org/node/1858196 is in.
-namespace Drupal\og\Controller;
-
-if (!function_exists('drupal_set_message')) {
-
- /**
- * Mocking for drupal_set_message().
- */
- function drupal_set_message() {
- }
-
-}