| 1 |
<?php |
<?php |
| 2 |
// $Id: css.module,v 1.1.2.3 2006/05/15 18:39:51 fax8 Exp $ |
// $Id: $ |
| 3 |
|
|
| 4 |
/** |
/** |
| 5 |
* @file |
* @file |
| 9 |
* @author Fabio Varesano <fvaresano at yahoo dot it> |
* @author Fabio Varesano <fvaresano at yahoo dot it> |
| 10 |
* @updated to Drupal 5 by Christopher Skauss <christopher skauss at gmail dot com> |
* @updated to Drupal 5 by Christopher Skauss <christopher skauss at gmail dot com> |
| 11 |
* @modified drupal 5 Version by Whispero |
* @modified drupal 5 Version by Whispero |
| 12 |
|
* @updated for Drupal 6 by Joshua Chan <josh at joshuachan dot ca> |
| 13 |
* |
* |
| 14 |
* To store this extra information, we need an auxiliary database table. |
* To store this extra information, we need an auxiliary database table. |
| 15 |
* |
* |
| 37 |
/** |
/** |
| 38 |
* Implementation of hook_menu() |
* Implementation of hook_menu() |
| 39 |
*/ |
*/ |
| 40 |
function css_menu($may_cache) { |
function css_menu() { |
| 41 |
$items = array(); |
$items = array(); |
| 42 |
if ($may_cache) { |
// defines the callback for getting the css file. we use |
| 43 |
// defines the callback for getting the css file. we use |
// css/get as path instead of only css to avoid that in some |
| 44 |
// css/get as path instead of only css to avoid that in some |
// installs users have yet a directory called css |
| 45 |
// installs users have yet a directory called css |
$items['css/get'] = array( |
| 46 |
$items[] = array('path' => 'css/get', 'title' => t('css'), |
'title' => 'css', |
| 47 |
'access' => user_access('access content'), |
'path' => 'css/get', |
| 48 |
'type' => MENU_CALLBACK, |
'page callback' => 'css_get', |
| 49 |
'callback' => 'css_get'); |
'access arguments' => array('access content'), |
| 50 |
} |
'type' => MENU_CALLBACK, |
| 51 |
|
); |
| 52 |
return $items; |
return $items; |
| 53 |
} |
} |
| 54 |
|
|
| 62 |
/** |
/** |
| 63 |
* Implemenation of hook_form_alter() |
* Implemenation of hook_form_alter() |
| 64 |
*/ |
*/ |
| 65 |
function css_form_alter($form_id, &$form) { |
function css_form_alter(&$form, $form_state, $form_id) { |
| 66 |
//Add a text area to the form where users will put their csses rules. |
//Add a text area to the form where users will put their csses rules. |
| 67 |
if (user_access('create css for nodes') && variable_get('css__'.$form['#node']->type, FALSE)) { |
if (user_access('create css for nodes') && variable_get('css__'.$form['#node']->type, FALSE)) { |
| 68 |
if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) { |
if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) { |
| 74 |
'#default_value' => $node->css_css, |
'#default_value' => $node->css_css, |
| 75 |
'#cols' => 60, |
'#cols' => 60, |
| 76 |
'#rows' => 10, |
'#rows' => 10, |
| 77 |
'#description' => t('Insert here the css rules for this node. You can use css defined for other nodes using @import "css/get/x" where x is the node identification number.'), |
'#description' => t('Insert here the css rules for this node. You can use css defined for other nodes using <em>@import "?q=css/get/x";</em> where x is the identification number of the node which contains the css you want to use.'), |
| 78 |
'#attributes' => NULL, |
'#attributes' => NULL, |
| 79 |
'#required' => FALSE, |
'#required' => FALSE, |
| 80 |
); |
); |
| 92 |
} |
} |
| 93 |
} |
} |
| 94 |
|
|
|
|
|
| 95 |
/** |
/** |
| 96 |
* Implementation of hook_nodeapi(). |
* Implementation of hook_nodeapi(). |
| 97 |
*/ |
*/ |
| 98 |
function css_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { |
function css_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { |
| 99 |
if (variable_get('css_'. $node->type, TRUE)) { // check that CSS editing is enabled for the given node type |
static $prev_op = NULL; |
| 100 |
|
|
| 101 |
|
if (variable_get('css__'. $node->type, FALSE)) { // check that CSS editing is enabled for the given node type |
| 102 |
switch ($op) { |
switch ($op) { |
| 103 |
|
|
| 104 |
// Controls for valid input data |
// Controls for valid input data |
| 105 |
case 'validate': |
case 'validate': |
| 106 |
;//do some input check here.. |
// Check for potentially malicious tags |
| 107 |
|
$pattern = '~<\s*\/?\s*(style|script|meta)\s*.*?>~i'; |
| 108 |
|
if (preg_match($pattern, $node->css_css)) { |
| 109 |
|
form_set_error('css_css', t('Please do not include any tags.')); |
| 110 |
|
} |
| 111 |
break; |
break; |
| 112 |
|
|
| 113 |
// Now that the form has been properly completed, it is time to commit the new |
// Now that the form has been properly completed, it is time to commit the new |
| 114 |
// data to the database. |
// data to the database. |
| 115 |
case 'insert': |
case 'insert': |
| 116 |
db_query('INSERT INTO {css} (nid, css) VALUES (%d, "%s")', $node->nid, $node->css_css); |
if (!empty($node->css_css)) { |
| 117 |
|
db_query("INSERT INTO {css} (nid, css) VALUES (%d, '%s')", $node->nid, $node->css_css); |
| 118 |
|
} |
| 119 |
break; |
break; |
| 120 |
|
|
| 121 |
// If the form was called to edit an existing node rather than create a new |
// If the form was called to edit an existing node rather than create a new |
| 122 |
// one, this operation gets called instead. We use a DELETE then INSERT rather |
// one, this operation gets called instead. We use a DELETE then INSERT rather |
| 123 |
// than an UPDATE just in case the rating didn't exist for some reason. |
// than an UPDATE just in case the rating didn't exist for some reason. |
| 124 |
case 'update': |
case 'update': |
| 125 |
db_query('DELETE FROM {css} WHERE nid = %d', $node->nid); |
db_query("DELETE FROM {css} WHERE nid = %d", $node->nid); |
| 126 |
db_query('INSERT INTO {css} (nid, css) VALUES (%d, "%s")', $node->nid, $node->css_css); |
if (!empty($node->css_css)) { |
| 127 |
|
db_query("INSERT INTO {css} (nid, css) VALUES (%d, '%s')", $node->nid, $node->css_css); |
| 128 |
|
} |
| 129 |
break; |
break; |
| 130 |
|
|
| 131 |
// If the node is being deleted, we need this opportunity to clean up after |
// If the node is being deleted, we need this opportunity to clean up after |
| 145 |
// filters transform user-supplied content, whereas we are extending it with |
// filters transform user-supplied content, whereas we are extending it with |
| 146 |
// additional information. |
// additional information. |
| 147 |
case 'view': |
case 'view': |
| 148 |
theme('css_import', $node->nid); |
if ($prev_op == 'validate') { |
| 149 |
|
// 'validate' immediately followed by 'view' means this is a preview |
| 150 |
|
if ($node->css_css) { |
| 151 |
|
$css = '<style type="text/css" media="all"> '. |
| 152 |
|
css_sanitize($node->css_css, 'preview'). |
| 153 |
|
' </style>'; |
| 154 |
|
drupal_set_html_head($css, 'preview'); |
| 155 |
|
} |
| 156 |
|
} else { |
| 157 |
|
// Drupal 6 seems to check for the physical existence of CSS files |
| 158 |
|
// before allowing them to be added. We have to include the virtual |
| 159 |
|
// CSS file manually since it does not really exist. |
| 160 |
|
if (!empty($node->css_css)) { |
| 161 |
|
$link = url('css/get/'.$node->nid); |
| 162 |
|
drupal_add_link(array( |
| 163 |
|
'type' => 'text/css', |
| 164 |
|
'rel' => 'stylesheet', |
| 165 |
|
'media' => 'all', |
| 166 |
|
'href' => $link, |
| 167 |
|
)); |
| 168 |
|
} |
| 169 |
|
} |
| 170 |
break; |
break; |
| 171 |
} |
} |
| 172 |
|
|
| 173 |
|
$prev_op = $op; // used to determine Preview state |
| 174 |
} |
} |
| 175 |
} |
} |
| 176 |
|
|
| 177 |
/** |
/** |
| 178 |
* Return the css attached to the node |
* Return the css attached to the node. |
| 179 |
* Last-Modified header is set to let browsers cache the css. |
* Last-Modified header is set to let browsers cache the css. |
| 180 |
*/ |
*/ |
| 181 |
function css_get($nid = 0) { |
function css_get($nid = 0) { |
| 182 |
if (is_numeric($nid) && $nid > 0) { |
if (is_numeric($nid) && $nid > 0) { |
| 183 |
$object = db_fetch_object(db_query('SELECT css, changed FROM {css} c, {node} n WHERE n.nid = %d AND n.nid = c.nid', $nid)); |
$object = db_fetch_object(db_query('SELECT css, changed FROM {css} c, {node} n WHERE n.nid = %d AND n.nid = c.nid', $nid)); |
| 184 |
if($object) { |
if ($object) { |
| 185 |
$date = gmdate('D, d M Y H:i:s', $object->changed) .' GMT'; |
$date = gmdate('D, d M Y H:i:s', $object->changed) .' GMT'; |
| 186 |
header("Last-Modified: $date"); |
header("Last-Modified: $date"); |
| 187 |
drupal_set_header('Content-Type: text/css; charset=utf-8'); |
drupal_set_header('Content-Type: text/css; charset=utf-8'); |
| 188 |
print($object->css); |
print(css_sanitize($object->css)); |
| 189 |
} |
} |
| 190 |
} |
} |
| 191 |
} |
} |
| 192 |
|
|
| 193 |
/** |
/** |
| 194 |
* Adds @import for the css in the head tag of page |
* Remove harmful code from CSS. |
| 195 |
* We use a theme function for this to let themers able |
*/ |
| 196 |
* to customize the behaviour of importing. |
function css_sanitize($css, $type = 'view') { |
| 197 |
*/ |
switch ($type) { |
| 198 |
function theme_css_import($nid) { |
case 'view': |
| 199 |
// let's link the CSS file to the HTML: |
// Are there any security vulnerabilites from external CSS files? |
| 200 |
// we use $path = '?q=css/get/' . $nid as without ?q= the hook would be called only when clean urls are enabled (mod_rewrite active) |
break; |
| 201 |
// we use $type = theme as we want to be parsed after module rules, so that we can override module defined rules |
|
| 202 |
// we use $preprocess = FALSE as we don't want to cache the CSS rules. |
case 'preview': |
| 203 |
drupal_add_css('?q=css/get/' . $nid, 'theme', 'all', FALSE); |
// Catch potentially malicious code |
| 204 |
|
$patterns = array( |
| 205 |
|
'~<\s*(/?)\s*(style|script|meta)\s*>~i', |
| 206 |
|
); |
| 207 |
|
$css = preg_replace($patterns, '<$1FILTERED $2>', $css); |
| 208 |
|
break; |
| 209 |
|
|
| 210 |
|
default: |
| 211 |
|
$css = ''; |
| 212 |
|
break; |
| 213 |
|
} |
| 214 |
|
|
| 215 |
|
return $css; |
| 216 |
} |
} |