/** * {@inheritdoc} */ public function validate($value, Constraint $constraint) { if (isset($value)) { $url_is_valid = TRUE; /** @var $link_item \Drupal\link\LinkItemInterface */ $link_item = $value; $link_type = $link_item->getFieldDefinition()->getSetting('link_type'); $url_string = $link_item->url; // Validate the url property. if ($url_string !== '') { try { // @todo This shouldn't be needed, but massageFormValues() may not // run. $parsed_url = UrlHelper::parse($url_string); $url = Url::createFromPath($parsed_url['path']); if ($url->isExternal() && !UrlHelper::isValid($url_string, TRUE)) { $url_is_valid = FALSE; } elseif ($url->isExternal() && !($link_type & LinkItemInterface::LINK_EXTERNAL)) { $url_is_valid = FALSE; } } catch (NotFoundHttpException $e) { $url_is_valid = FALSE; } catch (MatchingRouteNotFoundException $e) { $url_is_valid = FALSE; } catch (ParamNotConvertedException $e) { $url_is_valid = FALSE; } } if (!$url_is_valid) { $this->context->addViolation($this->message, array('%url' => $url_string)); } } }
/** * {@inheritdoc} */ public function getUrlIfValid($path) { $parsed_url = UrlHelper::parse($path); $options = []; if (!empty($parsed_url['query'])) { $options['query'] = $parsed_url['query']; } if (!empty($parsed_url['fragment'])) { $options['fragment'] = $parsed_url['fragment']; } if ($parsed_url['path'] == '<front>') { return new Url('<front>', [], $options); } elseif (UrlHelper::isExternal($path) && UrlHelper::isValid($path)) { if (empty($parsed_url['path'])) { return FALSE; } return Url::fromUri($path); } $path = ltrim($path, '/'); $request = Request::create('/' . $path); $attributes = $this->getPathAttributes($path, $request); if (!$attributes) { return FALSE; } $route_name = $attributes[RouteObjectInterface::ROUTE_NAME]; $route_parameters = $attributes['_raw_variables']->all(); return new Url($route_name, $route_parameters, $options + ['query' => $request->query->all()]); }
/** * {@inheritdoc} */ public function execute($object = NULL) { $url = $this->configuration['url']; // Leave external URLs unchanged, and assemble others as absolute URLs // relative to the site's base URL. if (!UrlHelper::isExternal($url)) { $parts = UrlHelper::parse($url); // @todo '<front>' is valid input for BC reasons, may be removed by // https://www.drupal.org/node/2421941 if ($parts['path'] === '<front>') { $parts['path'] = ''; } $uri = 'base:' . $parts['path']; $options = ['query' => $parts['query'], 'fragment' => $parts['fragment'], 'absolute' => TRUE]; // Treat this as if it's user input of a path relative to the site's // base URL. $url = $this->unroutedUrlAssembler->assemble($uri, $options); } $response = new RedirectResponse($url); $listener = function ($event) use($response) { $event->setResponse($response); }; // Add the listener to the event dispatcher. $this->dispatcher->addListener(KernelEvents::RESPONSE, $listener); }
/** * @param $url * * @see FillPdfLinkManipulatorInterface::parseUrlString() * * @return \Drupal\Core\Url */ protected function createUrlFromString($url) { $url_parts = UrlHelper::parse($url); $path = $url_parts['path']; $query = $url_parts['query']; $link = Url::fromUri($path, ['query' => $query]); return $link; }
/** * {@inheritdoc} */ public function setEmbedProvider($provider) { $provider_parsed = UrlHelper::parse($provider); $provider_parsed['query'] = array_filter($provider_parsed['query'], function ($value) { return $value !== '{callback}'; }); $provider_parsed['absolute'] = TRUE; $this->embed_provider = $this->urlAssembler->assemble($provider_parsed['path'], $provider_parsed); }
/** * Returns the next redirect path in a multipage sequence. * * @param array $destinations * An array of destinations to redirect to. * * @return array * The next destination to redirect to. */ public static function getNextDestination(array $destinations) { $next_destination = array_shift($destinations); if (is_array($next_destination)) { $next_destination['options']['query']['destinations'] = $destinations; } else { $options = UrlHelper::parse($next_destination); if ($destinations) { $options['query']['destinations'] = $destinations; } $next_destination = array($options['path'], $options); } return $next_destination; }
/** * Builds the cancel link for a confirmation form. * * @param \Drupal\Core\Form\ConfirmFormInterface $form * The confirmation form. * @param \Symfony\Component\HttpFoundation\Request $request * The current request. * * @return array * The link render array for the cancel form. */ public static function buildCancelLink(ConfirmFormInterface $form, Request $request) { // Prepare cancel link. $query = $request->query; // If a destination is specified, that serves as the cancel link. if ($query->has('destination')) { $options = UrlHelper::parse($query->get('destination')); // @todo Revisit this in https://www.drupal.org/node/2418219. $url = Url::fromUserInput('/' . $options['path'], $options); } else { $url = $form->getCancelUrl(); } return ['#type' => 'link', '#title' => $form->getCancelText(), '#attributes' => ['class' => ['button']], '#url' => $url]; }
/** * Builds the cancel link for a confirmation form. * * @param \Drupal\Core\Form\ConfirmFormInterface $form * The confirmation form. * @param \Symfony\Component\HttpFoundation\Request $request * The current request. * * @return array * The link render array for the cancel form. */ public static function buildCancelLink(ConfirmFormInterface $form, Request $request) { // Prepare cancel link. $query = $request->query; // If a destination is specified, that serves as the cancel link. if ($query->has('destination')) { $options = UrlHelper::parse($query->get('destination')); // @todo Use Url::fromPath() once https://www.drupal.org/node/2351379 is // resolved. $url = Url::fromUri('base://' . $options['path'], $options); } else { $url = $form->getCancelUrl(); } return ['#type' => 'link', '#title' => $form->getCancelText(), '#url' => $url]; }
/** * Builds the cancel link for a confirmation form. * * @param \Drupal\Core\Form\ConfirmFormInterface $form * The confirmation form. * @param \Symfony\Component\HttpFoundation\Request $request * The current request. * * @return array * The link render array for the cancel form. */ public static function buildCancelLink(ConfirmFormInterface $form, Request $request) { // Prepare cancel link. $query = $request->query; // If a destination is specified, that serves as the cancel link. if ($query->has('destination')) { $options = UrlHelper::parse($query->get('destination')); $link = array('#href' => $options['path'], '#options' => $options); } elseif ($route = $form->getCancelRoute()) { $link = $route->toRenderArray(); } $link['#type'] = 'link'; $link['#title'] = $form->getCancelText(); return $link; }
/** * Returns the next redirect path in a multipage sequence. * * @param array $destinations * An array of destinations to redirect to. * * @return \Drupal\Core\Url * The next destination to redirect to. */ public static function getNextDestination(array $destinations) { $next_destination = array_shift($destinations); if (is_array($next_destination)) { $next_destination['options']['query']['destinations'] = $destinations; $next_destination += array('route_parameters' => array()); $next_destination = Url::fromRoute($next_destination['route_name'], $next_destination['route_parameters'], $next_destination['options']); } else { $options = UrlHelper::parse($next_destination); if ($destinations) { $options['query']['destinations'] = $destinations; } // Redirect to any given path within the same domain. // @todo Revisit this in https://www.drupal.org/node/2418219. $next_destination = Url::fromUserInput('/' . $options['path']); } return $next_destination; }
/** * Returns the next redirect path in a multipage sequence. * * @param array $destinations * An array of destinations to redirect to. * * @return \Drupal\Core\Url * The next destination to redirect to. */ public static function getNextDestination(array $destinations) { $next_destination = array_shift($destinations); if (is_array($next_destination)) { $next_destination['options']['query']['destinations'] = $destinations; $next_destination += array('route_parameters' => array()); $next_destination = Url::fromRoute($next_destination['route_name'], $next_destination['route_parameters'], $next_destination['options']); } else { $options = UrlHelper::parse($next_destination); if ($destinations) { $options['query']['destinations'] = $destinations; } // Redirect to any given path within the same domain. // @todo Use Url::fromPath() once https://www.drupal.org/node/2351379 is // resolved. $next_destination = Url::fromUri('base://' . $options['path']); } return $next_destination; }
/** * Allows manipulation of the response object when performing a redirect. * * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event * The Event to process. */ public function checkRedirectUrl(FilterResponseEvent $event) { $response = $event->getResponse(); if ($response instanceof RedirectResponse) { $options = array(); $request = $event->getRequest(); $destination = $request->query->get('destination'); // A destination from \Drupal::request()->query always overrides the // current RedirectResponse. We do not allow absolute URLs to be passed // via \Drupal::request()->query, as this can be an attack vector, with // the following exception: // - Absolute URLs that point to this site (i.e. same base URL and // base path) are allowed. if ($destination) { if (!UrlHelper::isExternal($destination)) { // The destination query parameter can be a relative URL in the sense // of not including the scheme and host, but its path is expected to // be absolute (start with a '/'). For such a case, prepend the // scheme and host, because the 'Location' header must be absolute. if (strpos($destination, '/') === 0) { $destination = $request->getSchemeAndHttpHost() . $destination; } else { // Legacy destination query parameters can be relative paths that // have not yet been converted to URLs (outbound path processors // and other URL handling still needs to be performed). // @todo As generateFromPath() is deprecated, remove this in // https://www.drupal.org/node/2418219. $destination = UrlHelper::parse($destination); $path = $destination['path']; $options['query'] = $destination['query']; $options['fragment'] = $destination['fragment']; // The 'Location' HTTP header must always be absolute. $options['absolute'] = TRUE; $destination = $this->urlGenerator->generateFromPath($path, $options); } $response->setTargetUrl($destination); } elseif (UrlHelper::externalIsLocal($destination, $this->requestContext->getCompleteBaseUrl())) { $response->setTargetUrl($destination); } } } }
/** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items, $langcode) { // Define element array. $element = []; foreach ($items as $delta => $item) { // Convert the link-field item to a \Drupal\Core\Url object. $url = $this->buildUrl($item); // Parse the URL so we can check if the path contains a Github Gist. We do // this to check if we should actually render the element or provide a // notice in the log. $url = UrlHelper::parse($url->getUri()); if (FALSE !== Unicode::strpos($url['path'], 'gist.github.com')) { $element[$delta] = ['#theme' => 'gist_embed', '#url' => $url]; } else { $entity = $items->getEntity(); $message = $this->t('The entity type: "@type" with the ID: "@id" is trying to use a link that isn\'t a Github Gist link for the Gist Embed Formatter.', array('@type' => ucfirst($entity->getEntityType()->id()), '@id' => $entity->id())); \Drupal::logger('gist_embed')->notice($message); } } return $element; }
/** * Builds the cancel link for a confirmation form. * * @param \Drupal\Core\Form\ConfirmFormInterface $form * The confirmation form. * @param \Symfony\Component\HttpFoundation\Request $request * The current request. * * @return array * The link render array for the cancel form. */ public static function buildCancelLink(ConfirmFormInterface $form, Request $request) { // Prepare cancel link. $query = $request->query; $url = NULL; // If a destination is specified, that serves as the cancel link. if ($query->has('destination')) { $options = UrlHelper::parse($query->get('destination')); // @todo Revisit this in https://www.drupal.org/node/2418219. try { $url = Url::fromUserInput('/' . ltrim($options['path'], '/'), $options); } catch (\InvalidArgumentException $e) { // Suppress the exception and fall back to the form's cancel url. } } // Check for a route-based cancel link. if (!$url) { $url = $form->getCancelUrl(); } return ['#type' => 'link', '#title' => $form->getCancelText(), '#attributes' => ['class' => ['button']], '#url' => $url]; }
/** * Allows manipulation of the response object when performing a redirect. * * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event * The Event to process. */ public function checkRedirectUrl(FilterResponseEvent $event) { $response = $event->getResponse(); if ($response instanceof RedirectResponse) { $options = array(); $destination = $event->getRequest()->query->get('destination'); // A destination from \Drupal::request()->query always overrides the // current RedirectResponse. We do not allow absolute URLs to be passed // via \Drupal::request()->query, as this can be an attack vector, with // the following exception: // - Absolute URLs that point to this site (i.e. same base URL and // base path) are allowed. if ($destination && (!UrlHelper::isExternal($destination) || UrlHelper::externalIsLocal($destination, $GLOBALS['base_url']))) { $destination = UrlHelper::parse($destination); $path = $destination['path']; $options['query'] = $destination['query']; $options['fragment'] = $destination['fragment']; // The 'Location' HTTP header must always be absolute. $options['absolute'] = TRUE; $response->setTargetUrl($this->urlGenerator->generateFromPath($path, $options)); } } }
/** * 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); }
/** * {@inheritdoc} */ public function massageFormValues(array $values, array $form, array &$form_state) { foreach ($values as &$value) { if (!empty($value['url'])) { try { $parsed_url = UrlHelper::parse($value['url']); // If internal links are supported, look up whether the given value is // a path alias and store the system path instead. if ($this->supportsInternalLinks() && !UrlHelper::isExternal($value['url'])) { $parsed_url['path'] = \Drupal::service('path.alias_manager')->getPathByAlias($parsed_url['path']); } $url = Url::createFromPath($parsed_url['path']); $url->setOption('query', $parsed_url['query']); $url->setOption('fragment', $parsed_url['fragment']); $url->setOption('attributes', $value['attributes']); $value += $url->toArray(); // Reset the URL value to contain only the path. $value['url'] = $parsed_url['path']; } catch (NotFoundHttpException $e) { // Nothing to do here, LinkTypeConstraintValidator emits errors. } catch (MatchingRouteNotFoundException $e) { // Nothing to do here, LinkTypeConstraintValidator emits errors. } catch (ParamNotConvertedException $e) { // Nothing to do here, LinkTypeConstraintValidator emits errors. } } } return $values; }
/** * Validates the path of the display. * * @param string $path * The path to validate. * * @return array * A list of error strings. */ protected function validatePath($path) { $errors = array(); if (strpos($path, '%') === 0) { $errors[] = $this->t('"%" may not be used for the first segment of a path.'); } $parsed_url = UrlHelper::parse($path); if (empty($parsed_url['path'])) { $errors[] = $this->t('Path is empty.'); } if (!empty($parsed_url['query'])) { $errors[] = $this->t('No query allowed.'); } if (!parse_url('internal:/' . $path)) { $errors[] = $this->t('Invalid path. Valid characters are alphanumerics as well as "-", ".", "_" and "~".'); } $path_sections = explode('/', $path); // Symfony routing does not allow to use numeric placeholders. // @see \Symfony\Component\Routing\RouteCompiler $numeric_placeholders = array_filter($path_sections, function ($section) { return preg_match('/^%(.*)/', $section, $matches) && is_numeric($matches[1]); }); if (!empty($numeric_placeholders)) { $errors[] = $this->t("Numeric placeholders may not be used. Please use plain placeholders (%)."); } return $errors; }
/** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { parent::validateForm($form, $form_state); $source = $form_state->getValue(array('redirect_source', 0)); $redirect = $form_state->getValue(array('redirect_redirect', 0)); if ($source['path'] == '<front>') { $form_state->setErrorByName('redirect_source', t('It is not allowed to create a redirect from the front page.')); } if (strpos($source['path'], '#') !== FALSE) { $form_state->setErrorByName('redirect_source', t('The anchor fragments are not allowed.')); } if (strpos($source['path'], '/') === 0) { $form_state->setErrorByName('redirect_source', t('The url to redirect from should not start with a forward slash (/).')); } try { $source_url = Url::fromUri('internal:/' . $source['path']); $redirect_url = Url::fromUri($redirect['uri']); // It is relevant to do this comparison only in case the source path has // a valid route. Otherwise the validation will fail on the redirect path // being an invalid route. if ($source_url->toString() == $redirect_url->toString()) { $form_state->setErrorByName('redirect_redirect', t('You are attempting to redirect the page to itself. This will result in an infinite loop.')); } } catch (\InvalidArgumentException $e) { // Do nothing, we want to only compare the resulting URLs. } $parsed_url = UrlHelper::parse(trim($source['path'])); $path = isset($parsed_url['path']) ? $parsed_url['path'] : NULL; $query = isset($parsed_url['query']) ? $parsed_url['query'] : NULL; $hash = Redirect::generateHash($path, $query, $form_state->getValue('language')[0]['value']); // Search for duplicate. $redirects = \Drupal::entityManager()->getStorage('redirect')->loadByProperties(array('hash' => $hash)); if (!empty($redirects)) { $redirect = array_shift($redirects); if ($this->entity->isNew() || $redirect->id() != $this->entity->id()) { $form_state->setErrorByName('redirect_source', t('The source path %source is already being redirected. Do you want to <a href="@edit-page">edit the existing redirect</a>?', array('%source' => $source['path'], '@edit-page' => $redirect->url('edit-form')))); } } }
/** * Converts the passed in destination into an absolute URL. * * @param string $destination * The path for the destination. In case it starts with a slash it should * have the base path included already. * @param string $scheme_and_host * The scheme and host string of the current request. * * @return string * The destination as absolute URL. */ protected function getDestinationAsAbsoluteUrl($destination, $scheme_and_host) { if (!UrlHelper::isExternal($destination)) { // The destination query parameter can be a relative URL in the sense of // not including the scheme and host, but its path is expected to be // absolute (start with a '/'). For such a case, prepend the scheme and // host, because the 'Location' header must be absolute. if (strpos($destination, '/') === 0) { $destination = $scheme_and_host . $destination; } else { // Legacy destination query parameters can be internal paths that have // not yet been converted to URLs. $destination = UrlHelper::parse($destination); $uri = 'base:' . $destination['path']; $options = ['query' => $destination['query'], 'fragment' => $destination['fragment'], 'absolute' => TRUE]; // Treat this as if it's user input of a path relative to the site's // base URL. $destination = $this->unroutedUrlAssembler->assemble($uri, $options); } } return $destination; }
/** * Tests using entity fields of the link field type. */ public function testLinkItem() { // Create entity. $entity = entity_create('entity_test'); $url = 'https://www.drupal.org?test_param=test_value'; $parsed_url = UrlHelper::parse($url); $title = $this->randomMachineName(); $class = $this->randomMachineName(); $entity->field_test->uri = $parsed_url['path']; $entity->field_test->title = $title; $entity->field_test->first()->get('options')->set('query', $parsed_url['query']); $entity->field_test->first()->get('options')->set('attributes', array('class' => $class)); $entity->name->value = $this->randomMachineName(); $entity->save(); // Verify that the field value is changed. $id = $entity->id(); $entity = entity_load('entity_test', $id); $this->assertTrue($entity->field_test instanceof FieldItemListInterface, 'Field implements interface.'); $this->assertTrue($entity->field_test[0] instanceof FieldItemInterface, 'Field item implements interface.'); $this->assertEqual($entity->field_test->uri, $parsed_url['path']); $this->assertEqual($entity->field_test[0]->uri, $parsed_url['path']); $this->assertEqual($entity->field_test->title, $title); $this->assertEqual($entity->field_test[0]->title, $title); $this->assertEqual($entity->field_test->options['attributes']['class'], $class); $this->assertEqual($entity->field_test->options['query'], $parsed_url['query']); // Update only the entity name property to check if the link field data will // remain intact. $entity->name->value = $this->randomMachineName(); $entity->save(); $id = $entity->id(); $entity = entity_load('entity_test', $id); $this->assertEqual($entity->field_test->uri, $parsed_url['path']); $this->assertEqual($entity->field_test->options['attributes']['class'], $class); $this->assertEqual($entity->field_test->options['query'], $parsed_url['query']); // Verify changing the field value. $new_url = 'https://www.drupal.org'; $new_title = $this->randomMachineName(); $new_class = $this->randomMachineName(); $entity->field_test->uri = $new_url; $entity->field_test->title = $new_title; $entity->field_test->first()->get('options')->set('query', NULL); $entity->field_test->first()->get('options')->set('attributes', array('class' => $new_class)); $this->assertEqual($entity->field_test->uri, $new_url); $this->assertEqual($entity->field_test->title, $new_title); $this->assertEqual($entity->field_test->options['attributes']['class'], $new_class); $this->assertNull($entity->field_test->options['query']); // Read changed entity and assert changed values. $entity->save(); $entity = entity_load('entity_test', $id); $this->assertEqual($entity->field_test->uri, $new_url); $this->assertEqual($entity->field_test->title, $new_title); $this->assertEqual($entity->field_test->options['attributes']['class'], $new_class); // Check that if we only set uri the default values for title and options // are also initialized. $entity->field_test = ['uri' => 'internal:/node/add']; $this->assertEqual($entity->field_test->uri, 'internal:/node/add'); $this->assertNull($entity->field_test->title); $this->assertIdentical($entity->field_test->options, []); // Check that if set uri and serialize options then the default values are // properly initialized. $entity->field_test = ['uri' => 'internal:/node/add', 'options' => serialize(['query' => NULL])]; $this->assertEqual($entity->field_test->uri, 'internal:/node/add'); $this->assertNull($entity->field_test->title); $this->assertNull($entity->field_test->options['query']); // Check that if we set the direct value of link field it correctly set the // uri and the default values of the field. $entity->field_test = 'internal:/node/add'; $this->assertEqual($entity->field_test->uri, 'internal:/node/add'); $this->assertNull($entity->field_test->title); $this->assertIdentical($entity->field_test->options, []); // Check that setting LinkItem value NULL doesn't generate any error or // warning. $entity->field_test[0] = NULL; $this->assertNull($entity->field_test[0]->getValue()); // Test the generateSampleValue() method for generic, external, and internal // link types. $entity = entity_create('entity_test'); $entity->field_test->generateSampleItems(); $entity->field_test_external->generateSampleItems(); $entity->field_test_internal->generateSampleItems(); $this->entityValidateAndSave($entity); }
/** * Breaks up a user-entered URL or path into all the relevant parts. * * @param string $url * The user-entered URL or path. * * @return array * The extracted parts. */ protected function extractUrl($url) { $extracted = UrlHelper::parse($url); $external = UrlHelper::isExternal($url); if ($external) { $extracted['url'] = $extracted['path']; $extracted['route_name'] = NULL; $extracted['route_parameters'] = array(); } else { $extracted['url'] = ''; // If the path doesn't match a Drupal path, the route should end up empty. $extracted['route_name'] = NULL; $extracted['route_parameters'] = array(); try { // Find the route_name. $normal_path = $this->pathAliasManager->getPathByAlias($extracted['path']); $url_obj = Url::createFromPath($normal_path); $extracted['route_name'] = $url_obj->getRouteName(); $extracted['route_parameters'] = $url_obj->getRouteParameters(); } catch (MatchingRouteNotFoundException $e) { // The path doesn't match a Drupal path. } catch (ParamNotConvertedException $e) { // A path like node/99 matched a route, but the route parameter was // invalid (e.g. node with ID 99 does not exist). } } return $extracted; }
/** * Tests the fromUserInput method with valid paths. * * @covers ::fromUserInput * @dataProvider providerFromValidInternalUri */ public function testFromUserInput($path) { $url = Url::fromUserInput($path); $uri = $url->getUri(); $this->assertInstanceOf('Drupal\\Core\\Url', $url); $this->assertFalse($url->isRouted()); $this->assertEquals(0, strpos($uri, 'base:')); $parts = UrlHelper::parse($path); $options = $url->getOptions(); if (!empty($parts['fragment'])) { $this->assertSame($parts['fragment'], $options['fragment']); } else { $this->assertArrayNotHasKey('fragment', $options); } if (!empty($parts['query'])) { $this->assertEquals($parts['query'], $options['query']); } else { $this->assertArrayNotHasKey('query', $options); } }
/** * Tests url parsing. * * @dataProvider providerTestParse * @covers ::parse * * @param string $url * URL to test. * @param array $expected * Associative array with expected parameters. */ public function testParse($url, $expected) { $parsed = UrlHelper::parse($url); $this->assertEquals($expected, $parsed, 'The url was not properly parsed.'); }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $cart_links_config = $this->config('uc_cart_links.settings'); $actions = explode('-', urldecode($this->actions)); $rebuild_cart = FALSE; $messages = array(); $id = $this->t('(not specified)'); $cart = Cart::create(\Drupal::getContainer()); foreach ($actions as $action) { switch (Unicode::substr($action, 0, 1)) { // Set the ID of the Cart Link. case 'i': case 'I': $id = Unicode::substr($action, 1, 32); break; // Add a product to the cart. // Add a product to the cart. case 'p': case 'P': // Set the default product variables. $p = array('nid' => 0, 'qty' => 1, 'data' => array()); $msg = TRUE; // Parse the product action to adjust the product variables. $parts = explode('_', $action); foreach ($parts as $part) { switch (Unicode::substr($part, 0, 1)) { // Set the product node ID: p23 case 'p': case 'P': $p['nid'] = intval(Unicode::substr($part, 1)); break; // Set the quantity to add to cart: _q2 // Set the quantity to add to cart: _q2 case 'q': case 'Q': $p['qty'] = intval(Unicode::substr($part, 1)); break; // Set an attribute/option for the product: _a3o6 // Set an attribute/option for the product: _a3o6 case 'a': case 'A': $attribute = intval(Unicode::substr($part, 1, stripos($part, 'o') - 1)); $option = (string) Unicode::substr($part, stripos($part, 'o') + 1); if (!isset($p['attributes'][$attribute])) { $p['attributes'][$attribute] = $option; } else { // Multiple options for this attribute implies checkbox // attribute, which we must store as an array if (is_array($p['attributes'][$attribute])) { // Already an array, just append this new option $p['attributes'][$attribute][$option] = $option; } else { // Set but not an array, means we already have at least one // option, so put that into an array with this new option $p['attributes'][$attribute] = array($p['attributes'][$attribute] => $p['attributes'][$attribute], $option => $option); } } break; // Suppress the add to cart message: _s // Suppress the add to cart message: _s case 's': case 'S': $msg = FALSE; break; } } // Add the item to the cart, suppressing the default redirect. if ($p['nid'] > 0 && $p['qty'] > 0) { // If it's a product kit, we need black magic to make everything work // right. In other words, we have to simulate FAPI's form values. $node = node_load($p['nid']); if ($node->status) { if (isset($node->products) && is_array($node->products)) { foreach ($node->products as $nid => $product) { $p['data']['products'][$nid] = array('nid' => $nid, 'qty' => $product->qty); } } $cart->addItem($p['nid'], $p['qty'], $p['data'] + \Drupal::moduleHandler()->invokeAll('uc_add_to_cart_data', array($p)), NULL, $msg, FALSE, FALSE); $rebuild_cart = TRUE; } else { $this->logger('uc_cart_link')->error('Cart Link on %url tried to add an unpublished product to the cart.', array('%url' => $this->getRequest()->server->get('HTTP_REFERER'))); } } break; // Empty the shopping cart. // Empty the shopping cart. case 'e': case 'E': if ($cart_links_config->get('empty')) { $cart->emptyCart(); } break; // Display a pre-configured message. // Display a pre-configured message. case 'm': case 'M': // Load the messages if they haven't been loaded yet. if (empty($messages)) { $data = explode("\n", $cart_links_config->get('messages')); foreach ($data as $message) { list($mkey, $mdata) = explode('|', $message, 2); $messages[trim($mkey)] = trim($mdata); } } // Parse the message key and display it if it exists. $mkey = intval(Unicode::substr($action, 1)); if (!empty($messages[$mkey])) { drupal_set_message($messages[$mkey]); } break; } // Rebuild the cart cache if necessary. if ($rebuild_cart) { $cart->getContents(NULL, 'rebuild'); } } if ($cart_links_config->get('track')) { db_merge('uc_cart_link_clicks')->key(array('cart_link_id' => (string) $id))->fields(array('clicks' => 1, 'last_click' => REQUEST_TIME))->expression('clicks', 'clicks + :i', array(':i' => 1))->execute(); } $_SESSION['uc_cart_last_url'] = $this->getRequest()->server->get('HTTP_REFERER'); $query = $this->getRequest()->query; if ($query->has('destination')) { $options = UrlHelper::parse($query->get('destination')); $path = $options['path']; } else { $path = 'cart'; $options = array(); } $options += array('absolute' => TRUE); // Form redirect is for confirmed links. $form_state->setRedirectUrl(Url::fromUri('base:/' . $path, $options)); }
/** * Executes an Ajax form submission. * * This executes a POST as ajax.js does. The returned JSON data is used to * update $this->content via drupalProcessAjaxResponse(). It also returns * the array of AJAX commands received. * * @param \Drupal\Core\Url|string $path * Location of the form containing the Ajax enabled element to test. Can be * either a Drupal path or an absolute path or NULL to use the current page. * @param $edit * Field data in an associative array. Changes the current input fields * (where possible) to the values indicated. * @param $triggering_element * The name of the form element that is responsible for triggering the Ajax * functionality to test. May be a string or, if the triggering element is * a button, an associative array where the key is the name of the button * and the value is the button label. i.e.) array('op' => t('Refresh')). * @param $ajax_path * (optional) Override the path set by the Ajax settings of the triggering * element. * @param $options * (optional) Options to be forwarded to the url generator. * @param $headers * (optional) An array containing additional HTTP request headers, each * formatted as "name: value". Forwarded to drupalPostForm(). * @param $form_html_id * (optional) HTML ID of the form to be submitted, use when there is more * than one identical form on the same page and the value of the triggering * element is not enough to identify the form. Note this is not the Drupal * ID of the form but rather the HTML ID of the form. * @param $ajax_settings * (optional) An array of Ajax settings which if specified will be used in * place of the Ajax settings of the triggering element. * * @return * An array of Ajax commands. * * @see drupalPostForm() * @see drupalProcessAjaxResponse() * @see ajax.js */ protected function drupalPostAjaxForm($path, $edit, $triggering_element, $ajax_path = NULL, array $options = array(), array $headers = array(), $form_html_id = NULL, $ajax_settings = NULL) { // Get the content of the initial page prior to calling drupalPostForm(), // since drupalPostForm() replaces $this->content. if (isset($path)) { // Avoid sending the wrapper query argument to drupalGet so we can fetch // the form and populate the internal WebTest values. $get_options = $options; unset($get_options['query'][MainContentViewSubscriber::WRAPPER_FORMAT]); $this->drupalGet($path, $get_options); } $content = $this->content; $drupal_settings = $this->drupalSettings; // Provide a default value for the wrapper envelope. $options['query'][MainContentViewSubscriber::WRAPPER_FORMAT] = isset($options['query'][MainContentViewSubscriber::WRAPPER_FORMAT]) ? $options['query'][MainContentViewSubscriber::WRAPPER_FORMAT] : 'drupal_ajax'; // Get the Ajax settings bound to the triggering element. if (!isset($ajax_settings)) { if (is_array($triggering_element)) { $xpath = '//*[@name="' . key($triggering_element) . '" and @value="' . current($triggering_element) . '"]'; } else { $xpath = '//*[@name="' . $triggering_element . '"]'; } if (isset($form_html_id)) { $xpath = '//form[@id="' . $form_html_id . '"]' . $xpath; } $element = $this->xpath($xpath); $element_id = (string) $element[0]['id']; $ajax_settings = $drupal_settings['ajax'][$element_id]; } // Add extra information to the POST data as ajax.js does. $extra_post = array(); if (isset($ajax_settings['submit'])) { foreach ($ajax_settings['submit'] as $key => $value) { $extra_post[$key] = $value; } } $extra_post[AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER] = 1; $extra_post += $this->getAjaxPageStatePostData(); // Now serialize all the $extra_post values, and prepend it with an '&'. $extra_post = '&' . $this->serializePostValues($extra_post); // Unless a particular path is specified, use the one specified by the // Ajax settings. if (!isset($ajax_path)) { if (isset($ajax_settings['url'])) { // In order to allow to set for example the wrapper envelope query // parameter we need to get the system path again. $parsed_url = UrlHelper::parse($ajax_settings['url']); $options['query'] = $parsed_url['query'] + $options['query']; $options += ['fragment' => $parsed_url['fragment']]; // We know that $parsed_url['path'] is already with the base path // attached. $ajax_path = preg_replace('/^' . preg_quote(base_path(), '/') . '/', '', $parsed_url['path']); } } if (empty($ajax_path)) { throw new \Exception('No #ajax path specified.'); } $ajax_path = $this->container->get('unrouted_url_assembler')->assemble('base://' . $ajax_path, $options); // Submit the POST request. $return = Json::decode($this->drupalPostForm(NULL, $edit, array('path' => $ajax_path, 'triggering_element' => $triggering_element), $options, $headers, $form_html_id, $extra_post)); if ($this->assertAjaxHeader) { $this->assertIdentical($this->drupalGetHeader('X-Drupal-Ajax-Token'), '1', 'Ajax response header found.'); } // Change the page content by applying the returned commands. if (!empty($ajax_settings) && !empty($return)) { $this->drupalProcessAjaxResponse($content, $return, $ajax_settings, $drupal_settings); } $verbose = 'AJAX POST request to: ' . $path; $verbose .= '<br />AJAX controller path: ' . $ajax_path; $verbose .= '<hr />Ending URL: ' . $this->getUrl(); $verbose .= '<hr />' . $this->content; $this->verbose($verbose); return $return; }
/** * Tests UrlHelper::parse(). */ function testDrupalParseUrl() { // Relative, absolute, and external URLs, without/with explicit script path, // without/with Drupal path. foreach (array('', '/', 'http://drupal.org/') as $absolute) { foreach (array('', 'index.php/') as $script) { foreach (array('', 'foo/bar') as $path) { $url = $absolute . $script . $path . '?foo=bar&bar=baz&baz#foo'; $expected = array('path' => $absolute . $script . $path, 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''), 'fragment' => 'foo'); $this->assertEqual(UrlHelper::parse($url), $expected, 'URL parsed correctly.'); } } } // Relative URL that is known to confuse parse_url(). $url = 'foo/bar:1'; $result = array('path' => 'foo/bar:1', 'query' => array(), 'fragment' => ''); $this->assertEqual(UrlHelper::parse($url), $result, 'Relative URL parsed correctly.'); // Test that drupal can recognize an absolute URL. Used to prevent attack vectors. $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo'; $this->assertTrue(UrlHelper::isExternal($url), 'Correctly identified an external URL.'); // Test that UrlHelper::parse() does not allow spoofing a URL to force a malicious redirect. $parts = UrlHelper::parse('forged:http://cwe.mitre.org/data/definitions/601.html'); $this->assertFalse(UrlHelper::isValid($parts['path'], TRUE), '\\Drupal\\Component\\Utility\\UrlHelper::isValid() correctly parsed a forged URL.'); }
/** * 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; }
/** * 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']) : ''); }
/** * Tests using entity fields of the link field type. */ public function testLinkItem() { // Create entity. $entity = entity_create('entity_test'); $url = 'http://www.drupal.org?test_param=test_value'; $parsed_url = UrlHelper::parse($url); $title = $this->randomMachineName(); $class = $this->randomMachineName(); $entity->field_test->uri = $parsed_url['path']; $entity->field_test->title = $title; $entity->field_test->first()->get('options')->set('query', $parsed_url['query']); $entity->field_test->first()->get('options')->set('attributes', array('class' => $class)); $entity->name->value = $this->randomMachineName(); $entity->save(); // Verify that the field value is changed. $id = $entity->id(); $entity = entity_load('entity_test', $id); $this->assertTrue($entity->field_test instanceof FieldItemListInterface, 'Field implements interface.'); $this->assertTrue($entity->field_test[0] instanceof FieldItemInterface, 'Field item implements interface.'); $this->assertEqual($entity->field_test->uri, $parsed_url['path']); $this->assertEqual($entity->field_test[0]->uri, $parsed_url['path']); $this->assertEqual($entity->field_test->title, $title); $this->assertEqual($entity->field_test[0]->title, $title); $this->assertEqual($entity->field_test->options['attributes']['class'], $class); $this->assertEqual($entity->field_test->options['query'], $parsed_url['query']); // Update only the entity name property to check if the link field data will // remain intact. $entity->name->value = $this->randomMachineName(); $entity->save(); $id = $entity->id(); $entity = entity_load('entity_test', $id); $this->assertEqual($entity->field_test->uri, $parsed_url['path']); $this->assertEqual($entity->field_test->options['attributes']['class'], $class); $this->assertEqual($entity->field_test->options['query'], $parsed_url['query']); // Verify changing the field value. $new_url = 'http://drupal.org'; $new_title = $this->randomMachineName(); $new_class = $this->randomMachineName(); $entity->field_test->uri = $new_url; $entity->field_test->title = $new_title; $entity->field_test->first()->get('options')->set('query', NULL); $entity->field_test->first()->get('options')->set('attributes', array('class' => $new_class)); $this->assertEqual($entity->field_test->uri, $new_url); $this->assertEqual($entity->field_test->title, $new_title); $this->assertEqual($entity->field_test->options['attributes']['class'], $new_class); $this->assertNull($entity->field_test->options['query']); // Read changed entity and assert changed values. $entity->save(); $entity = entity_load('entity_test', $id); $this->assertEqual($entity->field_test->uri, $new_url); $this->assertEqual($entity->field_test->title, $new_title); $this->assertEqual($entity->field_test->options['attributes']['class'], $new_class); // Test the generateSampleValue() method. $entity = entity_create('entity_test'); $entity->field_test->generateSampleItems(); $this->entityValidateAndSave($entity); }