Ejemplo n.º 1
0
 /**
  * Clear values from upload form element.
  *
  * @param array $element
  *   Upload form element.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   Form state object.
  */
 protected function clearFormValues(array &$element, FormStateInterface $form_state)
 {
     // We propagated entities to the other parts of the system. We can now remove
     // them from our values.
     $form_state->setValueForElement($element['upload']['fids'], '');
     NestedArray::setValue($form_state->getUserInput(), $element['upload']['fids']['#parents'], '');
 }
 /**
  * Form validation handler for widget elements.
  *
  * @param array $element
  *   The form element.
  * @param array $form_state
  *   The form state.
  */
 public static function validateElement(array $element, FormStateInterface $form_state)
 {
     parent::validateElement($element, $form_state);
     // Massage submitted form values.
     // Drupal\Core\Field\WidgetBase::submit() expects values as
     // an array of values keyed by delta first, then by column, while our
     // widgets return the opposite.
     if (is_array($element['#value'])) {
         $values = array_values($element['#value']);
     } else {
         $values = array($element['#value']);
     }
     // Filter out the 'none' option. Use a strict comparison, because
     // 0 == 'any string'.
     $index = array_search('_none', $values, TRUE);
     if ($index !== FALSE) {
         unset($values[$index]);
     }
     // Transpose selections from field => delta to delta => field.
     $items = array();
     foreach ($values as $value) {
         $items[] = array($element['#key_column'] => $value);
     }
     NestedArray::setValue($form_state['values'], $element['#parents'], $items);
 }
Ejemplo n.º 3
0
 /**
  * Tests force-setting values.
  *
  * @covers ::setValue
  */
 public function testSetValueForce()
 {
     $new_value = array('one');
     $this->form['details']['non-array-parent'] = 'string';
     $parents = array('details', 'non-array-parent', 'child');
     NestedArray::setValue($this->form, $parents, $new_value, TRUE);
     $this->assertSame($new_value, $this->form['details']['non-array-parent']['child'], 'The nested element was not forced to the new value.');
 }
Ejemplo n.º 4
0
 public function execute()
 {
     $file = $this->getUnaliasedPath($this->configuration['in']);
     $data = file_exists($file) ? YAML::decode(file_get_contents($file)) : [];
     $keys = explode('/', $this->configuration['key']);
     NestedArray::setValue($data, $keys, $this->configuration['value']);
     file_put_contents($file, YAML::encode($data));
 }
Ejemplo n.º 5
0
 /**
  * Form element validation callback for upload element on file widget.
  *
  * Checks if user has uploaded more files than allowed.
  *
  * This validator is used only when cardinality not set to 1 or unlimited.
  *
  * @param array $element
  *   The element.
  * @param FormStateInterface $form_state
  *   The form state.
  * @param array $form
  *   The form.
  */
 public static function validateMultipleCount($element, FormStateInterface $form_state, $form)
 {
     $parents = $element['#parents'];
     $values = NestedArray::getValue($form_state->getValues(), $parents);
     array_pop($parents);
     $current = count(Element::children(NestedArray::getValue($form, $parents))) - 1;
     $field_storage_definitions = \Drupal::entityManager()->getFieldStorageDefinitions($element['#entity_type']);
     $field_storage = $field_storage_definitions[$element['#field_name']];
     $uploaded = count($values['fids']);
     $count = $uploaded + $current;
     if ($count > $field_storage->getCardinality()) {
         $keep = $uploaded - $count + $field_storage->getCardinality();
         $removed_files = array_slice($values['fids'], $keep);
         $removed_names = array();
         foreach ($removed_files as $id) {
             /** @var \Drupal\embridge\EmbridgeAssetEntityInterface $asset */
             $asset = EmbridgeAssetEntity::load($id);
             $removed_names[] = $asset->getFilename();
         }
         $args = array('%field' => $field_storage->getName(), '@max' => $field_storage->getCardinality(), '@count' => $uploaded, '%list' => implode(', ', $removed_names));
         $message = t('Field %field can only hold @max values but there were @count uploaded. The following files have been omitted as a result: %list.', $args);
         drupal_set_message($message, 'warning');
         $values['fids'] = array_slice($values['fids'], 0, $keep);
         NestedArray::setValue($form_state->getValues(), $element['#parents'], $values);
     }
 }
 /**
  * Form submission handler for upload/remove button of formElement().
  *
  * This runs in addition to and after file_managed_file_submit().
  *
  * @see file_managed_file_submit()
  */
 public static function submit($form, FormStateInterface $form_state)
 {
     // During the form rebuild, formElement() will create field item widget
     // elements using re-indexed deltas, so clear out FormState::$input to
     // avoid a mismatch between old and new deltas. The rebuilt elements will
     // have #default_value set appropriately for the current state of the field,
     // so nothing is lost in doing this.
     $button = $form_state->getTriggeringElement();
     $parents = array_slice($button['#parents'], 0, -2);
     NestedArray::setValue($form_state->getUserInput(), $parents, NULL);
     // Go one level up in the form, to the widgets container.
     $element = NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
     $field_name = $element['#field_name'];
     $parents = $element['#field_parents'];
     $submitted_values = NestedArray::getValue($form_state->getValues(), array_slice($button['#parents'], 0, -2));
     foreach ($submitted_values as $delta => $submitted_value) {
         if (empty($submitted_value['fids'])) {
             unset($submitted_values[$delta]);
         }
     }
     // If there are more files uploaded via the same widget, we have to separate
     // them, as we display each file in it's own widget.
     $new_values = array();
     foreach ($submitted_values as $delta => $submitted_value) {
         if (is_array($submitted_value['fids'])) {
             foreach ($submitted_value['fids'] as $fid) {
                 $new_value = $submitted_value;
                 $new_value['fids'] = array($fid);
                 $new_values[] = $new_value;
             }
         } else {
             $new_value = $submitted_value;
         }
     }
     // Re-index deltas after removing empty items.
     $submitted_values = array_values($new_values);
     // Update form_state values.
     NestedArray::setValue($form_state->getValues(), array_slice($button['#parents'], 0, -2), $submitted_values);
     // Update items.
     $field_state = static::getWidgetState($parents, $field_name, $form_state);
     $field_state['items'] = $submitted_values;
     static::setWidgetState($parents, $field_name, $form_state, $field_state);
 }
 /**
  * Submit callback to remove an item from the field UI multiple wrapper.
  *
  * When a remove button is submitted, we need to find the item that it
  * referenced and delete it. Since field UI has the deltas as a straight
  * unbroken array key, we have to renumber everything down. Since we do this
  * we *also* need to move all the deltas around in the $form_state->values
  * and $form_state input so that user changed values follow. This is a bit
  * of a complicated process.
  */
 public static function removeSubmit($form, FormStateInterface $form_state)
 {
     $button = $form_state->getTriggeringElement();
     $delta = $button['#delta'];
     // Where in the form we'll find the parent element.
     $address = array_slice($button['#array_parents'], 0, -4);
     // Go one level up in the form, to the widgets container.
     $parent_element = NestedArray::getValue($form, array_merge($address, array('widget')));
     $field_name = $parent_element['#field_name'];
     $parents = $parent_element['#field_parents'];
     $field_state = static::getWidgetState($parents, $field_name, $form_state);
     // Go ahead and renumber everything from our delta to the last
     // item down one. This will overwrite the item being removed.
     for ($i = $delta; $i <= $field_state['items_count']; $i++) {
         $old_element_address = array_merge($address, array('widget', $i + 1));
         $old_element_state_address = array_merge($address, array($i + 1));
         $new_element_state_address = array_merge($address, array($i));
         $moving_element = NestedArray::getValue($form, $old_element_address);
         $moving_element_value = NestedArray::getValue($form_state->getValues(), $old_element_state_address);
         $moving_element_input = NestedArray::getValue($form_state->getUserInput(), $old_element_state_address);
         // Tell the element where it's being moved to.
         $moving_element['#parents'] = $new_element_state_address;
         // Move the element around.
         $form_state->setValueForElement($moving_element, $moving_element_value);
         $user_input = $form_state->getUserInput();
         NestedArray::setValue($user_input, $moving_element['#parents'], $moving_element_input);
         $form_state->setUserInput($user_input);
         // Move the entity in our saved state.
         if (isset($field_state['field_collection_item'][$i + 1])) {
             $field_state['field_collection_item'][$i] = $field_state['field_collection_item'][$i + 1];
         } else {
             unset($field_state['field_collection_item'][$i]);
         }
     }
     // Replace the deleted entity with an empty one. This helps to ensure that
     // trying to add a new entity won't ressurect a deleted entity from the
     // trash bin.
     $count = count($field_state['field_collection_item']);
     $field_state['field_collection_item'][$count] = entity_create('field_collection_item', array('field_name' => $field_name));
     // Then remove the last item. But we must not go negative.
     if ($field_state['items_count'] > 0) {
         $field_state['items_count']--;
     }
     // Fix the weights. Field UI lets the weights be in a range of
     // (-1 * item_count) to (item_count). This means that when we remove one,
     // the range shrinks; weights outside of that range then get set to
     // the first item in the select by the browser, floating them to the top.
     // We use a brute force method because we lost weights on both ends
     // and if the user has moved things around, we have to cascade because
     // if I have items weight weights 3 and 4, and I change 4 to 3 but leave
     // the 3, the order of the two 3s now is undefined and may not match what
     // the user had selected.
     $input = NestedArray::getValue($form_state->getUserInput(), $address);
     // Sort by weight.
     uasort($input, '_field_collection_sort_items_helper');
     // Reweight everything in the correct order.
     $weight = -1 * $field_state['items_count'];
     foreach ($input as $key => $item) {
         if ($item) {
             $input[$key]['_weight'] = $weight++;
         }
     }
     $user_input = $form_state->getUserInput();
     NestedArray::setValue($user_input, $address, $input);
     $form_state->setUserInput($user_input);
     static::setWidgetState($parents, $field_name, $form_state, $field_state);
     $form_state->setRebuild();
 }
Ejemplo n.º 8
0
 /**
  * {@inheritdoc}
  */
 public function setValue($element, $value, &$form_state)
 {
     NestedArray::setValue($form_state['values'], $element['#parents'], $value, TRUE);
 }
Ejemplo n.º 9
0
 /**
  * Sets destination properties.
  *
  * @param string $property
  *   The name of the destination property.
  * @param mixed $value
  *   The property value to set on the destination.
  */
 public function setDestinationProperty($property, $value)
 {
     $this->rawDestination[$property] = $value;
     NestedArray::setValue($this->destination, explode(static::PROPERTY_SEPARATOR, $property), $value, TRUE);
 }
Ejemplo n.º 10
0
 /**
  * Implements \Drupal\Core\Form\FormStateInterface::setValue()
  */
 public function setValue($key, $value)
 {
     NestedArray::setValue($this->getValues(), (array) $key, $value, TRUE);
     return $this;
 }
Ejemplo n.º 11
0
 /**
  * Updates a (possible nested) entity property with a value.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
  *   The config entity.
  * @param array $parents
  *   The array of parents.
  * @param string|object $value
  *   The value to update to.
  */
 protected function updateEntityProperty(EntityInterface $entity, array $parents, $value)
 {
     $top_key = array_shift($parents);
     $entity_value = $entity->get($top_key);
     if (is_array($entity_value)) {
         NestedArray::setValue($entity_value, $parents, $value);
     } else {
         $entity_value = $value;
     }
     $entity->set($top_key, $entity_value);
 }
Ejemplo n.º 12
0
 /**
  * {@inheritdoc}
  */
 public function updateData($key, $values = array(), $replace = FALSE)
 {
     if ($replace) {
         if (!is_array($this->unserializedData)) {
             $this->unserializedData = unserialize($this->get('data')->value);
             if (!is_array($this->unserializedData)) {
                 $this->unserializedData = array();
             }
         }
         NestedArray::setValue($this->unserializedData, \Drupal::service('tmgmt.data')->ensureArrayKey($key), $values);
     }
     foreach ($values as $index => $value) {
         // In order to preserve existing values, we can not aplly the values array
         // at once. We need to apply each containing value on its own.
         // If $value is an array we need to advance the hierarchy level.
         if (is_array($value)) {
             $this->updateData(array_merge(\Drupal::service('tmgmt.data')->ensureArrayKey($key), array($index)), $value);
         } else {
             if (!is_array($this->unserializedData)) {
                 $this->unserializedData = unserialize($this->get('data')->value);
             }
             NestedArray::setValue($this->unserializedData, array_merge(\Drupal::service('tmgmt.data')->ensureArrayKey($key), array($index)), $value);
         }
     }
 }
Ejemplo n.º 13
0
 /**
  * Converts a flattened data structure into a nested array.
  *
  * This function can be used by translators to help with the data conversion.
  *
  * Nested keys will be created based on the colon, so for example
  * $flattened_data['key1][key2][key3'] will be converted into
  * $data['key1']['key2']['key3'].
  *
  * @param array $flattened_data
  *   The flattened data array.
  *
  * @return array
  *   The nested data array.
  */
 public function unflatten(array $flattened_data)
 {
     $data = array();
     foreach ($flattened_data as $key => $flattened_data_entry) {
         NestedArray::setValue($data, explode(static::TMGMT_ARRAY_DELIMITER, $key), $flattened_data_entry);
     }
     return $data;
 }
Ejemplo n.º 14
0
 /**
  * {@inheritdoc}
  */
 public function setValueForElement($element, $value)
 {
     $values = $this->getValues();
     NestedArray::setValue($values, $element['#parents'], $value, TRUE);
     $this->set('values', $values);
     return $this;
 }
Ejemplo n.º 15
0
 /**
  * {@inheritdoc}
  */
 public static function setWidgetState(array $parents, $field_name, array &$form_state, array $field_state)
 {
     NestedArray::setValue($form_state, static::getWidgetStateParents($parents, $field_name), $field_state);
 }
 /**
  * Clears the country-specific form values when the country changes.
  *
  * Implemented as an #after_build callback because #after_build runs before
  * validation, allowing the values to be cleared early enough to prevent the
  * "Illegal choice" error.
  */
 public static function clearValues(array $element, FormStateInterface $form_state)
 {
     $triggering_element = $form_state->getTriggeringElement();
     if (!$triggering_element) {
         return $element;
     }
     $triggering_element_name = end($triggering_element['#parents']);
     if ($triggering_element_name == 'country_code') {
         $keys = ['dependent_locality', 'locality', 'administrative_area'];
         $input =& $form_state->getUserInput();
         foreach ($keys as $key) {
             $parents = array_merge($element['#parents'], [$key]);
             NestedArray::setValue($input, $parents, '');
             $element[$key]['#value'] = '';
         }
     }
     return $element;
 }
Ejemplo n.º 17
0
 /**
  * {@inheritdoc}
  */
 public static function setWidgetState(array $parents, $field_name, FormStateInterface $form_state, array $field_state)
 {
     NestedArray::setValue($form_state->getStorage(), static::getWidgetStateParents($parents, $field_name), $field_state);
 }
Ejemplo n.º 18
0
 /**
  * Submit callback for remove buttons.
  */
 public static function removeItemSubmit(&$form, FormStateInterface $form_state)
 {
     $triggering_element = $form_state->getTriggeringElement();
     if (!empty($triggering_element['#attributes']['data-entity-id'])) {
         $id = $triggering_element['#attributes']['data-entity-id'];
         $parents = array_slice($triggering_element['#parents'], 0, -static::$deleteDepth);
         $array_parents = array_slice($triggering_element['#array_parents'], 0, -static::$deleteDepth);
         // Find and remove correct entity.
         $values = explode(' ', $form_state->getValue(array_merge($parents, ['target_id'])));
         $values = array_filter($values, function ($item) use($id) {
             return $item != $id;
         });
         $values = implode(' ', $values);
         // Set new value for this widget.
         $target_id_element =& NestedArray::getValue($form, array_merge($array_parents, ['target_id']));
         $form_state->setValueForElement($target_id_element, $values);
         NestedArray::setValue($form_state->getUserInput(), $target_id_element['#parents'], $values);
         // Rebuild form.
         $form_state->setRebuild();
     }
 }
Ejemplo n.º 19
0
 /**
  * Handles validation errors for forms with limited validation.
  *
  * If validation errors are limited then remove any non validated form values,
  * so that only values that passed validation are left for submit callbacks.
  *
  * @param array $form
  *   An associative array containing the structure of the form.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  * @param string $form_id
  *   The unique string identifying the form.
  */
 protected function handleErrorsWithLimitedValidation(&$form, FormStateInterface &$form_state, $form_id)
 {
     // If validation errors are limited then remove any non validated form values,
     // so that only values that passed validation are left for submit callbacks.
     $triggering_element = $form_state->getTriggeringElement();
     if (isset($triggering_element['#limit_validation_errors']) && $triggering_element['#limit_validation_errors'] !== FALSE) {
         $values = array();
         foreach ($triggering_element['#limit_validation_errors'] as $section) {
             // If the section exists within $form_state->getValues(), even if the
             // value is NULL, copy it to $values.
             $section_exists = NULL;
             $value = NestedArray::getValue($form_state->getValues(), $section, $section_exists);
             if ($section_exists) {
                 NestedArray::setValue($values, $section, $value);
             }
         }
         // A button's #value does not require validation, so for convenience we
         // allow the value of the clicked button to be retained in its normal
         // $form_state->getValues() locations, even if these locations are not
         // included in #limit_validation_errors.
         if (!empty($triggering_element['#is_button'])) {
             $button_value = $triggering_element['#value'];
             // Like all input controls, the button value may be in the location
             // dictated by #parents. If it is, copy it to $values, but do not
             // override what may already be in $values.
             $parents = $triggering_element['#parents'];
             if (!NestedArray::keyExists($values, $parents) && NestedArray::getValue($form_state->getValues(), $parents) === $button_value) {
                 NestedArray::setValue($values, $parents, $button_value);
             }
             // Additionally, self::doBuildForm() places the button value in
             // $form_state->getValue(BUTTON_NAME). If it's still there, after
             // validation handlers have run, copy it to $values, but do not override
             // what may already be in $values.
             $name = $triggering_element['#name'];
             if (!isset($values[$name]) && $form_state->getValue($name) === $button_value) {
                 $values[$name] = $button_value;
             }
         }
         $form_state->setValues($values);
     }
 }
Ejemplo n.º 20
0
 /**
  * Processes a machine-readable name form element.
  *
  * @param array $element
  *   The form element to process. See main class documentation for properties.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  * @param array $complete_form
  *   The complete form structure.
  *
  * @return array
  *   The processed element.
  */
 public static function processMachineName(&$element, FormStateInterface $form_state, &$complete_form)
 {
     // We need to pass the langcode to the client.
     $language = \Drupal::languageManager()->getCurrentLanguage();
     // Apply default form element properties.
     $element += array('#title' => t('Machine-readable name'), '#description' => t('A unique machine-readable name. Can only contain lowercase letters, numbers, and underscores.'), '#machine_name' => array(), '#field_prefix' => '', '#field_suffix' => '', '#suffix' => '');
     // A form element that only wants to set one #machine_name property (usually
     // 'source' only) would leave all other properties undefined, if the defaults
     // were defined by an element plugin. Therefore, we apply the defaults here.
     $element['#machine_name'] += array('source' => array('label'), 'target' => '#' . $element['#id'], 'label' => t('Machine name'), 'replace_pattern' => '[^a-z0-9_]+', 'replace' => '_', 'standalone' => FALSE, 'field_prefix' => $element['#field_prefix'], 'field_suffix' => $element['#field_suffix']);
     // By default, machine names are restricted to Latin alphanumeric characters.
     // So, default to LTR directionality.
     if (!isset($element['#attributes'])) {
         $element['#attributes'] = array();
     }
     $element['#attributes'] += array('dir' => LanguageInterface::DIRECTION_LTR);
     // The source element defaults to array('name'), but may have been overridden.
     if (empty($element['#machine_name']['source'])) {
         return $element;
     }
     // Retrieve the form element containing the human-readable name from the
     // complete form in $form_state. By reference, because we may need to append
     // a #field_suffix that will hold the live preview.
     $key_exists = NULL;
     $source = NestedArray::getValue($form_state->getCompleteForm(), $element['#machine_name']['source'], $key_exists);
     if (!$key_exists) {
         return $element;
     }
     $suffix_id = $source['#id'] . '-machine-name-suffix';
     $element['#machine_name']['suffix'] = '#' . $suffix_id;
     if ($element['#machine_name']['standalone']) {
         $element['#suffix'] = $element['#suffix'] . ' <small id="' . $suffix_id . '">&nbsp;</small>';
     } else {
         // Append a field suffix to the source form element, which will contain
         // the live preview of the machine name.
         $source += array('#field_suffix' => '');
         $source['#field_suffix'] = $source['#field_suffix'] . ' <small id="' . $suffix_id . '">&nbsp;</small>';
         $parents = array_merge($element['#machine_name']['source'], array('#field_suffix'));
         NestedArray::setValue($form_state->getCompleteForm(), $parents, $source['#field_suffix']);
     }
     $element['#attached']['library'][] = 'core/drupal.machine-name';
     $element['#attached']['drupalSettings']['machineName']['#' . $source['#id']] = $element['#machine_name'];
     $element['#attached']['drupalSettings']['langcode'] = $language->getId();
     return $element;
 }
Ejemplo n.º 21
0
 /**
  * Validate helper to have support for other entity reference widgets.
  *
  * @param $element
  * @param FormStateInterface $form_state
  * @param $form
  */
 public static function targetTypeValidate($element, FormStateInterface $form_state, $form)
 {
     $values =& $form_state->getValues();
     $element_values = NestedArray::getValue($values, $element['#parents']);
     $bundle_options = array();
     if ($element_values) {
         $enabled = 0;
         foreach ($element_values as $machine_name => $bundle_info) {
             if (isset($bundle_info['enabled']) && $bundle_info['enabled']) {
                 $bundle_options[$machine_name] = $machine_name;
                 $enabled++;
             }
         }
         // All disabled = all enabled.
         if ($enabled === 0) {
             foreach ($element_values as $machine_name => $bundle_info) {
                 $bundle_options[$machine_name] = $machine_name;
             }
         }
     }
     // New value parents.
     $parents = array_merge(array_slice($element['#parents'], 0, -1), array('target_bundles'));
     NestedArray::setValue($values, $parents, $bundle_options);
 }
Ejemplo n.º 22
0
 /**
  * {@inheritdoc}
  */
 public function set($property, $value)
 {
     NestedArray::setValue($this->storage, (array) $property, $value, TRUE);
     return $this;
 }
Ejemplo n.º 23
0
 /**
  * Sets a value in this configuration object.
  *
  * @param string $key
  *   Identifier to store value in configuration.
  * @param mixed $value
  *   Value to associate with identifier.
  *
  * @return $this
  *   The configuration object.
  *
  * @throws \Drupal\Core\Config\ConfigValueException
  *   If $value is an array and any of its keys in any depth contains a dot.
  */
 public function set($key, $value)
 {
     $value = $this->castSafeStrings($value);
     // The dot/period is a reserved character; it may appear between keys, but
     // not within keys.
     if (is_array($value)) {
         $this->validateKeys($value);
     }
     $parts = explode('.', $key);
     if (count($parts) == 1) {
         $this->data[$key] = $value;
     } else {
         NestedArray::setValue($this->data, $parts, $value);
     }
     return $this;
 }
Ejemplo n.º 24
0
 /**
  * Performs validation of elements that are not subject to limited validation.
  *
  * @param array $elements
  *   An associative array containing the structure of the form.
  * @param array $form_state
  *   A keyed array containing the current state of the form. The current
  *   user-submitted data is stored in $form_state['values'], though
  *   form validation functions are passed an explicit copy of the
  *   values for the sake of simplicity. Validation handlers can also
  *   $form_state to pass information on to submit handlers. For example:
  *     $form_state['data_for_submission'] = $data;
  *   This technique is useful when validation requires file parsing,
  *   web service requests, or other expensive requests that should
  *   not be repeated in the submission step.
  */
 protected function performRequiredValidation(&$elements, &$form_state)
 {
     // Verify that the value is not longer than #maxlength.
     if (isset($elements['#maxlength']) && Unicode::strlen($elements['#value']) > $elements['#maxlength']) {
         $this->setError($elements, $form_state, $this->t('!name cannot be longer than %max characters but is currently %length characters long.', array('!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title'], '%max' => $elements['#maxlength'], '%length' => Unicode::strlen($elements['#value']))));
     }
     if (isset($elements['#options']) && isset($elements['#value'])) {
         if ($elements['#type'] == 'select') {
             $options = OptGroup::flattenOptions($elements['#options']);
         } else {
             $options = $elements['#options'];
         }
         if (is_array($elements['#value'])) {
             $value = in_array($elements['#type'], array('checkboxes', 'tableselect')) ? array_keys($elements['#value']) : $elements['#value'];
             foreach ($value as $v) {
                 if (!isset($options[$v])) {
                     $this->setError($elements, $form_state, $this->t('An illegal choice has been detected. Please contact the site administrator.'));
                     $this->watchdog('form', 'Illegal choice %choice in !name element.', array('%choice' => $v, '!name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
                 }
             }
         } elseif ($elements['#type'] == 'select' && !$elements['#multiple'] && $elements['#required'] && !isset($elements['#default_value']) && $elements['#value'] === $elements['#empty_value']) {
             $elements['#value'] = NULL;
             NestedArray::setValue($form_state['values'], $elements['#parents'], NULL, TRUE);
         } elseif (!isset($options[$elements['#value']])) {
             $this->setError($elements, $form_state, $this->t('An illegal choice has been detected. Please contact the site administrator.'));
             $this->watchdog('form', 'Illegal choice %choice in %name element.', array('%choice' => $elements['#value'], '%name' => empty($elements['#title']) ? $elements['#parents'][0] : $elements['#title']), WATCHDOG_ERROR);
         }
     }
 }
Ejemplo n.º 25
0
 /**
  * Adds the #name and #value properties of an input element before rendering.
  */
 protected function handleInputElement($form_id, &$element, FormStateInterface &$form_state)
 {
     if (!isset($element['#name'])) {
         $name = array_shift($element['#parents']);
         $element['#name'] = $name;
         if ($element['#type'] == 'file') {
             // To make it easier to handle files in file.inc, we place all
             // file fields in the 'files' array. Also, we do not support
             // nested file names.
             // @todo Remove this files prefix now?
             $element['#name'] = 'files[' . $element['#name'] . ']';
         } elseif (count($element['#parents'])) {
             $element['#name'] .= '[' . implode('][', $element['#parents']) . ']';
         }
         array_unshift($element['#parents'], $name);
     }
     // Setting #disabled to TRUE results in user input being ignored regardless
     // of how the element is themed or whether JavaScript is used to change the
     // control's attributes. However, it's good UI to let the user know that
     // input is not wanted for the control. HTML supports two attributes for:
     // this: http://www.w3.org/TR/html401/interact/forms.html#h-17.12. If a form
     // wants to start a control off with one of these attributes for UI
     // purposes, only, but still allow input to be processed if it's submitted,
     // it can set the desired attribute in #attributes directly rather than
     // using #disabled. However, developers should think carefully about the
     // accessibility implications of doing so: if the form expects input to be
     // enterable under some condition triggered by JavaScript, how would someone
     // who has JavaScript disabled trigger that condition? Instead, developers
     // should consider whether a multi-step form would be more appropriate
     // (#disabled can be changed from step to step). If one still decides to use
     // JavaScript to affect when a control is enabled, then it is best for
     // accessibility for the control to be enabled in the HTML, and disabled by
     // JavaScript on document ready.
     if (!empty($element['#disabled'])) {
         if (!empty($element['#allow_focus'])) {
             $element['#attributes']['readonly'] = 'readonly';
         } else {
             $element['#attributes']['disabled'] = 'disabled';
         }
     }
     // With JavaScript or other easy hacking, input can be submitted even for
     // elements with #access=FALSE or #disabled=TRUE. For security, these must
     // not be processed. Forms that set #disabled=TRUE on an element do not
     // expect input for the element, and even forms submitted with
     // self::submitForm() must not be able to get around this. Forms that set
     // #access=FALSE on an element usually allow access for some users, so forms
     // submitted with self::submitForm() may bypass access restriction and be
     // treated as high-privilege users instead.
     $process_input = empty($element['#disabled']) && ($form_state->isProgrammed() && $form_state->isBypassingProgrammedAccessChecks() || $form_state->isProcessingInput() && (!isset($element['#access']) || $element['#access']));
     // Set the element's #value property.
     if (!isset($element['#value']) && !array_key_exists('#value', $element)) {
         // @todo Once all elements are converted to plugins in
         //   https://www.drupal.org/node/2311393, rely on
         //   $element['#value_callback'] directly.
         $value_callable = !empty($element['#value_callback']) ? $element['#value_callback'] : 'form_type_' . $element['#type'] . '_value';
         if (!is_callable($value_callable)) {
             $value_callable = '\\Drupal\\Core\\Render\\Element\\FormElement::valueCallback';
         }
         if ($process_input) {
             // Get the input for the current element. NULL values in the input need
             // to be explicitly distinguished from missing input. (see below)
             $input_exists = NULL;
             $input = NestedArray::getValue($form_state->getUserInput(), $element['#parents'], $input_exists);
             // For browser-submitted forms, the submitted values do not contain
             // values for certain elements (empty multiple select, unchecked
             // checkbox). During initial form processing, we add explicit NULL
             // values for such elements in FormState::$input. When rebuilding the
             // form, we can distinguish elements having NULL input from elements
             // that were not part of the initially submitted form and can therefore
             // use default values for the latter, if required. Programmatically
             // submitted forms can submit explicit NULL values when calling
             // self::submitForm() so we do not modify FormState::$input for them.
             if (!$input_exists && !$form_state->isRebuilding() && !$form_state->isProgrammed()) {
                 // Add the necessary parent keys to FormState::$input and sets the
                 // element's input value to NULL.
                 NestedArray::setValue($form_state->getUserInput(), $element['#parents'], NULL);
                 $input_exists = TRUE;
             }
             // If we have input for the current element, assign it to the #value
             // property, optionally filtered through $value_callback.
             if ($input_exists) {
                 // Skip all value callbacks except safe ones like text if the CSRF
                 // token was invalid.
                 if (!$form_state->hasInvalidToken() || $this->valueCallableIsSafe($value_callable)) {
                     $element['#value'] = call_user_func_array($value_callable, array(&$element, $input, &$form_state));
                 } else {
                     $input = NULL;
                 }
                 if (!isset($element['#value']) && isset($input)) {
                     $element['#value'] = $input;
                 }
             }
             // Mark all posted values for validation.
             if (isset($element['#value']) || !empty($element['#required'])) {
                 $element['#needs_validation'] = TRUE;
             }
         }
         // Load defaults.
         if (!isset($element['#value'])) {
             // Call #type_value without a second argument to request default_value
             // handling.
             $element['#value'] = call_user_func_array($value_callable, array(&$element, FALSE, &$form_state));
             // Final catch. If we haven't set a value yet, use the explicit default
             // value. Avoid image buttons (which come with garbage value), so we
             // only get value for the button actually clicked.
             if (!isset($element['#value']) && empty($element['#has_garbage_value'])) {
                 $element['#value'] = isset($element['#default_value']) ? $element['#default_value'] : '';
             }
         }
     }
     // Determine which element (if any) triggered the submission of the form and
     // keep track of all the clickable buttons in the form for
     // \Drupal\Core\Form\FormState::cleanValues(). Enforce the same input
     // processing restrictions as above.
     if ($process_input) {
         // Detect if the element triggered the submission via Ajax.
         if ($this->elementTriggeredScriptedSubmission($element, $form_state)) {
             $form_state->setTriggeringElement($element);
         }
         // If the form was submitted by the browser rather than via Ajax, then it
         // can only have been triggered by a button, and we need to determine
         // which button within the constraints of how browsers provide this
         // information.
         if (!empty($element['#is_button'])) {
             // All buttons in the form need to be tracked for
             // \Drupal\Core\Form\FormState::cleanValues() and for the
             // self::doBuildForm() code that handles a form submission containing no
             // button information in \Drupal::request()->request.
             $buttons = $form_state->getButtons();
             $buttons[] = $element;
             $form_state->setButtons($buttons);
             if ($this->buttonWasClicked($element, $form_state)) {
                 $form_state->setTriggeringElement($element);
             }
         }
     }
     // Set the element's value in $form_state->getValues(), but only, if its key
     // does not exist yet (a #value_callback may have already populated it).
     if (!NestedArray::keyExists($form_state->getValues(), $element['#parents'])) {
         $form_state->setValueForElement($element, $element['#value']);
     }
 }
Ejemplo n.º 26
0
 /**
  * {@inheritdoc}
  */
 public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state)
 {
     $field_name = $this->fieldDefinition->getName();
     // Extract the values from $form_state->getValues().
     $path = array_merge($form['#parents'], array($field_name));
     $key_exists = NULL;
     // Convert the field value into expected array format.
     $values = $form_state->getValues();
     $value = NestedArray::getValue($values, $path, $key_exists);
     if (empty($value)) {
         parent::extractFormValues($items, $form, $form_state);
         return;
     }
     if (!isset($value[0]['target_id'])) {
         NestedArray::setValue($values, $path, [['target_id' => reset($value)]]);
         $form_state->setValues($values);
     }
     parent::extractFormValues($items, $form, $form_state);
 }
Ejemplo n.º 27
0
 /**
  * {@inheritdoc}
  */
 public function setSetting($setting_name, $value)
 {
     NestedArray::setValue($this->settings, (array) $setting_name, $value);
     return $this;
 }
Ejemplo n.º 28
0
 /**
  * Overrides the specified library asset.
  *
  * @param array $library
  *   The containing library definition.
  * @param array $sub_key
  *   An array containing the sub-keys specifying the library asset, e.g.
  *   @code['js']@endcode or @code['css', 'component']@endcode
  * @param array $overrides
  *   Specifies the overrides, this is an array where the key is the asset to
  *   be overridden while the value is overriding asset.
  */
 protected function setOverrideValue(array &$library, array $sub_key, array $overrides, $theme_path)
 {
     foreach ($overrides as $original => $replacement) {
         // Get the attributes of the asset to be overridden. If the key does
         // not exist, then throw an exception.
         $key_exists = NULL;
         $parents = array_merge($sub_key, [$original]);
         // Save the attributes of the library asset to be overridden.
         $attributes = NestedArray::getValue($library, $parents, $key_exists);
         if ($key_exists) {
             // Remove asset to be overridden.
             NestedArray::unsetValue($library, $parents);
             // No need to replace if FALSE is specified, since that is a removal.
             if ($replacement) {
                 // Ensure the replacement path is relative to drupal root.
                 $replacement = $this->resolveThemeAssetPath($theme_path, $replacement);
                 $new_parents = array_merge($sub_key, [$replacement]);
                 // Replace with an override if specified.
                 NestedArray::setValue($library, $new_parents, $attributes);
             }
         }
     }
 }
Ejemplo n.º 29
0
 /**
  * {@inheritdoc}
  */
 public function buildCommentedEntityLinks(FieldableEntityInterface $entity, array &$context)
 {
     $entity_links = array();
     $view_mode = $context['view_mode'];
     if ($view_mode == 'search_index' || $view_mode == 'search_result' || $view_mode == 'print' || $view_mode == 'rss') {
         // Do not add any links if the entity is displayed for:
         // - search indexing.
         // - constructing a search result excerpt.
         // - print.
         // - rss.
         return array();
     }
     $fields = $this->commentManager->getFields($entity->getEntityTypeId());
     foreach ($fields as $field_name => $detail) {
         // Skip fields that the entity does not have.
         if (!$entity->hasField($field_name)) {
             continue;
         }
         $links = array();
         $commenting_status = $entity->get($field_name)->status;
         if ($commenting_status != CommentItemInterface::HIDDEN) {
             // Entity has commenting status open or closed.
             $field_definition = $entity->getFieldDefinition($field_name);
             if ($view_mode == 'teaser') {
                 // Teaser view: display the number of comments that have been posted,
                 // or a link to add new comments if the user has permission, the
                 // entity is open to new comments, and there currently are none.
                 if ($this->currentUser->hasPermission('access comments')) {
                     if (!empty($entity->get($field_name)->comment_count)) {
                         $links['comment-comments'] = array('title' => $this->formatPlural($entity->get($field_name)->comment_count, '1 comment', '@count comments'), 'attributes' => array('title' => $this->t('Jump to the first comment.')), 'fragment' => 'comments', 'url' => $entity->urlInfo());
                         if ($this->moduleHandler->moduleExists('history')) {
                             $links['comment-new-comments'] = array('title' => '', 'url' => Url::fromRoute('<current>'), 'attributes' => array('class' => 'hidden', 'title' => $this->t('Jump to the first new comment.'), 'data-history-node-last-comment-timestamp' => $entity->get($field_name)->last_comment_timestamp, 'data-history-node-field-name' => $field_name));
                         }
                     }
                 }
                 // Provide a link to new comment form.
                 if ($commenting_status == CommentItemInterface::OPEN) {
                     $comment_form_location = $field_definition->getSetting('form_location');
                     if ($this->currentUser->hasPermission('post comments')) {
                         $links['comment-add'] = array('title' => $this->t('Add new comments'), 'language' => $entity->language(), 'attributes' => array('title' => $this->t('Share your thoughts and opinions.')), 'fragment' => 'comment-form');
                         if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE) {
                             $links['comment-add']['url'] = Url::fromRoute('comment.reply', ['entity_type' => $entity->getEntityTypeId(), 'entity' => $entity->id(), 'field_name' => $field_name]);
                         } else {
                             $links['comment-add'] += ['url' => $entity->urlInfo()];
                         }
                     } elseif ($this->currentUser->isAnonymous()) {
                         $links['comment-forbidden'] = array('title' => $this->commentManager->forbiddenMessage($entity, $field_name));
                     }
                 }
             } else {
                 // Entity in other view modes: add a "post comment" link if the user
                 // is allowed to post comments and if this entity is allowing new
                 // comments.
                 if ($commenting_status == CommentItemInterface::OPEN) {
                     $comment_form_location = $field_definition->getSetting('form_location');
                     if ($this->currentUser->hasPermission('post comments')) {
                         // Show the "post comment" link if the form is on another page, or
                         // if there are existing comments that the link will skip past.
                         if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE || !empty($entity->get($field_name)->comment_count) && $this->currentUser->hasPermission('access comments')) {
                             $links['comment-add'] = array('title' => $this->t('Add new comment'), 'attributes' => array('title' => $this->t('Share your thoughts and opinions.')), 'fragment' => 'comment-form');
                             if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE) {
                                 $links['comment-add']['url'] = Url::fromRoute('comment.reply', ['entity_type' => $entity->getEntityTypeId(), 'entity' => $entity->id(), 'field_name' => $field_name]);
                             } else {
                                 $links['comment-add']['url'] = $entity->urlInfo();
                             }
                         }
                     } elseif ($this->currentUser->isAnonymous()) {
                         $links['comment-forbidden'] = array('title' => $this->commentManager->forbiddenMessage($entity, $field_name));
                     }
                 }
             }
         }
         if (!empty($links)) {
             $entity_links['comment__' . $field_name] = array('#theme' => 'links__entity__comment__' . $field_name, '#links' => $links, '#attributes' => array('class' => array('links', 'inline')));
             if ($view_mode == 'teaser' && $this->moduleHandler->moduleExists('history') && $this->currentUser->isAuthenticated()) {
                 $entity_links['comment__' . $field_name]['#cache']['contexts'][] = 'user';
                 $entity_links['comment__' . $field_name]['#attached']['library'][] = 'comment/drupal.node-new-comments-link';
                 // Embed the metadata for the "X new comments" link (if any) on this
                 // entity.
                 $entity_links['comment__' . $field_name]['#attached']['drupalSettings']['history']['lastReadTimestamps'][$entity->id()] = (int) history_read($entity->id());
                 $new_comments = $this->commentManager->getCountNewComments($entity);
                 if ($new_comments > 0) {
                     $page_number = $this->entityManager->getStorage('comment')->getNewCommentPageNumber($entity->{$field_name}->comment_count, $new_comments, $entity, $field_name);
                     $query = $page_number ? ['page' => $page_number] : NULL;
                     $value = ['new_comment_count' => (int) $new_comments, 'first_new_comment_link' => $entity->url('canonical', ['query' => $query, 'fragment' => 'new'])];
                     $parents = ['comment', 'newCommentsLinks', $entity->getEntityTypeId(), $field_name, $entity->id()];
                     NestedArray::setValue($entity_links['comment__' . $field_name]['#attached']['drupalSettings'], $parents, $value);
                 }
             }
         }
     }
     return $entity_links;
 }
Ejemplo n.º 30
0
 /**
  * {@inheritdoc}
  */
 public static function setModalState(FormStateInterface $form_state, array $field_state)
 {
     NestedArray::setValue($form_state->getStorage(), ['search_results'], $field_state);
 }