/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { foreach (Element::children($form_state->getValue('stock')) as $sku) { $stock = $form_state->getValue(['stock', $sku]); db_merge('uc_product_stock')->key(array('sku' => $sku))->updateFields(array('active' => $stock['active'], 'stock' => $stock['stock'], 'threshold' => $stock['threshold']))->insertFields(array('sku' => $sku, 'active' => $stock['active'], 'stock' => $stock['stock'], 'threshold' => $stock['threshold'], 'nid' => $form_state->getValue('nid')))->execute(); } drupal_set_message($this->t('Stock settings saved.')); }
/** * {@inheritdoc} */ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { $build = parent::view($entity, $view_mode, $langcode); if ($view_mode == 'mail') { // Convert field labels into headings. // @todo Improve drupal_html_to_text() to convert DIVs correctly. foreach (Element::children($build) as $key) { if (isset($build[$key]['#label_display']) && $build[$key]['#label_display'] == 'above') { $build[$key] += array('#prefix' => ''); $build[$key]['#prefix'] = $build[$key]['#title'] . ":\n"; $build[$key]['#label_display'] = 'hidden'; } } $build = array('#markup' => drupal_html_to_text(drupal_render($build))); } return $build; }
/** * Tests sorting by weight. */ function testDrupalRenderSorting() { $first = $this->randomMachineName(); $second = $this->randomMachineName(); // Build an array with '#weight' set for each element. $elements = array('second' => array('#weight' => 10, '#markup' => $second), 'first' => array('#weight' => 0, '#markup' => $first)); $output = drupal_render($elements); // The lowest weight element should appear last in $output. $this->assertTrue(strpos($output, $second) > strpos($output, $first), 'Elements were sorted correctly by weight.'); // Confirm that the $elements array has '#sorted' set to TRUE. $this->assertTrue($elements['#sorted'], "'#sorted' => TRUE was added to the array"); // Pass $elements through \Drupal\Core\Render\Element::children() and // ensure it remains sorted in the correct order. drupal_render() will // return an empty string if used on the same array in the same request. $children = Element::children($elements); $this->assertTrue(array_shift($children) == 'first', 'Child found in the correct order.'); $this->assertTrue(array_shift($children) == 'second', 'Child found in the correct order.'); // The same array structure again, but with #sorted set to TRUE. $elements = array('second' => array('#weight' => 10, '#markup' => $second), 'first' => array('#weight' => 0, '#markup' => $first), '#sorted' => TRUE); $output = drupal_render($elements); // The elements should appear in output in the same order as the array. $this->assertTrue(strpos($output, $second) < strpos($output, $first), 'Elements were not sorted.'); }
/** * {@inheritdoc} */ public function checkoutSettingsForm(array $form, FormStateInterface $form_state, JobInterface $job) { if (!Element::children($form)) { $form['#description'] = t("The @translator translator doesn't provide any checkout settings.", array('@translator' => $job->getTranslator()->label())); } return $form; }
protected function assertFallbackFormatter($entity, array $formatters = array(), array $expected_output) { $display = array('type' => 'fallback', 'settings' => array('formatters' => $formatters)); $output = $entity->test_text->view($display); $output = array_intersect_key($output, Element::children($output)); $this->assertEqual($output, $expected_output); }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { $filter_values = $this->translateFilterValues(); $langcode = $filter_values['langcode']; $this->languageManager->reset(); $languages = $this->languageManager->getLanguages(); $langname = isset($langcode) ? $languages[$langcode]->getName() : "- None -"; $form['#attached']['library'][] = 'locale/drupal.locale.admin'; $form['langcode'] = array('#type' => 'value', '#value' => $filter_values['langcode']); $form['strings'] = array('#type' => 'table', '#tree' => TRUE, '#language' => $langname, '#header' => [$this->t('Source string'), $this->t('Translation for @language', ['@language' => $langname])], '#empty' => $this->t('No strings available.'), '#attributes' => ['class' => ['locale-translate-edit-table']]); if (isset($langcode)) { $strings = $this->translateFilterLoadStrings(); $plural_formulas = $this->state->get('locale.translation.plurals') ?: array(); foreach ($strings as $string) { // Cast into source string, will do for our purposes. $source = new SourceString($string); // Split source to work with plural values. $source_array = $source->getPlurals(); $translation_array = $string->getPlurals(); if (count($source_array) == 1) { // Add original string value and mark as non-plural. $plural = FALSE; $form['strings'][$string->lid]['original'] = array('#type' => 'item', '#title' => $this->t('Source string (@language)', array('@language' => $this->t('Built-in English'))), '#title_display' => 'invisible', '#markup' => '<span lang="en">' . String::checkPlain($source_array[0]) . '</span>'); } else { // Add original string value and mark as plural. $plural = TRUE; $original_singular = ['#type' => 'item', '#title' => $this->t('Singular form'), '#markup' => '<span lang="en">' . String::checkPlain($source_array[0]) . '</span>', '#prefix' => '<span class="visually-hidden">' . $this->t('Source string (@language)', array('@language' => $this->t('Built-in English'))) . '</span>']; $original_plural = ['#type' => 'item', '#title' => $this->t('Plural form'), '#markup' => '<span lang="en">' . String::checkPlain($source_array[1]) . '</span>']; $form['strings'][$string->lid]['original'] = [$original_singular, ['#markup' => '<br>'], $original_plural]; } if (!empty($string->context)) { $form['strings'][$string->lid]['original'][] = ['#type' => 'inline_template', '#template' => '<br><small>{{ context_title }}: <span lang="en">{{ context }}</span></small>', '#context' => ['context_title' => $this->t('In Context'), 'context' => $string->context]]; } // Approximate the number of rows to use in the default textarea. $rows = min(ceil(str_word_count($source_array[0]) / 12), 10); if (!$plural) { $form['strings'][$string->lid]['translations'][0] = array('#type' => 'textarea', '#title' => $this->t('Translated string (@language)', array('@language' => $langname)), '#title_display' => 'invisible', '#rows' => $rows, '#default_value' => $translation_array[0], '#attributes' => array('lang' => $langcode)); } else { // Dealing with plural strings. if (isset($plural_formulas[$langcode]['plurals']) && $plural_formulas[$langcode]['plurals'] > 2) { // Add a textarea for each plural variant. for ($i = 0; $i < $plural_formulas[$langcode]['plurals']; $i++) { $form['strings'][$string->lid]['translations'][$i] = array('#type' => 'textarea', '#title' => $i == 0 ? $this->t('Singular form') : $this->formatPlural($i, 'First plural form', '@count. plural form'), '#rows' => $rows, '#default_value' => isset($translation_array[$i]) ? $translation_array[$i] : '', '#attributes' => array('lang' => $langcode), '#prefix' => $i == 0 ? '<span class="visually-hidden">' . $this->t('Translated string (@language)', array('@language' => $langname)) . '</span>' : ''); } } else { // Fallback for unknown number of plurals. $form['strings'][$string->lid]['translations'][0] = array('#type' => 'textarea', '#title' => $this->t('Singular form'), '#rows' => $rows, '#default_value' => $translation_array[0], '#attributes' => array('lang' => $langcode), '#prefix' => '<span class="visually-hidden">' . $this->t('Translated string (@language)', array('@language' => $langname)) . '</span>'); $form['strings'][$string->lid]['translations'][1] = array('#type' => 'textarea', '#title' => $this->t('Plural form'), '#rows' => $rows, '#default_value' => isset($translation_array[1]) ? $translation_array[1] : '', '#attributes' => array('lang' => $langcode)); } } } if (count(Element::children($form['strings']))) { $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Save translations')); } } $form['pager']['#type'] = 'pager'; return $form; }
/** * Stores the errors of each element directly on the element. * * We must provide a way for non-form functions to check the errors for a * specific element. The most common usage of this is a #pre_render callback. * * @param array $elements * An associative array containing the structure of a form element. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. */ protected function setElementErrorsFromFormState(array &$elements, FormStateInterface &$form_state) { // Recurse through all children. foreach (Element::children($elements) as $key) { if (isset($elements[$key]) && $elements[$key]) { $this->setElementErrorsFromFormState($elements[$key], $form_state); } } // Store the errors for this element on the element directly. $elements['#errors'] = $form_state->getError($elements); }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state, CartInterface $cart = NULL) { $form['#attached']['library'][] = 'uc_cart/uc_cart.styles'; $cart_config = $this->config('uc_cart.settings'); $form['items'] = array('#type' => 'table', '#tree' => TRUE, '#header' => array('remove' => array('data' => $this->t('Remove'), 'class' => array('remove')), 'image' => array('data' => $this->t('Products'), 'class' => array('image')), 'desc' => array('data' => '', 'class' => array('desc')), 'qty' => array('data' => $this->t('Quantity'), 'class' => array('qty')), 'total' => array('data' => $this->t('Total'), 'class' => array('price')))); $form['data'] = array('#tree' => TRUE, '#parents' => array('items')); $i = 0; $subtotal = 0; foreach ($cart->getContents() as $cart_item) { $item = \Drupal::moduleHandler()->invoke($cart_item->data->module, 'uc_cart_display', array($cart_item)); if (Element::children($item)) { $form['items'][$i]['remove'] = $item['remove']; $form['items'][$i]['remove']['#name'] = 'remove-' . $i; $form['items'][$i]['image'] = uc_product_get_picture($item['nid']['#value'], 'uc_cart'); $form['items'][$i]['desc']['title'] = $item['title']; $form['items'][$i]['desc']['description'] = $item['description']; $form['items'][$i]['qty'] = $item['qty']; $form['items'][$i]['total'] = array('#theme' => 'uc_price', '#price' => $item['#total'], '#wrapper_attributes' => array('class' => 'total')); if (!empty($item['#suffixes'])) { $form['items'][$i]['total']['#suffixes'] = $item['#suffixes']; } $form['data'][$i]['module'] = $item['module']; $form['data'][$i]['nid'] = $item['nid']; $form['data'][$i]['data'] = $item['data']; $form['data'][$i]['title'] = array('#type' => 'value', '#value' => $item['title']['#markup']); $subtotal += $item['#total']; } $i++; } $form['items'][]['total'] = array('#theme' => 'uc_price', '#prefix' => '<span id="subtotal-title">' . $this->t('Subtotal') . ':</span> ', '#price' => $subtotal, '#wrapper_attributes' => array('colspan' => 5, 'class' => array('subtotal'))); $form['actions'] = array('#type' => 'actions'); // If the continue shopping element is enabled... if (($cs_type = $cart_config->get('continue_shopping_type')) !== 'none') { // Add the element to the form based on the element type. if ($cart_config->get('continue_shopping_type') == 'link') { $form['actions']['continue_shopping'] = array('#markup' => $this->l($this->t('Continue shopping'), Url::fromUri('internal:' . $this->continueShoppingUrl()))); } elseif ($cart_config->get('continue_shopping_type') == 'button') { $form['actions']['continue_shopping'] = array('#type' => 'submit', '#value' => $this->t('Continue shopping'), '#submit' => array(array($this, 'submitForm'), array($this, 'continueShopping'))); } } // Add the empty cart button if enabled. if ($cart_config->get('empty_button')) { $form['actions']['empty'] = array('#type' => 'submit', '#value' => $this->t('Empty cart'), '#submit' => array(array($this, 'emptyCart'))); } // Add the control buttons for updating and proceeding to checkout. $form['actions']['update'] = array('#type' => 'submit', '#name' => 'update-cart', '#value' => $this->t('Update cart'), '#submit' => array(array($this, 'submitForm'), array($this, 'displayUpdateMessage'))); $form['actions']['checkout'] = array('#theme' => 'uc_cart_checkout_buttons'); if ($cart_config->get('checkout_enabled')) { $form['actions']['checkout']['checkout'] = array('#type' => 'submit', '#value' => $this->t('Checkout'), '#button_type' => 'primary', '#submit' => array(array($this, 'submitForm'), array($this, 'checkout'))); } $this->renderer->addCacheableDependency($form, $cart); $this->renderer->addCacheableDependency($form, $cart_config); return $form; }
/** * {@inheritdoc} */ public function preRender(&$element, $rendering_object) { $element += array('#prefix' => '<div class=" ' . implode(' ', $this->getClasses()) . '">', '#suffix' => '</div>', '#tree' => TRUE, '#parents' => array($this->group->group_name), '#default_tab' => ''); if ($this->getSetting('id')) { $element['#id'] = Html::getId($this->getSetting('id')); } // By default tabs don't have titles but you can override it in the theme. if ($this->getLabel()) { $element['#title'] = SafeMarkup::checkPlain($this->getLabel()); } $form_state = new \Drupal\Core\Form\FormState(); if ($this->getSetting('direction') == 'vertical') { $element += array('#type' => 'vertical_tabs', '#theme_wrappers' => array('vertical_tabs')); $complete_form = array(); $element = \Drupal\Core\Render\Element\VerticalTabs::processVerticalTabs($element, $form_state, $complete_form); } else { $element += array('#type' => 'horizontal_tabs', '#theme_wrappers' => array('horizontal_tabs')); $on_form = $this->context == 'form'; $element = \Drupal\field_group\Element\HorizontalTabs::processHorizontalTabs($element, $form_state, $on_form); } // Make sure the group has 1 child. This is needed to succeed at form_pre_render_vertical_tabs(). // Skipping this would force us to move all child groups to this array, making it an un-nestable. $element['group']['#groups'][$this->group->group_name] = array(0 => array()); $element['group']['#groups'][$this->group->group_name]['#group_exists'] = TRUE; // Search for a tab that was marked as open. First one wins. foreach (\Drupal\Core\Render\Element::children($element) as $tab_name) { if (!empty($element[$tab_name]['#open'])) { $element[$this->group->group_name . '__active_tab']['#default_value'] = $tab_name; break; } } }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { $forum_config = $this->config('forum.settings'); $vid = $forum_config->get('vocabulary'); $vocabulary = $this->entityManager->getStorage('taxonomy_vocabulary')->load($vid); if (!$vocabulary) { throw new NotFoundHttpException(); } // Build base taxonomy term overview. $form = parent::buildForm($form, $form_state, $vocabulary); foreach (Element::children($form['terms']) as $key) { if (isset($form['terms'][$key]['#term'])) { $term = $form['terms'][$key]['#term']; $form['terms'][$key]['term']['#url'] = Url::fromRoute('forum.page', ['taxonomy_term' => $term->id()]); unset($form['terms'][$key]['operations']['#links']['delete']); $route_parameters = $form['terms'][$key]['operations']['#links']['edit']['url']->getRouteParameters(); if (!empty($term->forum_container->value)) { $form['terms'][$key]['operations']['#links']['edit']['title'] = $this->t('edit container'); $form['terms'][$key]['operations']['#links']['edit']['url'] = Url::fromRoute('entity.taxonomy_term.forum_edit_container_form', $route_parameters); } else { $form['terms'][$key]['operations']['#links']['edit']['title'] = $this->t('edit forum'); $form['terms'][$key]['operations']['#links']['edit']['url'] = Url::fromRoute('entity.taxonomy_term.forum_edit_form', $route_parameters); } // We don't want the redirect from the link so we can redirect the // delete action. unset($form['terms'][$key]['operations']['#links']['edit']['query']['destination']); } } // Remove the alphabetical reset. unset($form['actions']['reset_alphabetical']); // Use the existing taxonomy overview submit handler. $form['terms']['#empty'] = $this->t('No containers or forums available. <a href="@container">Add container</a> or <a href="@forum">Add forum</a>.', array('@container' => $this->url('forum.add_container'), '@forum' => $this->url('forum.add_forum'))); return $form; }
/** * Will replace placeholders in the #text offsets. * * @param array $data * Data structures where to replace placeholders. * @param $variables * Key value pairs. */ protected function replacePlaceholders(&$data, $variables) { foreach (Element::children($data) as $key) { if (isset($data[$key]['#text'])) { $data[$key]['#text'] = (string) new FormattableMarkup($data[$key]['#text'], $variables); } else { $this->replacePlaceholders($data[$key], $variables); } } }
/** * {@inheritdoc} */ public function view(FieldItemListInterface $items, $langcode = NULL) { // Default the language to the current content language. if (empty($langcode)) { $langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); } $elements = $this->viewElements($items, $langcode); // If there are actual renderable children, use #theme => field, otherwise, // let access cacheability metadata pass through for correct bubbling. if (Element::children($elements)) { $entity = $items->getEntity(); $entity_type = $entity->getEntityTypeId(); $field_name = $this->fieldDefinition->getName(); $info = array('#theme' => 'field', '#title' => $this->fieldDefinition->getLabel(), '#label_display' => $this->label, '#view_mode' => $this->viewMode, '#language' => $items->getLangcode(), '#field_name' => $field_name, '#field_type' => $this->fieldDefinition->getType(), '#field_translatable' => $this->fieldDefinition->isTranslatable(), '#entity_type' => $entity_type, '#bundle' => $entity->bundle(), '#object' => $entity, '#items' => $items, '#formatter' => $this->getPluginId(), '#is_multiple' => $this->fieldDefinition->getFieldStorageDefinition()->isMultiple()); $elements = array_merge($info, $elements); } return $elements; }
/** * Allows modules to modify forms before Drupal invokes hook_form_alter(). * * This hook will normally be used by core modules so any form modifications * they make can be further modified by contrib modules using a normal * hook_form_alter(). At this point, drupal_prepare_form() has not been called, * so none of the automatic form data (e.g.: #parameters, #build_id, etc.) has * been added yet. * * @see hook_form_alter() */ function hook_uc_form_alter(&$form, &$form_state, $form_id) { // If the node has a product list, add attributes to them if (isset($form['products']) && count(Element::children($form['products']))) { foreach (Element::children($form['products']) as $key) { $form['products'][$key]['attributes'] = _uc_attribute_alter_form(node_load($key)); if (is_array($form['products'][$key]['attributes'])) { $form['products'][$key]['attributes']['#tree'] = TRUE; $form['products'][$key]['#type'] = 'details'; } } } else { $form['attributes'] = _uc_attribute_alter_form($node); if (is_array($form['attributes'])) { $form['attributes']['#tree'] = TRUE; $form['attributes']['#weight'] = -1; } } }
/** * {@inheritdoc} */ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { $build = parent::view($entity, $view_mode, $langcode); if ($view_mode == 'mail') { // Convert field labels into headings. // @todo Improve \Drupal\Core\Mail\MailFormatHelper::htmlToText() to // convert DIVs correctly. foreach (Element::children($build) as $key) { if (isset($build[$key]['#label_display']) && $build[$key]['#label_display'] == 'above') { $build[$key] += array('#prefix' => ''); $build[$key]['#prefix'] = $build[$key]['#title'] . ":\n"; $build[$key]['#label_display'] = 'hidden'; } } $build['#post_render'][] = function ($html, array $elements) { return MailFormatHelper::htmlToText($html); }; } return $build; }
/** * {@inheritdoc} */ public function build() { $cart = Cart::create(\Drupal::getContainer()); $product_count = count($cart->getContents()); // Display nothing if the block is set to hide on empty and there are no // items in the cart. if (!$this->configuration['hide_empty'] || $product_count) { $items = array(); $item_count = 0; $total = 0; if ($product_count) { foreach ($cart->getContents() as $item) { $display_item = \Drupal::moduleHandler()->invoke($item->data->module, 'uc_cart_display', array($item)); if (count(Element::children($display_item))) { $items[] = array('nid' => $display_item['nid']['#value'], 'qty' => $display_item['qty']['#default_value'], 'title' => $display_item['title']['#markup'], 'price' => $display_item['#total'], 'desc' => isset($display_item['description']['#markup']) ? $display_item['description']['#markup'] : FALSE); $total += $display_item['#total']; $item_count += $display_item['qty']['#default_value']; } } } // Build the cart links. $summary_links['view-cart'] = array('title' => $this->t('View cart'), 'url' => Url::fromRoute('uc_cart.cart'), 'attributes' => array('rel' => ['nofollow'])); // Only add the checkout link if checkout is enabled. if (\Drupal::config('uc_cart.settings')->get('checkout_enabled')) { $summary_links['checkout'] = array('title' => $this->t('Checkout'), 'url' => Url::fromRoute('uc_cart.checkout'), 'attributes' => array('rel' => ['nofollow'])); } $build['block'] = array('#theme' => 'uc_cart_block', '#items' => $items, '#item_count' => $item_count, '#total' => $total, '#summary_links' => $summary_links, '#collapsed' => $this->configuration['collapsed']); // Add the cart block CSS. $build['#attached']['library'][] = 'uc_cart/uc_cart_block.styles'; // If the block is collapsible, add the appropriate JS. if ($this->configuration['collapsible']) { $build['#attached']['library'][] = 'system/drupal.system'; $build['#attached']['library'][] = 'uc_cart/uc_cart_block.scripts'; } return $build; } }
/** * Tests that #cache_properties are properly handled. * * @param array $expected_results * An associative array of expected results keyed by property name. * * @covers ::render * @covers ::doRender * @covers \Drupal\Core\Render\RenderCache::get * @covers \Drupal\Core\Render\RenderCache::set * @covers \Drupal\Core\Render\RenderCache::createCacheID * @covers \Drupal\Core\Render\RenderCache::getCacheableRenderArray * * @dataProvider providerTestRenderCacheProperties */ public function testRenderCacheProperties(array $expected_results) { $this->setUpRequest(); $this->setupMemoryCache(); $element = $original = [ '#cache' => [ 'keys' => ['render_cache_test'], ], // Collect expected property names. '#cache_properties' => array_keys(array_filter($expected_results)), 'child1' => ['#markup' => Markup::create('1')], 'child2' => ['#markup' => Markup::create('2')], // Mark the value as safe. '#custom_property' => Markup::create('custom_value'), '#custom_property_array' => ['custom value'], ]; $this->renderer->renderRoot($element); $cache = $this->cacheFactory->get('render'); $data = $cache->get('render_cache_test:en:stark')->data; // Check that parent markup is ignored when caching children's markup. $this->assertEquals($data['#markup'] === '', (bool) Element::children($data)); // Check that the element properties are cached as specified. foreach ($expected_results as $property => $expected) { $cached = !empty($data[$property]); $this->assertEquals($cached, (bool) $expected); // Check that only the #markup key is preserved for children. if ($cached) { $this->assertEquals($data[$property], $original[$property]); } } // #custom_property_array can not be a safe_cache_property. $safe_cache_properties = array_diff(Element::properties(array_filter($expected_results)), ['#custom_property_array']); foreach ($safe_cache_properties as $cache_property) { $this->assertTrue(SafeMarkup::isSafe($data[$cache_property]), "$cache_property is marked as a safe string"); } }
/** * Helper method to allow for easy menu link tree structure assertions. * * Converts the result of MenuLinkTree::build() in a "menu link ID tree". * * @param array $build * The return value of of MenuLinkTree::build() * * @return array * The "menu link ID tree" representation of the given render array. */ protected function convertBuiltMenuToIdTree(array $build) { $level = []; foreach (Element::children($build) as $id) { $level[$id] = []; if (isset($build[$id]['below'])) { $level[$id] = $this->convertBuiltMenuToIdTree($build[$id]['below']); } } return $level; }
/** * Builds the renderable view of an entity. * * Entities postpone the composition of their renderable arrays to #pre_render * functions in order to maximize cache efficacy. This means that the full * renderable array for an entity is constructed in drupal_render(). Some * tests require the complete renderable array for an entity outside of the * drupal_render process in order to verify the presence of specific values. * This method isolates the steps in the render process that produce an * entity's renderable array. * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity to prepare a renderable array for. * @param string $view_mode * (optional) The view mode that should be used to build the entity. * @param null $langcode * (optional) For which language the entity should be prepared, defaults to * the current content language. * @param bool $reset * (optional) Whether to clear the cache for this entity. * @return array * * @see drupal_render() */ protected function drupalBuildEntityView(EntityInterface $entity, $view_mode = 'full', $langcode = NULL, $reset = FALSE) { $ensure_fully_built = function (&$elements) use(&$ensure_fully_built) { // If the default values for this element have not been loaded yet, populate // them. if (isset($elements['#type']) && empty($elements['#defaults_loaded'])) { $elements += \Drupal::service('element_info')->getInfo($elements['#type']); } // Make any final changes to the element before it is rendered. This means // that the $element or the children can be altered or corrected before the // element is rendered into the final text. if (isset($elements['#pre_render'])) { foreach ($elements['#pre_render'] as $callable) { $elements = call_user_func($callable, $elements); } } // And recurse. $children = Element::children($elements, TRUE); foreach ($children as $key) { $ensure_fully_built($elements[$key]); } }; $render_controller = $this->container->get('entity.manager')->getViewBuilder($entity->getEntityTypeId()); if ($reset) { $render_controller->resetCache(array($entity->id())); } $build = $render_controller->view($entity, $view_mode, $langcode); $ensure_fully_built($build); return $build; }
/** * Adds members of this group as actual elements for rendering. * * @param array $element * An associative array containing the properties and children of the * element. * * @return array * The modified element with all group members. */ public static function preRenderGroup($element) { // The element may be rendered outside of a Form API context. if (!isset($element['#parents']) || !isset($element['#groups'])) { return $element; } // Inject group member elements belonging to this group. $parents = implode('][', $element['#parents']); $children = Element::children($element['#groups'][$parents]); if (!empty($children)) { foreach ($children as $key) { // Break references and indicate that the element should be rendered as // group member. $child = (array) $element['#groups'][$parents][$key]; $child['#group_details'] = TRUE; // Inject the element as new child element. $element[] = $child; $sort = TRUE; } // Re-sort the element's children if we injected group member elements. if (isset($sort)) { $element['#sorted'] = FALSE; } } if (isset($element['#group'])) { // Contains form element summary functionalities. $element['#attached']['library'][] = 'core/drupal.form'; $group = $element['#group']; // If this element belongs to a group, but the group-holding element does // not exist, we need to render it (at its original location). if (!isset($element['#groups'][$group]['#group_exists'])) { // Intentionally empty to clarify the flow; we simply return $element. } elseif (!empty($element['#group_details'])) { // Intentionally empty to clarify the flow; we simply return $element. } elseif (Element::children($element['#groups'][$group])) { $element['#printed'] = TRUE; } } return $element; }
/** * Alter the render array generated by an EntityDisplay for an entity. * * @param array $build * The renderable array generated by the EntityDisplay. * @param array $context * An associative array containing: * - entity: The entity being rendered. * - view_mode: The view mode; for example, 'full' or 'teaser'. * - display: The EntityDisplay holding the display options. * * @ingroup entity_crud */ function hook_entity_display_build_alter(&$build, $context) { // Append RDF term mappings on displayed taxonomy links. foreach (Element::children($build) as $field_name) { $element =& $build[$field_name]; if ($element['#field_type'] == 'entity_reference' && $element['#formatter'] == 'entity_reference_label') { foreach ($element['#items'] as $delta => $item) { $term = $item->entity; if (!empty($term->rdf_mapping['rdftype'])) { $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype']; } if (!empty($term->rdf_mapping['name']['predicates'])) { $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates']; } } } } }
/** * Builds the Toolbar as a structured array ready for drupal_render(). * * Since building the toolbar takes some time, it is done just prior to * rendering to ensure that it is built only if it will be displayed. * * @param array $element * A renderable array. * * @return array * A renderable array. * * @see toolbar_page_top(). */ public static function preRenderToolbar($element) { // Get the configured breakpoints to switch from vertical to horizontal // toolbar presentation. $breakpoints = static::breakpointManager()->getBreakpointsByGroup('toolbar'); if (!empty($breakpoints)) { $media_queries = array(); foreach ($breakpoints as $id => $breakpoint) { $media_queries[$id] = $breakpoint->getMediaQuery(); } $element['#attached']['drupalSettings']['toolbar']['breakpoints'] = $media_queries; } $module_handler = static::moduleHandler(); // Get toolbar items from all modules that implement hook_toolbar(). $items = $module_handler->invokeAll('toolbar'); // Allow for altering of hook_toolbar(). $module_handler->alter('toolbar', $items); // Sort the children. uasort($items, array('\\Drupal\\Component\\Utility\\SortArray', 'sortByWeightProperty')); // Merge in the original toolbar values. $element = array_merge($element, $items); // Assign each item a unique ID, based on its key. foreach (Element::children($element) as $key) { $element[$key]['#id'] = Html::getId('toolbar-item-' . $key); } return $element; }
/** * Rewrite #states selectors. * * @param array $elements * A renderable array element having a #states property. * @param string $search * A partial or entire jQuery selector string to replace in #states. * @param string $replace * The string to replace all instances of $search with. * * @see drupal_process_states() */ public static function rewriteStatesSelector(array &$elements, $search, $replace) { if (!empty($elements['#states'])) { foreach ($elements['#states'] as $state => $ids) { static::processStatesArray($elements['#states'][$state], $search, $replace); } } foreach (Element::children($elements) as $key) { static::rewriteStatesSelector($elements[$key], $search, $replace); } }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $config = $this->config('cosign.settings'); foreach (Element::children($form) as $variable) { $config->set($variable, $form_state->getValue($form[$variable]['#parents'])); } $config->save(); if (method_exists($this, '_submitForm')) { $this->_submitForm($form, $form_state); } parent::submitForm($form, $form_state); }
/** * Returns the title for the element. * * If the element has no title, this will recurse through all children of the * element until a title is found. * * @param array $element * An associative array containing the properties of the form element. * * @return string * The title of the element, or an empty string if none is found. */ public static function getElementTitle(array $element) { $title = ''; if (isset($element['#title'])) { $title = $element['#title']; } else { foreach (Element::children($element) as $key) { if ($title = static::getElementTitle($element[$key])) { break; } } } return $title; }
/** * {@inheritdoc} */ public function view(FieldItemListInterface $items) { $elements = $this->viewElements($items); // If there are actual renderable children, use #theme => field, otherwise, // let access cacheability metadata pass through for correct bubbling. if (Element::children($elements)) { $entity = $items->getEntity(); $entity_type = $entity->getEntityTypeId(); $field_name = $this->fieldDefinition->getName(); $info = array('#theme' => 'field', '#title' => $this->fieldDefinition->getLabel(), '#label_display' => $this->label, '#view_mode' => $this->viewMode, '#language' => $items->getLangcode(), '#field_name' => $field_name, '#field_type' => $this->fieldDefinition->getType(), '#field_translatable' => $this->fieldDefinition->isTranslatable(), '#entity_type' => $entity_type, '#bundle' => $entity->bundle(), '#object' => $entity, '#items' => $items, '#formatter' => $this->getPluginId()); $elements = array_merge($info, $elements); } return $elements; }
/** * Implements preRenderAddThisWrapper() * - Defines consistent markup for the addthis_wrapper render element. * @param $element * @return mixed */ public static function preRenderAddThisWrapper($element) { $tag_open = '<' . $element['#tag'] . new Attribute($element['#attributes']) . '>'; $children = Element::children($element); $output = ''; if (count($children) > 0) { foreach ($children as $child) { $output .= render($element[$child]); } } $tag_close = '</' . $element['#tag'] . "> \n"; $element['#markup'] = $tag_open . $output . $tag_close; return $element; }
/** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { $form = parent::buildForm($form, $form_state); // If we are adding the field storage as a dependency to delete, then that // will list the field as a dependency. That is confusing, so remove it. // Also remove the entity type and the whole entity deletions details // element if nothing else is in there. if (isset($form['entity_deletes']['field_config']['#items']) && isset($form['entity_deletes']['field_config']['#items'][$this->entity->id()])) { unset($form['entity_deletes']['field_config']['#items'][$this->entity->id()]); if (empty($form['entity_deletes']['field_config']['#items'])) { unset($form['entity_deletes']['field_config']); if (!Element::children($form['entity_deletes'])) { $form['entity_deletes']['#access'] = FALSE; } } } return $form; }
/** * Converts a nested data array into a flattened structure with a combined key. * * This function can be used by translators to help with the data conversion. * * Nested keys will be joined together using a colon, so for example * $data['key1']['key2']['key3'] will be converted into * $flattened_data['key1][key2][key3']. * * @param array $data * The nested array structure that should be flattened. * @param string $prefix * Internal use only, indicates the current key prefix when recursing into * the data array. * @param array $label * Label for the data. * * @return array * The flattened data array. */ public function flatten(array $data, $prefix = NULL, $label = array()) { $flattened_data = array(); if (isset($data['#label'])) { $label[] = $data['#label']; } // Each element is either a text (has #text property defined) or has children, // not both. if (!empty($data['#text'])) { $flattened_data[$prefix] = $data; $flattened_data[$prefix]['#parent_label'] = $label; } else { $prefix = isset($prefix) ? $prefix . static::TMGMT_ARRAY_DELIMITER : ''; foreach (Element::children($data) as $key) { $flattened_data += $this->flatten($data[$key], $prefix . $key, $label); } } return $flattened_data; }
/** * #pre_render callback for #type 'actions'. * * This callback iterates over all child elements of the #type 'actions' * container to look for elements with a #dropbutton property, so as to group * those elements into dropbuttons. As such, it works similar to #group, but is * specialized for dropbuttons. * * The value of #dropbutton denotes the dropbutton to group the child element * into. For example, two different values of 'foo' and 'bar' on child elements * would generate two separate dropbuttons, which each contain the corresponding * buttons. * * @param array $element * The #type 'actions' element to process. * @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 #type 'actions' element, including individual buttons grouped * into new #type 'dropbutton' elements. */ public static function preRenderActionsDropbutton(&$element, FormStateInterface $form_state, &$complete_form) { $dropbuttons = array(); foreach (Element::children($element, TRUE) as $key) { if (isset($element[$key]['#dropbutton'])) { $dropbutton = $element[$key]['#dropbutton']; // If there is no dropbutton for this button group yet, create one. if (!isset($dropbuttons[$dropbutton])) { $dropbuttons[$dropbutton] = array('#type' => 'dropbutton'); } // Add this button to the corresponding dropbutton. // @todo Change #type 'dropbutton' to be based on theme_item_list() // instead of links.html.twig to avoid this preemptive rendering. $button = drupal_render($element[$key]); $dropbuttons[$dropbutton]['#links'][$key] = array('title' => $button, 'html' => TRUE); } } // @todo For now, all dropbuttons appear first. Consider to invent a more // fancy sorting/injection algorithm here. return $dropbuttons + $element; }
/** * {@inheritdoc} */ public function buildConfigurationForm(array $form, FormStateInterface $form_state) { $form = $this->plugin->buildConfigurationForm([], $form_state); // Move the menu levels section to the bottom. $form['menu_levels']['#weight'] = 100; $form['entity_form'] = ['#type' => 'details', '#title' => $this->t('Edit menu %label', array('%label' => $this->entity->label())), '#open' => TRUE]; $form['entity_form'] += $this->getEntityForm($this->entity)->buildForm([], $form_state); // Print the menu link titles as text instead of a link. if (!empty($form['entity_form']['links']['links'])) { foreach (Element::children($form['entity_form']['links']['links']) as $child) { $title = $form['entity_form']['links']['links'][$child]['title'][1]['#title']; $form['entity_form']['links']['links'][$child]['title'][1] = ['#markup' => $title]; } } // Change the header text. $form['entity_form']['links']['links']['#header'][0] = $this->t('Link'); $form['entity_form']['links']['links']['#header'][1]['data'] = $this->t('On'); // Remove the label, ID, description, and buttons from the entity form. unset($form['entity_form']['label'], $form['entity_form']['id'], $form['entity_form']['description'], $form['entity_form']['actions']); // Since the overview form is further nested than expected, update the // #parents. See \Drupal\menu_ui\MenuForm::form(). $form_state->set('menu_overview_form_parents', ['settings', 'entity_form', 'links']); return $form; }