/**
  * Handles the response for inline entity form autocompletion.
  *
  * @return \Symfony\Component\HttpFoundation\JsonResponse
  */
 public function autocomplete($entity_type_id, $field_name, $bundle, Request $request)
 {
     $string = $request->query->get('q');
     $fields = $this->entityManager->getFieldDefinitions($entity_type_id, $bundle);
     $widget = $this->entityManager->getStorage('entity_form_display')->load($entity_type_id . '.' . $bundle . '.default')->getComponent($field_name);
     // The path was passed invalid parameters, or the string is empty.
     // strlen() is used instead of empty() since '0' is a valid value.
     if (!isset($fields[$field_name]) || !$widget || !strlen($string)) {
         throw new AccessDeniedHttpException();
     }
     $field = $fields[$field_name];
     $results = array();
     if ($field->getType() == 'entity_reference') {
         /** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface $handler */
         $handler = $this->selectionManager->getSelectionHandler($field);
         $entity_labels = $handler->getReferenceableEntities($string, $widget['settings']['match_operator'], 10);
         foreach ($entity_labels as $bundle => $labels) {
             // Loop through each entity type, and autocomplete with its titles.
             foreach ($labels as $entity_id => $label) {
                 // entityreference has already check_plain-ed the title.
                 $results[] = t('!label (!entity_id)', array('!label' => $label, '!entity_id' => $entity_id));
             }
         }
     }
     $matches = array();
     foreach ($results as $result) {
         // Strip things like starting/trailing white spaces, line breaks and tags.
         $key = preg_replace('/\\s\\s+/', ' ', str_replace("\n", '', trim(Html::decodeEntities(strip_tags($result)))));
         $matches[] = ['value' => $key, 'label' => '<div class="reference-autocomplete">' . $result . '</div>'];
     }
     return new JsonResponse($matches);
 }
示例#2
0
文件: Links.php 项目: ddrozdik/dmaps
 /**
  * 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(Html::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::destination()->getAsArray();
         }
     }
     return $links;
 }
示例#3
0
 /**
  * {@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 = Html::escape($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 = Html::decodeEntities($caption);
             $caption = FilteredMarkup::create(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.
             $tag = $node->tagName;
             $classes = $node->getAttribute('class');
             $node->removeAttribute('class');
             $node = $node->parentNode->tagName === 'a' ? $node->parentNode : $node;
             $filter_caption = array('#theme' => 'filter_caption', '#node' => FilteredMarkup::create($node->C14N()), '#tag' => $tag, '#caption' => $caption, '#classes' => $classes);
             $altered_html = drupal_render($filter_caption);
             // Load the altered HTML into a new DOMDocument and retrieve the element.
             $updated_nodes = Html::load($altered_html)->getElementsByTagName('body')->item(0)->childNodes;
             foreach ($updated_nodes as $updated_node) {
                 // 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);
                 $node->parentNode->insertBefore($updated_node, $node);
             }
             // Finally, remove the original data-caption node.
             $node->parentNode->removeChild($node);
         }
         $result->setProcessedText(Html::serialize($dom))->addAttachments(array('library' => array('filter/caption')));
     }
     return $result;
 }
示例#4
0
 /**
  * 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(Html::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;
 }
示例#5
0
 /**
  * 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(Html::decodeEntities($val));
         }
     }
     return $tokens;
 }
示例#6
0
 protected function getAllEvents(DrupalStyle $io, $eventType, $eventSeverity, $userId, $asc, $offset, $limit)
 {
     $connection = $this->getDrupalService('database');
     $dateFormatter = $this->getDrupalService('date.formatter');
     $userStorage = $this->getDrupalService('entity_type.manager')->getStorage('user');
     $severity = RfcLogLevel::getLevels();
     $query = $connection->select('watchdog', 'w');
     $query->fields('w', ['wid', 'uid', 'severity', 'type', 'timestamp', 'message', 'variables']);
     if ($eventType) {
         $query->condition('type', $eventType);
     }
     if ($eventSeverity) {
         if (!in_array($eventSeverity, $severity)) {
             $io->error(sprintf($this->trans('commands.database.log.debug.messages.invalid-severity'), $eventSeverity));
             return false;
         }
         $query->condition('severity', array_search($eventSeverity, $severity));
     }
     if ($userId) {
         $query->condition('uid', $userId);
     }
     if ($asc) {
         $query->orderBy('wid', 'ASC');
     } else {
         $query->orderBy('wid', 'DESC');
     }
     if ($limit) {
         $query->range($offset, $limit);
     }
     $result = $query->execute();
     $tableHeader = [$this->trans('commands.database.log.debug.messages.event-id'), $this->trans('commands.database.log.debug.messages.type'), $this->trans('commands.database.log.debug.messages.date'), $this->trans('commands.database.log.debug.messages.message'), $this->trans('commands.database.log.debug.messages.user'), $this->trans('commands.database.log.debug.messages.severity')];
     $tableRows = [];
     foreach ($result as $dblog) {
         $user = $userStorage->load($dblog->uid);
         $tableRows[] = [$dblog->wid, $dblog->type, $dateFormatter->format($dblog->timestamp, 'short'), Unicode::truncate(Html::decodeEntities(strip_tags($this->formatMessage($dblog))), 56, true, true), $user->getUsername() . ' (' . $user->id() . ')', $severity[$dblog->severity]];
     }
     $io->table($tableHeader, $tableRows);
     return true;
 }
示例#7
0
 /**
  * Determines if a given button triggered the form submission.
  *
  * This detects button controls that trigger a form submission by being
  * clicked and having the click processed by the browser rather than being
  * captured by JavaScript. Essentially, it detects if the button's name and
  * value are part of the POST data, but with extra code to deal with the
  * convoluted way in which browsers submit data for image button clicks.
  *
  * This does not detect button clicks processed by Ajax (that is done in
  * self::elementTriggeredScriptedSubmission()) and it does not detect form
  * submissions from Internet Explorer in response to an ENTER key pressed in a
  * textfield (self::doBuildForm() has extra code for that).
  *
  * Because this function contains only part of the logic needed to determine
  * $form_state->getTriggeringElement(), it should not be called from anywhere
  * other than within the Form API. Form validation and submit handlers needing
  * to know which button was clicked should get that information from
  * $form_state->getTriggeringElement().
  */
 protected function buttonWasClicked($element, FormStateInterface &$form_state)
 {
     // First detect normal 'vanilla' button clicks. Traditionally, all standard
     // buttons on a form share the same name (usually 'op'), and the specific
     // return value is used to determine which was clicked. This ONLY works as
     // long as $form['#name'] puts the value at the top level of the tree of
     // \Drupal::request()->request data.
     $input = $form_state->getUserInput();
     // The input value attribute is treated as CDATA by browsers. This means
     // that they replace character entities with characters. Therefore, we need
     // to decode the value in $element['#value']. For more details see
     // http://www.w3.org/TR/html401/types.html#type-cdata.
     if (isset($input[$element['#name']]) && $input[$element['#name']] == Html::decodeEntities($element['#value'])) {
         return TRUE;
     } elseif (!empty($element['#has_garbage_value']) && isset($element['#value']) && $element['#value'] !== '') {
         return TRUE;
     }
     return FALSE;
 }
示例#8
0
    /**
     * Sends BigPipe placeholders' replacements as embedded AJAX responses.
     *
     * @param array $placeholders
     *   Associative array; the BigPipe placeholders. Keys are the BigPipe
     *   selectors.
     * @param array $placeholder_order
     *   Indexed array; the order in which the BigPipe placeholders must be sent.
     *   Values are the BigPipe selectors. (These values correspond to keys in
     *   $placeholders.)
     * @param \Drupal\Core\Asset\AttachedAssetsInterface $cumulative_assets
     *   The cumulative assets sent so far; to be updated while rendering BigPipe
     *   placeholders.
     */
    protected function sendPlaceholders(array $placeholders, array $placeholder_order, AttachedAssetsInterface $cumulative_assets)
    {
        // Return early if there are no BigPipe placeholders to send.
        if (empty($placeholders)) {
            return;
        }
        // Send a container and the start signal.
        print "\n";
        print '<script type="application/json" data-big-pipe-event="start"></script>' . "\n";
        flush();
        // A BigPipe response consists of a HTML response plus multiple embedded
        // AJAX responses. To process the attachments of those AJAX responses, we
        // need a fake request that is identical to the master request, but with
        // one change: it must have the right Accept header, otherwise the work-
        // around for a bug in IE9 will cause not JSON, but <textarea>-wrapped JSON
        // to be returned.
        // @see \Drupal\Core\EventSubscriber\AjaxResponseSubscriber::onResponse()
        $fake_request = $this->requestStack->getMasterRequest()->duplicate();
        $fake_request->headers->set('Accept', 'application/json');
        foreach ($placeholder_order as $placeholder) {
            if (!isset($placeholders[$placeholder])) {
                continue;
            }
            // Render the placeholder.
            $placeholder_render_array = $placeholders[$placeholder];
            $elements = $this->renderPlaceholder($placeholder, $placeholder_render_array);
            // Create a new AjaxResponse.
            $ajax_response = new AjaxResponse();
            // JavaScript's querySelector automatically decodes HTML entities in
            // attributes, so we must decode the entities of the current BigPipe
            // placeholder (which has HTML entities encoded since we use it to find
            // the placeholders).
            $big_pipe_js_selector = Html::decodeEntities($placeholder);
            $ajax_response->addCommand(new ReplaceCommand(sprintf('[data-big-pipe-selector="%s"]', $big_pipe_js_selector), $elements['#markup']));
            $ajax_response->setAttachments($elements['#attached']);
            // Push a fake request with the asset libraries loaded so far and dispatch
            // KernelEvents::RESPONSE event. This results in the attachments for the
            // AJAX response being processed by AjaxResponseAttachmentsProcessor and
            // hence:
            // - the necessary AJAX commands to load the necessary missing asset
            //   libraries and updated AJAX page state are added to the AJAX response
            // - the attachments associated with the response are finalized, which
            //   allows us to track the total set of asset libraries sent in the
            //   initial HTML response plus all embedded AJAX responses sent so far.
            $fake_request->request->set('ajax_page_state', ['libraries' => implode(',', $cumulative_assets->getAlreadyLoadedLibraries())] + $cumulative_assets->getSettings()['ajaxPageState']);
            $this->requestStack->push($fake_request);
            $event = new FilterResponseEvent($this->httpKernel, $fake_request, HttpKernelInterface::SUB_REQUEST, $ajax_response);
            $this->eventDispatcher->dispatch(KernelEvents::RESPONSE, $event);
            $ajax_response = $event->getResponse();
            $this->requestStack->pop();
            // Send this embedded AJAX response.
            $json = $ajax_response->getContent();
            $output = <<<EOF
    <script type="application/json" data-big-pipe-placeholder="{$placeholder}" data-drupal-ajax-processor="big_pipe">
    {$json}
    </script>
EOF;
            print $output;
            flush();
            // Another placeholder was rendered and sent, track the set of asset
            // libraries sent so far. Any new settings are already sent; we don't need
            // to track those.
            if (isset($ajax_response->getAttachments()['drupalSettings']['ajaxPageState']['libraries'])) {
                $cumulative_assets->setAlreadyLoadedLibraries(explode(',', $ajax_response->getAttachments()['drupalSettings']['ajaxPageState']['libraries']));
            }
        }
        // Send the stop signal.
        print '<script type="application/json" data-big-pipe-event="stop"></script>' . "\n";
        flush();
    }
示例#9
0
 /**
  * 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 bool
  *   TRUE on pass, FALSE on fail.
  */
 function assertNoNormalized($haystack, $needle, $message = '', $group = 'Other')
 {
     return $this->assertTrue(strpos(strtolower(Html::decodeEntities($haystack)), $needle) === FALSE, $message, $group);
 }
示例#10
0
 /**
  * Menu callback; autocomplete.js callback to return a list of files.
  */
 public static function autocomplete(Request $request, $entity_type, $bundle_name, $field_name)
 {
     $matches = array();
     $string = Unicode::strtolower($request->query->get('q'));
     $field_definition = entity_load('field_config', $entity_type . '.' . $bundle_name . '.' . $field_name);
     $handler = \Drupal::getContainer()->get('plugin.manager.entity_reference_selection')->getSelectionHandler($field_definition);
     if (isset($string)) {
         // Get an array of matching entities.
         $widget = entity_get_form_display($entity_type, $bundle_name, 'default')->getComponent($field_name);
         $autocomplete_type = $widget['third_party_settings']['filefield_sources']['filefield_sources']['source_reference']['autocomplete'];
         $match_operator = !empty($autocomplete_type) ? $autocomplete_type : FILEFIELD_SOURCE_REFERENCE_CONTAINS_AUTOCOMPLETE_TYPE;
         $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} [fid:{$entity_id}]";
                 // Strip things like starting/trailing white spaces, line breaks and
                 // tags.
                 $key = preg_replace('/\\s\\s+/', ' ', str_replace("\n", '', trim(Html::decodeEntities(strip_tags($key)))));
                 // Names containing commas or quotes must be wrapped in quotes.
                 $matches[] = array('value' => $key, 'label' => $label);
             }
         }
     }
     return new JsonResponse($matches);
 }
示例#11
0
 /**
  * Confirms that a log message appears on the database log overview screen.
  *
  * This function should only be used for the admin/reports/dblog page, because
  * it checks for the message link text truncated to 56 characters. Other log
  * pages have no detail links so they contain the full message text.
  *
  * @param string $log_message
  *   The database log message to check.
  * @param string $message
  *   The message to pass to simpletest.
  */
 protected function assertLogMessage($log_message, $message)
 {
     $message_text = Unicode::truncate(Html::decodeEntities(strip_tags($log_message)), 56, TRUE, TRUE);
     $this->assertLink($message_text, 0, $message);
 }
示例#12
0
 /**
  * Tests rewriting the output to a link.
  */
 public function testAlterUrl()
 {
     /** @var \Drupal\Core\Render\RendererInterface $renderer */
     $renderer = \Drupal::service('renderer');
     $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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $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.
     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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
             return $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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
             return $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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
             return $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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
             return $id_field->theme($row);
         });
         $this->assertSubString(Html::decodeEntities($result), Html::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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
             return $id_field->theme($row);
         });
         $this->assertSubString(Html::decodeEntities($result), Html::decodeEntities($expected_result));
         $expected_result = \Drupal::url('<front>', [], ['absolute' => $absolute]);
         $alter['path'] = '<front>';
         $result = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
             return $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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $id_field->theme($row);
     });
     $this->assertSubString($output, str_replace(' ', '-', $path));
     $id_field->options['alter']['replace_spaces'] = FALSE;
     $output = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $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 = 'www.drupal.org';
     $output = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $id_field->theme($row);
     });
     $this->assertSubString($output, 'http://www.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 = 'www.drupal.org';
     $output = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $id_field->theme($row);
     });
     $this->assertNotSubString($output, 'http://www.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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $id_field->theme($row);
     });
     $this->assertSubString($output, $path);
     // Switch to uppercase and lowercase.
     $id_field->options['alter']['path_case'] = 'upper';
     $output = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $id_field->theme($row);
     });
     $this->assertSubString($output, strtoupper($path));
     $id_field->options['alter']['path_case'] = 'lower';
     $output = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $id_field->theme($row);
     });
     $this->assertSubString($output, UrlHelper::encodePath('Drupal has a great community'));
     $id_field->options['alter']['path_case'] = 'ucwords';
     $output = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $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 actually exists in the
     // output.
     $id_field->options['alter']['link_class'] = $class = $this->randomMachineName();
     $output = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $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 = $renderer->executeInRenderContext(new RenderContext(), function () use($id_field, $row) {
         return $id_field->theme($row);
     });
     $elements = $this->xpathContent($output, '//a[contains(@target, :target)]', array(':target' => $target));
     $this->assertTrue($elements);
     unset($id_field->options['alter']['target']);
 }
示例#13
0
 /**
  * Removes superfluous whitespace and unescapes HTML entities.
  *
  * @param string $value
  *   The text to process.
  *
  * @return string
  *   The text without unnecessary whitespace and HTML entities transformed
  *   back to plain text.
  */
 protected function normalizeText($value) {
   $value = Html::decodeEntities($value);
   $value = trim($value);
   $value = preg_replace('/\s+/', ' ', $value);
   return $value;
 }
示例#14
0
 /**
  * {@inheritdoc}
  */
 protected function sanitizeLabel(&$label)
 {
     // Select form inputs allow unencoded HTML entities, but no HTML tags.
     $label = Html::decodeEntities(strip_tags($label));
 }
示例#15
0
 /**
  * Tests Html::decodeEntities().
  *
  * @dataProvider providerDecodeEntities
  * @covers ::decodeEntities
  */
 public function testDecodeEntities($text, $expected)
 {
     $this->assertEquals($expected, Html::decodeEntities($text));
 }
 /**
  * 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, Html::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://www.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://www.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');
 }
示例#17
0
 /**
  * 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(Html::decodeEntities(strtr($path, $tokens)));
             $links[$field] = array('href' => $path, 'title' => $title);
             if (!empty($this->options['destination'])) {
                 $links[$field]['query'] = $this->getDestinationArray();
             }
         }
     }
     // 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 '';
     }
 }
 /**
  * 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="https://www.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(Html::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;
 }
示例#19
0
 /**
  * {@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()) == '') {
         if ($comment->hasField('comment_body')) {
             // 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(Html::decodeEntities(strip_tags($comment_text))), 29, TRUE, 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;
 }
示例#20
0
 /**
  * {@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 encoded by \Drupal\Component\Utility\Html::escape().
         $tokens["!{$count}"] = isset($this->view->args[$count - 1]) ? strip_tags(Html::decodeEntities($this->view->args[$count - 1])) : '';
     }
     return $tokens;
 }
示例#21
0
 /**
  * Asserts expected BigPipe placeholders are present and replaced.
  *
  * @param array $expected_big_pipe_placeholders
  *   Keys: BigPipe placeholder IDs. Values: expected AJAX response.
  */
 protected function assertBigPipePlaceholders(array $expected_big_pipe_placeholders)
 {
     $this->pass('Verifying BigPipe placeholders & replacements…', 'Debug');
     $this->assertSetsEqual(array_keys($expected_big_pipe_placeholders), explode(' ', $this->drupalGetHeader('BigPipe-Test-Placeholders')));
     $placeholder_positions = [];
     $placeholder_replacement_positions = [];
     foreach ($expected_big_pipe_placeholders as $big_pipe_placeholder_id => $expected_ajax_response) {
         $this->pass('BigPipe placeholder: ' . $big_pipe_placeholder_id, 'Debug');
         // Verify expected placeholder.
         $expected_placeholder_html = '<div data-big-pipe-placeholder-id="' . $big_pipe_placeholder_id . '"></div>';
         $this->assertRaw($expected_placeholder_html, 'BigPipe placeholder for placeholder ID "' . $big_pipe_placeholder_id . '" found.');
         $pos = strpos($this->getRawContent(), $expected_placeholder_html);
         $placeholder_positions[$pos] = $big_pipe_placeholder_id;
         // Verify expected placeholder replacement.
         $result = $this->xpath('//script[@data-big-pipe-replacement-for-placeholder-with-id=:id]', [':id' => Html::decodeEntities($big_pipe_placeholder_id)]);
         $this->assertEqual($expected_ajax_response, trim((string) $result[0]));
         $expected_placeholder_replacement = '<script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="' . $big_pipe_placeholder_id . '">';
         $this->assertRaw($expected_placeholder_replacement);
         $pos = strpos($this->getRawContent(), $expected_placeholder_replacement);
         $placeholder_replacement_positions[$pos] = $big_pipe_placeholder_id;
     }
     ksort($placeholder_positions, SORT_NUMERIC);
     $this->assertEqual(array_keys($expected_big_pipe_placeholders), array_values($placeholder_positions));
     $this->assertEqual(count($expected_big_pipe_placeholders), preg_match_all('/' . preg_quote('<div data-big-pipe-placeholder-id="', '/') . '/', $this->getRawContent()));
     $this->assertEqual(array_keys($expected_big_pipe_placeholders), array_values($placeholder_replacement_positions));
     $this->assertEqual(count($expected_big_pipe_placeholders), preg_match_all('/' . preg_quote('<script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="', '/') . '/', $this->getRawContent()));
     $this->pass('Verifying BigPipe start/stop signals…', 'Debug');
     $this->assertRaw(BigPipe::START_SIGNAL, 'BigPipe start signal present.');
     $this->assertRaw(BigPipe::STOP_SIGNAL, 'BigPipe stop signal present.');
     $start_signal_position = strpos($this->getRawContent(), BigPipe::START_SIGNAL);
     $stop_signal_position = strpos($this->getRawContent(), BigPipe::STOP_SIGNAL);
     $this->assertTrue($start_signal_position < $stop_signal_position, 'BigPipe start signal appears before stop signal.');
     $this->pass('Verifying BigPipe placeholder replacements and start/stop signals were streamed in the correct order…', 'Debug');
     $expected_stream_order = array_keys($expected_big_pipe_placeholders);
     array_unshift($expected_stream_order, BigPipe::START_SIGNAL);
     array_push($expected_stream_order, BigPipe::STOP_SIGNAL);
     $actual_stream_order = $placeholder_replacement_positions + [$start_signal_position => BigPipe::START_SIGNAL, $stop_signal_position => BigPipe::STOP_SIGNAL];
     ksort($actual_stream_order, SORT_NUMERIC);
     $this->assertEqual($expected_stream_order, array_values($actual_stream_order));
 }
示例#22
0
 /**
  * {@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);
     }
     // Empty author ID should revert to anonymous.
     $author_id = $form_state->getValue('uid');
     if ($comment->id() && $this->currentUser->hasPermission('administer comments')) {
         // Admin can leave the author ID blank to revert to anonymous.
         $author_id = $author_id ?: 0;
     }
     if (!is_null($author_id)) {
         if ($author_id === 0 && $form['author']['name']['#access']) {
             // Use the author name value when the form has access to the element and
             // the author ID is anonymous.
             $comment->setAuthorName($form_state->getValue('name'));
         } else {
             // Ensure the author name is not set.
             $comment->setAuthorName(NULL);
         }
     } else {
         $author_id = $this->currentUser->id();
     }
     $comment->setOwnerId($author_id);
     // Validate the comment's subject. If not specified, extract from comment
     // body.
     if (trim($comment->getSubject()) == '') {
         if ($comment->hasField('comment_body')) {
             // 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(Html::decodeEntities(strip_tags($comment_text))), 29, TRUE, 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(Html::decodeEntities((string) $label));
         }
     }
 }
示例#24
0
 /**
  * Returns snippets from a piece of text, with certain keywords highlighted.
  *
  * Largely copied from search_excerpt().
  *
  * @param string $text
  *   The text to extract fragments from.
  * @param array $keys
  *   The search keywords entered by the user.
  *
  * @return string|null
  *   A string containing HTML for the excerpt. Or NULL if no excerpt could be
  *   created.
  */
 protected function createExcerpt($text, array $keys)
 {
     // Prepare text by stripping HTML tags and decoding HTML entities.
     $text = strip_tags(str_replace(array('<', '>'), array(' <', '> '), $text));
     $text = Html::decodeEntities($text);
     $text = preg_replace('/\\s+/', ' ', $text);
     $text = trim($text, ' ');
     $text_length = strlen($text);
     // Try to reach the requested excerpt length with about two fragments (each
     // with a keyword and some context).
     $ranges = array();
     $length = 0;
     $look_start = array();
     $remaining_keys = $keys;
     // Get the set excerpt length from the configuration. If the length is too
     // small, only use one fragment.
     $excerpt_length = $this->configuration['excerpt_length'];
     $context_length = round($excerpt_length / 4) - 3;
     if ($context_length < 32) {
         $context_length = round($excerpt_length / 2) - 1;
     }
     while ($length < $excerpt_length && !empty($remaining_keys)) {
         $found_keys = array();
         foreach ($remaining_keys as $key) {
             if ($length >= $excerpt_length) {
                 break;
             }
             // Remember where we last found $key, in case we are coming through a
             // second time.
             if (!isset($look_start[$key])) {
                 $look_start[$key] = 0;
             }
             // See if we can find $key after where we found it the last time. Since
             // we are requiring a match on a word boundary, make sure $text starts
             // and ends with a space.
             $matches = array();
             if (preg_match('/' . self::$boundary . preg_quote($key, '/') . self::$boundary . '/iu', ' ' . $text . ' ', $matches, PREG_OFFSET_CAPTURE, $look_start[$key])) {
                 $found_position = $matches[0][1];
                 $look_start[$key] = $found_position + 1;
                 // Keep track of which keys we found this time, in case we need to
                 // pass through again to find more text.
                 $found_keys[] = $key;
                 // Locate a space before and after this match, leaving some context on
                 // each end.
                 if ($found_position > $context_length) {
                     $before = strpos($text, ' ', $found_position - $context_length);
                     if ($before !== FALSE) {
                         ++$before;
                     }
                 } else {
                     $before = 0;
                 }
                 if ($before !== FALSE && $before <= $found_position) {
                     if ($text_length > $found_position + $context_length) {
                         $after = strrpos(substr($text, 0, $found_position + $context_length), ' ', $found_position);
                     } else {
                         $after = $text_length;
                     }
                     if ($after !== FALSE && $after > $found_position) {
                         if ($before < $after) {
                             // Save this range.
                             $ranges[$before] = $after;
                             $length += $after - $before;
                         }
                     }
                 }
             }
         }
         // Next time through this loop, only look for keys we found this time,
         // if any.
         $remaining_keys = $found_keys;
     }
     if (!$ranges) {
         // We didn't find any keyword matches, return NULL.
         return NULL;
     }
     // Sort the text ranges by starting position.
     ksort($ranges);
     // Collapse overlapping text ranges into one. The sorting makes it O(n).
     $new_ranges = array();
     $max_end = 0;
     $working_from = $working_to = NULL;
     foreach ($ranges as $this_from => $this_to) {
         $max_end = max($max_end, $this_to);
         if (is_null($working_from)) {
             // This is the first time through this loop: initialize.
             $working_from = $this_from;
             $working_to = $this_to;
             continue;
         }
         if ($this_from <= $working_to) {
             // The ranges overlap: combine them.
             $working_to = max($working_to, $this_to);
         } else {
             // The ranges do not overlap: save the working range and start a new
             // one.
             $new_ranges[$working_from] = $working_to;
             $working_from = $this_from;
             $working_to = $this_to;
         }
     }
     // Save the remaining working range.
     $new_ranges[$working_from] = $working_to;
     // Fetch text within the combined ranges we found.
     $out = array();
     foreach ($new_ranges as $from => $to) {
         $out[] = Html::escape(substr($text, $from, $to - $from));
     }
     if (!$out) {
         return NULL;
     }
     $ellipses = $this->getEllipses();
     $excerpt = $ellipses[0] . implode($ellipses[1], $out) . $ellipses[2];
     return $this->highlightField($excerpt, $keys);
 }
示例#25
0
    /**
     * Sends BigPipe placeholders' replacements as embedded AJAX responses.
     *
     * @param array $placeholders
     *   Associative array; the BigPipe placeholders. Keys are the BigPipe
     *   placeholder IDs.
     * @param array $placeholder_order
     *   Indexed array; the order in which the BigPipe placeholders must be sent.
     *   Values are the BigPipe placeholder IDs. (These values correspond to keys
     *   in $placeholders.)
     * @param \Drupal\Core\Asset\AttachedAssetsInterface $cumulative_assets
     *   The cumulative assets sent so far; to be updated while rendering BigPipe
     *   placeholders.
     *
     * @throws \Exception
     *   If an exception is thrown during the rendering of a placeholder, it is
     *   caught to allow the other placeholders to still be replaced. But when
     *   error logging is configured to be verbose, the exception is rethrown to
     *   simplify debugging.
     */
    protected function sendPlaceholders(array $placeholders, array $placeholder_order, AttachedAssetsInterface $cumulative_assets)
    {
        // Return early if there are no BigPipe placeholders to send.
        if (empty($placeholders)) {
            return;
        }
        // Send the start signal.
        print "\n";
        print static::START_SIGNAL;
        print "\n";
        flush();
        // A BigPipe response consists of a HTML response plus multiple embedded
        // AJAX responses. To process the attachments of those AJAX responses, we
        // need a fake request that is identical to the master request, but with
        // one change: it must have the right Accept header, otherwise the work-
        // around for a bug in IE9 will cause not JSON, but <textarea>-wrapped JSON
        // to be returned.
        // @see \Drupal\Core\EventSubscriber\AjaxResponseSubscriber::onResponse()
        $fake_request = $this->requestStack->getMasterRequest()->duplicate();
        $fake_request->headers->set('Accept', 'application/vnd.drupal-ajax');
        foreach ($placeholder_order as $placeholder_id) {
            if (!isset($placeholders[$placeholder_id])) {
                continue;
            }
            // Render the placeholder.
            $placeholder_render_array = $placeholders[$placeholder_id];
            try {
                $elements = $this->renderPlaceholder($placeholder_id, $placeholder_render_array);
            } catch (\Exception $e) {
                if (\Drupal::config('system.logging')->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE) {
                    throw $e;
                } else {
                    trigger_error($e, E_USER_ERROR);
                    continue;
                }
            }
            // Create a new AjaxResponse.
            $ajax_response = new AjaxResponse();
            // JavaScript's querySelector automatically decodes HTML entities in
            // attributes, so we must decode the entities of the current BigPipe
            // placeholder ID (which has HTML entities encoded since we use it to find
            // the placeholders).
            $big_pipe_js_placeholder_id = Html::decodeEntities($placeholder_id);
            $ajax_response->addCommand(new ReplaceCommand(sprintf('[data-big-pipe-placeholder-id="%s"]', $big_pipe_js_placeholder_id), $elements['#markup']));
            $ajax_response->setAttachments($elements['#attached']);
            // Push a fake request with the asset libraries loaded so far and dispatch
            // KernelEvents::RESPONSE event. This results in the attachments for the
            // AJAX response being processed by AjaxResponseAttachmentsProcessor and
            // hence:
            // - the necessary AJAX commands to load the necessary missing asset
            //   libraries and updated AJAX page state are added to the AJAX response
            // - the attachments associated with the response are finalized, which
            //   allows us to track the total set of asset libraries sent in the
            //   initial HTML response plus all embedded AJAX responses sent so far.
            $fake_request->request->set('ajax_page_state', ['libraries' => implode(',', $cumulative_assets->getAlreadyLoadedLibraries())] + $cumulative_assets->getSettings()['ajaxPageState']);
            try {
                $ajax_response = $this->filterEmbeddedResponse($fake_request, $ajax_response);
            } catch (\Exception $e) {
                if (\Drupal::config('system.logging')->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE) {
                    throw $e;
                } else {
                    trigger_error($e, E_USER_ERROR);
                    continue;
                }
            }
            // Send this embedded AJAX response.
            $json = $ajax_response->getContent();
            $output = <<<EOF
    <script type="application/vnd.drupal-ajax" data-big-pipe-replacement-for-placeholder-with-id="{$placeholder_id}">
    {$json}
    </script>
EOF;
            print $output;
            flush();
            // Another placeholder was rendered and sent, track the set of asset
            // libraries sent so far. Any new settings are already sent; we don't need
            // to track those.
            if (isset($ajax_response->getAttachments()['drupalSettings']['ajaxPageState']['libraries'])) {
                $cumulative_assets->setAlreadyLoadedLibraries(explode(',', $ajax_response->getAttachments()['drupalSettings']['ajaxPageState']['libraries']));
            }
        }
        // Send the stop signal.
        print "\n";
        print static::STOP_SIGNAL;
        print "\n";
        flush();
    }
示例#26
0
 /**
  * Renders an embedded entity.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
  *   The entity to be rendered.
  * @param array $context
  *   (optional) Array of context values, corresponding to the attributes on
  *   the embed HTML tag.
  *
  * @return string
  *   The HTML of the entity rendered with the Entity Embed Display plugin.
  */
 protected function renderEntityEmbed(EntityInterface $entity, array $context = array())
 {
     // Support the deprecated view-mode data attribute.
     if (isset($context['data-view-mode']) && !isset($context['data-entity-embed-display']) && !isset($context['data-entity-embed-settings'])) {
         $context['data-entity-embed-display'] = 'entity_reference:entity_reference_entity_view';
         $context['data-entity-embed-settings'] = ['view_mode' => &$context['data-view-mode']];
     }
     // Merge in default attributes.
     $context += array('data-entity-id' => $entity->id(), 'data-entity-type' => $entity->getEntityTypeId(), 'data-entity-embed-display' => 'entity_reference:entity_reference_entity_view', 'data-entity-embed-settings' => array());
     // The default Entity Embed Display plugin has been deprecated by the
     // rendered entity field formatter.
     if ($context['data-entity-embed-display'] === 'default') {
         $context['data-entity-embed-display'] = 'entity_reference:entity_reference_entity_view';
     }
     // The caption text is double-encoded, so decode it here.
     if (isset($context['data-caption'])) {
         $context['data-caption'] = Html::decodeEntities($context['data-caption']);
     }
     // Allow modules to alter the entity prior to embed rendering.
     $this->moduleHandler()->alter(array("{$context['data-entity-type']}_embed_context", 'entity_embed_context'), $context, $entity);
     // Build and render the Entity Embed Display plugin, allowing modules to
     // alter the result before rendering.
     $build = $this->renderEntityEmbedDisplayPlugin($entity, $context['data-entity-embed-display'], $context['data-entity-embed-settings'], $context);
     // Maintain data-align if it is there.
     if (isset($context['data-align'])) {
         $build['#attributes']['data-align'] = $context['data-align'];
     } elseif (isset($context['class'])) {
         $build['#attributes']['class'][] = $context['class'];
     }
     // Maintain data-caption if it is there.
     if (isset($context['data-caption'])) {
         $build['#attributes']['data-caption'] = $context['data-caption'];
     }
     // @todo Should this hook get invoked if $build is an empty array?
     $this->moduleHandler()->alter(array("{$context['data-entity-type']}_embed", 'entity_embed'), $build, $entity, $context);
     $entity_output = $this->renderer()->render($build);
     return $entity_output;
 }
示例#27
0
 /**
  * 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(Html::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;
 }
示例#28
0
 /**
  * {@inheritdoc}
  */
 public function cleanString($string, array $options = array())
 {
     if (empty($this->cleanStringCache)) {
         // Generate and cache variables used in this method.
         $config = $this->configFactory->get('pathauto.settings');
         $this->cleanStringCache = array('separator' => $config->get('separator'), 'strings' => array(), 'transliterate' => $config->get('transliterate'), 'punctuation' => array(), 'reduce_ascii' => (bool) $config->get('reduce_ascii'), 'ignore_words_regex' => FALSE, 'lowercase' => (bool) $config->get('case'), 'maxlength' => min($config->get('max_component_length'), $this->aliasStorageHelper->getAliasSchemaMaxLength()));
         // Generate and cache the punctuation replacements for strtr().
         $punctuation = $this->getPunctuationCharacters();
         foreach ($punctuation as $name => $details) {
             $action = $config->get('punctuation.' . $name);
             switch ($action) {
                 case PathautoManagerInterface::PUNCTUATION_REMOVE:
                     $cache['punctuation'][$details['value']] = '';
                     $this->cleanStringCache;
                 case PathautoManagerInterface::PUNCTUATION_REPLACE:
                     $this->cleanStringCache['punctuation'][$details['value']] = $this->cleanStringCache['separator'];
                     break;
                 case PathautoManagerInterface::PUNCTUATION_DO_NOTHING:
                     // Literally do nothing.
                     break;
             }
         }
         // Generate and cache the ignored words regular expression.
         $ignore_words = $config->get('ignore_words');
         $ignore_words_regex = preg_replace(array('/^[,\\s]+|[,\\s]+$/', '/[,\\s]+/'), array('', '\\b|\\b'), $ignore_words);
         if ($ignore_words_regex) {
             $this->cleanStringCache['ignore_words_regex'] = '\\b' . $ignore_words_regex . '\\b';
             if (function_exists('mb_eregi_replace')) {
                 mb_regex_encoding('UTF-8');
                 $this->cleanStringCache['ignore_words_callback'] = 'mb_eregi_replace';
             } else {
                 $this->cleanStringCache['ignore_words_callback'] = 'preg_replace';
                 $this->cleanStringCache['ignore_words_regex'] = '/' . $this->cleanStringCache['ignore_words_regex'] . '/i';
             }
         }
     }
     // Empty strings do not need any processing.
     if ($string === '' || $string === NULL) {
         return '';
     }
     $langcode = NULL;
     if (!empty($options['language'])) {
         $langcode = $options['language']->getId();
     } elseif (!empty($options['langcode'])) {
         $langcode = $options['langcode'];
     }
     // Check if the string has already been processed, and if so return the
     // cached result.
     if (isset($this->cleanStringCache['strings'][$langcode][(string) $string])) {
         return $this->cleanStringCache['strings'][$langcode][(string) $string];
     }
     // Remove all HTML tags from the string.
     $output = Html::decodeEntities($string);
     $output = PlainTextOutput::renderFromHtml($output);
     // Optionally transliterate.
     if ($this->cleanStringCache['transliterate']) {
         // If the reduce strings to letters and numbers is enabled, don't bother
         // replacing unknown characters with a question mark. Use an empty string
         // instead.
         $output = $this->transliteration->transliterate($output, $langcode, $this->cleanStringCache['reduce_ascii'] ? '' : '?');
     }
     // Replace or drop punctuation based on user settings.
     $output = strtr($output, $this->cleanStringCache['punctuation']);
     // Reduce strings to letters and numbers.
     if ($this->cleanStringCache['reduce_ascii']) {
         $output = preg_replace('/[^a-zA-Z0-9\\/]+/', $this->cleanStringCache['separator'], $output);
     }
     // Get rid of words that are on the ignore list.
     if ($this->cleanStringCache['ignore_words_regex']) {
         $words_removed = $this->cleanStringCache['ignore_words_callback']($this->cleanStringCache['ignore_words_regex'], '', $output);
         if (Unicode::strlen(trim($words_removed)) > 0) {
             $output = $words_removed;
         }
     }
     // Always replace whitespace with the separator.
     $output = preg_replace('/\\s+/', $this->cleanStringCache['separator'], $output);
     // Trim duplicates and remove trailing and leading separators.
     $output = $this->getCleanSeparators($this->getCleanSeparators($output, $this->cleanStringCache['separator']));
     // Optionally convert to lower case.
     if ($this->cleanStringCache['lowercase']) {
         $output = Unicode::strtolower($output);
     }
     // Shorten to a logical place based on word boundaries.
     $output = Unicode::truncate($output, $this->cleanStringCache['maxlength'], TRUE);
     // Cache this result in the static array.
     $this->cleanStringCache['strings'][$langcode][(string) $string] = $output;
     return $output;
 }
示例#29
0
文件: WebTestBase.php 项目: Wylbur/gj
 /**
  * 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() && (!isset($this->maximumMetaRefreshCount) || $this->metaRefreshCount < $this->maximumMetaRefreshCount)) {
         $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)) {
                 $this->metaRefreshCount++;
                 return $this->drupalGet($this->getAbsoluteUrl(Html::decodeEntities($match['url'])));
             }
         }
     }
     return FALSE;
 }
示例#30
0
 protected function setDefaultSubjectIfNeeded(&$comment)
 {
     // Extract subject from comment body.
     if (!isset($comment['subject'])) {
         $comment['subject'] = '';
     }
     if (trim($comment['subject']) == '') {
         if (isset($comment['comment_body'])) {
             // 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']['value'];
             $comment['subject'] = Unicode::truncate(trim(Html::decodeEntities(strip_tags($comment_text))), 29, TRUE, TRUE);
         }
         // Edge cases where the comment body is populated only by HTML tags will
         // require a default subject.
         if ($comment['subject'] == '') {
             $comment['subject'] = t('(No subject)');
         }
     }
 }