filter.module 12.3 KB
Newer Older
1 2 3
<?php
// $Id$

4 5 6 7 8 9 10
define('FILTER_HTML_DONOTHING', 0);
define('FILTER_HTML_STRIP', 1);
define('FILTER_HTML_ESCAPE', 2);

define('FILTER_STYLE_ALLOW', 0);
define('FILTER_STYLE_STRIP', 1);

11 12 13 14 15
function filter_help($section = "admin/help#filter") {
  switch ($section) {
    case 'admin/system/modules#description':
      return t("Framework for handling filtering of content.");
    case 'admin/system/filters':
Dries's avatar
 
Dries committed
16 17 18 19 20 21 22
      return t("
<p>Filters fit between the raw text in a node and the HTML output. They allow you to replace text selectively. Uses include automatic conversion of emoticons into graphics and filtering HTML content from users' submissions.</p>
<p>If you notice some filters are causing conflicts in the output, you can <a href=\"%url\">rearrange them</a>.</p>", array("%url" => url("admin/system/filters/order")));
    case 'admin/system/filters/order':
      return t("
<p>Because of the flexible filtering system, you might encounter a situation where one filter prevents another from doing its job. For example: a word in an URL gets converted into a glossary term, before the URL can be converted in a clickable link. When this happens, you will need to rearrange the order in which filters get executed.</p>
<p>Filters are executed from top-to-bottom. You can use the weight column to rearrange them: heavier filters 'sink' to the bottom. Standard HTML filtering is always run first.</p>");
Dries's avatar
 
Dries committed
23 24
    case 'filter#long-tip':
    case 'filter#short-tip':
25
      switch (variable_get("filter_html", FILTER_HTML_DONOTHING)) {
Dries's avatar
 
Dries committed
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
        case 0:
          return t("All HTML tags allowed");
          break;
        case 1:
          if ($allowed_html = variable_get("allowed_html", "<a> <b> <dd> <dl> <dt> <i> <li> <ol> <u> <ul>")) {
            return t("Allowed HTML tags") .": ". htmlspecialchars($allowed_html);
          } else {
            return t("No HTML tags allowed");
          }
          break;
        case 2:
          return t("No HTML tags allowed");
          break;
      }
      break;
41 42 43
  }
}

Dries's avatar
 
Dries committed
44 45 46
/**
 * Implementation of hook_link().
 */
47
function filter_link($type) {
Dries's avatar
 
Dries committed
48 49 50 51
  if ($type == 'system') {
    menu('admin/system/filters', t('filters'), user_access('administer site configuration') ? 'filter_admin' : MENU_DENIED, 5);
    menu('admin/system/filters/order', t('ordering'), user_access('administer site configuration') ? 'filter_admin' : MENU_DENIED, 5);
    menu('filter/tips', t('compose tips'), 'filter_tips_long', 0, MENU_HIDE);
52 53 54
  }
}

Dries's avatar
 
Dries committed
55 56 57 58 59
function filter_admin_order() {
  $edit = $_POST["edit"];
  $op = $_POST["op"];
  if ($op == t("Save configuration")) {
    foreach ($edit as $module => $filter) {
60
      db_query("UPDATE {filters} SET weight = %d WHERE module = '%s'", $filter["weight"], $module);
Dries's avatar
 
Dries committed
61 62 63 64 65 66 67
    }
  }

  // Get list (with forced refresh)
  filter_refresh();
  $filters = filter_list();

68
  $header = array(t('name'), t('weight'));
Dries's avatar
 
Dries committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
  $rows = array();

  // Standard HTML filters are always run first, we add a dummy row to indicate this
  $rows[] = array(t("HTML filtering"), array("data" => t("locked")));

  foreach ($filters as $module => $filter) {
    $name = module_invoke($module, "filter", "name");
    $rows[] = array($name, array("data" => form_weight(NULL, $module ."][weight", $filter["weight"])));
  }

  $form  = theme("table", $header, $rows);
  $form .= form_submit(t("Save configuration"));
  $output = form($form);

  return $output;
}

function filter_admin_settings() {
  system_settings_save();

  filter_refresh();

  $form  = filter_default_settings();
  $form .= implode("\n", module_invoke_all("filter", "settings"));
  $output = system_settings_form($form);

  return $output;
}

98
function filter_admin() {
Dries's avatar
 
Dries committed
99 100 101 102 103 104 105
  switch (arg(3)) {
    case "order":
      $output = filter_admin_order();
      break;
    default:
      $output = filter_admin_settings();
      break;
106
  }
Dries's avatar
 
Dries committed
107
  print theme("page", $output);
108 109
}

Dries's avatar
 
Dries committed
110 111 112
function filter_refresh() {
  $modules = module_list();
  $filters = filter_list();
113

Dries's avatar
 
Dries committed
114 115 116 117 118
  // Update list in database
  db_query("DELETE FROM {filters}");
  foreach ($modules as $module) {
    if (module_hook($module, "filter")) {
      $weight = $filters[$module]["weight"];
119

120
      db_query("INSERT INTO {filters} (module, weight) VALUES ('%s', %d)", $module, $weight);
Dries's avatar
 
Dries committed
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
    }
  }

  filter_list(1);
}

function filter_list($force = 0) {
  static $filters;

  if (!is_array($filters) || $force) {
    $filters = array();
    $result = db_query("SELECT * FROM {filters} ORDER BY weight ASC");
    while ($filter = db_fetch_array($result)) {
      // Fail-safe in case a module was deleted/changed without disabling it
      if (module_hook($filter["module"], "filter")) {
        $filters[$filter["module"]] = $filter;
      }
    }
  }

  return $filters;
142 143 144 145
}

function check_output($text) {
  if (isset($text)) {
Dries's avatar
 
Dries committed
146 147 148
    // Filter content on output:
    $filters = filter_list();

149 150 151 152 153 154
    // Give filters the chance to escape HTML-like data such as code or formulas
    // (from this point on, the input can be treated as HTML)
    if (variable_get("filter_html", FILTER_HTML_DONOTHING) != FILTER_HTML_ESCAPE) {
      foreach ($filters as $module => $filter) {
        $text = module_invoke($module, "filter", "prepare", $text);
      }
Dries's avatar
 
Dries committed
155
    }
156

Dries's avatar
 
Dries committed
157
    // HTML handling is done before all regular filtering activities
158 159
    $text = filter_default($text);

Dries's avatar
 
Dries committed
160 161 162
    // Regular filtering
    foreach ($filters as $module => $filter) {
      $text = module_invoke($module, "filter", "process", $text);
163 164 165 166 167 168
    }

    /*
    ** If only inline elements are used and no block level elements, we
    ** replace all newlines with HTML line breaks.
    */
Dries's avatar
 
Dries committed
169
    if (strip_tags($text, '<a><br><span><bdo><map><object><img><tt><i><b><u><big><small><em><strong><dfn><code><q><samp><kbd><var><cite><abbr><acronym><sub><sup><input><select><textarea><label><button><ins><del><script>') == $text) {
170 171 172 173 174 175 176 177 178 179 180
      $text = nl2br($text);
    }
  }
  else {
    $text = message_na();
  }

  return $text;
}

function filter_default($text) {
181
  if (variable_get("filter_html", FILTER_HTML_DONOTHING) == FILTER_HTML_STRIP) {
182 183
    // Allow users to enter HTML, but filter it
    $text = strip_tags($text, variable_get("allowed_html", ""));
184
    if (variable_get("filter_style", FILTER_STYLE_STRIP)) {
185 186 187 188 189
      $text = preg_replace("/\Wstyle\s*=[^>]+?>/i", ">", $text);
    }
    $text = preg_replace("/\Won[a-z]+\s*=[^>]+?>/i", ">", $text);
  }

190
  if (variable_get("filter_html", FILTER_HTML_DONOTHING) == FILTER_HTML_ESCAPE) {
Dries's avatar
 
Dries committed
191
    // Escape HTML
192 193 194 195 196 197
    $text = htmlspecialchars($text);
  }

  return trim($text);
}

Dries's avatar
 
Dries committed
198
function filter_default_settings() {
199
  $group   = form_radios(t("Filter HTML tags"), "filter_html", variable_get("filter_html", FILTER_HTML_DONOTHING), array(FILTER_HTML_DONOTHING => t("Do not filter"), FILTER_HTML_STRIP => t("Strip tags"), FILTER_HTML_ESCAPE => t("Escape tags")), t("How to deal with HTML and PHP tags in user-contributed content. If set to \"Strip tags\", dangerous tags are removed (see below).  If set to \"Escape tags\", all HTML is escaped and presented as it was typed."));
200
  $group  .= form_textfield(t("Allowed HTML tags"), "allowed_html", variable_get("allowed_html", "<a> <b> <dd> <dl> <dt> <i> <li> <ol> <u> <ul>"), 64, 255, t("If \"Strip tags\" is selected, optionally specify tags which should not be stripped.  'ON*' attributes are always stripped."));
201
  $group  .= form_radios(t("HTML style attributes"), "filter_style", variable_get("filter_style", FILTER_STYLE_STRIP), array(FILTER_STYLE_ALLOW => t("Allowed"), FILTER_STYLE_STRIP => t("Removed")), t("If \"Strip tags\" is selected, you can choose whether 'STYLE' attributes are allowed or removed from input."));
Dries's avatar
 
Dries committed
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
  $output .= form_group(t("HTML filtering"), $group);

  return $output;
}

function filter_filter($op, $text = "") {
  switch ($op) {
    case "name":
      return t("Legacy filtering");
    case "process":
      if (variable_get("rewrite_old_urls", 0)) {
        $text = filter_old_urls($text);
      }
      return $text;
    case "settings":
      $group   = form_radios(t("Rewrite old URLs"), "rewrite_old_urls", variable_get("rewrite_old_urls", 0), array(t("Disabled"), t("Enabled")), t("The introduction of 'clean URLs' in Drupal 4.2.0 breaks internal URLs that date back from Drupal 4.1.0 and before.  If enabled, this filter will attempt to rewrite the old style URLs to avoid broken links.  If <code>mod_rewrite</code> is available on your system, use the rewrite rules in Drupal's <code>.htaccess</code> file instead as these will also correct external referrers."));
      $output .= form_group(t("Legacy filtering"), $group);
      return $output;
    default:
      return $text;
  }
}

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
function filter_old_urls($text) {
  global $base_url;

  $end = substr($base_url, 12);

  /*
  ** This is a *temporary* filter to rewrite old-style URLs to new-style
  ** URLs (clean URLs).  Currently, URLs are being rewritten dynamically
  ** (ie. "on output"), however when these rewrite rules have been tested
  ** enough, we will use them to permanently rewrite the links in node
  ** and comment bodies.
  */

  if (variable_get("clean_url", "0") == "0") {
    /*
    ** Relative URLs:
    */

    // rewrite 'node.php?id=<number>[&cid=<number>]' style URLs:
    $text = eregi_replace("\"(node)\.php\?id=([[:digit:]]+)(&cid=)?([[:digit:]]*)", "\"?q=\\1/view/\\2/\\4", $text);

    // rewrite 'module.php?mod=<name>{&<op>=<value>}' style URLs:
    $text = ereg_replace("\"module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "\"?q=\\2/\\4/\\6" , $text);
    $text = ereg_replace("\"module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "\"?q=\\2/\\4", $text);
    $text = ereg_replace("\"module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))", "\"?q=\\2", $text);

    /*
    ** Absolute URLs:
    */

    // rewrite 'node.php?id=<number>[&cid=<number>]' style URLs:
    $text = eregi_replace("$end/(node)\.php\?id=([[:digit:]]+)(&cid=)?([[:digit:]]*)", "$end/?q=\\1/view/\\2/\\4", $text);

    // rewrite 'module.php?mod=<name>{&<op>=<value>}' style URLs:
    $text = ereg_replace("$end/module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "$end/?q=\\2/\\4/\\6" , $text);
    $text = ereg_replace("$end/module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "$end/?q=\\2/\\4", $text);
    $text = ereg_replace("$end/module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))", "\"$end/?q=\\2", $text);
  }
  else {
    /*
    ** Relative URLs:
    */

    // rewrite 'node.php?id=<number>[&cid=<number>]' style URLs:
    $text = eregi_replace("\"(node)\.php\?id=([[:digit:]]+)(&cid=)?([[:digit:]]*)", "\"\\1/view/\\2/\\4", $text);

    // rewrite 'module.php?mod=<name>{&<op>=<value>}' style URLs:
    $text = ereg_replace("\"module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "\"\\2/\\4/\\6", $text);
    $text = ereg_replace("\"module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "\"\\2/\\4", $text);
    $text = ereg_replace("\"module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))", "\"\\2", $text);

    /*
    ** Absolute URLs:
    */

    // rewrite 'node.php?id=<number>[&cid=<number>]' style URLs:
    $text = eregi_replace("$end/(node)\.php\?id=([[:digit:]]+)(&cid=)?([[:digit:]]*)", "$end/\\1/view/\\2/\\4", $text);

    // rewrite 'module.php?mod=<name>{&<op>=<value>}' style URLs:
    $text = ereg_replace("$end/module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "$end/\\2/\\4/\\6", $text);
    $text = ereg_replace("$end/module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))(&?[[:alpha:]]+=([[:alnum:]]+))", "$end/\\2/\\4", $text);
    $text = ereg_replace("$end/module\.php\?(&?[[:alpha:]]+=([[:alnum:]]+))", "$end/\\2", $text);
  }

  return $text;
}

Dries's avatar
 
Dries committed
292 293 294 295 296
function filter_tips_long() {
  $tiplist = '';
  foreach (module_list() as $name) {
    if ($tip = module_invoke($name, 'help', 'filter#long-tip')) {
      $tiplist .= "<li id=\"filter-$name\">$tip</li>\n";
Dries's avatar
 
Dries committed
297 298
    }
  }
Dries's avatar
 
Dries committed
299
  $output = "<ul class=\"filter-tips-long\">\n$tiplist\n</ul>\n";
Dries's avatar
 
Dries committed
300
  print theme("page", $output, t('Compose Tips'));
Dries's avatar
 
Dries committed
301 302 303
}

function filter_tips_short() {
Dries's avatar
 
Dries committed
304
  $tiplist = '';
Dries's avatar
 
Dries committed
305 306
  foreach (module_list() as $name) {
    if ($tip = module_invoke($name, 'help', 'filter#short-tip')) {
Dries's avatar
 
Dries committed
307
      $tiplist .= "<li>$tip</li>\n";
Dries's avatar
 
Dries committed
308 309
    }
  }
Dries's avatar
 
Dries committed
310 311
  $tiplist .= '<li class="more-tips">' . l(t('More information on formatting options'), 'filter/tips') . '</li>';
  return "<ul class=\"filter-tips-short\">\n$tiplist\n</ul>\n";
Dries's avatar
 
Dries committed
312 313 314
}

?>