/** * Tests the rendering of blocks. */ public function testBasicRendering() { \Drupal::state()->set('block_test.content', ''); $entity = $this->controller->create(array('id' => 'test_block1', 'theme' => 'stark', 'plugin' => 'test_html')); $entity->save(); // Test the rendering of a block. $entity = Block::load('test_block1'); $output = entity_view($entity, 'block'); $expected = array(); $expected[] = '<div id="block-test-block1">'; $expected[] = ' '; $expected[] = ' '; $expected[] = ' '; $expected[] = ' </div>'; $expected[] = ''; $expected_output = implode("\n", $expected); $this->assertEqual($this->renderer->renderRoot($output), $expected_output); // Reset the HTML IDs so that the next render is not affected. Html::resetSeenIds(); // Test the rendering of a block with a given title. $entity = $this->controller->create(array('id' => 'test_block2', 'theme' => 'stark', 'plugin' => 'test_html', 'settings' => array('label' => 'Powered by Bananas'))); $entity->save(); $output = entity_view($entity, 'block'); $expected = array(); $expected[] = '<div id="block-test-block2">'; $expected[] = ' '; $expected[] = ' <h2>Powered by Bananas</h2>'; $expected[] = ' '; $expected[] = ' '; $expected[] = ' </div>'; $expected[] = ''; $expected_output = implode("\n", $expected); $this->assertEqual($this->renderer->renderRoot($output), $expected_output); }
/** * {@inheritdoc} */ protected function setUp() { parent::setUp(); $this->installConfig(['jquery_ui_filter']); // Reset HTML ids. Html::resetSeenIds(); // Get \Drupal\jquery_ui_filter\Plugin\Filter\jQueryUiFilter object. $filter_bag = new FilterPluginCollection(\Drupal::service('plugin.manager.filter'), ['jquery_ui_filter']); $this->filter = $filter_bag->get('jquery_ui_filter'); }
/** * {@inheritdoc} */ public function getForm(ViewEntityInterface $view, $display_id, $js) { $form_state = $this->getFormState($view, $display_id, $js); $view = $form_state->get('view'); $key = $form_state->get('form_key'); // @todo Remove the need for this. \Drupal::moduleHandler()->loadInclude('views_ui', 'inc', 'admin'); // Reset the cache of IDs. Drupal rather aggressively prevents ID // duplication but this causes it to remember IDs that are no longer even // being used. Html::resetSeenIds(); // check to see if this is the top form of the stack. If it is, pop // it off; if it isn't, the user clicked somewhere else and the stack is // now irrelevant. if (!empty($view->stack)) { $identifier = implode('-', array_filter([$key, $view->id(), $display_id, $form_state->get('type'), $form_state->get('id')])); // Retrieve the first form from the stack without changing the integer keys, // as they're being used for the "2 of 3" progress indicator. reset($view->stack); list($key, $top) = each($view->stack); unset($view->stack[$key]); if (array_shift($top) != $identifier) { $view->stack = array(); } } // Automatically remove the form cache if it is set and the key does // not match. This way navigating away from the form without hitting // update will work. if (isset($view->form_cache) && $view->form_cache['key'] != $key) { unset($view->form_cache); } $form_class = get_class($form_state->getFormObject()); $response = $this->ajaxFormWrapper($form_class, $form_state); // If the form has not been submitted, or was not set for rerendering, stop. if (!$form_state->isSubmitted() || $form_state->get('rerender')) { return $response; } // Sometimes we need to re-generate the form for multi-step type operations. if (!empty($view->stack)) { $stack = $view->stack; $top = array_shift($stack); // Build the new form state for the next form in the stack. $reflection = new \ReflectionClass($view::$forms[$top[1]]); /** @var $form_state \Drupal\Core\Form\FormStateInterface */ $form_state = $reflection->newInstanceArgs(array_slice($top, 3, 2))->getFormState($view, $top[2], $form_state->get('ajax')); $form_class = get_class($form_state->getFormObject()); $form_state->setUserInput(array()); $form_url = views_ui_build_form_url($form_state); if (!$form_state->get('ajax')) { return new RedirectResponse($form_url->setAbsolute()->toString()); } $form_state->set('url', $form_url); $response = $this->ajaxFormWrapper($form_class, $form_state); } elseif (!$form_state->get('ajax')) { // if nothing on the stack, non-js forms just go back to the main view editor. $display_id = $form_state->get('display_id'); return new RedirectResponse($this->url('entity.view.edit_display_form', ['view' => $view->id(), 'display_id' => $display_id], ['absolute' => TRUE])); } else { $response = new AjaxResponse(); $response->addCommand(new CloseModalDialogCommand()); $response->addCommand(new Ajax\ShowButtonsCommand(!empty($view->changed))); $response->addCommand(new Ajax\TriggerPreviewCommand()); if ($page_title = $form_state->get('page_title')) { $response->addCommand(new Ajax\ReplaceTitleCommand($page_title)); } } // If this form was for view-wide changes, there's no need to regenerate // the display section of the form. if ($display_id !== '') { \Drupal::entityManager()->getFormObject('view', 'edit')->rebuildCurrentTab($view, $response, $display_id); } return $response; }
/** * Add another form to the stack; clicking 'apply' will go to this form * rather than closing the ajax popup. */ public function addFormToStack($key, $display_id, $type, $id = NULL, $top = FALSE, $rebuild_keys = FALSE) { // Reset the cache of IDs. Drupal rather aggressively prevents ID // duplication but this causes it to remember IDs that are no longer even // being used. Html::resetSeenIds(); if (empty($this->stack)) { $this->stack = array(); } $stack = array(implode('-', array_filter(array($key, $this->id(), $display_id, $type, $id))), $key, $display_id, $type, $id); // If we're being asked to add this form to the bottom of the stack, no // special logic is required. Our work is equally easy if we were asked to add // to the top of the stack, but there's nothing in it yet. if (!$top || empty($this->stack)) { $this->stack[] = $stack; } else { $keys = array_keys($this->stack); $first = current($keys); $last = end($keys); for ($i = $last; $i >= $first; $i--) { if (!isset($this->stack[$i])) { continue; } // Move form number $i to the next position in the stack. $this->stack[$i + 1] = $this->stack[$i]; unset($this->stack[$i]); } // Now that the previously $first slot is free, move the new form into it. $this->stack[$first] = $stack; ksort($this->stack); // Start the keys from 0 again, if requested. if ($rebuild_keys) { $this->stack = array_values($this->stack); } } }
/** * {@inheritdoc} */ protected function tearDown() { Html::resetSeenIds(); (new FormState())->clearErrors(); }
/** * {@inheritdoc} */ public function processForm($form_id, &$form, FormStateInterface &$form_state) { $form_state->setValues([]); // With GET, these forms are always submitted if requested. if ($form_state->isMethodType('get') && $form_state->getAlwaysProcess()) { $input = $form_state->getUserInput(); if (!isset($input['form_build_id'])) { $input['form_build_id'] = $form['#build_id']; } if (!isset($input['form_id'])) { $input['form_id'] = $form_id; } if (!isset($input['form_token']) && isset($form['#token'])) { $input['form_token'] = $this->csrfToken->get($form['#token']); } $form_state->setUserInput($input); } // self::doBuildForm() finishes building the form by calling element // #process functions and mapping user input, if any, to #value properties, // and also storing the values in $form_state->getValues(). We need to // retain the unprocessed $form in case it needs to be cached. $unprocessed_form = $form; $form = $this->doBuildForm($form_id, $form, $form_state); // Only process the input if we have a correct form submission. if ($form_state->isProcessingInput()) { // Form values for programmed form submissions typically do not include a // value for the submit button. But without a triggering element, a // potentially existing #limit_validation_errors property on the primary // submit button is not taken account. Therefore, check whether there is // exactly one submit button in the form, and if so, automatically use it // as triggering_element. $buttons = $form_state->getButtons(); if ($form_state->isProgrammed() && !$form_state->getTriggeringElement() && count($buttons) == 1) { $form_state->setTriggeringElement(reset($buttons)); } $this->formValidator->validateForm($form_id, $form, $form_state); // \Drupal\Component\Utility\Html::getUniqueId() maintains a cache of // element IDs it has seen, so it can prevent duplicates. We want to be // sure we reset that cache when a form is processed, so scenarios that // result in the form being built behind the scenes and again for the // browser don't increment all the element IDs needlessly. if (!FormState::hasAnyErrors()) { // In case of errors, do not break HTML IDs of other forms. Html::resetSeenIds(); } // If there are no errors and the form is not rebuilding, submit the form. if (!$form_state->isRebuilding() && !FormState::hasAnyErrors()) { $submit_response = $this->formSubmitter->doSubmitForm($form, $form_state); // If this form was cached, delete it from the cache after submission. if ($form_state->isCached()) { $this->deleteCache($form['#build_id']); } // If the form submission directly returned a response, return it now. if ($submit_response) { return $submit_response; } } // Don't rebuild or cache form submissions invoked via self::submitForm(). if ($form_state->isProgrammed()) { return; } // If $form_state->isRebuilding() has been set and input has been // processed without validation errors, we are in a multi-step workflow // that is not yet complete. A new $form needs to be constructed based on // the changes made to $form_state during this request. Normally, a submit // handler sets $form_state->isRebuilding() if a fully executed form // requires another step. However, for forms that have not been fully // executed (e.g., Ajax submissions triggered by non-buttons), there is no // submit handler to set $form_state->isRebuilding(). It would not make // sense to redisplay the identical form without an error for the user to // correct, so we also rebuild error-free non-executed forms, regardless // of $form_state->isRebuilding(). // @todo Simplify this logic; considering Ajax and non-HTML front-ends, // along with element-level #submit properties, it makes no sense to // have divergent form execution based on whether the triggering element // has #executes_submit_callback set to TRUE. if (($form_state->isRebuilding() || !$form_state->isExecuted()) && !FormState::hasAnyErrors()) { // Form building functions (e.g., self::handleInputElement()) may use // $form_state->isRebuilding() to determine if they are running in the // context of a rebuild, so ensure it is set. $form_state->setRebuild(); $form = $this->rebuildForm($form_id, $form_state, $form); } } // After processing the form, the form builder or a #process callback may // have called $form_state->setCached() to indicate that the form and form // state shall be cached. But the form may only be cached if // $form_state->disableCache() is not called. Only cache $form as it was // prior to self::doBuildForm(), because self::doBuildForm() must run for // each request to accommodate new user input. Rebuilt forms are not cached // here, because self::rebuildForm() already takes care of that. if (!$form_state->isRebuilding() && $form_state->isCached()) { $this->setCache($form['#build_id'], $unprocessed_form, $form_state); } }
/** * Tests the Html::getUniqueId() method. * * @param string $expected * The expected result. * @param string $source * The string being transformed to an ID. * @param bool $reset * (optional) If TRUE, reset the list of seen IDs. Defaults to FALSE. * * @dataProvider providerTestHtmlGetUniqueIdWithAjaxIds * * @covers ::getUniqueId */ public function testHtmlGetUniqueIdWithAjaxIds($expected, $source, $reset = FALSE) { if ($reset) { Html::resetSeenIds(); } Html::setAjaxHtmlIds('test-unique-id1 test-unique-id2--3'); $this->assertSame($expected, Html::getUniqueId($source)); }
/** * {@inheritdoc} */ protected function tearDown() { Html::resetSeenIds(); }
/** * Tests the Html::getUniqueId() method. * * @param string $expected * The expected result. * @param string $source * The string being transformed to an ID. * @param bool $reset * (optional) If TRUE, reset the list of seen IDs. Defaults to FALSE. * * @dataProvider providerTestHtmlGetUniqueId * * @covers ::getUniqueId */ public function testHtmlGetUniqueId($expected, $source, $reset = FALSE) { if ($reset) { Html::resetSeenIds(); } $this->assertSame($expected, Html::getUniqueId($source)); }