og-8.x-1.x-dev/src/Plugin/EntityReferenceSelection/OgSelection.php
src/Plugin/EntityReferenceSelection/OgSelection.php
<?php
declare(strict_types=1);
namespace Drupal\og\Plugin\EntityReferenceSelection;
use Drupal\Core\Entity\Attribute\EntityReferenceSelection;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\og\MembershipManagerInterface;
use Drupal\og\Og;
use Drupal\og\OgAccessInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Provide default OG selection handler.
*
* Note that the id is correctly defined as "og:default" and not the other way
* around, as seen in most other default selection handler (e.g. "default:node")
* as OG's selection handler is a wrapper around those entity specific default
* ones. That is, the same selection handler will be returned no matter what is
* the target type of the reference field. Internally, it will call the original
* selection handler, and use it for building the queries.
*/
#[EntityReferenceSelection(
id: 'og:default',
label: new TranslatableMarkup('OG selection'),
group: 'og',
weight: 1,
)]
class OgSelection extends DefaultSelection {
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
EntityTypeManagerInterface $entity_type_manager,
ModuleHandlerInterface $module_handler,
AccountInterface $current_user,
EntityFieldManagerInterface $entity_field_manager,
EntityTypeBundleInfoInterface $entity_type_bundle_info,
EntityRepositoryInterface $entity_repository,
protected SelectionPluginManagerInterface $selectionManager,
protected OgAccessInterface $ogAccess,
protected MembershipManagerInterface $membershipManager,
protected ?Request $request,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $module_handler, $current_user, $entity_field_manager, $entity_type_bundle_info, $entity_repository);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('module_handler'),
$container->get('current_user'),
$container->get('entity_field.manager'),
$container->get('entity_type.bundle.info'),
$container->get('entity.repository'),
$container->get('plugin.manager.entity_reference_selection'),
$container->get('og.access'),
$container->get('og.membership_manager'),
$container->get('request_stack')->getCurrentRequest(),
);
}
/**
* Get the selection handler of the field.
*
* @return \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface
* Returns the selection handler.
*/
public function getSelectionHandler(): SelectionInterface {
$options = $this->getConfiguration();
// The 'handler' key intentionally absent as we want the selection manager
// to choose the best option.
// @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager::getInstance()
unset($options['handler']);
// Remove also the backwards compatibility layer because that will be passed
// to the chosen selection handler setter and, as an effect, will trigger a
// deprecation notice.
// @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginBase::resolveBackwardCompatibilityConfiguration()
unset($options['handler_settings']);
$plugin = $this->selectionManager->getInstance($options);
assert($plugin instanceof SelectionInterface);
return $plugin;
}
/**
* Overrides ::buildEntityQuery.
*
* Return only group in the matching results.
*
* @param string|null $match
* (Optional) Text to match the label against. Defaults to NULL.
* @param string $match_operator
* (Optional) The operation the matching should be done with. Defaults
* to "CONTAINS".
*
* @return \Drupal\Core\Entity\Query\QueryInterface
* The EntityQuery object with the basic conditions and sorting applied to
* it.
*/
protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
// Getting the original entity selection handler. OG selection handler using
// the default selection handler of the entity, which the field reference
// to, and add another logic to the query object i.e. check if the entities
// bundle defined as group.
$query = $this->getSelectionHandler()->buildEntityQuery($match, $match_operator);
$target_type = $this->configuration['target_type'];
$definition = $this->entityTypeManager->getDefinition($target_type);
if ($bundle_key = $definition->getKey('bundle')) {
$group_bundle_ids = Og::groupTypeManager()->getGroupMap()[$target_type];
if ($group_bundle_ids) {
$query->condition($bundle_key, $group_bundle_ids, 'IN');
}
}
if ($this->currentUser->hasPermission('administer organic groups')) {
// User can see all the groups.
return $query;
}
$entity = $this->configuration['entity'] ?? NULL;
if (!$entity && $this->request instanceof Request) {
// Get the autocomplete query parameters from the entity 'add' form to
// build a lightweight entity stub.
$entity_type_id = $this->request->query->get('entity_type');
if (is_string($entity_type_id) && !empty($entity_type_id)) {
$storage = $this->entityTypeManager->getStorage($entity_type_id);
$entity_type = $storage->getEntityType();
$values = [];
$bundle = $this->request->query->get('bundle');
if (is_string($bundle) && !empty($bundle) && $entity_type->hasKey('bundle')) {
$values[$entity_type->getKey('bundle')] = $bundle;
}
$entity = $storage->create($values);
$this->configuration['entity'] = $entity;
}
}
// We still don't have a group content entity.
if (empty($this->configuration['entity'])) {
throw new \Exception('No group content entity to assess access from groups.');
}
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = $this->configuration['entity'];
$ids = [];
foreach ($this->getUserGroups() as $group) {
// Check if the current user can perform the "create" operation on this
// entity.
$access_result = $this->ogAccess->userAccessGroupContentEntityOperation('create', $group, $entity, $this->currentUser);
if ($access_result->isAllowed()) {
$ids[] = $group->id();
}
}
// Get the identifier key of the entity.
$identifier_key = $definition->getKey('id');
if ($ids) {
$query->condition($identifier_key, $ids, 'IN');
}
else {
// User doesn't have permission to select any group so falsify this
// query.
$query->condition($identifier_key, -1);
}
return $query;
}
/**
* Return all the user's groups.
*
* @return \Drupal\Core\Entity\ContentEntityInterface[]
* Array with the user's group, or an empty array if none found.
*/
protected function getUserGroups() {
$other_groups = $this->membershipManager->getUserGroups($this->currentUser->id());
return $other_groups[$this->configuration['target_type']] ?? [];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
// Remove creation of entities that don't exist.
unset($form['auto_create']);
// Filter out the bundles that are not groups.
$entity_type_id = $this->configuration['target_type'];
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
$bundles_info = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
if ($entity_type->hasKey('bundle')) {
$group_bundle_ids = Og::groupTypeManager()->getGroupMap()[$entity_type_id];
$bundle_options = [];
foreach ($group_bundle_ids as $group_bundle_id) {
$bundle_options[$group_bundle_id] = $bundles_info[$group_bundle_id]['label'];
}
natsort($bundle_options);
$form['target_bundles']['#options'] = $bundle_options;
// Ensure the target bundles aren't required.
$form['target_bundles']['#required'] = FALSE;
$form['target_bundles']['#description'] = new TranslatableMarkup('The bundles of the entity type that can be referenced. Optional, leave empty for all bundles.');
}
return $form;
}
}
