コード例 #1
0
 /**
  * Returns the libraries that need to be loaded.
  *
  * For example, with core/a depending on core/c and core/b on core/d:
  * @code
  * $assets = new AttachedAssets();
  * $assets->setLibraries(['core/a', 'core/b', 'core/c']);
  * $assets->setAlreadyLoadedLibraries(['core/c']);
  * $resolver->getLibrariesToLoad($assets) === ['core/a', 'core/b', 'core/d']
  * @endcode
  *
  * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets
  *   The assets attached to the current response.
  *
  * @return string[]
  *   A list of libraries and their dependencies, in the order they should be
  *   loaded, excluding any libraries that have already been loaded.
  */
 protected function getLibrariesToLoad(AttachedAssetsInterface $assets)
 {
     return array_diff($this->libraryDependencyResolver->getLibrariesWithDependencies($assets->getLibraries()), $this->libraryDependencyResolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()));
 }
コード例 #2
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();
    }
コード例 #3
0
ファイル: BigPipe.php プロジェクト: DrupalTV/DrupalTV
    /**
     * 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();
    }