Commit 660f9928 authored by Steven Wittens's avatar Steven Wittens

The Input formats - filter patch has landed. I still need to make update...

The Input formats - filter patch has landed. I still need to make update instructions for modules and update the hook docs.

Here's an overview of the changes:
1) Multiple Input formats: they are complete filter configurations (what filters to use, in what order and with which settings). Input formats are admin-definable, and usage of them is role-dependant. For example, you can set it up so that regular users can only use limited HTML, while admins can free HTML without any tag limitations.
The input format can be chosen per content item (nodes, comments, blocks, ...) when you add/edit them. If only a single format is available, there is no choice, and nothing changes with before.

The default install (and the upgrade) contains a basic set of formats which should satisfy the average user's needs.

2) Filters have toggles
Because now you might want to enable a filter only on some input formats, an explicit toggle is provided by the filter system. Modules do not need to worry about it and filters that still have their own on/off switch should get rid of it.

3) Multiple filters per module
This was necessary to accomodate the next change, and it's also a logical extension of the filter system.

4) Embedded PHP is now a filter
Thanks to the multiple input formats, I was able to move the 'embedded PHP' feature from block.module, page.module and book.module into a simple filter which executes PHP code. This filter is part of filter.module, and by default there is an input format 'PHP', restricted to the administrator only, which contains this filter.
This change means that block.module now passes custom block contents through the filter system.
As well as from reducing code duplication and avoiding two type selectors for page/book nodes, you can now combine PHP code with other filters.

5) User-supplied PHP code now requires <?php ?> tags.
This is required for teasers to work with PHP code. Because PHP evaluation is now just another step in the filter process, we can't do this. Also, because teasers are generated before filtering, this would result in errors when the teaser generation would cut off a piece of PHP code.

Also, regular PHP syntax explicitly includes the <?php ?> tags for PHP files, so it makes sense to use the same convention for embedded PHP in Drupal.

6) Filter caching was added.
Benchmarking shows that even for a simple setup (basic html filtering + legacy URL rewriting), filtercache can offer speedups. Unlike the old filtercache, this uses the normal cache table.

7) Filtertips were moved from help into a hook_filter_tips(). This was required to accomodate the fact that there are multiple filters per module, and that filter settings are format dependant. Shoehorning filter tips into _help was ugly and silly. The display of the filter tips is done through the input format selector, so filter_tips_short() no longer exists.

8) A more intelligent linebreak convertor was added, which doesn't stop working if you use block-level tags and which adds <p> tags.
parent 09fc61c0
......@@ -135,7 +135,6 @@ CREATE TABLE book (
nid int(10) unsigned NOT NULL default '0',
parent int(10) NOT NULL default '0',
weight tinyint(3) NOT NULL default '0',
format tinyint(2) default '0',
log longtext,
PRIMARY KEY (nid),
KEY parent (parent)
......@@ -150,7 +149,7 @@ CREATE TABLE boxes (
title varchar(64) NOT NULL default '',
body longtext,
info varchar(128) NOT NULL default '',
type tinyint(2) NOT NULL default '0',
format int(4) NOT NULL default '0',
PRIMARY KEY (bid),
UNIQUE KEY title (title),
UNIQUE KEY info (info)
......@@ -184,6 +183,7 @@ CREATE TABLE comments (
timestamp int(11) NOT NULL default '0',
score mediumint(9) NOT NULL default '0',
status tinyint(3) unsigned NOT NULL default '0',
format int(4) NOT NULL default '0',
thread varchar(255) NOT NULL,
users longtext,
name varchar(60) default NULL,
......@@ -207,14 +207,28 @@ CREATE TABLE directory (
PRIMARY KEY (link)
) TYPE=MyISAM;
--
-- Table structure for table 'filter_formats'
--
CREATE TABLE filter_formats (
format int(4) NOT NULL default '0' auto_increment,
name varchar(255) NOT NULL default '',
roles varchar(255) NOT NULL default '',
cache tinyint(2) NOT NULL default '0',
PRIMARY KEY format (format)
) TYPE=MyISAM;
--
-- Table structure for table 'filters'
--
CREATE TABLE filters (
format int(4) NOT NULL default '0',
module varchar(64) NOT NULL default '',
delta tinyint(2) DEFAULT '0' NOT NULL,
weight tinyint(2) DEFAULT '0' NOT NULL,
KEY module (module)
INDEX (weight)
) TYPE=MyISAM;
--
......@@ -330,6 +344,7 @@ CREATE TABLE node (
body longtext NOT NULL,
revisions longtext NOT NULL,
sticky int(2) NOT NULL default '0',
format int(4) NOT NULL default '0',
PRIMARY KEY (nid),
KEY node_type (type(4)),
KEY node_title_type (title,type(4)),
......@@ -362,7 +377,6 @@ CREATE TABLE node_access (
CREATE TABLE page (
nid int(10) unsigned NOT NULL default '0',
link varchar(128) NOT NULL default '',
format tinyint(2) NOT NULL default '0',
description varchar(128) NOT NULL default '',
PRIMARY KEY (nid)
) TYPE=MyISAM;
......@@ -700,3 +714,12 @@ REPLACE blocks SET module = 'user', delta = '1', status = '1';
INSERT INTO sequences (name, id) VALUES ('menu_mid', 1);
INSERT INTO node_access VALUES (0, 0, 'all', 1, 0, 0);
INSERT INTO filter_formats VALUES (1,'Filtered HTML',',1,2,',1);
INSERT INTO filter_formats VALUES (2,'PHP code','',0);
INSERT INTO filter_formats VALUES (3,'Full HTML','',1);
INSERT INTO filters VALUES (1,'filter',0,0);
INSERT INTO filters VALUES (1,'filter',3,1);
INSERT INTO filters VALUES (2,'filter',1,0);
INSERT INTO filters VALUES (3,'filter',3,0);
INSERT INTO variable (name,value) VALUES ('filter_html_1','i:1;');
......@@ -694,6 +694,16 @@ INSERT INTO permission VALUES (2,'access comments, access content, post comments
INSERT INTO blocks(module,delta,status) VALUES('user', '0', '1');
INSERT INTO blocks(module,delta,status) VALUES('user', '1', '1');
INSERT INTO filter_formats VALUES (1,'Filtered HTML',',1,2,',1);
INSERT INTO filter_formats VALUES (2,'PHP code','',0);
INSERT INTO filter_formats VALUES (3,'Full HTML','',1));
INSERT INTO filters VALUES (1,'filter',0,0);
INSERT INTO filters VALUES (1,'filter',3,1);
INSERT INTO filters VALUES (2,'filter',1,0);
INSERT INTO filters VALUES (3,'filter',3,0);
INSERT INTO variable (name,value) VALUES ('filter_html_1','i:1;');
---
--- Functions
---
......
......@@ -69,7 +69,8 @@
"2004-07-30" => "update_95",
"2004-08-04" => "update_96",
"2004-08-06" => "update_97",
"2004-08-07" => "update_98"
"2004-08-07" => "update_98",
"2004-08-09" => "update_99"
);
function update_32() {
......@@ -1308,6 +1309,159 @@ function update_98() {
return array();
}
function update_99() {
// Filter patch - Multiple input formats
$ret = array();
/*
** Load the list of PHP book and page nodes.
*/
$php_nodes = array();
$res = db_query("SELECT nid FROM {book} WHERE format = 1");
while ($book = db_fetch_object($res)) {
$php_nodes[] = $book->nid;
}
$res = db_query("SELECT nid FROM {page} WHERE format = 1");
while ($page = db_fetch_object($res)) {
$php_nodes[] = $page->nid;
}
/*
** Apply database changes
*/
if ($GLOBALS['db_type'] == 'mysql') {
// Filters table
$ret[] = update_sql("ALTER TABLE {filters} ADD format int(4) NOT NULL default '0'");
$ret[] = update_sql("ALTER TABLE {filters} ADD delta tinyint(2) NOT NULL default '0'");
// Filter_formats table
$ret[] = update_sql("CREATE TABLE {filter_formats} (
format int(4) NOT NULL auto_increment,
name varchar(255) NOT NULL default '',
roles varchar(255) NOT NULL default '',
cache tinyint(2) NOT NULL default '1',
PRIMARY KEY (format)
)");
// Store formats in nodes, comments and boxes
$ret[] = update_sql("ALTER TABLE {boxes} CHANGE type format int(4) NOT NULL default '0'");
$ret[] = update_sql("ALTER TABLE {comments} ADD format int(4) NOT NULL default '0'");
$ret[] = update_sql("ALTER TABLE {node} ADD format int(4) NOT NULL default '0'");
// Get rid of the old book/page type info
$ret[] = update_sql("ALTER TABLE {book} DROP format");
$ret[] = update_sql("ALTER TABLE {page} DROP format");
}
else if ($GLOBALS['db_type'] == 'pgsql') {
// TODO: add pgsql equivalent. Whoever does this should pay attention that
// the keys/indices for the 'filters' table are correct. There was some
// inconsistency between the MySQL and PGSQL version before.
}
// Initialize all nodes and comments to the legacy format (see below)
$ret[] = update_sql("UPDATE {node} SET format = 1");
$ret[] = update_sql("UPDATE {comments} SET format = 1");
// Set format to PHP for the old PHP book/page nodes.
if (count($php_nodes)) {
$ret[] = update_sql("UPDATE {node} SET format = 2 WHERE nid IN (". implode(',', $php_nodes) .")");
}
// Boxes now use the filtering system as well.
// Type 0 (HTML) maps to Format 3 (Full HTML).
// Type 1 (PHP) maps to Format 2 (PHP).
$ret[] = update_sql("UPDATE {boxes} SET format = 3 - format");
/*
** Update PHP content to use <?php ?> tags.
*/
if ($GLOBALS['db_type'] == 'mysql') {
$ret[] = update_sql("UPDATE {node} SET teaser = CONCAT('<?php ', teaser) WHERE format = 2");
$ret[] = update_sql("UPDATE {node} SET body = CONCAT('<?php ', body) WHERE format = 2");
$ret[] = update_sql("UPDATE {boxes} SET body = CONCAT('<?php ', body) WHERE format = 2");
}
else if ($GLOBALS['db_type'] == 'pgsql') {
// TODO: someone needs to verify if this works.
$ret[] = update_sql("UPDATE {node} SET teaser = '<?php ' || teaser WHERE format = 2");
$ret[] = update_sql("UPDATE {node} SET body = '<?php ' || body WHERE format = 2");
$ret[] = update_sql("UPDATE {boxes} SET body = '<?php ' || body WHERE format = 2");
}
/*
** We now set up some input formats. One of these is a 'legacy' format which
** tries to preserve as much settings as possible from before the patch.
** The other two are 'PHP code' and 'Full HTML'.
*/
// We pick an appropriate name for the legacy format.
$old_html_filter = variable_get('filter_html', 0);
if ($old_html_filter == FILTER_HTML_ESCAPE) {
$default = 'Plain text';
}
else {
$default = 'Filtered HTML';
}
// Make sure the legacy format is accessible to all roles
$all_roles = array_keys(user_roles());
$ret[] = update_sql("INSERT INTO {filter_formats} VALUES (1,'$default',',". implode(',', $all_roles) .",',1)");
// Determine which roles have the old 'create php content' permission.
$res = db_query("SELECT rid FROM {permission} WHERE perm LIKE '%create php content%'");
$php_roles = array();
while ($role = db_fetch_object($res)) {
$php_roles[] = $role->rid;
}
$ret[] = update_sql("INSERT INTO {filter_formats} VALUES (2,'PHP code','". implode(',', $php_roles) .",',0)");
// This is a 'Full HTML' format which allows all HTML without restrictions.
$ret[] = update_sql("INSERT INTO {filter_formats} VALUES (3,'Full HTML','',1)");
// Set the default format to the legacy format
variable_set('filter_default_format', 1);
// Put the old filters into the legacy format
$ret[] = update_sql("UPDATE {filters} SET format = 1");
// Add filter.module's standard filters (these used to be hardcoded).
if (!variable_get('rewrite_old_urls', 0)) {
$ret[] = update_sql("DELETE FROM {filters} WHERE module = 'filter'");
}
else {
$ret[] = update_sql("UPDATE {filters} SET delta = 2 WHERE module ='filter'");
}
if ($old_html_filter != 0) {
$ret[] = update_sql("INSERT INTO {filters} (format, module, delta, weight) VALUES (1,'filter',0,0)"); // HTML tag/style filter
}
$ret[] = update_sql("INSERT INTO {filters} (format, module, delta, weight) VALUES (1,'filter',3,1)"); // Linebreak filter
$ret[] = update_sql("INSERT INTO {filters} (format, module, delta, weight) VALUES (2,'filter',1,0)"); // PHP evaluator
$ret[] = update_sql("INSERT INTO {filters} (format, module, delta, weight) VALUES (3,'filter',3,0)"); // Linebreak filter
// Migrate the settings for all core/contrib filtering modules into the legacy
// format.
$migrate = array('filter_html', // filter.module
'allowed_html',
'filter_style',
'anyfilter_regexps', // anyfilter.module
'htmlcorrector_smartclose', // htmlcorrector.module
'htmlcorrector_xhtmlify',
'htmlcorrector_valueentities',
'project_filter', // project.module
'latex_filter_link' // latex.module
);
foreach ($migrate as $variable) {
$value = variable_get($variable, NULL);
if ($value != NULL) {
variable_set($variable .'_1', $value);
variable_del($variable);
}
}
return $ret;
}
function update_sql($sql) {
$edit = $_POST["edit"];
$result = db_query($sql);
......
......@@ -133,13 +133,22 @@ function cache_set($cid, $data, $expire = 0, $headers = NULL) {
* @param $cid
* If set, the cache ID to delete. Otherwise, all cache entries that can expire
* are deleted.
*
* @param $wildcard
* If set to true, the $cid is treated as a substring to match rather than a
* complete ID.
*/
function cache_clear_all($cid = NULL) {
function cache_clear_all($cid = NULL, $wildcard = false) {
if (empty($cid)) {
db_query("DELETE FROM {cache} WHERE expire <> 0");
}
else {
db_query("DELETE FROM {cache} WHERE cid = '%s'", $cid);
if ($wildcard) {
db_query("DELETE FROM {cache} WHERE cid LIKE '%%%s%%'", $cid);
}
else {
db_query("DELETE FROM {cache} WHERE cid = '%s'", $cid);
}
}
}
......
......@@ -813,7 +813,7 @@ function format_rss_item($title, $link, $description, $args = array()) {
$output = "<item>\n";
$output .= ' <title>'. drupal_specialchars(strip_tags($title)) ."</title>\n";
$output .= ' <link>'. drupal_specialchars(strip_tags($link)) ."</link>\n";
$output .= ' <description>'. drupal_specialchars(check_output($description)) ."</description>\n";
$output .= ' <description>'. drupal_specialchars($description) ."</description>\n";
foreach ($args as $key => $value) {
$output .= ' <'. $key .'>'. drupal_specialchars(strip_tags($value)) ."</$key>\n";
}
......@@ -1770,6 +1770,20 @@ function truncate_utf8($string, $len) {
return substr($string, 0, $len);
}
/**
* Wrapper around PHP's eval(). Uses output buffering to capture both returned
* and printed text. Unlike eval(), we require code to be surrounded by <?php ?>
* tags (in other words, we evaluate the code as if it were a stand-alone PHP
* file).
*
* @param $code The code to evaluate
*/
function drupal_eval($code) {
ob_start();
print eval('?>'. $code);
return ob_get_clean();
}
include_once 'includes/theme.inc';
include_once 'includes/pager.inc';
include_once 'includes/menu.inc';
......
......@@ -319,6 +319,13 @@ tr.light .form-item, tr.dark .form-item {
margin: 0;
font-size: 0.8em;
}
.tips {
margin-top: 0px;
margin-bottom: 0px;
padding-top: 0px;
padding-bottom: 0px;
font-size: 0.9em;
}
#forum .description {
font-size: 0.9em;
margin: 0.5em;
......
......@@ -470,7 +470,12 @@ function aggregator_parse_feed(&$data, $feed) {
// Prepare the item:
foreach ($item as $key => $value) {
$item[$key] = filter_default(strtr(trim($value), $tt));
// TODO: Make handling of aggregated HTML more flexible/configurable.
$value = strtr(trim($value), $tt);
$value = strip_tags($value, '<a> <b> <br> <dd> <dl> <dt> <em> <i> <li> <ol> <p> <strong> <u> <ul>');
$value = preg_replace('/\Wstyle\s*=[^>]+?>/i', '>', $value);
$value = preg_replace('/\Won[a-z]+\s*=[^>]+?>/i', '>', $value);
$item[$key] = $value;
}
/*
......
......@@ -470,7 +470,12 @@ function aggregator_parse_feed(&$data, $feed) {
// Prepare the item:
foreach ($item as $key => $value) {
$item[$key] = filter_default(strtr(trim($value), $tt));
// TODO: Make handling of aggregated HTML more flexible/configurable.
$value = strtr(trim($value), $tt);
$value = strip_tags($value, '<a> <b> <br> <dd> <dl> <dt> <em> <i> <li> <ol> <p> <strong> <u> <ul>');
$value = preg_replace('/\Wstyle\s*=[^>]+?>/i', '>', $value);
$value = preg_replace('/\Won[a-z]+\s*=[^>]+?>/i', '>', $value);
$item[$key] = $value;
}
/*
......
......@@ -8,7 +8,7 @@ function block_help($section) {
switch ($section) {
case 'admin/help#block':
return t("
<p>Blocks are the boxes visible in the sidebar(s) of your web site. These are usually generated automatically by modules (e.g. recent forum topics), but you can also create your own blocks using either static HTML or dynamic PHP content.</p>
<p>Blocks are the boxes visible in the sidebar(s) of your web site. These are usually generated automatically by modules (e.g. recent forum topics), but you can also create your own blocks.</p>
<p>The sidebar each block appears in depends on both which theme you're using (some are left-only, some right, some both), and on the settings in block management.</p><p>Whether a block is visible in the first place depends on four things:</p><ul><li>It must have its \"enabled\" box checked in block management.</li><li>If it has its \"custom\" box checked in block management, the user must have chosen to display it in their user preferences.</li><li>If the \"path\" field in block management is set, the visitor must be on a page that matches the path specification (more on this later).</li><li>If the block has its throttle box checked, the user will only see the block if the site throttle level is low.</li></ul>
<p>The block management screen also lets you specify the vertical sort-order of the blocks within a sidebar. You do this by assigning a <strong>weight</strong> to each block. Lighter blocks (smaller weight) \"float up\" towards the top of the sidebar. Heavier ones \"sink down\" towards the bottom of it.</p>
<p>The path setting lets you define the pages on which a specific block is visible. If you leave the path blank it will appear on all pages. The path uses a regular expression syntax so remember to escape special characters! The path expression is matched against the relative URL of a Drupal page, e.g. <code>book</code>, <code>node/12</code>, <code>admin</code>.</p>
......@@ -16,26 +16,7 @@ function block_help($section) {
<p>However, for basic tasks it is sufficient to look at the following examples:</p>
<p>If the block should only show up on blog pages, use &lt;^blog&gt;. To display on all node views use &lt;^node&gt;. The angular brackets are used as delimiters of the regular expression. To show up on either forum or book pages use &lt;^(forum|book)&gt;. The round brackets form a group of expressions, divided by the | character. It matches if any of the expressions in it match. A more complicated example is &lt;^node/add/(story|blog|image)&gt;. Blocks which have their paths set to this expression will show up on story, block, or image composition pages. If you want to show a block an all pages, but not the search page, use &lt;^(?!search)&gt;.</p>
<h3>Administrator Defined Blocks</h3>
<p>An administrator defined block contains HTML, text or PHP content supplied by you (as opposed to being generated automatically by a module). Each admin-defined block consists of a title, a description, and a body containing text, HTML, or PHP code which can be as long as you wish. The Drupal engine will 'render' the content of the block.</p>
<h4>PHP in admin-defined blocks</h4>
<p>If you know how to script in PHP, Drupal gives you the power to embed any script you like inside a block. It will be executed when the page is viewed and dynamically embedded into the page. This gives you amazing flexibility and power, but of course with that comes danger and insecurity if you don't write good code. If you are not familiar with PHP, SQL or with the site engine, avoid experimenting with PHP blocks because you can corrupt your database or render your site insecure or even unusable! If you don't plan to do fancy stuff with your blocks then you're probably better off with straight HTML.</p>
<p>Remember that the code within each PHP block must be valid PHP code - including things like correctly terminating statements with a semicolon so that the parser won't die. It is highly recommended that you develop your blocks separately using a simple test script on top of a test database before migrating to your production environment.</p>
<p>Notes:</p><ul><li>You can use global variables, such as configuration parameters, within the scope of a PHP box but remember that variables which have been given values in a PHP box will retain these values in the engine or module afterwards.</li><li>register_globals is now set to <strong>off</strong> by default. If you need form information you need to get it from the \"superglobals\" \$_POST, \$_GET, etc.</li><li>You should use the <code>return</code> statement to return the actual content for your block.</li></ul>
<p>A basic example:</p>
<blockquote><p>You want to have a box with the title \"Welcome\" that you use to greet your visitors. The content for this box could be created by going:</p>
<pre>
return t(\"Welcome visitor, ... welcome message goes here ...\");
</pre>
<p>If we are however dealing with a registered user, we can customize the message by using:</p>
<pre>
if (\$user->uid) {
return t(\"Welcome \$user->name, ... welcome message goes here ...\");
}
else {
return t(\"Welcome visitor, ... welcome message goes here ...\");
}
</pre></blockquote>
<p>For more in-depth examples, we recommend that you check the existing boxes and use them as a starting point.</p>", array('%pcre' => 'http://php.net/pcre/'));
<p>An administrator defined block contains content supplied by you (as opposed to being generated automatically by a module). Each admin-defined block consists of a title, a description, and a body which can be as long as you wish. The Drupal engine will 'render' the content of the block.</p>", array('%pcre' => 'http://php.net/pcre/'));
case 'admin/modules#description':
return t('Controls the boxes that are displayed around the main content.');
case 'admin/block':
......@@ -101,7 +82,7 @@ function block_block($op = 'list', $delta = 0) {
else {
$block = db_fetch_object(db_query('SELECT * FROM {boxes} WHERE bid = %d', $delta));
$data['subject'] = $block->title;
$data['content'] = ($block->type == 1) ? eval($block->body) : $block->body;
$data['content'] = check_output($block->body, $block->format);
return $data;
}
}
......@@ -179,11 +160,20 @@ function block_admin_display() {
$blocks = _block_rehash();
// Fetch input formats used by admin-defined boxes.
$formats = array();
$result = db_query('SELECT bid, format FROM {boxes}');
while ($box = db_fetch_object($result)) {
$formats[$box->bid] = $box->format;
}
$header = array(t('block'), t('enabled'), t('custom'), t('throttle'), t('weight'), t('region'), t('path'), array('data' => t('operations'), 'colspan' => 2));
foreach ($blocks as $block) {
if ($block['module'] == 'block') {
$edit = l(t('edit'), 'admin/block/edit/'. $block['delta']);
if (filter_access($formats[$block['delta']])) {
$edit = l(t('edit'), 'admin/block/edit/'. $block['delta']);
}
$delete = l(t('delete'), 'admin/block/delete/'. $block['delta']);
}
else {
......@@ -232,14 +222,10 @@ function block_box_edit($bid = 0) {
}
function block_box_form($edit = array()) {
$type = array(0 => 'HTML', 1 => 'PHP');
$group = form_textfield(t('Block title'), 'title', $edit['title'], 50, 64, t('The title of the block as shown to the user.'));
$group .= filter_form('format', $edit['format']);
$group .= form_textarea(t('Block body'), 'body', $edit['body'], 70, 10, t('The content of the block as shown to the user.'));
$group .= form_textfield(t('Block description'), 'info', $edit['info'], 50, 64, t('A brief description of your block. Used on the <a href="%overview">block overview page</a>.', array('%overview' => url('admin/block'))));
if (user_access('create php content')) {
$group .= form_radios(t('Block type'), 'type', $edit['type'], $type, t("If you would like to use PHP code inside your block, set the above option to 'PHP' instead of 'HTML'."));
}
if ($edit['bid']) {
$group .= form_hidden('bid', $edit['bid']);
......@@ -252,16 +238,16 @@ function block_box_form($edit = array()) {
}
function block_box_save($edit) {
if (!user_access('create php content')) {
$edit['type'] = 0;
if (!filter_access($edit['format'])) {
$edit['format'] = FILTER_FORMAT_DEFAULT;
}
if ($edit['bid']) {
db_query("UPDATE {boxes} SET title = '%s', body = '%s', info = '%s', type = %d WHERE bid = %d", $edit['title'], $edit['body'], $edit['info'], $edit['type'], $edit['bid']);
db_query("UPDATE {boxes} SET title = '%s', body = '%s', info = '%s', format = %d WHERE bid = %d", $edit['title'], $edit['body'], $edit['info'], $edit['format'], $edit['bid']);
return t('the block has been updated.');
}
else {
db_query("INSERT INTO {boxes} (title, body, info, type) VALUES ('%s', '%s', '%s', %d)", $edit['title'], $edit['body'], $edit['info'], $edit['type']);
db_query("INSERT INTO {boxes} (title, body, info, format) VALUES ('%s', '%s', '%s', %d)", $edit['title'], $edit['body'], $edit['info'], $edit['format']);
return t('the new block has been added.');
}
}
......
......@@ -8,7 +8,7 @@ function block_help($section) {
switch ($section) {
case 'admin/help#block':
return t("
<p>Blocks are the boxes visible in the sidebar(s) of your web site. These are usually generated automatically by modules (e.g. recent forum topics), but you can also create your own blocks using either static HTML or dynamic PHP content.</p>
<p>Blocks are the boxes visible in the sidebar(s) of your web site. These are usually generated automatically by modules (e.g. recent forum topics), but you can also create your own blocks.</p>
<p>The sidebar each block appears in depends on both which theme you're using (some are left-only, some right, some both), and on the settings in block management.</p><p>Whether a block is visible in the first place depends on four things:</p><ul><li>It must have its \"enabled\" box checked in block management.</li><li>If it has its \"custom\" box checked in block management, the user must have chosen to display it in their user preferences.</li><li>If the \"path\" field in block management is set, the visitor must be on a page that matches the path specification (more on this later).</li><li>If the block has its throttle box checked, the user will only see the block if the site throttle level is low.</li></ul>
<p>The block management screen also lets you specify the vertical sort-order of the blocks within a sidebar. You do this by assigning a <strong>weight</strong> to each block. Lighter blocks (smaller weight) \"float up\" towards the top of the sidebar. Heavier ones \"sink down\" towards the bottom of it.</p>
<p>The path setting lets you define the pages on which a specific block is visible. If you leave the path blank it will appear on all pages. The path uses a regular expression syntax so remember to escape special characters! The path expression is matched against the relative URL of a Drupal page, e.g. <code>book</code>, <code>node/12</code>, <code>admin</code>.</p>
......@@ -16,26 +16,7 @@ function block_help($section) {
<p>However, for basic tasks it is sufficient to look at the following examples:</p>
<p>If the block should only show up on blog pages, use &lt;^blog&gt;. To display on all node views use &lt;^node&gt;. The angular brackets are used as delimiters of the regular expression. To show up on either forum or book pages use &lt;^(forum|book)&gt;. The round brackets form a group of expressions, divided by the | character. It matches if any of the expressions in it match. A more complicated example is &lt;^node/add/(story|blog|image)&gt;. Blocks which have their paths set to this expression will show up on story, block, or image composition pages. If you want to show a block an all pages, but not the search page, use &lt;^(?!search)&gt;.</p>
<h3>Administrator Defined Blocks</h3>
<p>An administrator defined block contains HTML, text or PHP content supplied by you (as opposed to being generated automatically by a module). Each admin-defined block consists of a title, a description, and a body containing text, HTML, or PHP code which can be as long as you wish. The Drupal engine will 'render' the content of the block.</p>
<h4>PHP in admin-defined blocks</h4>
<p>If you know how to script in PHP, Drupal gives you the power to embed any script you like inside a block. It will be executed when the page is viewed and dynamically embedded into the page. This gives you amazing flexibility and power, but of course with that comes danger and insecurity if you don't write good code. If you are not familiar with PHP, SQL or with the site engine, avoid experimenting with PHP blocks because you can corrupt your database or render your site insecure or even unusable! If you don't plan to do fancy stuff with your blocks then you're probably better off with straight HTML.</p>
<p>Remember that the code within each PHP block must be valid PHP code - including things like correctly terminating statements with a semicolon so that the parser won't die. It is highly recommended that you develop your blocks separately using a simple test script on top of a test database before migrating to your production environment.</p>
<p>Notes:</p><ul><li>You can use global variables, such as configuration parameters, within the scope of a PHP box but remember that variables which have been given values in a PHP box will retain these values in the engine or module afterwards.</li><li>register_globals is now set to <strong>off</strong> by default. If you need form information you need to get it from the \"superglobals\" \$_POST, \$_GET, etc.</li><li>You should use the <code>return</code> statement to return the actual content for your block.</li></ul>
<p>A basic example:</p>
<blockquote><p>You want to have a box with the title \"Welcome\" that you use to greet your visitors. The content for this box could be created by going:</p>
<pre>
return t(\"Welcome visitor, ... welcome message goes here ...\");
</pre>
<p>If we are however dealing with a registered user, we can customize the message by using:</p>
<pre>
if (\$user->uid) {
return t(\"Welcome \$user->name, ... welcome message goes here ...\");
}
else {
return t(\"Welcome visitor, ... welcome message goes here ...\");
}
</pre></blockquote>
<p>For more in-depth examples, we recommend that you check the existing boxes and use them as a starting point.</p>", array('%pcre' => 'http://php.net/pcre/'));
<p>An administrator defined block contains content supplied by you (as opposed to being generated automatically by a module). Each admin-defined block consists of a title, a description, and a body which can be as long as you wish. The Drupal engine will 'render' the content of the block.</p>", array('%pcre' => 'http://php.net/pcre/'));
case 'admin/modules#description':
return t('Controls the boxes that are displayed around the main content.');
case 'admin/block':
......@@ -101,7 +82,7 @@ function block_block($op = 'list', $delta = 0) {
else {
$block = db_fetch_object(db_query('SELECT * FROM {boxes} WHERE bid = %d', $delta));
$data['subject'] = $block->title;
$data['content'] = ($block->type == 1) ? eval($block->body) : $block->body;
$data['content'] = check_output($block->body, $block->format);
return $data;
}
}
......@@ -179,11 +160,20 @@ function block_admin_display() {
$blocks = _block_rehash();
// Fetch input formats used by admin-defined boxes.
$formats = array();
$result = db_query('SELECT bid, format FROM {boxes}');
while ($box = db_fetch_object($result)) {
$formats[$box->bid] = $box->format;
}
$header = array(t('block'), t('enabled'), t('custom'), t('throttle'), t('weight'), t('region'), t('path'), array('data' => t('operations'), 'colspan' => 2));
foreach ($blocks as $block) {
if ($block['module'] == 'block') {
$edit = l(t('edit'), 'admin/block/edit/'. $block['delta']);
if (filter_access($formats[$block['delta']])) {
$edit = l(t('edit'), 'admin/block/edit/'. $block['delta']);
}
$delete = l(t('delete'), 'admin/block/delete/'. $block['delta']);
}
else {
......@@ -232,14 +222,10 @@ function block_box_edit($bid = 0) {
}
function block_box_form($edit = array()) {
$type = array(0 => 'HTML', 1 => 'PHP');
$group = form_textfield(t('Block title'), 'title', $edit['title'], 50, 64, t('The title of the block as shown to the user.'));
$group .= filter_form('format', $edit['format']);
$group .= form_textarea(t('Block body'), 'body', $edit['body'], 70, 10, t('The content of the block as shown to the user.'));
$group .= form_textfield(t('Block description'), 'info', $edit['info'], 50, 64, t('A brief description of your block. Used on the <a href="%overview">block overview page</a>.', array('%overview' => url('admin/block'))));
if (user_access('create php content')) {
$group .= form_radios(t('Block type'), 'type', $edit['type'], $type, t("If you would like to use PHP code inside your block, set the above option to 'PHP' instead of 'HTML'."));
}
if ($edit['bid']) {
$group .= form_hidden('bid', $edit['bid']);
......@@ -252,16 +238,16 @@ function block_box_form($edit = array()) {
}
function block_box_save($edit) {
if (!user_access('create php content')) {
$edit['type'] = 0;
if (!filter_access($edit['format'])) {
$edit['format'] = FILTER_FORMAT_DEFAULT;
}
if ($edit['bid']) {
db_query("UPDATE {boxes} SET title = '%s', body = '%s', info = '%s', type = %d WHERE bid = %d", $edit['title'], $edit['body'], $edit['info'], $edit['type'], $edit['bid']);
db_query("UPDATE {boxes} SET title = '%s', body = '%s', info = '%s', format = %d WHERE bid = %d", $edit['title'], $edit['body'], $edit['info'], $edit['format'], $edit['bid']);
return t('the block has been updated.');
}
else {
db_query("INSERT INTO {boxes} (title, body, info, type) VALUES ('%s', '%s', '%s', %d)", $edit['title'], $edit['body'], $edit['info'], $edit['type']);
db_query("INSERT INTO {boxes} (title, body, info, format) VALUES ('%s', '%s', '%s', %d)", $edit['title'], $edit['body'], $edit['info'], $edit['format']);
return t('the new block has been added.');
}
}
......
......@@ -197,7 +197,7 @@ function blog_form(&$node) {
if ($iid && $item = db_fetch_object(db_query('SELECT i.*, f.title as ftitle, f.link as flink FROM {aggregator_item} i, {aggregator_feed} f WHERE i.iid = %d AND i.fid = f.fid', $iid))) {
$node->title = $item->title;
$node->body = "<a href=\"$item->link\">$item->title</a> - <i>". check_output($item->description) ."</i> [<a href=\"$item->flink\">$item->ftitle</a>]\n";
$node->body = "<a href=\"$item->link\">$item->title</a> - <i>". $item->description ."</i> [<a href=\"$item->flink\">$item->ftitle</a>]\n";
}
}
......@@ -205,7 +205,7 @@ function blog_form(&$node) {
$output .= implode('', taxonomy_node_form('blog', $node));
}
$output .= form_textarea(t('Body'), 'body', $node->body, 60, 15, filter_tips_short(), NULL, TRUE);
$output .= form_textarea(t('Body'), 'body', $node->body, 60, 15, '', NULL, TRUE);
return $output;
}
......
......@@ -197,7 +197,7 @@ function blog_form(&$node) {
if ($iid && $item = db_fetch_object(db_query('SELECT i.*, f.title as ftitle, f.link as flink FROM {aggregator_item} i, {aggregator_feed} f WHERE i.iid = %d AND i.fid = f.fid', $iid))) {
$node->title = $item->title;
$node->body = "<a href=\"$item->link\">$item->title</a> - <i>". check_output($item->description) ."</i> [<a href=\"$item->flink\">$item->ftitle</a>]\n";
$node->body = "<a href=\"$item->link\">$item->title</a> - <i>". $item->description ."</i> [<a href=\"$item->flink\">$item->ftitle</a>]\n";
}
}
......@@ -205,7 +205,7 @@ function blog_form(&$node) {
$output .= implode('', taxonomy_node_form('blog', $node));
}
$output .= form_textarea(t('Body'), 'body', $node->body, 60, 15, filter_tips_short(), NULL, TRUE);
$output .= form_textarea(t('Body'), 'body', $node->body, 60, 15, '', NULL, TRUE);
return $output;
}
......
......@@ -130,6 +130,7 @@ function blogapi_new_post($req_params) {
'promote' => $promote,
'comment' => $comment,
'moderate' => $moderate,
'format' => FILTER_DEFAULT_FORMAT,
'revision' => $revision
));
......
......@@ -130,6 +130,7 @@ function blogapi_new_post($req_params) {
'promote' => $promote,
'comment' => $comment,
'moderate' => $moderate,
'format' => FILTER_DEFAULT_FORMAT,
'revision' => $revision
));
......
......@@ -39,10 +39,11 @@ function book_access($op, $node) {
// Only registered users can update book pages. Given the nature
// of the book module this is considered to be a good/safe idea.
// One can only update a book page if there are no suggested updates
// of that page waiting for approval, it is not a PHP page, and
// the "create new revision" bit is set. That is, only updates that
// don't overwrite the current or pending information are allowed.
return user_access('maintain books') && !$node->moderate && !$node->format && $node->revision;
// of that page waiting for approval and as long as the "create new
// revision"-bit is set. That is, only updates that don't overwrite
// the current or pending information are allowed.
return user_access('maintain books') && !$node->moderate && $node->revision;
}
}
......@@ -139,7 +140,7 @@ function book_block($op = 'list', $delta = 0) {
function book_load($node) {
global $user;
$book = db_fetch_object(db_query('SELECT format, parent, weight, log FROM {book} WHERE nid = %d', $node->nid));
$book = db_fetch_object(db_query('SELECT parent, weight, log FROM {book} WHERE nid = %d', $node->nid));
if (arg(1) == 'edit' && !user_access('administer nodes')) {
// If a user is about to update a book page, we overload some
......@@ -166,14 +167,14 @@ function book_load($node) {
* Implementation of hook_insert().
*/
function book_insert($node) {
db_query("INSERT INTO {book} (nid, format, parent, weight, log) VALUES (%d, %d, %d, %d, '%s')", $node->nid, $node->format, $node->parent, $node->weight, $node->log);
db_query("INSERT INTO {book} (nid, parent, weight, log) VALUES (%d, %d, %d, '%s')", $node->nid, $node->parent, $node->weight, $node->log);