filter.module 12.2 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 44
  }
}

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

Dries's avatar
 
Dries committed
54 55 56 57 58
function filter_admin_order() {
  $edit = $_POST["edit"];
  $op = $_POST["op"];
  if ($op == t("Save configuration")) {
    foreach ($edit as $module => $filter) {
59
      db_query("UPDATE {filters} SET weight = %d WHERE module = '%s'", $filter["weight"], $module);
Dries's avatar
 
Dries committed
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 90 91 92 93 94 95 96
    }
  }

  // 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;
}

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

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

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

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

  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;
141 142 143 144
}

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

148 149 150 151 152 153
    // 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
154
    }
155

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

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

    /*
    ** If only inline elements are used and no block level elements, we
    ** replace all newlines with HTML line breaks.
    */
Dries's avatar
 
Dries committed
168
    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) {
169 170 171 172 173 174 175 176 177 178 179
      $text = nl2br($text);
    }
  }
  else {
    $text = message_na();
  }

  return $text;
}

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

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

  return trim($text);
}

Dries's avatar
 
Dries committed
197
function filter_default_settings() {
198
  $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."));
199
  $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."));
200
  $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
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
  $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;
  }
}

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 286 287 288 289 290
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
291 292 293 294 295
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
296 297
    }
  }
Dries's avatar
 
Dries committed
298
  $output = "<ul class=\"filter-tips-long\">\n$tiplist\n</ul>\n";
Dries's avatar
 
Dries committed
299
  print theme("page", $output, t('Compose Tips'));
Dries's avatar
 
Dries committed
300 301 302
}

function filter_tips_short() {
Dries's avatar
 
Dries committed
303
  $tiplist = '';
Dries's avatar
 
Dries committed
304 305
  foreach (module_list() as $name) {
    if ($tip = module_invoke($name, 'help', 'filter#short-tip')) {
Dries's avatar
 
Dries committed
306
      $tiplist .= "<li>$tip</li>\n";
Dries's avatar
 
Dries committed
307 308
    }
  }
Dries's avatar
 
Dries committed
309 310
  $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
311 312 313
}

?>