Issue #3354935: Configurable quick forms

This commit is contained in:
Michael Stenta 2023-10-06 14:13:03 -04:00
commit 6ac77f8106
37 changed files with 1418 additions and 162 deletions

View File

@ -12,11 +12,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [farmOS v2 CSV import module #722](https://github.com/farmOS/farmOS/pull/722)
- [Add a Group membership assignment quick form #723](https://github.com/farmOS/farmOS/pull/723)
- [farmOS Setup Menu #706](https://github.com/farmOS/farmOS/pull/706)
- [Issue #3354935: Configurable quick forms](https://www.drupal.org/project/farm/issues/3354935)
### Changed
- [Misc quick form code and documentation improvements #703](https://github.com/farmOS/farmOS/pull/703)
### Deprecated
- [QuickFormInterface::getId() is replaced by QuickFormInterface::getQuickId()](https://www.drupal.org/node/3379686)
### Fixed
- [Validate quantity entities created by create_quantity #721](https://github.com/farmOS/farmOS/pull/721)

View File

@ -222,6 +222,225 @@ Available traits and the methods that they provide include:
given a name and vocabulary. If the term does not exist, a new term will be
created.
## Configurable quick forms
A "configurable" quick form is one that allows users to change how the quick
form behaves, by providing settings and a configuration form for customizing
them.
To make an existing quick form configurable:
1. Add `implements ConfigurableQuickFormInterface` to the quick form's class
definition. This indicates to farmOS that the quick form is configurable,
builds a router item for the configuration form, adds it to the UI, etc.
2. Add `use ConfigurableQuickFormTrait` to the quick form's class definition.
This adds default methods required by the `ConfigurableQuickFormInterface`.
3. Add a `defaultConfiguration()` method that returns an array of default
configuration values.
4. Add a `buildConfigurationForm()` method that builds a configuration form
with form items for each of the properties defined in
`defaultConfiguration()`.
5. Add a `submitConfigurationForm()` method that processes submitted values and
assigns configuration to `$this->configuration`.
6. Add a `config/schema/[mymodule].schema.yml` file that describes the
[configuration schema/metatdata](https://www.drupal.org/docs/drupal-apis/configuration-api/configuration-schemametadata).
The following is the same "Harvest" example as above, with the new interface
and methods, followed by the schema file that describes the settings.
```php
<?php
namespace Drupal\farm_quick_harvest\Plugin\QuickForm;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Form\FormStateInterface;
use Drupal\farm_quick\Plugin\QuickForm\ConfigurableQuickFormInterface;
use Drupal\farm_quick\Plugin\QuickForm\QuickFormBase;
use Drupal\farm_quick\Traits\ConfigurableQuickFormTrait;
use Drupal\farm_quick\Traits\QuickLogTrait;
/**
* Harvest quick form.
*
* @QuickForm(
* id = "harvest",
* label = @Translation("Harvest"),
* description = @Translation("Record when a harvest takes place."),
* helpText = @Translation("Use this form to record a harvest."),
* permissions = {
* "create harvest log",
* }
* )
*/
class Harvest extends QuickFormBase implements ConfigurableQuickFormInterface {
use ConfigurableQuickFormTrait;
use QuickLogTrait;
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'default_quantity' => 100,
];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, string $id = NULL) {
// Date+time selection field (defaults to now).
$form['timestamp'] = [
'#type' => 'datetime',
'#title' => $this->t('Date'),
'#default_value' => new DrupalDateTime('now', \Drupal::currentUser()->getTimeZone()),
'#required' => TRUE,
];
// Asset reference field (allow multiple).
$form['asset'] = [
'#type' => 'entity_autocomplete',
'#title' => $this->t('Assets'),
'#target_type' => 'asset',
'#tags' => TRUE,
];
// Harvest quantity field.
$form['quantity'] = [
'#type' => 'number',
'#title' => $this->t('Quantity'),
'#required' => TRUE,
'#default_value' => $this->configuration['default_quantity'],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Draft a harvest log from the user-submitted data.
$timestamp = $form_state->getValue('timestamp')->getTimestamp();
$asset = $form_state->getValue('asset');
$quantity = $form_state->getValue('quantity');
$log = [
'type' => 'harvest',
'timestamp' => $timestamp,
'asset' => $asset,
'quantity' => [
[
'type' => 'standard',
'value' => $quantity,
],
],
'status' => 'done',
];
// Create the log.
$this->createLog($log);
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
// Default quantity configuration.
$form['default_quantity'] = [
'#type' => 'number',
'#title' => $this->t('Default quantity'),
'#default_value' => $this->configuration['default_quantity'],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['default_quantity'] = $form_state->getValue('default_quantity');
}
}
```
`config/schema/farm_quick_harvest.schema.yml`:
```yaml
farm_quick.settings.harvest:
type: quick_form_settings
label: 'Harvest quick form settings'
mapping:
default_quantity:
type: integer
label: 'Default quantity'
```
### Methods
The `ConfigurableQuickFormTrait` class add all the necessary methods required
by `ConfigurableQuickFormInterface` (which is used to designate a quick form as
"configurable"). Child classes can override these methods to customize their
behavior. At a minimum, most configurable quick form classes should override
`defaultConfiguration()`, `buildConfigurationForm()`, and
`submitConfigurationForm()`.
Available methods include:
- `defaultConfiguration()` - Provide an array of default configuration values.
- `buildConfigurationForm()` - Build the configuration form as an array using
[Drupal Form API](https://www.drupal.org/docs/drupal-apis/form-api/introduction-to-form-api).
- `validateConfigurationForm()` - Perform validation on the user input.
- `submitConfigurationForm()` - Perform logic when the form is submitted to
prepare the quick form configuration entity. This will not run if validation
fails.
## Quick form configuration entities
Each quick form that is displayed to the user in farmOS is represented as a
[configuration entity](https://www.drupal.org/docs/drupal-apis/entity-api/configuration-entity).
Each configuration entity specifies which quick form plugin it uses (aka which
PHP class that extends from `QuickFormBase`), along with other information like
label, description, help text, and configuration settings (used by configurable
quick forms).
However, if a configuration entity is not saved, farmOS will try to provide a
"default instance" of the quick form plugin. From a module developer's
perspective, this means that the module does not need to provide any config
entity YML files in `config/install`. It can rely on farmOS's default quick
form instance logic to show the quick form.
In the case of configurable quick forms, a config entity will be automatically
created when the user modifies the quick form's configuration and submits the
configuration form.
Quick form configuration entities can also be used to override defaults,
including the label, description, and help text. They can also be used to
disable a quick form entirely by setting the config entity's `status` to
false.
If multiple configuration entities are provided for the same plugin, multiple
quick forms will be displayed in the UI. This is useful if you want to create
a set of similar quick forms with pre-set configuration options.
### Disable default instance
In some cases, a plugin may not want a "default instance" to be created.
Instead, they may want to require that a quick form configuration entity be
explicitly created. For example, if a plugin requires configuration settings,
but there isn't a sensible default for that configuration and user input is
required, a "default instance" may not be possible.
In that case, the plugin can add `requiresEntity = True` to its annotation,
which will tell farmOS not to create a default instance of the quick form.
The quick form will only be made available if a configuration entity is saved.
## Quick form actions
farmOS provides lists of logs and assets throughout its interface. Many of

View File

@ -0,0 +1,4 @@
# Quick form settings data type for quick form configuration entity.
quick_form_settings:
type: mapping
label: 'Quick form settings'

View File

@ -0,0 +1,25 @@
# Schema for quick form instance configuration entities.
farm_quick.quick_form.*:
type: config_entity
label: 'Quick form instance'
mapping:
id:
type: string
label: 'Machine-readable name'
plugin:
type: string
label: 'Plugin'
label:
type: label
label: 'Label'
description:
type: text
label: 'Description'
helpText:
type: text
label: 'Help text'
settings:
type: farm_quick.settings.[%parent.plugin]
farm_quick.settings.*:
type: quick_form_settings

View File

@ -4,6 +4,7 @@ type: module
package: farmOS
core_version_requirement: ^9
dependencies:
- entity:entity
- drupal:taxonomy
- farm:asset
- farm:farm_log_quantity

View File

@ -0,0 +1,9 @@
farm_quick.quick_form:
title: Quick Form
route_name: farm_quick.quick_form
base_route: farm_quick.quick_form
farm_quick.configure:
title: Configure
route_name: farm_quick.configure
base_route: farm_quick.quick_form

View File

@ -1,3 +1,5 @@
farm_quick:
config_permissions:
- update quick_form
default_permissions:
- view quick forms index
- view quick_form

View File

@ -20,12 +20,9 @@ function farm_quick_help($route_name, RouteMatchInterface $route_match) {
}
// Load help text for individual quick forms.
if (strpos($route_name, 'farm.quick.') === 0) {
$quick_form_id = $route_match->getParameter('id');
if ($route_name == 'farm.quick.' . $quick_form_id) {
$quick_form = \Drupal::service('plugin.manager.quick_form')->createInstance($quick_form_id);
$help_text = $quick_form->getHelpText();
if (!empty($help_text)) {
if ($route_name == 'farm_quick.quick_form') {
if (($quick_form_id = $route_match->getParameter('quick_form')) && $quick_form = \Drupal::service('quick_form.instance_manager')->getInstance($quick_form_id)) {
if ($help_text = $quick_form->getHelpText()) {
$output .= '<p>' . $help_text . '</p>';
}
}

View File

@ -1,2 +0,0 @@
view quick forms index:
title: 'View quick forms index'

View File

@ -4,7 +4,28 @@ farm.quick:
_controller: '\Drupal\farm_quick\Controller\QuickFormController::index'
_title: 'Quick forms'
requirements:
_permission: 'view quick forms index'
_permission: 'view quick_form'
route_callbacks:
- '\Drupal\farm_quick\Routing\QuickFormRoutes::routes'
farm_quick.quick_form:
path: /quick/{quick_form}
defaults:
_form: \Drupal\farm_quick\Form\QuickForm
_title_callback: \Drupal\farm_quick\Form\QuickForm::getTitle
requirements:
_custom_access: \Drupal\farm_quick\Form\QuickForm::access
options:
parameters:
quick_form:
type: string
farm_quick.configure:
path: /quick/{quick_form}/configure
defaults:
_entity_form: quick_form.configure
_title_callback: \Drupal\farm_quick\Form\ConfigureQuickForm::getTitle
requirements:
_custom_access: \Drupal\farm_quick\Form\ConfigureQuickForm::access
options:
parameters:
quick_form:
type: string

View File

@ -1,4 +1,8 @@
services:
plugin.manager.quick_form:
class: Drupal\farm_quick\QuickFormManager
class: Drupal\farm_quick\QuickFormPluginManager
parent: default_plugin_manager
quick_form.instance_manager:
class: Drupal\farm_quick\QuickFormInstanceManager
arguments:
['@entity_type.manager', '@plugin.manager.quick_form']

View File

@ -52,4 +52,11 @@ class QuickForm extends Plugin {
*/
public $permissions;
/**
* Require a quick form instance entity to instantiate.
*
* @var bool
*/
public $requiresEntity;
}

View File

@ -2,10 +2,11 @@
namespace Drupal\farm_quick\Controller;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\farm_quick\QuickFormManager;
use Drupal\farm_quick\QuickFormInstanceManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@ -16,17 +17,20 @@ class QuickFormController extends ControllerBase {
use StringTranslationTrait;
/**
* The quick form manager.
* The quick form instance manager.
*
* @var \Drupal\farm_quick\QuickFormManager
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
*/
protected $quickFormManager;
protected $quickFormInstanceManager;
/**
* Quick form controller constructor.
*
* @param \Drupal\farm_quick\QuickFormInstanceManagerInterface $quick_form_instance_manager
* The quick form instance manager.
*/
public function __construct(QuickFormManager $quick_form_manager) {
$this->quickFormManager = $quick_form_manager;
public function __construct(QuickFormInstanceManagerInterface $quick_form_instance_manager) {
$this->quickFormInstanceManager = $quick_form_instance_manager;
}
/**
@ -34,7 +38,7 @@ class QuickFormController extends ControllerBase {
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.quick_form'),
$container->get('quick_form.instance_manager'),
);
}
@ -45,14 +49,22 @@ class QuickFormController extends ControllerBase {
* Returns a render array.
*/
public function index(): array {
$quick_forms = $this->quickFormManager->getDefinitions();
// Start cacheability object with quick form config entity list tag.
$cacheability = new CacheableMetadata();
$cacheability->addCacheTags($this->entityTypeManager()->getStorage('quick_form')->getEntityType()->getListCacheTags());
// Build list item for each quick form.
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface[] $quick_forms */
$quick_forms = $this->quickFormInstanceManager->getInstances();
$items = [];
foreach ($quick_forms as $quick_form) {
$url = Url::fromRoute('farm.quick.' . $quick_form['id']);
foreach ($quick_forms as $id => $quick_form) {
$url = Url::fromRoute('farm_quick.quick_form', ['quick_form' => $id]);
if ($url->access()) {
$cacheability->addCacheableDependency($quick_form);
$items[] = [
'title' => $quick_form['label'],
'description' => $quick_form['description'],
'title' => $quick_form->getLabel(),
'description' => $quick_form->getDescription(),
'url' => $url,
];
}
@ -68,6 +80,7 @@ class QuickFormController extends ControllerBase {
'#markup' => $this->t('You do not have any quick forms.'),
];
}
$cacheability->applyTo($output);
return $output;
}

View File

@ -0,0 +1,178 @@
<?php
namespace Drupal\farm_quick\Entity;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityWithPluginCollectionInterface;
use Drupal\farm_quick\QuickFormPluginCollection;
/**
* Defines the quick form instance config entity.
*
* @ConfigEntityType(
* id = "quick_form",
* label = @Translation("Quick form"),
* label_collection = @Translation("Quick forms"),
* label_singular = @Translation("quick form"),
* label_plural = @Translation("quick forms"),
* label_count = @PluralTranslation(
* singular = "@count quick form",
* plural = "@count quick forms",
* ),
* handlers = {
* "access" = "\Drupal\entity\EntityAccessControlHandler",
* "permission_provider" = "\Drupal\entity\EntityPermissionProvider",
* "form" = {
* "configure" = "Drupal\farm_quick\Form\ConfigureQuickForm",
* },
* },
* entity_keys = {
* "id" = "id",
* "status" = "status"
* },
* config_export = {
* "id",
* "plugin",
* "label",
* "description",
* "helpText",
* "settings",
* },
* )
*/
class QuickFormInstance extends ConfigEntityBase implements QuickFormInstanceInterface, EntityWithPluginCollectionInterface {
/**
* The ID of the quick form instance.
*
* @var string
*/
protected $id;
/**
* The plugin instance ID.
*
* @var string
*/
protected $plugin;
/**
* The plugin collection that holds the quick form plugin for this entity.
*
* @var \Drupal\farm_quick\QuickFormPluginCollection
*/
protected $pluginCollection;
/**
* The quick form label.
*
* @var string
*/
protected $label;
/**
* A brief description of the quick form.
*
* @var string
*/
protected $description;
/**
* Help text for the quick form.
*
* @var string
*/
protected $helpText;
/**
* The plugin instance settings.
*
* @var array
*/
protected $settings = [];
/**
* {@inheritdoc}
*/
public function getPlugin() {
return $this->getPluginCollection()->get($this->plugin);
}
/**
* Encapsulates the creation of the farm_quick's plugin collection.
*
* @return \Drupal\Component\Plugin\LazyPluginCollection
* The block's plugin collection.
*/
protected function getPluginCollection() {
if (!$this->pluginCollection) {
$this->pluginCollection = new QuickFormPluginCollection(\Drupal::service('plugin.manager.quick_form'), $this->plugin, $this->get('settings'), $this->id());
}
return $this->pluginCollection;
}
/**
* {@inheritdoc}
*/
public function getPluginCollections() {
return [
'settings' => $this->getPluginCollection(),
];
}
/**
* {@inheritdoc}
*/
public function getPluginId() {
return $this->plugin;
}
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->label;
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->description;
}
/**
* {@inheritdoc}
*/
public function getHelpText() {
return $this->helpText;
}
/**
* {@inheritdoc}
*/
public function getSettings() {
return $this->settings;
}
/**
* {@inheritdoc}
*/
public static function preCreate(EntityStorageInterface $storage, array &$values) {
parent::preCreate($storage, $values);
/** @var \Drupal\farm_quick\QuickFormPluginManager $quick_form_plugin_manager */
$quick_form_plugin_manager = \Drupal::service('plugin.manager.quick_form');
// If the plugin is set use the default label, description and helpText.
if (isset($values['plugin']) && $plugin = $quick_form_plugin_manager->getDefinition($values['plugin'], FALSE)) {
foreach (['label', 'description', 'helpText'] as $field) {
if (!isset($values[$field])) {
$values[$field] = $plugin[$field];
}
}
}
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace Drupal\farm_quick\Entity;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
/**
* Provides an interface for defining quick form instance config entities.
*/
interface QuickFormInstanceInterface extends ConfigEntityInterface {
/**
* Returns the plugin instance.
*
* @return \Drupal\farm_quick\Plugin\QuickForm\QuickFormInterface
* The plugin instance for this quick form.
*/
public function getPlugin();
/**
* Returns the plugin ID.
*
* @return string
* The plugin ID for this quick form.
*/
public function getPluginId();
/**
* Returns the quick form label.
*
* @return string
* The label for this quick form.
*/
public function getLabel();
/**
* Returns the quick form description.
*
* @return string
* The description for this quick form.
*/
public function getDescription();
/**
* Returns the quick form help text.
*
* @return string
* The help text for this quick form.
*/
public function getHelpText();
/**
* Returns the quick form settings.
*
* @return array
* An associative array of settings.
*/
public function getSettings();
}

View File

@ -0,0 +1,159 @@
<?php
namespace Drupal\farm_quick\Form;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\farm_quick\Plugin\QuickForm\ConfigurableQuickFormInterface;
use Drupal\farm_quick\QuickFormInstanceManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
/**
* Form that renders quick form configuration forms.
*
* @ingroup farm
*/
class ConfigureQuickForm extends EntityForm {
/**
* The entity being used by this form.
*
* @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface
*/
protected $entity;
/**
* The quick form instance manager.
*
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
*/
protected $quickFormInstanceManager;
/**
* Class constructor.
*
* @param \Drupal\farm_quick\QuickFormInstanceManagerInterface $quick_form_instance_manager
* The quick form instance manager.
*/
public function __construct(QuickFormInstanceManagerInterface $quick_form_instance_manager) {
$this->quickFormInstanceManager = $quick_form_instance_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('quick_form.instance_manager'),
);
}
/**
* Get the title of the quick form.
*
* @param string $quick_form
* The quick form ID.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
* Quick form title.
*/
public function getTitle(string $quick_form) {
$quick_form_title = NULL;
if ($quick_form = $this->getQuickFormInstance($quick_form)) {
$quick_form_title = $quick_form->getLabel();
}
return $this->t('Configure @quick_form', ['@quick_form' => $quick_form_title]);
}
/**
* Checks access for configuration of a specific quick form.
*
* @param \Drupal\Core\Session\AccountInterface $account
* Run access checks for this account.
* @param string|null $quick_form
* The quick form ID.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access(AccountInterface $account, string $quick_form = NULL) {
// Get a quick form config entity.
if ($quick_form !== NULL) {
$quick_form = $this->getQuickFormInstance($quick_form);
}
// Raise 404 if no quick form exists. This is the case with a quick form
// ID that is not a valid quick form plugin ID.
if ($quick_form === NULL) {
throw new ResourceNotFoundException();
}
// Deny access if the quick form plugin is not configurable.
if (!$quick_form->getPlugin() instanceof ConfigurableQuickFormInterface) {
return AccessResult::forbidden();
}
// Check the update quick_form permission.
$configure_form_access = AccessResult::allowedIfHasPermissions($account, ['update quick_form']);
return $quick_form->getPlugin()->access($account)->andIf($configure_form_access);
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form = parent::form($form, $form_state);
$form['settings'] = [
'#tree' => TRUE,
];
$form['settings'] = $this->entity->getPlugin()->buildConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
$this->entity->getPlugin()->validateConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
$this->entity->getPlugin()->submitConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
}
/**
* {@inheritdoc}
*/
public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entity_type_id) {
$entity = NULL;
if ($route_match->getRawParameter($entity_type_id) !== NULL) {
$entity = $this->getQuickFormInstance($route_match->getParameter($entity_type_id));
}
return $entity;
}
/**
* Helper function to get a quick form instance.
*
* @param string $quick_form_id
* The quick form ID.
*
* @return \Drupal\farm_quick\Entity\QuickFormInstanceInterface|null
* The quick form instance or NULL if does not exist.
*/
protected function getQuickFormInstance(string $quick_form_id) {
return $this->quickFormInstanceManager->getInstance($quick_form_id);
}
}

View File

@ -6,8 +6,9 @@ use Drupal\Core\Form\BaseFormIdInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\farm_quick\QuickFormManager;
use Drupal\farm_quick\QuickFormInstanceManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
/**
* Form that renders quick forms.
@ -17,11 +18,11 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
class QuickForm extends FormBase implements BaseFormIdInterface {
/**
* The quick form manager.
* The quick form instance manager.
*
* @var \Drupal\farm_quick\QuickFormManager
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
*/
protected $quickFormManager;
protected $quickFormInstanceManager;
/**
* The quick form ID.
@ -33,11 +34,11 @@ class QuickForm extends FormBase implements BaseFormIdInterface {
/**
* Class constructor.
*
* @param \Drupal\farm_quick\QuickFormManager $quick_form_manager
* The quick form manager.
* @param \Drupal\farm_quick\QuickFormInstanceManagerInterface $quick_form_instance_manager
* The quick form instance manager.
*/
public function __construct(QuickFormManager $quick_form_manager) {
$this->quickFormManager = $quick_form_manager;
public function __construct(QuickFormInstanceManagerInterface $quick_form_instance_manager) {
$this->quickFormInstanceManager = $quick_form_instance_manager;
}
/**
@ -45,7 +46,7 @@ class QuickForm extends FormBase implements BaseFormIdInterface {
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.quick_form')
$container->get('quick_form.instance_manager'),
);
}
@ -61,9 +62,9 @@ class QuickForm extends FormBase implements BaseFormIdInterface {
*/
public function getFormId() {
$form_id = $this->getBaseFormId();
$id = $this->getRouteMatch()->getParameter('id');
$id = $this->getRouteMatch()->getParameter('quick_form');
if (!is_null($id)) {
$form_id .= '_' . $this->quickFormManager->createInstance($id)->getFormId();
$form_id .= '_' . $this->quickFormInstanceManager->getInstance($id)->getPlugin()->getFormId();
}
return $form_id;
}
@ -71,14 +72,14 @@ class QuickForm extends FormBase implements BaseFormIdInterface {
/**
* Get the title of the quick form.
*
* @param string $id
* @param string $quick_form
* The quick form ID.
*
* @return string
* Quick form title.
*/
public function getTitle(string $id) {
return $this->quickFormManager->createInstance($id)->getLabel();
public function getTitle(string $quick_form) {
return $this->quickFormInstanceManager->getInstance($quick_form)->getLabel();
}
/**
@ -86,27 +87,31 @@ class QuickForm extends FormBase implements BaseFormIdInterface {
*
* @param \Drupal\Core\Session\AccountInterface $account
* Run access checks for this account.
* @param string $id
* @param string $quick_form
* The quick form ID.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access(AccountInterface $account, string $id) {
return $this->quickFormManager->createInstance($id)->access($account);
public function access(AccountInterface $account, string $quick_form) {
if ($quick_form = $this->quickFormInstanceManager->getInstance($quick_form)) {
return $quick_form->getPlugin()->access($account);
}
// Raise 404 if the quick form does not exist.
throw new ResourceNotFoundException();
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $id = NULL) {
public function buildForm(array $form, FormStateInterface $form_state, $quick_form = NULL) {
// Save the quick form ID.
$this->quickFormId = $id;
$this->quickFormId = $quick_form;
// Load the quick form.
$quick_form = $this->quickFormManager->createInstance($id);
$form = $quick_form->buildForm($form, $form_state);
$form = $this->quickFormInstanceManager->getInstance($quick_form)->getPlugin()->buildForm($form, $form_state);
// Add a submit button, if one wasn't provided.
if (empty($form['actions']['submit'])) {
@ -127,14 +132,14 @@ class QuickForm extends FormBase implements BaseFormIdInterface {
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$this->quickFormManager->createInstance($this->quickFormId)->validateForm($form, $form_state);
$this->quickFormInstanceManager->getInstance($this->quickFormId)->getPlugin()->validateForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->quickFormManager->createInstance($this->quickFormId)->submitForm($form, $form_state);
$this->quickFormInstanceManager->getInstance($this->quickFormId)->getPlugin()->submitForm($form, $form_state);
}
}

View File

@ -4,7 +4,7 @@ namespace Drupal\farm_quick\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\farm_quick\QuickFormManager;
use Drupal\farm_quick\QuickFormInstanceManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@ -13,20 +13,20 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
class QuickFormMenuLink extends DeriverBase implements ContainerDeriverInterface {
/**
* The quick form manager.
* The quick form instance manager.
*
* @var \Drupal\farm_quick\QuickFormManager
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
*/
protected $quickFormManager;
protected $quickFormInstanceManager;
/**
* FarmQuickMenuLink constructor.
*
* @param \Drupal\farm_quick\QuickFormManager $quick_form_manager
* The quick form manager.
* @param \Drupal\farm_quick\QuickFormInstanceManagerInterface $quick_form_instance_manager
* The quick form instance manager.
*/
public function __construct(QuickFormManager $quick_form_manager) {
$this->quickFormManager = $quick_form_manager;
public function __construct(QuickFormInstanceManagerInterface $quick_form_instance_manager) {
$this->quickFormInstanceManager = $quick_form_instance_manager;
}
/**
@ -34,7 +34,7 @@ class QuickFormMenuLink extends DeriverBase implements ContainerDeriverInterface
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$container->get('plugin.manager.quick_form')
$container->get('quick_form.instance_manager'),
);
}
@ -45,7 +45,8 @@ class QuickFormMenuLink extends DeriverBase implements ContainerDeriverInterface
$links = [];
// Load quick forms.
$quick_forms = $this->quickFormManager->getDefinitions();
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface[] $quick_forms */
$quick_forms = $this->quickFormInstanceManager->getInstances();
// Add a top level menu parent.
if (!empty($quick_forms)) {
@ -57,12 +58,15 @@ class QuickFormMenuLink extends DeriverBase implements ContainerDeriverInterface
}
// Add a link for each quick form.
foreach ($quick_forms as $quick_form) {
$route_id = 'farm.quick.' . $quick_form['id'];
foreach ($quick_forms as $id => $quick_form) {
$route_id = 'farm.quick.' . $id;
$links[$route_id] = [
'title' => $quick_form['label'],
'title' => $quick_form->getLabel(),
'parent' => 'farm.quick:farm.quick',
'route_name' => $route_id,
'route_name' => 'farm_quick.quick_form',
'route_parameters' => [
'quick_form' => $id,
],
] + $base_plugin_definition;
}

View File

@ -0,0 +1,13 @@
<?php
namespace Drupal\farm_quick\Plugin\QuickForm;
use Drupal\Component\Plugin\ConfigurableInterface;
use Drupal\Core\Plugin\PluginFormInterface;
/**
* Interface for configurable quick forms.
*/
interface ConfigurableQuickFormInterface extends QuickFormInterface, ConfigurableInterface, PluginFormInterface {
}

View File

@ -20,6 +20,13 @@ class QuickFormBase extends PluginBase implements QuickFormInterface, ContainerF
use MessengerTrait;
use StringTranslationTrait;
/**
* The quick form ID.
*
* @var string
*/
protected string $quickId;
/**
* Constructs a QuickFormBase object.
*
@ -49,18 +56,32 @@ class QuickFormBase extends PluginBase implements QuickFormInterface, ContainerF
);
}
/**
* {@inheritdoc}
*/
final public function setQuickId(string $id) {
return $this->quickId = $id;
}
/**
* {@inheritdoc}
*/
final public function getQuickId() {
return $this->quickId ?? $this->getPluginId();
}
/**
* {@inheritdoc}
*/
public function getId() {
return $this->getPluginId();
return $this->getQuickId();
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return $this->getId();
return $this->getQuickId();
}
/**

View File

@ -16,6 +16,27 @@ interface QuickFormInterface extends FormInterface {
* @return string
* The quick form ID.
*/
public function getQuickId();
/**
* Sets the quick form ID.
*
* @param string $id
* The quick form ID.
*/
public function setQuickId(string $id);
/**
* Returns the quick form ID.
*
* @return string
* The quick form ID.
*
* @deprecated in farm:2.2.0 and is removed from farm:3.0.0.
* Use QuickFormInterface::getQuickId() instead.
*
* @see https://www.drupal.org/node/3379686
*/
public function getId();
/**

View File

@ -0,0 +1,103 @@
<?php
namespace Drupal\farm_quick;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\farm_quick\Entity\QuickFormInstance;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Quick form instance manager.
*/
class QuickFormInstanceManager implements QuickFormInstanceManagerInterface {
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The quick form plugin manager.
*
* @var \Drupal\farm_quick\QuickFormPluginManager
*/
protected $quickFormPluginManager;
/**
* Constructs a QuickFormInstanceManager object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager service.
* @param \Drupal\farm_quick\QuickFormPluginManager $quick_form_plugin_manager
* The quick form plugin manager.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, QuickFormPluginManager $quick_form_plugin_manager) {
$this->entityTypeManager = $entity_type_manager;
$this->quickFormPluginManager = $quick_form_plugin_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('plugin.manager.quick_form'),
);
}
/**
* {@inheritdoc}
*/
public function getInstances(): array {
$instances = [];
// Iterate through quick form plugin definitions.
foreach ($this->quickFormPluginManager->getDefinitions() as $plugin) {
// Load quick form instance configuration entities for this plugin.
// Exclude disabled quick forms.
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface[] $entities */
$entities = $this->entityTypeManager->getStorage('quick_form')->loadByProperties(['plugin' => $plugin['id'], 'status' => TRUE]);
foreach ($entities as $entity) {
$entity->getPlugin()->setQuickId($entity->id());
$instances[$entity->id()] = $entity;
}
// Or, if this plugin does not require a quick form instance configuration
// entity, then add a new (unsaved) config entity with default values from
// the plugin.
if (!isset($instances[$plugin['id']]) && empty($plugin['requiresEntity'])) {
$instances[$plugin['id']] = QuickFormInstance::create(['id' => $plugin['id'], 'plugin' => $plugin['id']]);
}
}
return $instances;
}
/**
* {@inheritdoc}
*/
public function getInstance($id) {
// First attempt to load a quick form instance config entity.
$entity = $this->entityTypeManager->getStorage('quick_form')->load($id);
if (!empty($entity)) {
$entity->getPlugin()->setQuickId($id);
return $entity;
}
// Or, if this plugin does not require a quick form instance configuration
// entity, then add a new (unsaved) config entity with default values from
// the plugin.
elseif (($plugin = $this->quickFormPluginManager->getDefinition($id, FALSE)) && empty($plugin['requiresEntity'])) {
return QuickFormInstance::create(['id' => $id, 'plugin' => $id]);
}
// No quick form could be instantiated.
return NULL;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Drupal\farm_quick;
/**
* Quick form instance manager.
*/
interface QuickFormInstanceManagerInterface {
/**
* Get all quick form instances.
*
* @return \Drupal\farm_quick\Entity\QuickFormInstanceInterface[]
* An array of quick form instances.
*/
public function getInstances();
/**
* Get an instance of a quick form.
*
* @param string $id
* The quick form ID.
*
* @return \Drupal\farm_quick\Entity\QuickFormInstanceInterface|null
* Returns an instantiated quick form object.
*/
public function getInstance($id);
}

View File

@ -0,0 +1,48 @@
<?php
namespace Drupal\farm_quick;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
/**
* Provides a collection of quick form plugins.
*/
class QuickFormPluginCollection extends DefaultSingleLazyPluginCollection {
/**
* The quick form ID this plugin collection belongs to.
*
* @var string
*/
protected $quickFormId;
/**
* Constructs a new QuickFormPluginCollection.
*
* @param \Drupal\Component\Plugin\PluginManagerInterface $manager
* The manager to be used for instantiating plugins.
* @param string $instance_id
* The ID of the plugin instance.
* @param array $configuration
* An array of configuration.
* @param string $quick_form_id
* The unique ID of the quick form entity using this plugin.
*/
public function __construct(PluginManagerInterface $manager, $instance_id, array $configuration, $quick_form_id) {
parent::__construct($manager, $instance_id, $configuration);
$this->quickFormId = $quick_form_id;
}
/**
* {@inheritdoc}
*/
protected function initializePlugin($instance_id) {
if (!$instance_id) {
throw new PluginException("The quick form '{$this->quickFormId}' did not specify a plugin.");
}
parent::initializePlugin($instance_id);
}
}

View File

@ -9,10 +9,10 @@ use Drupal\Core\Plugin\DefaultPluginManager;
/**
* Quick form manager class.
*/
class QuickFormManager extends DefaultPluginManager {
class QuickFormPluginManager extends DefaultPluginManager {
/**
* Constructs a QuickFormManager object.
* Constructs a QuickFormPluginManager object.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths

View File

@ -1,68 +0,0 @@
<?php
namespace Drupal\farm_quick\Routing;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\farm_quick\Form\QuickForm;
use Drupal\farm_quick\QuickFormManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
* Defines quick form routes.
*/
class QuickFormRoutes implements ContainerInjectionInterface {
/**
* The quick form manager.
*
* @var \Drupal\farm_quick\QuickFormManager
*/
protected $quickFormManager;
/**
* Constructs a QuickFormRoutes object.
*
* @param \Drupal\farm_quick\QuickFormManager $quick_form_manager
* The quick form manager.
*/
public function __construct(QuickFormManager $quick_form_manager) {
$this->quickFormManager = $quick_form_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.quick_form'),
);
}
/**
* Provides routes for quick forms.
*
* @return \Symfony\Component\Routing\RouteCollection
* Returns a route collection.
*/
public function routes(): RouteCollection {
$route_collection = new RouteCollection();
foreach ($this->quickFormManager->getDefinitions() as $id => $definition) {
$route = new Route(
"/quick/$id",
[
'_form' => QuickForm::class,
'_title_callback' => QuickForm::class . '::getTitle',
'id' => $id,
],
[
'_custom_access' => QuickForm::class . '::access',
],
);
$route_collection->add("farm.quick.$id", $route);
}
return $route_collection;
}
}

View File

@ -0,0 +1,57 @@
<?php
namespace Drupal\farm_quick\Traits;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Implements \Drupal\Component\Plugin\ConfigurableQuickFormInterface.
*
* @ingroup farm
*/
trait ConfigurableQuickFormTrait {
use ConfigurableTrait;
use MessengerTrait;
use StringTranslationTrait;
/**
* Returns the quick form ID.
*
* This must be implemented by the quick form class that uses this trait.
*
* @see \Drupal\farm_quick\Plugin\QuickForm\QuickFormInterface
*
* @return string
* The quick form ID.
*/
abstract public function getQuickId();
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
return [];
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
// Validation is optional.
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
// @todo Save configuration entity.
// Add a status message.
$this->messenger->addStatus($this->t('Configuration saved.'));
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace Drupal\farm_quick\Traits;
use Drupal\Component\Utility\NestedArray;
/**
* Implements \Drupal\Component\Plugin\ConfigurableInterface.
*
* In order for configurable plugins to maintain their configuration, the
* default configuration must be merged into any explicitly defined
* configuration. This trait provides the appropriate getters and setters to
* handle this logic, removing the need for excess boilerplate.
*
* @ingroup Plugin
*
* @todo Replace with core trait when available.
* @see https://www.drupal.org/project/drupal/issues/2852463
*/
trait ConfigurableTrait {
/**
* Configuration information passed into the plugin.
*
* @var array
*/
protected $configuration;
/**
* Gets this plugin's configuration.
*
* @return array
* An array of this plugin's configuration.
*
* @see \Drupal\Component\Plugin\ConfigurableInterface::getConfiguration()
*/
public function getConfiguration() {
return $this->configuration;
}
/**
* Sets the configuration for this plugin instance.
*
* @param array $configuration
* An associative array containing the plugin's configuration. Provided
* value is merged with default configuration.
*
* @return $this
*
* @see \Drupal\Component\Plugin\ConfigurableInterface::setConfiguration()
*/
public function setConfiguration(array $configuration) {
$this->configuration = NestedArray::mergeDeepArray([$this->defaultConfiguration(), $configuration], TRUE);
return $this;
}
/**
* Gets default configuration for this plugin.
*
* @return array
* An associative array with the default configuration.
*
* @see \Drupal\Component\Plugin\ConfigurableInterface::defaultConfiguration()
*/
public function defaultConfiguration() {
return [];
}
}

View File

@ -25,7 +25,7 @@ trait QuickAssetTrait {
* @return string
* The quick form ID.
*/
abstract public function getId();
abstract public function getQuickId();
/**
* Create an asset.
@ -48,7 +48,7 @@ trait QuickAssetTrait {
$asset = Asset::create($values);
// Track which quick form created the entity.
$asset->quick[] = $this->getId();
$asset->quick[] = $this->getQuickId();
// Save the asset.
$asset->save();

View File

@ -26,7 +26,7 @@ trait QuickLogTrait {
* @return string
* The quick form ID.
*/
abstract public function getId();
abstract public function getQuickId();
/**
* Create a log.
@ -70,7 +70,7 @@ trait QuickLogTrait {
}
// Track which quick form created the entity.
$log->quick[] = $this->getId();
$log->quick[] = $this->getQuickId();
// Save the log.
$log->save();

View File

@ -20,7 +20,7 @@ trait QuickPrepopulateTrait {
* @return string
* The quick form ID.
*/
abstract public function getId();
abstract public function getQuickId();
/**
* Get prepopulated entities.
@ -61,7 +61,7 @@ trait QuickPrepopulateTrait {
// Load the temp store for the quick form.
/** @var \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory */
$temp_store_factory = \Drupal::service('tempstore.private');
$temp_store = $temp_store_factory->get('farm_quick.' . $this->getId());
$temp_store = $temp_store_factory->get('farm_quick.' . $this->getQuickId());
// Load entities from the temp store.
$temp_store_key = $user->id() . ':' . $entity_type;

View File

@ -0,0 +1,9 @@
langcode: en
status: true
id: configurable_test2
plugin: configurable_test
label: Test configurable quick form 2
description: Overridden description
helpText: Overridden help text
settings:
test_default: 500

View File

@ -0,0 +1,7 @@
farm_quick.settings.configurable_test:
type: quick_form_settings
label: 'Test configurable quick form settings'
mapping:
test_default:
type: integer
label: 'The default test value.'

View File

@ -0,0 +1,66 @@
<?php
namespace Drupal\farm_quick_test\Plugin\QuickForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\farm_quick\Plugin\QuickForm\ConfigurableQuickFormInterface;
use Drupal\farm_quick\Traits\ConfigurableQuickFormTrait;
/**
* Test configurable quick form.
*
* @QuickForm(
* id = "configurable_test",
* label = @Translation("Test configurable quick form"),
* description = @Translation("Test configurable quick form description."),
* helpText = @Translation("Test configurable quick form help text."),
* permissions = {
* "create test log",
* }
* )
*/
class ConfigurableTest extends Test implements ConfigurableQuickFormInterface {
use ConfigurableQuickFormTrait;
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'test_default' => 100,
];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, string $id = NULL) {
$form = parent::buildForm($form, $form_state, $id);
// Set a default value from configuration.
$form['test']['#default_value'] = $this->configuration['test_default'];
return $form;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['test_default'] = [
'#type' => 'number',
'#title' => $this->t('Default value'),
'#default_value' => $this->configuration['test_default'],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['test_default'] = $form_state->getValue('test_default');
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Drupal\farm_quick_test\Plugin\QuickForm;
/**
* Test quick form that requires a configuration entity.
*
* @QuickForm(
* id = "requires_entity_test",
* label = @Translation("Test requiresEntity quick form"),
* description = @Translation("Test requiresEntity quick form description."),
* helpText = @Translation("Test requiresEntity quick form help text."),
* permissions = {
* "create test log",
* },
* requiresEntity = True
* )
*/
class RequiresEntityTest extends Test {
}

View File

@ -3,6 +3,7 @@
namespace Drupal\Tests\farm_quick\Functional;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\farm_quick\Entity\QuickFormInstance;
use Drupal\Tests\farm_test\Functional\FarmBrowserTestBase;
/**
@ -19,8 +20,19 @@ class QuickFormTest extends FarmBrowserTestBase {
*/
protected static $modules = [
'farm_quick_test',
'help',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Add the help block, so we can test help text.
$this->drupalPlaceBlock('help_block');
}
/**
* Test quick forms.
*/
@ -34,8 +46,8 @@ class QuickFormTest extends FarmBrowserTestBase {
$this->drupalGet('quick');
$this->assertSession()->statusCodeEquals(403);
// Create and login a test user with access to the quick form index.
$user = $this->createUser(['view quick forms index']);
// Create and login a test user with access to view quick forms.
$user = $this->createUser(['view quick_form']);
$this->drupalLogin($user);
// Go to the quick form index and confirm that access is granted, but no
@ -50,18 +62,100 @@ class QuickFormTest extends FarmBrowserTestBase {
// Create and login a test user with access to the quick form index, and
// permission to create test logs.
$user = $this->createUser(['view quick forms index', 'create test log']);
$user = $this->createUser(['view quick_form', 'create test log']);
$this->drupalLogin($user);
// Go to the quick form index and confirm that access is granted, and the
// test quick form item is visible.
// Go to the quick form index and confirm that:
// 1. access is granted.
// 2. the test quick form item is visible.
// 3. the default configurable_test quick form item is visible.
// 4. the second instance of configurable_test quick form item is visible.
// 5. the requires_entity_test quick form item is NOT visible.
$this->drupalGet('quick');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains($this->t('Test quick form'));
$this->assertSession()->pageTextContains($this->t('Test configurable quick form'));
$this->assertSession()->pageTextContains($this->t('Test configurable quick form 2'));
$this->assertSession()->pageTextNotContains($this->t('Test requiresEntity quick form'));
// Go to the test quick form and confirm that the test field is visible.
// Go to the test quick form and confirm that the help text and test field
// is visible.
$this->drupalGet('quick/test');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains($this->t('Test quick form help text.'));
$this->assertSession()->pageTextContains($this->t('Test field'));
// Go to the default configurable_test quick form and confirm access is
// granted and the default value is 100.
$this->drupalGet('quick/configurable_test');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->responseContains('value="100"');
// Go to the test configuration form and confirm that access is denied.
$this->drupalGet('quick/configurable_test/configure');
$this->assertSession()->statusCodeEquals(403);
// Create and login a test user with permission to create test logs and
// permission to update quick forms.
$user = $this->createUser(['view quick_form', 'create test log', 'update quick_form']);
$this->drupalLogin($user);
// Go to the default configurable_test quick form and confirm that the
// default value field is visible and the default value is 100.
$this->drupalGet('quick/configurable_test/configure');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains($this->t('Default value'));
$this->assertSession()->responseContains('value="100"');
// Go to the configurable_test2 quick form and confirm access is granted and
// the default value is 500.
$this->drupalGet('quick/configurable_test2');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->responseContains('value="500"');
// Go to the configurable_test2 quick form and confirm that the default
// value field is visible and the default value is 500.
$this->drupalGet('quick/configurable_test2/configure');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains($this->t('Default value'));
$this->assertSession()->responseContains('value="500"');
// Save the configurable_test2 config entity to change the value and
// confirm that it is updated in the quick form and configuration form.
$config_entity = \Drupal::entityTypeManager()->getStorage('quick_form')->load('configurable_test2');
$config_entity->set('settings', ['test_default' => 600]);
$config_entity->save();
$this->drupalGet('quick/configurable_test2');
$this->assertSession()->responseContains('value="600"');
$this->drupalGet('quick/configurable_test2/configure');
$this->assertSession()->responseContains('value="600"');
// Attempt to load a configuration form for a non-existent quick form and
// confirm 404 not found.
$this->drupalGet('quick/foo/configure');
$this->assertSession()->statusCodeEquals(404);
// Go to the requires_entity_test quick form and confirm 404 not found.
$this->drupalGet('quick/requires_entity_test');
$this->assertSession()->statusCodeEquals(404);
// Create a config entity for the requires_entity_test plugin.
$config_entity = QuickFormInstance::create([
'id' => 'requires_entity_test',
'plugin' => 'requires_entity_test',
]);
$config_entity->save();
// Go to the quick form index and confirm that the requires_entity_test
// quick form item is visible.
$this->drupalGet('quick');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains($this->t('Test requiresEntity quick form'));
// Go to the default requires_entity_test quick form and confirm access
// granted and the default value is 100.
$this->drupalGet('quick/requires_entity_test');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains($this->t('Test field'));
}

View File

@ -3,6 +3,7 @@
namespace Drupal\Tests\farm_quick\Kernel;
use Drupal\Core\Form\FormState;
use Drupal\farm_quick\Form\ConfigureQuickForm;
use Drupal\KernelTests\KernelTestBase;
/**
@ -13,11 +14,11 @@ use Drupal\KernelTests\KernelTestBase;
class QuickFormTest extends KernelTestBase {
/**
* The quick form manager.
* The quick form instance manager.
*
* @var \Drupal\farm_quick\QuickFormManager
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
*/
protected $quickFormManager;
protected $quickFormInstanceManager;
/**
* {@inheritdoc}
@ -45,7 +46,7 @@ class QuickFormTest extends KernelTestBase {
*/
protected function setUp(): void {
parent::setUp();
$this->quickFormManager = \Drupal::service('plugin.manager.quick_form');
$this->quickFormInstanceManager = \Drupal::service('quick_form.instance_manager');
$this->installEntitySchema('asset');
$this->installEntitySchema('log');
$this->installEntitySchema('taxonomy_term');
@ -61,21 +62,38 @@ class QuickFormTest extends KernelTestBase {
*/
public function testQuickFormDiscovery() {
// Load quick form definitions.
$quick_forms = $this->quickFormManager->getDefinitions();
// Load quick forms.
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface[] $quick_forms */
$quick_forms = $this->quickFormInstanceManager->getInstances();
// Confirm that one quick form was discovered.
$this->assertEquals(1, count($quick_forms));
// Confirm that three quick forms were discovered.
$this->assertEquals(3, count($quick_forms));
// Initialize the test quick form.
/** @var \Drupal\farm_quick\Plugin\QuickForm\QuickFormInterface $test_quick_form */
$test_quick_form = $this->quickFormManager->createInstance('test');
// Confirm the label, description, helpText, and permissions of the test
// quick form.
$this->assertEquals('Test quick form', $quick_forms['test']->getLabel());
$this->assertEquals('Test quick form description.', $quick_forms['test']->getDescription());
$this->assertEquals('Test quick form help text.', $quick_forms['test']->getHelpText());
$this->assertEquals(['create test log'], $quick_forms['test']->getPlugin()->getPermissions());
// Confirm the label, description, helpText, and permissions.
$this->assertEquals('Test quick form', $test_quick_form->getLabel());
$this->assertEquals('Test quick form description.', $test_quick_form->getDescription());
$this->assertEquals('Test quick form help text.', $test_quick_form->getHelpText());
$this->assertEquals(['create test log'], $test_quick_form->getPermissions());
// Confirm the label, description, helpText, and permissions of the
// configurable_test quick form.
$this->assertEquals('Test configurable quick form', $quick_forms['configurable_test']->getLabel());
$this->assertEquals('Test configurable quick form description.', $quick_forms['configurable_test']->getDescription());
$this->assertEquals('Test configurable quick form help text.', $quick_forms['configurable_test']->getHelpText());
$this->assertEquals(['create test log'], $quick_forms['configurable_test']->getPlugin()->getPermissions());
// Confirm default configuration.
$this->assertEquals(['test_default' => 100], $quick_forms['configurable_test']->getPlugin()->defaultConfiguration());
// Confirm overridden label, description, and helpText of the
// configurable_test2 quick form.
$this->assertEquals('Test configurable quick form 2', $quick_forms['configurable_test2']->getLabel());
$this->assertEquals('Overridden description', $quick_forms['configurable_test2']->getDescription());
$this->assertEquals('Overridden help text', $quick_forms['configurable_test2']->getHelpText());
// Confirm configuration of configurable_test2 quick form.
$this->assertEquals(['test_default' => 500], $quick_forms['configurable_test2']->getPlugin()->getConfiguration());
}
/**
@ -122,4 +140,41 @@ class QuickFormTest extends KernelTestBase {
$this->assertTrue($match);
}
/**
* Test configurable quick forms.
*/
public function testConfigurableQuickForm() {
// Load the configurable_test quick form.
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface $quick_form */
$quick_form = \Drupal::service('quick_form.instance_manager')->getInstance('configurable_test');
// Confirm that the config entity for this quick form has not been saved.
$this->assertTrue($quick_form->isNew());
// Programmatically submit the configurable_test config form.
$form = ConfigureQuickForm::create(\Drupal::getContainer());
$form->setModuleHandler(\Drupal::moduleHandler());
$form->setEntity($quick_form);
$form_state = (new FormState())->setValues([
'settings' => [
'test_default' => '101',
],
]);
$form_state->setTriggeringElement(\Drupal::formBuilder()->getForm($form)['actions']['submit']);
\Drupal::formBuilder()->submitForm($form, $form_state);
// Reload the configurable_test quick form.
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface $quick_form */
$quick_form = \Drupal::service('quick_form.instance_manager')->getInstance('configurable_test');
// Confirm that a config entity was saved with all the proper defaults and
// the submitted configuration value.
$this->assertNotTrue($quick_form->isNew());
$this->assertEquals($quick_form->getPlugin()->getLabel(), $quick_form->get('label'));
$this->assertEquals($quick_form->getPlugin()->getDescription(), $quick_form->get('description'));
$this->assertEquals($quick_form->getPlugin()->getHelpText(), $quick_form->get('helpText'));
$this->assertEquals('101', $quick_form->get('settings')['test_default']);
}
}