[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/ctools/includes/ -> css.inc (source)

   1  <?php
   2  /* $Id: css.inc,v 1.10.2.7 2010/10/15 21:10:11 merlinofchaos Exp $ */
   3  
   4  /*
   5   * @file
   6   * CSS filtering functions. Contains a disassembler, filter, compressor, and
   7   * decompressor.
   8   *
   9   * The general usage of this tool is:
  10   *
  11   * To simply filter CSS:
  12   * @code
  13   *   $filtered_css = ctools_css_filter($css, TRUE);
  14   * @endcode
  15   *
  16   * In the above, if the second argument is TRUE, the returned CSS will
  17   * be compressed. Otherwise it will be returned in a well formatted
  18   * syntax.
  19   *
  20   * To cache unfiltered CSS in a file, which will be filtered:
  21   *
  22   * @code
  23   *   $filename = ctools_css_cache($css, TRUE);
  24   * @endcode
  25   *
  26   * In the above, if the second argument is FALSE, the CSS will not be filtered.
  27   *
  28   * This file will be cached within the Drupal files system. This system cannot
  29   * detect when this file changes, so it is YOUR responsibility to remove and
  30   * re-cache this file when the CSS is changed. Your system should also contain
  31   * a backup method of re-generating the CSS cache in case it is removed, so
  32   * that it is easy to force a re-cache by simply deleting the contents of the
  33   * directory.
  34   *
  35   * Finally, if for some reason your application cannot store the filename
  36   * (which is true of Panels where the style can't force the display to
  37   * resave unconditionally) you can use the ctools storage mechanism. You
  38   * simply have to come up with a unique Id:
  39   *
  40   * @code
  41   *   $filename = ctools_css_store($id, $css, TRUE);
  42   * @endcode
  43   *
  44   * Then later on:
  45   * @code
  46   *   $filename = ctools_css_retrieve($id);
  47   *   ctools_css_add_css($filename);
  48   * @endcode
  49   *
  50   * The CSS that was generated will be stored in the database, so even if the
  51   * file was removed the cached CSS will be used. If the CSS cache is
  52   * cleared you may be required to regenerate your CSS. This will normally
  53   * only be cleared by an administrator operation, not during normal usage.
  54   *
  55   * You may remove your stored CSS this way:
  56   *
  57   * @code
  58   *   ctools_css_clear($id);
  59   * @endcode
  60   */
  61  
  62  /**
  63   * Store CSS with a given id and return the filename to use.
  64   *
  65   * This function associates a piece of CSS with an id, and stores the
  66   * cached filename and the actual CSS for later use with
  67   * ctools_css_retrieve.
  68   */
  69  function ctools_css_store($id, $css, $filter = TRUE) {
  70    $filename = db_result(db_query("SELECT filename FROM {ctools_css_cache} WHERE cid = '%s'", $id));
  71    if ($filename && file_exists($filename)) {
  72      file_delete($filename);
  73    }
  74    // Remove any previous records.
  75    db_query("DELETE FROM {ctools_css_cache} WHERE cid = '%s'", $id);
  76  
  77    $filename = ctools_css_cache($css, $filter);
  78  
  79    db_query("INSERT INTO {ctools_css_cache} (cid, filename, css, filter) VALUES ('%s', '%s', '%s', %d)", $id, $filename, $css, $filter);
  80  
  81    return $filename;
  82  }
  83  
  84  /**
  85   * Retrieve a filename associated with an id of previously cached CSS.
  86   *
  87   * This will ensure the file still exists and, if not, create it.
  88   */
  89  function ctools_css_retrieve($id) {
  90    $cache = db_fetch_object(db_query("SELECT * FROM {ctools_css_cache} WHERE cid = '%s'", $id));
  91    if (!$cache) {
  92      return;
  93    }
  94  
  95    if (!file_exists($cache->filename)) {
  96      $filename = ctools_css_cache($cache->css, $cache->filter);
  97      if ($filename != $cache->filename) {
  98        db_query("UPDATE {ctools_css_cache} SET filename = '%s' WHERE cid = '%s'", $filename, $id);
  99        $cache->filename = $filename;
 100      }
 101    }
 102  
 103    return $cache->filename;
 104  }
 105  
 106  /**
 107   * Remove stored CSS and any associated file.
 108   */
 109  function ctools_css_clear($id) {
 110    $cache = db_fetch_object(db_query("SELECT * FROM {ctools_css_cache} WHERE cid = '%s'", $id));
 111    if (!$cache) {
 112      return;
 113    }
 114  
 115    if (file_exists($cache->filename)) {
 116      file_delete($cache->filename);
 117      // If we remove an existing file, there may be cached pages that refer
 118      // to it. We must get rid of them:
 119      cache_clear_all();
 120    }
 121  
 122    db_query("DELETE FROM {ctools_css_cache} WHERE cid = '%s'", $id);
 123  }
 124  
 125  /**
 126   * Write a chunk of CSS to a temporary cache file and return the file name.
 127   *
 128   * This function optionally filters the CSS (always compressed, if so) and
 129   * generates a unique filename based upon md5. It returns that filename that
 130   * can be used with ctools_css_add_css(). Note that as a cache file, technically
 131   * this file is volatile so it should be checked before it is used to ensure
 132   * that it exists.
 133   *
 134   * You can use file_exists() to test for the file and file_delete() to remove
 135   * it if it needs to be cleared.
 136   *
 137   * @param $css
 138   *   A chunk of well-formed CSS text to cache.
 139   * @param $filter
 140   *   If TRUE the css will be filtered. If FALSE the text will be cached
 141   *   as-is.
 142   *
 143   * @return $filename
 144   *   The filename the CSS will be cached in.
 145   */
 146  function ctools_css_cache($css, $filter = TRUE) {
 147    if ($filter) {
 148      $css = ctools_css_filter($css);
 149    }
 150  
 151    // Create the css/ within the files folder.
 152    $path = file_create_path('ctools/css');
 153    if (!file_check_directory($path)) {
 154      $path = file_directory_path() . '/ctools';
 155      file_check_directory($path, FILE_CREATE_DIRECTORY);
 156      $path .= '/css';
 157      file_check_directory($path, FILE_CREATE_DIRECTORY);
 158    }
 159  
 160    if (!file_check_directory($path)) {
 161      drupal_set_message(t('Unable to create CTools CSS cache directory. Check the permissions on your files directory.'), 'error');
 162      return;
 163    }
 164  
 165    // @todo Is this slow? Does it matter if it is?
 166    $filename = $path . '/' . md5($css) . '.css';
 167  
 168    // This will do renames if the file already exists, ensuring we don't
 169    // accidentally overwrite other files who share the same md5. Yes this
 170    // is a very miniscule chance but it's safe.
 171    $filename = file_save_data($css, $filename);
 172  
 173    return $filename;
 174  }
 175  
 176  /**
 177   * Add a CTools CSS file to the page.
 178   *
 179   * Because drupal_add_css() does not handle files that it cannot stat, it
 180   * can't add files that are stored in a private file system. This will
 181   * will check to see if we're using the private file system and use
 182   * drupal_set_html_head() instead if that is the case.
 183   *
 184   * Sadly that will preclude aggregation of any sort, but there doesn't seem to
 185   * be any ways around that. Also it will screw with stylesheet order. Again,
 186   * sorry.
 187   */
 188  function ctools_css_add_css($filename = NULL, $type = 'module', $media = 'all', $preprocess = TRUE) {
 189    switch (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC)) {
 190      case FILE_DOWNLOADS_PUBLIC:
 191        drupal_add_css($filename, $type, $media, $preprocess);
 192        break;
 193      case FILE_DOWNLOADS_PRIVATE:
 194        $path = file_create_path($filename);
 195        if ($path) {
 196          $url = file_create_url($path);
 197        }
 198        else {
 199          $url = $filename;
 200        }
 201  
 202        $output = '<link type="text/css" rel="stylesheet" media="' . $media . '" href="' . $url . '" />'."\n";
 203        drupal_set_html_head($output);
 204    }
 205  
 206  }
 207  
 208  /**
 209   * Filter a chunk of CSS text.
 210   *
 211   * This function disassembles the CSS into a raw format that makes it easier
 212   * for our tool to work, then runs it through the filter and reassembles it.
 213   * If you find that you want the raw data for some reason or another, you
 214   * can use the disassemble/assemble functions yourself.
 215   *
 216   * @param $css
 217   *   The CSS text to filter.
 218   * @param $compressed
 219   *   If true, generate compressed output; if false, generate pretty output.
 220   *   Defaults to TRUE.
 221   */
 222  function ctools_css_filter($css, $compressed = TRUE) {
 223    $css_data = ctools_css_disassemble($css);
 224  
 225    // Note: By using this function yourself you can control the allowed
 226    // properties and values list.
 227    $filtered = ctools_css_filter_css_data($css_data);
 228  
 229    return $compressed ? ctools_css_compress($filtered) : ctools_css_assemble($filtered);
 230  }
 231  
 232  /**
 233   * Re-assemble a css string and format it nicely.
 234   *
 235   * @param array $css_data
 236   *   An array of css data, as produced by @see ctools_css_disassemble()
 237   *   disassembler and the @see ctools_css_filter_css_data() filter.
 238   *
 239   * @return string $css
 240   *   css optimized for human viewing.
 241   */
 242  function ctools_css_assemble($css_data) {
 243    // Initialize the output.
 244    $css = '';
 245    // Iterate through all the statements.
 246    foreach ($css_data as $selector_str => $declaration) {
 247      // Add the selectors, separating them with commas and line feeds.
 248      $css .= strpos($selector_str, ',') === FALSE ? $selector_str : str_replace(", ", ",\n", $selector_str);
 249      // Add the opening curly brace.
 250      $css .= " {\n";
 251      // Iterate through all the declarations.
 252      foreach ($declaration as $property => $value) {
 253        $css .= "  " . $property . ": " . $value . ";\n";
 254      }
 255      // Add the closing curly brace.
 256      $css .= "}\n\n";
 257    }
 258    // Return the output.
 259    return $css;
 260  }
 261  
 262  /**
 263   * Compress css data (filter it first!) to optimize for use on view.
 264   *
 265   * @param array $css_data
 266   *   An array of css data, as produced by @see ctools_css_disassemble()
 267   *   disassembler and the @see ctools_css_filter_css_data() filter.
 268   *
 269   * @return string $css
 270   *   css optimized for use.
 271   */
 272  function ctools_css_compress($css_data) {
 273    // Initialize the output.
 274    $css = '';
 275    // Iterate through all the statements.
 276    foreach ($css_data as $selector_str => $declaration) {
 277      if (empty($declaration)) {
 278        // Skip this statement if filtering removed all parts of the declaration.
 279        continue;
 280      }
 281      // Add the selectors, separating them with commas.
 282      $css .= $selector_str;
 283      // And, the opening curly brace.
 284      $css .= "{";
 285      // Iterate through all the statement properties.
 286      foreach ($declaration as $property => $value) {
 287        $css .= $property . ':' . $value . ';';
 288      }
 289      // Add the closing curly brace.
 290      $css .= "}";
 291    }
 292    // Return the output.
 293    return $css;
 294  }
 295  
 296  /**
 297   * Disassemble the css string.
 298   *
 299   * Strip the css of irrelevant characters, invalid/malformed selectors and
 300   * declarations, and otherwise prepare it for processing.
 301   *
 302   * @param string $css
 303   *   A string containing the css to be disassembled.
 304   *
 305   * @return array $disassembled_css
 306   *   An array of disassembled, slightly cleaned-up/formatted css statements.
 307   */
 308  function ctools_css_disassemble($css) {
 309    $disassembled_css = array();
 310    // Remove comments.
 311    $css = preg_replace("/\/\*(.*)?\*\//Usi", "", $css);
 312    // Split out each statement
 313    $statements = explode("}", $css);
 314    // If we have any statements, parse them.
 315    if (!empty($statements)) {
 316      // Iterate through all of the statements.
 317      foreach ($statements as $statement) {
 318        // Get the selector(s) and declaration.
 319        if (empty($statement) || !strpos($statement, '{')) {
 320          continue;
 321        }
 322  
 323        list($selector_str, $declaration) = explode('{', $statement);
 324  
 325        // If the selector exists, then disassemble it, check it, and regenerate
 326        // the selector string.
 327        $selector_str = empty($selector_str) ? FALSE : _ctools_css_disassemble_selector($selector_str);
 328        if (empty($selector_str)) {
 329          // No valid selectors. Bomb out and start the next item.
 330          continue;
 331        }
 332  
 333        // Disassemble the declaration, check it and tuck it into an array.
 334        $declarations = _ctools_css_disassemble_declaration($declaration);
 335        if (!isset($disassembled_css[$selector_str])) {
 336          $disassembled_css[$selector_str] = $declarations;
 337        }
 338        else {
 339          $disassembled_css[$selector_str] += $declarations;
 340        }
 341      }
 342    }
 343    return $disassembled_css;
 344  }
 345  
 346  function _ctools_css_disassemble_selector($selector_str) {
 347    // Get all selectors individually.
 348    $selectors = explode(",", trim($selector_str));
 349    // Iterate through all the selectors, sanity check them and return if they
 350    // pass. Note that this handles 0, 1, or more valid selectors gracefully.
 351    foreach ($selectors as $key => $selector) {
 352      // Replace un-needed characters and do a little cleanup.
 353      $selector = preg_replace("/[\n|\t|\\|\s]+/", ' ', trim($selector));
 354      // Make sure this is still a real selector after cleanup.
 355      if (!empty($selector)) {
 356        $selectors[$key] = $selector;
 357      }
 358      else {
 359        // Selector is no good, so we scrap it.
 360        unset($selectors[$key]);
 361      }
 362    }
 363    // Check for malformed selectors; if found, we skip this declaration.
 364    if (empty($selectors)) {
 365      return FALSE;
 366    }
 367    return implode(', ', $selectors);
 368  }
 369  
 370  function _ctools_css_disassemble_declaration($declaration) {
 371    $formatted_statement = array();
 372    $propval_pairs = explode(";", $declaration);
 373    // Make sure we actually have some properties to work with.
 374    if (!empty($propval_pairs)) {
 375      // Iterate through the remains and parse them.
 376      foreach ($propval_pairs as $key => $propval_pair) {
 377        // Check that we have a ':', otherwise it's an invalid pair.
 378        if (strpos($propval_pair, ':') === FALSE) {
 379          continue;
 380        }
 381        // Clean up the current property-value pair.
 382        $propval_pair = preg_replace("/[\n|\t|\\|\s]+/", ' ', trim($propval_pair));
 383        // Explode the remaining fragements some more, but clean them up first.
 384        list($property, $value) = explode(':', $propval_pair, 2);
 385        // If the property survived, toss it onto the stack.
 386        if (!empty($property)) {
 387          $formatted_statement[trim($property)] = trim($value);
 388        }
 389      }
 390    }
 391    return $formatted_statement;
 392  }
 393  
 394  /**
 395   * Run disassembled $css through the filter.
 396   *
 397   * @param $css
 398   *   CSS code disassembled by ctools_dss_disassemble().
 399   * @param $allowed_properties
 400   *   A list of properties that are allowed by the filter. If empty
 401   *   ctools_css_filter_default_allowed_properties() will provide the
 402   *   list.
 403   * @param $allowed_values
 404   *   A list of values that are allowed by the filter. If empty
 405   *   ctools_css_filter_default_allowed_values() will provide the
 406   *   list.
 407   *
 408   * @return
 409   *   An array of disassembled, filtered CSS.
 410   */
 411  function ctools_css_filter_css_data($css, $allowed_properties = array(), $allowed_values = array(), $allowed_values_regex = '', $disallowed_values_regex = '') {
 412  //function ctools_css_filter_css_data($css, &$filtered = NULL, $allowed_properties = array(), $allowed_values = array(), $allowed_values_regex = '', $disallowed_values_regex = '') {
 413    // Retrieve the default list of allowed properties if none is provided.
 414    $allowed_properties = !empty($allowed_properties) ? $allowed_properties : ctools_css_filter_default_allowed_properties();
 415    // Retrieve the default list of allowed values if none is provided.
 416    $allowed_values = !empty($allowed_values) ? $allowed_values : ctools_css_filter_default_allowed_values();
 417    // Define allowed values regex if none is provided.
 418    $allowed_values_regex = !empty($allowed_values_regex) ? $allowed_values_regex : '/(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)/';
 419    // Define disallowed url() value contents, if none is provided.
 420    // $disallowed_values_regex = !empty($disallowed_values_regex) ? $disallowed_values_regex : '/[url|expression]\s*\(\s*[^\s)]+?\s*\)\s*/';
 421    $disallowed_values_regex = !empty($disallowed_values_regex) ? $disallowed_values_regex : '/(url|expression)/';
 422  
 423    foreach ($css as $selector_str => $declaration) {
 424      foreach ($declaration as $property => $value) {
 425        if (!in_array($property, $allowed_properties)) {
 426          // $filtered['properties'][$selector_str][$property] = $value;
 427          unset($css[$selector_str][$property]);
 428          continue;
 429        }
 430        $value = str_replace('!important', '', $value);
 431        if (preg_match($disallowed_values_regex, $value) || !(in_array($value, $allowed_values) || preg_match($allowed_values_regex, $value))) {
 432          // $filtered['values'][$selector_str][$property] = $value;
 433          unset($css[$selector_str][$property]);
 434          continue;
 435        }
 436      }
 437    }
 438    return $css;
 439  }
 440  
 441  /**
 442   * Provide a deafult list of allowed properties by the filter.
 443   */
 444  function ctools_css_filter_default_allowed_properties() {
 445    return array(
 446      'azimuth',
 447      'background',
 448      'background-color',
 449      'background-image',
 450      'background-repeat',
 451      'background-attachment',
 452      'background-position',
 453      'border',
 454      'border-top-width',
 455      'border-right-width',
 456      'border-bottom-width',
 457      'border-left-width',
 458      'border-width',
 459      'border-top-color',
 460      'border-right-color',
 461      'border-bottom-color',
 462      'border-left-color',
 463      'border-color',
 464      'border-top-style',
 465      'border-right-style',
 466      'border-bottom-style',
 467      'border-left-style',
 468      'border-style',
 469      'border-top',
 470      'border-right',
 471      'border-bottom',
 472      'border-left',
 473      'clear',
 474      'color',
 475      'cursor',
 476      'direction',
 477      'display',
 478      'elevation',
 479      'float',
 480      'font',
 481      'font-family',
 482      'font-size',
 483      'font-style',
 484      'font-variant',
 485      'font-weight',
 486      'height',
 487      'letter-spacing',
 488      'line-height',
 489      'margin',
 490      'margin-top',
 491      'margin-right',
 492      'margin-bottom',
 493      'margin-left',
 494      'overflow',
 495      'padding',
 496      'padding-top',
 497      'padding-right',
 498      'padding-bottom',
 499      'padding-left',
 500      'pause',
 501      'pause-after',
 502      'pause-before',
 503      'pitch',
 504      'pitch-range',
 505      'richness',
 506      'speak',
 507      'speak-header',
 508      'speak-numeral',
 509      'speak-punctuation',
 510      'speech-rate',
 511      'stress',
 512      'text-align',
 513      'text-decoration',
 514      'text-indent',
 515      'text-transform',
 516      'unicode-bidi',
 517      'vertical-align',
 518      'voice-family',
 519      'volume',
 520      'white-space',
 521      'width',
 522      'fill',
 523      'fill-opacity',
 524      'fill-rule',
 525      'stroke',
 526      'stroke-width',
 527      'stroke-linecap',
 528      'stroke-linejoin',
 529      'stroke-opacity',
 530    );
 531  }
 532  
 533  /**
 534   * Provide a default list of allowed values by the filter.
 535   */
 536  function ctools_css_filter_default_allowed_values() {
 537    return array(
 538      'auto',
 539      'aqua',
 540      'black',
 541      'block',
 542      'blue',
 543      'bold',
 544      'both',
 545      'bottom',
 546      'brown',
 547      'capitalize',
 548      'center',
 549      'collapse',
 550      'dashed',
 551      'dotted',
 552      'fuchsia',
 553      'gray',
 554      'green',
 555      'italic',
 556      'inherit',
 557      'left',
 558      'lime',
 559      'lowercase',
 560      'maroon',
 561      'medium',
 562      'navy',
 563      'normal',
 564      'nowrap',
 565      'olive',
 566      'pointer',
 567      'purple',
 568      'red',
 569      'right',
 570      'solid',
 571      'silver',
 572      'teal',
 573      'top',
 574      'transparent',
 575      'underline',
 576      'uppercase',
 577      'white',
 578      'yellow',
 579    );
 580  }
 581  
 582  /**
 583   * Delegated implementation of hook_flush_caches()
 584   */
 585  function ctools_css_flush_caches() {
 586    file_scan_directory(file_create_path('ctools/css'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE);
 587    db_query("DELETE FROM {ctools_css_cache}");
 588  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7