/** * Tests onHandleException with a GET request. */ public function testHandleWithGetRequest() { $request = Request::create('/test', 'GET', array('name' => 'druplicon', 'pass' => '12345')); $this->kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { return new Response($request->getMethod() . ' ' . UrlHelper::buildQuery($request->query->all())); })); $event = new GetResponseForExceptionEvent($this->kernel, $request, 'foo', new \Exception('foo')); $this->exceptionListener->onKernelException($event); $response = $event->getResponse(); $this->assertEquals('GET name=druplicon&pass=12345 ', $response->getContent() . " " . UrlHelper::buildQuery($request->request->all())); }
/** * {@inheritdoc} */ public function get() { if (!isset($this->destination)) { $query = $this->requestStack->getCurrentRequest()->query; if (UrlHelper::isExternal($query->get('destination'))) { $this->destination = '/'; } elseif ($query->has('destination')) { $this->destination = $query->get('destination'); } else { $this->destination = $this->urlGenerator->generateFromRoute('<current>', [], ['query' => UrlHelper::buildQuery(UrlHelper::filterQueryParameters($query->all()))]); } } return $this->destination; }
/** * {@inheritdoc} */ public function createPlaceholder(array $element) { $placeholder_render_array = array_intersect_key($element, ['#lazy_builder' => TRUE, '#cache' => TRUE]); // Generate placeholder markup. Note that the only requirement is that this // is unique markup that isn't easily guessable. The #lazy_builder callback // and its arguments are put in the placeholder markup solely to simplify<<< // debugging. $callback = $placeholder_render_array['#lazy_builder'][0]; $arguments = UrlHelper::buildQuery($placeholder_render_array['#lazy_builder'][1]); $token = hash('crc32b', serialize($placeholder_render_array)); $placeholder_markup = '<drupal-render-placeholder callback="' . Html::escape($callback) . '" arguments="' . Html::escape($arguments) . '" token="' . Html::escape($token) . '"></drupal-render-placeholder>'; // Build the placeholder element to return. $placeholder_element = []; $placeholder_element['#markup'] = Markup::create($placeholder_markup); $placeholder_element['#attached']['placeholders'][$placeholder_markup] = $placeholder_render_array; return $placeholder_element; }
/** * Return the logout URL for the CAS server. * * @param \Symfony\Component\HttpFoundation\Request $request * The current request, to provide base url context. * * @return string * The fully constructed server logout URL. */ public function getServerLogoutUrl($request) { $base_url = $this->getServerBaseUrl() . 'logout'; if ($this->settings->get('redirection.logout_destination') != '') { $destination = $this->settings->get('redirection.logout_destination'); if ($destination == '<front>') { // If we have '<front>', resolve the path. $params['service'] = $this->urlGenerator->generate($destination, array(), TRUE); } elseif ($this->isExternal($destination)) { // If we have an absolute URL, use that. $params['service'] = $destination; } else { // This is a regular Drupal path. $params['service'] = $request->getSchemeAndHttpHost() . '/' . ltrim($destination, '/'); } return $base_url . '?' . UrlHelper::buildQuery($params); } else { return $base_url; } }
/** * Tests onHandleException with a GET request. */ public function testHandleWithGetRequest() { $this->setupStubAliasManager(); $request = Request::create('/test', 'GET', array('name' => 'druplicon', 'pass' => '12345')); $this->kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { return new Response($request->getMethod() . ' ' . UrlHelper::buildQuery($request->query->all())); })); $event = new GetResponseForExceptionEvent($this->kernel, $request, 'foo', new NotFoundHttpException('foo')); $this->customPageSubscriber->onException($event); $response = $event->getResponse(); $result = $response->getContent() . " " . UrlHelper::buildQuery($request->request->all()); $this->assertEquals('GET name=druplicon&pass=12345&destination=test&_exception_statuscode=404 ', $result); }
/** * {@inheritdoc} */ protected function buildLocalUrl($uri, array $options = [], $collect_bubbleable_metadata = FALSE) { $generated_url = $collect_bubbleable_metadata ? new GeneratedUrl() : NULL; $this->addOptionDefaults($options); $request = $this->requestStack->getCurrentRequest(); // Remove the base: scheme. // @todo Consider using a class constant for this in // https://www.drupal.org/node/2417459 $uri = substr($uri, 5); // Allow (outbound) path processing, if needed. A valid use case is the path // alias overview form: // @see \Drupal\path\Controller\PathController::adminOverview(). if (!empty($options['path_processing'])) { // Do not pass the request, since this is a special case and we do not // want to include e.g. the request language in the processing. $uri = $this->pathProcessor->processOutbound($uri, $options, NULL, $generated_url); } // Strip leading slashes from internal paths to prevent them becoming // external URLs without protocol. /example.com should not be turned into // //example.com. $uri = ltrim($uri, '/'); // Add any subdirectory where Drupal is installed. $current_base_path = $request->getBasePath() . '/'; if ($options['absolute']) { $current_base_url = $request->getSchemeAndHttpHost() . $current_base_path; if (isset($options['https'])) { if (!empty($options['https'])) { $base = str_replace('http://', 'https://', $current_base_url); $options['absolute'] = TRUE; } else { $base = str_replace('https://', 'http://', $current_base_url); $options['absolute'] = TRUE; } } else { $base = $current_base_url; } if ($collect_bubbleable_metadata) { $generated_url->addCacheContexts(['url.site']); } } else { $base = $current_base_path; } $prefix = empty($uri) ? rtrim($options['prefix'], '/') : $options['prefix']; $uri = str_replace('%2F', '/', rawurlencode($prefix . $uri)); $query = $options['query'] ? '?' . UrlHelper::buildQuery($options['query']) : ''; $url = $base . $options['script'] . $uri . $query . $options['fragment']; return $collect_bubbleable_metadata ? $generated_url->setGeneratedUrl($url) : $url; }
/** * Tests onHandleException with a GET request. */ public function testHandleWithGetRequest() { $request = Request::create('/test', 'GET', array('name' => 'druplicon', 'pass' => '12345')); $request->attributes->set(AccessAwareRouterInterface::ACCESS_RESULT, AccessResult::forbidden()->addCacheTags(['druplicon'])); $this->kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { return new Response($request->getMethod() . ' ' . UrlHelper::buildQuery($request->query->all())); })); $event = new GetResponseForExceptionEvent($this->kernel, $request, 'foo', new NotFoundHttpException('foo')); $this->customPageSubscriber->onException($event); $response = $event->getResponse(); $result = $response->getContent() . " " . UrlHelper::buildQuery($request->request->all()); $this->assertEquals('GET name=druplicon&pass=12345&destination=test&_exception_statuscode=404 ', $result); $this->assertEquals(AccessResult::forbidden()->addCacheTags(['druplicon', 'foo', 'bar']), $request->attributes->get(AccessAwareRouterInterface::ACCESS_RESULT)); }
/** * Render this field as a link, with the info from a fieldset set by * the user. */ protected function renderAsLink($alter, $text, $tokens) { $options = array('absolute' => !empty($alter['absolute']) ? TRUE : FALSE, 'alias' => FALSE, 'entity' => NULL, 'entity_type' => NULL, 'fragment' => NULL, 'language' => NULL, 'query' => []); $alter += ['path' => NULL]; $path = $alter['path']; if (empty($alter['url'])) { if (!parse_url($path, PHP_URL_SCHEME)) { // @todo Views should expect and store a leading /. See // https://www.drupal.org/node/2423913. $alter['url'] = CoreUrl::fromUserInput('/' . ltrim($path, '/')); } else { $alter['url'] = CoreUrl::fromUri($path); } } $options = $alter['url']->getOptions() + $options; $path = $alter['url']->setOptions($options)->toUriString(); // strip_tags() removes <front>, so check whether its different to front. if ($path != 'route:<front>') { // Unescape Twig delimiters that may have been escaped by the // Url::toUriString() call above, because we support twig tokens in // rewrite settings of views fields. // In that case the original path looks like // internal:/admin/content/files/usage/{{ fid }}, which will be escaped by // the toUriString() call above. $path = preg_replace(['/(\\%7B){2}(\\%20)*/', '/(\\%20)*(\\%7D){2}/'], ['{{', '}}'], $path); // Use strip tags as there should never be HTML in the path. // However, we need to preserve special characters like " that // were removed by SafeMarkup::checkPlain(). $path = strip_tags(Html::decodeEntities($this->viewsTokenReplace($path, $tokens))); if (!empty($alter['path_case']) && $alter['path_case'] != 'none' && !$alter['url']->isRouted()) { $path = str_replace($alter['path'], $this->caseTransform($alter['path'], $this->options['alter']['path_case']), $path); } if (!empty($alter['replace_spaces'])) { $path = str_replace(' ', '-', $path); } } // Parse the URL and move any query and fragment parameters out of the path. $url = UrlHelper::parse($path); // Seriously malformed URLs may return FALSE or empty arrays. if (empty($url)) { return $text; } // If the path is empty do not build a link around the given text and return // it as is. // http://www.example.com URLs will not have a $url['path'], so check host as well. if (empty($url['path']) && empty($url['host']) && empty($url['fragment']) && empty($url['url'])) { return $text; } // If we get to here we have a path from the url parsing. So assign that to // $path now so we don't get query strings or fragments in the path. $path = $url['path']; // If no scheme is provided in the $path, assign the default 'http://'. // This allows a url of 'www.example.com' to be converted to 'http://www.example.com'. // Only do this on for external URLs. if ($alter['external']) { if (!isset($url['scheme'])) { // There is no scheme, add the default 'http://' to the $path. // Use the original $alter['path'] instead of the parsed version. $path = "http://" . $alter['path']; // Reset the $url array to include the new scheme. $url = UrlHelper::parse($path); } } if (isset($url['query'])) { // Remove query parameters that were assigned a query string replacement // token for which there is no value available. foreach ($url['query'] as $param => $val) { if ($val == '%' . $param) { unset($url['query'][$param]); } // Replace any empty query params from URL parsing with NULL. So the // query will get built correctly with only the param key. // @see \Drupal\Component\Utility\UrlHelper::buildQuery(). if ($val === '') { $url['query'][$param] = NULL; } } $options['query'] = $url['query']; } if (isset($url['fragment'])) { $path = strtr($path, array('#' . $url['fragment'] => '')); // If the path is empty we want to have a fragment for the current site. if ($path == '') { $options['external'] = TRUE; } $options['fragment'] = $url['fragment']; } $alt = $this->viewsTokenReplace($alter['alt'], $tokens); // Set the title attribute of the link only if it improves accessibility if ($alt && $alt != $text) { $options['attributes']['title'] = Html::decodeEntities($alt); } $class = $this->viewsTokenReplace($alter['link_class'], $tokens); if ($class) { $options['attributes']['class'] = array($class); } if (!empty($alter['rel']) && ($rel = $this->viewsTokenReplace($alter['rel'], $tokens))) { $options['attributes']['rel'] = $rel; } // Not sure if this SafeMarkup::checkPlain() is needed here? $target = SafeMarkup::checkPlain(trim($this->viewsTokenReplace($alter['target'], $tokens))); if (!empty($target)) { $options['attributes']['target'] = $target; } // Allow the addition of arbitrary attributes to links. Additional attributes // currently can only be altered in preprocessors and not within the UI. if (isset($alter['link_attributes']) && is_array($alter['link_attributes'])) { foreach ($alter['link_attributes'] as $key => $attribute) { if (!isset($options['attributes'][$key])) { $options['attributes'][$key] = $this->viewsTokenReplace($attribute, $tokens); } } } // If the query and fragment were programmatically assigned overwrite any // parsed values. if (isset($alter['query'])) { // Convert the query to a string, perform token replacement, and then // convert back to an array form for _l(). $options['query'] = UrlHelper::buildQuery($alter['query']); $options['query'] = $this->viewsTokenReplace($options['query'], $tokens); $query = array(); parse_str($options['query'], $query); $options['query'] = $query; } if (isset($alter['alias'])) { // Alias is a boolean field, so no token. $options['alias'] = $alter['alias']; } if (isset($alter['fragment'])) { $options['fragment'] = $this->viewsTokenReplace($alter['fragment'], $tokens); } if (isset($alter['language'])) { $options['language'] = $alter['language']; } // If the url came from entity_uri(), pass along the required options. if (isset($alter['entity'])) { $options['entity'] = $alter['entity']; } if (isset($alter['entity_type'])) { $options['entity_type'] = $alter['entity_type']; } // The path has been heavily processed above, so it should be used as-is. $final_url = CoreUrl::fromUri($path, $options); // Build the link based on our altered Url object, adding on the optional // prefix and suffix $value = ''; if (!empty($alter['prefix'])) { $value .= Xss::filterAdmin($this->viewsTokenReplace($alter['prefix'], $tokens)); } $value .= $this->linkGenerator()->generate($text, $final_url); if (!empty($alter['suffix'])) { $value .= Xss::filterAdmin($this->viewsTokenReplace($alter['suffix'], $tokens)); } return $value; }
/** * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); // We always show the internal path here. /** @var \Drupal\Core\Url $url */ $url = $this->getEntity()->getUrlObject(); if ($url->isExternal()) { $default_value = $url->toString(); } elseif ($url->getRouteName() == '<front>') { // The default route for new entities is <front>, but we just want an // empty form field. $default_value = $this->getEntity()->isNew() ? '' : '<front>'; } else { // @todo Url::getInternalPath() calls UrlGenerator::getPathFromRoute() // which need a replacement since it is deprecated. // https://www.drupal.org/node/2307061 $default_value = $url->getInternalPath(); // @todo Add a helper method to Url to render just the query string and // fragment. https://www.drupal.org/node/2305013 $options = $url->getOptions(); if (isset($options['query'])) { $default_value .= $options['query'] ? '?' . UrlHelper::buildQuery($options['query']) : ''; } if (isset($options['fragment']) && $options['fragment'] !== '') { $default_value .= '#' . $options['fragment']; } } $form['url'] = array('#title' => $this->t('Link path'), '#type' => 'textfield', '#description' => $this->t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')), '#default_value' => $default_value, '#required' => TRUE, '#weight' => -2); $language_configuration = $this->moduleHandler->invoke('language', 'get_default_configuration', array('menu_link_content', 'menu_link_content')); if ($this->entity->isNew()) { $default_language = isset($language_configuration['langcode']) ? $language_configuration['langcode'] : $this->languageManager->getDefaultLanguage()->getId(); } else { $default_language = $this->entity->getUntranslated()->language()->getId(); } $form['langcode'] = array('#title' => t('Language'), '#type' => 'language_select', '#default_value' => $default_language, '#languages' => Language::STATE_ALL, '#access' => !empty($language_configuration['language_show'])); $form['enabled'] = array('#type' => 'checkbox', '#title' => $this->t('Enable menu link'), '#description' => $this->t('Menu links that are not enabled will not be listed in any menu.'), '#default_value' => !$this->entity->isHidden(), '#weight' => 0); $default = $this->entity->getMenuName() . ':' . $this->entity->getParentId(); $form['menu_parent'] = $this->menuParentSelector->parentSelectElement($default, $this->entity->getPluginId()); $form['menu_parent']['#weight'] = 10; $form['menu_parent']['#title'] = $this->t('Parent link'); $form['menu_parent']['#description'] = $this->t('The maximum depth for a link and all its children is fixed. Some menu links may not be available as parents if selecting them would exceed this limit.'); $form['menu_parent']['#attributes']['class'][] = 'menu-title-select'; return $form; }
/** * Tests query building. * * @dataProvider providerTestBuildQuery * @covers ::buildQuery * * @param array $query * The array of query parameters. * @param string $expected * The expected query string. * @param string $message * The assertion message. */ public function testBuildQuery($query, $expected, $message) { $this->assertEquals(UrlHelper::buildQuery($query), $expected, $message); }
/** * {@inheritdoc} */ public function generateFromPath($path = NULL, $options = array()) { $request = $this->requestStack->getCurrentRequest(); $current_base_path = $request->getBasePath() . '/'; $current_base_url = $request->getSchemeAndHttpHost() . $current_base_path; $current_script_path = ''; $base_path_with_script = $request->getBaseUrl(); if (!empty($base_path_with_script)) { $script_name = $request->getScriptName(); if (strpos($base_path_with_script, $script_name) !== FALSE) { $current_script_path = ltrim(substr($script_name, strlen($current_base_path)), '/') . '/'; } } // Merge in defaults. $options += array('fragment' => '', 'query' => array(), 'absolute' => FALSE, 'prefix' => ''); if (!isset($options['external'])) { // Return an external link if $path contains an allowed absolute URL. Only // call the slow // \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() if $path // contains a ':' before any / ? or #. Note: we could use // \Drupal\Component\Utility\UrlHelper::isExternal($path) here, but that // would require another function call, and performance inside _url() is // critical. $colonpos = strpos($path, ':'); $options['external'] = $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && UrlHelper::stripDangerousProtocols($path) == $path; } if (isset($options['fragment']) && $options['fragment'] !== '') { $options['fragment'] = '#' . $options['fragment']; } if ($options['external']) { // Split off the fragment. if (strpos($path, '#') !== FALSE) { list($path, $old_fragment) = explode('#', $path, 2); // If $options contains no fragment, take it over from the path. if (isset($old_fragment) && !$options['fragment']) { $options['fragment'] = '#' . $old_fragment; } } // Append the query. if ($options['query']) { $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($options['query']); } if (isset($options['https']) && $this->mixedModeSessions) { if ($options['https'] === TRUE) { $path = str_replace('http://', 'https://', $path); } elseif ($options['https'] === FALSE) { $path = str_replace('https://', 'http://', $path); } } // Reassemble. return $path . $options['fragment']; } else { $path = ltrim($this->processPath($path, $options), '/'); } if (!isset($options['script'])) { $options['script'] = $current_script_path; } // The base_url might be rewritten from the language rewrite in domain mode. if (!isset($options['base_url'])) { if (isset($options['https']) && $this->mixedModeSessions) { if ($options['https'] === TRUE) { $options['base_url'] = str_replace('http://', 'https://', $current_base_url); $options['absolute'] = TRUE; } elseif ($options['https'] === FALSE) { $options['base_url'] = str_replace('https://', 'http://', $current_base_url); $options['absolute'] = TRUE; } } else { $options['base_url'] = $current_base_url; } } elseif (rtrim($options['base_url'], '/') == $options['base_url']) { $options['base_url'] .= '/'; } $base = $options['absolute'] ? $options['base_url'] : $current_base_path; $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix']; $path = str_replace('%2F', '/', rawurlencode($prefix . $path)); $query = $options['query'] ? '?' . UrlHelper::buildQuery($options['query']) : ''; return $base . $options['script'] . $path . $query . $options['fragment']; }
/** * {@inheritdoc} */ protected function buildLocalUrl($uri, array $options = []) { $this->addOptionDefaults($options); $request = $this->requestStack->getCurrentRequest(); // Remove the base:// scheme. $uri = substr($uri, 7); // Add any subdirectory where Drupal is installed. $current_base_path = $request->getBasePath() . '/'; if ($options['absolute']) { $current_base_url = $request->getSchemeAndHttpHost() . $current_base_path; if (isset($options['https'])) { if (!empty($options['https'])) { $base = str_replace('http://', 'https://', $current_base_url); $options['absolute'] = TRUE; } else { $base = str_replace('https://', 'http://', $current_base_url); $options['absolute'] = TRUE; } } else { $base = $current_base_url; } } else { $base = $current_base_path; } $prefix = empty($uri) ? rtrim($options['prefix'], '/') : $options['prefix']; $uri = str_replace('%2F', '/', rawurlencode($prefix . $uri)); $query = $options['query'] ? '?' . UrlHelper::buildQuery($options['query']) : ''; return $base . $options['script'] . $uri . $query . $options['fragment']; }
/** * Loads and renders a view via AJAX. * * @param \Symfony\Component\HttpFoundation\Request $request * The current request object. * * @return \Drupal\views\Ajax\ViewAjaxResponse * The view response as ajax response. * * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException * Thrown when the view was not found. */ public function ajaxView(Request $request) { $name = $request->request->get('view_name'); $display_id = $request->request->get('view_display_id'); if (isset($name) && isset($display_id)) { $args = $request->request->get('view_args'); $args = isset($args) && $args !== '' ? explode('/', $args) : array(); // Arguments can be empty, make sure they are passed on as NULL so that // argument validation is not triggered. $args = array_map(function ($arg) { return $arg == '' ? NULL : $arg; }, $args); $path = $request->request->get('view_path'); $dom_id = $request->request->get('view_dom_id'); $dom_id = isset($dom_id) ? preg_replace('/[^a-zA-Z0-9_-]+/', '-', $dom_id) : NULL; $pager_element = $request->request->get('pager_element'); $pager_element = isset($pager_element) ? intval($pager_element) : NULL; $response = new ViewAjaxResponse(); // Remove all of this stuff from the query of the request so it doesn't // end up in pagers and tablesort URLs. foreach (array('view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', 'ajax_html_ids') as $key) { $request->query->remove($key); $request->request->remove($key); } // Load the view. if (!($entity = $this->storage->load($name))) { throw new NotFoundHttpException(); } $view = $this->executableFactory->get($entity); if ($view && $view->access($display_id)) { $response->setView($view); // Fix the current path for paging. if (!empty($path)) { $this->currentPath->setPath('/' . $path, $request); } // Add all POST data, because AJAX is always a post and many things, // such as tablesorts, exposed filters and paging assume GET. $request_all = $request->request->all(); $query_all = $request->query->all(); $request->query->replace($request_all + $query_all); // Overwrite the destination. // @see the redirect.destination service. $origin_destination = $path; $query = UrlHelper::buildQuery($request->query->all()); if ($query != '') { $origin_destination .= '?' . $query; } $this->redirectDestination->set($origin_destination); // Override the display's pager_element with the one actually used. if (isset($pager_element)) { $response->addCommand(new ScrollTopCommand(".view-dom-id-{$dom_id}")); $view->displayHandlers->get($display_id)->setOption('pager_element', $pager_element); } // Reuse the same DOM id so it matches that in drupalSettings. $view->dom_id = $dom_id; if ($preview = $view->preview($display_id, $args)) { $response->addCommand(new ReplaceCommand(".view-dom-id-{$dom_id}", $this->renderer->render($preview))); $response->setAttachments($preview['#attached']); } return $response; } else { throw new AccessDeniedHttpException(); } } else { throw new NotFoundHttpException(); } }
/** * {@inheritdoc} */ public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) { // If appropriate, process outbound to add a query parameter to the url and // remove the language option, so that url negotiator does not rewrite the // url. // First, check if processing conditions are met. if (!($request && !empty($options['route']) && $this->hasLowerLanguageNegotiationWeight() && $this->meetsContentEntityRoutesCondition($options['route'], $request))) { return $path; } if (isset($options['language']) || ($langcode = $this->getLangcode($request))) { // If the language option is set, unset it, so that the url language // negotiator does not rewrite the url. if (isset($options['language'])) { $langcode = $options['language']->getId(); unset($options['language']); } if (isset($options['query']) && is_string($options['query'])) { $query = []; parse_str($options['query'], $query); $options['query'] = $query; } else { $options['query'] = []; } if (!isset($options['query'][static::QUERY_PARAMETER])) { $query_addon = [static::QUERY_PARAMETER => $langcode]; $options['query'] += $query_addon; // @todo Remove this once https://www.drupal.org/node/2507005 lands. $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($query_addon); } if ($bubbleable_metadata) { // Cached URLs that have been processed by this outbound path // processor must be: $bubbleable_metadata->addCacheContexts(['url.query_args:' . static::QUERY_PARAMETER]); } } return $path; }
/** * Creates a BigPipe JS placeholder. * * @param string $original_placeholder * The original placeholder. * @param array $placeholder_render_array * The render array for a placeholder. * * @return array * The resulting BigPipe JS placeholder render array. */ protected static function createBigPipeJsPlaceholder($original_placeholder, array $placeholder_render_array) { // Generate a BigPipe selector (to be used by BigPipe's JavaScript). // @see \Drupal\Core\Render\PlaceholderGenerator::createPlaceholder() if (isset($placeholder_render_array['#lazy_builder'])) { $callback = $placeholder_render_array['#lazy_builder'][0]; $arguments = $placeholder_render_array['#lazy_builder'][1]; $token = hash('crc32b', serialize($placeholder_render_array)); $big_pipe_js_selector = UrlHelper::buildQuery(['callback' => $callback, 'args' => $arguments, 'token' => $token]); } else { $big_pipe_js_selector = Html::getId($original_placeholder); } return ['#markup' => '<div data-big-pipe-selector="' . Html::escape($big_pipe_js_selector) . '"></div>', '#cache' => ['max-age' => 0, 'contexts' => ['session.exists']], '#attached' => ['library' => ['big_pipe/big_pipe'], 'drupalSettings' => ['bigPipePlaceholders' => [$big_pipe_js_selector => TRUE]], 'big_pipe_placeholders' => [Html::escape($big_pipe_js_selector) => $placeholder_render_array]]]; }
/** * Builds the $form['#action']. * * @return string * The URL to be used as the $form['#action']. */ protected function buildFormAction() { // @todo Use <current> instead of the master request in // https://www.drupal.org/node/2505339. $request_uri = $this->requestStack->getMasterRequest()->getRequestUri(); // @todo Remove this parsing once these are removed from the request in // https://www.drupal.org/node/2504709. $parsed = UrlHelper::parse($request_uri); unset($parsed['query'][static::AJAX_FORM_REQUEST], $parsed['query'][MainContentViewSubscriber::WRAPPER_FORMAT]); return $parsed['path'] . ($parsed['query'] ? '?' . UrlHelper::buildQuery($parsed['query']) : ''); }
/** * Render this field as a link, with the info from a fieldset set by * the user. */ protected function renderAsLink($alter, $text, $tokens) { $options = array( 'absolute' => !empty($alter['absolute']) ? TRUE : FALSE, 'alias' => FALSE, 'entity' => NULL, 'entity_type' => NULL, 'fragment' => NULL, 'language' => NULL, 'query' => [], ); $alter += [ 'path' => NULL ]; $path = $alter['path']; // strip_tags() and viewsTokenReplace remove <front>, so check whether it's // different to front. if ($path != '<front>') { // Use strip_tags as there should never be HTML in the path. // However, we need to preserve special characters like " that were // removed by SafeMarkup::checkPlain(). $path = Html::decodeEntities($this->viewsTokenReplace($alter['path'], $tokens)); // Tokens might contain <front>, so check for <front> again. if ($path != '<front>') { $path = strip_tags($path); } // Tokens might have resolved URL's, as is the case for tokens provided by // Link fields, so all internal paths will be prefixed by base_path(). For // proper further handling reset this to internal:/. if (strpos($path, base_path()) === 0) { $path = 'internal:/' . substr($path, strlen(base_path())); } // If we have no $path and no $alter['url'], we have nothing to work with, // so we just return the text. if (empty($path) && empty($alter['url'])) { return $text; } // If no scheme is provided in the $path, assign the default 'http://'. // This allows a url of 'www.example.com' to be converted to // 'http://www.example.com'. // Only do this when flag for external has been set, $path doesn't contain // a scheme and $path doesn't have a leading /. if ($alter['external'] && !parse_url($path, PHP_URL_SCHEME) && strpos($path, '/') !== 0) { // There is no scheme, add the default 'http://' to the $path. $path = "http://" . $path; } } if (empty($alter['url'])) { if (!parse_url($path, PHP_URL_SCHEME)) { // @todo Views should expect and store a leading /. See // https://www.drupal.org/node/2423913. $alter['url'] = CoreUrl::fromUserInput('/' . ltrim($path, '/')); } else { $alter['url'] = CoreUrl::fromUri($path); } } $options = $alter['url']->getOptions() + $options; $path = $alter['url']->setOptions($options)->toUriString(); if (!empty($alter['path_case']) && $alter['path_case'] != 'none' && !$alter['url']->isRouted()) { $path = str_replace($alter['path'], $this->caseTransform($alter['path'], $this->options['alter']['path_case']), $path); } if (!empty($alter['replace_spaces'])) { $path = str_replace(' ', '-', $path); } // Parse the URL and move any query and fragment parameters out of the path. $url = UrlHelper::parse($path); // Seriously malformed URLs may return FALSE or empty arrays. if (empty($url)) { return $text; } // If the path is empty do not build a link around the given text and return // it as is. // http://www.example.com URLs will not have a $url['path'], so check host as well. if (empty($url['path']) && empty($url['host']) && empty($url['fragment']) && empty($url['url'])) { return $text; } // If we get to here we have a path from the url parsing. So assign that to // $path now so we don't get query strings or fragments in the path. $path = $url['path']; if (isset($url['query'])) { // Remove query parameters that were assigned a query string replacement // token for which there is no value available. foreach ($url['query'] as $param => $val) { if ($val == '%' . $param) { unset($url['query'][$param]); } // Replace any empty query params from URL parsing with NULL. So the // query will get built correctly with only the param key. // @see \Drupal\Component\Utility\UrlHelper::buildQuery(). if ($val === '') { $url['query'][$param] = NULL; } } $options['query'] = $url['query']; } if (isset($url['fragment'])) { $path = strtr($path, array('#' . $url['fragment'] => '')); // If the path is empty we want to have a fragment for the current site. if ($path == '') { $options['external'] = TRUE; } $options['fragment'] = $url['fragment']; } $alt = $this->viewsTokenReplace($alter['alt'], $tokens); // Set the title attribute of the link only if it improves accessibility if ($alt && $alt != $text) { $options['attributes']['title'] = Html::decodeEntities($alt); } $class = $this->viewsTokenReplace($alter['link_class'], $tokens); if ($class) { $options['attributes']['class'] = array($class); } if (!empty($alter['rel']) && $rel = $this->viewsTokenReplace($alter['rel'], $tokens)) { $options['attributes']['rel'] = $rel; } $target = trim($this->viewsTokenReplace($alter['target'], $tokens)); if (!empty($target)) { $options['attributes']['target'] = $target; } // Allow the addition of arbitrary attributes to links. Additional attributes // currently can only be altered in preprocessors and not within the UI. if (isset($alter['link_attributes']) && is_array($alter['link_attributes'])) { foreach ($alter['link_attributes'] as $key => $attribute) { if (!isset($options['attributes'][$key])) { $options['attributes'][$key] = $this->viewsTokenReplace($attribute, $tokens); } } } // If the query and fragment were programmatically assigned overwrite any // parsed values. if (isset($alter['query'])) { // Convert the query to a string, perform token replacement, and then // convert back to an array form for // \Drupal\Core\Utility\LinkGeneratorInterface::generate(). $options['query'] = UrlHelper::buildQuery($alter['query']); $options['query'] = $this->viewsTokenReplace($options['query'], $tokens); $query = array(); parse_str($options['query'], $query); $options['query'] = $query; } if (isset($alter['alias'])) { // Alias is a boolean field, so no token. $options['alias'] = $alter['alias']; } if (isset($alter['fragment'])) { $options['fragment'] = $this->viewsTokenReplace($alter['fragment'], $tokens); } if (isset($alter['language'])) { $options['language'] = $alter['language']; } // If the url came from entity_uri(), pass along the required options. if (isset($alter['entity'])) { $options['entity'] = $alter['entity']; } if (isset($alter['entity_type'])) { $options['entity_type'] = $alter['entity_type']; } // The path has been heavily processed above, so it should be used as-is. $final_url = CoreUrl::fromUri($path, $options); // Build the link based on our altered Url object, adding on the optional // prefix and suffix $render = [ '#type' => 'link', '#title' => $text, '#url' => $final_url, ]; if (!empty($alter['prefix'])) { $render['#prefix'] = $this->viewsTokenReplace($alter['prefix'], $tokens); } if (!empty($alter['suffix'])) { $render['#suffix'] = $this->viewsTokenReplace($alter['suffix'], $tokens); } return $this->getRenderer()->render($render); }
/** * Generates a BigPipe placeholder ID. * * @param string $original_placeholder * The original placeholder. * @param array $placeholder_render_array * The render array for a placeholder. * * @return string * The generated BigPipe placeholder ID. */ protected static function generateBigPipePlaceholderId($original_placeholder, array $placeholder_render_array) { // Generate a BigPipe placeholder ID (to be used by BigPipe's JavaScript). // @see \Drupal\Core\Render\PlaceholderGenerator::createPlaceholder() if (isset($placeholder_render_array['#lazy_builder'])) { $callback = $placeholder_render_array['#lazy_builder'][0]; $arguments = $placeholder_render_array['#lazy_builder'][1]; $token = hash('crc32b', serialize($placeholder_render_array)); return UrlHelper::buildQuery(['callback' => $callback, 'args' => $arguments, 'token' => $token]); } else { return Html::getId($original_placeholder); } }
/** * Sets the redirect destination URL data. * * @param string $url * The base url of the redirect destination. * @param array $query * Query arguments. * @param array $options * The source url options. */ public function setRedirect($url, array $query = array(), array $options = array()) { $uri = $url . ($query ? '?' . UrlHelper::buildQuery($query) : ''); $this->redirect_redirect->set(0, ['uri' => 'internal:/' . ltrim($uri, '/'), 'options' => $options]); }
/** * Turns this element into a placeholder. * * Placeholdering allows us to avoid "poor cacheability contamination": this * maps the current render array to one that only has #markup and #attached, * and #attached contains a placeholder with this element's prior cacheability * metadata. In other words: this placeholder is perfectly cacheable, the * placeholder replacement logic effectively cordons off poor cacheability. * * @param array $element * The render array to create a placeholder for. * * @return array * Render array with placeholder markup and the attached placeholder * replacement metadata. */ protected function createPlaceholder(array $element) { $placeholder_render_array = array_intersect_key($element, ['#lazy_builder' => TRUE, '#cache' => TRUE]); // Generate placeholder markup. Note that the only requirement is that this // is unique markup that isn't easily guessable. The #lazy_builder callback // and its arguments are put in the placeholder markup solely to simplify // debugging. $attributes = new Attribute(); $attributes['callback'] = $placeholder_render_array['#lazy_builder'][0]; $attributes['arguments'] = UrlHelper::buildQuery($placeholder_render_array['#lazy_builder'][1]); $attributes['token'] = hash('sha1', serialize($placeholder_render_array)); $placeholder_markup = SafeMarkup::format('<drupal-render-placeholder@attributes></drupal-render-placeholder>', ['@attributes' => $attributes]); // Build the placeholder element to return. $placeholder_element = []; $placeholder_element['#markup'] = $placeholder_markup; $placeholder_element['#attached']['placeholders'][$placeholder_markup] = $placeholder_render_array; return $placeholder_element; }
/** * Builds the $form['#action']. * * @return string * The URL to be used as the $form['#action']. */ protected function buildFormAction() { // @todo Use <current> instead of the master request in // https://www.drupal.org/node/2505339. $request = $this->requestStack->getMasterRequest(); $request_uri = $request->getRequestUri(); // Prevent cross site requests via the Form API by using an absolute URL // when the request uri starts with multiple slashes.. if (strpos($request_uri, '//') === 0) { $request_uri = $request->getUri(); } // @todo Remove this parsing once these are removed from the request in // https://www.drupal.org/node/2504709. $parsed = UrlHelper::parse($request_uri); unset($parsed['query'][static::AJAX_FORM_REQUEST], $parsed['query'][MainContentViewSubscriber::WRAPPER_FORMAT]); return $parsed['path'] . ($parsed['query'] ? '?' . UrlHelper::buildQuery($parsed['query']) : ''); }
/** * {@inheritdoc} */ public function generateFromPath($path = NULL, $options = array(), $collect_cacheability_metadata = FALSE) { $generated_url = $collect_cacheability_metadata ? new GeneratedUrl() : NULL; $request = $this->requestStack->getCurrentRequest(); $current_base_path = $request->getBasePath() . '/'; $current_base_url = $request->getSchemeAndHttpHost() . $current_base_path; $current_script_path = ''; $base_path_with_script = $request->getBaseUrl(); if (!empty($base_path_with_script)) { $script_name = $request->getScriptName(); if (strpos($base_path_with_script, $script_name) !== FALSE) { $current_script_path = ltrim(substr($script_name, strlen($current_base_path)), '/') . '/'; } } // Merge in defaults. $options += array('fragment' => '', 'query' => array(), 'absolute' => FALSE, 'prefix' => ''); // A duplicate of the code from // \Drupal\Component\Utility\UrlHelper::isExternal() to avoid needing // another function call, since performance inside url() is critical. if (!isset($options['external'])) { $colonpos = strpos($path, ':'); // Avoid calling drupal_strip_dangerous_protocols() if there is any slash // (/), hash (#) or question_mark (?) before the colon (:) occurrence - // if any - as this would clearly mean it is not a URL. If the path starts // with 2 slashes then it is always considered an external URL without an // explicit protocol part. $options['external'] = strpos($path, '//') === 0 || $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && UrlHelper::stripDangerousProtocols($path) == $path; } if (isset($options['fragment']) && $options['fragment'] !== '') { $options['fragment'] = '#' . $options['fragment']; } if ($options['external']) { // Split off the fragment. if (strpos($path, '#') !== FALSE) { list($path, $old_fragment) = explode('#', $path, 2); // If $options contains no fragment, take it over from the path. if (isset($old_fragment) && !$options['fragment']) { $options['fragment'] = '#' . $old_fragment; } } // Append the query. if ($options['query']) { $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($options['query']); } if (isset($options['https'])) { if ($options['https'] === TRUE) { $path = str_replace('http://', 'https://', $path); } elseif ($options['https'] === FALSE) { $path = str_replace('https://', 'http://', $path); } } // Reassemble. $url = $path . $options['fragment']; return $collect_cacheability_metadata ? $generated_url->setGeneratedUrl($url) : $url; } else { $path = ltrim($this->processPath($path, $options, $generated_url), '/'); } if (!isset($options['script'])) { $options['script'] = $current_script_path; } // The base_url might be rewritten from the language rewrite in domain mode. if (!isset($options['base_url'])) { if (isset($options['https'])) { if ($options['https'] === TRUE) { $options['base_url'] = str_replace('http://', 'https://', $current_base_url); $options['absolute'] = TRUE; } elseif ($options['https'] === FALSE) { $options['base_url'] = str_replace('https://', 'http://', $current_base_url); $options['absolute'] = TRUE; } } else { $options['base_url'] = $current_base_url; } } elseif (rtrim($options['base_url'], '/') == $options['base_url']) { $options['base_url'] .= '/'; } $base = $options['absolute'] ? $options['base_url'] : $current_base_path; $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix']; if ($options['absolute'] && $collect_cacheability_metadata) { $generated_url->addCacheContexts(['url.site']); } $path = str_replace('%2F', '/', rawurlencode($prefix . $path)); $query = $options['query'] ? '?' . UrlHelper::buildQuery($options['query']) : ''; $url = $base . $options['script'] . $path . $query . $options['fragment']; return $collect_cacheability_metadata ? $generated_url->setGeneratedUrl($url) : $url; }
/** * Proxy authenticates to a target service. * * Returns cookies from the proxied service in a * CookieJar object for use when later accessing resources. * * @param string $target_service * The service to be proxied. * * @return \GuzzleHttp\Cookie\CookieJar * A CookieJar object (array storage) containing cookies from the * proxied service. * * @throws CasProxyException */ public function proxyAuthenticate($target_service) { // Check to see if we have proxied this application already. if (isset($_SESSION['cas_proxy_helper'][$target_service])) { $cookies = array(); foreach ($_SESSION['cas_proxy_helper'][$target_service] as $cookie) { $cookies[$cookie['Name']] = $cookie['Value']; } $domain = $cookie['Domain']; $jar = CookieJar::fromArray($cookies, $domain); $this->casHelper->log("{$target_service} already proxied. Returning information from session."); return $jar; } if (!($this->casHelper->isProxy() && isset($_SESSION['cas_pgt']))) { // We can't perform proxy authentication in this state. throw new CasProxyException("Session state not sufficient for proxying."); } // Make request to CAS server to retrieve a proxy ticket for this service. $cas_url = $this->getServerProxyURL($target_service); try { $this->casHelper->log("Retrieving proxy ticket from: {$cas_url}"); $response = $this->httpClient->get($cas_url); $this->casHelper->log("Received: " . htmlspecialchars($response->getBody()->__toString())); } catch (ClientException $e) { throw new CasProxyException($e->getMessage()); } $proxy_ticket = $this->parseProxyTicket($response->getBody()); $this->casHelper->log("Extracted proxy ticket: {$proxy_ticket}"); // Make request to target service with our new proxy ticket. // The target service will validate this ticket against the CAS server // and set a cookie that grants authentication for further resource calls. $params['ticket'] = $proxy_ticket; $service_url = $target_service . "?" . UrlHelper::buildQuery($params); $cookie_jar = new CookieJar(); try { $this->casHelper->log("Contacting service: {$service_url}"); $this->httpClient->get($service_url, ['cookies' => $cookie_jar]); } catch (ClientException $e) { throw new CasProxyException($e->getMessage()); } // Store in session storage for later reuse. $_SESSION['cas_proxy_helper'][$target_service] = $cookie_jar->toArray(); $this->casHelper->log("Stored cookies from {$target_service} in session."); return $cookie_jar; }
/** * {@inheritdoc} */ public function buildUrl($path, $clean_urls = NULL) { $uri = $this->buildUri($path); // The token query is added even if the // 'image.settings:allow_insecure_derivatives' configuration is TRUE, so // that the emitted links remain valid if it is changed back to the default // FALSE. However, sites which need to prevent the token query from being // emitted at all can additionally set the // 'image.settings:suppress_itok_output' configuration to TRUE to achieve // that (if both are set, the security token will neither be emitted in the // image derivative URL nor checked for in // \Drupal\image\ImageStyleInterface::deliver()). $token_query = array(); if (!\Drupal::config('image.settings')->get('suppress_itok_output')) { // The passed $path variable can be either a relative path or a full URI. $original_uri = file_uri_scheme($path) ? file_stream_wrapper_uri_normalize($path) : file_build_uri($path); $token_query = array(IMAGE_DERIVATIVE_TOKEN => $this->getPathToken($original_uri)); } if ($clean_urls === NULL) { // Assume clean URLs unless the request tells us otherwise. $clean_urls = TRUE; try { $request = \Drupal::request(); $clean_urls = RequestHelper::isCleanUrl($request); } catch (ServiceNotFoundException $e) { } } // If not using clean URLs, the image derivative callback is only available // with the script path. If the file does not exist, use Url::fromUri() to // ensure that it is included. Once the file exists it's fine to fall back // to the actual file path, this avoids bootstrapping PHP once the files are // built. if ($clean_urls === FALSE && file_uri_scheme($uri) == 'public' && !file_exists($uri)) { $directory_path = file_stream_wrapper_get_instance_by_uri($uri)->getDirectoryPath(); return Url::fromUri('base:' . $directory_path . '/' . file_uri_target($uri), array('absolute' => TRUE, 'query' => $token_query))->toString(); } $file_url = file_create_url($uri); // Append the query string with the token, if necessary. if ($token_query) { $file_url .= (strpos($file_url, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($token_query); } return $file_url; }
/** * Generates a URI string that represents tha data in the Url object. * * The URI will typically have the scheme of route: even if the object was * constructed using an entity: or internal: scheme. A internal: URI that * does not match a Drupal route with be returned here with the base: scheme, * and external URLs will be returned in their original form. * * @return string * A URI representation of the Url object data. */ public function toUriString() { if ($this->isRouted()) { $uri = 'route:' . $this->routeName; if ($this->routeParameters) { $uri .= ';' . UrlHelper::buildQuery($this->routeParameters); } } else { $uri = $this->uri; } $query = !empty($this->options['query']) ? '?' . UrlHelper::buildQuery($this->options['query']) : ''; $fragment = isset($this->options['fragment']) && strlen($this->options['fragment']) ? '#' . $this->options['fragment'] : ''; return $uri . $query . $fragment; }
/** * Creates a placeholder. * * This generates its own placeholder markup for one major reason: to not have * FilterProcessResult depend on the Renderer service, because this is a value * object. As a side-effect and added benefit, this makes it easier to * distinguish placeholders for filtered text versus generic render system * placeholders. * * @param string $callback * The #lazy_builder callback that will replace the placeholder with its * eventual markup. * @param array $args * The arguments for the #lazy_builder callback. * * @return string * The placeholder markup. */ public function createPlaceholder($callback, array $args) { // Generate placeholder markup. // @see \Drupal\Core\Render\PlaceholderGenerator::createPlaceholder() $arguments = UrlHelper::buildQuery($args); $token = hash('crc32b', serialize([$callback, $args])); $placeholder_markup = '<drupal-filter-placeholder callback="' . Html::escape($callback) . '" arguments="' . Html::escape($arguments) . '" token="' . Html::escape($token) . '"></drupal-filter-placeholder>'; // Add the placeholder attachment. $this->addAttachments(['placeholders' => [$placeholder_markup => ['#lazy_builder' => [$callback, $args]]]]); // Return the placeholder markup, so that the filter wanting to use a // placeholder can actually insert the placeholder markup where it needs the // placeholder to be replaced. return $placeholder_markup; }
/** * Loads and renders a view via AJAX. * * @param \Symfony\Component\HttpFoundation\Request $request * The current request object. * * @return \Drupal\views\Ajax\ViewAjaxResponse * The view response as ajax response. * * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException * Thrown when the view was not found. */ public function ajaxView(Request $request) { $name = $request->request->get('view_name'); $display_id = $request->request->get('view_display_id'); if (isset($name) && isset($display_id)) { $args = $request->request->get('view_args'); $args = isset($args) && $args !== '' ? explode('/', $args) : array(); // Arguments can be empty, make sure they are passed on as NULL so that // argument validation is not triggered. $args = array_map(function ($arg) { return $arg == '' ? NULL : $arg; }, $args); $path = $request->request->get('view_path'); $dom_id = $request->request->get('view_dom_id'); $dom_id = isset($dom_id) ? preg_replace('/[^a-zA-Z0-9_-]+/', '-', $dom_id) : NULL; $pager_element = $request->request->get('pager_element'); $pager_element = isset($pager_element) ? intval($pager_element) : NULL; $response = new ViewAjaxResponse(); // Remove all of this stuff from the query of the request so it doesn't // end up in pagers and tablesort URLs. foreach (array('view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER) as $key) { $request->query->remove($key); $request->request->remove($key); } // Load the view. if (!($entity = $this->storage->load($name))) { throw new NotFoundHttpException(); } $view = $this->executableFactory->get($entity); if ($view && $view->access($display_id)) { $response->setView($view); // Fix the current path for paging. if (!empty($path)) { $this->currentPath->setPath('/' . $path, $request); } // Add all POST data, because AJAX is always a post and many things, // such as tablesorts, exposed filters and paging assume GET. $request_all = $request->request->all(); $query_all = $request->query->all(); $request->query->replace($request_all + $query_all); // Overwrite the destination. // @see the redirect.destination service. $origin_destination = $path; // Remove some special parameters you never want to have part of the // destination query. $used_query_parameters = $request->query->all(); // @todo Remove this parsing once these are removed from the request in // https://www.drupal.org/node/2504709. unset($used_query_parameters[FormBuilderInterface::AJAX_FORM_REQUEST], $used_query_parameters[MainContentViewSubscriber::WRAPPER_FORMAT], $used_query_parameters['ajax_page_state']); $query = UrlHelper::buildQuery($used_query_parameters); if ($query != '') { $origin_destination .= '?' . $query; } $this->redirectDestination->set($origin_destination); // Override the display's pager_element with the one actually used. if (isset($pager_element)) { $response->addCommand(new ScrollTopCommand(".js-view-dom-id-{$dom_id}")); $view->displayHandlers->get($display_id)->setOption('pager_element', $pager_element); } // Reuse the same DOM id so it matches that in drupalSettings. $view->dom_id = $dom_id; $context = new RenderContext(); $preview = $this->renderer->executeInRenderContext($context, function () use($view, $display_id, $args) { return $view->preview($display_id, $args); }); if (!$context->isEmpty()) { $bubbleable_metadata = $context->pop(); BubbleableMetadata::createFromRenderArray($preview)->merge($bubbleable_metadata)->applyTo($preview); } $response->addCommand(new ReplaceCommand(".js-view-dom-id-{$dom_id}", $preview)); return $response; } else { throw new AccessDeniedHttpException(); } } else { throw new NotFoundHttpException(); } }
/** * {@inheritdoc} */ public function loadByRoute($route_name, array $route_parameters = array(), $menu_name = NULL) { // Sort the route parameters so that the query string will be the same. asort($route_parameters); // Since this will be urlencoded, it's safe to store and match against a // text field. // @todo Standardize an efficient way to load by route name and parameters // in place of system path. https://www.drupal.org/node/2302139 $param_key = $route_parameters ? UrlHelper::buildQuery($route_parameters) : ''; $query = $this->connection->select($this->table, $this->options); $query->fields($this->table, $this->definitionFields()); $query->condition('route_name', $route_name); $query->condition('route_param_key', $param_key); if ($menu_name) { $query->condition('menu_name', $menu_name); } // Make the ordering deterministic. $query->orderBy('depth'); $query->orderBy('weight'); $query->orderBy('id'); $loaded = $this->safeExecuteSelect($query)->fetchAllAssoc('id', \PDO::FETCH_ASSOC); foreach ($loaded as $id => $link) { $loaded[$id] = $this->prepareLink($link); } return $loaded; }
/** * Render this field as a link, with the info from a fieldset set by * the user. */ protected function renderAsLink($alter, $text, $tokens) { $value = ''; if (!empty($alter['prefix'])) { $value .= Xss::filterAdmin(strtr($alter['prefix'], $tokens)); } $options = array('html' => TRUE, 'absolute' => !empty($alter['absolute']) ? TRUE : FALSE); // $path will be run through check_url() by l() so we do not need to // sanitize it ourselves. $path = $alter['path']; // strip_tags() removes <front>, so check whether its different to front. if ($path != '<front>') { // Use strip tags as there should never be HTML in the path. // However, we need to preserve special characters like " that // were removed by String::checkPlain(). $path = strip_tags(decode_entities(strtr($path, $tokens))); if (!empty($alter['path_case']) && $alter['path_case'] != 'none') { $path = $this->caseTransform($path, $this->options['alter']['path_case']); } if (!empty($alter['replace_spaces'])) { $path = str_replace(' ', '-', $path); } } // Parse the URL and move any query and fragment parameters out of the path. $url = parse_url($path); // Seriously malformed URLs may return FALSE or empty arrays. if (empty($url)) { return $text; } // If the path is empty do not build a link around the given text and return // it as is. // http://www.example.com URLs will not have a $url['path'], so check host as well. if (empty($url['path']) && empty($url['host']) && empty($url['fragment'])) { return $text; } // If no scheme is provided in the $path, assign the default 'http://'. // This allows a url of 'www.example.com' to be converted to 'http://www.example.com'. // Only do this on for external URLs. if ($alter['external']) { if (!isset($url['scheme'])) { // There is no scheme, add the default 'http://' to the $path. $path = "http://{$path}"; // Reset the $url array to include the new scheme. $url = parse_url($path); } } if (isset($url['query'])) { $path = strtr($path, array('?' . $url['query'] => '')); $query = array(); parse_str($url['query'], $query); // Remove query parameters that were assigned a query string replacement // token for which there is no value available. foreach ($query as $param => $val) { if ($val == '%' . $param) { unset($query[$param]); } } $options['query'] = $query; } if (isset($url['fragment'])) { $path = strtr($path, array('#' . $url['fragment'] => '')); // If the path is empty we want to have a fragment for the current site. if ($path == '') { $options['external'] = TRUE; } $options['fragment'] = $url['fragment']; } $alt = strtr($alter['alt'], $tokens); // Set the title attribute of the link only if it improves accessibility if ($alt && $alt != $text) { $options['attributes']['title'] = decode_entities($alt); } $class = strtr($alter['link_class'], $tokens); if ($class) { $options['attributes']['class'] = array($class); } if (!empty($alter['rel']) && ($rel = strtr($alter['rel'], $tokens))) { $options['attributes']['rel'] = $rel; } $target = String::checkPlain(trim(strtr($alter['target'], $tokens))); if (!empty($target)) { $options['attributes']['target'] = $target; } // Allow the addition of arbitrary attributes to links. Additional attributes // currently can only be altered in preprocessors and not within the UI. if (isset($alter['link_attributes']) && is_array($alter['link_attributes'])) { foreach ($alter['link_attributes'] as $key => $attribute) { if (!isset($options['attributes'][$key])) { $options['attributes'][$key] = strtr($attribute, $tokens); } } } // If the query and fragment were programatically assigned overwrite any // parsed values. if (isset($alter['query'])) { // Convert the query to a string, perform token replacement, and then // convert back to an array form for l(). $options['query'] = UrlHelper::buildQuery($alter['query']); $options['query'] = strtr($options['query'], $tokens); $query = array(); parse_str($options['query'], $query); $options['query'] = $query; } if (isset($alter['alias'])) { // Alias is a boolean field, so no token. $options['alias'] = $alter['alias']; } if (isset($alter['fragment'])) { $options['fragment'] = strtr($alter['fragment'], $tokens); } if (isset($alter['language'])) { $options['language'] = $alter['language']; } // If the url came from entity_uri(), pass along the required options. if (isset($alter['entity'])) { $options['entity'] = $alter['entity']; } if (isset($alter['entity_type'])) { $options['entity_type'] = $alter['entity_type']; } $value .= l($text, $path, $options); if (!empty($alter['suffix'])) { $value .= Xss::filterAdmin(strtr($alter['suffix'], $tokens)); } return $value; }
/** * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { parent::form($form, $form_state); $form['#title'] = $this->t('Edit menu link %title', ['%title' => $this->entity->getTitle()]); // Put the title field first. $form['title'] = ['#type' => 'textfield', '#title' => $this->t('Title'), '#default_value' => $this->entity->getTitle(), '#weight' => -10]; $form['id'] = ['#type' => 'machine_name', '#maxlength' => 128, '#machine_name' => ['source' => ['title'], 'exists' => '\\Drupal\\menu_link_config\\Controller\\MenuController::getMenuLink'], '#disabled' => !$this->entity->isNew(), '#weight' => -9]; $form['description'] = ['#type' => 'textfield', '#title' => $this->t('Description'), '#description' => $this->t('Shown when hovering over the menu link.'), '#default_value' => $this->entity->getDescription(), '#weight' => -5]; $link = ['#type' => 'link', '#title' => $this->entity->getTitle()] + $this->entity->getUrlObject()->toRenderArray(); $form['info'] = ['link' => $link, '#type' => 'item', '#title' => $this->t('Link')]; // We always show the internal path here. /** @var \Drupal\Core\Url $url */ $url = $this->entity->getUrlObject(); if ($url->isExternal()) { $default_value = $url->toString(); } elseif ($url->getRouteName() == '<front>') { // The default route for new entities is <front>, but we just want an // empty form field. $default_value = ''; } else { // @todo Url::getInternalPath() calls UrlGenerator::getPathFromRoute() // which need a replacement since it is deprecated. // https://www.drupal.org/node/2307061 try { $default_value = $url->getInternalPath(); } catch (\Exception $e) { $default_value = 'broken path'; } // @todo Add a helper method to Url to render just the query string and // fragment. https://www.drupal.org/node/2305013 $options = $url->getOptions(); if (isset($options['query'])) { $default_value .= $options['query'] ? '?' . UrlHelper::buildQuery($options['query']) : ''; } if (isset($options['fragment']) && $options['fragment'] !== '') { $default_value .= '#' . $options['fragment']; } } $form['url'] = ['#title' => $this->t('Link path'), '#type' => 'textfield', '#description' => $this->t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', ['%front' => '<front>', '%add-node' => '/node/add', '%drupal' => 'http://drupal.org']), '#default_value' => $default_value, '#required' => TRUE, '#weight' => -2]; $form['enabled'] = ['#type' => 'checkbox', '#title' => $this->t('Enable menu link'), '#description' => $this->t('Menu links that are not enabled will not be listed in any menu.'), '#default_value' => $this->entity->status()]; $form['expanded'] = ['#type' => 'checkbox', '#title' => t('Show as expanded'), '#description' => $this->t('If selected and this menu link has children, the menu will always appear expanded.'), '#default_value' => $this->entity->isExpanded()]; $menu_parent = $this->entity->getMenuName() . ':' . $this->entity->getParent(); $form['menu_parent'] = $this->menuParentSelector->parentSelectElement($menu_parent, $this->entity->getPluginId()); $form['menu_parent']['#title'] = $this->t('Parent link'); $form['menu_parent']['#description'] = $this->t('The maximum depth for a link and all its children is fixed. Some menu links may not be available as parents if selecting them would exceed this limit.'); $form['menu_parent']['#attributes']['class'][] = 'menu-title-select'; $delta = max(abs($this->entity->getWeight()), 50); $form['weight'] = ['#type' => 'number', '#min' => -$delta, '#max' => $delta, '#default_value' => $this->entity->getWeight(), '#title' => $this->t('Weight'), '#description' => $this->t('Link weight among links in the same menu at the same depth. In the menu, the links with high weight will sink and links with a low weight will be positioned nearer the top.')]; return $form; }