Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 77 |
QuickEditFieldForm | |
0.00% |
0 / 1 |
|
0.00% |
0 / 9 |
552 | |
0.00% |
0 / 77 |
__construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 5 |
|||
create | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 7 |
|||
getFormId | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
buildForm | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 19 |
|||
init | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 15 |
|||
validateForm | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
submitForm | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 4 |
|||
buildEntity | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 8 |
|||
simplify | |
0.00% |
0 / 1 |
56 | |
0.00% |
0 / 14 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\quickedit\Form\QuickEditFieldForm. | |
*/ | |
namespace Drupal\quickedit\Form; | |
use Drupal\Core\Entity\EntityInterface; | |
use Drupal\Core\Entity\EntityStorageInterface; | |
use Drupal\Core\Entity\EntityChangedInterface; | |
use Drupal\Core\Extension\ModuleHandlerInterface; | |
use Drupal\Core\Form\FormBase; | |
use Drupal\Core\Form\FormStateInterface; | |
use Drupal\Core\Render\Element; | |
use Drupal\Core\Entity\Entity\EntityFormDisplay; | |
use Drupal\user\PrivateTempStoreFactory; | |
use Symfony\Component\DependencyInjection\ContainerInterface; | |
use Symfony\Component\Validator\Validator\ValidatorInterface; | |
/** | |
* Builds and process a form for editing a single entity field. | |
*/ | |
class QuickEditFieldForm extends FormBase { | |
/** | |
* Stores the tempstore factory. | |
* | |
* @var \Drupal\user\PrivateTempStoreFactory | |
*/ | |
protected $tempStoreFactory; | |
/** | |
* The module handler. | |
* | |
* @var \Drupal\Core\Extension\ModuleHandlerInterface | |
*/ | |
protected $moduleHandler; | |
/** | |
* The node type storage. | |
* | |
* @var \Drupal\Core\Entity\EntityStorageInterface | |
*/ | |
protected $nodeTypeStorage; | |
/** | |
* The typed data validator. | |
* | |
* @var \Symfony\Component\Validator\Validator\ValidatorInterface | |
*/ | |
protected $validator; | |
/** | |
* Constructs a new EditFieldForm. | |
* | |
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory | |
* The tempstore factory. | |
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | |
* The module handler. | |
* @param \Drupal\Core\Entity\EntityStorageInterface $node_type_storage | |
* The node type storage. | |
* @param \Symfony\Component\Validator\Validator\ValidatorInterface $validator | |
* The typed data validator service. | |
*/ | |
public function __construct(PrivateTempStoreFactory $temp_store_factory, ModuleHandlerInterface $module_handler, EntityStorageInterface $node_type_storage, ValidatorInterface $validator) { | |
$this->moduleHandler = $module_handler; | |
$this->nodeTypeStorage = $node_type_storage; | |
$this->tempStoreFactory = $temp_store_factory; | |
$this->validator = $validator; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public static function create(ContainerInterface $container) { | |
return new static( | |
$container->get('user.private_tempstore'), | |
$container->get('module_handler'), | |
$container->get('entity.manager')->getStorage('node_type'), | |
$container->get('typed_data_manager')->getValidator() | |
); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getFormId() { | |
return 'quickedit_field_form'; | |
} | |
/** | |
* {@inheritdoc} | |
* | |
* Builds a form for a single entity field. | |
*/ | |
public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $entity = NULL, $field_name = NULL) { | |
if (!$form_state->has('entity')) { | |
$this->init($form_state, $entity, $field_name); | |
} | |
// Add the field form. | |
$form_state->get('form_display')->buildForm($entity, $form, $form_state); | |
// Add a dummy changed timestamp field to attach form errors to. | |
if ($entity instanceof EntityChangedInterface) { | |
$form['changed_field'] = array( | |
'#type' => 'hidden', | |
'#value' => $entity->getChangedTime(), | |
); | |
} | |
// Add a submit button. Give it a class for easy JavaScript targeting. | |
$form['actions'] = array('#type' => 'actions'); | |
$form['actions']['submit'] = array( | |
'#type' => 'submit', | |
'#value' => t('Save'), | |
'#attributes' => array('class' => array('quickedit-form-submit')), | |
); | |
// Simplify it for optimal in-place use. | |
$this->simplify($form, $form_state); | |
return $form; | |
} | |
/** | |
* Initialize the form state and the entity before the first form build. | |
*/ | |
protected function init(FormStateInterface $form_state, EntityInterface $entity, $field_name) { | |
// @todo Rather than special-casing $node->revision, invoke prepareEdit() | |
// once https://www.drupal.org/node/1863258 lands. | |
if ($entity->getEntityTypeId() == 'node') { | |
$node_type = $this->nodeTypeStorage->load($entity->bundle()); | |
$entity->setNewRevision($node_type->isNewRevision()); | |
$entity->revision_log = NULL; | |
} | |
$form_state->set('entity', $entity); | |
$form_state->set('field_name', $field_name); | |
// Fetch the display used by the form. It is the display for the 'default' | |
// form mode, with only the current field visible. | |
$display = EntityFormDisplay::collectRenderDisplay($entity, 'default'); | |
foreach ($display->getComponents() as $name => $options) { | |
if ($name != $field_name) { | |
$display->removeComponent($name); | |
} | |
} | |
$form_state->set('form_display', $display); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function validateForm(array &$form, FormStateInterface $form_state) { | |
$entity = $this->buildEntity($form, $form_state); | |
$form_state->get('form_display')->validateFormValues($entity, $form, $form_state); | |
} | |
/** | |
* {@inheritdoc} | |
* | |
* Saves the entity with updated values for the edited field. | |
*/ | |
public function submitForm(array &$form, FormStateInterface $form_state) { | |
$entity = $this->buildEntity($form, $form_state); | |
$form_state->set('entity', $entity); | |
// Store entity in tempstore with its UUID as tempstore key. | |
$this->tempStoreFactory->get('quickedit')->set($entity->uuid(), $entity); | |
} | |
/** | |
* Returns a cloned entity containing updated field values. | |
* | |
* Calling code may then validate the returned entity, and if valid, transfer | |
* it back to the form state and save it. | |
*/ | |
protected function buildEntity(array $form, FormStateInterface $form_state) { | |
/** @var $entity \Drupal\Core\Entity\EntityInterface */ | |
$entity = clone $form_state->get('entity'); | |
$field_name = $form_state->get('field_name'); | |
$form_state->get('form_display')->extractFormValues($entity, $form, $form_state); | |
// @todo Refine automated log messages and abstract them to all entity | |
// types: https://www.drupal.org/node/1678002. | |
if ($entity->getEntityTypeId() == 'node' && $entity->isNewRevision() && $entity->revision_log->isEmpty()) { | |
$entity->revision_log = t('Updated the %field-name field through in-place editing.', array('%field-name' => $entity->get($field_name)->getFieldDefinition()->getLabel())); | |
} | |
return $entity; | |
} | |
/** | |
* Simplifies the field edit form for in-place editing. | |
* | |
* This function: | |
* - Hides the field label inside the form, because JavaScript displays it | |
* outside the form. | |
* - Adjusts textarea elements to fit their content. | |
* | |
* @param array &$form | |
* A reference to an associative array containing the structure of the form. | |
* @param \Drupal\Core\Form\FormStateInterface $form_state | |
* The current state of the form. | |
*/ | |
protected function simplify(array &$form, FormStateInterface $form_state) { | |
$field_name = $form_state->get('field_name'); | |
$widget_element =& $form[$field_name]['widget']; | |
// Hide the field label from displaying within the form, because JavaScript | |
// displays the equivalent label that was provided within an HTML data | |
// attribute of the field's display element outside of the form. Do this for | |
// widgets without child elements (like Option widgets) as well as for ones | |
// with per-delta elements. Skip single checkboxes, because their title is | |
// key to their UI. Also skip widgets with multiple subelements, because in | |
// that case, per-element labeling is informative. | |
$num_children = count(Element::children($widget_element)); | |
if ($num_children == 0 && $widget_element['#type'] != 'checkbox') { | |
$widget_element['#title_display'] = 'invisible'; | |
} | |
if ($num_children == 1 && isset($widget_element[0]['value'])) { | |
// @todo While most widgets name their primary element 'value', not all | |
// do, so generalize this. | |
$widget_element[0]['value']['#title_display'] = 'invisible'; | |
} | |
// Adjust textarea elements to fit their content. | |
if (isset($widget_element[0]['value']['#type']) && $widget_element[0]['value']['#type'] == 'textarea') { | |
$lines = count(explode("\n", $widget_element[0]['value']['#default_value'])); | |
$widget_element[0]['value']['#rows'] = $lines + 1; | |
} | |
} | |
} |