clean install

This commit is contained in:
2024-12-10 15:08:16 +01:00
commit e14eb2d8fd
31193 changed files with 3555714 additions and 0 deletions

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

@@ -0,0 +1,13 @@
name: farmOS Quick Forms
description: Provides a framework for farmOS quick forms.
type: module
package: farmOS
core_version_requirement: ^10
dependencies:
- entity:entity
- drupal:taxonomy
- farm:asset
- farm:farm_log_quantity
- farm:farm_setup
- farm:quantity
- log:log

View File

@@ -0,0 +1,5 @@
farm_quick.add_page:
title: 'Add quick form'
route_name: farm_quick.add_page
appears_on:
- entity.quick_form.collection

View File

@@ -0,0 +1,8 @@
farm.quick:
class: Drupal\Core\Menu\MenuLinkDefault
deriver: Drupal\farm_quick\Plugin\Derivative\QuickFormMenuLink
farm.quick_setup:
title: Quick Forms
description: Quick forms make it easy to record common activities.
parent: farm.setup
route_name: entity.quick_form.collection

View File

@@ -0,0 +1,2 @@
farm.quick:
deriver: Drupal\farm_quick\Plugin\Derivative\QuickFormTaskLink

View File

@@ -0,0 +1,7 @@
farm_quick:
config_permissions:
- create quick_form
- update quick_form
- administer quick_form
default_permissions:
- view quick_form

View File

@@ -0,0 +1,105 @@
<?php
/**
* @file
* The farmOS Quick Form module.
*/
use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_help().
*/
function farm_quick_help($route_name, RouteMatchInterface $route_match) {
$output = '';
// Quick forms index help text.
if ($route_name == 'farm.quick') {
$output .= '<p>' . t('Quick forms make it easy to record common activities.') . '</p>';
}
// 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) {
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface $quick_form */
$quick_form = \Drupal::service('quick_form.instance_manager')->getInstance($quick_form_id);
$output = [
'#type' => 'html_tag',
'#tag' => 'p',
'#value' => Html::escape($quick_form->getHelpText()),
'#cache' => [
'tags' => $quick_form->getCacheTags(),
],
];
}
}
return $output;
}
/**
* Implements hook_farm_entity_bundle_field_info().
*/
function farm_quick_farm_entity_bundle_field_info(EntityTypeInterface $entity_type, string $bundle) {
$fields = [];
// We only act on asset and log entities.
if (!in_array($entity_type->id(), ['asset', 'log'])) {
return $fields;
}
// Add a hidden quick form field.
$options = [
'type' => 'string',
'label' => t('Quick form'),
'description' => t('References the quick form that was used to create this record.'),
'multiple' => TRUE,
'hidden' => TRUE,
];
$fields['quick'] = \Drupal::service('farm_field.factory')->bundleFieldDefinition($options);
return $fields;
}
/**
* Implements hook_form_alter().
*/
function farm_quick_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// Only alter views_form_ forms.
if (!str_starts_with($form_id, 'views_form_')) {
return;
}
$target = NULL;
if (isset($form['header']['asset_bulk_form']['action'])) {
$target = 'asset_bulk_form';
}
if (isset($form['header']['log_bulk_form']['action'])) {
$target = 'log_bulk_form';
}
// Alter action options for the target entity type bulk form.
if ($target) {
// Check for disabled quick forms.
$disabled_quick_forms = \Drupal::entityTypeManager()->getStorage('quick_form')->getQuery()
->accessCheck(TRUE)
->condition('status', FALSE)
->execute();
if (empty($disabled_quick_forms)) {
return;
}
// Remove system actions that end with quick_* for a disabled quick form.
foreach (array_keys($form['header'][$target]['action']['#options']) as $option_id) {
if ((preg_match("/quick_(.*)/", $option_id, $matches)) && in_array($matches[1], $disabled_quick_forms)) {
unset($form['header'][$target]['action']['#options'][$option_id]);
}
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* @file
* Post update hooks for the farmOS Quick Form module.
*/
/**
* Install the new quick_form entity type.
*/
function farm_quick_post_update_install_quick_form_entity_type(&$sandbox) {
\Drupal::entityDefinitionUpdateManager()->installEntityType(
\Drupal::entityTypeManager()->getDefinition('quick_form')
);
}

View File

@@ -0,0 +1,29 @@
farm.quick:
path: '/quick'
defaults:
_controller: '\Drupal\farm_quick\Controller\QuickFormController::index'
_title: 'Quick forms'
requirements:
_permission: 'view quick_form'
farm_quick.add_page:
path: 'setup/quick/add'
defaults:
_controller: \Drupal\farm_quick\Controller\QuickFormAddPage::addPage
_title: 'Add quick form'
requirements:
_permission: 'create quick_form'
farm_quick.add_form:
path: '/setup/quick/add/{plugin}'
defaults:
_entity_form: quick_form.add
requirements:
_permission: 'create quick_form'
options:
parameters:
plugin:
type: string
route_callbacks:
- '\Drupal\farm_quick\Routing\QuickFormRoutes::routes'

View File

@@ -0,0 +1,8 @@
services:
plugin.manager.quick_form:
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

@@ -0,0 +1,62 @@
<?php
namespace Drupal\farm_quick\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a quick form annotation object.
*
* @Annotation
*/
class QuickForm extends Plugin {
/**
* The quick form ID.
*
* @var string
*/
public $id;
/**
* The quick form label.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
/**
* The quick form description.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $description;
/**
* The quick form help text.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $helpText;
/**
* An array of access permissions for the quick form.
*
* @var string[]
*/
public $permissions;
/**
* Require a quick form instance entity to instantiate.
*
* @var bool
*/
public $requiresEntity;
}

View File

@@ -0,0 +1,79 @@
<?php
namespace Drupal\farm_quick\Controller;
use Drupal\Component\Utility\Html;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Link;
use Drupal\farm_quick\QuickFormPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Page that renders links to create instances of quick form plugins.
*/
class QuickFormAddPage extends ControllerBase {
/**
* The quick form plugin manager.
*
* @var \Drupal\farm_quick\QuickFormPluginManager
*/
protected $quickFormPluginManager;
/**
* Constructs a new QuickFormAddPage object.
*/
public function __construct(QuickFormPluginManager $quick_form_plugin_manager) {
$this->quickFormPluginManager = $quick_form_plugin_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.quick_form'),
);
}
/**
* Add quick form page callback.
*
* @return array
* Render array.
*/
public function addPage(): array {
$render = [
'#theme' => 'entity_add_list',
'#bundles' => [],
'#cache' => [
'tags' => $this->quickFormPluginManager->getCacheTags(),
],
];
// Filter to configurable quick form plugins.
$plugins = array_filter($this->quickFormPluginManager->getDefinitions(), function (array $plugin) {
if (($instance = $this->quickFormPluginManager->createInstance($plugin['id'])) && $instance->isConfigurable()) {
return TRUE;
}
return FALSE;
});
if (empty($plugins)) {
$render['#add_bundle_message'] = $this->t('No quick forms are available. Enable a module that provides quick forms.');
}
// Add link for each configurable plugin.
foreach ($plugins as $plugin_id => $plugin) {
$render['#bundles'][$plugin_id] = [
'label' => Html::escape($plugin['label']),
'description' => Html::escape($plugin['description']) ?? '',
'add_link' => Link::createFromRoute($plugin['label'], 'farm_quick.add_form', ['plugin' => $plugin_id]),
];
}
return $render;
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Drupal\farm_quick\Controller;
use Drupal\Component\Utility\Html;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Render\Markup;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\farm_quick\QuickFormInstanceManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Quick form controller.
*/
class QuickFormController extends ControllerBase {
use StringTranslationTrait;
/**
* The quick form instance manager.
*
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
*/
protected $quickFormInstanceManager;
/**
* Quick form controller 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'),
);
}
/**
* The index of quick forms.
*
* @return array
* Returns a render array.
*/
public function index(): array {
// 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 $id => $quick_form) {
$cacheability->addCacheableDependency($quick_form);
$url = Url::fromRoute('farm.quick.' . $id);
if ($url->access()) {
$items[] = [
// Wrap the title in Markup::create() because the template preprocess
// function for admin_block_content uses Link::fromTextAndUrl(), which
// sanitizes strings automatically. This avoids double-sanitization,
// but also ensures we are sanitizing consistently in this code, in
// case anything changes later.
// @see template_preprocess_admin_block_content()
// @see \Drupal\Core\Link::fromTextAndUrl()
'title' => Markup::create(Html::escape($quick_form->getLabel())),
'description' => Html::escape($quick_form->getDescription()),
'url' => $url,
];
}
}
// Render items.
if (!empty($items)) {
$output = [
'#theme' => 'admin_block_content',
'#content' => $items,
];
}
else {
$output = [
'#markup' => $this->t('You do not have any quick forms.'),
];
}
$cacheability->applyTo($output);
return $output;
}
}

View File

@@ -0,0 +1,208 @@
<?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",
* "list_builder" = "Drupal\farm_quick\QuickFormListBuilder",
* "form" = {
* "add" = "Drupal\farm_quick\Form\QuickFormEntityForm",
* "edit" = "Drupal\farm_quick\Form\QuickFormEntityForm",
* "configure" = "Drupal\farm_quick\Form\ConfigureQuickForm",
* "delete" = "\Drupal\Core\Entity\EntityDeleteForm",
* },
* "route_provider" = {
* "default" = "Drupal\entity\Routing\DefaultHtmlRouteProvider",
* },
* },
* admin_permission = "administer quick_form",
* entity_keys = {
* "id" = "id",
* "status" = "status",
* "label" = "label",
* },
* links = {
* "edit-form" = "/setup/quick/{quick_form}/edit",
* "delete-form" = "/setup/quick/{quick_form}/delete",
* "collection" = "/setup/quick"
* },
* 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];
}
}
}
}
/**
* {@inheritdoc}
*/
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
parent::postSave($storage, $update);
\Drupal::service('router.builder')->setRebuildNeeded();
}
/**
* {@inheritdoc}
*/
public static function postDelete(EntityStorageInterface $storage, array $entities) {
parent::postDelete($storage, $entities);
\Drupal::service('router.builder')->setRebuildNeeded();
}
}

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,145 @@
<?php
namespace Drupal\farm_quick\Form;
use Drupal\Core\Form\BaseFormIdInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\farm_quick\QuickFormInstanceManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
/**
* Form that renders quick forms.
*
* @ingroup farm
*/
class QuickForm extends FormBase implements BaseFormIdInterface {
/**
* The quick form instance manager.
*
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
*/
protected $quickFormInstanceManager;
/**
* The quick form ID.
*
* @var string
*/
protected $quickFormId;
/**
* 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'),
);
}
/**
* {@inheritdoc}
*/
public function getBaseFormId() {
return 'quick_form';
}
/**
* {@inheritdoc}
*/
public function getFormId() {
$form_id = $this->getBaseFormId();
$id = $this->getRouteMatch()->getParameter('id');
if (!is_null($id)) {
$form_id .= '_' . $this->quickFormInstanceManager->getInstance($id)->getPlugin()->getFormId();
}
return $form_id;
}
/**
* Get the title of the quick form.
*
* @param string $id
* The quick form ID.
*
* @return string
* Quick form title.
*/
public function getTitle(string $id) {
return $this->quickFormInstanceManager->getInstance($id)->getLabel();
}
/**
* Checks access for a specific quick form.
*
* @param \Drupal\Core\Session\AccountInterface $account
* Run access checks for this account.
* @param string $id
* The quick form ID.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access(AccountInterface $account, string $id) {
if ($quick_form = $this->quickFormInstanceManager->getInstance($id)) {
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) {
// Save the quick form ID.
$this->quickFormId = $id;
// Load the quick form.
$form = $this->quickFormInstanceManager->getInstance($id)->getPlugin()->buildForm($form, $form_state);
// Add a submit button, if one wasn't provided.
if (empty($form['actions']['submit'])) {
$form['actions'] = [
'#type' => 'actions',
'#weight' => 1000,
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Submit'),
];
}
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$this->quickFormInstanceManager->getInstance($this->quickFormId)->getPlugin()->validateForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->quickFormInstanceManager->getInstance($this->quickFormId)->getPlugin()->submitForm($form, $form_state);
}
}

View File

@@ -0,0 +1,210 @@
<?php
namespace Drupal\farm_quick\Form;
use Drupal\Component\Utility\Html;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\farm_quick\Entity\QuickFormInstance;
use Drupal\farm_quick\QuickFormPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Form that renders quick form configuration forms.
*/
class QuickFormEntityForm extends EntityForm {
/**
* The entity being used by this form.
*
* @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface
*/
protected $entity;
/**
* The quick form plugin manager.
*
* @var \Drupal\farm_quick\QuickFormPluginManager
*/
protected $quickFormPluginManager;
/**
* Constructs a new QuickFormEntityForm object.
*
* @param \Drupal\farm_quick\QuickFormPluginManager $quick_form_plugin_manager
* The quick form plugin manager.
*/
public function __construct(QuickFormPluginManager $quick_form_plugin_manager) {
$this->quickFormPluginManager = $quick_form_plugin_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.quick_form'),
);
}
/**
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state, ?string $plugin = NULL) {
$form = parent::form($form, $form_state);
// Add tabs if the quick form plugin is configurable.
$tab_group = NULL;
if ($this->entity->getPlugin()->isConfigurable()) {
$form['tabs'] = [
'#type' => 'vertical_tabs',
];
$form['quick_form'] = [
'#type' => 'details',
'#title' => $this->t('Quick form'),
'#group' => 'tabs',
];
$tab_group = 'quick_form';
// Render the plugin form in settings tab.
$form['settings_tab'] = [
'#type' => 'details',
'#title' => Html::escape($this->entity->getPlugin()->getLabel()),
'#group' => 'tabs',
'#weight' => 50,
];
$form['settings'] = [
'#tree' => TRUE,
'#type' => 'container',
'#group' => 'settings_tab',
];
$form['settings'] = $this->entity->getPlugin()->buildConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
}
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => 255,
'#required' => TRUE,
'#group' => $tab_group,
];
$form['id'] = [
'#type' => 'machine_name',
'#machine_name' => [
'exists' => '\Drupal\farm_quick\Entity\QuickFormInstance::load',
],
'#disabled' => !$this->entity->isNew() || $this->getRequest()->get('override'),
'#group' => $tab_group,
];
// Provide default label and ID for existing config entities
// or if the override parameter is set.
if (!$this->entity->isNew() || $this->getRequest()->get('override')) {
$form['label']['#default_value'] = $this->entity->label();
$form['id']['#default_value'] = $this->entity->id();
}
// Adjust form title.
if ($this->entity->isNew()) {
$form['#title'] = $this->t('Add quick form: @label', ['@label' => $this->entity->getPlugin()->getLabel()]);
if ($this->getRequest()->get('override')) {
$form['#title'] = $this->t('Override quick form: @label', ['@label' => $this->entity->getPlugin()->getLabel()]);
}
}
else {
$form['#title'] = $this->t('Edit quick form: @label', ['@label' => $this->entity->label()]);
}
$form['description'] = [
'#type' => 'textfield',
'#title' => $this->t('Description'),
'#description' => $this->t('A brief description of this quick form.'),
'#default_value' => $this->entity->getDescription(),
'#group' => $tab_group,
];
$form['status'] = [
'#type' => 'checkbox',
'#title' => $this->t('Enabled'),
'#description' => $this->t('Enable the quick form.'),
'#default_value' => $this->entity->status(),
'#group' => $tab_group,
];
$form['helpText'] = [
'#type' => 'textarea',
'#title' => $this->t('Help Text'),
'#description' => $this->t('Help text to display for the quick form.'),
'#default_value' => $this->entity->getHelpText(),
'#group' => $tab_group,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
// Validate plugin form.
if ($this->entity->getPlugin()->isConfigurable()) {
$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);
// Submit plugin form.
if ($this->entity->getPlugin()->isConfigurable()) {
$this->entity->getPlugin()->submitConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
}
}
/**
* {@inheritdoc}
*/
public function save(array $form, FormStateInterface $form_state) {
$status = parent::save($form, $form_state);
$entity_type_label = $this->entity->getEntityType()->getSingularLabel();
$this->messenger()->addMessage($this->t('Saved @entity_type_label: %label', ['@entity_type_label' => $entity_type_label, '%label' => $this->entity->label()]));
$form_state->setRedirect('entity.quick_form.collection');
return $status;
}
/**
* {@inheritdoc}
*/
public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entity_type_id) {
// Get existing quick form entity from route parameter.
if ($route_match->getRawParameter($entity_type_id) !== NULL) {
$entity = $route_match->getParameter($entity_type_id);
}
// Else create a new quick form entity, the plugin must be specified.
else {
if (($plugin = $route_match->getRawParameter('plugin')) && $this->quickFormPluginManager->hasDefinition($plugin)) {
$entity = QuickFormInstance::create(['plugin' => $plugin]);
if ($this->getRequest()->get('override')) {
$entity->set('id', $plugin);
}
}
}
if (empty($entity)) {
throw new NotFoundHttpException();
}
return $entity;
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace Drupal\farm_quick\Plugin\Action;
use Drupal\Core\Action\Plugin\Action\EntityActionBase;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Base class for quick form action plugins.
*/
abstract class QuickFormActionBase extends EntityActionBase {
/**
* The tempstore object.
*
* @var \Drupal\Core\TempStore\SharedTempStore
*/
protected $tempStore;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Constructs a new EntityFlag object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
* @param \Drupal\Core\Session\AccountInterface $current_user
* Current user.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
$quick_form_id = $this->getQuickFormId();
$this->tempStore = $temp_store_factory->get("farm_quick.$quick_form_id");
$this->currentUser = $current_user;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('tempstore.private'),
$container->get('current_user')
);
}
/**
* Get the quick form ID the action is associated with.
*
* @return string
* The quick form ID.
*/
abstract public function getQuickFormId(): string;
/**
* {@inheritdoc}
*/
public function executeMultiple(array $entities) {
/** @var \Drupal\Core\Entity\EntityInterface[] $entities */
$this->tempStore->set($this->currentUser->id() . ':' . $this->getPluginDefinition()['type'], $entities);
}
/**
* {@inheritdoc}
*/
public function execute($object = NULL) {
$this->executeMultiple([$object]);
}
/**
* {@inheritdoc}
*/
public function access($object, ?AccountInterface $account = NULL, $return_as_object = FALSE) {
$result = $object->access('view', $account, TRUE);
return $return_as_object ? $result : $result->isAllowed();
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace Drupal\farm_quick\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Component\Utility\Html;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\farm_quick\QuickFormInstanceManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides menu links for quick forms.
*/
class QuickFormMenuLink extends DeriverBase implements ContainerDeriverInterface {
/**
* The quick form instance manager.
*
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
*/
protected $quickFormInstanceManager;
/**
* FarmQuickMenuLink 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, $base_plugin_id) {
return new static(
$container->get('quick_form.instance_manager'),
);
}
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
$links = [];
// Load quick forms.
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface[] $quick_forms */
$quick_forms = $this->quickFormInstanceManager->getInstances();
// Add a top level menu parent.
if (!empty($quick_forms)) {
$links['farm.quick'] = [
'title' => 'Quick forms',
'route_name' => 'farm.quick',
'weight' => -100,
] + $base_plugin_definition;
}
// Add a link for each quick form.
foreach ($quick_forms as $id => $quick_form) {
// Skip disabled quick forms.
if (!$quick_form->status()) {
continue;
}
// Create link.
$route_id = 'farm.quick.' . $id;
$links[$route_id] = [
'title' => Html::escape($quick_form->getLabel()),
'parent' => 'farm.quick:farm.quick',
'route_name' => $route_id,
] + $base_plugin_definition;
}
return $links;
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace Drupal\farm_quick\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\farm_quick\QuickFormInstanceManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides task links for farmOS Quick Forms.
*/
class QuickFormTaskLink extends DeriverBase implements ContainerDeriverInterface {
use StringTranslationTrait;
/**
* The quick form instance manager.
*
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
*/
protected $quickFormInstanceManager;
/**
* QuickFormTaskLink constructor.
*
* @param \Drupal\farm_quick\QuickFormInstanceManagerInterface $quick_form_instance_manager
* The quick form plugin manager.
*/
public function __construct(QuickFormInstanceManagerInterface $quick_form_instance_manager) {
$this->quickFormInstanceManager = $quick_form_instance_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$container->get('quick_form.instance_manager')
);
}
/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
$links = [];
// Load quick forms.
$quick_forms = $this->quickFormInstanceManager->getInstances();
// Add links for each quick form.
foreach ($quick_forms as $id => $quick_form) {
$route_name = 'farm.quick.' . $id;
$links[$route_name] = [
'title' => $this->t('Quick form'),
'route_name' => $route_name,
'base_route' => $route_name,
'weight' => 0,
] + $base_plugin_definition;
}
return $links;
}
}

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

@@ -0,0 +1,137 @@
<?php
namespace Drupal\farm_quick\Plugin\QuickForm;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Psr\Container\ContainerInterface;
/**
* Base class for quick forms.
*/
class QuickFormBase extends PluginBase implements QuickFormInterface, ContainerFactoryPluginInterface {
use MessengerTrait;
use StringTranslationTrait;
/**
* The quick form ID.
*
* @var string
*/
protected string $quickId;
/**
* Constructs a QuickFormBase object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, MessengerInterface $messenger) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->messenger = $messenger;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('messenger')
);
}
/**
* {@inheritdoc}
*/
final public function setQuickId(string $id) {
return $this->quickId = $id;
}
/**
* {@inheritdoc}
*/
final public function getQuickId() {
return $this->quickId ?? $this->getPluginId();
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return $this->getQuickId();
}
/**
* {@inheritdoc}
*/
public function getLabel() {
return $this->pluginDefinition['label'] ?? '';
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->pluginDefinition['description'] ?? '';
}
/**
* {@inheritdoc}
*/
public function getHelpText() {
return $this->pluginDefinition['helpText'] ?? '';
}
/**
* {@inheritdoc}
*/
public function getPermissions() {
return $this->pluginDefinition['permissions'] ?? [];
}
/**
* {@inheritdoc}
*/
public function access(AccountInterface $account) {
$permissions = $this->getPermissions();
return AccessResult::allowedIfHasPermissions($account, $permissions);
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
return [];
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// Validation is optional.
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Submit is optional, but presumably this will be overridden.
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Drupal\farm_quick\Plugin\QuickForm;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Interface for quick forms.
*/
interface QuickFormInterface extends FormInterface {
/**
* Returns the quick form ID.
*
* @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 label.
*
* @return string
* The quick form label.
*/
public function getLabel();
/**
* Returns the quick form description.
*
* @return string
* The quick form description.
*/
public function getDescription();
/**
* Returns the quick form help text.
*
* @return string
* The quick form help text.
*/
public function getHelpText();
/**
* Returns the list of access permissions for the quick form.
*
* @return string[]
* An array of permission strings.
*/
public function getPermissions();
/**
* Checks access for the quick form.
*
* @param \Drupal\Core\Session\AccountInterface $account
* Run access checks for this account.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
*/
public function access(AccountInterface $account);
}

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']]);
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,157 @@
<?php
namespace Drupal\farm_quick;
use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a listing of template entities.
*/
class QuickFormListBuilder extends ConfigEntityListBuilder {
/**
* The quick form instance manager.
*
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
*/
protected $quickFormInstanceManager;
/**
* Constructs a new QuickFormListBuilder object.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type definition.
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* The entity storage class.
* @param \Drupal\farm_quick\QuickFormInstanceManagerInterface $quick_form_instance_manager
* The quick form instance manager.
*/
public function __construct(EntityTypeInterface $entity_type, EntityStorageInterface $storage, QuickFormInstanceManagerInterface $quick_form_instance_manager) {
parent::__construct($entity_type, $storage);
$this->quickFormInstanceManager = $quick_form_instance_manager;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$entity_type,
$container->get('entity_type.manager')->getStorage($entity_type->id()),
$container->get('quick_form.instance_manager'),
);
}
/**
* {@inheritdoc}
*/
public function load() {
return $this->quickFormInstanceManager->getInstances();
}
/**
* {@inheritdoc}
*/
public function render() {
$render['table'] = [
'#type' => 'table',
'#header' => $this->buildHeader(),
'#caption' => $this->t('Configured quick forms'),
'#rows' => [],
'#empty' => $this->t('There are no configured @label.', ['@label' => $this->entityType->getPluralLabel()]),
'#cache' => [
'contexts' => $this->entityType->getListCacheContexts(),
'tags' => $this->entityType->getListCacheTags(),
],
];
$render['default'] = [
'#type' => 'table',
'#header' => $this->buildHeader(),
'#caption' => $this->t('Default quick forms'),
'#rows' => [],
'#empty' => $this->t('There are no default @label.', ['@label' => $this->entityType->getPluralLabel()]),
];
// Load all quick form instances into proper table.
$quick_form_instances = $this->load();
foreach ($quick_form_instances as $entity) {
$target = $entity->isNew() ? 'default' : 'table';
if ($row = $this->buildRow($entity)) {
$render[$target][$entity->id()] = $row;
}
}
return $render;
}
/**
* {@inheritdoc}
*/
public function buildHeader() {
$header['enabled'] = $this->t('Enabled');
$header['type'] = $this->t('Plugin');
$header['label'] = $this->t('Label');
$header['id'] = $this->t('ID');
$header['description'] = $this->t('Description');
return $header + parent::buildHeader();
}
/**
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface $quick_form */
$quick_form = $entity;
$row['enabled'] = [
'#type' => 'checkbox',
'#checked' => $quick_form->status(),
'#attributes' => [
'disabled' => 'disabled',
],
];
$row['type'] = [
'#plain_text' => $quick_form->getPlugin()->getLabel(),
];
$row['label'] = [
'#plain_text' => $quick_form->getLabel(),
];
$row['id'] = [
'#plain_text' => $quick_form->id(),
];
$row['description'] = [
'#plain_text' => $quick_form->getDescription(),
];
return $row + parent::buildRow($entity);
}
/**
* {@inheritdoc}
*/
public function getDefaultOperations(EntityInterface $entity) {
$operations = parent::getDefaultOperations($entity);
// Override operations for default quick form instances.
if ($entity->isNew()) {
// Remove edit operation.
unset($operations['edit']);
// Add override operation.
$operations['override'] = [
'title' => $this->t('Override'),
'weight' => 0,
'url' => $this->ensureDestination(Url::fromRoute('farm_quick.add_form', ['plugin' => $entity->getPluginId()], ['query' => ['override' => TRUE]])),
];
}
return $operations;
}
}

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

@@ -0,0 +1,37 @@
<?php
namespace Drupal\farm_quick;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
/**
* Quick form manager class.
*/
class QuickFormPluginManager extends DefaultPluginManager {
/**
* Constructs a QuickFormPluginManager object.
*
* @param \Traversable $namespaces
* An object that implements \Traversable which contains the root paths
* keyed by the corresponding namespace to look for plugin implementations.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
* Cache backend instance to use.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler to invoke the alter hook with.
*/
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
parent::__construct(
'Plugin/QuickForm',
$namespaces,
$module_handler,
'Drupal\farm_quick\Plugin\QuickForm\QuickFormInterface',
'Drupal\farm_quick\Annotation\QuickForm'
);
$this->alterInfo('quick_form_info');
$this->setCacheBackend($cache_backend, 'quick_forms');
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Drupal\farm_quick\Routing;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\farm_quick\Form\QuickForm;
use Drupal\farm_quick\QuickFormInstanceManagerInterface;
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 instance manager.
*
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
*/
protected $quickFormInstanceManager;
/**
* Constructs a QuickFormRoutes object.
*
* @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'),
);
}
/**
* Provides routes for quick forms.
*
* @return \Symfony\Component\Routing\RouteCollection
* Returns a route collection.
*/
public function routes(): RouteCollection {
$route_collection = new RouteCollection();
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface[] $quick_forms */
$quick_forms = $this->quickFormInstanceManager->getInstances();
foreach ($quick_forms as $id => $quick_form) {
// Skip quick forms that are disabled.
if (!$quick_form->status()) {
continue;
}
// Build a route for the quick form.
$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

@@ -0,0 +1,64 @@
<?php
namespace Drupal\farm_quick\Traits;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\asset\Entity\Asset;
/**
* Provides methods for working with assets.
*/
trait QuickAssetTrait {
use MessengerTrait;
use StringTranslationTrait;
use QuickStringTrait;
/**
* 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();
/**
* Create an asset.
*
* @param array $values
* An array of values to initialize the asset with.
*
* @return \Drupal\asset\Entity\AssetInterface
* The asset entity that was created.
*/
protected function createAsset(array $values = []) {
// Trim the asset name to 255 characters.
if (!empty($values['name'])) {
$values['name'] = $this->trimString($values['name'], 255);
}
// Start a new asset entity with the provided values.
/** @var \Drupal\asset\Entity\AssetInterface $asset */
$asset = Asset::create($values);
// Track which quick form created the entity.
$asset->quick[] = $this->getQuickId();
// Save the asset.
$asset->save();
// Display a message with a link to the asset.
$message = $this->t('Asset created: <a href=":url">@name</a>', [':url' => $asset->toUrl()->toString(), '@name' => $asset->label()]);
$this->messenger->addStatus($message);
// Return the asset entity.
return $asset;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Drupal\farm_quick\Traits;
/**
* Provides methods for building common quick form elements.
*/
trait QuickFormElementsTrait {
/**
* Build an inline container element.
*
* @return array
* Returns a render array.
*/
public function buildInlineContainer() {
return [
'#type' => 'container',
'#attributes' => [
'class' => [
'inline-container',
],
],
];
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Drupal\farm_quick\Traits;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\log\Entity\Log;
/**
* Provides methods for working with logs.
*/
trait QuickLogTrait {
use MessengerTrait;
use StringTranslationTrait;
use QuickQuantityTrait;
use QuickStringTrait;
/**
* 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();
/**
* Create a log.
*
* @param array $values
* An array of values to initialize the log with.
*
* @return \Drupal\log\Entity\LogInterface
* The log entity that was created.
*/
protected function createLog(array $values = []) {
// Trim the log name to 255 characters.
if (!empty($values['name'])) {
$values['name'] = $this->trimString($values['name'], 255);
}
// Start a new log entity with the provided values.
/** @var \Drupal\log\Entity\LogInterface $log */
$log = Log::create($values);
// If quantity measurements are provided, reference them from the log.
if (!empty($values['quantity'])) {
foreach ($values['quantity'] as $qty) {
// If the quantity is an array of values, pass it to createQuantity.
if (is_array($qty)) {
$log->quantity[] = $this->createQuantity($qty, $log->bundle());
}
// Otherwise, add it directly to the log.
else {
$log->quantity[] = $qty;
}
}
}
// If not specified, set the log's status to "done".
if (!isset($values['status'])) {
$log->status = 'done';
}
// Track which quick form created the entity.
$log->quick[] = $this->getQuickId();
// Save the log.
$log->save();
// Display a message with a link to the log.
$message = $this->t('Log created: <a href=":url">@name</a>', [':url' => $log->toUrl()->toString(), '@name' => $log->label()]);
$this->messenger->addStatus($message);
// Return the log entity.
return $log;
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace Drupal\farm_quick\Traits;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides methods for loading prepopulated entity references.
*/
trait QuickPrepopulateTrait {
/**
* 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();
/**
* Get prepopulated entities.
*
* @param string $entity_type
* The entity type to prepopulate.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* An array of entities.
*/
protected function getPrepopulatedEntities(string $entity_type, FormStateInterface $form_state) {
// Initialize a temporary value in the form state.
if (!$form_state->hasTemporaryValue("quick_prepopulate_$entity_type")) {
$this->initPrepoluatedEntities($entity_type, $form_state);
}
// Return the prepopulated entities saved in the form state.
$entity_ids = $form_state->getTemporaryValue("quick_prepopulate_$entity_type") ?? [];
return \Drupal::entityTypeManager()->getStorage($entity_type)->loadMultiple($entity_ids);
}
/**
* Helper function to initialize prepopulated entities in the form state.
*
* @param string $entity_type
* The entity type to prepopulate.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*/
protected function initPrepoluatedEntities(string $entity_type, FormStateInterface $form_state) {
// Save the current user.
$user = \Drupal::currentUser();
// 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->getQuickId());
// Load entities from the temp store.
$temp_store_key = $user->id() . ':' . $entity_type;
$temp_store_entities = $temp_store->get($temp_store_key) ?? [];
// Convert entities to entity ids.
$temp_store_entity_ids = array_map(function (EntityInterface $entity) {
return $entity->id();
}, $temp_store_entities);
// Load entities from the query params.
$query = \Drupal::request()->query;
$query_entity_ids = $query->has('asset') ? (array) $query->all()['asset'] : [];
// Only include the unique ids.
$entity_ids = array_unique(array_merge($temp_store_entity_ids, $query_entity_ids));
// Filter to entities the user has access to.
$accessible_entities = [];
if (!empty($entity_ids)) {
// Return entities the user has access to.
$entities = \Drupal::entityTypeManager()->getStorage($entity_type)->loadMultiple($entity_ids);
$accessible_entities = array_filter($entities, function (EntityInterface $asset) use ($user) {
return $asset->access('view', $user);
});
}
// Save the accessible entity ids as a temporary value in the form state.
$accessible_entity_ids = array_map(function (EntityInterface $entity) {
return $entity->id();
}, $accessible_entities);
$form_state->setTemporaryValue("quick_prepopulate_$entity_type", $accessible_entity_ids);
// Finally, remove the entities from the temp store.
$temp_store->delete($temp_store_key);
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Drupal\farm_quick\Traits;
use Drupal\fraction\Fraction;
use Drupal\quantity\Entity\Quantity;
/**
* Provides methods for working with quantities.
*/
trait QuickQuantityTrait {
use QuickStringTrait;
use QuickTermTrait;
/**
* Create a quantity.
*
* @param array $values
* An array of values to initialize the quantity with.
* @param string|null $log_type
* Optionally specify the log type this quantity will be added to. This is
* used to automatically determine what the default quantity type of the
* log should be.
*
* @return \Drupal\quantity\Entity\QuantityInterface
* The quantity entity that was created.
*/
protected function createQuantity(array $values = [], ?string $log_type = NULL) {
// Trim the quantity label to 255 characters.
if (!empty($values['label'])) {
$values['label'] = $this->trimString($values['label'], 255);
}
// If a type isn't set, get the default type.
if (empty($values['type'])) {
$values['type'] = farm_log_quantity_default_type($log_type);
}
// Split value into numerator and denominator, if it isn't already.
if (!empty($values['value']) && !is_array($values['value'])) {
$fraction = Fraction::createFromDecimal($values['value']);
$values['value'] = [
'numerator' => $fraction->getNumerator(),
'denominator' => $fraction->getDenominator(),
];
}
// If the units are a term name, create or load the unit taxonomy term.
if (!empty($values['units']) && is_string($values['units'])) {
$term = $this->createOrLoadTerm($values['units'], 'unit');
$values['units'] = $term->id();
}
// Else check if a units term ID is provided and use that instead.
elseif (!empty($values['units_id'])) {
$values['units'] = $values['units_id'];
unset($values['units_id']);
}
// Start a new quantity entity with the provided values.
/** @var \Drupal\quantity\Entity\QuantityInterface $quantity */
$quantity = Quantity::create($values);
// Save the quantity.
$quantity->save();
// Return the quantity entity.
return $quantity;
}
}

View File

@@ -0,0 +1,168 @@
<?php
namespace Drupal\farm_quick\Traits;
/**
* Provides methods for generating record name strings.
*/
trait QuickStringTrait {
/**
* Trims a string down to the specified length, respecting word boundaries.
*
* @param string $value
* The string which should be trimmed.
* @param int $max_length
* Maximum length of the string, the rest gets truncated.
* @param string $suffix
* A suffix to append to the end of the string, if it is trimmed.
* Defaults to an ellipsis ().
*
* @return string
* The trimmed string.
*/
protected function trimString(string $value, int $max_length, string $suffix = '…') {
// First trim whitespace.
$value = trim($value);
// If the string fits, we're done here.
if (mb_strlen($value) <= $max_length) {
return $value;
}
// Use PHP wordwrap() to wrap the text to multiple lines on word boundaries,
// then explode() the lines into an array so we can take the first line.
// Subtract the suffix length so we can add it afterwards.
$width = $max_length - mb_strlen($suffix);
if (empty($width)) {
return $suffix;
}
$lines = explode("\n", wordwrap($value, $width, "\n", TRUE));
return reset($lines) . $suffix;
}
/**
* Concatenate prioritized strings together into one, respecting max length.
*
* @param array $strings
* An array of string values to include, in order of appearance. These
* strings will be concatenated with a space. This array can be optionally
* keyed to allow the $priority_keys argument to specify which strings
* should not be truncated (if possible).
* @param array $priority_keys
* An array of strings that correspond to keys in the $strings array which
* should be given higher priority. This is used if the total length of the
* generated string exceeds the maximum allowed length.
* @param int $max_length
* The maximum length of the final string. Defaults to 255.
* @param string $suffix
* A suffix to append to the end of the string, if it is trimmed.
* Defaults to an ellipsis ().
*
* @return string
* The joined, prioritized, and trimmed string.
*/
protected function prioritizedString(array $strings = [], array $priority_keys = [], int $max_length = 255, string $suffix = '…') {
// Trim each string and remove empty ones.
foreach ($strings as $key => $string) {
$strings[$key] = trim($string);
if (empty($strings[$key])) {
unset($strings[$key]);
}
}
// Concatenate all the strings together, separated by spaces.
$combined = implode(' ', $strings);
// If the full string fits, return it.
if (mb_strlen($combined) <= $max_length) {
return $combined;
}
// If no priority keys were specified, or all keys are priority, trim the
// combined string and return it.
if (empty($priority_keys) || count($strings) == count($priority_keys)) {
return $this->trimString($combined, $max_length, $suffix);
}
// Split strings into priority and non-priority.
$priority_strings = [];
$non_priority_strings = [];
foreach ($strings as $key => $value) {
if (in_array($key, $priority_keys)) {
$priority_strings[$key] = $value;
}
else {
$non_priority_strings[$key] = $value;
}
}
// If the priority strings alone will not fit, join and trim them alone.
$priority_string = implode(' ', $priority_strings);
if (mb_strlen($priority_string) > $max_length) {
return $this->trimString($priority_string, $max_length);
}
// Measure how many characters are left after accounting for priority
// strings and spaces between strings.
$remaining_length = $max_length - mb_strlen($priority_string) - count($non_priority_strings);
// Divide the remaining characters by the number of non-priority strings.
$non_priority_max_length = floor($remaining_length / count($non_priority_strings));
// If the maximum length of non-priority strings is greater than zero,
// trim each, concatenate the full string, perform a final trim, and return.
if (!empty($non_priority_max_length)) {
$parts = [];
foreach ($strings as $key => $value) {
if (in_array($key, $priority_keys)) {
$parts[] = $value;
}
else {
$parts[] = $this->trimString($value, $non_priority_max_length);
}
}
return $this->trimString(implode(' ', $parts), $max_length);
}
// Otherwise, trim and return the priority string.
return $this->trimString($priority_string, $max_length);
}
/**
* Generate a summary of entity labels.
*
* Note that this does NOT sanitize the entity labels. It is the
* responsibility of downstream code to do so, if it is printing text to the
* page.
*
* @param array $entities
* An array of entities.
* @param int $cutoff
* The number of entity labels to include before summarizing the rest.
* If the number of entities exceeds the cutoff, the rest will be summarized
* as "(+X more)". If the number of entities is less than or equal to the
* cutoff, or if the cutoff is 0, all entity labels will be included.
*
* @return string
* Returns a string summarizing the entity labels.
*/
protected function entityLabelsSummary(array $entities, $cutoff = 3) {
$names = [];
foreach ($entities as $entity) {
$names[] = $entity->label();
}
if ($cutoff != 0) {
array_splice($names, $cutoff);
}
$output = implode(', ', $names);
$diff = count($entities) - count($names);
if ($diff > 0) {
$output .= ' (+' . $diff . ' ' . t('more') . ')';
}
return $output;
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Drupal\farm_quick\Traits;
use Drupal\taxonomy\Entity\Term;
/**
* Provides methods for working with terms.
*/
trait QuickTermTrait {
use QuickStringTrait;
/**
* Create a term.
*
* @param array $values
* An array of values to initialize the term with.
*
* @return \Drupal\taxonomy\TermInterface
* The term entity that was created.
*/
protected function createTerm(array $values = []) {
// Trim the term name to 255 characters.
if (!empty($values['name'])) {
$values['name'] = $this->trimString($values['name'], 255);
}
// Alias 'vocabulary' to 'vid'.
if (!empty($values['vocabulary'])) {
$values['vid'] = $values['vocabulary'];
}
// Start a new term entity with the provided values.
/** @var \Drupal\taxonomy\TermInterface $term */
$term = Term::create($values);
// Save the term.
$term->save();
// Return the term entity.
return $term;
}
/**
* Given a term name, create or load a matching term entity.
*
* @param string $name
* The term name.
* @param string $vocabulary
* The vocabulary to search or create in.
*
* @return \Drupal\taxonomy\TermInterface
* The term entity that was created or loaded.
*/
protected function createOrLoadTerm(string $name, string $vocabulary) {
// First try to load an existing term.
$search = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties(['name' => $name, 'vid' => $vocabulary]);
if (!empty($search)) {
return reset($search);
}
// Create a new term.
return $this->createTerm([
'name' => $name,
'vid' => $vocabulary,
]);
}
}

View File

@@ -0,0 +1,7 @@
langcode: en
status: true
id: test
label: Test
description: ''
workflow: asset_default
new_revision: true

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,11 @@
langcode: en
status: true
id: test
label: Test
description: ''
name_pattern: 'Test log [log:id]'
workflow: log_default
new_revision: true
third_party_settings:
farm_log_quantity:
default_quantity_type: test

View File

@@ -0,0 +1,10 @@
langcode: en
status: true
dependencies:
enforced:
module:
- farm_quick_test
id: test
label: Test
description: 'Test quantity type.'
new_revision: true

View File

@@ -0,0 +1,10 @@
langcode: en
status: true
dependencies:
enforced:
module:
- farm_quick_test
id: test2
label: Test2
description: 'Test2 quantity type.'
new_revision: true

View File

@@ -0,0 +1,6 @@
langcode: en
status: true
name: Test
vid: test
description: ''
weight: 0

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,10 @@
name: farmOS quick form tests
type: module
description: Support module for farmOS quick form testing.
package: Testing
core_version_requirement: ^10
dependencies:
- farm:farm_quick
- farm:asset
- farm:quantity
- log:log

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

@@ -0,0 +1,105 @@
<?php
namespace Drupal\farm_quick_test\Plugin\QuickForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\farm_quick\Plugin\QuickForm\QuickFormBase;
use Drupal\farm_quick\Traits\QuickAssetTrait;
use Drupal\farm_quick\Traits\QuickLogTrait;
use Drupal\farm_quick\Traits\QuickQuantityTrait;
use Drupal\farm_quick\Traits\QuickTermTrait;
/**
* Test quick form.
*
* @QuickForm(
* id = "test",
* label = @Translation("Test quick form"),
* description = @Translation("Test quick form description."),
* helpText = @Translation("Test quick form help text."),
* permissions = {
* "create test log",
* }
* )
*/
class Test extends QuickFormBase {
use QuickAssetTrait;
use QuickLogTrait;
use QuickQuantityTrait;
use QuickTermTrait;
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, ?string $id = NULL) {
// Test field.
$form['test'] = [
'#type' => 'number',
'#title' => $this->t('Test field'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Get the submitted value.
$value = $form_state->getValue('test');
// Create an asset.
$asset = $this->createAsset([
'type' => 'test',
'name' => $value,
]);
// Create a log.
$log = $this->createLog([
'type' => 'test',
'name' => $value,
'quantity' => [
[
'measure' => 'count',
'value' => $value,
'units' => 'tests',
],
],
]);
// Create a quantity.
$quantity = $this->createQuantity([
'measure' => 'count',
'value' => $value,
'units' => 'tests',
'label' => $this->t('test label'),
'type' => 'test2',
]);
// Create a term.
$term1 = $this->createTerm([
'name' => 'test1',
'vocabulary' => 'test',
]);
// Create a term with createOrLoadTerm().
$term2 = $this->createOrLoadTerm('test2', 'test');
// Load a term with createOrLoadTerm().
$term3 = $this->createOrLoadTerm('test2', 'test');
// Save entities to form state for automated test review.
$storage = [];
$storage['assets'][] = $asset;
$storage['logs'][] = $log;
$storage['quantities'][] = $quantity;
$storage['terms'][] = $term1;
$storage['terms'][] = $term2;
$storage['terms'][] = $term3;
$form_state->setStorage($storage);
}
}

View File

@@ -0,0 +1,173 @@
<?php
namespace Drupal\Tests\farm_quick\Functional;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Tests\farm_test\Functional\FarmBrowserTestBase;
use Drupal\farm_quick\Entity\QuickFormInstance;
/**
* Tests the quick form framework.
*
* @group farm
*/
class QuickFormTest extends FarmBrowserTestBase {
use StringTranslationTrait;
/**
* {@inheritdoc}
*/
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.
*/
public function testQuickForms() {
// Create and login a test user with no permissions.
$user = $this->createUser();
$this->drupalLogin($user);
// Go to the quick form index and confirm that access is denied.
$this->drupalGet('quick');
$this->assertSession()->statusCodeEquals(403);
// 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
// quick forms are visible.
$this->drupalGet('quick');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextContains($this->t('You do not have any quick forms.'));
// Go to the test quick form and confirm that access is denied.
$this->drupalGet('quick/test');
$this->assertSession()->statusCodeEquals(403);
// Create and login a test user with access to the quick form index, and
// permission to create test logs.
$user = $this->createUser(['view quick_form', 'create test log']);
$this->drupalLogin($user);
// 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 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"');
// Attempt to load the edit form for the unsaved configurable_test quick
// form and confirm 404 not found.
$this->drupalGet('setup/quick/foo/configurable_test');
$this->assertSession()->statusCodeEquals(404);
// 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"');
// Attempt to load the edit form for saved configurable_test2 quick
// form and confirm 403.
$this->drupalGet('setup/quick/configurable_test2/edit');
$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 configurable_test2 quick form and confirm that the default
// value field is visible and the default value is 500.
$this->drupalGet('setup/quick/configurable_test2/edit');
$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('setup/quick/configurable_test2/edit');
$this->assertSession()->responseContains('value="600"');
// Attempt to load an edit form for a non-existent quick form and
// confirm 404 not found.
$this->drupalGet('setup/quick/foo/edit');
$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();
// Rebuild routes.
\Drupal::service('router.builder')->rebuildIfNeeded();
// 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'));
// Delete the config entity and confirm that it is removed.
$config_entity->delete();
\Drupal::service('router.builder')->rebuildIfNeeded();
$this->drupalGet('quick');
$this->assertSession()->statusCodeEquals(200);
$this->assertSession()->pageTextNotContains($this->t('Test requiresEntity quick form'));
$this->drupalGet('quick/requires_entity_test');
$this->assertSession()->statusCodeEquals(404);
}
}

View File

@@ -0,0 +1,184 @@
<?php
namespace Drupal\Tests\farm_quick\Kernel;
use Drupal\Core\Form\FormState;
use Drupal\KernelTests\KernelTestBase;
use Drupal\farm_quick\Form\QuickFormEntityForm;
/**
* Tests for farmOS quick forms.
*
* @group farm
*/
class QuickFormTest extends KernelTestBase {
/**
* The quick form instance manager.
*
* @var \Drupal\farm_quick\QuickFormInstanceManagerInterface
*/
protected $quickFormInstanceManager;
/**
* {@inheritdoc}
*/
protected static $modules = [
'asset',
'entity_reference_revisions',
'farm_field',
'farm_log_quantity',
'farm_quick',
'farm_quick_test',
'farm_unit',
'fraction',
'log',
'options',
'quantity',
'state_machine',
'taxonomy',
'text',
'user',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->quickFormInstanceManager = \Drupal::service('quick_form.instance_manager');
$this->installEntitySchema('asset');
$this->installEntitySchema('log');
$this->installEntitySchema('taxonomy_term');
$this->installEntitySchema('quantity');
$this->installEntitySchema('user');
$this->installConfig([
'farm_quick_test',
]);
}
/**
* Test quick form discovery.
*/
public function testQuickFormDiscovery() {
// Load quick forms.
/** @var \Drupal\farm_quick\Entity\QuickFormInstanceInterface[] $quick_forms */
$quick_forms = $this->quickFormInstanceManager->getInstances();
// Confirm that three quick forms were discovered.
$this->assertEquals(3, count($quick_forms));
// 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 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());
}
/**
* Test quick form submission.
*/
public function testQuickFormSubmission() {
// Programmatically submit the test quick form.
$form_state = (new FormState())->setValues([
'test' => '12',
]);
\Drupal::formBuilder()->submitForm('\Drupal\farm_quick\Form\QuickForm', $form_state, 'test');
// Load the form state storage.
$storage = $form_state->getStorage();
// Confirm that an asset was created.
$this->assertNotEmpty($storage['assets'][0]->id());
// Confirm that the asset is linked to the quick form.
$this->assertEquals('test', $storage['assets'][0]->quick[0]);
// Confirm that a log was created.
$this->assertNotEmpty($storage['logs'][0]->id());
// Confirm that the log is linked to the quick form.
$this->assertEquals('test', $storage['logs'][0]->quick[0]);
// Confirm that the log's quantity type is test.
$this->assertEquals('test', $storage['logs'][0]->get('quantity')->referencedEntities()[0]->bundle());
// Confirm that a quantity was created and its type is test2.
$this->assertNotEmpty($storage['quantities'][0]->id());
$this->assertEquals('test2', $storage['quantities'][0]->bundle());
// Confirm that three terms were created or loaded.
$this->assertEquals(3, count($storage['terms']));
foreach ($storage['terms'] as $term) {
$this->assertNotEmpty($term->id());
}
// Confirm that the second and third terms have the same ID.
$match = $storage['terms'][1]->id() == $storage['terms'][2]->id();
$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 quick form entity form.
$form = QuickFormEntityForm::create(\Drupal::getContainer());
$form->setModuleHandler(\Drupal::moduleHandler());
$form->setEntity($quick_form);
$form_state = (new FormState())->setValues([
// Set the ID and label because no default value is provided for these
// in the form unless the override query param is set.
'id' => $quick_form->id(),
'label' => (string) $quick_form->label(),
'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']);
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace Drupal\Tests\farm_quick\Kernel;
use Drupal\Core\Form\FormState;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\user\Traits\UserCreationTrait;
/**
* Base class that modules can use to test their quick forms.
*
* @group farm
*
* @internal
*/
abstract class QuickFormTestBase extends KernelTestBase {
use UserCreationTrait;
/**
* Quick form ID.
*
* @var string
*/
protected $quickFormId;
/**
* Asset entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $assetStorage;
/**
* Log entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $logStorage;
/**
* Taxonomy term entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $termStorage;
/**
* Quantity entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $quantityStorage;
/**
* {@inheritdoc}
*/
protected static $modules = [
'asset',
'entity',
'entity_reference_revisions',
'farm_entity',
'farm_entity_fields',
'farm_field',
'farm_format',
'farm_location',
'farm_log',
'farm_log_asset',
'farm_log_quantity',
'farm_map',
'farm_quick',
'file',
'filter',
'fraction',
'geofield',
'image',
'log',
'options',
'quantity',
'rest',
'serialization',
'state_machine',
'system',
'taxonomy',
'text',
'user',
'views',
'views_geojson',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->setUpCurrentUser([], [], TRUE);
$this->assetStorage = \Drupal::entityTypeManager()->getStorage('asset');
$this->logStorage = \Drupal::entityTypeManager()->getStorage('log');
$this->termStorage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$this->quantityStorage = \Drupal::entityTypeManager()->getStorage('quantity');
$this->installEntitySchema('asset');
$this->installEntitySchema('log');
$this->installEntitySchema('taxonomy_term');
$this->installEntitySchema('quantity');
$this->installEntitySchema('user');
$this->installEntitySchema('user_role');
$this->installConfig([
'farm_format',
'farm_location',
'system',
]);
}
/**
* Helper function for performing a quick form submission.
*
* @param array $values
* The values to submit.
*/
protected function submitQuickForm(array $values = []) {
$form_arg = '\Drupal\farm_quick\Form\QuickForm';
$form_state = (new FormState())->setValues($values);
\Drupal::formBuilder()->submitForm($form_arg, $form_state, $this->quickFormId);
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace Drupal\Tests\farm_quick\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\asset\Entity\Asset;
use Drupal\asset\Entity\AssetType;
use Drupal\farm_quick\Traits\QuickStringTrait;
/**
* Tests for quick string trait methods.
*
* @group farm
*/
class QuickStringTest extends KernelTestBase {
use QuickStringTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'asset',
'farm_quick',
'state_machine',
'user',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('asset');
}
/**
* Test trimString() method.
*/
public function testTrimString() {
// Test that a 255 character string is not trimmed.
$long_string = 'Lorem ipsum dolor sit amet, nonummy ligula volutpat hac integer nonummy. Suspendisse ultricies, congue etiam tellus, erat libero, nulla eleifend, mauris pellentesque. Suspendisse integer praesent vel, integer gravida mauris, fringilla vehicula lacinia non';
$name = $this->trimString($long_string, 255);
$this->assertEquals($long_string, $name);
// Test that a 256 character string is trimmed on a word boundary.
$extra_long_string = 'Lorem ipsum dolor sit amet, nonummy ligula volutpat hac integer nonummy. Suspendisse ultricies, congue etiam tellus, erat libero, nulla eleifend, mauris pellentesque. Suspendisse integer praesent vel, integer gravida mauris, fringilla vehicula lacinia non!';
$trimmed_extra_long_string = 'Lorem ipsum dolor sit amet, nonummy ligula volutpat hac integer nonummy. Suspendisse ultricies, congue etiam tellus, erat libero, nulla eleifend, mauris pellentesque. Suspendisse integer praesent vel, integer gravida mauris, fringilla vehicula lacinia…';
$name = $this->trimString($extra_long_string, 255);
$this->assertEquals($trimmed_extra_long_string, $name);
}
/**
* Test prioritizedString() method.
*/
public function testPrioritizedString() {
// Define simple name parts.
$parts = [
'foo' => 'Foo',
'bar' => 'Bar',
'baz' => 'Baz',
];
// Test simple name.
$name = $this->prioritizedString($parts);
$this->assertEquals('Foo Bar Baz', $name);
// Test simple maximum lengths.
$name = $this->prioritizedString($parts, [], 1);
$this->assertEquals('…', $name);
$name = $this->prioritizedString($parts, [], 5);
$this->assertEquals('Foo…', $name);
$name = $this->prioritizedString($parts, [], 10);
$this->assertEquals('Foo Bar…', $name);
// Test custom suffix.
$name = $this->prioritizedString($parts, [], 3, 'OO');
$this->assertEquals('FOO', $name);
// Test priority keys.
$priority_keys = ['foo', 'baz'];
$name = $this->prioritizedString($parts, $priority_keys, 10);
$this->assertEquals('Foo B… Baz', $name);
}
/**
* Test entityLabelsSummary() method.
*/
public function testEntityLabelsSummary() {
// Create a test asset type.
$asset_type = AssetType::create([
'id' => 'test',
'label' => 'Test',
'workflow' => 'asset_default',
]);
$asset_type->save();
// Create 10 assets with randomly generated names.
$assets = [];
for ($i = 0; $i < 10; $i++) {
$asset = Asset::create([
'name' => $this->randomString(),
'type' => 'test',
'status' => 'active',
]);
$asset->save();
$assets[] = $asset;
}
// Test default with a cutoff of 3.
$expected = $assets[0]->label() . ', ' . $assets[1]->label() . ', ' . $assets[2]->label() . ' (+7 more)';
$name_summary = $this->entityLabelsSummary($assets);
$this->assertEquals($expected, $name_summary);
// Test with a cutoff of 1.
$expected = $assets[0]->label() . ' (+9 more)';
$name_summary = $this->entityLabelsSummary($assets, 1);
$this->assertEquals($expected, $name_summary);
// Test with a cutoff of 0.
$labels = [];
foreach ($assets as $asset) {
$labels[] = $asset->label();
}
$expected = implode(', ', $labels);
$name_summary = $this->entityLabelsSummary($assets, 0);
$this->assertEquals($expected, $name_summary);
}
}