/** * Gets the list of links used by this field. * * @return array * The links which are used by the render function. */ protected function getLinks() { $links = array(); foreach ($this->options['fields'] as $field) { if (empty($this->view->field[$field]->last_render_text)) { continue; } $title = $this->view->field[$field]->last_render_text; $path = ''; $url = NULL; if (!empty($this->view->field[$field]->options['alter']['path'])) { $path = $this->view->field[$field]->options['alter']['path']; } elseif (!empty($this->view->field[$field]->options['alter']['url']) && $this->view->field[$field]->options['alter']['url'] instanceof UrlObject) { $url = $this->view->field[$field]->options['alter']['url']; } // Make sure that tokens are replaced for this paths as well. $tokens = $this->getRenderTokens(array()); $path = strip_tags(String::decodeEntities($this->viewsTokenReplace($path, $tokens))); $links[$field] = array('url' => $path ? UrlObject::fromUri('internal:/' . $path) : $url, 'title' => $title); if (!empty($this->options['destination'])) { $links[$field]['query'] = drupal_get_destination(); } } return $links; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); if (stristr($text, 'data-caption') !== FALSE) { $dom = Html::load($text); $xpath = new \DOMXPath($dom); foreach ($xpath->query('//*[@data-caption]') as $node) { // Read the data-caption attribute's value, then delete it. $caption = String::checkPlain($node->getAttribute('data-caption')); $node->removeAttribute('data-caption'); // Sanitize caption: decode HTML encoding, limit allowed HTML tags; only // allow inline tags that are allowed by default, plus <br>. $caption = String::decodeEntities($caption); $caption = Xss::filter($caption, array('a', 'em', 'strong', 'cite', 'code', 'br')); // The caption must be non-empty. if (Unicode::strlen($caption) === 0) { continue; } // Given the updated node and caption: re-render it with a caption, but // bubble up the value of the class attribute of the captioned element, // this allows it to collaborate with e.g. the filter_align filter. $classes = $node->getAttribute('class'); $node->removeAttribute('class'); $filter_caption = array('#theme' => 'filter_caption', '#node' => SafeMarkup::set($node->C14N()), '#tag' => $node->tagName, '#caption' => $caption, '#classes' => $classes); $altered_html = drupal_render($filter_caption); // Load the altered HTML into a new DOMDocument and retrieve the element. $updated_node = Html::load($altered_html)->getElementsByTagName('body')->item(0)->childNodes->item(0); // Import the updated node from the new DOMDocument into the original // one, importing also the child nodes of the updated node. $updated_node = $dom->importNode($updated_node, TRUE); // Finally, replace the original image node with the new image node! $node->parentNode->replaceChild($updated_node, $node); } $result->setProcessedText(Html::serialize($dom))->addAssets(array('library' => array('filter/caption'))); } return $result; }
/** * Returns matched labels based on a given search string. * * @param string $target_type * The ID of the target entity type. * @param string $selection_handler * The plugin ID of the entity reference selection handler. * @param array $selection_settings * An array of settings that will be passed to the selection handler. * @param string $string * (optional) The label of the entity to query by. * * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException * Thrown when the current user doesn't have access to the specifies entity. * * @return array * An array of matched entity labels, in the format required by the AJAX * autocomplete API (e.g. array('value' => $value, 'label' => $label)). * * @see \Drupal\system\Controller\EntityAutocompleteController */ public function getMatches($target_type, $selection_handler, $selection_settings, $string = '') { $matches = array(); $options = array('target_type' => $target_type, 'handler' => $selection_handler, 'handler_settings' => $selection_settings); $handler = $this->selectionManager->getInstance($options); if (isset($string)) { // Get an array of matching entities. $match_operator = !empty($selection_settings['match_operator']) ? $selection_settings['match_operator'] : 'CONTAINS'; $entity_labels = $handler->getReferenceableEntities($string, $match_operator, 10); // Loop through the entities and convert them into autocomplete output. foreach ($entity_labels as $values) { foreach ($values as $entity_id => $label) { $key = "{$label} ({$entity_id})"; // Strip things like starting/trailing white spaces, line breaks and // tags. $key = preg_replace('/\\s\\s+/', ' ', str_replace("\n", '', trim(String::decodeEntities(strip_tags($key))))); // Names containing commas or quotes must be wrapped in quotes. $key = Tags::encode($key); $matches[] = array('value' => $key, 'label' => $label); } } } return $matches; }
/** * Asserts that text transformed to lowercase with HTML entities decoded does not contain a given string. * * Otherwise fails the test with a given message, similar to all the * SimpleTest assert* functions. * * Note that this does not remove nulls, new lines, and other character that * could be used to obscure a tag or an attribute name. * * @param $haystack * Text to look in. * @param $needle * Lowercase, plain text to look for. * @param $message * (optional) Message to display if failed. Defaults to an empty string. * @param $group * (optional) The group this message belongs to. Defaults to 'Other'. * @return * TRUE on pass, FALSE on fail. */ function assertNoNormalized($haystack, $needle, $message = '', $group = 'Other') { return $this->assertTrue(strpos(strtolower(String::decodeEntities($haystack)), $needle) === FALSE, $message, $group); }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { parent::submitForm($form, $form_state); /** @var \Drupal\comment\CommentInterface $comment */ $comment = $this->entity; // If the comment was posted by a registered user, assign the author's ID. // @todo Too fragile. Should be prepared and stored in comment_form() // already. $author_name = $comment->getAuthorName(); if (!$comment->is_anonymous && !empty($author_name) && ($account = user_load_by_name($author_name))) { $comment->setOwner($account); } // If the comment was posted by an anonymous user and no author name was // required, use "Anonymous" by default. if ($comment->is_anonymous && (!isset($author_name) || $author_name === '')) { $comment->setAuthorName($this->config('user.settings')->get('anonymous')); } // Validate the comment's subject. If not specified, extract from comment // body. if (trim($comment->getSubject()) == '') { // The body may be in any format, so: // 1) Filter it into HTML // 2) Strip out all HTML tags // 3) Convert entities back to plain-text. $comment_text = $comment->comment_body->processed; $comment->setSubject(Unicode::truncate(trim(String::decodeEntities(strip_tags($comment_text))), 29, TRUE)); // Edge cases where the comment body is populated only by HTML tags will // require a default subject. if ($comment->getSubject() == '') { $comment->setSubject($this->t('(No subject)')); } } return $comment; }
/** * Sanitizes the HTML select element's options. * * The function is recursive to support optgroups. */ protected function prepareFilterSelectOptions(&$options) { foreach ($options as $value => $label) { // Recurse for optgroups. if (is_array($label)) { $this->prepareFilterSelectOptions($options[$value]); } elseif (is_object($label) && isset($label->option)) { $this->prepareFilterSelectOptions($options[$value]->option); } else { // Cast the label to a string since it can be an object. // @see \Drupal\Core\StringTranslation\TranslationWrapper $options[$value] = strip_tags(UtilityString::decodeEntities((string) $label)); } } }
/** * Checks for meta refresh tag and if found call drupalGet() recursively. * * This function looks for the http-equiv attribute to be set to "Refresh" and * is case-sensitive. * * @return * Either the new page content or FALSE. */ protected function checkForMetaRefresh() { if (strpos($this->getRawContent(), '<meta ') && $this->parse()) { $refresh = $this->xpath('//meta[@http-equiv="Refresh"]'); if (!empty($refresh)) { // Parse the content attribute of the meta tag for the format: // "[delay]: URL=[page_to_redirect_to]". if (preg_match('/\\d+;\\s*URL=(?<url>.*)/i', $refresh[0]['content'], $match)) { return $this->drupalGet($this->getAbsoluteUrl(String::decodeEntities($match['url']))); } } } return FALSE; }
/** * Build all the arguments. */ protected function _buildArguments() { // Initially, we want to build sorts and fields. This can change, though, // if we get a summary view. if (empty($this->argument)) { return TRUE; } // build arguments. $position = -1; $substitutions = array(); $status = TRUE; // Get the title. $title = $this->display_handler->getOption('title'); // Iterate through each argument and process. foreach ($this->argument as $id => $arg) { $position++; $argument = $this->argument[$id]; if ($argument->broken()) { continue; } $argument->setRelationship(); $arg = isset($this->args[$position]) ? $this->args[$position] : NULL; $argument->position = $position; if (isset($arg) || $argument->hasDefaultArgument()) { if (!isset($arg)) { $arg = $argument->getDefaultArgument(); // make sure default args get put back. if (isset($arg)) { $this->args[$position] = $arg; } // remember that this argument was computed, not passed on the URL. $argument->is_default = TRUE; } // Set the argument, which will also validate that the argument can be set. if (!$argument->setArgument($arg)) { $status = $argument->validateFail($arg); break; } if ($argument->isException()) { $arg_title = $argument->exceptionTitle(); } else { $arg_title = $argument->getTitle(); $argument->query($this->display_handler->useGroupBy()); } // Add this argument's substitution $substitutions['%' . ($position + 1)] = $arg_title; $substitutions['!' . ($position + 1)] = strip_tags(String::decodeEntities($arg)); // Test to see if we should use this argument's title if (!empty($argument->options['title_enable']) && !empty($argument->options['title'])) { $title = $argument->options['title']; } } else { // determine default condition and handle. $status = $argument->defaultAction(); break; } // Be safe with references and loops: unset($argument); } // set the title in the build info. if (!empty($title)) { $this->build_info['title'] = $title; } // Store the arguments for later use. $this->build_info['substitutions'] = $substitutions; return $status; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); if (stristr($text, 'data-caption') !== FALSE || stristr($text, 'data-align') !== FALSE) { $caption_found = FALSE; $dom = Html::load($text); $xpath = new \DOMXPath($dom); foreach ($xpath->query('//*[@data-caption or @data-align]') as $node) { $caption = NULL; $align = NULL; // Retrieve, then remove the data-caption and data-align attributes. if ($node->hasAttribute('data-caption')) { $caption = String::checkPlain($node->getAttribute('data-caption')); $node->removeAttribute('data-caption'); // Sanitize caption: decode HTML encoding, limit allowed HTML tags; // only allow inline tags that are allowed by default, plus <br>. $caption = String::decodeEntities($caption); $caption = Xss::filter($caption, array('a', 'em', 'strong', 'cite', 'code', 'br')); // The caption must be non-empty. if (Unicode::strlen($caption) === 0) { $caption = NULL; } } if ($node->hasAttribute('data-align')) { $align = $node->getAttribute('data-align'); $node->removeAttribute('data-align'); // Only allow 3 values: 'left', 'center' and 'right'. if (!in_array($align, array('left', 'center', 'right'))) { $align = NULL; } } // Don't transform the HTML if there isn't a caption after validation. if ($caption === NULL) { // If there is a valid alignment, then transform the data-align // attribute to a corresponding alignment class. if ($align !== NULL) { $classes = $node->getAttribute('class'); $classes = strlen($classes) > 0 ? explode(' ', $classes) : array(); $classes[] = 'align-' . $align; $node->setAttribute('class', implode(' ', $classes)); } continue; } else { $caption_found = TRUE; } // Given the updated node, caption and alignment: re-render it with a // caption. $filter_caption = array('#theme' => 'filter_caption', '#node' => SafeMarkup::set($node->C14N()), '#tag' => $node->tagName, '#caption' => $caption, '#align' => $align); $altered_html = drupal_render($filter_caption); // Load the altered HTML into a new DOMDocument and retrieve the element. $updated_node = Html::load($altered_html)->getElementsByTagName('body')->item(0)->childNodes->item(0); // Import the updated node from the new DOMDocument into the original // one, importing also the child nodes of the updated node. $updated_node = $dom->importNode($updated_node, TRUE); // Finally, replace the original image node with the new image node! $node->parentNode->replaceChild($updated_node, $node); } $result->setProcessedText(Html::serialize($dom)); if ($caption_found) { $result->addAssets(array('library' => array('filter/caption'))); } } return $result; }
/** * {@inheritdoc} */ public function getArgumentsTokens() { $tokens = array(); if (!empty($this->view->build_info['substitutions'])) { $tokens = $this->view->build_info['substitutions']; } // Add tokens for every argument (contextual filter) and path arg. $handlers = count($this->view->display_handler->getHandlers('argument')); for ($count = 1; $count <= $handlers; $count++) { if (!isset($tokens["%{$count}"])) { $tokens["%{$count}"] = ''; } // 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(). $tokens["!{$count}"] = isset($this->view->args[$count - 1]) ? strip_tags(String::decodeEntities($this->view->args[$count - 1])) : ''; } return $tokens; }
/** * Transforms an HTML string into plain text, preserving its structure. * * The output will be suitable for use as 'format=flowed; delsp=yes' text * (RFC 3676) and can be passed directly to MailManagerInterface::mail() for sending. * * We deliberately use LF rather than CRLF, see MailManagerInterface::mail(). * * This function provides suitable alternatives for the following tags: * <a> <em> <i> <strong> <b> <br> <p> <blockquote> <ul> <ol> <li> <dl> <dt> * <dd> <h1> <h2> <h3> <h4> <h5> <h6> <hr> * * @param string $string * The string to be transformed. * @param array $allowed_tags * (optional) If supplied, a list of tags that will be transformed. If * omitted, all supported tags are transformed. * * @return string * The transformed string. */ public static function htmlToText($string, $allowed_tags = NULL) { // Cache list of supported tags. if (empty(static::$supportedTags)) { static::$supportedTags = array('a', 'em', 'i', 'strong', 'b', 'br', 'p', 'blockquote', 'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr'); } // Make sure only supported tags are kept. $allowed_tags = isset($allowed_tags) ? array_intersect(static::$supportedTags, $allowed_tags) : static::$supportedTags; // Make sure tags, entities and attributes are well-formed and properly // nested. $string = Html::normalize(Xss::filter($string, $allowed_tags)); // Apply inline styles. $string = preg_replace('!</?(em|i)((?> +)[^>]*)?>!i', '/', $string); $string = preg_replace('!</?(strong|b)((?> +)[^>]*)?>!i', '*', $string); // Replace inline <a> tags with the text of link and a footnote. // 'See <a href="http://drupal.org">the Drupal site</a>' becomes // 'See the Drupal site [1]' with the URL included as a footnote. static::htmlToMailUrls(NULL, TRUE); $pattern = '@(<a[^>]+?href="([^"]*)"[^>]*?>(.+?)</a>)@i'; $string = preg_replace_callback($pattern, 'static::htmlToMailUrls', $string); $urls = static::htmlToMailUrls(); $footnotes = ''; if (count($urls)) { $footnotes .= "\n"; for ($i = 0, $max = count($urls); $i < $max; $i++) { $footnotes .= '[' . ($i + 1) . '] ' . $urls[$i] . "\n"; } } // Split tags from text. $split = preg_split('/<([^>]+?)>/', $string, -1, PREG_SPLIT_DELIM_CAPTURE); // Note: PHP ensures the array consists of alternating delimiters and // literals and begins and ends with a literal (inserting $null as // required). // Odd/even counter (tag or no tag). $tag = FALSE; // Case conversion function. $casing = NULL; $output = ''; // All current indentation string chunks. $indent = array(); // Array of counters for opened lists. $lists = array(); foreach ($split as $value) { // Holds a string ready to be formatted and output. $chunk = NULL; // Process HTML tags (but don't output any literally). if ($tag) { list($tagname) = explode(' ', strtolower($value), 2); switch ($tagname) { // List counters. case 'ul': array_unshift($lists, '*'); break; case 'ol': array_unshift($lists, 1); break; case '/ul': case '/ol': array_shift($lists); // Ensure blank new-line. $chunk = ''; break; // Quotation/list markers, non-fancy headers. // Quotation/list markers, non-fancy headers. case 'blockquote': // Format=flowed indentation cannot be mixed with lists. $indent[] = count($lists) ? ' "' : '>'; break; case 'li': $indent[] = isset($lists[0]) && is_numeric($lists[0]) ? ' ' . $lists[0]++ . ') ' : ' * '; break; case 'dd': $indent[] = ' '; break; case 'h3': $indent[] = '.... '; break; case 'h4': $indent[] = '.. '; break; case '/blockquote': if (count($lists)) { // Append closing quote for inline quotes (immediately). $output = rtrim($output, "> \n") . "\"\n"; // Ensure blank new-line. $chunk = ''; } // Fall-through. // Fall-through. case '/li': case '/dd': array_pop($indent); break; case '/h3': case '/h4': array_pop($indent); case '/h5': case '/h6': // Ensure blank new-line. $chunk = ''; break; // Fancy headers. // Fancy headers. case 'h1': $indent[] = '======== '; $casing = '\\Drupal\\Component\\Utility\\Unicode::strtoupper'; break; case 'h2': $indent[] = '-------- '; $casing = '\\Drupal\\Component\\Utility\\Unicode::strtoupper'; break; case '/h1': case '/h2': $casing = NULL; // Pad the line with dashes. $output = static::htmlToTextPad($output, $tagname == '/h1' ? '=' : '-', ' '); array_pop($indent); // Ensure blank new-line. $chunk = ''; break; // Horizontal rulers. // Horizontal rulers. case 'hr': // Insert immediately. $output .= static::wrapMail('', implode('', $indent)) . "\n"; $output = static::htmlToTextPad($output, '-'); break; // Paragraphs and definition lists. // Paragraphs and definition lists. case '/p': case '/dl': // Ensure blank new-line. $chunk = ''; break; } } else { // Convert inline HTML text to plain text; not removing line-breaks or // white-space, since that breaks newlines when sanitizing plain-text. $value = trim(String::decodeEntities($value)); if (Unicode::strlen($value)) { $chunk = $value; } } // See if there is something waiting to be output. if (isset($chunk)) { // Apply any necessary case conversion. if (isset($casing)) { $chunk = call_user_func($casing, $chunk); } $line_endings = Settings::get('mail_line_endings', PHP_EOL); // Format it and apply the current indentation. $output .= static::wrapMail($chunk, implode('', $indent)) . $line_endings; // Remove non-quotation markers from indentation. $indent = array_map('\\Drupal\\Core\\Mail\\MailFormatHelper::htmlToTextClean', $indent); } $tag = !$tag; } return $output . $footnotes; }
/** * {@inheritdoc} */ protected function sanitizeLabel(&$label) { // Select form inputs allow unencoded HTML entities, but no HTML tags. $label = String::decodeEntities(strip_tags($label)); }
/** * Overrides \Drupal\views\Plugin\views\field\FieldPluginBase::render(). * * Renders the contextual fields. * * @param \Drupal\views\ResultRow $values * The values retrieved from a single row of a view's query result. * * @see contextual_preprocess() * @see contextual_contextual_links_view_alter() */ public function render(ResultRow $values) { $links = array(); foreach ($this->options['fields'] as $field) { $rendered_field = $this->view->style_plugin->getField($values->index, $field); if (empty($rendered_field)) { continue; } $title = $this->view->field[$field]->last_render_text; $path = ''; if (!empty($this->view->field[$field]->options['alter']['path'])) { $path = $this->view->field[$field]->options['alter']['path']; } elseif (!empty($this->view->field[$field]->options['alter']['url']) && $this->view->field[$field]->options['alter']['url'] instanceof Url) { $path = $this->view->field[$field]->options['alter']['url']->toString(); } if (!empty($title) && !empty($path)) { // Make sure that tokens are replaced for this paths as well. $tokens = $this->getRenderTokens(array()); $path = strip_tags(String::decodeEntities(strtr($path, $tokens))); $links[$field] = array('href' => $path, 'title' => $title); if (!empty($this->options['destination'])) { $links[$field]['query'] = drupal_get_destination(); } } } // Renders a contextual links placeholder. if (!empty($links)) { $contextual_links = array('contextual' => array('', array(), array('contextual-views-field-links' => UrlHelper::encodePath(Json::encode($links))))); $element = array('#type' => 'contextual_links_placeholder', '#id' => _contextual_links_to_id($contextual_links)); return drupal_render($element); } else { return ''; } }
/** * Recursive function to add replacements for nested query string parameters. * * E.g. if you pass in the following array: * array( * 'foo' => array( * 'a' => 'value', * 'b' => 'value', * ), * 'bar' => array( * 'a' => 'value', * 'b' => array( * 'c' => value, * ), * ), * ); * * Would yield the following array of tokens: * array( * '%foo_a' => 'value' * '%foo_b' => 'value' * '%bar_a' => 'value' * '%bar_b_c' => 'value' * ); * * @param $array * An array of values. * * @param $parent_keys * An array of parent keys. This will represent the array depth. * * @return * An array of available tokens, with nested keys representative of the array structure. */ protected function getTokenValuesRecursive(array $array, array $parent_keys = array()) { $tokens = array(); foreach ($array as $param => $val) { if (is_array($val)) { // Copy parent_keys array, so we don't affect other elements of this // iteration. $child_parent_keys = $parent_keys; $child_parent_keys[] = $param; // Get the child tokens. $child_tokens = $this->getTokenValuesRecursive($val, $child_parent_keys); // Add them to the current tokens array. $tokens += $child_tokens; } else { // Create a token key based on array element structure. $token_string = !empty($parent_keys) ? implode('_', $parent_keys) . '_' . $param : $param; $tokens['%' . $token_string] = strip_tags(String::decodeEntities($val)); } } return $tokens; }
/** * Tests rewriting the output to a link. */ public function testAlterUrl() { $view = Views::getView('test_view'); $view->setDisplay(); $view->initHandlers(); $this->executeView($view); $row = $view->result[0]; $id_field = $view->field['id']; // Setup the general settings required to build a link. $id_field->options['alter']['make_link'] = TRUE; $id_field->options['alter']['path'] = $path = $this->randomMachineName(); // Tests that the suffix/prefix appears on the output. $id_field->options['alter']['prefix'] = $prefix = $this->randomMachineName(); $id_field->options['alter']['suffix'] = $suffix = $this->randomMachineName(); $output = $id_field->theme($row); $this->assertSubString($output, $prefix); $this->assertSubString($output, $suffix); unset($id_field->options['alter']['prefix']); unset($id_field->options['alter']['suffix']); $output = $id_field->theme($row); $this->assertSubString($output, $path, 'Make sure that the path is part of the output'); // Some generic test code adapted from the UrlTest class, which tests // mostly the different options for the path. global $base_url, $script_path; foreach (array(FALSE, TRUE) as $absolute) { $alter =& $id_field->options['alter']; $alter['path'] = 'node/123'; $expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['absolute' => $absolute]); $alter['absolute'] = $absolute; $result = $id_field->theme($row); $this->assertSubString($result, $expected_result); $expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['fragment' => 'foo', 'absolute' => $absolute]); $alter['path'] = 'node/123#foo'; $result = $id_field->theme($row); $this->assertSubString($result, $expected_result); $expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['query' => ['foo' => NULL], 'absolute' => $absolute]); $alter['path'] = 'node/123?foo'; $result = $id_field->theme($row); $this->assertSubString($result, $expected_result); $expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['query' => ['foo' => 'bar', 'bar' => 'baz'], 'absolute' => $absolute]); $alter['path'] = 'node/123?foo=bar&bar=baz'; $result = $id_field->theme($row); $this->assertSubString(String::decodeEntities($result), String::decodeEntities($expected_result)); // @todo The route-based URL generator strips out NULL attributes. // $expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['query' => ['foo' => NULL], 'fragment' => 'bar', 'absolute' => $absolute]); $expected_result = \Drupal::urlGenerator()->generateFromPath('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute)); $alter['path'] = 'node/123?foo#bar'; $result = $id_field->theme($row); $this->assertSubString(String::decodeEntities($result), String::decodeEntities($expected_result)); $expected_result = \Drupal::url('<front>', [], ['absolute' => $absolute]); $alter['path'] = '<front>'; $result = $id_field->theme($row); $this->assertSubString($result, $expected_result); } // Tests the replace spaces with dashes feature. $id_field->options['alter']['replace_spaces'] = TRUE; $id_field->options['alter']['path'] = $path = $this->randomMachineName() . ' ' . $this->randomMachineName(); $output = $id_field->theme($row); $this->assertSubString($output, str_replace(' ', '-', $path)); $id_field->options['alter']['replace_spaces'] = FALSE; $output = $id_field->theme($row); // The url has a space in it, so to check we have to decode the url output. $this->assertSubString(urldecode($output), $path); // Tests the external flag. // Switch on the external flag should output an external url as well. $id_field->options['alter']['external'] = TRUE; $id_field->options['alter']['path'] = $path = 'drupal.org'; $output = $id_field->theme($row); $this->assertSubString($output, 'http://drupal.org'); // Setup a not external url, which shouldn't lead to an external url. $id_field->options['alter']['external'] = FALSE; $id_field->options['alter']['path'] = $path = 'drupal.org'; $output = $id_field->theme($row); $this->assertNotSubString($output, 'http://drupal.org'); // Tests the transforming of the case setting. $id_field->options['alter']['path'] = $path = $this->randomMachineName(); $id_field->options['alter']['path_case'] = 'none'; $output = $id_field->theme($row); $this->assertSubString($output, $path); // Switch to uppercase and lowercase. $id_field->options['alter']['path_case'] = 'upper'; $output = $id_field->theme($row); $this->assertSubString($output, strtoupper($path)); $id_field->options['alter']['path_case'] = 'lower'; $output = $id_field->theme($row); $this->assertSubString($output, strtolower($path)); // Switch to ucfirst and ucwords. $id_field->options['alter']['path_case'] = 'ucfirst'; $id_field->options['alter']['path'] = 'drupal has a great community'; $output = $id_field->theme($row); $this->assertSubString($output, UrlHelper::encodePath('Drupal has a great community')); $id_field->options['alter']['path_case'] = 'ucwords'; $output = $id_field->theme($row); $this->assertSubString($output, UrlHelper::encodePath('Drupal Has A Great Community')); unset($id_field->options['alter']['path_case']); // Tests the linkclass setting and see whether it actuall exists in the output. $id_field->options['alter']['link_class'] = $class = $this->randomMachineName(); $output = $id_field->theme($row); $elements = $this->xpathContent($output, '//a[contains(@class, :class)]', array(':class' => $class)); $this->assertTrue($elements); // @fixme link_class, alt, rel cannot be unset, which should be fixed. $id_field->options['alter']['link_class'] = ''; // Tests the alt setting. $id_field->options['alter']['alt'] = $rel = $this->randomMachineName(); $output = $id_field->theme($row); $elements = $this->xpathContent($output, '//a[contains(@title, :alt)]', array(':alt' => $rel)); $this->assertTrue($elements); $id_field->options['alter']['alt'] = ''; // Tests the rel setting. $id_field->options['alter']['rel'] = $rel = $this->randomMachineName(); $output = $id_field->theme($row); $elements = $this->xpathContent($output, '//a[contains(@rel, :rel)]', array(':rel' => $rel)); $this->assertTrue($elements); $id_field->options['alter']['rel'] = ''; // Tests the target setting. $id_field->options['alter']['target'] = $target = $this->randomMachineName(); $output = $id_field->theme($row); $elements = $this->xpathContent($output, '//a[contains(@target, :target)]', array(':target' => $target)); $this->assertTrue($elements); unset($id_field->options['alter']['target']); }
/** * Tests the failed search text, and various other text on the search page. */ function testSearchText() { $this->drupalLogin($this->searchingUser); $this->drupalGet('search/node'); $this->assertText(t('Enter your keywords')); $this->assertText(t('Search')); $this->assertTitle(t('Search') . ' | Drupal', 'Search page title is correct'); $edit = array(); $search_terms = 'bike shed ' . $this->randomMachineName(); $edit['keys'] = $search_terms; $this->drupalPostForm('search/node', $edit, t('Search')); $this->assertText('search yielded no results'); $this->assertText(t('Search')); $title_source = 'Search for @keywords | Drupal'; $this->assertTitle(t($title_source, array('@keywords' => Unicode::truncate($search_terms, 60, TRUE, TRUE))), 'Search page title is correct'); $this->assertNoText('Node', 'Erroneous tab and breadcrumb text is not present'); $this->assertNoText(t('Node'), 'Erroneous translated tab and breadcrumb text is not present'); $this->assertText(t('Content'), 'Tab and breadcrumb text is present'); $this->clickLink('Search help'); $this->assertText('Search help', 'Correct title is on search help page'); $this->assertText('Use upper-case OR to get more results', 'Correct text is on content search help page'); // Search for a longer text, and see that it is in the title, truncated. $edit = array(); $search_terms = 'Every word is like an unnecessary stain on silence and nothingness.'; $edit['keys'] = $search_terms; $this->drupalPostForm('search/node', $edit, t('Search')); $this->assertTitle(t($title_source, array('@keywords' => 'Every word is like an unnecessary stain on silence and…')), 'Search page title is correct'); // Search for a string with a lot of special characters. $search_terms = 'Hear nothing > "see nothing" `feel' . " '1982."; $edit['keys'] = $search_terms; $this->drupalPostForm('search/node', $edit, t('Search')); $actual_title = (string) current($this->xpath('//title')); $this->assertEqual($actual_title, String::decodeEntities(t($title_source, array('@keywords' => Unicode::truncate($search_terms, 60, TRUE, TRUE)))), 'Search page title is correct'); $edit['keys'] = $this->searchingUser->getUsername(); $this->drupalPostForm('search/user', $edit, t('Search')); $this->assertText(t('Search')); $this->assertTitle(t($title_source, array('@keywords' => Unicode::truncate($this->searchingUser->getUsername(), 60, TRUE, TRUE)))); $this->clickLink('Search help'); $this->assertText('Search help', 'Correct title is on search help page'); $this->assertText('user names and partial user names', 'Correct text is on user search help page'); // Test that search keywords containing slashes are correctly loaded // from the GET params and displayed in the search form. $arg = $this->randomMachineName() . '/' . $this->randomMachineName(); $this->drupalGet('search/node', array('query' => array('keys' => $arg))); $input = $this->xpath("//input[@id='edit-keys' and @value='{$arg}']"); $this->assertFalse(empty($input), 'Search keys with a / are correctly set as the default value in the search box.'); // Test a search input exceeding the limit of AND/OR combinations to test // the Denial-of-Service protection. $limit = $this->config('search.settings')->get('and_or_limit'); $keys = array(); for ($i = 0; $i < $limit + 1; $i++) { // Use a key of 4 characters to ensure we never generate 'AND' or 'OR'. $keys[] = $this->randomMachineName(4); if ($i % 2 == 0) { $keys[] = 'OR'; } } $edit['keys'] = implode(' ', $keys); $this->drupalPostForm('search/node', $edit, t('Search')); $this->assertRaw(t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', array('@count' => $limit))); // Test that a search on Node or User with no keywords entered generates // the "Please enter some keywords" message. $this->drupalPostForm('search/node', array(), t('Search')); $this->assertText(t('Please enter some keywords'), 'With no keywords entered, message is displayed on node page'); $this->drupalPostForm('search/user', array(), t('Search')); $this->assertText(t('Please enter some keywords'), 'With no keywords entered, message is displayed on user page'); // Make sure the "Please enter some keywords" message is NOT displayed if // you use "or" words or phrases in Advanced Search. $this->drupalPostForm('search/node', array('or' => $this->randomMachineName() . ' ' . $this->randomMachineName()), t('Advanced search')); $this->assertNoText(t('Please enter some keywords'), 'With advanced OR keywords entered, no keywords message is not displayed on node page'); $this->drupalPostForm('search/node', array('phrase' => '"' . $this->randomMachineName() . '" "' . $this->randomMachineName() . '"'), t('Advanced search')); $this->assertNoText(t('Please enter some keywords'), 'With advanced phrase entered, no keywords message is not displayed on node page'); // Verify that if you search for a too-short keyword, you get the right // message, and that if after that you search for a longer keyword, you // do not still see the message. $this->drupalPostForm('search/node', array('keys' => $this->randomMachineName(1)), t('Search')); $this->assertText('You must include at least one positive keyword', 'Keyword message is displayed when searching for short word'); $this->assertNoText(t('Please enter some keywords'), 'With short word entered, no keywords message is not displayed'); $this->drupalPostForm(NULL, array('keys' => $this->randomMachineName()), t('Search')); $this->assertNoText('You must include at least one positive keyword', 'Keyword message is not displayed when searching for long word after short word search'); // Test that if you search for a URL with .. in it, you still end up at // the search page. See issue https://drupal.org/node/890058. $this->drupalPostForm('search/node', array('keys' => '../../admin'), t('Search')); $this->assertResponse(200, 'Searching for ../../admin with non-admin user does not lead to a 403 error'); $this->assertText('no results', 'Searching for ../../admin with non-admin user gives you a no search results page'); // Test that if you search for a URL starting with "./", you still end up // at the search page. See issue https://drupal.org/node/1421560. $this->drupalPostForm('search/node', array('keys' => '.something'), t('Search')); $this->assertResponse(200, 'Searching for .something does not lead to a 403 error'); $this->assertText('no results', 'Searching for .something gives you a no search results page'); }
/** * {@inheritdoc} */ public function buildEntity(array $form, FormStateInterface $form_state) { /** @var \Drupal\comment\CommentInterface $comment */ $comment = parent::buildEntity($form, $form_state); if (!$form_state->isValueEmpty('date') && $form_state->getValue('date') instanceof DrupalDateTime) { $comment->setCreatedTime($form_state->getValue('date')->getTimestamp()); } else { $comment->setCreatedTime(REQUEST_TIME); } $author_name = $form_state->getValue('name'); if (!$this->currentUser->isAnonymous()) { // Assign the owner based on the given user name - none means anonymous. $accounts = $this->entityManager->getStorage('user')->loadByProperties(array('name' => $author_name)); $account = reset($accounts); $uid = $account ? $account->id() : 0; $comment->setOwnerId($uid); } // If the comment was posted by an anonymous user and no author name was // required, use "Anonymous" by default. if ($comment->getOwnerId() === 0 && (!isset($author_name) || $author_name === '')) { $comment->setAuthorName($this->config('user.settings')->get('anonymous')); } // Validate the comment's subject. If not specified, extract from comment // body. if (trim($comment->getSubject()) == '') { // The body may be in any format, so: // 1) Filter it into HTML // 2) Strip out all HTML tags // 3) Convert entities back to plain-text. $comment_text = $comment->comment_body->processed; $comment->setSubject(Unicode::truncate(trim(String::decodeEntities(strip_tags($comment_text))), 29, TRUE)); // Edge cases where the comment body is populated only by HTML tags will // require a default subject. if ($comment->getSubject() == '') { $comment->setSubject($this->t('(No subject)')); } } return $comment; }