瀏覽代碼

D8 port step 1: basic config UI working.

Frederic G. MARAND 7 年之前
父節點
當前提交
b803bfddc1

+ 55 - 0
README.md

@@ -0,0 +1,55 @@
+
+# URL Replace Filter Drupal module.
+
+
+## Description
+
+The URL Replace Filter module allows administrators to replace the base URL in
+`<img>` and `<a>` elements.
+
+Users tend to create links and images in their content with absolute URLs. This
+can be a problem if the site moves to another domain (perhaps between
+development and production sites) or is behind a proxy, with a different address
+for authenticated users.
+
+### Some replacement examples:
+
+* Link
+  * Before: `<a href="http://example.com:8080/somepath">Some link</a>`
+  * After: `<a href="/somepath">Some link</a>`
+* Image
+  * Before: `<img src="http://dev.example.com/files/image.jpg" alt="Some image" />`
+  * After: `<img src="/files/image.jpg" alt="Some image" />`
+
+You can setup such replacements in the URL Replace Filter settings as follow:
+
+* Link
+  * Original: `http://example.com:8080/`
+  * Replacement: `%baseurl/`
+* Image
+  * Original: `http://dev.example.com/`
+  * Replacement: `%baseurl/`
+
+`%baseurl` is a token for your site's base URL. The above examples assume a site
+located in the domain's root directory (in which case `%baseurl` is actually
+empty).
+
+Like any Drupal filter, the original user-entered content is not altered. The
+filter is only applied (and its result cached) when the node is viewed.
+
+
+## Installation
+
+1. Enable the module
+
+2. Go to the `Configuration` > `Content authoring` > `Text formats and editors` 
+   page, and click configure next to the text format that shall replace URLs.
+
+3. Enable the URL Replace Filter in the text format's configuration page, and
+   save the configuration.
+
+4. Click the `URL Replace filter` vertical tab at the bottom of the page. 
+   In the URL Replace Filter box, enter original and replacement URLs in the 
+   appropriate fields and save the configuration. More empty replacement fields 
+   will automatically be added after saving, in case you need more fields than 
+   provided by default.

+ 0 - 55
README.txt

@@ -1,55 +0,0 @@
-
-README file for the URL Replace Filter Drupal module.
-
-
-Description
-***********
-
-The URL Replace Filter module allows administrators to replace the base URL in
-<img> and <a> elements.
-
-Users tend to create links and images in their content with absolute URLs. This
-can be a problem if the site moves to another domain (perhaps between
-development and production sites) or is behind a proxy, with a different address
-for authenticated users.
-
-Some replacement examples:
-
-Before: <a href="http://example.com:8080/somepath">Some link</a>
-After: <a href="/somepath">Some link</a>
-
-Before: <img src="http://dev.example.com/files/image.jpg" alt="Some image" />
-After: <img src="/files/image.jpg" alt="Some image" />
-
-You can setup such replacements in the URL Replace Filter settings as follow:
-
-Original: http://example.com:8080/
-Replacement: %baseurl/
-
-Original: http://dev.example.com/
-Replacement: %baseurl/
-
-%baseurl is a token for your site's base URL. The above examples assume a site
-%located in the domain's root directory (in which case %baseurl is actually
-%empty).
-
-Like any Drupal filter, the original user-entered content is not altered. The
-filter is only applied (and its result cached) when the node is viewed.
-
-
-Installation
-************
-
-1. Extract the 'url_replace_filter' module directory into your site's modules
-   directory.
-
-2. Go to the Administer > Site configuration > Input formats page, and click
-   configure next to the input format that shall replace URLs.
-
-3. Enable the URL Replace Filter in the input format's configuration page, and
-   save the configuration.
-
-4. Click the Configure tab. In the URL Replace Filter box, enter original and
-   replacement URLs in the appropriate fields and save the configuration. More
-   empty replacement fields will automatically be added after saving, in case
-   you need more fields than provided by default.

+ 376 - 0
src/Plugin/Filter/UrlReplaceFilter.php

@@ -0,0 +1,376 @@
+<?php
+
+namespace Drupal\url_replace_filter\Plugin\Filter;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Routing\CurrentRouteMatch;
+use Drupal\Core\Url;
+use Drupal\filter\FilterProcessResult;
+use Drupal\filter\Plugin\FilterBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Text filter for highlighting PHP source code.
+ *
+ * @Filter(
+ *   id = "url_replace_filter",
+ *   description = @Translation("Allows administrators to replace the base URL in &lt;img&gt; and &lt;a&gt; elements."),
+ *   module = "url_replace_filter",
+ *   title = @Translation("URL Replace filter"),
+ *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE,
+ *   settings = {
+ *     "replacements" = ''
+ *   }
+ * )
+ */
+class UrlReplaceFilter extends FilterBase implements ContainerFactoryPluginInterface {
+
+  const SETTING_NAME = 'replacements';
+
+  /**
+   * @var \Drupal\Core\Routing\CurrentRouteMatch
+   */
+  protected $currentRouteMatch;
+
+  /**
+   * @var \Drupal\Core\Messenger\MessengerInterface
+   */
+  protected $messenger;
+
+  /**
+   * UrlReplaceFilter constructor.
+   *
+   * @param array $configuration
+   *   The plugin configuration.
+   * @param $plugin_id
+   *   The plugin id.
+   * @param $plugin_definition
+   *   The plugin definition.
+   * @param \Drupal\Core\Routing\CurrentRouteMatch $currentRouteMatch
+   *   The current_route_match service.
+   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+   *   The messenger service.
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    $plugin_definition,
+    CurrentRouteMatch $currentRouteMatch,
+    MessengerInterface $messenger
+  ) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->currentRouteMatch = $currentRouteMatch;
+    $this->messenger = $messenger;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(
+    ContainerInterface $container,
+    array $configuration,
+    $plugin_id,
+    $plugin_definition
+  ) {
+    $messenger = $container->get('messenger');
+    $currentRouteMatch = $container->get('current_route_match');
+    return new static($configuration, $plugin_id, $plugin_definition, $currentRouteMatch, $messenger);
+  }
+
+  /**
+   * Helper to build rows in the table built by _url_replace_filter_settings().
+   *
+   * @param array $form
+   *   The form array.
+   * @param int $index
+   *   The format index.
+   * @param string $original
+   *   The original value to be replaced.
+   * @param string $replacement
+   *   The replacement for the original value.
+   *
+   * @return array
+   *   A form array.
+   */
+  protected function buildRowForm(array $form, int $index, string $original, string $replacement) {
+    $form[self::SETTING_NAME]["replacement-{$index}"]['original'] = [
+      '#type' => 'textfield',
+      '#size' => 50,
+      '#default_value' => $original,
+    ];
+    $form[self::SETTING_NAME]["replacement-{$index}"]['replacement'] = [
+      '#type' => 'textfield',
+      '#size' => 50,
+      '#default_value' => $replacement,
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, FormStateInterface $form_state) {
+
+    $empty = 0;
+    $form[self::SETTING_NAME] = [
+      '#type' => 'details',
+      '#title' => $this->t('URL Replace Filter'),
+      '#open' => TRUE,
+      '#theme' => 'url_replace_filter_settings_form',
+    ];
+    $form['#element_validate'][] = [$this, 'settingsFormValidate'];
+
+    $stringSettings = $this->settings[self::SETTING_NAME];
+    $settings = $stringSettings ? array_values(unserialize($stringSettings)) : [];
+
+    foreach ((array) $settings as $index => $setting) {
+      $form = $this->buildRowForm($form, $index, $setting['original'], $setting['replacement']);
+      if (!$setting['original']) {
+        $empty++;
+      }
+    }
+
+    // Append up to 3 empty fields.
+    $index = count($settings);
+    while ($empty < 3) {
+      $form = $this->buildRowForm($form, $index, '', '');
+      $index++;
+      $empty++;
+    }
+
+    return $form;
+  }
+
+  /**
+   * Submit handler for _url_replace_filter_settings() form.
+   *
+   * Remove useless empty settings to keep variable as small as possible.
+   *
+   * Needs to be public to be usable as a #element_validate callback.
+   */
+  public function settingsFormValidate(array $form, FormStateInterface &$form_state) {
+    $settings = $form_state->getValue('filters')['url_replace_filter']['settings'][self::SETTING_NAME];
+
+    $validSettings = array_filter($settings, function (array $setting) {
+      return !(empty($setting['original']) && empty($setting['replacement']));
+    });
+
+    $result = serialize($validSettings);
+    $form_state->setValue(['filters', 'url_replace_filter', 'settings', self::SETTING_NAME], $result);
+
+    if (empty($validSettings)) {
+      $parameterName = 'filter_format';
+      /** @var \Drupal\filter\FilterFormatInterface $parameterValue */
+      $parameterValue = $this->currentRouteMatch->getParameter($parameterName);
+      $this->messenger->addMessage($this->t('URL Replace filter configuration is empty for @format: you could <a href=":edit">remove it</a> from this input format.', [
+        '@format' => $parameterValue->label(),
+        ':edit' => Url::fromRoute('entity.filter_format.edit_form', [
+          $parameterName => $parameterValue->id(),
+      ])->toString()]), MessengerInterface::TYPE_WARNING);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function tips($long = FALSE) {
+    if ($long) {
+      return $this->t('To post pieces of code, surround them with &lt;code&gt;...&lt;/code&gt; tags. For PHP code, you can use &lt;?php ... ?&gt;, which will also colour it based on syntax.');
+    }
+    else {
+      return $this->t('You may post code using &lt;code&gt;...&lt;/code&gt; (generic) or &lt;?php ... ?&gt; (highlighted PHP) tags.');
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function process($text, $langcode) {
+    // Escape code tags to prevent other filters from acting on them.
+    $text = preg_replace_callback('@<code>(.+?)</code>@s', [get_class($this), 'codeTagCallback'], $text);
+    $text = preg_replace_callback('@[\[<](\?php)(.+?)(\?)[\]>]@s', [get_class($this), 'phpTagCallback'], $text);
+
+    // Replace code.
+    $text = preg_replace_callback('@\[codefilter_code\](.+?)\[/codefilter_code\]@s', [get_class($this), 'processCodeCallback'], $text);
+    $text = preg_replace_callback('@\[codefilter_php\](.+?)\[/codefilter_php\]@s', [get_class($this), 'processPHPCallback'], $text);
+
+    // A hack, so we can conditionally nowrap based on filter settings.
+    // @todo Refactor how replacements are done so we can do this more cleanly.
+    if ($this->settings['nowrap_expand']) {
+      $text = str_replace('class="codeblock"', 'class="codeblock nowrap-expand"', $text);
+    }
+    return new FilterProcessResult($text);
+  }
+
+  /**
+   * Filter the given text.
+   */
+  public function _url_replace_filter_process($text, $format) {
+    $settings = _url_replace_filter_get_settings($format);
+    foreach ($settings as $setting) {
+      if ($setting['original']) {
+        $pattern = '!((<a\s[^>]*href)|(<img\s[^>]*src))\s*=\s*"' . preg_quote($setting['original']) . '!iU';
+        if (preg_match_all($pattern, $text, $matches)) {
+          $replacement = str_replace('%baseurl', rtrim(base_path(), '/'), $setting['replacement']);
+          foreach ($matches[0] as $key => $match) {
+            $text = str_replace($match, $matches[1][$key] . '="' . $replacement, $text);
+          }
+        }
+      }
+    }
+    return $text;
+  }
+
+  /**
+   * Callback to replace content of the <code> elements.
+   *
+   * @param array $matches
+   *   An array of matches passed by preg_replace_callback().
+   *
+   * @return string
+   *   A formatted string.
+   */
+  public static function processCodeCallback(array $matches) {
+    return self::processCode($matches[1]);
+  }
+
+  /**
+   * Callback to replace content of the <?php ?> elements.
+   *
+   * @param array $matches
+   *   An array of matches passed by preg_replace_callback().
+   *
+   * @return string
+   *   A formatted string.
+   */
+  public static function processPHPCallback(array $matches) {
+    return self::processPHP($matches[1]);
+  }
+
+  /**
+   * Escape code blocks.
+   *
+   * @param string $text
+   *   The string to escape.
+   * @param string $type
+   *   The type of code block, either 'code' or 'php'.
+   *
+   * @return string
+   *   The escaped string.
+   */
+  public static function escape($text, $type = 'code') {
+    // Note, pay attention to odd preg_replace-with-/e behaviour on slashes.
+    $text = Html::escape(str_replace('\"', '"', $text));
+
+    // Protect newlines from line break converter.
+    $text = str_replace(["\r", "\n"], ['', '&#10;'], $text);
+
+    // Add codefilter escape tags.
+    $text = "[codefilter_$type]{$text}[/codefilter_$type]";
+
+    return $text;
+  }
+
+  /**
+   * Processes chunks of escaped code into HTML.
+   */
+  public static function processCode($text) {
+    // Undo linebreak escaping.
+    $text = str_replace('&#10;', "\n", $text);
+    // Inline or block level piece?
+    $multiline = strpos($text, "\n") !== FALSE;
+    // Note, pay attention to odd preg_replace-with-/e behaviour on slashes.
+    $text = preg_replace("/^\n/", '', preg_replace('@</?(br|p)\s*/?>@', '', str_replace('\"', '"', $text)));
+    // Trim leading and trailing linebreaks.
+    $text = trim($text, "\n");
+    // Escape newlines.
+    $text = nl2br($text);
+
+    // PHP code in regular code.
+    $text = preg_replace_callback('/&lt;\?php.+?\?&gt;/s', [get_class(), 'processPHPInline'], $text);
+
+    $text = '<code>' . self::fixSpaces(str_replace(' ', '&nbsp;', $text)) . '</code>';
+    $text = $multiline ? '<div class="codeblock">' . $text . '</div>' : $text;
+    // Remove newlines to avoid clashing with the linebreak filter.
+    return str_replace("\n", '', $text);
+  }
+
+  /**
+   * Helper function for processCode().
+   */
+  public static function processPHPInline($matches) {
+    // Undo nl2br.
+    $text = str_replace('<br />', '', $matches[0]);
+    // Decode entities (the highlighter re-entifies) and highlight text.
+    $text = highlight_string(Html::decodeEntities($text), 1);
+    // Remove PHPs own added code tags.
+    $text = str_replace(['<code>', '</code>', "\n"], ['', '', ''], $text);
+    return $text;
+  }
+
+  /**
+   * Processes chunks of escaped PHP code into HTML.
+   */
+  public static function processPHP($text) {
+    // Note, pay attention to odd preg_replace-with-/e behaviour on slashes.
+    // Undo possible linebreak filter conversion.
+    $text = preg_replace('@</?(br|p)\s*/?>@', '', str_replace('\"', '"', $text));
+    // Undo the escaping in the prepare step.
+    $text = Html::decodeEntities($text);
+    // Trim leading and trailing linebreaks.
+    $text = trim($text, "\r\n");
+    // Highlight as PHP.
+    $text = '<div class="codeblock">' . highlight_string("<?php\n$text\n?>", 1) . '</div>';
+    // Remove newlines to avoid clashing with the linebreak filter.
+    $text = str_replace("\n", '', $text);
+    return self::fixSpaces($text);
+  }
+
+  /**
+   * Replace html space elements with literal space characters.
+   *
+   * @param string $text
+   *   A string to fix spaces for.
+   *
+   * @return string
+   *   A formatted string.
+   */
+  public static function fixSpaces($text) {
+    $text = preg_replace('@&nbsp;(?!&nbsp;)@', ' ', $text);
+    // A single space before text is ignored by browsers. If a single space
+    // follows a break tag, replace it with a non-breaking space.
+    $text = preg_replace('@<br /> ([^ ])@', '<br />&nbsp;$1', $text);
+    return $text;
+  }
+
+  /**
+   * Callback to escape content of <code> tags.
+   *
+   * @param array $matches
+   *   An array of matches passed by preg_replace_callback().
+   *
+   * @return string
+   *   A formatted string.
+   */
+  public static function codeTagCallback(array $matches) {
+    return self::escape($matches[1], 'code');
+  }
+
+  /**
+   * Callback to escape content of <?php ?>, [?php ?], <% %>, and [% %] tags.
+   *
+   * @param array $matches
+   *   An array of matches passed by preg_replace_callback().
+   *
+   * @return string
+   *   A formatted string.
+   */
+  public static function phpTagCallback(array $matches) {
+    return self::escape($matches[2], 'php');
+  }
+
+}

文件差異過大導致無法顯示
+ 51 - 0
src/Tests/UrlReplaceFilterTest.php


+ 1 - 0
templates/url-replace-filter-settings-form.html.twig

@@ -0,0 +1 @@
+{{ replacements }}

+ 0 - 3
url_replace_filter.info

@@ -1,3 +0,0 @@
-name = "URL Replace Filter"
-description = "Allows administrators to replace base URLs in &lt;img&gt; and &lt;a&gt; elements."
-core = 6.x

+ 7 - 0
url_replace_filter.info.yml

@@ -0,0 +1,7 @@
+name: "URL Replace Filter"
+type: module
+description: "Allows administrators to replace base URLs in &lt;img&gt; and &lt;a&gt; elements."
+package: OSInet
+core: 8.x
+dependencies:
+  - filter

+ 1 - 1
url_replace_filter.install

@@ -13,7 +13,7 @@
  *
  * Ensure the module is actually used in at least one filter.
  */
-function url_replace_filter_requirements($phase) {
+function Zurl_replace_filter_requirements($phase) {
   if ($phase != 'runtime') {
     return array();
   }

+ 6 - 153
url_replace_filter.module

@@ -9,154 +9,7 @@
  * @license Licensed under the General Public License version 2.0 or later.
  */
 
-/**
- * Implements hook_filter().
- */
-function url_replace_filter_filter($operation, $delta = 0, $format = -1, $text = '') {
-  if (intval($delta) !== 0) {
-    watchdog('url_replace_filter', '@function invoked with non-zero delta: @delta. This should never happen.', array(
-      '@function' => __FUNCTION__,
-      '@delta' => $delta,
-    ), WATCHDOG_ERROR);
-  }
-
-  switch ($operation) {
-    case 'list':
-      return array(0 => t('URL Replace Filter'));
-
-    case 'description':
-      return t('Allows administrators to replace the base URL in &lt;img&gt; and &lt;a&gt; elements.');
-
-    case 'settings':
-      return _url_replace_filter_settings($format);
-
-    case 'process':
-      $text = _url_replace_filter_process($text, $format);
-      return $text;
-
-    default:
-      return $text;
-  }
-}
-
-/**
- * Filter the given text.
- */
-function _url_replace_filter_process($text, $format) {
-  $settings = _url_replace_filter_get_settings($format);
-  foreach ($settings as $setting) {
-    if ($setting['original']) {
-      $pattern = '!((<a\s[^>]*href)|(<img\s[^>]*src))\s*=\s*"' . preg_quote($setting['original']) . '!iU';
-      if (preg_match_all($pattern, $text, $matches)) {
-        $replacement = str_replace('%baseurl', rtrim(base_path(), '/'), $setting['replacement']);
-        foreach ($matches[0] as $key => $match) {
-          $text = str_replace($match, $matches[1][$key] . '="' . $replacement, $text);
-        }
-      }
-    }
-  }
-  return $text;
-}
-
-/**
- * Settings for the HTML filter.
- */
-function _url_replace_filter_settings($format) {
-  $empty = 0;
-  $form['url_replace_filter_' . $format] = array(
-    '#type' => 'fieldset',
-    '#title' => t('URL Replace Filter'),
-    '#collapsible' => TRUE,
-    '#collapsed' => FALSE,
-    '#theme' => 'url_replace_filter_settings_form',
-    '#tree' => TRUE,
-  );
-  $form['#submit'][] = '_url_replace_filter_settings_form_submit';
-
-  $settings = _url_replace_filter_get_settings($format);
-  $index = 0;
-  foreach ($settings as $index => $setting) {
-    _url_replace_filter_row_form($form, $format, $index, $setting['original'], $setting['replacement']);
-    if (!$setting['original']) {
-      $empty++;
-    }
-  }
-  // Append some empty fields.
-  while ($empty < 3) {
-    $index++;
-    $empty++;
-    _url_replace_filter_row_form($form, $format, $index, '', '');
-  }
-  return $form;
-}
-
-/**
- * Submit handler for _url_replace_filter_settings() form.
- *
- * Remove useless empty settings to keep variable as small as possible.
- */
-function _url_replace_filter_settings_form_submit($form, &$form_state) {
-  $format = $form_state['values']['format'];
-  $format_id = 'url_replace_filter_' . $format;
-  if (!isset($form_state['values'][$format_id])) {
-    return;
-  }
-
-  // Remove empty sets.
-  foreach ($form_state['values'][$format_id] as $key => $value) {
-    if (empty($value['original']) && empty($value['replacement'])) {
-      unset($form_state['values'][$format_id][$key]);
-    }
-  }
-
-  if (empty($form_state['values'][$format_id])) {
-    drupal_set_message(t('URL Replace filter configuration is empty : you could <a href="!edit">remove it</a> from this input format.', array(
-      '!edit' => check_url(url('admin/settings/filters/1')),
-      )), 'warning');
-  }
-}
-
-/**
- * Helper to build rows in the table built by _url_replace_filter_settings().
- *
- * @param array $form
- *   The form array.
- * @param int $format
- *   The format index.
- * @param int $index
- *   The format index.
- * @param string $original
- *   The original value to be replaced.
- * @param string $replacement
- *   The replacement for the original value.
- */
-function _url_replace_filter_row_form(array &$form, $format, $index, $original, $replacement) {
-  $form['url_replace_filter_' . $format][$index]['original'] = array(
-    '#type' => 'textfield',
-    '#size' => 50,
-    '#default_value' => $original,
-  );
-  $form['url_replace_filter_' . $format][$index]['replacement'] = array(
-    '#type' => 'textfield',
-    '#size' => 50,
-    '#default_value' => $replacement,
-  );
-}
-
-/**
- * Helper for _url_replace_filter_process().
- *
- * @param int $format
- *   The id of the format.
- *
- * @return mixed
- *   The replacement information for the format.
- */
-function _url_replace_filter_get_settings($format) {
-  return variable_get('url_replace_filter_' . $format, array(
-    0 => array('original' => '', 'replacement' => ''),
-  ));
-}
+use Drupal\url_replace_filter\Plugin\Filter\UrlReplaceFilter;
 
 /**
  * Return the list of input formats containing the URL Replace filter.
@@ -250,11 +103,11 @@ function url_replace_filter_form_system_modules_alter(array &$form) {
  * Implements hook_theme().
  */
 function url_replace_filter_theme() {
-  $ret = array(
-    'url_replace_filter_settings_form' => array(
-      'arguments' => array('form' => NULL),
-    ),
-  );
+  $ret = [
+    'url_replace_filter_settings_form' => [
+      'render element' => UrlReplaceFilter::SETTING_NAME,
+    ],
+  ];
 
   return $ret;
 }

部分文件因文件數量過多而無法顯示