/**
  * Processes AJAX file uploads and deletions.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The current request object.
  *
  * @return \Drupal\Core\Ajax\AjaxResponse
  *   An AjaxResponse object.
  */
 public function upload(Request $request)
 {
     $form_parents = explode('/', $request->query->get('element_parents'));
     $form_build_id = $request->query->get('form_build_id');
     $request_form_build_id = $request->request->get('form_build_id');
     if (empty($request_form_build_id) || $form_build_id !== $request_form_build_id) {
         // Invalid request.
         drupal_set_message(t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', array('@size' => format_size(file_upload_max_size()))), 'error');
         $response = new AjaxResponse();
         $status_messages = array('#theme' => 'status_messages');
         return $response->addCommand(new ReplaceCommand(NULL, drupal_render($status_messages)));
     }
     try {
         /** @var $ajaxForm \Drupal\system\FileAjaxForm */
         $ajaxForm = $this->getForm($request);
         $form = $ajaxForm->getForm();
         $form_state = $ajaxForm->getFormState();
         $commands = $ajaxForm->getCommands();
     } catch (HttpExceptionInterface $e) {
         // Invalid form_build_id.
         drupal_set_message(t('An unrecoverable error occurred. Use of this form has expired. Try reloading the page and submitting again.'), 'error');
         $response = new AjaxResponse();
         $status_messages = array('#theme' => 'status_messages');
         return $response->addCommand(new ReplaceCommand(NULL, drupal_render($status_messages)));
     }
     // Get the current element and count the number of files.
     $current_element = NestedArray::getValue($form, $form_parents);
     $current_file_count = isset($current_element['#file_upload_delta']) ? $current_element['#file_upload_delta'] : 0;
     // Process user input. $form and $form_state are modified in the process.
     drupal_process_form($form['#form_id'], $form, $form_state);
     // Retrieve the element to be rendered.
     $form = NestedArray::getValue($form, $form_parents);
     // Add the special Ajax class if a new file was added.
     if (isset($form['#file_upload_delta']) && $current_file_count < $form['#file_upload_delta']) {
         $form[$current_file_count]['#attributes']['class'][] = 'ajax-new-content';
     } else {
         $form['#suffix'] .= '<span class="ajax-new-content"></span>';
     }
     $status_messages = array('#theme' => 'status_messages');
     $form['#prefix'] .= drupal_render($status_messages);
     $output = drupal_render($form);
     drupal_process_attached($form);
     $js = _drupal_add_js();
     $settings = drupal_merge_js_settings($js['settings']['data']);
     $response = new AjaxResponse();
     foreach ($commands as $command) {
         $response->addCommand($command, TRUE);
     }
     return $response->addCommand(new ReplaceCommand(NULL, $output, $settings));
 }
 /**
  * If $sticky is FALSE, no tableheader.js should be included.
  */
 function testThemeTableNoStickyHeaders()
 {
     $header = array('one', 'two', 'three');
     $rows = array(array(1, 2, 3), array(4, 5, 6), array(7, 8, 9));
     $attributes = array();
     $caption = NULL;
     $colgroups = array();
     $table = array('#type' => 'table', '#header' => $header, '#rows' => $rows, '#attributes' => $attributes, '#caption' => $caption, '#colgroups' => $colgroups, '#sticky' => FALSE);
     $this->render($table);
     $js = _drupal_add_js();
     $this->assertFalse(isset($js['core/misc/tableheader.js']), 'tableheader.js not found.');
     $this->assertNoRaw('sticky-enabled');
     drupal_static_reset('_drupal_add_js');
 }
Exemple #3
0
 /**
  * Tests AjaxResponse::prepare() AJAX commands ordering.
  */
 public function testOrder()
 {
     $path = drupal_get_path('module', 'system');
     $expected_commands = array();
     // Expected commands, in a very specific order.
     $expected_commands[0] = new SettingsCommand(array('ajax' => 'test'), TRUE);
     drupal_static_reset('_drupal_add_css');
     $attached = array('#attached' => array('css' => array($path . '/css/system.admin.css' => array(), $path . '/css/system.maintenance.css' => array())));
     drupal_render($attached);
     drupal_process_attached($attached);
     $expected_commands[1] = new AddCssCommand(drupal_get_css(_drupal_add_css(), TRUE));
     drupal_static_reset('_drupal_add_js');
     $attached = array('#attached' => array('js' => array($path . '/system.js' => array())));
     drupal_render($attached);
     drupal_process_attached($attached);
     $expected_commands[2] = new PrependCommand('head', drupal_get_js('header', _drupal_add_js(), TRUE));
     drupal_static_reset('_drupal_add_js');
     $attached = array('#attached' => array('js' => array($path . '/system.modules.js' => array('scope' => 'footer'))));
     drupal_render($attached);
     drupal_process_attached($attached);
     $expected_commands[3] = new AppendCommand('body', drupal_get_js('footer', _drupal_add_js(), TRUE));
     $expected_commands[4] = new HtmlCommand('body', 'Hello, world!');
     // Load any page with at least one CSS file, at least one JavaScript file
     // and at least one #ajax-powered element. The latter is an assumption of
     // drupalPostAjaxForm(), the two former are assumptions of
     // AjaxReponse::ajaxRender().
     // @todo refactor AJAX Framework + tests to make less assumptions.
     $this->drupalGet('ajax_forms_test_lazy_load_form');
     // Verify AJAX command order — this should always be the order:
     // 1. JavaScript settings
     // 2. CSS files
     // 3. JavaScript files in the header
     // 4. JavaScript files in the footer
     // 5. Any other AJAX commands, in whatever order they were added.
     $commands = $this->drupalPostAjaxForm(NULL, array(), NULL, 'ajax-test/order', array(), array(), NULL, array());
     $this->assertCommand(array_slice($commands, 0, 1), $expected_commands[0]->render(), 'Settings command is first.');
     $this->assertCommand(array_slice($commands, 1, 1), $expected_commands[1]->render(), 'CSS command is second (and CSS files are ordered correctly).');
     $this->assertCommand(array_slice($commands, 2, 1), $expected_commands[2]->render(), 'Header JS command is third.');
     $this->assertCommand(array_slice($commands, 3, 1), $expected_commands[3]->render(), 'Footer JS command is fourth.');
     $this->assertCommand(array_slice($commands, 4, 1), $expected_commands[4]->render(), 'HTML command is fifth.');
 }
 /**
  * {@inheritdoc}
  */
 public function getForm(ViewStorageInterface $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');
     \Drupal::moduleHandler()->loadInclude('views', 'inc', 'includes/ajax');
     // 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);
     }
     // With the below logic, we may end up rendering a form twice (or two forms
     // each sharing the same element ids), potentially resulting in
     // _drupal_add_js() being called twice to add the same setting. drupal_get_js()
     // is ok with that, but until \Drupal\Core\Ajax\AjaxResponse::ajaxRender()
     // is (http://drupal.org/node/208611), reset the _drupal_add_js() static
     // before rendering the second time.
     $drupal_add_js_original = _drupal_add_js();
     $drupal_add_js =& drupal_static('_drupal_add_js');
     $form_class = get_class($form_state->getFormObject());
     $response = views_ajax_form_wrapper($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)) {
         $drupal_add_js = $drupal_add_js_original;
         $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_path = views_ui_build_form_path($form_state);
         if (!$form_state->get('ajax')) {
             return new RedirectResponse(_url($form_path, array('absolute' => TRUE)));
         }
         $form_state->set('path', $form_path);
         $response = views_ajax_form_wrapper($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;
 }
 /**
  * Tests JavaScript files that have querystrings attached get added right.
  */
 function testAddJsFileWithQueryString()
 {
     $js = drupal_get_path('module', 'node') . '/node.js';
     _drupal_add_js($js);
     $query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
     $scripts = drupal_get_js();
     $this->assertTrue(strpos($scripts, $js . '?' . $query_string), 'Query string was appended correctly to JS.');
 }
Exemple #6
0
 /**
  * Tests css/js storage and restoring mechanism.
  */
 function testHeaderStorage()
 {
     // Create a view with output caching enabled.
     // Some hook_views_pre_render in views_test_data.module adds the test css/js file.
     // so they should be added to the css/js storage.
     $view = Views::getView('test_view');
     $view->setDisplay();
     $view->storage->set('id', 'test_cache_header_storage');
     $view->display_handler->overrideOption('cache', array('type' => 'time', 'options' => array('output_lifespan' => '3600')));
     $output = $view->preview();
     drupal_render($output);
     unset($view->pre_render_called);
     drupal_static_reset('_drupal_add_css');
     drupal_static_reset('_drupal_add_js');
     $view->destroy();
     $view->setDisplay();
     $output = $view->preview();
     drupal_render($output);
     $css = _drupal_add_css();
     $css_path = drupal_get_path('module', 'views_test_data') . '/views_cache.test.css';
     $js_path = drupal_get_path('module', 'views_test_data') . '/views_cache.test.js';
     $js = _drupal_add_js();
     $this->assertTrue(isset($css[basename($css_path)]), 'Make sure the css is added for cached views.');
     $this->assertTrue(isset($js[$js_path]), 'Make sure the js is added for cached views.');
     $this->assertFalse(!empty($view->build_info['pre_render_called']), 'Make sure hook_views_pre_render is not called for the cached view.');
     // Now add some css/jss before running the view.
     // Make sure that this css is not added when running the cached view.
     $view->storage->set('id', 'test_cache_header_storage_2');
     $attached = array('#attached' => array('css' => array(drupal_get_path('module', 'system') . '/css/system.maintenance.css' => array()), 'js' => array(drupal_get_path('module', 'user') . '/user.permissions.js' => array())));
     drupal_render($attached);
     $view->destroy();
     $output = $view->preview();
     drupal_render($output);
     drupal_static_reset('_drupal_add_css');
     drupal_static_reset('_drupal_add_js');
     $view->destroy();
     $output = $view->preview();
     drupal_render($output);
     $css = _drupal_add_css();
     $js = _drupal_add_js();
     $this->assertFalse(isset($css['system.maintenance.css']), 'Make sure that unrelated css is not added.');
     $this->assertFalse(isset($js[drupal_get_path('module', 'user') . '/user.permissions.js']), 'Make sure that unrelated js is not added.');
 }
Exemple #7
0
 /**
  * Prepares the AJAX commands for sending back to the client.
  *
  * @param Request $request
  *   The request object that the AJAX is responding to.
  *
  * @return array
  *   An array of commands ready to be returned as JSON.
  */
 protected function ajaxRender(Request $request)
 {
     // Ajax responses aren't rendered with html.html.twig, so we have to call
     // drupal_get_css() and drupal_get_js() here, in order to have new files
     // added during this request to be loaded by the page. We only want to send
     // back files that the page hasn't already loaded, so we implement simple
     // diffing logic using array_diff_key().
     $ajax_page_state = $request->request->get('ajax_page_state');
     foreach (array('css', 'js') as $type) {
         // It is highly suspicious if
         // $request->request->get("ajax_page_state[$type]") is empty, since the
         // base page ought to have at least one JS file and one CSS file loaded.
         // It probably indicates an error, and rather than making the page reload
         // all of the files, instead we return no new files.
         if (empty($ajax_page_state[$type])) {
             $items[$type] = array();
         } else {
             $function = '_drupal_add_' . $type;
             $items[$type] = $function();
             \Drupal::moduleHandler()->alter($type, $items[$type]);
             // @todo Inline CSS and JS items are indexed numerically. These can't be
             //   reliably diffed with array_diff_key(), since the number can change
             //   due to factors unrelated to the inline content, so for now, we
             //   strip the inline items from Ajax responses, and can add support for
             //   them when _drupal_add_css() and _drupal_add_js() are changed to use
             //   a hash of the inline content as the array key.
             foreach ($items[$type] as $key => $item) {
                 if (is_numeric($key)) {
                     unset($items[$type][$key]);
                 }
             }
             // Ensure that the page doesn't reload what it already has.
             $items[$type] = array_diff_key($items[$type], $ajax_page_state[$type]);
         }
     }
     // Render the HTML to load these files, and add AJAX commands to insert this
     // HTML in the page. We pass TRUE as the $skip_alter argument to prevent the
     // data from being altered again, as we already altered it above. Settings
     // are handled separately, afterwards.
     if (isset($items['js']['settings'])) {
         unset($items['js']['settings']);
     }
     $styles = drupal_get_css($items['css'], TRUE);
     $scripts_footer = drupal_get_js('footer', $items['js'], TRUE, TRUE);
     $scripts_header = drupal_get_js('header', $items['js'], TRUE, TRUE);
     // Prepend commands to add the resources, preserving their relative order.
     $resource_commands = array();
     if (!empty($styles)) {
         $resource_commands[] = new AddCssCommand($styles);
     }
     if (!empty($scripts_header)) {
         $resource_commands[] = new PrependCommand('head', $scripts_header);
     }
     if (!empty($scripts_footer)) {
         $resource_commands[] = new AppendCommand('body', $scripts_footer);
     }
     foreach (array_reverse($resource_commands) as $resource_command) {
         $this->addCommand($resource_command, TRUE);
     }
     // Prepend a command to merge changes and additions to drupalSettings.
     $scripts = _drupal_add_js();
     if (!empty($scripts['settings'])) {
         $settings = drupal_merge_js_settings($scripts['settings']['data']);
         // During Ajax requests basic path-specific settings are excluded from
         // new drupalSettings values. The original page where this request comes
         // from already has the right values for the keys below. An Ajax request
         // would update them with values for the Ajax request and incorrectly
         // override the page's values.
         // @see _drupal_add_js()
         foreach (array('basePath', 'currentPath', 'scriptPath', 'pathPrefix') as $item) {
             unset($settings[$item]);
         }
         $this->addCommand(new SettingsCommand($settings, TRUE), TRUE);
     }
     $commands = $this->commands;
     \Drupal::moduleHandler()->alter('ajax_render', $commands);
     return $commands;
 }