filter.module 11.7 KB
Newer Older
1 2 3 4 5 6 7 8
<?php
// $Id$

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
9 10 11 12 13 14 15
      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
16 17
    case 'filter#long-tip':
    case 'filter#short-tip':
Dries's avatar
 
Dries committed
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
      switch (variable_get("filter_html", 1)) {
        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;
34 35 36 37
  }
}

function filter_link($type) {
Dries's avatar
 
Dries committed
38 39 40
  if ($type == "system") {
    if (user_access("administer site configuration")) {
      menu("admin/system/filters", t("filters"), "filter_admin", 5);
Dries's avatar
 
Dries committed
41
      menu("admin/system/filters/order", t("ordering"), "filter_admin", 5);
Dries's avatar
 
Dries committed
42
    }
Dries's avatar
 
Dries committed
43
    menu("filter/tips", t("Compose tips"), "filter_tips_long", 0, MENU_HIDE);
44 45 46
  }
}

Dries's avatar
 
Dries committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
function filter_admin_order() {
  $edit = $_POST["edit"];
  $op = $_POST["op"];
  if ($op == t("Save configuration")) {
    foreach ($edit as $module => $filter) {
      db_query("UPDATE {filters} SET weight = '%d' WHERE module = '%s'", $filter["weight"], $module);
    }
  }

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

  $header = array("name", "weight");
  $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;
}

90 91
function filter_admin() {
  if (user_access("administer site configuration")) {
Dries's avatar
 
Dries committed
92 93 94 95 96 97 98 99
    switch (arg(3)) {
      case "order":
        $output = filter_admin_order();
        break;
      default:
        $output = filter_admin_settings();
        break;
    }
100 101 102 103 104 105 106
    print theme("page", $output);
  }
  else {
    print theme("page", message_access());
  }
}

Dries's avatar
 
Dries committed
107 108 109
function filter_refresh() {
  $modules = module_list();
  $filters = filter_list();
110

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

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

  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;
139 140 141 142
}

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

    // Give filters the chance to escape HTML-like data before being passed to the HTML stripper
    foreach ($filters as $module => $filter) {
      $text = module_invoke($module, "filter", "prepare", $text);
    }
150

Dries's avatar
 
Dries committed
151
    // HTML handling is done before all regular filtering activities
152 153
    $text = filter_default($text);

Dries's avatar
 
Dries committed
154 155 156
    // Regular filtering
    foreach ($filters as $module => $filter) {
      $text = module_invoke($module, "filter", "process", $text);
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
    }

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

  return $text;
}

function filter_default($text) {
  if (variable_get("filter_html", 0) == 1) {
    // Allow users to enter HTML, but filter it
    $text = strip_tags($text, variable_get("allowed_html", ""));
    if (variable_get("filter_style", 1)) {
      $text = preg_replace("/\Wstyle\s*=[^>]+?>/i", ">", $text);
    }
    $text = preg_replace("/\Won[a-z]+\s*=[^>]+?>/i", ">", $text);
  }

  if (variable_get("filter_html", 0) == 2) {
Dries's avatar
 
Dries committed
185
    // Escape HTML
186 187 188 189 190 191
    $text = htmlspecialchars($text);
  }

  return trim($text);
}

Dries's avatar
 
Dries committed
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
function filter_default_settings() {
  $group   = form_radios(t("Filter HTML tags"), "filter_html", variable_get("filter_html", 0), array(0 => t("Do not filter"), 1 => t("Strip tags"), 2 => 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."));
  $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 and unclosed tags are always stripped."));
  $group  .= form_radios(t("HTML style attributes"), "filter_style", variable_get("filter_style", 1), array(t("Allowed"), t("Removed")), t("If \"Strip tags\" is selected, you can choose whether 'STYLE' attributes are allowed or removed from input."));
  $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;
  }
}

219 220 221 222 223 224 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
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
286 287 288 289 290
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
291 292 293
    }
  }
  $output = "<ul class=\"compose-tips\">\n$tiplist\n</ul>\n";
Dries's avatar
 
Dries committed
294
  print theme("page", $output, t('Compose Tips'));
Dries's avatar
 
Dries committed
295 296 297
}

function filter_tips_short() {
Dries's avatar
 
Dries committed
298 299 300 301 302 303 304 305
  $output = '';
  foreach (module_list() as $name) {
    if ($tip = module_invoke($name, 'help', 'filter#short-tip')) {
      $output .= "$tip<br />";
    }
  }
  $output .= l(t('More information on formatting options'), 'filter/tips');
  return $output;
Dries's avatar
 
Dries committed
306 307 308
}

?>