Index: database/database.mysql =================================================================== RCS file: /cvs/drupal/drupal/database/database.mysql,v retrieving revision 1.170 diff -u -F^f -r1.170 database.mysql --- database/database.mysql 21 Feb 2005 19:47:44 -0000 1.170 +++ database/database.mysql 5 Mar 2005 19:16:28 -0000 @@ -135,11 +135,12 @@ -- CREATE TABLE book ( + vid int(10) unsigned NOT NULL default '0', nid int(10) unsigned NOT NULL default '0', parent int(10) NOT NULL default '0', weight tinyint(3) NOT NULL default '0', - log longtext, - PRIMARY KEY (nid), + PRIMARY KEY (vid), + KEY nid (nid), KEY parent (parent) ) TYPE=MyISAM; @@ -232,6 +233,7 @@ CREATE TABLE files ( fid int(10) unsigned NOT NULL default '0', nid int(10) unsigned NOT NULL default '0', + vid int(10) unsigned NOT NULL default '0', filename varchar(255) NOT NULL default '', filepath varchar(255) NOT NULL default '', filemime varchar(255) NOT NULL default '', @@ -279,9 +281,11 @@ -- CREATE TABLE forum ( + vid int(10) unsigned NOT NULL default '0', nid int(10) unsigned NOT NULL default '0', tid int(10) unsigned NOT NULL default '0', - PRIMARY KEY (nid), + PRIMARY KEY (vid), + KEY nid (nid), KEY tid (tid) ) TYPE=MyISAM; @@ -392,8 +396,8 @@ CREATE TABLE node ( nid int(10) unsigned NOT NULL auto_increment, + vid int(10) unsigned NOT NULL default '1', type varchar(16) NOT NULL default '', - title varchar(128) NOT NULL default '', uid int(10) NOT NULL default '0', status int(4) NOT NULL default '1', created int(11) NOT NULL default '0', @@ -401,16 +405,12 @@ comment int(2) NOT NULL default '0', promote int(2) NOT NULL default '0', moderate int(2) NOT NULL default '0', - teaser longtext NOT NULL, - 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)), KEY status (status), KEY uid (uid), + KEY vid (vid), KEY node_moderate (moderate), KEY node_promote_status (promote, status), KEY node_created (created), @@ -431,6 +431,24 @@ grant_delete tinyint(1) unsigned NOT NULL default '0', PRIMARY KEY (nid,gid,realm) ) TYPE=MyISAM; + +-- +-- Table structure for table 'node_revisions' +-- + +CREATE TABLE node_revisions ( + nid int(10) unsigned NOT NULL, + vid int(10) unsigned NOT NULL, + uid int(10) NOT NULL default '0', + title varchar(128) NOT NULL default '', + body longtext NOT NULL default '', + teaser longtext NOT NULL default '', + log longtext NOT NULL default '', + timestamp int(11) NOT NULL default '0', + format int(4) NOT NULL default '0', + PRIMARY KEY (nid,vid), + KEY uid (uid) +) TYPE=MyISAM; -- -- Table structure for table 'profile_fields' Index: database/database.pgsql =================================================================== RCS file: /cvs/drupal/drupal/database/database.pgsql,v retrieving revision 1.106 diff -u -F^f -r1.106 database.pgsql --- database/database.pgsql 27 Feb 2005 15:40:35 -0000 1.106 +++ database/database.pgsql 5 Mar 2005 19:16:32 -0000 @@ -133,10 +133,10 @@ -- CREATE TABLE book ( + vid integer NOT NULL default '0', nid integer NOT NULL default '0', parent integer NOT NULL default '0', weight smallint NOT NULL default '0', - log text default '', PRIMARY KEY (nid) ); CREATE INDEX book_nid_idx ON book(nid); @@ -231,6 +231,7 @@ CREATE TABLE files ( fid SERIAL, nid integer NOT NULL default '0', + vid integer NOT NULL default '0', filename varchar(255) NOT NULL default '', filepath varchar(255) NOT NULL default '', filemime varchar(255) NOT NULL default '', @@ -279,11 +280,13 @@ -- CREATE TABLE forum ( + vid integer NOT NULL default '0', nid integer NOT NULL default '0', tid integer NOT NULL default '0', - shadow integer NOT NULL default '0', - PRIMARY KEY (nid) + PRIMARY KEY (vid) ); +CREATE INDEX forum_vid_idx ON forum(vid); +CREATE INDEX forum_nid_idx ON forum(nid); CREATE INDEX forum_tid_idx ON forum(tid); -- @@ -434,6 +437,26 @@ PRIMARY KEY (nid,gid,realm) ); +-- +-- Table structure for table 'node_revisions' +-- + +CREATE TABLE node_revisions ( + vid integer NOT NULL default '0', + nid integer NOT NULL default '0', + uid integer NOT NULL default '0', + title varchar(128) NOT NULL default '', + body text NOT NULL default '', + teaser text NOT NULL default '', + log text NOT NULL default '', + format smallint NOT NULL default '0', + timestamp integer NOT NULL default '0', + data text NOT NULL default '', + PRIMARY KEY (vid) +); + +CREATE INDEX node_revisions_nid_idx ON node_revisions(nid); +CREATE INDEX node_revisions_uid_idx ON node_revisions(uid); -- -- Table structure for table 'node_counter' Index: database/updates.inc =================================================================== RCS file: /cvs/drupal/drupal/database/updates.inc,v retrieving revision 1.98 diff -u -F^f -r1.98 updates.inc --- database/updates.inc 3 Mar 2005 20:21:51 -0000 1.98 +++ database/updates.inc 5 Mar 2005 19:16:53 -0000 @@ -102,7 +102,9 @@ "2005-01-28" => "update_123", "2005-02-11" => "update_124", "2005-02-23" => "update_125", - "2005-03-03" => "update_126" + "2005-03-03" => "update_126", + "2005-03-04" => "update_127", + "2005-03-05" => "update_128" ); function update_32() { @@ -2313,6 +2315,154 @@ function update_126() { return array(); } +function update_127() { + $ret = array(); + + if ($GLOBALS['db_type'] == 'mysql') { + $ret[] = update_sql("CREATE TABLE {node_revisions} ( + nid int(10) unsigned NOT NULL, + vid int(10) unsigned NOT NULL, + uid int(10) NOT NULL default '0', + title varchar(128) NOT NULL default '', + body longtext NOT NULL default '', + teaser longtext NOT NULL default '', + log longtext NOT NULL default '', + timestamp int(11) NOT NULL default '0', + format int(4) NOT NULL default '0', + PRIMARY KEY (vid), + KEY nid (nid), + KEY uid (uid) + )"); + $ret[] = update_sql("ALTER TABLE {node} ADD vid int(10) unsigned NOT NULL default '0'"); + $ret[] = update_sql("ALTER TABLE {files} ADD vid int(10) unsigned NOT NULL default '0'"); + $ret[] = update_sql("ALTER TABLE {book} ADD vid int(10) unsigned NOT NULL default '0'"); + $ret[] = update_sql("ALTER TABLE {forum} ADD vid int(10) unsigned NOT NULL default '0'"); + $ret[] = update_sql("ALTER TABLE {book} DROP PRIMARY KEY"); + $ret[] = update_sql("ALTER TABLE {forum} DROP PRIMARY KEY"); + $ret[] = update_sql("ALTER TABLE {book} ADD KEY nid (nid)"); + $ret[] = update_sql("ALTER TABLE {forum} ADD KEY nid (nid)"); + } + else { + $ret[] = update_sql("CREATE TABLE {node_revisions} ( + vid integer NOT NULL default '0', + nid integer NOT NULL default '0', + uid integer NOT NULL default '0', + title varchar(128) NOT NULL default '', + body text NOT NULL default '', + teaser text NOT NULL default '', + log text NOT NULL default '', + timestamp integer NOT NULL default '0', + format smallint NOT NULL default '0', + PRIMARY KEY (vid) + )"); + $ret[] = update_sql("ALTER TABLE {node} ADD vid integer NOT NULL default '0'"); + $ret[] = update_sql("ALTER TABLE {files} ADD vid integer NOT NULL default '0'"); + $ret[] = update_sql("ALTER TABLE {book} ADD vid integer NOT NULL default '0'"); + $ret[] = update_sql("ALTER TABLE {forum} ADD vid integer NOT NULL default '0'"); + $ret[] = update_sql("CREATE INDEX node_revisions_uid_idx ON node_revisions(uid)"); + $ret[] = update_sql("CREATE INDEX node_revisions_nid_idx ON node_revisions(nid)"); + $ret[] = update_sql("ALTER TABLE {book} DROP INDEX nid"); + $ret[] = update_sql("ALTER TABLE {forum} DROP INDEX nid"); +/* This needs to be ported to pgsql + $ret[] = update_sql("ALTER TABLE {book} ADD PRIMARY KEY vid (vid)"); + $ret[] = update_sql("ALTER TABLE {forum} ADD PRIMARY KEY vid (vid)"); + $ret[] = update_sql("ALTER TABLE {book} ADD KEY nid (nid)"); + $ret[] = update_sql("ALTER TABLE {forum} ADD KEY nid (nid)"); +*/ + } + + return $ret; +} + +function update_128() { + $ret = array(); + + $vid = 0; + $result = db_query("SELECT nid, uid, type, title, body, teaser, changed, format, revisions FROM {node}"); + while ($row = db_fetch_array($result)) { + $nid = $row['nid']; + $vid++; + db_query("INSERT INTO {node_revisions} (nid, vid, uid, title, body, teaser, timestamp, format) VALUES (%d, %d, %d, '%s', '%s', '%s', %d, %d)", $nid, $vid, $row['uid'], $row['title'], $row['body'], $row['teaser'], $row['changed'], $row['format']); + switch ($row['type']) { + case 'forum': + db_query("UPDATE {forum} SET vid = %d WHERE nid = %d", $vid, $nid); + break; + case 'book': + db_query("UPDATE {book} SET vid = %d WHERE nid = %d", $vid, $nid); + break; + } + + db_query("UPDATE {node} SET vid = %d WHERE nid = %d", $vid, $nid); + + $revisions = unserialize($row['revisions']); + if (is_array($revisions) && count($revisions) > 0) { + foreach ($revisions as $version) { + $revision = array(); + foreach ($version['node'] as $node_field => $node_value) { + $revision[$node_field] = $node_value; + } + $revision['uid'] = $version['uid']; + $revision['timestamp'] = $version['timestamp']; + + $vid++; + db_query("INSERT INTO {node_revisions} (nid, vid, uid, title, body, teaser, log, timestamp, format) VALUES (%d, %d, %d, '%s', '%s', '%s', '%s', %d, %d)", $nid, $vid, $revision['uid'], $revision['title'], $revision['body'], $revision['teaser'], $revision['log'], $revision['timestamp'], $revision['format']); + switch ($row['type']) { + case 'forum': + db_query("INSERT INTO {forum} (vid, nid, tid) VALUES (%d, %d, %d)", $vid, $nid, $revision['tid']); + break; + case 'book': + db_query("INSERT INTO {book} (vid, nid, parent, weight) VALUES (%d, %d, %d, %d, '%s')", $vid, $nid, $revision['parent'], $revision['weight']); + break; + } + } + } + } + + // Other node types might be added to the book. Move logs too. + $result = db_query("SELECT n.nid, n.vid, n.type, b.log FROM {book} b INNER JOIN {node} n ON b.nid = n.nid"); + while ($row = db_fetch_object($result)) { + if ($row->type != 'book') { + db_query("UPDATE {book} SET vid = %d WHERE nid = %d", $row->vid, $row->nid); + } + if ($row->log != '') { + db_query("UPDATE {node_revisions} SET log = '%s' WHERE vid = %d", $row->log, $row->vid); + } + } + + // Assign existing files to current vid + $result = db_query("SELECT n.nid, n.vid FROM {node} INNER JOIN {files} f ON n.nid = f.nid"); + while ($row = db_fetch_object($result)) { + db_query("UPDATE {files} SET vid = %d WHERE nid = %d", $row->vid, $row->nid); + } + + if ($GLOBALS['db_type'] == 'mysql') { + $ret[] = update_sql("ALTER TABLE {book} ADD PRIMARY KEY vid (vid)"); + $ret[] = update_sql("ALTER TABLE {forum} ADD PRIMARY KEY vid (vid)"); + $ret[] = update_sql("CREATE TABLE {old_revisions} ( + nid integer NOT NULL default '0', + revisions text NOT NULL default '', + PRIMARY KEY (nid) + )"); + } + else { // pgsql update + } + + // Move old revisions to old_revisions table. + $result = db_query("SELECT nid, revisions FROM {node} WHERE revisions != ''"); + while ($row = db_fetch_object($result)) { + db_query("INSERT INTO {old_revisions} (nid, revisions) VALUES (%d, '%s')", $row->nid, $row->revisions); + } + + $ret[] = update_sql("INSERT INTO {sequences} (name, id) VALUES ('{node_revisions}_vid', $vid)"); + $ret[] = update_sql("ALTER TABLE {book} DROP log"); + $ret[] = update_sql("ALTER TABLE {node} DROP teaser"); + $ret[] = update_sql("ALTER TABLE {node} DROP body"); + $ret[] = update_sql("ALTER TABLE {node} DROP format"); + $ret[] = update_sql("ALTER TABLE {node} DROP revisions"); + + return $ret; +} + function update_sql($sql) { $edit = $_POST["edit"]; $result = db_query($sql); Index: includes/database.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.inc,v retrieving revision 1.39 diff -u -F^f -r1.39 database.inc --- includes/database.inc 19 Feb 2005 22:24:23 -0000 1.39 +++ includes/database.inc 5 Mar 2005 19:16:56 -0000 @@ -129,8 +129,13 @@ function db_set_active($name = 'default' * A string containing an SQL query. * @param ... * A variable number of arguments which are substituted into the query using - * printf() syntax. Instead of a variable number of query arguments, you may - * also pass a single array containing the query arguments. + * printf() syntax. The query arguments can be enclosed in one array instead. + * For INSERT and UPDATE queries the arguments should be present in an + * array in which the keys name the database columns. For UPDATE queries + * additional parameters can be passed as single arguments after the array. + * Examples: + * db_query('INSERT INTO {node} %a', array('nid' => 42, 'title' => 'foo bar', ...)); + * db_query('UPDATE {node} SET %a WHERE nid = %d', array('uid' => 42, 'title' => 'foo bar'), $node->nid); * @return * A database query result resource, or FALSE if the query was not executed * correctly. @@ -139,8 +144,20 @@ function db_query($query) { $args = func_get_args(); $query = db_prefix_tables($query); if (count($args) > 1) { + if (strpos($query, '%a') !== FALSE) { + if (strpos($query, 'INSERT') !== FALSE) { + $keys = array_keys($args[1]); + $insert = '('. implode(', ', $keys) .') VALUES ('. implode(', ', array_pad(array(), count($keys), "'%s'")) .')'; + $query = str_replace('%a', $insert, $query); + } + else if (strpos($query, 'UPDATE') !== FALSE) { + $keys = array_keys($args[1]); + $update = implode(" = '%s', ", $keys) ." = '%s'"; + $query = str_replace('%a', $update, $query); + } + } if (is_array($args[1])) { - $args = array_merge(array($query), $args[1]); + $args = array_merge(array($query), $args[1], array_slice($args, 2)); } $args = array_map('db_escape_string', $args); $args[0] = $query; Index: includes/database.mysql.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.mysql.inc,v retrieving revision 1.27 diff -u -F^f -r1.27 database.mysql.inc --- includes/database.mysql.inc 29 Nov 2004 13:10:43 -0000 1.27 +++ includes/database.mysql.inc 5 Mar 2005 19:16:59 -0000 @@ -236,6 +236,92 @@ function db_escape_string($text) { } /** + * Begins a transaction/lock + * + * Use this when you need to update multiple tables in such a way that + * either all of the updates go through or none do at all. This is + * especially useful for the improved revisions system. + * + * In order to accomodate older versions of MySQL that don't support + * table types that have transactions, rollback is not used, and we + * must specify which tables are going to be + * + * So, your code will look something like this: + * db_begin_transaction(array('table1', 'table2')); + * // Check if conditions are okay for this update + * if ($conditions_ok) { + * // run queries + * } + * db_commit_transaction(); + * + * You code should ALWAYS call both db_begin_transaction() and + * db_commit_transaction() and ALWAYS call them in the same order + * + * @param $tables array of tables to lock + * @return a DB_Result object or a DB_Error + */ +function db_begin_transaction($tables) { + foreach ($tables as $num => $table) { + $tables[$num]= $table .' WRITE'; + } + return db_query('LOCK TABLES '. implode(', ', $tables)); +} + +/** + * Commits a transaction/lock + * + * See db_begin_transaction() for usage details + * + * @return a DB_Result object or a DB_Error + */ +function db_commit_transaction() { + return db_query('UNLOCK TABLES'); +} + +/** + * Begins a transaction/lock + * + * Use this when you need to update multiple tables in such a way that + * either all of the updates go through or none do at all. This is + * especially useful for the improved revisions system. + * + * In order to accomodate older versions of MySQL that don't support + * table types that have transactions, rollback is not used, and we + * must specify which tables are going to be + * + * So, your code will look something like this: + * db_begin_transaction(array('table1', 'table2')); + * // Check if conditions are okay for this update + * if ($conditions_ok) { + * // run queries + * } + * db_commit_transaction(); + * + * You code should ALWAYS call both db_begin_transaction() and + * db_commit_transaction() and ALWAYS call them in the same order + * + * @param $tables array of tables to lock + * @return a DB_Result object or a DB_Error + */ +function db_begin_transaction($tables) { + foreach ($tables as $num => $table) { + $tables[$num]= '{'. $table .'} WRITE'; + } + return db_query('LOCK TABLES '. implode(', ', $tables)); +} + +/** + * Commits a transaction/lock + * + * See db_begin_transaction() for usage details + * + * @return a DB_Result object or a DB_Error + */ +function db_commit_transaction() { + return db_query('UNLOCK TABLES'); +} + +/** * @} End of "ingroup database". */ Index: includes/database.pgsql.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.pgsql.inc,v retrieving revision 1.6 diff -u -F^f -r1.6 database.pgsql.inc --- includes/database.pgsql.inc 7 Jan 2005 19:18:05 -0000 1.6 +++ includes/database.pgsql.inc 5 Mar 2005 19:17:01 -0000 @@ -231,6 +231,45 @@ function db_escape_string($text) { } /** + * Begins a transaction/lock + * + * Use this when you need to update multiple tables in such a way that + * either all of the updates go through or none do at all. This is + * especially useful for the improved revisions system. + * + * In order to accomodate older versions of MySQL that don't support + * table types that have transactions, rollback is not used, and we + * must specify which tables are going to be + * + * So, your code will look something like this: + * db_begin_transaction(array('table1', 'table2')); + * // Check if conditions are okay for this update + * if ($conditions_ok) { + * // run queries + * } + * db_commit_transaction(); + * + * You code should ALWAYS call both db_begin_transaction() and + * db_commit_transaction() and ALWAYS call them in the same order + * + * @param $tables array of tables to lock + * @return a DB_Result object or a DB_Error + */ +function db_begin_transaction($tables) { + return db_query('START TRANSACTION'); +} + +/** + * Commits a transaction/lock + * + * See db_begin_transaction() for usage details + * + * @return a DB_Result object or a DB_Error + */ +function db_commit_transaction() { + return db_query('COMMIT'); +} +/** * @} End of "ingroup database". */ Index: modules/blogapi.module =================================================================== RCS file: /cvs/drupal/drupal/modules/blogapi.module,v retrieving revision 1.37 diff -u -F^f -r1.37 blogapi.module --- modules/blogapi.module 31 Jan 2005 19:36:20 -0000 1.37 +++ modules/blogapi.module 5 Mar 2005 19:17:10 -0000 @@ -370,7 +370,12 @@ function blogapi_get_recent_posts($req_p } $type = _blogapi_blogid($params[0]); - $result = db_query_range('SELECT n.nid, n.title,'. ($bodies ? ' n.body,' : '') ." n.created, u.name FROM {node} n, {users} u WHERE n.uid=u.uid AND n.type = '%s' AND n.uid = %d ORDER BY n.created DESC", $type, $user->uid, 0, $params[3]); + if ($bodies) { + $result = db_query_range("SELECT n.nid, n.title, r.body, n.created, u.name FROM {node} n, {node_revisions} r, {users} u WHERE n.uid = u.uid AND n.vid = r.vid AND n.type = '%s' AND n.uid = %d ORDER BY n.created DESC", $type, $user->uid, 0, $params[3]); + } + else { + $result = db_query_range("SELECT n.nid, n.title, n.created, u.name FROM {node} n, {users} u WHERE n.uid = u.uid AND n.type = '%s' AND n.uid = %d ORDER BY n.created DESC", $type, $user->uid, 0, $params[3]); + } while ($blog = db_fetch_object($result)) { $xmlrpcval = _blogapi_get_post($blog, $bodies); $blogs[] = $xmlrpcval; Index: modules/book.module =================================================================== RCS file: /cvs/drupal/drupal/modules/book.module,v retrieving revision 1.285 diff -u -F^f -r1.285 book.module --- modules/book.module 12 Feb 2005 07:51:14 -0000 1.285 +++ modules/book.module 5 Mar 2005 19:17:20 -0000 @@ -111,7 +111,7 @@ function book_menu($may_cache) { // We don't want to cache these menu items because they could change whenever // a book page or outline node is edited. if (arg(0) == 'admin' && arg(1) == 'node' && arg(2) == 'book') { - $result = db_query(db_rewrite_sql('SELECT n.nid, n.title FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = 0 ORDER BY b.weight, n.title')); + $result = db_query(db_rewrite_sql('SELECT n.nid, n.title FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = 0 ORDER BY b.weight, n.title')); while ($book = db_fetch_object($result)) { $items[] = array('path' => 'admin/node/book/'. $book->nid, 'title' => t('"%title" book', array('%title' => $book->title))); } @@ -136,7 +136,7 @@ function book_block($op = 'list', $delta else if ($op == 'view') { // Only display this block when the user is browsing a book: if (arg(0) == 'node' && is_numeric(arg(1))) { - $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.nid = %d'), arg(1)); + $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.nid = %d'), arg(1)); if (db_num_rows($result) > 0) { $node = db_fetch_object($result); @@ -163,7 +163,7 @@ function book_block($op = 'list', $delta function book_load($node) { global $user; - $book = db_fetch_object(db_query('SELECT parent, weight, log FROM {book} WHERE nid = %d', $node->nid)); + $book = db_fetch_object(db_query('SELECT parent, weight FROM {book} WHERE vid = %d', $node->vid)); if (arg(2) == 'edit' && !user_access('administer nodes')) { // If a user is about to update a book page, we overload some @@ -185,14 +185,19 @@ function book_load($node) { * Implementation of hook_insert(). */ function book_insert($node) { - db_query("INSERT INTO {book} (nid, parent, weight, log) VALUES (%d, %d, %d, '%s')", $node->nid, $node->parent, $node->weight, $node->log); + db_query("INSERT INTO {book} (nid, vid, parent, weight) VALUES (%d, %d, %d, %d)", $node->nid, $node->vid, $node->parent, $node->weight); } /** * Implementation of hook_update(). */ function book_update($node) { - db_query("UPDATE {book} SET parent = %d, weight = %d, log = '%s' WHERE nid = %d", $node->parent, $node->weight, $node->log, $node->nid); + if ($node->is_new || $node->revision) { + db_query("INSERT INTO {book} (nid, vid, parent, weight) VALUES (%d, %d, %d, %d, '%s')", $node->nid, $node->vid, $node->parent, $node->weight); + } + else { + db_query("UPDATE {book} SET parent = %d, weight = %d WHERE vid = %d", $node->parent, $node->weight, $node->vid); + } } /** @@ -229,6 +234,7 @@ function book_form(&$node) { $output .= form_textarea(t('Body'), 'body', $node->body, 60, 20, '', NULL, TRUE); $output .= filter_form('format', $node->format); + $output .= form_textarea(t('Log message'), 'log', $node->log, 60, 5, t('An explanation of the additions or updates being made to help other authors understand your motivations.')); if (user_access('administer nodes')) { @@ -256,13 +262,15 @@ function book_outline() { if ($node->nid) { switch ($op) { case t('Add to book outline'): - db_query('INSERT INTO {book} (nid, parent, weight) VALUES (%d, %d, %d)', $node->nid, $edit['parent'], $edit['weight']); + db_query('INSERT INTO {book} (nid, vid, parent, weight) VALUES (%d, %d, %d, %d)', $node->nid, $node->vid, $edit['parent'], $edit['weight']); + db_query("UPDATE {node_revisions} SET log = '%s' WHERE vid = %d", $edit['log'], $node->vid); drupal_set_message(t('Added the post to the book.')); drupal_goto("node/$node->nid"); break; case t('Update book outline'): - db_query('UPDATE {book} SET parent = %d, weight = %d WHERE nid = %d', $edit['parent'], $edit['weight'], $node->nid); + db_query('UPDATE {book} SET parent = %d, weight = %d WHERE vid = %d', $edit['parent'], $edit['weight'], $node->vid); + db_query("UPDATE {node_revisions} SET log = '%s' WHERE vid = %d", $edit['log'], $node->vid); drupal_set_message(t('Updated the book outline.')); drupal_goto("node/$node->nid"); break; @@ -274,10 +282,11 @@ function book_outline() { break; default: - $page = db_fetch_object(db_query('SELECT * FROM {book} WHERE nid = %d', $node->nid)); + $page = db_fetch_object(db_query('SELECT * FROM {book} WHERE vid = %d', $node->vid)); $output = form_select(t('Parent'), 'parent', $page->parent, book_toc($node->nid), t('The parent page in the book.')); - $output .= form_weight(t('Weight'), 'weight', $node->weight, 15, t('Pages at a given level are ordered first by weight and then by title.')); + $output .= form_weight(t('Weight'), 'weight', $page->weight, 15, t('Pages at a given level are ordered first by weight and then by title.')); + $output .= form_textarea(t('Log message'), 'log', $node->log, 60, 5, t('An explanation of the additions or updates being made to help other authors understand your motivations.')); if ($page->nid) { $output .= form_submit(t('Update book outline')); @@ -325,7 +334,7 @@ function book_revision_load($page, $cond * Return the path (call stack) to a certain book page. */ function book_location($node, $nodes = array()) { - $parent = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.nid = %d'), $node->parent)); + $parent = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.nid = %d'), $node->parent)); if ($parent->title) { $nodes = book_location($parent, $nodes); array_push($nodes, $parent); @@ -334,7 +343,7 @@ function book_location($node, $nodes = a } function book_location_down($node, $nodes = array()) { - $last_direct_child = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = %d ORDER BY b.weight DESC, n.title DESC'), $node->nid)); + $last_direct_child = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d ORDER BY b.weight DESC, n.title DESC'), $node->nid)); if ($last_direct_child) { array_push($nodes, $last_direct_child); $nodes = book_location_down($last_direct_child, $nodes); @@ -352,7 +361,7 @@ function book_prev($node) { } // Previous on the same level: - $direct_above = db_fetch_object(db_query(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = %d AND n.status = 1 AND n.moderate = 0 AND (b.weight < %d OR (b.weight = %d AND n.title < '%s')) ORDER BY b.weight DESC, n.title DESC"), $node->parent, $node->weight, $node->weight, $node->title)); + $direct_above = db_fetch_object(db_query(db_rewrite_sql("SELECT n.nid, n.title FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d AND n.status = 1 AND n.moderate = 0 AND (b.weight < %d OR (b.weight = %d AND n.title < '%s')) ORDER BY b.weight DESC, n.title DESC"), $node->parent, $node->weight, $node->weight, $node->title)); if ($direct_above) { // Get last leaf of $above. $path = book_location_down($direct_above); @@ -361,7 +370,7 @@ function book_prev($node) { } else { // Direct parent: - $prev = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.nid = %d AND n.status = 1 AND n.moderate = 0'), $node->parent)); + $prev = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.nid = %d AND n.status = 1 AND n.moderate = 0'), $node->parent)); return $prev; } } @@ -371,7 +380,7 @@ function book_prev($node) { */ function book_next($node) { // get first direct child - $child = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = %d AND n.status = 1 AND n.moderate = 0 ORDER BY b.weight ASC, n.title ASC'), $node->nid)); + $child = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d AND n.status = 1 AND n.moderate = 0 ORDER BY b.weight ASC, n.title ASC'), $node->nid)); if ($child) { return $child; } @@ -380,7 +389,7 @@ function book_next($node) { array_push($path = book_location($node), $node); // Path to top-level node including this one. while (($leaf = array_pop($path)) && count($path)) { - $next = db_fetch_object(db_query(db_rewrite_sql("SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = %d AND n.status = 1 AND n.moderate = 0 AND (b.weight > %d OR (b.weight = %d AND n.title > '%s')) ORDER BY b.weight ASC, n.title ASC"), $leaf->parent, $leaf->weight, $leaf->weight, $leaf->title)); + $next = db_fetch_object(db_query(db_rewrite_sql("SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d AND n.status = 1 AND n.moderate = 0 AND (b.weight > %d OR (b.weight = %d AND n.title > '%s')) ORDER BY b.weight ASC, n.title ASC"), $leaf->parent, $leaf->weight, $leaf->weight, $leaf->title)); if ($next) { return $next; } @@ -415,10 +424,6 @@ function book_content($node, $teaser = F */ function book_view(&$node, $teaser = FALSE, $page = FALSE) { $node = book_content($node, $teaser); - - if (!$teaser && $node->moderate) { - $node->body .= '
'. t('Log') .':
'. $node->log .'
'; - } } /** @@ -430,7 +435,7 @@ function book_nodeapi(&$node, $op, $teas switch ($op) { case 'view': if (!$teaser) { - $book = db_fetch_array(db_query('SELECT * FROM {book} WHERE nid = %d', $node->nid)); + $book = db_fetch_array(db_query('SELECT * FROM {book} WHERE vid = %d', $node->vid)); if ($book) { if ($node->moderate && user_access('administer nodes')) { drupal_set_message(t("This update/post awaits moderation and won't be accessible until approved.")); @@ -521,7 +526,7 @@ function book_toc_recurse($nid, $indent, } function book_toc($exclude = 0) { - $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 ORDER BY b.weight, n.title')); + $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 ORDER BY b.weight, n.title')); while ($node = db_fetch_object($result)) { if (!$children[$node->parent]) { @@ -574,7 +579,7 @@ function book_tree_recurse($nid, $depth, } function book_tree($parent = 0, $depth = 3, $unfold = array()) { - $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND n.moderate = 0 ORDER BY b.weight, n.title')); + $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.parent, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 AND n.moderate = 0 ORDER BY b.weight, n.title')); while ($node = db_fetch_object($result)) { $list = $children[$node->parent] ? $children[$node->parent] : array(); @@ -591,7 +596,7 @@ function book_tree($parent = 0, $depth = * Menu callback; prints a listing of all books. */ function book_render() { - $result = db_query(db_rewrite_sql('SELECT n.nid FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = 0 AND n.status = 1 AND n.moderate = 0 ORDER BY b.weight, n.title')); + $result = db_query(db_rewrite_sql('SELECT n.nid FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = 0 AND n.status = 1 AND n.moderate = 0 ORDER BY b.weight, n.title')); while ($page = db_fetch_object($result)) { // Load the node: @@ -617,7 +622,7 @@ function book_render() { */ function book_print($nid = 0, $depth = 1) { global $base_url; - $result = db_query(db_rewrite_sql('SELECT DISTINCT(n.nid), n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND n.nid = %d AND n.moderate = 0 ORDER BY b.weight, n.title'), $nid); + $result = db_query(db_rewrite_sql('SELECT DISTINCT(n.nid), n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 AND n.nid = %d AND n.moderate = 0 ORDER BY b.weight, n.title'), $nid); while ($page = db_fetch_object($result)) { // load the node: @@ -650,7 +655,7 @@ function book_print($nid = 0, $depth = 1 } function book_print_recurse($parent = '', $depth = 1) { - $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE n.status = 1 AND b.parent = %d AND n.moderate = 0 ORDER BY b.weight, n.title'), $parent); + $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, b.weight FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE n.status = 1 AND b.parent = %d AND n.moderate = 0 ORDER BY b.weight, n.title'), $parent); while ($page = db_fetch_object($result)) { // Load the node: @@ -687,7 +692,7 @@ function book_admin_view_line($node, $de } function book_admin_view_book($nid, $depth = 1) { - $result = db_query(db_rewrite_sql('SELECT n.nid FROM {node} n INNER JOIN {book} b ON n.nid = b.nid WHERE b.parent = %d ORDER BY b.weight, n.title'), $nid); + $result = db_query(db_rewrite_sql('SELECT n.nid FROM {node} n INNER JOIN {book} b ON n.vid = b.vid WHERE b.parent = %d ORDER BY b.weight, n.title'), $nid); while ($node = db_fetch_object($result)) { $node = node_load(array('nid' => $node->nid)); @@ -724,15 +729,16 @@ function book_admin_save($nid, $edit = a foreach ($edit as $nid => $value) { // Check to see whether the title needs updating: - $title = db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $nid)); - if ($title != $value['title']) { + $node = db_fetch_object(db_query('SELECT title, vid FROM {node} WHERE nid = %d', $nid)); + if ($node->title != $value['title']) { db_query("UPDATE {node} SET title = '%s' WHERE nid = %d", $value['title'], $nid); + db_query("UPDATE {book} SET title = '%s' WHERE vid = %d", $value['title'], $node->vid); } // Check to see whether the weight needs updating: - $weight = db_result(db_query('SELECT weight FROM {book} WHERE nid = %d', $nid)); - if ($weight != $value['weight']) { - db_query('UPDATE {book} SET weight = %d WHERE nid = %d', $value['weight'], $nid); + $node = db_fetch_object(db_query('SELECT b.vid, b.weight FROM {book} b INNER JOIN {node} n ON n.vid = b.vid WHERE n.nid = %d', $nid)); + if ($node->weight != $value['weight']) { + db_query('UPDATE {book} SET weight = %d WHERE vid = %d', $value['weight'], $node->vid); } } @@ -747,7 +753,7 @@ function book_admin_save($nid, $edit = a * Menu callback; displays a listing of all orphaned book pages. */ function book_admin_orphan() { - $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, n.status, b.parent FROM {node} n INNER JOIN {book} b ON n.nid = b.nid')); + $result = db_query(db_rewrite_sql('SELECT n.nid, n.title, n.status, b.parent FROM {node} n INNER JOIN {book} b ON v.nid = b.vid')); while ($page = db_fetch_object($result)) { $pages[$page->nid] = $page; @@ -795,7 +801,7 @@ function book_help($section) { return t("

The book organises content into a nested hierarchical structure. It is particularly good for manuals, Frequently Asked Questions (FAQs) and the like, allowing you to have chapters, sections, etc.

A book is simply a collection of nodes that have been linked together. These nodes are usually of type book page, but you can insert nodes of any type into a book outline. Every node in the book has a parent node which \"contains\" it. This is how book.module establishes its hierarchy. At any given level in the hierarchy, a book can contain many nodes. All these sibling nodes are sorted according to the weight that you give them.

-

Book pages contain a log message field which helps your users understand the motivation behind an edit of a book page. Each edited version of a book page is stored as a new revision of a node. This capability makes it easy to revert to an old version of a page, should that be desirable.

+

Each edited version of a book page is stored as a new revision of a node. This capability makes it easy to revert to an old version of a page, should that be desirable.

Like other node types, book submissions and edits may be subject to moderation, depending on your configuration. Similarly, books use permissions to determine who may read and write to them. Only administrators are allowed to create new books, which are really just nodes whose parent is <top-level>. To include an existing node in your book, click on the \"outline\"-tab on the node's page. This enables you to place the node wherever you'd like within the book hierarchy. To add a new node into your book, use the create content » book page link.

Administrators may review the hierarchy of their books by clicking on the collaborative book link in the administration pages. There, nodes may be edited, reorganized, removed from book, and deleted. This behavior may change in the future. When a parent node is deleted, it may leave behind child nodes. These nodes are now orphans. Administrators should periodically review their books for orphans and reaffiliate those pages as desired. Finally, administrators may also export their books to a single, flat HTML page which is suitable for printing.

Maintaining a FAQ using a collaborative book

Index: modules/forum.module =================================================================== RCS file: /cvs/drupal/drupal/modules/forum.module,v retrieving revision 1.233 diff -u -F^f -r1.233 forum.module --- modules/forum.module 3 Mar 2005 20:51:27 -0000 1.233 +++ modules/forum.module 5 Mar 2005 19:17:37 -0000 @@ -292,7 +292,7 @@ function forum_admin_configure() { * Implementation of hook_load(). */ function forum_load($node) { - $forum = db_fetch_object(db_query('SELECT * FROM {forum} WHERE nid = %d', $node->nid)); + $forum = db_fetch_object(db_query('SELECT * FROM {forum} WHERE vid = %d', $node->vid)); return $forum; } @@ -497,7 +497,12 @@ function forum_validate(&$node) { * Implementation of hook_update(). */ function forum_update($node) { - db_query('UPDATE {forum} SET tid = %d WHERE nid = %d', $node->tid, $node->nid); + if ($node->is_new || $node->revision) { + db_query("INSERT INTO {forum} (nid, vid, tid) VALUES (%d, %d, %d)", $node->nid, $node->vid, $node->tid); + } + else { + db_query('UPDATE {forum} SET tid = %d WHERE vid = %d', $node->tid, $node->vid); + } } /** @@ -530,7 +535,7 @@ function forum_form(&$node) { * Implementation of hook_insert(). */ function forum_insert($node) { - db_query('INSERT INTO {forum} (nid, tid) VALUES (%d, %d)', $node->nid, $node->tid); + db_query('INSERT INTO {forum} (nid, vid, tid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $node->tid); } /** @@ -655,7 +660,7 @@ function forum_get_topics($tid, $sortby, $term = taxonomy_get_term($tid); - $sql = db_rewrite_sql("SELECT n.nid, f.tid, n.title, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, IF(l.last_comment_uid, cu.name, l.last_comment_name) AS last_comment_name, l.last_comment_uid, l.comment_count AS num_comments FROM {node} n, {node_comment_statistics} l, {users} cu, {term_node} r, {users} u, {forum} f WHERE n.status = 1 AND l.last_comment_uid = cu.uid AND n.nid = l.nid AND n.nid = r.nid AND r.tid = %d AND n.uid = u.uid AND n.nid = f.nid"); + $sql = db_rewrite_sql("SELECT n.nid, f.tid, n.title, n.sticky, u.name, u.uid, n.created AS timestamp, n.comment AS comment_mode, l.last_comment_timestamp, IF(l.last_comment_uid, cu.name, l.last_comment_name) AS last_comment_name, l.last_comment_uid, l.comment_count AS num_comments FROM {node} n, {node_comment_statistics} l, {users} cu, {term_node} r, {users} u, {forum} f WHERE n.status = 1 AND l.last_comment_uid = cu.uid AND n.nid = l.nid AND n.nid = r.nid AND r.tid = %d AND n.uid = u.uid AND n.vid = f.vid"); $sql .= tablesort_sql($forum_topic_list_header, 'n.sticky DESC,'); $sql_count = db_rewrite_sql("SELECT COUNT(n.nid) FROM {node} n INNER JOIN {term_node} r ON n.nid = r.nid AND r.tid = %d WHERE n.status = 1 AND n.type = 'forum'"); Index: modules/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node.module,v retrieving revision 1.474 diff -u -F^f -r1.474 node.module --- modules/node.module 5 Mar 2005 11:05:07 -0000 1.474 +++ modules/node.module 5 Mar 2005 19:18:00 -0000 @@ -389,31 +389,27 @@ function node_load($conditions, $revisio } // Retrieve the node. - $node = db_fetch_object(db_query('SELECT n.*, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid WHERE '. implode(' AND ', $cond))); - $node = drupal_unpack($node); - - // Unserialize the revisions and user data fields. - if ($node->revisions) { - $node->revisions = unserialize($node->revisions); + if ($revision) { + $node = db_fetch_object(db_query('SELECT n.nid, n.vid, n.type, n.status, n.created, n.changed, n.comment, n.promote, n.moderate, n.sticky, r.timestamp AS revision_timestamp, r.title, r.body, r.teaser, r.format, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.nid = n.nid AND r.vid = %d WHERE '. implode(' AND ', $cond), $revision)); } - - // Call the node specific callback (if any) and piggy-back the - // results to the node or overwrite some values. - if ($extra = node_invoke($node, 'load')) { - foreach ($extra as $key => $value) { - $node->$key = $value; - } + else { + $node = db_fetch_object(db_query('SELECT n.nid, n.vid, n.type, n.status, n.created, n.changed, n.comment, n.promote, n.moderate, n.sticky, r.timestamp AS revision_timestamp, r.title, r.body, r.teaser, r.format, u.uid, u.name, u.picture, u.data FROM {node} n INNER JOIN {users} u ON u.uid = n.uid INNER JOIN {node_revisions} r ON r.vid = n.vid WHERE '. implode(' AND ', $cond))); } - if ($extra = node_invoke_nodeapi($node, 'load')) { - foreach ($extra as $key => $value) { - $node->$key = $value; + if ($node->nid) { + // Call the node specific callback (if any) and piggy-back the + // results to the node or overwrite some values. + if ($extra = node_invoke($node, 'load')) { + foreach ($extra as $key => $value) { + $node->$key = $value; + } + } + + if ($extra = node_invoke_nodeapi($node, 'load')) { + foreach ($extra as $key => $value) { + $node->$key = $value; + } } - } - - // Return the desired revision. - if (!is_null($revision) && is_array($node->revisions[$revision])) { - $node = $node->revisions[$revision]['node']; } if ($cachable) { @@ -427,61 +423,100 @@ function node_load($conditions, $revisio * Save a node object into the database. */ function node_save($node) { - // Fetch fields to save to node table: - $fields = node_invoke_nodeapi($node, 'fields'); + global $user; - // Serialize the revisions field: - if ($node->revisions) { - $node->revisions = serialize($node->revisions); - } + $node->is_new = false; // Apply filters to some default node fields: if (empty($node->nid)) { // Insert a new node. + $node->is_new = true; // Set some required fields: if (!$node->created) { $node->created = time(); } - if (!$node->changed) { - $node->changed = time(); - } $node->nid = db_next_id('{node}_nid'); - - // Prepare the query: - foreach ($node as $key => $value) { - if (in_array((string) $key, $fields)) { - $k[] = db_escape_string($key); - $v[] = $value; - $s[] = "'%s'"; - } + $node->vid = db_next_id('{node_revisions}_vid');; + } + else { + // We need to ensure that all node fields are filled. + $node_current = node_load(array('nid' => $node->nid)); + foreach ($node as $field => $data) { + $node_current->$field = $data; } + $node = $node_current; - // Insert the node into the database: - db_query("INSERT INTO {node} (". implode(", ", $k) .") VALUES(". implode(", ", $s) .")", $v); - - // Call the node specific callback (if any): - node_invoke($node, 'insert'); - node_invoke_nodeapi($node, 'insert'); + if ($node->revision) { + $node->vid = db_next_id('{node_revisions}_vid');; + } } - else { - // Update an existing node. - // Set some required fields: + if (!$node->changed) { $node->changed = time(); + } - // Prepare the query: - foreach ($node as $key => $value) { - if (in_array($key, $fields)) { - $q[] = db_escape_string($key) ." = '%s'"; - $v[] = $value; + // Split off revisions data to another structure + $revisions_table_values = array('nid' => $node->nid, 'vid' => $node->vid, + 'title' => $node->title, 'body' => $node->body, + 'teaser' => $node->teaser, 'log' => $node->log, 'timestamp' => $node->changed, + 'uid' => $user->uid, 'format' => $node->format); + $revisions_table_types = array('nid' => '%d', 'vid' => '%d', + 'title' => "'%s'", 'body' => "'%s'", + 'teaser' => "'%s'", 'log' => "'%s'", 'timestamp' => '%d', + 'uid' => '%d', 'format' => '%d'); + $node_table_values = array('nid' => $node->nid, 'vid' => $node->vid, + 'title' => $node->title, 'type' => $node->type, 'uid' => $node->uid, + 'status' => $node->status, 'created' => $node->created, + 'changed' => $node->changed, 'comment' => $node->comment, + 'promote' => $node->promote, 'moderate' => $node->moderate, + 'sticky' => $node->sticky); + $node_table_types = array('nid' => '%d', 'vid' => '%d', + 'title' => "'%s'", 'type' => "'%s'", 'uid' => '%d', + 'status' => '%d', 'created' => '%d', + 'changed' => '%d', 'comment' => '%d', + 'promote' => '%d', 'moderate' => '%d', + 'sticky' => '%d'); + + //Generate the node table query + if ($node->is_new) { + $node_query = 'INSERT INTO {node} ('. implode(', ', array_keys($node_table_types)) .') VALUES ('. implode(', ', $node_table_types) .')'; + $revisions_query = 'INSERT INTO {node_revisions} ('. implode(', ', array_keys($revisions_table_types)) .') VALUES ('. implode(', ', $revisions_table_types) .')'; + } + else { + $arr = array(); + foreach ($node_table_types as $key => $value) { + $arr[] = $key .' = '. $value; + } + $node_table_values[] = $node->nid; + $node_query = 'UPDATE {node} SET '. implode(', ', $arr) .' WHERE nid = %d'; + if ($node->revision) { + $revisions_query = 'INSERT INTO {node_revisions} ('. implode(', ', array_keys($revisions_table_types)) .') VALUES ('. implode(', ', $revisions_table_types) .')'; + } + else { + $arr = array(); + foreach ($revisions_table_types as $key => $value) { + $arr[] = $key .' = '. $value; } + $revisions_table_values[] = $node->vid; + $revisions_query = 'UPDATE {node_revisions} SET '. implode(', ', $arr) .' WHERE vid = %d'; } + } + + //Generate the node_revisions table query - // Update the node in the database: - db_query("UPDATE {node} SET ". implode(', ', $q) ." WHERE nid = '$node->nid'", $v); + // Insert the node into the database: + db_begin_transaction(array('node', 'node_revisions', 'watchdog', 'sessions')); + db_query($node_query, $node_table_values); + db_query($revisions_query, $revisions_table_values); + db_commit_transaction(); - // Call the node specific callback (if any): + // Call the node specific callback (if any): + if ($node->is_new) { + node_invoke($node, 'insert'); + node_invoke_nodeapi($node, 'insert'); + } + else { node_invoke($node, 'update'); node_invoke_nodeapi($node, 'update'); } @@ -515,6 +550,10 @@ function node_view($node, $teaser = FALS // TODO: this strips legitimate uses of '' also. $node->body = str_replace('', '', $node->body); + if ($node->log != '' && !$teaser && $node->moderate) { + $node->body .= '
'. t('Log') .':
'. $node->log .'
'; + } + // The 'view' hook can be implemented to overwrite the default function // to display nodes. if (node_hook($node, 'view')) { @@ -704,19 +743,16 @@ function node_menu($may_cache) { 'access' => node_access('update', $node), 'weight' => 1, 'type' => MENU_LOCAL_TASK); + $items[] = array('path' => 'node/'. arg(1) .'/revisions', 'title' => t('revisions'), + 'callback' => 'node_page', + 'access' => user_access('administer nodes'), + 'weight' => 2, + 'type' => MENU_LOCAL_TASK); $items[] = array('path' => 'node/'. arg(1) .'/delete', 'title' => t('delete'), 'callback' => 'node_page', 'access' => node_access('delete', $node), 'weight' => 1, 'type' => MENU_CALLBACK); - - if ($node->revisions) { - $items[] = array('path' => 'node/'. arg(1) .'/revisions', 'title' => t('revisions'), - 'callback' => 'node_page', - 'access' => user_access('administer nodes'), - 'weight' => 2, - 'type' => MENU_LOCAL_TASK); - } } } else if (arg(0) == 'admin' && arg(1) == 'node' && arg(2) == 'configure' && arg(3) == 'types' && is_string(arg(4))) { @@ -985,13 +1021,26 @@ function node_revision_overview($nid) { if (user_access('administer nodes')) { $node = node_load(array('nid' => $nid)); - drupal_set_title($node->title); + drupal_set_title(t('Revisions for %title', array('%title' => $node->title))); + + if ($node->vid) { + $header = array('', t('Author'), t('Title'), t('Date'), array('colspan' => '3', 'data' => t('Operations'))); - if ($node->revisions) { - $header = array(t('Older revisions'), array('colspan' => '3', 'data' => t('Operations'))); + $revisions = node_revision_list($node); - foreach ($node->revisions as $key => $revision) { - $rows[] = array(t('revision #%r revised by %u on %d', array('%r' => $key, '%u' => format_name(user_load(array('uid' => $revision['uid']))), '%d' => format_date($revision['timestamp'], 'small'))) . ($revision['history'] ? '
'. $revision['history'] .'' : ''), l(t('view'), "node/$node->nid", array(), "revision=$key"), l(t('rollback'), "node/$node->nid/rollback-revision/$key"), l(t('delete'), "node/$node->nid/delete-revision/$key")); + $i = 0; + foreach ($revisions as $key => $revision) { + $rows[] = array( + array('data' => ++$i, 'rowspan' => ($revision->log != '') ? 2 : 1), + format_name($revision), + $revision->title, + format_date($revision->timestamp, 'small'), + l(t('view'), "node/$node->nid/revision/". $revision->vid), + l(t('rollback'), "node/$node->nid/rollback-revision/". $revision->vid), + l(t('delete'), "node/$node->nid/delete-revision/". $revision->vid)); + if ($revision->log != '') { + $rows[] = array('', t('Log message:'), $revision->log, array('colspan' => 5)); + } } $output .= theme('table', $header, $rows); } @@ -1000,32 +1049,6 @@ function node_revision_overview($nid) { return $output; } - -/** - * Return the revision with the specified revision number. - */ -function node_revision_load($node, $revision) { - return $node->revisions[$revision]['node']; -} - -/** - * Create and return a new revision of the given node. - */ -function node_revision_create($node) { - global $user; - - // "Revision" is the name of the field used to indicate that we have to - // create a new revision of a node. - if ($node->nid && $node->revision) { - $prev = node_load(array('nid' => $node->nid)); - $node->revisions = $prev->revisions; - unset($prev->revisions); - $node->revisions[] = array('uid' => $user->uid, 'timestamp' => time(), 'node' => $prev, 'history' => $node->history); - } - - return $node; -} - /** * Roll back to the revision with the specified revision number. */ @@ -1033,29 +1056,13 @@ function node_revision_rollback($nid, $r global $user; if (user_access('administer nodes')) { - $node = node_load(array('nid' => $nid)); - - // Extract the specified revision: - $rev = $node->revisions[$revision]['node']; - // Inherit all the past revisions: - $rev->revisions = $node->revisions; + $node = node_load(array('nid' => $nid), $revision); - // Save the original/current node: - $rev->revisions[] = array('uid' => $user->uid, 'timestamp' => time(), 'node' => $node); + node_save($node); - // Remove the specified revision: - unset($rev->revisions[$revision]); - - // Save the node: - foreach ($node as $key => $value) { - $filter[] = $key; - } - - node_save($rev, $filter); - - drupal_set_message(t('Rolled back to revision %revision of %title', array('%revision' => "#$revision", '%title' => "$node->title"))); - drupal_goto('node/'. $nid .'/revisions'); + drupal_set_message(t('Rolled back to the revision with the ID %revision of %title', array('%revision' => "$revision", '%title' => "$node->title"))); + drupal_goto("node/$nid/revisions"); } } @@ -1064,14 +1071,22 @@ function node_revision_rollback($nid, $r */ function node_revision_delete($nid, $revision) { if (user_access('administer nodes')) { + // Start a transaction so that the current node and node count + // don't change between the time we load it from the database and + // delete the node + db_begin_transaction(array('node', 'node_revisions', 'watchdog', 'sessions')); $node = node_load(array('nid' => $nid)); + $revisions = node_revision_list($node); - unset($node->revisions[$revision]); + // Don't delete the last revision of the node or the current revision + if ((count($revisions) > 0) && ($revision != $node->vid)) { + db_query("DELETE FROM {node_revisions} WHERE nid = %d AND vid = %d", array($nid, $revision)); + } - node_save($node, array('nid', 'revisions')); + db_commit_transaction(); - drupal_set_message(t('Deleted revision %revision of %title', array('%revision' => "#$revision", '%title' => "$node->title"))); - drupal_goto('node/'. $nid . (count($node->revisions) ? '/revisions' : '')); + drupal_set_message(t('Deleted revision with the ID %revision of %title', array('%revision' => "$revision", '%title' => "$node->title"))); + drupal_goto("node/$nid/revisions"); } } @@ -1079,12 +1094,13 @@ function node_revision_delete($nid, $rev * Return a list of all the existing revision numbers. */ function node_revision_list($node) { - if (is_array($node->revisions)) { - return array_keys($node->revisions); - } - else { - return array(); + $revisions = array(); + $result = db_query('SELECT r.vid, r.title, r.log, r.uid, r.timestamp, u.name FROM {node_revisions} r INNER JOIN {users} u ON u.uid = r.uid WHERE r.nid = %d ORDER BY r.timestamp ASC', $node->nid); + while ($revision = db_fetch_object($result)) { + $revisions[] = $revision; } + + return $revisions; } /** @@ -1216,9 +1232,6 @@ function node_validate($node) { form_set_error('changed', t('This content has been modified by another user, unable to save changes.')); } - // Create a new revision when required. - $node = node_revision_create($node); - if (user_access('administer nodes')) { // Set up default values, if required. if (!$node->created) { @@ -1588,8 +1601,12 @@ function node_delete($edit) { if (node_access('delete', $node)) { if ($edit['confirm']) { - // Delete the specified node: + // Delete the specified node: + // We don't need to lock this in a transaction because once the + // entry in node disappears, the entry in node_revisions won't be + // accessable db_query('DELETE FROM {node} WHERE nid = %d', $node->nid); + db_query('DELETE FROM {node_revisions} WHERE nid = %d', $node->nid); // Call the node-specific callback (if any): node_invoke($node, 'delete'); @@ -1677,9 +1694,21 @@ function node_page() { case 'delete-revision': node_revision_delete(arg(1), arg(3)); break; + case 'revision': + if (is_numeric(arg(1)) && is_numeric(arg(3))) { + $node = node_load(array('nid' => arg(1)), arg(3)); + if ($node->nid) { + drupal_set_title($node->title); + print theme('page', node_show($node, arg(2))); + } + else { + drupal_not_found(); + } + } + break; case 'view': if (is_numeric(arg(1))) { - $node = node_load(array('nid' => arg(1)), $_GET['revision']); + $node = node_load(array('nid' => arg(1))); if ($node->nid) { drupal_set_title($node->title); print theme('page', node_show($node, arg(2))); @@ -1779,9 +1808,6 @@ function node_nodeapi(&$node, $op, $arg case 'settings': // $node contains the type name in this operation return form_checkboxes(t('Default options'), 'node_options_'. $node, variable_get('node_options_'. $node, array('status', 'promote')), array('status' => t('Published'), 'moderate' => t('In moderation queue'), 'promote' => t('Promoted to front page'), 'sticky' => t('Sticky at top of lists'), 'revision' => t('Create new revision')), t('Users with the administer nodes permission will be able to override these options.')); - - case 'fields': - return array('nid', 'uid', 'type', 'title', 'teaser', 'body', 'revisions', 'status', 'promote', 'moderate', 'sticky', 'created', 'changed', 'format'); } } Index: modules/page.module =================================================================== RCS file: /cvs/drupal/drupal/modules/page.module,v retrieving revision 1.132 diff -u -F^f -r1.132 page.module --- modules/page.module 8 Feb 2005 19:44:39 -0000 1.132 +++ modules/page.module 5 Mar 2005 19:18:00 -0000 @@ -74,6 +74,8 @@ function page_form(&$node) { $output .= form_textarea(t('Body'), 'body', $node->body, 60, 20, '', NULL, TRUE); $output .= filter_form('format', $node->format); + $output .= form_textarea(t('Log message'), 'log', $node->log, 60, 5, t('An explanation of the additions or updates being made to help other authors understand your motivations.')); + return $output; } Index: modules/upload.module =================================================================== RCS file: /cvs/drupal/drupal/modules/upload.module,v retrieving revision 1.28 diff -u -F^f -r1.28 upload.module --- modules/upload.module 8 Feb 2005 19:43:02 -0000 1.28 +++ modules/upload.module 5 Mar 2005 19:18:20 -0000 @@ -282,10 +282,10 @@ function upload_nodeapi(&$node, $op, $ar function upload_count_size($uid = 0) { if ($uid) { - $result = db_query("SELECT SUM(f.filesize) FROM {files} f INNER JOIN {node} n ON f.nid = n.nid WHERE uid = %d", $uid); + $result = db_query("SELECT SUM(f.filesize) FROM {files} f INNER JOIN {node_revisions} n ON f.vid = n.vid WHERE uid = %d", $uid); } else { - $result = db_query("SELECT SUM(f.filesize) FROM {files} f INNER JOIN {node} n ON f.nid = n.nid"); + $result = db_query("SELECT SUM(f.filesize) FROM {files} f INNER JOIN {node_revisions} n ON f.vid = n.vid"); } return db_result($result); @@ -300,8 +300,8 @@ function upload_save($node) { // Insert new files: if ($file = file_save_upload($file, $file->filename)) { $fid = db_next_id('{files}_fid'); - db_query("INSERT INTO {files} (fid, nid, filename, filepath, filemime, filesize, list) VALUES (%d, %d, '%s', '%s', '%s', %d, %d)", - $fid, $node->nid, $file->filename, $file->filepath, $file->filemime, $file->filesize, $node->list[$key]); + db_query("INSERT INTO {files} (fid, nid, vid, filename, filepath, filemime, filesize, list) VALUES (%d, %d, %d, '%s', '%s', '%s', %d, %d)", + $fid, $node->nid, $node->vid, $file->filename, $file->filepath, $file->filemime, $file->filesize, $node->list[$key]); } } else { @@ -355,8 +355,8 @@ function upload_form($node) { function upload_load($node) { $files = array(); - if ($node->nid) { - $result = db_query("SELECT * FROM {files} WHERE nid = %d", $node->nid); + if ($node->vid) { + $result = db_query("SELECT * FROM {files} WHERE vid = %d", $node->vid); while ($file = db_fetch_object($result)) { $files[$file->fid] = $file; } Index: themes/xtemplate/pushbutton/xtemplate.css =================================================================== RCS file: /cvs/drupal/drupal/themes/xtemplate/pushbutton/Attic/xtemplate.css,v retrieving revision 1.15 diff -u -F^f -r1.15 xtemplate.css --- themes/xtemplate/pushbutton/xtemplate.css 5 Aug 2004 01:21:15 -0000 1.15 +++ themes/xtemplate/pushbutton/xtemplate.css 5 Mar 2005 19:18:31 -0000 @@ -1,4 +1,595 @@ +<<<<<<< xtemplate.css +<<<<<<< xtemplate.css +<<<<<<< xtemplate.css +<<<<<<< xtemplate.css +<<<<<<< xtemplate.css +/* $Id: xtemplate.css,v 1.6 2004/03/29 18:41:00 dries Exp $ */ + +/* +** HTML elements +*/ +body { + color: #000; + background-color: #fff; + margin: 0; + padding: 0; +} +body, p, td, li, ul, ol { + font-family: Verdana, Helvetica, Arial, sans-serif; +} +h1, h2, h3, h4, h5, h6 { + font-family: "Trebuchet MS", Geneva, Arial, Helvetica, SunSans-Regular, Verdana, sans-serif; + color: #9E2535; +} +img { + display: block; + border: 0; +} +tr.dark { + background-color: #ddd; +} +tr.light { + background-color: #eee; +} +tr.dark td, tr.light td { + padding: 0.3em; +} +h1, h2, h3, h4, h5, h6 { + margin: 0; +} +h1 { + font-size: 1.2em; + font-weight: bold; +} +h2 { + font-size: 1.1em; +} +h3, h4, h5, h6 { + font-size: 1em; +} +p, li { + text-align: left; + font-size: 0.85em; + line-height: 1.3; +} +a:link { + text-decoration: none; + font-weight: bold; + color: #ff8c00; +} +a:visited { + text-decoration: none; + font-weight: bold; + color: #c96; +} +a:hover, a:active { + font-weight: bold; + color: #ff4500; + text-decoration: underline; +} +fieldset { + border: 1px solid #ccc; +} +p { + margin: 0 0 .5em 0; + padding: 0; +} +blockquote { + border-left: 4px solid #69c; + padding: 0 15px 0 15px; + margin: 25px 100px 25px 50px; + color: #696969; + text-align: left; + font-size: 1.01em; + line-height: 1.3; + font-family: "Trebuchet MS", Geneva, Arial, Helvetica, SunSans-Regular, Verdana, sans-serif; +} +pre { + background-color: #eee; + padding: 0.75em 1.5em; + font-size: 1.2em; + border: 1px solid #ddd; +} +.form-item { + margin-top: 1em; +} +.form-item label { + color: #9E2535; +} +.item-list .title { + color: #9E2535; + font-size: 0.85em; +} + +/* +** Page layout blocks / IDs +*/ +#primary-menu { + background-color: #FFFAF0; +} +#primary-menu tr { + background: transparent url(header-a.jpg) left bottom repeat; +} +td#home { + background: transparent url(logo-background.jpg) left top repeat; +} +td#home a:link img, td#home a:visited img { + background: transparent url(logo-active.jpg) repeat; + width: 144px; + height: 63px; +} +td#home a:hover img { + background: transparent url(logo-hover.jpg) repeat; + width: 144px; + height: 63px; +} +.primary-links, .primary-links a:link, .primary-links a:visited { + color: #9E2535; +} +.primary-links a:hover { + color: #000; +} +#primary-menu .primary-links { + background: transparent url(header-b.jpg) left top no-repeat; + font-size: 0.79em; +} +#primary-menu .primary-links h1, #primary-menu .primary-links h2, #primary-menu .primary-links h3 { + font-size: 2.3em; + color: #369; +} +#secondary-menu { + background-color: #9E2535; + border-top: 3px solid #FF6600; + border-bottom: 3px solid #FF6600; +} +.secondary-links, .secondary-links a:link, .secondary-links a:visited { + color: #e4e9eb; +} +.secondary-links a:hover { + color: #fff; + text-decoration: underline; +} +#secondary-menu .secondary-links { + font-size: 0.85em; +} +#content { + background-color: #fff; +} +#contentstart { + background-color: #fff; +} +#menu { + padding: 0.5em 0.5em 0 0.5em; + text-align: right; + vertical-align: middle; +} +#search .form-text, #search .form-submit { + border: 1px solid #9E2535; + font-size: 0.85em; +} +#search .form-text { + width: 8em; + height: 1.4em; + padding: 0 0.5em 0 0.5em; + margin: 0 0 0.5em 0; +} +#search .form-submit { + height: 1.5em; +} +#mission { + background-color: #fff; + color: #696969; + border-top: 2px solid #dcdcdc; + border-bottom: 2px solid #dcdcdc; + padding: 10px 10px 10px 10px; + margin: 20px 30px 0px 35px; + font-family: "Trebuchet MS", Geneva, Arial, Helvetica, SunSans-Regular, Verdana, sans-serif; + font-size: 1.1em; + font-weight: normal; +} +#main { + /* padding in px not ex because IE messes up 100% width tables otherwise */ + padding: 25px 30px 45px 30px; + background: transparent url(background.gif) center center no-repeat; +} +#mission, .node .content, .comment .content { + line-height: 1.4; +} +#help { + font-size: 0.9em; + margin-bottom: 1em; +} +.breadcrumb { + margin-bottom: .5em; +} +.message { + background-color: #eee; + border: 1px solid #ccc; + padding: 0.3em; + margin-bottom: 1em; +} +.error { + border-color: red; +} +.nav { + padding: 0px 0px 0px 0px; + margin: 0px 0px 0px 0px; +} +#sidebar-left, #sidebar-right { + font-size: 0.75em; + width: 175px; + /* padding in px not ex because IE messes up 100% width tables otherwise */ + padding: 25px 10px 75px 10px; + vertical-align: top; + background: #FFFAF0; +} +#sidebar-left { + border-right: 3px solid #f5f5f5; +} +#sidebar-right { + border-left: 3px solid #f5f5f5; +} +#sidebar-left li, #sidebar-right li { + font-size: 1em; +} +#footer-message { + padding: 15px 100px 30px 100px; + font-size: 0.85em; + text-align: center; + color: #aaa; +} +table#footer-menu { + border-top: 3px solid #FF6600; + border-bottom: 3px solid #FF6600; + background-color: #9E2535; + color: #e4e9eb; +} +#footer-menu td { + padding: 5px; + font-size: 0.75em; +} +#footer-menu .primary-links, #footer-menu a:link, #footer-menu a:visited { + color: #e4e9eb; +} +#footer-menu a:hover { + color: #fff; + text-decoration: underline; +} +#footer-menu .primary-links h1, #footer-menu .primary-links h2, #footer-menu .primary-links h3 { + font-size: 1.3em; + color: #e4e9eb; +} +/* +** Common declarations for child classes of node, comment, block, box, etc. +** If you want any of them styled differently for a specific parent, add +** additional rules /with only the differing properties!/ to .parent .class. +** See .comment .title for an example. +*/ +.title, .title a { + font-weight: bold; + font-size: 1.3em; + color: #9E2535; + margin: 0 auto 0 auto; /* decrease default margins for h.title */ +} +.submitted { + color: #999; + font-size: 0.79em; +} +.links { + color: #ff8c00; + font-size: 0.8em; + padding: 0px 0px 0px 0px; + margin: 0px 0px 0px 0px; +} +.links a { + font-weight: bold; +} +.box { + padding: 0 0 1.5em 0; +} +.box { + padding: 0px 0px 0px 0px; + margin: 0px 0px 0px 0px; +} +.box h2 { + font-size: 9px; +} +.block .title h3 { + border-bottom: 2px solid #FF6600; + color: #9E2535; + font-size: 16px; + font-weight: bold; + padding: 10px 5px 10px 30px; + margin-bottom: .25em; + background: transparent url(icon-block.gif) left center no-repeat; +} +.block .content { + padding: 5px 5px 5px 5px; +} +.block { + margin-bottom: 0.7em; +} +.box .title { + font-size: 1.1em; +} +.node { + margin: .5em 0 2.5em 0; +} +.node .content, .comment .content { + padding: .5em 0 .75em 0; +} +.node .taxonomy { + color: #999; + font-size: 0.83em; + padding: 1.5em; +} +.node .picture { + border: 1px solid #fff; + float: right; + margin: 0.5em; +} +.comment { + border: 1px solid #abc; + padding: .5em; + margin-bottom: 1em; +} +.comment .title { + font-size: 1em; + padding: 10px 0px 12px 19px; + background: transparent url(icon-comment.gif) left center no-repeat; +} +.comment .new { + font-weight: bold; + font-size: 1em; + margin-left: 2px; + color: red; +} +.comment .picture { + border: 1px solid #fff; + float: right; + margin: 10px; +} +.links { + font-size: 0.75em; +} +.links .prev, .links .next, .links .up { + font-size: 1.15em; +} +.titles .prev, .titles .next { + font-size: 0.85em; + font-weight: bold; + color: #444; +} +.hide { + display: none +} +.nav .links .next a:link { + padding: 15px 20px 16px 0; + background: transparent url(arrow-next.gif) right center no-repeat; +} +.nav .links .next a:hover { + padding: 15px 20px 16px 0; + background: transparent url(arrow-next-hover.gif) right center no-repeat; +} +.nav .links .prev a:link { + padding: 15px 0 16px 20px; + background: transparent url(arrow-prev.gif) left center no-repeat; +} +.nav .links .prev a:hover { + padding: 15px 0 16px 20px; + background: transparent url(arrow-prev-hover.gif) left center no-repeat; +} +.nav .links .up a:link { + padding: 12px 0 20px 0; + background: transparent url(arrow-up.gif) center top no-repeat; +} +.nav .links .up a:hover { + padding: 12px 0 20px 0; + background: transparent url(arrow-up-hover.gif) center top no-repeat; +} + +/* +** Module specific styles +*/ +.content .active { + color: #9E2535; +} +#aggregator .feed { + background-color: #eee; + border: 1px solid #ccc; + padding: 1em; + margin: 1em 0 1em 0; +} +#aggregator .news-item .source { + color: #999; + font-style: italic; + font-size: 0.85em; +} +#aggregator .title { + font-size: 1em; +} +#aggregator h3 { + margin-top: 1em; +} +#tracker th { + text-align: center; + background-color: #f5f5f5; + border-bottom: 1px solid #ddd; + border-right: 1px solid #ddd; + border-left: 1px solid #fafafa; +} +#tracker th img { + float: right; +} +#tracker tr.light, #tracker tr.dark { + background-color: #fff; +} +#tracker td { + vertical-align: top; + padding: 1em 1em 1em 0; + border-bottom: 1px solid #bbb; +} +#forum { + margin: 15px 0 15px 0; + background-color: #fff; +} +#forum table { + width: 100%; + border: 2px solid #69c; +} +#forum table tr th { + text-align: center; + background: #69c; + color: #fff; + font-size: 0.75em; + border-bottom: 1px solid #aaa; +} +#forum table tr th a { + color: #fff; + text-decoration: underline; +} +#forum table tr th img { + margin: 0; +} +#forum tr.dark { + background: #FFFAF0; +} +#forum tr.light { + background: #fff; +} +#forum td { + padding: 0.5em 0.5em 0.5em 0.5em; +} +#forum td.container { + color: #000; + background: #369 url(forum-container.jpg) right top no-repeat; + border: 2px solid #69c; +} +#forum td.container a { + color: #e4e9eb; + padding: 20px 0 20px 35px; + background: transparent url(forum-link.gif) left center no-repeat; +} +#forum td.container a:visited { + color: #e4e9eb; +} +#forum td.statistics, #forum td.settings, #forum td.pager { + height: 1.5em; + border: 1px solid #bbb; +} +#forum td .name { + color: #96c; +} +#forum td .links { + padding-top: 0.7em; + font-size: 0.9em; +} +.block-forum h3 { + margin-bottom: .5em; +} +.calendar a { + text-decoration: none; +} +.calendar td { + padding: 0; + border-color: #888; +} +.calendar td div { + padding: 0.4em 0; +} +.calendar .row-week td a { + padding: 0.4em 0; +} +.calendar .day-today { + background-color: #69c; +} +.calendar .day-today a { + color: #fff; +} +.calendar .day-selected { + background-color: #9E2535; + color: #fff; +} +.calendar .header-month { + background-color: #bbb; +} +.calendar .header-week { + background-color: #ccc; +} +.calendar .day-blank { + background-color: #ccc; +} +.calendar .row-week td a:hover { + background-color: #fff; color: #000; +} +.secondary-links { + width:100%; +} +.secondary-links UL { + width: auto; + float: left; + margin: 0; + padding: 0; + display: inline; + list-style-type: none; +} +.secondary-links UL LI { + width: auto; + max-width:35em; + float: left; + margin: 0; + padding: 0; + display: inline; + list-style-type: none; +} +.secondary-links UL LI a { + width: auto; + display: block; + font-size: 1em; + line-height: 1.5em; + margin: 0; + padding: 0px 5px; + border-right: 1px solid; +} +.secondary-links UL LI a:hover { + width: auto; + border-right: 1px solid black; +} +#secondary-menu #search { + width: 13em; + display: inline; + float: right; +} +.flexinode-23 label, .flexinode-24 label, .flexinode-3 label, .flexinode-4 label { + display: none; +} +.flexinode-7 br, .flexinode-6 br, .flexinode-5 br, .flexinode-23 br, .flexinode-24 br, .flexinode-3 br, .flexinode-4 br, .flexinode-16 br, .flexinode-17 br, .flexinode-12 br, .flexinode-13 br, .flexinode-14 br, .flexinode-15 br, .flexinode-18 br, .flexinode-19 br { + display: none; +} +.flexinode-23 .form-item, .flexinode-24 .form-item, .flexinode-3 .form-item, .flexinode-4 .form-item, .flexinode-7 .form-item, .flexinode-6 .form-item, .flexinode-5 .form-item { + margin-bottom: 0em; + margin-top: 0em; +} +.flexinode-29 label { + display:none; +} +.flexinode-body { + margin-bottom: 1em; +} +======= +/* $Id: xtemplate.css,v 1.11 2004/06/02 05:35:51 dries Exp $ */ +======= +/* $Id: xtemplate.css,v 1.12 2004/06/20 20:04:28 dries Exp $ */ +>>>>>>> 1.12 +======= +/* $Id: xtemplate.css,v 1.13 2004/07/08 16:04:07 dries Exp $ */ +>>>>>>> 1.13 +======= +/* $Id: xtemplate.css,v 1.14 2004/07/25 22:18:32 unconed Exp $ */ +>>>>>>> 1.14 +======= /* $Id: xtemplate.css,v 1.15 2004/08/05 01:21:15 unconed Exp $ */ +>>>>>>> 1.15 /* ** HTML elements @@ -518,3 +1109,4 @@ fieldset { .calendar .row-week td a:hover { background-color: #fff; color: #000; } +>>>>>>> 1.11