group-8.x-1.x-dev/src/Hook/FormHooks.php
src/Hook/FormHooks.php
<?php
declare(strict_types=1);
namespace Drupal\group\Hook;
use Drupal\Core\Entity\EntityFormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\Render\Element;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Drupal\group\Entity\GroupInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Form hook implementations for Group.
*/
final class FormHooks {
use StringTranslationTrait;
public function __construct(
protected PrivateTempStoreFactory $privateTempStoreFactory,
protected RequestStack $requestStack,
TranslationInterface $translation,
) {
$this->stringTranslation = $translation;
}
/**
* Implements hook_form_FORM_ID_alter().
*/
#[Hook('form_block_form_alter')]
public function formBlockFormAlter(array &$form, FormStateInterface $form_state, string $form_id): void {
if (isset($form['visibility']['group_type'])) {
$form['visibility_tabs']['#attached']['library'][] = 'group/block';
$form['visibility']['group_type']['#title'] = $this->t('Group types');
$form['visibility']['group_type']['negate']['#type'] = 'value';
$form['visibility']['group_type']['negate']['#title_display'] = 'invisible';
$form['visibility']['group_type']['negate']['#value'] = $form['visibility']['group_type']['negate']['#default_value'];
}
}
/**
* Implements hook_form_alter().
*/
#[Hook('form_alter')]
public function formAlter(array &$form, FormStateInterface $form_state, string $form_id): void {
// GroupRelationshipController::createForm() tends to load entity forms for
// adding entities to a group. We need to add or alter the submit handlers
// of those forms for the process to work properly.
if ($form_state->has('group_wizard') && $form_state->get('group_wizard_id') == 'group_entity') {
if ($wizard = $form_state->get('group_wizard')) {
$store = $this->privateTempStoreFactory->get($form_state->get('group_wizard_id'));
$store_id = $form_state->get('store_id');
// Bail out if we are on step 2 of the wizard. We only want to alter the
// submit handlers for the first step or if we are not in a wizard.
if ($store->get("$store_id:step") === 2) {
return;
}
}
$actions = $form['actions'] ?? [];
foreach (Element::children($actions) as $name) {
// Remove preview button as it redirects back to the wrong form.
if ($name == 'preview') {
unset($form['actions'][$name]);
continue;
}
// Skip buttons without submit handlers.
if (empty($form['actions'][$name]['#submit'])) {
continue;
}
// Skip buttons that do not properly build and save an entity.
$submit = $form['actions'][$name]['#submit'];
if (!in_array('::submitForm', $submit) || !in_array('::save', $submit)) {
continue;
}
// If we are using the wizard, we need to substitute the entity save
// handler in order to add the entity to the private temp store.
if ($wizard) {
foreach ($submit as $key => $handler) {
if ($handler == '::save') {
$form['actions'][$name]['#submit'][$key] = [$this, 'groupRelationshipWizardStore'];
}
}
}
// Otherwise, we can simply add our submit handler and be done with it.
else {
$form['actions'][$name]['#submit'][] = [$this, 'groupRelationshipEntitySubmit'];
}
}
// If we are using the wizard, we also add a cancel button to step 1.
if ($wizard) {
$form['actions']['cancel'] = [
'#type' => 'submit',
'#value' => t('Cancel'),
'#submit' => [[$this, 'groupRelationshipWizardCancel']],
'#limit_validation_errors' => [],
];
}
}
}
/**
* Stores a content entity from the wizard step 1 in the temp store.
*
* @see ::formAlter()
* @see \Drupal\group\Entity\Controller\GroupRelationshipController::createForm()
*/
public function groupRelationshipWizardStore(array $form, FormStateInterface $form_state): void {
$form_object = $form_state->getFormObject();
assert($form_object instanceof EntityFormInterface);
// Store the unsaved entity in the temp store.
$store = $this->privateTempStoreFactory->get($form_state->get('group_wizard_id'));
$store_id = $form_state->get('store_id');
$store->set("$store_id:entity", $form_object->getEntity());
$store->set("$store_id:step", 2);
// Disable any URL-based redirect until the final step.
$request = $this->requestStack->getCurrentRequest();
$form_state->setRedirect('<current>', [], ['query' => $request->query->all()]);
$request->query->remove('destination');
}
/**
* Cancels the wizard for relationship creation.
*
* @see ::formAlter()
* @see \Drupal\group\Entity\Controller\GroupRelationshipController::createForm()
*/
public function groupRelationshipWizardCancel(array $form, FormStateInterface $form_state): void {
$store = $this->privateTempStoreFactory->get($form_state->get('group_wizard_id'));
$store_id = $form_state->get('store_id');
$store->delete("$store_id:entity");
$store->delete("$store_id:step");
$group = $form_state->get('group');
assert($group instanceof GroupInterface);
// Redirect to the group page if no destination was set in the URL.
$form_state->setRedirect('entity.group.canonical', ['group' => $group->id()]);
}
/**
* Adds a newly saved entity to a group.
*
* @see ::formAlter()
* @see \Drupal\group\Entity\Controller\GroupRelationshipController::createForm()
*/
public function groupRelationshipEntitySubmit(array $form, FormStateInterface $form_state): void {
$form_object = $form_state->getFormObject();
assert($form_object instanceof EntityFormInterface);
$entity = $form_object->getEntity();
$group = $form_state->get('group');
assert($group instanceof GroupInterface);
$group->addRelationship($entity, $form_state->get('group_relation'));
// This submit handler is only called when creating new content within a
// group without using the 2-step wizard. We can therefore safely assume the
// user wants to see the entity itself and not the relationship. This only
// applies if there was no "destination" query argument.
if ($entity->access('view')) {
$form_state->setRedirectUrl($entity->toUrl());
}
elseif ($group->access('view')) {
$form_state->setRedirectUrl($group->toUrl());
}
else {
$form_state->setRedirect('<front>');
}
}
}
