contacts-8.x-1.x-dev/modules/group/contacts_group.module
modules/group/contacts_group.module
<?php
/**
* @file
* General hook implementations for Contacts Group.
*/
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\contacts_group\Form\ContactOrgRelationshipForm;
use Drupal\contacts_group\Plugin\Field\ContactsOrgGroupItemList;
use Drupal\contacts_group\Plugin\Validation\Constraint\GroupRelationshipCardinality;
use Drupal\entity\BundleFieldDefinition;
use Drupal\group\Entity\GroupMembershipInterface;
/**
* Implements hook_entity_type_build().
*/
function contacts_group_entity_type_build(array &$entity_types) {
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
if (isset($entity_types['group_relationship'])) {
// Group 3.x.
$entity_types['group_relationship']->setFormClass('contacts-org', ContactOrgRelationshipForm::class);
}
elseif (isset($entity_types['group_content'])) {
// Group 2.x.
$entity_types['group_content']->setFormClass('contacts-org', ContactOrgRelationshipForm::class);
}
}
/**
* Implements hook_entity_base_field_info().
*/
function contacts_group_entity_base_field_info(EntityTypeInterface $entity_type) {
$fields = [];
if ($entity_type->id() == 'user') {
$fields['group'] = BaseFieldDefinition::create('entity_reference')
->setName('group')
->setLabel('Organisation group')
->setComputed(TRUE)
->setSetting('target_type', 'group')
->setClass(ContactsOrgGroupItemList::class);
$fields['organisations'] = BaseFieldDefinition::create('group_membership')
->setName('group_memberships')
->setLabel('Group memberships')
->setComputed(TRUE)
->setCardinality(-1)
->setSetting('target_type', _contacts_group_get_group_relationship_entity_type_id())
->setSetting('group_type', 'contacts_org');
}
return $fields;
}
/**
* Implements hook_entity_field_storage_info().
*/
function contacts_group_entity_field_storage_info(EntityTypeInterface $entity_type) {
$fields = [];
if ($entity_type->id() == 'group') {
$fields['contacts_org'] = BundleFieldDefinition::create('entity_reference')
->setTargetEntityTypeId('group')
->setName('contacts_org')
->setLabel(new TranslatableMarkup('Organisation'))
->setDescription(new TranslatableMarkup('The organisation this group is for.'))
->setSetting('target_type', 'user');
}
return $fields;
}
/**
* Implements hook_entity_bundle_field_info().
*/
function contacts_group_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
$fields = [];
if ($entity_type->id() == 'group' && $bundle == 'contacts_org') {
$fields['contacts_org'] = BundleFieldDefinition::create('entity_reference')
->setTargetEntityTypeId('group')
->setTargetBundle('contacts_org')
->setName('contacts_org')
->setRequired(TRUE)
->setReadOnly(TRUE)
->setLabel(new TranslatableMarkup('Organisation'))
->setDescription(new TranslatableMarkup('The organisation this group is for.'))
->setSetting('target_type', 'user')
->setSetting('handler', 'default:user')
->setSetting('handler_settings', [
'include_anonymous' => FALSE,
'filter' => [
'type' => 'role',
'role' => ['crm_org'],
],
'auto_create' => FALSE,
])
->addConstraint('ContactsUniqueReference', [
'bundle' => TRUE,
])
->setDisplayOptions('form', [
'type' => 'entity_reference_autocomplete',
'weight' => -1,
])
->setDisplayConfigurable('form', FALSE)
->setDisplayConfigurable('view', TRUE);
}
return $fields;
}
/**
* Implements hook_entity_bundle_field_info_alter().
*/
function contacts_group_entity_bundle_field_info_alter(&$fields, EntityTypeInterface $entity_type, $bundle) {
// Support both group 3.x (group_relationship)
// and group 2.x (group_content).
if (($entity_type->id() == 'group_content' || $entity_type->id() == 'group_relationship') && $bundle == 'contacts_org-group_membership') {
// Default user selection applies an additional check on the user's status
// when validating the entity reference, to ensure that users are always
// active. For contacts, we don't want this as we want to be able to add
// inactive users to groups. Do this by overriding the entity reference
// selection handler to use our search_api_user_multiline handler instead.
$fields['entity_id']
->setSetting('handler', 'search_api_user_multiline')
->setSetting('handler_settings', [
'index' => 'contacts_index',
]);
}
}
/**
* Implements hook_form_BASE_FORM_ID_alter() for group_form.
*/
function contacts_group_form_group_form_alter(&$form, FormStateInterface $form_state, $form_id) {
/** @var \Drupal\group\Entity\GroupInterface $group */
$group = $form_state->getFormObject()->getEntity();
if ($group->bundle() == 'contacts_org') {
$form['label']['#access'] = FALSE;
if (!$group->isNew()) {
$form['contacts_org']['widget'][0]['target_id']['#disabled'] = TRUE;
}
$form['#entity_builders'][] = 'contacts_group_form_group_form_contacts_org_entity_builder';
}
}
/**
* Entity builder callback for the group form.
*/
function contacts_group_form_group_form_contacts_org_entity_builder($entity_type_id, EntityInterface $entity, array $form, FormStateInterface $form_state) {
/** @var \Drupal\user\UserInterface|null $organisation */
if ($organisation = $entity->contacts_org->entity) {
$entity->set('label', $organisation->label());
}
}
/**
* Implements hook_entity_field_access().
*/
function contacts_group_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, ?FieldItemListInterface $items = NULL) {
// Field access for organisation memberships.
// Support both group 2.x (group_content)
// and group 3.x (group_relationship).
if (($field_definition->getTargetEntityTypeId() == 'group_content' || $field_definition->getTargetEntityTypeId() == 'group_relationship') && $field_definition->getTargetBundle() == 'contacts_org-group_membership') {
// Deny access for all operations for roles and job title unless we know
// we're dealing with an individual member. Organisation members don't have
// roles or titles.
$field_names = ['indiv_role', 'group_roles', 'job_title'];
if (in_array($field_definition->getName(), $field_names)) {
if ($items) {
/** @var \Drupal\user\UserInterface $member */
$member = $items->getEntity()->getEntity();
return AccessResult::forbiddenIf(!$member || !$member->hasRole('crm_indiv'));
}
}
}
return AccessResult::neutral();
}
/**
* Implements hook_validation_constraint_alter().
*/
function contacts_group_validation_constraint_alter(array &$definitions) {
$definitions['GroupRelationshipCardinality']['class'] = GroupRelationshipCardinality::class;
}
/**
* Implements hook_preprocess_HOOK() for block.
*/
function contacts_group_preprocess_block(&$variables) {
// The title still shows for the relationship view, even if argument
// validation should hide the view.
if ($variables['base_plugin_id'] == 'views_block' && substr($variables['derivative_plugin_id'], 0, 13) == 'contacts_orgs') {
if (!isset($variables['content']['#view'])) {
unset($variables['label']);
}
}
}
/**
* Implements hook_block_alter().
*
* Declare the membership enabler relationship block under the old machine name.
*/
function contacts_group_block_alter(&$definitions) {
if (isset($definitions['contacts_org_relationship_form:group_membership'])) {
$definitions['contacts_org_relationship_form'] = $definitions['contacts_org_relationship_form:group_membership'];
}
}
/**
* Implements hook_form_BASE_ID_alter() for group_relationship_confirm_form.
*
* This is the Group 3.x version of the hook.
*/
function contacts_group_form_group_relationship_confirm_form_alter(array &$form, FormStateInterface $form_state, string $form_id) {
// Just delegate to the group 2.x version of the hook.
contacts_group_form_group_content_confirm_form_alter($form, $form_state, $form_id);
}
/**
* Implements hook_form_BASE_ID_alter().
*
* This is the Group 2.x version of the hook.
*/
function contacts_group_form_group_content_confirm_form_alter(array &$form, FormStateInterface $form_state, string $form_id) {
// If this member is a group admin, prevent removal unless there is at least
// one more.
$form_ids = [
'group_content_contacts_org-group_membership_delete_form',
'group_relationship_contacts_org-group_membership_delete_form',
];
if (!in_array($form_id, $form_ids)) {
return;
}
/** @var \Drupal\group\Entity\GroupRelationshipInterface $relationship */
$relationship = $form_state->getFormObject()->getEntity();
$group = $relationship->getGroup();
if (!($relationship instanceof GroupMembershipInterface)) {
return;
}
$form['#title'] = new TranslatableMarkup(
'Are you sure you want to remove %indiv from %org?',
[
'%indiv' => $relationship->label(),
'%org' => $group->label(),
]
);
$form['actions']['submit']['#value'] = new TranslatableMarkup('Remove');
if (!in_array('contacts_org-admin', array_keys($relationship->getRoles()))) {
return;
}
$admin_members = $group->getMembers(['admin']);
if (count($admin_members) > 1) {
return;
}
$form['description']['#markup'] = new TranslatableMarkup(
'You cannot remove the only administrator of %org. Please add a new administrator first.',
['%org' => $group->label()]
);
$form['actions']['submit']['#access'] = FALSE;
}
/**
* Implements hook_link_alter().
*/
function contacts_group_link_alter(&$variables) {
if (($variables['options']['attributes']['target'] ?? NULL) == '_contacts_modal') {
unset($variables['options']['attributes']['target']);
$variables['options']['attributes'] += [
'data-dialog-type' => 'modal',
];
$variables['options']['attributes']['class'][] = 'use-ajax';
// Set a redirect URL if there isn't one.
if (empty($variables['options']['query']['destination'])) {
$variables['options']['query']['destination'] = Url::fromRoute('<current>')->toString();
}
}
}
/**
* Implements hook_ENTITY_TYPE_update() for profile.
*/
function contacts_group_profile_update(EntityInterface $entity) {
// When an organisation is renamed, ensure that the group is renamed too.
if ($entity->bundle() === 'crm_org' && $entity->hasField('crm_org_name')) {
/** @var \Drupal\user\UserInterface $user */
$user = $entity->getOwner();
if ($user->hasField('group')) {
/** @var \Drupal\group\Entity\Group $group */
$group = $user->group->entity;
if ($group && $group->bundle() === 'contacts_org') {
if ($group->label() !== $entity->get('crm_org_name')->value) {
$group->set('label', $entity->get('crm_org_name')->value);
$group->save();
}
}
}
}
}
/**
* Gets the entity type ID of the group_relationship entity type.
*
* This is different depending on whether we're running on group 2.x
* (group_content) or group 3.x (group_relationship).
*
* @return string|null
* Entity type id, or null if group isn't installed.
*/
function _contacts_group_get_group_relationship_entity_type_id(): ?string {
/** @var \Drupal\Core\Extension\ModuleExtensionList $module_extension_list */
$module_extension_list = \Drupal::service('extension.list.module');
$group_version = $module_extension_list->getExtensionInfo('group')['version'] ?? '0';
if (version_compare($group_version, '2.0.0', '>=') && version_compare($group_version, '3.0.0', '<')) {
return 'group_content';
}
if (version_compare($group_version, '3.0.0', '>=')) {
return 'group_relationship';
}
return NULL;
}
