Exemplo n.º 1
0
 /**
  * {@inheritdoc}
  */
 public function renderResponse(array $main_content, Request $request, RouteMatchInterface $route_match)
 {
     $response = new AjaxResponse();
     if (isset($main_content['#type']) && $main_content['#type'] == 'ajax') {
         // Complex Ajax callbacks can return a result that contains an error
         // message or a specific set of commands to send to the browser.
         $main_content += $this->elementInfoManager->getInfo('ajax');
         $error = $main_content['#error'];
         if (!empty($error)) {
             // Fall back to some default message otherwise use the specific one.
             if (!is_string($error)) {
                 $error = 'An error occurred while handling the request: The server received invalid input.';
             }
             $response->addCommand(new AlertCommand($error));
         }
     }
     $html = $this->drupalRenderRoot($main_content);
     $response->setAttachments($main_content['#attached']);
     // The selector for the insert command is NULL as the new content will
     // replace the element making the Ajax call. The default 'replaceWith'
     // behavior can be changed with #ajax['method'].
     $response->addCommand(new InsertCommand(NULL, $html));
     $status_messages = array('#type' => 'status_messages');
     $output = $this->drupalRenderRoot($status_messages);
     if (!empty($output)) {
         $response->addCommand(new PrependCommand(NULL, $output));
     }
     return $response;
 }
  /**
   * Returns an AjaxResponse; settings command set last.
   *
   * Helps verifying AjaxResponse reorders commands to ensure correct execution.
   */
  public function order() {
    $response = new AjaxResponse();
    // HTML insertion command.
    $response->addCommand(new HtmlCommand('body', 'Hello, world!'));
    $build['#attached']['library'][] = 'ajax_test/order';
    $response->setAttachments($build['#attached']);

    return $response;
  }
 function er_browser_widget_search_content(array &$form, FormStateInterface $form_state)
 {
     $form = \Drupal::formBuilder()->getForm('Drupal\\er_browser_widget\\Form\\EntityReferenceBrowserWidgetForm');
     $response = new AjaxResponse();
     $title = $this->t('Entity Search and Reference.');
     $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
     $response->setAttachments($form['#attached']);
     $content = views_embed_view('entity_reference_browser_widget');
     $options = array('dialogClass' => 'test-dialog', 'width' => '75%');
     $modal = new OpenModalDialogCommand($title, $form, $options);
     $response->addCommand($modal);
     return $response;
 }
Exemplo n.º 4
0
 /**
  * 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('#type' => 'status_messages');
         return $response->addCommand(new ReplaceCommand(NULL, $this->renderer->renderRoot($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('#type' => 'status_messages');
         return $response->addCommand(new ReplaceCommand(NULL, $this->renderer->renderRoot($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.
     $this->formBuilder->processForm($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('#type' => 'status_messages');
     $form['#prefix'] .= $this->renderer->renderRoot($status_messages);
     $output = $this->renderer->renderRoot($form);
     $response = new AjaxResponse();
     $response->setAttachments($form['#attached']);
     foreach ($commands as $command) {
         $response->addCommand($command, TRUE);
     }
     return $response->addCommand(new ReplaceCommand(NULL, $output));
 }
Exemplo n.º 5
0
 /**
  * {@inheritdoc}
  */
 public function renderResponse(array $main_content, Request $request, RouteMatchInterface $route_match)
 {
     $response = new AjaxResponse();
     // First render the main content, because it might provide a title.
     $content = $this->renderer->renderRoot($main_content);
     // Attach the library necessary for using the OpenOffCanvasDialogCommand and
     // set the attachments for this Ajax response.
     $main_content['#attached']['library'][] = 'outside_in/drupal.off_canvas';
     $response->setAttachments($main_content['#attached']);
     // If the main content doesn't provide a title, use the title resolver.
     $title = isset($main_content['#title']) ? $main_content['#title'] : $this->titleResolver->getTitle($request, $route_match->getRouteObject());
     // Determine the title: use the title provided by the main content if any,
     // otherwise get it from the routing information.
     $options = $request->request->get('dialogOptions', []);
     $response->addCommand(new OpenOffCanvasDialogCommand($title, $content, $options));
     return $response;
 }
Exemplo n.º 6
0
 /**
  * {@inheritdoc}
  */
 public function renderResponse(array $main_content, Request $request, RouteMatchInterface $route_match)
 {
     $response = new AjaxResponse();
     // First render the main content, because it might provide a title.
     $content = drupal_render_root($main_content);
     // Attach the library necessary for using the OpenDialogCommand and set the
     // attachments for this Ajax response.
     $main_content['#attached']['library'][] = 'core/drupal.dialog.ajax';
     $response->setAttachments($main_content['#attached']);
     // Determine the title: use the title provided by the main content if any,
     // otherwise get it from the routing information.
     $title = isset($main_content['#title']) ? $main_content['#title'] : $this->titleResolver->getTitle($request, $route_match->getRouteObject());
     // Determine the dialog options and the target for the OpenDialogCommand.
     $options = $request->request->get('dialogOptions', array());
     $target = $this->determineTargetSelector($options, $route_match);
     $response->addCommand(new OpenDialogCommand($target, $title, $content, $options));
     return $response;
 }
Exemplo n.º 7
0
 /**
  * Wrapper for handling AJAX forms.
  *
  * Wrapper around \Drupal\Core\Form\FormBuilderInterface::buildForm() to
  * handle some AJAX stuff automatically.
  * This makes some assumptions about the client.
  *
  * @param \Drupal\Core\Form\FormInterface|string $form_class
  *   The value must be one of the following:
  *   - The name of a class that implements \Drupal\Core\Form\FormInterface.
  *   - An instance of a class that implements \Drupal\Core\Form\FormInterface.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  *
  * @return \Drupal\Core\Ajax\AjaxResponse|string|array
  *   Returns one of three possible values:
  *   - A \Drupal\Core\Ajax\AjaxResponse object.
  *   - The rendered form, as a string.
  *   - A render array with the title in #title and the rendered form in the
  *   #markup array.
  */
 protected function ajaxFormWrapper($form_class, FormStateInterface &$form_state)
 {
     /** @var \Drupal\Core\Render\RendererInterface $renderer */
     $renderer = \Drupal::service('renderer');
     // This won't override settings already in.
     if (!$form_state->has('rerender')) {
         $form_state->set('rerender', FALSE);
     }
     $ajax = $form_state->get('ajax');
     // Do not overwrite if the redirect has been disabled.
     if (!$form_state->isRedirectDisabled()) {
         $form_state->disableRedirect($ajax);
     }
     $form_state->disableCache();
     // Builds the form in a render context in order to ensure that cacheable
     // metadata is bubbled up.
     $render_context = new RenderContext();
     $callable = function () use($form_class, &$form_state) {
         return \Drupal::formBuilder()->buildForm($form_class, $form_state);
     };
     $form = $renderer->executeInRenderContext($render_context, $callable);
     if (!$render_context->isEmpty()) {
         BubbleableMetadata::createFromRenderArray($form)->merge($render_context->pop())->applyTo($form);
     }
     $output = $renderer->renderRoot($form);
     drupal_process_attached($form);
     // These forms have the title built in, so set the title here:
     $title = $form_state->get('title') ?: '';
     if ($ajax && (!$form_state->isExecuted() || $form_state->get('rerender'))) {
         // If the form didn't execute and we're using ajax, build up an
         // Ajax command list to execute.
         $response = new AjaxResponse();
         // Attach the library necessary for using the OpenModalDialogCommand and
         // set the attachments for this Ajax response.
         $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
         $response->setAttachments($form['#attached']);
         $display = '';
         $status_messages = array('#type' => 'status_messages');
         if ($messages = $renderer->renderRoot($status_messages)) {
             $display = '<div class="views-messages">' . $messages . '</div>';
         }
         $display .= $output;
         $options = array('dialogClass' => 'views-ui-dialog', 'width' => '75%');
         $response->addCommand(new OpenModalDialogCommand($title, $display, $options));
         if ($section = $form_state->get('#section')) {
             $response->addCommand(new Ajax\HighlightCommand('.' . Html::cleanCssIdentifier($section)));
         }
         return $response;
     }
     return $title ? ['#title' => $title, '#markup' => $output] : $output;
 }
Exemplo n.º 8
0
    /**
     * Sends BigPipe placeholders' replacements as embedded AJAX responses.
     *
     * @param array $placeholders
     *   Associative array; the BigPipe placeholders. Keys are the BigPipe
     *   placeholder IDs.
     * @param array $placeholder_order
     *   Indexed array; the order in which the BigPipe placeholders must be sent.
     *   Values are the BigPipe placeholder IDs. (These values correspond to keys
     *   in $placeholders.)
     * @param \Drupal\Core\Asset\AttachedAssetsInterface $cumulative_assets
     *   The cumulative assets sent so far; to be updated while rendering BigPipe
     *   placeholders.
     *
     * @throws \Exception
     *   If an exception is thrown during the rendering of a placeholder, it is
     *   caught to allow the other placeholders to still be replaced. But when
     *   error logging is configured to be verbose, the exception is rethrown to
     *   simplify debugging.
     */
    protected function sendPlaceholders(array $placeholders, array $placeholder_order, AttachedAssetsInterface $cumulative_assets)
    {
        // Return early if there are no BigPipe placeholders to send.
        if (empty($placeholders)) {
            return;
        }
        // Send the start signal.
        print "\n";
        print static::START_SIGNAL;
        print "\n";
        flush();
        // A BigPipe response consists of a HTML response plus multiple embedded
        // AJAX responses. To process the attachments of those AJAX responses, we
        // need a fake request that is identical to the master request, but with
        // one change: it must have the right Accept header, otherwise the work-
        // around for a bug in IE9 will cause not JSON, but <textarea>-wrapped JSON
        // to be returned.
        // @see \Drupal\Core\EventSubscriber\AjaxResponseSubscriber::onResponse()
        $fake_request = $this->requestStack->getMasterRequest()->duplicate();
        $fake_request->headers->set('Accept', 'application/vnd.drupal-ajax');
        foreach ($placeholder_order as $placeholder_id) {
            if (!isset($placeholders[$placeholder_id])) {
                continue;
            }
            // Render the placeholder.
            $placeholder_render_array = $placeholders[$placeholder_id];
            try {
                $elements = $this->renderPlaceholder($placeholder_id, $placeholder_render_array);
            } catch (\Exception $e) {
                if (\Drupal::config('system.logging')->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE) {
                    throw $e;
                } else {
                    trigger_error($e, E_USER_ERROR);
                    continue;
                }
            }
            // Create a new AjaxResponse.
            $ajax_response = new AjaxResponse();
            // JavaScript's querySelector automatically decodes HTML entities in
            // attributes, so we must decode the entities of the current BigPipe
            // placeholder ID (which has HTML entities encoded since we use it to find
            // the placeholders).
            $big_pipe_js_placeholder_id = Html::decodeEntities($placeholder_id);
            $ajax_response->addCommand(new ReplaceCommand(sprintf('[data-big-pipe-placeholder-id="%s"]', $big_pipe_js_placeholder_id), $elements['#markup']));
            $ajax_response->setAttachments($elements['#attached']);
            // Push a fake request with the asset libraries loaded so far and dispatch
            // KernelEvents::RESPONSE event. This results in the attachments for the
            // AJAX response being processed by AjaxResponseAttachmentsProcessor and
            // hence:
            // - the necessary AJAX commands to load the necessary missing asset
            //   libraries and updated AJAX page state are added to the AJAX response
            // - the attachments associated with the response are finalized, which
            //   allows us to track the total set of asset libraries sent in the
            //   initial HTML response plus all embedded AJAX responses sent so far.
            $fake_request->request->set('ajax_page_state', ['libraries' => implode(',', $cumulative_assets->getAlreadyLoadedLibraries())] + $cumulative_assets->getSettings()['ajaxPageState']);
            try {
                $ajax_response = $this->filterEmbeddedResponse($fake_request, $ajax_response);
            } catch (\Exception $e) {
                if (\Drupal::config('system.logging')->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE) {
                    throw $e;
                } else {
                    trigger_error($e, E_USER_ERROR);
                    continue;
                }
            }
            // Send this embedded AJAX response.
            $json = $ajax_response->getContent();
            $output = <<<EOF
    <script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="{$placeholder_id}">
    {$json}
    </script>
EOF;
            print $output;
            flush();
            // Another placeholder was rendered and sent, track the set of asset
            // libraries sent so far. Any new settings are already sent; we don't need
            // to track those.
            if (isset($ajax_response->getAttachments()['drupalSettings']['ajaxPageState']['libraries'])) {
                $cumulative_assets->setAlreadyLoadedLibraries(explode(',', $ajax_response->getAttachments()['drupalSettings']['ajaxPageState']['libraries']));
            }
        }
        // Send the stop signal.
        print "\n";
        print static::STOP_SIGNAL;
        print "\n";
        flush();
    }
Exemplo n.º 9
0
 /**
  * Returns a single field edit form as an Ajax response.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
  *   The entity being edited.
  * @param string $field_name
  *   The name of the field that is being edited.
  * @param string $langcode
  *   The name of the language for which the field is being edited.
  * @param string $view_mode_id
  *   The view mode the field should be rerendered in.
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The current request object containing the search string.
  *
  * @return \Drupal\Core\Ajax\AjaxResponse
  *   The Ajax response.
  */
 public function fieldForm(EntityInterface $entity, $field_name, $langcode, $view_mode_id, Request $request)
 {
     $response = new AjaxResponse();
     // Replace entity with PrivateTempStore copy if available and not resetting,
     // init PrivateTempStore copy otherwise.
     $tempstore_entity = $this->tempStoreFactory->get('quickedit')->get($entity->uuid());
     if ($tempstore_entity && $request->request->get('reset') !== 'true') {
         $entity = $tempstore_entity;
     } else {
         $this->tempStoreFactory->get('quickedit')->set($entity->uuid(), $entity);
     }
     $form_state = (new FormState())->set('langcode', $langcode)->disableRedirect()->addBuildInfo('args', [$entity, $field_name]);
     $form = $this->formBuilder()->buildForm('Drupal\\quickedit\\Form\\QuickEditFieldForm', $form_state);
     if ($form_state->isExecuted()) {
         // The form submission saved the entity in PrivateTempStore. Return the
         // updated view of the field from the PrivateTempStore copy.
         $entity = $this->tempStoreFactory->get('quickedit')->get($entity->uuid());
         // Closure to render the field given a view mode.
         $render_field_in_view_mode = function ($view_mode_id) use($entity, $field_name, $langcode) {
             return $this->renderField($entity, $field_name, $langcode, $view_mode_id);
         };
         // Re-render the updated field.
         $output = $render_field_in_view_mode($view_mode_id);
         // Re-render the updated field for other view modes (i.e. for other
         // instances of the same logical field on the user's page).
         $other_view_mode_ids = $request->request->get('other_view_modes') ?: array();
         $other_view_modes = array_map($render_field_in_view_mode, array_combine($other_view_mode_ids, $other_view_mode_ids));
         $response->addCommand(new FieldFormSavedCommand($output, $other_view_modes));
     } else {
         $output = $this->renderer->renderRoot($form);
         // When working with a hidden form, we don't want its CSS/JS to be loaded.
         if ($request->request->get('nocssjs') !== 'true') {
             $response->setAttachments($form['#attached']);
         }
         $response->addCommand(new FieldFormCommand($output));
         $errors = $form_state->getErrors();
         if (count($errors)) {
             $status_messages = array('#type' => 'status_messages');
             $response->addCommand(new FieldFormValidationErrorsCommand($this->renderer->renderRoot($status_messages)));
         }
     }
     return $response;
 }
Exemplo n.º 10
0
 /**
  * Regression test: Settings command exists regardless of JS aggregation.
  */
 public function testAttachedSettings()
 {
     $assert = function ($message) {
         $response = new AjaxResponse();
         $response->setAttachments(['library' => ['core/drupalSettings'], 'drupalSettings' => ['foo' => 'bar']]);
         $ajax_response_attachments_processor = \Drupal::service('ajax_response.attachments_processor');
         $subscriber = new AjaxResponseSubscriber($ajax_response_attachments_processor);
         $event = new FilterResponseEvent(\Drupal::service('http_kernel'), new Request(), HttpKernelInterface::MASTER_REQUEST, $response);
         $subscriber->onResponse($event);
         $expected = ['command' => 'settings'];
         $this->assertCommand($response->getCommands(), $expected, $message);
     };
     $config = $this->config('system.performance');
     $config->set('js.preprocess', FALSE)->save();
     $assert('Settings command exists when JS aggregation is disabled.');
     $config->set('js.preprocess', TRUE)->save();
     $assert('Settings command exists when JS aggregation is enabled.');
 }
Exemplo n.º 11
0
 /**
  * Renders form and status messages and returns an ajax response.
  *
  * Used for both submission buttons.
  *
  * @param array $form
  *   The form.
  *
  * @return \Drupal\Core\Ajax\AjaxResponse
  *   An ajax response to replace the form.
  */
 protected function ajaxRenderFormAndMessages(array &$form)
 {
     $response = new AjaxResponse();
     // Retrieve the element to be rendered.
     $status_messages = ['#type' => 'status_messages', '#weight' => -10];
     // For some crazy reason, if we do this inline in the replace command, it
     // breaks ajax functionality entirely.
     $output = $this->renderer->renderRoot($form);
     $messages = $this->renderer->renderRoot($status_messages);
     $message_wrapper_id = '#' . self::MESSAGE_WRAPPER_ID;
     $response->setAttachments($form['#attached']);
     $response->addCommand(new ReplaceCommand(NULL, $output));
     $response->addCommand(new HtmlCommand($message_wrapper_id, ''));
     $response->addCommand(new AppendCommand($message_wrapper_id, $messages));
     return $response;
 }
Exemplo n.º 12
0
 /**
  * #ajax callback for managed_file upload forms.
  *
  * This ajax callback takes care of the following things:
  *   - Ensures that broken requests due to too big files are caught.
  *   - Adds a class to the response to be able to highlight in the UI, that a
  *     new file got uploaded.
  *
  * @param array $form
  *   The build form.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The form state.
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The current request.
  *
  * @return \Drupal\Core\Ajax\AjaxResponse
  *   The ajax response of the ajax upload.
  */
 public static function uploadAjaxCallback(&$form, FormStateInterface &$form_state, Request $request)
 {
     /** @var \Drupal\Core\Render\RendererInterface $renderer */
     $renderer = \Drupal::service('renderer');
     $form_parents = explode('/', $request->query->get('element_parents'));
     // Retrieve the element to be rendered.
     $form = NestedArray::getValue($form, $form_parents);
     // Add the special AJAX class if a new file was added.
     $current_file_count = $form_state->get('file_upload_delta_initial');
     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 = ['#type' => 'status_messages'];
     $form['#prefix'] .= $renderer->renderRoot($status_messages);
     $output = $renderer->renderRoot($form);
     $response = new AjaxResponse();
     $response->setAttachments($form['#attached']);
     return $response->addCommand(new ReplaceCommand(NULL, $output));
 }
Exemplo n.º 13
0
    /**
     * Sends BigPipe placeholders' replacements as embedded AJAX responses.
     *
     * @param array $placeholders
     *   Associative array; the BigPipe placeholders. Keys are the BigPipe
     *   selectors.
     * @param array $placeholder_order
     *   Indexed array; the order in which the BigPipe placeholders must be sent.
     *   Values are the BigPipe selectors. (These values correspond to keys in
     *   $placeholders.)
     * @param \Drupal\Core\Asset\AttachedAssetsInterface $cumulative_assets
     *   The cumulative assets sent so far; to be updated while rendering BigPipe
     *   placeholders.
     */
    protected function sendPlaceholders(array $placeholders, array $placeholder_order, AttachedAssetsInterface $cumulative_assets)
    {
        // Return early if there are no BigPipe placeholders to send.
        if (empty($placeholders)) {
            return;
        }
        // Send a container and the start signal.
        print "\n";
        print '<script type="application/json" data-big-pipe-event="start"></script>' . "\n";
        flush();
        // A BigPipe response consists of a HTML response plus multiple embedded
        // AJAX responses. To process the attachments of those AJAX responses, we
        // need a fake request that is identical to the master request, but with
        // one change: it must have the right Accept header, otherwise the work-
        // around for a bug in IE9 will cause not JSON, but <textarea>-wrapped JSON
        // to be returned.
        // @see \Drupal\Core\EventSubscriber\AjaxResponseSubscriber::onResponse()
        $fake_request = $this->requestStack->getMasterRequest()->duplicate();
        $fake_request->headers->set('Accept', 'application/json');
        foreach ($placeholder_order as $placeholder) {
            if (!isset($placeholders[$placeholder])) {
                continue;
            }
            // Render the placeholder.
            $placeholder_render_array = $placeholders[$placeholder];
            $elements = $this->renderPlaceholder($placeholder, $placeholder_render_array);
            // Create a new AjaxResponse.
            $ajax_response = new AjaxResponse();
            // JavaScript's querySelector automatically decodes HTML entities in
            // attributes, so we must decode the entities of the current BigPipe
            // placeholder (which has HTML entities encoded since we use it to find
            // the placeholders).
            $big_pipe_js_selector = Html::decodeEntities($placeholder);
            $ajax_response->addCommand(new ReplaceCommand(sprintf('[data-big-pipe-selector="%s"]', $big_pipe_js_selector), $elements['#markup']));
            $ajax_response->setAttachments($elements['#attached']);
            // Push a fake request with the asset libraries loaded so far and dispatch
            // KernelEvents::RESPONSE event. This results in the attachments for the
            // AJAX response being processed by AjaxResponseAttachmentsProcessor and
            // hence:
            // - the necessary AJAX commands to load the necessary missing asset
            //   libraries and updated AJAX page state are added to the AJAX response
            // - the attachments associated with the response are finalized, which
            //   allows us to track the total set of asset libraries sent in the
            //   initial HTML response plus all embedded AJAX responses sent so far.
            $fake_request->request->set('ajax_page_state', ['libraries' => implode(',', $cumulative_assets->getAlreadyLoadedLibraries())] + $cumulative_assets->getSettings()['ajaxPageState']);
            $this->requestStack->push($fake_request);
            $event = new FilterResponseEvent($this->httpKernel, $fake_request, HttpKernelInterface::SUB_REQUEST, $ajax_response);
            $this->eventDispatcher->dispatch(KernelEvents::RESPONSE, $event);
            $ajax_response = $event->getResponse();
            $this->requestStack->pop();
            // Send this embedded AJAX response.
            $json = $ajax_response->getContent();
            $output = <<<EOF
    <script type="application/json" data-big-pipe-placeholder="{$placeholder}" data-drupal-ajax-processor="big_pipe">
    {$json}
    </script>
EOF;
            print $output;
            flush();
            // Another placeholder was rendered and sent, track the set of asset
            // libraries sent so far. Any new settings are already sent; we don't need
            // to track those.
            if (isset($ajax_response->getAttachments()['drupalSettings']['ajaxPageState']['libraries'])) {
                $cumulative_assets->setAlreadyLoadedLibraries(explode(',', $ajax_response->getAttachments()['drupalSettings']['ajaxPageState']['libraries']));
            }
        }
        // Send the stop signal.
        print '<script type="application/json" data-big-pipe-event="stop"></script>' . "\n";
        flush();
    }
Exemplo n.º 14
0
 /**
  * Regression test: Settings command exists regardless of JS aggregation.
  */
 public function testAttachedSettings()
 {
     $assert = function ($message) {
         $response = new AjaxResponse();
         $response->setAttachments(['library' => ['core/drupalSettings'], 'drupalSettings' => ['foo' => 'bar']]);
         $response->prepare(new Request());
         $expected = ['command' => 'settings'];
         $this->assertCommand($response->getCommands(), $expected, $message);
     };
     $config = $this->config('system.performance');
     $config->set('js.preprocess', FALSE)->save();
     $assert('Settings command exists when JS aggregation is disabled.');
     $config->set('js.preprocess', TRUE)->save();
     $assert('Settings command exists when JS aggregation is enabled.');
 }
 /**
  * Prepares the AJAX commands to attach assets.
  *
  * @param \Drupal\Core\Ajax\AjaxResponse $response
  *   The AJAX response to update.
  * @param \Symfony\Component\HttpFoundation\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 buildAttachmentsCommands(AjaxResponse $response, Request $request)
 {
     $ajax_page_state = $request->request->get('ajax_page_state');
     // Aggregate CSS/JS if necessary, but only during normal site operation.
     $optimize_css = !defined('MAINTENANCE_MODE') && $this->config->get('css.preprocess');
     $optimize_js = !defined('MAINTENANCE_MODE') && $this->config->get('js.preprocess');
     $attachments = $response->getAttachments();
     // Resolve the attached libraries into asset collections.
     $assets = new AttachedAssets();
     $assets->setLibraries(isset($attachments['library']) ? $attachments['library'] : [])->setAlreadyLoadedLibraries(isset($ajax_page_state['libraries']) ? explode(',', $ajax_page_state['libraries']) : [])->setSettings(isset($attachments['drupalSettings']) ? $attachments['drupalSettings'] : []);
     $css_assets = $this->assetResolver->getCssAssets($assets, $optimize_css);
     list($js_assets_header, $js_assets_footer) = $this->assetResolver->getJsAssets($assets, $optimize_js);
     // First, AttachedAssets::setLibraries() ensures duplicate libraries are
     // removed: it converts it to a set of libraries if necessary. Second,
     // AssetResolver::getJsSettings() ensures $assets contains the final set of
     // JavaScript settings. AttachmentsResponseProcessorInterface also mandates
     // that the response it processes contains the final attachment values, so
     // update both the 'library' and 'drupalSettings' attachments accordingly.
     $attachments['library'] = $assets->getLibraries();
     $attachments['drupalSettings'] = $assets->getSettings();
     $response->setAttachments($attachments);
     // Render the HTML to load these files, and add AJAX commands to insert this
     // HTML in the page. Settings are handled separately, afterwards.
     $settings = [];
     if (isset($js_assets_header['drupalSettings'])) {
         $settings = $js_assets_header['drupalSettings']['data'];
         unset($js_assets_header['drupalSettings']);
     }
     if (isset($js_assets_footer['drupalSettings'])) {
         $settings = $js_assets_footer['drupalSettings']['data'];
         unset($js_assets_footer['drupalSettings']);
     }
     // Prepend commands to add the assets, preserving their relative order.
     $resource_commands = array();
     if ($css_assets) {
         $css_render_array = $this->cssCollectionRenderer->render($css_assets);
         $resource_commands[] = new AddCssCommand($this->renderer->renderPlain($css_render_array));
     }
     if ($js_assets_header) {
         $js_header_render_array = $this->jsCollectionRenderer->render($js_assets_header);
         $resource_commands[] = new PrependCommand('head', $this->renderer->renderPlain($js_header_render_array));
     }
     if ($js_assets_footer) {
         $js_footer_render_array = $this->jsCollectionRenderer->render($js_assets_footer);
         $resource_commands[] = new AppendCommand('body', $this->renderer->renderPlain($js_footer_render_array));
     }
     foreach (array_reverse($resource_commands) as $resource_command) {
         $response->addCommand($resource_command, TRUE);
     }
     // Prepend a command to merge changes and additions to drupalSettings.
     if (!empty($settings)) {
         // 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. An Ajax request would update them
         // with values for the Ajax request and incorrectly override the page's
         // values.
         // @see system_js_settings_alter()
         unset($settings['path']);
         $response->addCommand(new SettingsCommand($settings, TRUE), TRUE);
     }
     $commands = $response->getCommands();
     $this->moduleHandler->alter('ajax_render', $commands);
     return $commands;
 }
Exemplo n.º 16
0
 /**
  * Sends an ajax response.
  */
 public function ajaxCallback(array $form, FormStateInterface $form_state)
 {
     $renderer = \Drupal::service('renderer');
     $type = $form_state->getTriggeringElement()['#plugin_type'];
     $response = new AjaxResponse();
     // Set URL hash so that the correct settings tab is open.
     if (isset($form[$type . '_configuration']['#id'])) {
         $hash = ltrim($form[$type . '_configuration']['#id'], '#');
         $response->addCommand(new SetHashCommand($hash));
     }
     // Update the forms.
     $plugin_settings = $renderer->renderRoot($form['plugin_settings']);
     $advanced_settings = $renderer->renderRoot($form[$type . '_wrapper']['advanced']);
     $response->addCommand(new ReplaceCommand('#feeds-ajax-form-wrapper', $plugin_settings));
     $response->addCommand(new ReplaceCommand('#feeds-plugin-' . $type . '-advanced', $advanced_settings));
     // Add attachments.
     $attachments = NestedArray::mergeDeep($form['plugin_settings']['#attached'], $form[$type . '_wrapper']['advanced']['#attached']);
     $response->setAttachments($attachments);
     // Display status messages.
     $status_messages = ['#type' => 'status_messages'];
     $output = $renderer->renderRoot($status_messages);
     if (!empty($output)) {
         $response->addCommand(new HtmlCommand('.region-messages', $output));
     }
     return $response;
 }