/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); if (stristr($text, 'data-entity-type="file"') !== FALSE) { $dom = Html::load($text); $xpath = new \DOMXPath($dom); $processed_uuids = array(); foreach ($xpath->query('//*[@data-entity-type="file" and @data-entity-uuid]') as $node) { $uuid = $node->getAttribute('data-entity-uuid'); // If there is a 'src' attribute, set it to the file entity's current // URL. This ensures the URL works even after the file location changes. if ($node->hasAttribute('src')) { $file = $this->entityManager->loadEntityByUuid('file', $uuid); if ($file) { $node->setAttribute('src', file_url_transform_relative(file_create_url($file->getFileUri()))); } } // Only process the first occurrence of each file UUID. if (!isset($processed_uuids[$uuid])) { $processed_uuids[$uuid] = TRUE; $file = $this->entityManager->loadEntityByUuid('file', $uuid); if ($file) { $result->addCacheTags($file->getCacheTags()); } } } $result->setProcessedText(Html::serialize($dom)); } return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); // The changes made by this filter are language-specific. $result->addCacheContexts(['languages:' . LanguageInterface::TYPE_CONTENT]); return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); // Track if widget has been found so that we can attached the // jquery_ui_filter library and settings. $has_widget = FALSE; foreach (self::$widgets as $name => $widget) { if (strpos($text, '[' . $name) === FALSE) { continue; } $has_widget = TRUE; // Remove block tags around tokens. $text = preg_replace('#<(p|div)[^>]*>\\s*(\\[/?' . $name . '[^]]*\\])\\s*</\\1>#', '\\2', $text); // Convert opening [token] to opening <div data-ui-*> tag. $text = preg_replace_callback('#\\[' . $name . '([^]]*)?\\]#is', function ($match) use($name) { // Set data-ui-* attributes from role and options. $attributes = new Attribute(['data-ui-role' => $name]); $options = $this->parseOptions($match[1]); foreach ($options as $name => $value) { $attributes->setAttribute('data-ui-' . $name, $value); } return "<div{$attributes}>"; }, $text); // Convert closing [/token] to closing </div> tag. $text = str_replace('[/' . $name . ']', '</div>', $text); } if ($has_widget) { $result->setAttachments(['library' => ['jquery_ui_filter/jquery_ui_filter'], 'drupalSettings' => ['jquery_ui_filter' => \Drupal::config('jquery_ui_filter.settings')->get()]]); } return $result->setProcessedText($text); }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); $result->addCacheTags(array('foo:bar')); $result->addCacheTags(array('foo:baz')); return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $response = new FilterProcessResult($text); // Use a look ahead to match the capture groups in any order. if (preg_match_all('/<p>(?<json>{(?=.*preview_thumbnail\\b)(?=.*settings\\b)(?=.*video_url\\b)(?=.*settings_summary)(.*)})<\\/p>/', $text, $matches)) { foreach ($matches['json'] as $delta => $match) { // Ensure the JSON string is valid. $embed_data = json_decode($match, TRUE); if (!is_array($embed_data)) { continue; } // If the URL can't matched to a provider or the settings are invalid, // ignore it. $provider = $this->providerManager->loadProviderFromInput($embed_data['video_url']); if (!$provider || !$this->validSettings($embed_data['settings'])) { continue; } $embed_code = $provider->renderEmbedCode($embed_data['settings']['width'], $embed_data['settings']['height'], $embed_data['settings']['autoplay']); // Add the container to make the video responsive if it's been //configured as such. This usually is attached to field output in the // case of a formatter, but a custom container must be used where one is // not present. if ($embed_data['settings']['responsive']) { $embed_code = ['#type' => 'container', '#attributes' => ['class' => ['video-embed-field-responsive-video']], 'children' => $embed_code]; } // Replace the JSON settings with a video. $text = str_replace($matches[0][$delta], $this->renderer->renderRoot($embed_code), $text); } } // Add the required responsive video library and update the response text. $response->setProcessedText($text); $response->addAttachments(['library' => ['video_embed_field/responsive-video']]); return $response; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); $placeholder = $result->createPlaceholder('\\Drupal\\filter_test\\Plugin\\Filter\\FilterTestPlaceholders::renderDynamicThing', ['llama']); $result->setProcessedText($text . '<p>' . $placeholder . '</p>'); return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); if (strpos($text, '<oembed') !== FALSE) { $result->setProcessedText($this->ckeditor_media_embed->processEmbeds($text)); } return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $callback = '\\Drupal\\filter_test\\Plugin\\Filter\\FilterTestPostRenderCache::renderDynamicThing'; $context = array('thing' => 'llama'); $placeholder = drupal_render_cache_generate_placeholder($callback, $context); $result = new FilterProcessResult($text . '<p>' . $placeholder . '</p>'); $result->addPostRenderCacheCallback($callback, $context); return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $invitation = $this->settings['celebrate_invitation'] ? ' Come on!' : ''; $replace = '<span class="celebrate-filter">' . $this->t('Good Times!' . $invitation) . '</span>'; $new_text = str_replace('[celebrate]', $replace, $text); $result = new FilterProcessResult($new_text); $result->setAttachments(array('library' => array('custom_filters/celebrate-shake'))); return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); // Replace node macros with entity content. $pattern = '/\\[mailchimp_campaign\\|entity_type=(\\w+)\\|entity_id=(\\d+)\\|view_mode=(\\w+)\\]/s'; $text = preg_replace_callback($pattern, array($this, 'mailchimp_campaign_process_callback'), $text); // Convert URL to absolute. $text = $this->convertUrl($text); $result->setProcessedText($text); return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); if (stristr($text, '<pre') !== FALSE) { $attach = FALSE; $nodes = Html::load($text)->getElementsByTagName('pre'); foreach ($nodes as $node) { if (preg_match('/\\bbrush\\b:(.*?);/i', $node->getAttribute('class'))) { $attach = TRUE; break; } } if ($attach) { $result->addAttachments(['library' => ['syntax_highlighter/highlight']]); } } return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $new_text = $text; $filter_result = new FilterProcessResult($text); if (preg_match('/\\[survey\\:.+\\]/', $text, $result)) { $token = $result[0]; $start = strpos($token, ':') + 1; $length = strpos($token, ']') - $start; $id = substr($token, $start, $length); $block = Block::load($id); if ($block) { $replace = $block->get('settings')['html']; $new_text = str_replace($token, $replace, $text); $filter_result->setProcessedText($new_text); $filter_result->setCacheTags($block->getCacheTags()); } } return $filter_result; }
/** * {@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; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); if (stristr($text, 'data-entity-type="file"') !== FALSE) { $dom = Html::load($text); $xpath = new \DOMXPath($dom); $processed_uuids = array(); foreach ($xpath->query('//*[@data-entity-type="file" and @data-entity-uuid]') as $node) { $uuid = $node->getAttribute('data-entity-uuid'); // Only process the first occurrence of each file UUID. if (!isset($processed_uuids[$uuid])) { $processed_uuids[$uuid] = TRUE; $file = $this->entityManager->loadEntityByUuid('file', $uuid); if ($file) { $result->addCacheTags($file->getCacheTags()); } } } } return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); if (stristr($text, 'data-align') !== FALSE) { $dom = Html::load($text); $xpath = new \DOMXPath($dom); foreach ($xpath->query('//*[@data-align]') as $node) { // Read the data-align attribute's value, then delete it. $align = $node->getAttribute('data-align'); $node->removeAttribute('data-align'); // If one of the allowed alignments, add the corresponding class. if (in_array($align, array('left', 'center', 'right'))) { $classes = $node->getAttribute('class'); $classes = strlen($classes) > 0 ? explode(' ', $classes) : array(); $classes[] = 'align-' . $align; $node->setAttribute('class', implode(' ', $classes)); } } $result->setProcessedText(Html::serialize($dom)); } return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); $dom = Html::load($text); $xpath = new \DOMXPath($dom); /** @var \DOMNode $node */ foreach ($xpath->query('//img') as $node) { // Read the data-align attribute's value, then delete it. $width = $node->getAttribute('width'); $height = $node->getAttribute('height'); $src = $node->getAttribute('src'); if (!UrlHelper::isExternal($src)) { if ($width || $height) { /** @var \DOMNode $element */ $element = $dom->createElement('a'); $element->setAttribute('href', $src); $node->parentNode->replaceChild($element, $node); $element->appendChild($node); } } } $result->setProcessedText(Html::serialize($dom)); return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); if (stristr($text, '<img ') !== FALSE) { $dom = Html::load($text); $images = $dom->getElementsByTagName('img'); foreach ($images as $image) { $src = $image->getAttribute("src"); // The src must be non-empty. if (Unicode::strlen($src) === 0) { continue; } // The src must not already be an external URL if (stristr($src, 'http://') !== FALSE || stristr($src, 'https://') !== FALSE) { continue; } $url = Url::fromUri('internal:' . $src, array('absolute' => TRUE)); $url_string = $url->toString(); $image->setAttribute('src', $url_string); } $result->setProcessedText(Html::serialize($dom)); } return $result; }
/** * Implements preg_replace_callback() callback. * * @see self::process() */ function processCallback(array $matches) { $currency_code = $matches[1]; $amount = $this->input->parseAmount($matches[2]); // The amount is invalid, so return the token. if (!$amount) { return $matches[0]; } /** @var \Drupal\currency\Entity\CurrencyInterface $currency */ $currency = $this->currencyStorage->load($currency_code); $this->currentFilterProcessResult->addCacheableDependency($currency); if ($currency) { return $currency->formatAmount($amount); } // The currency code is invalid, so return the token. return $matches[0]; }
/** * Implements preg_replace_callback() callback. * * @see self::process() */ function processCallback(array $matches) { $currency_code_from = $matches[1]; $currency_code_to = $matches[2]; $amount = str_replace(':', '', $matches[3]); if (strlen($amount) !== 0) { $amount = $this->input->parseAmount($amount); // The amount is invalid, so return the token. if (!$amount) { return $matches[0]; } } else { $amount = 1; } $exchange_rate = $this->exchangeRateProvider->load($currency_code_from, $currency_code_to); $this->currentFilterProcessResult->addCacheableDependency($exchange_rate); if ($exchange_rate) { return bcmul($amount, $exchange_rate->getRate(), 6); } // No exchange rate could be loaded, so return the token. return $matches[0]; }
/** * {@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 process($text, $langcode) { // Supporting both [fn] and <fn> now. Thanks to fletchgqc // http://drupal.org/node/268026. // Convert all square brackets to angle brackets. This way all further code // just manipulates angle brackets. (Angle brackets are preferred here for // the simple reason that square brackets are more tedious to use in // regexps). $text = preg_replace('|\\[fn([^\\]]*)\\]|', '<fn$1>', $text); $text = preg_replace('|\\[/fn\\]|', '</fn>', $text); $text = preg_replace('|\\[footnotes([^\\]]*)\\]|', '<footnotes$1>', $text); // Check that there are an even number of open and closing tags. // If there is one closing tag missing, append this to the end. // If there is more disparity, throw a warning and continue. // A closing tag may sometimes be missing when we are processing a teaser // and it has been cut in the middle of the footnote. // See http://drupal.org/node/253326 $foo = array(); $open_tags = preg_match_all("|<fn([^>]*)>|", $text, $foo); $close_tags = preg_match_all("|</fn>|", $text, $foo); if ($open_tags == $close_tags + 1) { $text = $text . '</fn>'; } elseif ($open_tags > $close_tags + 1) { trigger_error(t("You have unclosed fn tags. This is invalid and will\n produce unpredictable results.")); } // Before doing the replacement, the callback function needs to know which // options to use. $this->replaceCallback($this->settings['footnotes_collapse'], 'prepare'); $pattern = '|<fn([^>]*)>(.*?)</fn>|s'; $text = preg_replace_callback($pattern, array($this, 'replaceCallback'), $text); // Replace tag <footnotes> with the list of footnotes. // If tag is not present, by default add the footnotes at the end. // Thanks to acp on drupal.org for this idea. see // http://drupal.org/node/87226. $footer = $this->replaceCallback(NULL, 'output footer'); $pattern = '|(<footnotes([^\\]]*)>)|'; if (preg_match($pattern, $text) > 0) { $text = preg_replace($pattern, $footer, $text, 1); } else { $text .= "\n\n" . $footer; } $result = new FilterProcessResult($text); $result->setAttachments(array('library' => array('footnotes/footnotes'))); return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); $result->addAttachments(array('library' => array('filter/caption'))); return $result; }
/** * {@inheritdoc} */ public function process($text, $langcode) { $result = new FilterProcessResult($text); try { // Load GeSHi library (if not already). $geshi_library = libraries_load('geshi'); if (!$geshi_library['loaded']) { throw new \Exception($geshi_library['error message']); } // Get the available tags. list($generic_code_tags, $language_tags, $tag_to_lang) = $this->getTags(); if (in_array(GeshiFilter::BRACKETS_PHPBLOCK, array_filter($this->tagStyles()))) { $language_tags[] = 'questionmarkphp'; $tag_to_lang['questionmarkphp'] = 'php'; } $tags = array_merge($generic_code_tags, $language_tags); // Escape special (regular expression) characters in tags (for tags like // 'c++' and 'c#'). $tags = preg_replace('#(\\+|\\#)#', '\\\\$1', $tags); $tags_string = implode('|', $tags); // Pattern for matching the prepared "<code>...</code>" stuff. $pattern = '#\\[geshifilter-(' . $tags_string . ')([^\\]]*)\\](.*?)(\\[/geshifilter-\\1\\])#s'; $text = preg_replace_callback($pattern, array($this, 'replaceCallback'), $text); // Create the object with result. $result = new FilterProcessResult($text); // Add the css file when necessary. if ($this->config->get('css_mode') == GeshiFilter::CSS_CLASSES_AUTOMATIC) { $result->setAttachments(array('library' => array('geshifilter/geshifilter'))); } // Add cache tags, so we can re-create the node when some geshifilter // settings change. $cache_tags = array('geshifilter'); $result->addCacheTags($cache_tags); } catch (\Exception $e) { watchdog_exception('geshifilter', $e); drupal_set_message($geshi_library['error message'], 'error'); } return $result; }