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