/** * Ajax callback to remove a field collection from a multi-valued field. * * @param array $form * @param \Drupal\Core\Form\FormStateInterface $form_state * * @return \Drupal\Core\Ajax\AjaxResponse * An AjaxResponse object. */ function ajaxRemove(array $form, FormStateInterface &$form_state) { // Process user input. $form and $form_state are modified in the process. //\Drupal::formBuilder()->processForm($form['#form_id'], $form, $form_state); // Retrieve the element to be rendered. $trigger = $form_state->getTriggeringElement(); $form_parents = explode('/', $trigger['#ajax']['options']['query']['element_parents']); $address = array_slice($form_parents, 0, -1); $form = NestedArray::getValue($form, $address); $status_messages = array('#theme' => 'status_messages'); $renderer = \Drupal::service('renderer'); $form['#prefix'] = empty($form['#prefix']) ? $renderer->render($status_messages) : $form['#prefix'] . $renderer->render($status_messages); $output = $renderer->render($form); drupal_process_attached($form); // TODO: Preserve javascript. See https://www.drupal.org/node/2502743 . $response = new AjaxResponse(); return $response->addCommand(new ReplaceCommand(NULL, $output)); }
/** * {@inheritdoc} */ public function render(array $render_array) { $content = $this->drupalRenderRoot($render_array); if (!empty($render_array)) { drupal_process_attached($render_array); } $cache = !empty($render_array['#cache']['tags']) ? ['tags' => $render_array['#cache']['tags']] : []; $fragment = new HtmlFragment($content, $cache); if (isset($render_array['#title'])) { $fragment->setTitle($render_array['#title'], Title::FILTER_XSS_ADMIN); } $attached = isset($render_array['#attached']) ? $render_array['#attached'] : []; $attached += ['feed' => [], 'html_head' => [], 'html_head_link' => []]; // Add feed links from the page content. foreach ($attached['feed'] as $feed) { $fragment->addLinkElement(new FeedLinkElement($feed[1], $this->urlGenerator->generateFromPath($feed[0]))); } // Add generic links from the page content. foreach ($attached['html_head_link'] as $link) { $fragment->addLinkElement(new LinkElement($this->urlGenerator->generateFromPath($link[0]['href']), $link[0]['rel'])); } // @todo Also transfer the contents of "_drupal_add_html_head" once // https://www.drupal.org/node/2296951 lands. // @todo Transfer CSS and JS over to the fragment once those are supported // on the fragment object. return $fragment; }
/** * Verifies that the datepicker can be localized. * * @see locale_library_info_alter() */ public function testLibraryInfoAlter() { $attached['#attached']['library'][] = 'core/jquery.ui.datepicker'; drupal_render($attached); drupal_process_attached($attached); $scripts = drupal_get_js(); $this->assertTrue(strpos($scripts, 'locale.datepicker.js'), 'locale.datepicker.js added to scripts.'); }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { if ($form_state->getValue('add_files')) { $path = drupal_get_path('module', 'system'); $attached = array('#attached' => array('css' => array($path . '/css/system.admin.css' => array()), 'js' => array(0 => array('type' => 'setting', 'data' => array('ajax_forms_test_lazy_load_form_submit' => 'executed')), $path . '/system.js' => array()))); drupal_render($attached); drupal_process_attached($attached); } $form_state->setRebuild(); }
/** * Tests drupal_process_attached(). */ public function testDrupalProcessAttached() { // Specify invalid attachments in a render array. $build['#attached']['library'][] = 'core/drupal.states'; $build['#attached']['drupal_process_states'][] = []; try { drupal_process_attached($build); $this->fail("Invalid #attachment 'drupal_process_states' allowed"); } catch (\Exception $e) { $this->pass("Invalid #attachment 'drupal_process_states' not allowed"); } }
/** * {@inheritdoc} */ public function build() { // Grab test attachment fixtures from // Drupal\render_attached_test\Controller\TestController. $controller = new TestController(); $attached = BubbleableMetadata::mergeAttachments($controller->feed(), $controller->head()); $attached = BubbleableMetadata::mergeAttachments($attached, $controller->header()); $attached = BubbleableMetadata::mergeAttachments($attached, $controller->teapotHeaderStatus()); // Use drupal_process_attached() to attach all the #attached stuff. drupal_process_attached($attached); // Return some arbitrary markup so the block doesn't disappear. return ['#markup' => 'Headers handled by drupal_process_attached().']; }
/** * 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)); }
/** * Tests attaching feeds with paths, URLs, and titles. */ function testBasicFeedAddNoTitle() { $path = $this->randomMachineName(12); $external_url = 'http://' . $this->randomMachineName(12) . '/' . $this->randomMachineName(12); $fully_qualified_local_url = Url::fromUri('base:' . $this->randomMachineName(12), array('absolute' => TRUE))->toString(); $path_for_title = $this->randomMachineName(12); $external_for_title = 'http://' . $this->randomMachineName(12) . '/' . $this->randomMachineName(12); $fully_qualified_for_title = Url::fromUri('base:' . $this->randomMachineName(12), array('absolute' => TRUE))->toString(); $urls = array('path without title' => array('url' => Url::fromUri('base:' . $path, array('absolute' => TRUE))->toString(), 'title' => ''), 'external URL without title' => array('url' => $external_url, 'title' => ''), 'local URL without title' => array('url' => $fully_qualified_local_url, 'title' => ''), 'path with title' => array('url' => Url::fromUri('base:' . $path_for_title, array('absolute' => TRUE))->toString(), 'title' => $this->randomMachineName(12)), 'external URL with title' => array('url' => $external_for_title, 'title' => $this->randomMachineName(12)), 'local URL with title' => array('url' => $fully_qualified_for_title, 'title' => $this->randomMachineName(12))); $build = []; foreach ($urls as $feed_info) { $build['#attached']['feed'][] = [$feed_info['url'], $feed_info['title']]; } drupal_process_attached($build); $this->setRawContent(drupal_get_html_head()); foreach ($urls as $description => $feed_info) { $this->assertPattern($this->urlToRSSLinkPattern($feed_info['url'], $feed_info['title']), format_string('Found correct feed header for %description', array('%description' => $description))); } }
/** * 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 render(HtmlFragmentInterface $fragment, $status_code = 200) { // Converts the given HTML fragment which represents the main content region // of the page into a render array. $page_content['main'] = array('#markup' => $fragment->getContent()); $page_content['#title'] = $fragment->getTitle(); if ($fragment instanceof CacheableInterface) { $page_content['main']['#cache']['tags'] = $fragment->getCacheTags(); } // Build the full page array by calling drupal_prepare_page(), which invokes // hook_page_build(). This adds the other regions to the page. $page_array = drupal_prepare_page($page_content); // Build the HtmlPage object. $page = new HtmlPage('', array(), $fragment->getTitle()); $page = $this->preparePage($page, $page_array); $page->setBodyTop(drupal_render($page_array['page_top'])); $page->setBodyBottom(drupal_render($page_array['page_bottom'])); $page->setContent(drupal_render($page_array)); $page->setStatusCode($status_code); drupal_process_attached($page_array); if (isset($page_array['page_top'])) { drupal_process_attached($page_array['page_top']); } if (isset($page_array['page_bottom'])) { drupal_process_attached($page_array['page_bottom']); } if ($fragment instanceof CacheableInterface) { // Persist cache tags associated with this page. Also associate the // "rendered" cache tag. This allows us to invalidate the entire render // cache, regardless of the cache bin. $cache_tags = $page_array['#cache']['tags']; $cache_tags[] = 'rendered'; // Only keep unique cache tags. We need to prevent duplicates here already // rather than only in the cache layer, because they are also used by // reverse proxies (like Varnish), not only by Drupal's page cache. $page->setCacheTags(array_unique($cache_tags)); } return $page; }
/** * Test attached feed rendering as a side-effect. * * @return array * A render array using the 'feed' directive. */ public function feedDpa() { drupal_process_attached($this->feed()); return ['#markup' => "I'm some markup here to fool the kernel into rendering this page."]; }
/** * Renders a render array. * * @param array $elements * The elements to render. * * @return string * The rendered string output (typically HTML). */ protected function render(array &$elements) { $content = drupal_render($elements); drupal_process_attached($elements); $this->setRawContent($content); $this->verbose('<pre style="white-space: pre-wrap">' . String::checkPlain($content)); return $content; }
/** * Wraps drupal_render. * * @param array $elements * The structured array describing the data to be rendered. * * @return string * The rendered HTML. * * @todo Remove once drupal_render is converted to autoloadable code. * @see https://drupal.org/node/2171071 */ protected function drupalRender(array $elements) { $output = drupal_render($elements); drupal_process_attached($elements); return $output; }
/** * Returns AJAX commands to load in-place editors' attachments. * * Given a list of in-place editor IDs as POST parameters, render AJAX * commands to load those in-place editors. * * @return \Drupal\Core\Ajax\AjaxResponse * The Ajax response. */ public function attachments(Request $request) { $response = new AjaxResponse(); $editors = $request->request->get('editors'); if (!isset($editors)) { throw new NotFoundHttpException(); } $elements['#attached'] = $this->editorSelector->getEditorAttachments($editors); drupal_process_attached($elements); return $response; }
/** * {@inheritdoc} */ public function processAttachments(AttachmentsInterface $response) { // @todo Convert to assertion once https://www.drupal.org/node/2408013 lands if (!$response instanceof HtmlResponse) { throw new \InvalidArgumentException('\\Drupal\\Core\\Render\\HtmlResponse instance expected.'); } // First, render the actual placeholders; this may cause additional // attachments to be added to the response, which the attachment // placeholders rendered by renderHtmlResponseAttachmentPlaceholders() will // need to include. $response = $this->renderPlaceholders($response); $attached = $response->getAttachments(); // Get the placeholders from attached and then remove them. $attachment_placeholders = $attached['html_response_attachment_placeholders']; unset($attached['html_response_attachment_placeholders']); $variables = $this->processAssetLibraries($attached, $attachment_placeholders); // Handle all non-asset attachments. This populates drupal_get_html_head(). $all_attached = ['#attached' => $attached]; drupal_process_attached($all_attached); // Get HTML head elements - if present. if (isset($attachment_placeholders['head'])) { $variables['head'] = drupal_get_html_head(FALSE); } // Now replace the attachment placeholders. $this->renderHtmlResponseAttachmentPlaceholders($response, $attachment_placeholders, $variables); // Finally set the headers on the response if any bubbled. if (!empty($attached['http_header'])) { $this->setHeaders($response, $attached['http_header']); } return $response; }
/** * {@inheritdoc} */ public function processAttachments(AttachmentsInterface $response) { // @todo Convert to assertion once https://www.drupal.org/node/2408013 lands if (!$response instanceof HtmlResponse) { throw new \InvalidArgumentException('\\Drupal\\Core\\Render\\HtmlResponse instance expected.'); } // First, render the actual placeholders; this may cause additional // attachments to be added to the response, which the attachment // placeholders rendered by renderHtmlResponseAttachmentPlaceholders() will // need to include. // // @todo Exceptions should not be used for code flow control. However, the // Form API does not integrate with the HTTP Kernel based architecture of // Drupal 8. In order to resolve this issue properly it is necessary to // completely separate form submission from rendering. // @see https://www.drupal.org/node/2367555 try { $response = $this->renderPlaceholders($response); } catch (EnforcedResponseException $e) { return $e->getResponse(); } $attached = $response->getAttachments(); // Get the placeholders from attached and then remove them. $attachment_placeholders = $attached['html_response_attachment_placeholders']; unset($attached['html_response_attachment_placeholders']); $variables = $this->processAssetLibraries($attached, $attachment_placeholders); // Handle all non-asset attachments. This populates drupal_get_html_head(). $all_attached = ['#attached' => $attached]; drupal_process_attached($all_attached); // Get HTML head elements - if present. if (isset($attachment_placeholders['head'])) { $variables['head'] = drupal_get_html_head(FALSE); } // Now replace the attachment placeholders. $this->renderHtmlResponseAttachmentPlaceholders($response, $attachment_placeholders, $variables); // Finally set the headers on the response if any bubbled. if (!empty($attached['http_header'])) { $this->setHeaders($response, $attached['http_header']); } return $response; }
/** * Wraps drupal_render. * * @param string $name * The name of the library. * * @todo Remove once drupal_render is converted to autoloadable code. * @see https://drupal.org/node/2171071 */ protected function drupalAttachLibrary($name) { $attached['#attached']['library'][] = $name; drupal_process_attached($attached); }
/** * Wraps drupal_render_root(). * * @todo: Remove as part of https://drupal.org/node/2182149 */ protected function drupalRenderRoot(&$elements) { $output = drupal_render_root($elements); drupal_process_attached($elements); return $output; }
/** * 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); $view->destroy(); $view->setDisplay(); $output = $view->preview(); drupal_render($output); $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'; $this->assertTrue(in_array($css_path, $output['#attached']['css']), 'Make sure the css is added for cached views.'); $this->assertTrue(in_array($js_path, $output['#attached']['js']), '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); drupal_process_attached($attached); $view->destroy(); $output = $view->preview(); drupal_render($output); $this->assertTrue(empty($output['#attached']['css']), 'The view does not have attached CSS.'); $this->assertTrue(empty($output['#attached']['js']), 'The view does not have attached JS.'); $view->destroy(); $output = $view->preview(); drupal_render($output); $this->assertTrue(empty($output['#attached']['css']), 'The cached view does not have attached CSS.'); $this->assertTrue(empty($output['#attached']['js']), 'The cached view does not have attached JS.'); }
/** * {@inheritdoc} */ public function render(array $output, $status_code = 200) { if (!isset($output['#title'])) { $output['#title'] = $this->titleResolver->getTitle(\Drupal::request(), \Drupal::routeMatch()->getRouteObject()); } $page = new HtmlPage('', isset($output['#cache']) ? $output['#cache'] : array(), $output['#title']); $page_array = drupal_prepare_page($output); $page = $this->fragmentRenderer->preparePage($page, $page_array); $page->setBodyTop(drupal_render($page_array['page_top'])); $page->setBodyBottom(drupal_render($page_array['page_bottom'])); $page->setContent(drupal_render($page_array)); drupal_process_attached($page_array); if (isset($page_array['page_top'])) { drupal_process_attached($page_array['page_top']); } if (isset($page_array['page_bottom'])) { drupal_process_attached($page_array['page_bottom']); } $page->setStatusCode($status_code); return $page; }
/** * Theme callback for rendering an Omega layout. */ function theme_omega_page_layout($variables) { // Clean up the theme hook suggestion so we don't end up in an infinite loop. unset($variables['theme_hook_suggestion'], $variables['theme_hook_suggestions']); $layout = $variables['omega_layout']; drupal_process_attached(array('#attached' => $layout['attached'])); omega_layout_load_theme_assets($layout['name']); $hook = str_replace('-', '_', $variables['omega_layout']['template']); return theme($hook, $variables); }
/** * Displays content in a dialog. * * @param \Symfony\Component\HttpFoundation\Request $request * The request object. * @param \Drupal\Core\Routing\RouteMatchInterface $route_match * The route match. * @param mixed $_content * A controller definition string, or a callable object/closure. * @param bool $modal * (optional) TRUE to render a modal dialog. Defaults to FALSE. * * @return \Drupal\Core\Ajax\AjaxResponse * AjaxResponse to return the content wrapper in a dialog. */ public function dialog(Request $request, RouteMatchInterface $route_match, $_content, $modal = FALSE) { $page_content = $this->getContentResult($request, $_content); // Allow controllers to return a HtmlPage or a Response object directly. if ($page_content instanceof HtmlPage) { $page_content = $page_content->getContent(); } if ($page_content instanceof Response) { $page_content = $page_content->getContent(); } // Most controllers return a render array, but some return a string. if (!is_array($page_content)) { $page_content = array('#markup' => $page_content); } $content = drupal_render($page_content); drupal_process_attached($page_content); $title = isset($page_content['#title']) ? $page_content['#title'] : $this->titleResolver->getTitle($request, $route_match->getRouteObject()); $response = new AjaxResponse(); // Fetch any modal options passed in from data-dialog-options. $options = $request->request->get('dialogOptions', array()); // Set modal flag and re-use the modal ID. if ($modal) { $options['modal'] = TRUE; $target = '#drupal-modal'; } else { // Generate the target wrapper for the dialog. if (isset($options['target'])) { // If the target was nominated in the incoming options, use that. $target = $options['target']; // Ensure the target includes the #. if (substr($target, 0, 1) != '#') { $target = '#' . $target; } // This shouldn't be passed on to jQuery.ui.dialog. unset($options['target']); } else { // Generate a target based on the route id. $route_name = $route_match->getRouteName(); $target = '#' . drupal_html_id("drupal-dialog-{$route_name}"); } } $response->addCommand(new OpenDialogCommand($target, $title, $content, $options)); return $response; }
/** * Renders a render array. * * @param array $elements * The elements to render. * * @return string * The rendered string output (typically HTML). */ protected function render(array &$elements) { $content = $this->container->get('renderer')->renderRoot($elements); drupal_process_attached($elements); $this->setRawContent($content); $this->verbose('<pre style="white-space: pre-wrap">' . SafeMarkup::checkPlain($content)); return $content; }
/** * Theme callback for rendering an Omega layout. */ function omega_omega_layout($variables) { drupal_process_attached(array('#attached' => $variables['omega_layout']['attached'])); // Clean up the theme hook suggestion so we don't end up in an infinite loop. unset($variables['theme_hook_suggestion'], $variables['theme_hook_suggestions']); return theme($variables['omega_layout']['template'], $variables); }
/** * Renders a page using a custom page theme hook and optional region content. * * Temporary shim to facilitate modernization progress for special front * controllers (install.php, update.php, authorize.php), maintenance mode, and * the exception handler. * * Do NOT use this method in your code. This method will be removed as soon * as architecturally possible. * * This is functionally very similar to DefaultHtmlFragmentRenderer::render() * but with the following important differences: * * - drupal_prepare_page() and hook_page_build() cannot be invoked on the * maintenance and install pages, since possibly enabled page layout/block * modules would replace the main page content with configured region * content. * - This function composes a complete page render array including a page * template theme suggestion (as opposed to the main page content only). * - The render cache and cache tags is skipped. * * @param array|string $main * A render array or string containing the main page content. * @param string $title * (optional) The page title. * @param string $theme * (optional) The theme hook to use for rendering the page. Defaults to * 'maintenance'. The given value will be appended with '_page' to compose * the #theme property for #type 'page' currently; e.g., 'maintenance' * becomes 'maintenance_page'. Ultimately this parameter will be converted * into a page template theme suggestion; i.e., 'page__$theme'. * @param array $regions * (optional) Additional region content to add to the page. The given array * is added to the page render array, so this parameter may also be used to * pass e.g. the #show_messages property for #type 'page'. * * @return string * The rendered HTML page. * * @internal */ public static function renderPage($main, $title = '', $theme = 'maintenance', array $regions = array()) { // Automatically convert the main page content into a render array. if (!is_array($main)) { $main = array('#markup' => $main); } $page = new HtmlPage('', array(), $title); $page_array = array('#type' => 'page', '#theme' => $theme . '_page', '#title' => $title, 'content' => array('system_main' => $main)); // Append region content. $page_array += $regions; // Add default properties. $page_array += element_info('page'); // hook_page_build() cannot be invoked on the maintenance and install pages, // because the application is in an unknown or special state. // In particular on the install page, invoking hook_page_build() directly // after e.g. Block module has been installed would *replace* the installer // output with the configured blocks of the installer theme (loaded from // default configuration of the installation profile). // Allow modules and themes to alter the page render array. // This allows e.g. themes to attach custom libraries. \Drupal::moduleHandler()->alter('page', $page_array); // @todo Move preparePage() before alter() above, so $page_array['#page'] is // available in hook_page_alter(), so that HTML attributes can be altered. $page = \Drupal::service('html_fragment_renderer')->preparePage($page, $page_array); $page->setBodyTop(drupal_render($page_array['page_top'])); $page->setBodyBottom(drupal_render($page_array['page_bottom'])); $page->setContent(drupal_render($page_array)); drupal_process_attached($page_array); if (isset($page_array['page_top'])) { drupal_process_attached($page_array['page_top']); } if (isset($page_array['page_bottom'])) { drupal_process_attached($page_array['page_bottom']); } return \Drupal::service('html_page_renderer')->render($page); }
/** * {@inheritdoc} */ public function processAttachments(AttachmentsInterface $response) { // @todo Convert to assertion once https://www.drupal.org/node/2408013 lands if (!$response instanceof HtmlResponse) { throw new \InvalidArgumentException('\\Drupal\\Core\\Render\\HtmlResponse instance expected.'); } $attached = $response->getAttachments(); // Get the placeholders from attached and then remove them. $placeholders = $attached['html_response_placeholders']; unset($attached['html_response_placeholders']); $variables = $this->processAssetLibraries($attached, $placeholders); // Handle all non-asset attachments. This populates drupal_get_html_head() // and drupal_get_http_header(). $all_attached = ['#attached' => $attached]; drupal_process_attached($all_attached); // Get HTML head elements - if present. if (isset($placeholders['head'])) { $variables['head'] = drupal_get_html_head(FALSE); } // Now replace the placeholders in the response content with the real data. $this->renderPlaceholders($response, $placeholders, $variables); // Finally set the headers on the response. $headers = drupal_get_http_header(); $this->setHeaders($response, $headers); return $response; }
/** * 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; }
/** * Wraps drupal_render(). * * @todo: Remove as part of https://drupal.org/node/2182149 */ protected function drupalRender(&$elements, $is_recursive_call = FALSE) { $output = drupal_render($elements, $is_recursive_call); drupal_process_attached($elements); return $output; }