Newer
Older
// $Id$
/**
* @file
* The core that allows content to be submitted to the site.
*/
Kjartan Mannes
committed
define('NODE_NEW_LIMIT', time() - 30 * 24 * 60 * 60);
/**
* Implementation of hook_help().
*/
function node_help($section) {
Dries Buytaert
committed
switch ($section) {
<h3>Nodes</h3>
<p>The core of the Drupal system is the node. All of the contents of the system are placed in nodes, or extensions of nodes.
A base node contains:<dl>
<dt>A Title</dt><dd>Up to 128 characters of text that titles the node.</dd>
<dt>A Teaser</dt><dd>A small block of text that is meant to get you interested in the rest of node. Drupal will automatically pull a small amount of the body of the node to make the teaser (To configure how long the teaser will be <a href=\"%teaser\">click here</a>). The teaser can be changed if you don't like what Drupal grabs.</dd>
<dt>The Body</dt><dd>The main text that comprises your content.</dd>
<dt>A Type</dt><dd>What kind of node is this? Blog, book, forum, comment, unextended, etc.</dd>
Dries Buytaert
committed
<dt>An Author</dt><dd>The author's name. It will either be \"anonymous\" or a valid user. You <em>cannot</em> set it to an arbitrary value.</dd>
<dt>Authored on</dt><dd>The date the node was written.</dd>
<dt>Changed</dt><dd>The last time this node was changed.</dd>
<dt>Sticky at top of lists</dt><dd>In listings such as the frontpage or a taxonomy overview the teasers of a selected amount of nodes is displayed. If you want to force a node to appear on the top of such a listing, you must set it to 'sticky'. This way it will float to the top of a listing, and it will not be pushed down by newer content.
<dt>Allow user comments</dt><dd>A node can have comments. These comments can be written by other users (Read-write), or only by admins (Read-only).</dd>
<dt>Revisions</dt><dd>Drupal has a revision system so that you can \"roll back\" to an older version of a post if the new version is not what you want.</dd>
<dt>Promote to front page</dt><dd>To get people to look at the new stuff on your site you can choose to move it to the front page. The front page is configured to show the teasers from only a few of the total nodes you have on your site (To configure how many teasers <a href=\"%teaser\">click here</a>).</dd>
<dt>Published</dt><dd>When using Drupal's moderation system a node remains unpublished -- unavailable to non-moderators -- until it is marked Published.</dd></dl>
<p>Now that you know what is in a node, here are some of the types of nodes available.</p>", array("%teaser" => url("admin/node/configure/settings")));
foreach (node_get_types() as $type => $name) {
$output .= '<h3>'. t('Node type: %name', array('%name' => $name)). '</h3>';
$output .= implode("\n", module_invoke_all('help', 'node/add#'. $type));
Dries Buytaert
committed
Dries Buytaert
committed
return t('Allows content to be submitted to the site and displayed on pages.');
case 'admin/node/configure':
case 'admin/node/configure/settings':
Dries Buytaert
committed
return t('<p>Settings for the core of Drupal. Almost everything is a node so these settings will affect most of the site.</p>');
Dries Buytaert
committed
case 'admin/node':
return t('<p>Below is a list of all of the posts on your site. Other forms of content are listed elsewhere (e.g. <a href="%comments">comments</a>).</p><p>Clicking a title views the post, while clicking an author\'s name views their user information.</p>', array('%comments' => url('admin/comment')));
Dries Buytaert
committed
case 'admin/node/search':
Dries Buytaert
committed
return t('<p>Enter a simple pattern to search for a post. This can include the wildcard character *.<br />For example, a search for "br*" might return "bread bakers", "our daily bread" and "brenda".</p>');
if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'revisions') {
return t('The revisions let you track differences between multiple versions of a post.');
}
Dries Buytaert
committed
if (arg(0) == 'node' && arg(1) == 'add' && $type = arg(2)) {
return variable_get($type .'_help', '');
}
Kjartan Mannes
committed
function node_cron() {
Kjartan Mannes
committed
}
Dries Buytaert
committed
* A DB result object from a query to fetch node objects. If your query joins the <code>node_comment_statistics</code> table so that the <code>comment_count</code> field is available, a title attribute will be added to show the number of comments.
* @param $title
* A heading for the resulting list.
*
* @return
* An HTML list suitable as content for a block.
*/
function node_title_list($result, $title = NULL) {
while ($node = db_fetch_object($result)) {
$items[] = l($node->title, 'node/'. $node->nid, $node->comment_count ? array('title' => format_plural($node->comment_count, '1 comment', '%count comments')) : '');
/**
* Update the 'last viewed' timestamp of the specified node for current user.
*/
function node_tag_new($nid) {
global $user;
if ($user->uid) {
db_query('UPDATE {history} SET timestamp = %d WHERE uid = %d AND nid = %d', time(), $user->uid, $nid);
@db_query('INSERT INTO {history} (uid, nid, timestamp) VALUES (%d, %d, %d)', $user->uid, $nid, time());
/**
* Retrieves the timestamp at which the current user last viewed the
* specified node.
*/
if (!isset($history[$nid])) {
$history[$nid] = db_fetch_object(db_query("SELECT timestamp FROM {history} WHERE uid = '$user->uid' AND nid = %d", $nid));
}
return ($history[$nid]->timestamp ? $history[$nid]->timestamp : 0);
Dries Buytaert
committed
* Decide on the type of marker to be displayed for a given node.
* @param $nid
* Node ID whose history supplies the "last viewed" timestamp.
* @param $timestamp
* Time which is compared against node's "last viewed" timestamp.
Dries Buytaert
committed
* @return
* One of the MARK constants.
Dries Buytaert
committed
function node_mark($nid, $timestamp) {
Dries Buytaert
committed
if (!$user->uid) {
return MARK_READ;
}
Dries Buytaert
committed
$cache[$nid] = node_last_viewed($nid);
Dries Buytaert
committed
if ($cache[$nid] == 0 && $timestamp > NODE_NEW_LIMIT) {
return MARK_NEW;
}
elseif ($timestamp > $cache[$nid] && $timestamp > NODE_NEW_LIMIT) {
return MARK_UPDATED;
}
return MARK_READ;
Steven Wittens
committed
* Automatically generate a teaser for a node body in a given format.
Steven Wittens
committed
function node_teaser($body, $format = NULL) {
// find where the delimiter is in the body
$delimiter = strpos($body, '<!--break-->');
Steven Wittens
committed
// If the size is zero, and there is no delimiter, the entire body is the teaser.
Steven Wittens
committed
// We check for the presence of the PHP evaluator filter in the current
// format. If the body contains PHP code, we do not split it up to prevent
// parse errors.
if (isset($format)) {
$filters = filter_list_format($format);
Steven Wittens
committed
if (isset($filters['filter/1']) && strpos($body, '<?') !== false) {
Steven Wittens
committed
return $body;
}
Steven Wittens
committed
}
// If a valid delimiter has been specified, use it to chop of the teaser.
Steven Wittens
committed
// If we have a short body, the entire body is the teaser.
// In some cases, no delimiter has been specified (e.g. when posting using
// the Blogger API). In this case, we try to split at paragraph boundaries.
Steven Wittens
committed
// When even the first paragraph is too long, we try to split at the end of
$breakpoints = array('</p>' => 4, '<br />' => 0, '<br>' => 0, "\n" => 0, '. ' => 1, '! ' => 1, '? ' => 1, '。' => 1, '؟ ' => 1);
Steven Wittens
committed
foreach ($breakpoints as $point => $charnum) {
if ($length = strpos($body, $point, $size)) {
return substr($body, 0, $length + $charnum);
}
Steven Wittens
committed
// If all else fails, we simply truncate the string.
Steven Wittens
committed
return truncate_utf8($body, $size);
function _node_names($op = '', $node = NULL) {
static $node_names, $node_list;
if (!isset($node_names)) {
$node_names = module_invoke_all('node_info');
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
foreach ($node_names as $type => $value) {
$node_list[$type] = $value['name'];
}
}
if ($node) {
if (is_array($node)) {
$type = $node['type'];
}
elseif (is_object($node)) {
$type = $node->type;
}
elseif (is_string($node)) {
$type = $node;
}
if (!isset($node_names[$type])) {
return FALSE;
}
}
switch ($op) {
case 'base':
return $node_names[$type]['base'];
case 'list':
return $node_list;
case 'name':
return $node_list[$type];
}
}
* Determine the basename for hook_load etc.
* @param $node
* Either a node object, a node array, or a string containing the node type.
* The basename for hook_load, hook_nodeapi etc.
function node_get_base($node) {
return _node_names('base', $node);
}
Dries Buytaert
committed
/**
* Determine the human readable name for a given type.
*
* @param $node
* Either a node object, a node array, or a string containing the node type.
* @return
* The human readable name of the node type.
*/
function node_get_name($node) {
return _node_names('name', $node);
* Return the list of available node types.
* @param $node
* Either a node object, a node array, or a string containing the node type.
* An array consisting (type => name) pairs.
function node_get_types() {
return _node_names('list');
* Determine whether a node hook exists.
*
* @param &$node
* Either a node object, node array, or a string containing the node type.
* @param $hook
* A string containing the name of the hook.
* @return
* TRUE iff the $hook exists in the node type of $node.
*/
function node_hook(&$node, $hook) {
return module_hook(node_get_base($node), $hook);
* Invoke a node hook.
*
* @param &$node
* Either a node object, node array, or a string containing the node type.
* @param $hook
* A string containing the name of the hook.
* @param $a2, $a3, $a4
* Arguments to pass on to the hook, after the $node argument.
* @return
*/
function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
if (node_hook($node, $hook)) {
$function = node_get_base($node) ."_$hook";
/**
* Invoke a hook_nodeapi() operation in all modules.
*
* @param &$node
* @param $op
* A string containing the name of the nodeapi operation.
* @param $a3, $a4
* Arguments to pass on to the hook, after the $node and $op arguments.
* @return
* The returned value of the invoked hooks.
*/
foreach (module_implements('nodeapi') as $name) {
$result = $function($node, $op, $a3, $a4);
if (is_array($result)) {
$return = array_merge($return, $result);
}
else if (isset($result)) {
$return[] = $result;
* @param $param
* Either the nid of the node or an array of conditions to match against in the database query
* @param $revision
* Which numbered revision to load. Defaults to the current version.
function node_load($param = array(), $revision = NULL, $reset = NULL) {
static $nodes = array();
if ($reset) {
$nodes = array();
}
if (is_numeric($param)) {
$cachable = $revision == NULL;
if ($cachable && $nodes[$param]) {
return $nodes[$param];
}
$cond = 'n.nid = '. $param;
else {
// Turn the conditions into a query.
foreach ($param as $key => $value) {
$cond[] = 'n.'. db_escape_string($key) ." = '". db_escape_string($value) ."'";
}
$cond = implode(' AND ', $cond);
if ($revision) {
$node = db_fetch_object(db_query(db_rewrite_sql('SELECT n.nid, r.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.log, 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 '. $cond), $revision));
else {
$node = db_fetch_object(db_query(db_rewrite_sql('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.log, 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 '. $cond)));
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;
}
}
$node->is_new = false;
$node->is_new = true;
if (!$node->created) {
$node->created = time();
}
$node->vid = db_next_id('{node_revisions}_vid');;
}
else {
// We need to ensure that all node fields are filled.
$node_current = node_load($node->nid);
foreach ($node as $field => $data) {
$node_current->$field = $data;
$node = $node_current;
if ($node->revision) {
$node->old_vid = $node->vid;
$node->vid = db_next_id('{node_revisions}_vid');
// We always update the timestamp for new revisions.
$node->changed = time();
}
if (!$node->changed) {
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
// 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 and the
//the node_revisions 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';
// Insert the node into the database:
db_query($node_query, $node_table_values);
db_query($revisions_query, $revisions_table_values);
// 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');
// Clear the cache so an anonymous poster can see the node being added or updated.
/**
* Generate a display of the given node.
*
* @param $node
* A node array or node object.
* @param $teaser
* Whether to display the teaser only, as on the main page.
* @param $page
* Whether the node is being displayed by itself as a page.
Dries Buytaert
committed
* @param $links
* Whether or not to display node links. Links are omitted for node previews.
*
* @return
* An HTML representation of the themed node.
*/
Dries Buytaert
committed
function node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE) {
Dries Buytaert
committed
// Remove the delimiter (if any) that separates the teaser from the body.
if ($node->log != '' && !$teaser && $node->moderate) {
$node->body .= '<div class="log"><div class="title">'. t('Log') .':</div>'. $node->log .'</div>';
}
// The 'view' hook can be implemented to overwrite the default function
// to display nodes.
// Allow modules to change $node->body before viewing.
node_invoke_nodeapi($node, 'view', $teaser, $page);
Dries Buytaert
committed
if ($links) {
$node->links = module_invoke_all('link', 'node', $node, !$page);
}
// unset unused $node part so that a bad theme can not open a security hole
if ($teaser) {
unset($node->body);
}
else {
unset($node->teaser);
}
$node->body = check_markup($node->body, $node->format, FALSE);
$node->teaser = check_markup($node->teaser, $node->format, FALSE);
/**
* Generate a page displaying a single node, along with its comments.
*/
if (function_exists('comment_render') && $node->comment) {
$output .= comment_render($node, $cid);
// Update the history table, stating that this user viewed this node.
node_tag_new($node->nid);
function node_search($op = 'search', $keys = null) {
switch ($op) {
case 'name':
return t('content');
case 'reset':
variable_del('node_cron_last');
return;
case 'status':
$last = variable_get('node_cron_last', 0);
$total = db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = 1 AND moderate = 0'));
$remaining = db_result(db_query('SELECT COUNT(*) FROM {node} n LEFT JOIN {node_comment_statistics} c ON n.nid = c.nid WHERE n.status = 1 AND n.moderate = 0 AND (n.created > %d OR n.changed > %d OR c.last_comment_timestamp > %d)', $last, $last, $last));
return array('remaining' => $remaining, 'total' => $total);
case 'search':
list($join, $where) = _db_rewrite_sql();
$find = do_search($keys, 'node', 'INNER JOIN {node} n ON n.nid = i.sid '. $join .' INNER JOIN {users} u ON n.uid = u.uid', 'n.status = 1'. (empty($where) ? '' : ' AND '. $where));
$results = array();
foreach ($find as $item) {
$node = node_load($item);
// Get node output (filtered and with module-specific fields).
if (node_hook($node, 'view')) {
node_invoke($node, 'view', false, false);
}
else {
$node = node_prepare($node, false);
}
// Allow modules to change $node->body before viewing.
node_invoke_nodeapi($node, 'view', false, false);
$extra = node_invoke_nodeapi($node, 'search result');
$results[] = array('link' => url('node/'. $item),
'type' => node_get_name($node),
'title' => $node->title,
Dries Buytaert
committed
'user' => theme('username', $node),
'date' => $node->changed,
'snippet' => search_excerpt($keys, $node->body));
}
return $results;
}
*/
function node_configure() {
if ($_POST) {
system_settings_save();
}
$output .= form_select(t('Number of posts on main page'), 'default_nodes_main', variable_get('default_nodes_main', 10), drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)), t('The default maximum number of posts to display per page on overview pages such as the main page.'));
Steven Wittens
committed
$output .= form_select(t('Length of trimmed posts'), 'teaser_length', variable_get('teaser_length', 600), array(0 => t('Unlimited'), 200 => t('200 characters'), 400 => t('400 characters'), 600 => t('600 characters'), 800 => t('800 characters'), 1000 => t('1000 characters'), 1200 => t('1200 characters'), 1400 => t('1400 characters'), 1600 => t('1600 characters'), 1800 => t('1800 characters'), 2000 => t('2000 characters')), t("The maximum number of characters used in the trimmed version of a post. Drupal will use this setting to determine at which offset long posts should be trimmed. The trimmed version of a post is typically used as a teaser when displaying the post on the main page, in XML feeds, etc. To disable teasers, set to 'Unlimited'. Note that this setting will only affect new or updated content and will not affect existing teasers."));
$output .= form_radios(t('Preview post'), 'node_preview', variable_get('node_preview', 0), array(t('Optional'), t('Required')), t('Must users preview posts before submitting?'));
/**
* Retrieve the comment mode for the given node ID (none, read, or read/write).
*/
$comment_mode[$nid] = db_result(db_query('SELECT comment FROM {node} WHERE nid = %d', $nid));
Kjartan Mannes
committed
function node_link($type, $node = 0, $main = 0) {
$links[] = l(t('read more'), "node/$node->nid", array('title' => t('Read the rest of this posting.'), 'class' => 'read-more'));
if ($may_cache) {
$items[] = array('path' => 'admin/node', 'title' => t('content'),
$items[] = array('path' => 'admin/node/action', 'title' => t('content'),
'type' => MENU_CALLBACK);
$items[] = array('path' => 'admin/node/overview', 'title' => t('list'),
'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
$items[] = array('path' => 'admin/settings/node', 'title' => t('posts'),
'access' => user_access('administer nodes'));
$items[] = array('path' => 'admin/settings/content-types', 'title' => t('content types'),
Dries Buytaert
committed
'callback' => 'node_types_configure',
'access' => user_access('administer nodes'));
if (module_exist('search')) {
$items[] = array('path' => 'admin/node/search', 'title' => t('search'),
'callback' => 'node_admin',
'access' => user_access('administer nodes'),
'type' => MENU_LOCAL_TASK);
}
'access' => user_access('access content'),
'type' => MENU_SUGGESTED_ITEM);
$items[] = array('path' => 'node/add', 'title' => t('create content'),
'access' => user_access('access content'),
'type' => MENU_ITEM_GROUPING,
'weight' => 1);
}
else {
if (arg(0) == 'node' && is_numeric(arg(1))) {
$node = node_load(arg(1));
Dries Buytaert
committed
if ($node->nid) {
$items[] = array('path' => 'node/'. arg(1), 'title' => t('view'),
Dries Buytaert
committed
'access' => node_access('view', $node),
'type' => MENU_CALLBACK);
$items[] = array('path' => 'node/'. arg(1) .'/view', 'title' => t('view'),
'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
$items[] = array('path' => 'node/'. arg(1) .'/edit', 'title' => t('edit'),
'callback' => 'node_page',
'access' => node_access('update', $node),
'weight' => 1,
$items[] = array('path' => 'node/'. arg(1) .'/delete', 'title' => t('delete'),
Dries Buytaert
committed
'callback' => 'node_delete_page',
'access' => node_access('delete', $node),
'weight' => 1,
'type' => MENU_CALLBACK);
if (user_access('administer nodes') && db_result(db_query('SELECT COUNT(vid) FROM {node_revisions} WHERE nid = %d', arg(1))) > 1) {
Dries Buytaert
committed
$items[] = array('path' => 'node/'. arg(1) .'/revisions', 'title' => t('revisions'),
'callback' => 'node_page',
'access' => user_access('administer nodes'),
'weight' => 2,
'type' => MENU_LOCAL_TASK);
}
Dries Buytaert
committed
else if (arg(0) == 'admin' && arg(1) == 'settings' && arg(2) == 'content-types' && is_string(arg(3))) {
$items[] = array('path' => 'admin/settings/content-types/'. arg(3),
'title' => t("'%name' content type", array('%name' => node_get_name(arg(3)))),
Dries Buytaert
committed
'type' => MENU_CALLBACK);
}
Dries Buytaert
committed
function node_last_changed($nid) {
$node = db_fetch_object(db_query('SELECT changed FROM {node} WHERE nid = %d', $nid));
return ($node->changed);
}
'approve' => array(t('Approve the selected posts'), 'UPDATE {node} SET status = 1, moderate = 0 WHERE nid = %d'),
'promote' => array(t('Promote the selected posts'), 'UPDATE {node} SET status = 1, promote = 1 WHERE nid = %d'),
'sticky' => array(t('Make the selected posts sticky'), 'UPDATE {node} SET status = 1, sticky = 1 WHERE nid = %d'),
'demote' => array(t('Demote the selected posts'), 'UPDATE {node} SET promote = 0 WHERE nid = %d'),
'unpublish' => array(t('Unpublish the selected posts'), 'UPDATE {node} SET status = 0 WHERE nid = %d'),
'delete' => array(t('Delete the selected posts'), '')
if (($op == t('Update') || $op == t('Delete all')) && isset($edit['operation']) && isset($edit['nodes'])) {
$edit['nodes'] = array_diff($edit['nodes'], array(0));
if (count($edit['nodes']) == 0) {
form_set_error('', t('Please select some items to perform the update on.'));
}
else {
if ($operations[$edit['operation']][1]) {
// Flag changes
$operation = $operations[$edit['operation']][1];
foreach ($edit['nodes'] as $nid => $value) {
if ($value) {
db_query($operation, $nid);
}
}
drupal_set_message(t('The update has been performed.'));
}
else if ($edit['operation'] == 'delete') {
// Mass delete
if ($edit['confirm']) {
foreach ($edit['nodes'] as $nid => $value) {
node_delete(array('nid' => $nid, 'confirm' => 1));
}
drupal_set_message(t('The items have been deleted.'));
}
else {
$extra = '<ul>';
foreach ($edit['nodes'] as $nid => $value) {
if ($value) {
$title = db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $nid));
$extra .= '<li>'. form_hidden('nodes]['. $nid, 1) . check_plain($title) .'</li>';
$extra .= '</ul>';
$extra .= form_hidden('operation', 'delete');
$output = theme('confirm',
t('Are you sure you want to delete these items?'),
'admin/node',
t('This action cannot be undone.'),
t('Delete all'),
t('Cancel'),
$extra);
/*
** Filters
*/
// Regular filters
$filters = array(
Steven Wittens
committed
'status' => array('title' => t('status'),
'options' => array('status-1' => t('published'), 'status-0' => t('not published'),
'moderate-1' => t('in moderation'), 'moderate-0' => t('not in moderation'),
'promote-1' => t('promoted'), 'promote-0' => t('not promoted'),
'sticky-1' => t('sticky'), 'sticky-0' => t('not sticky'))),
'type' => array('title' => t('type'), 'where' => "n.type = '%s'",
'options' => node_get_types()));
// Merge all vocabularies into one for retrieving $value below
if ($taxonomy = module_invoke('taxonomy', 'form_all')) {
$terms = array();
foreach ($taxonomy as $key => $value) {
$terms = $terms + $value;
}
$filters['category'] = array('title' => t('category'), 'where' => 'tn.tid = %d',
'options' => $terms, 'join' => 'INNER JOIN {term_node} tn ON n.nid = tn.nid');
}
if (!isset($_SESSION['node_overview_filter']) || !is_array($_SESSION['node_overview_filter']) || $op == t('Reset')) {
$_SESSION['node_overview_filter'] = array();
}
$session = &$_SESSION['node_overview_filter'];
$filter = $edit['filter'];
if (($op == t('Filter') || $op == t('Refine')) && isset($filter)) {
if (isset($filters[$filter]['options'][$edit[$filter]])) {
$session[] = array($filter, $edit[$filter]);
}
}
if ($op == t('Undo')) {
array_pop($session);
}
if ($op != '') {
drupal_goto('admin/node');
/*
** Form
*/
$output .= '<div id="node-admin-filter">';
// Existing filters
$form = '<ul>';
$i = 0;
foreach ($session as $filter) {
list($type, $value) = $filter;
$params = array('%a' => '<strong>'. $filters[$type]['title'] .'</strong>', '%b' => '<strong>'. $filters[$type]['options'][$value] .'</strong>');
$form .= '<li>'. ($i++ ? t('<em>and</em> where <strong>%a</strong> is <strong>%b</strong>', $params) : t('<strong>%a</strong> is <strong>%b</strong>', $params)) .'</li>';
}
if (isset($filters['category'])) {
$filters['category']['options'] = $taxonomy;
}
$options[$key] = $value['title'];
$b .= form_select('', $key, 1, $filters[$key]['options']);
$buttons = '';
if (count($options)) {
$form .= '<li><dl class="multiselect">';
$a = '';
foreach ($options as $key => $value) {
$a .= form_radio($value, 'filter', $key);
}
if (!$i) {
$form .= t('<dd class="a">%a</dd> <dt>is</dt> <dd class="b">%b</dd>', array('%a' => $a, '%b' => $b));
}
else {
$form .= t('<dt><em>and</em> where</dt> <dd class="a">%a</dd> <dt>is</dt> <dd class="b">%b</dd>', array('%a' => $a, '%b' => $b));
}
$form .= '</dl>';
$buttons = form_submit(count($session) ? t('Refine') : t('Filter'));
}
if (count($session)) {
$buttons .= form_submit(t('Undo')) . form_submit(t('Reset'));
}
$form .= '<div class="container-inline" id="node-admin-buttons">'. $buttons .'</div>';
$form .= '</li></ul><br class="clear" />';
$output .= form_group(t('Show only items where'), $form);
// Build query
$where = $args = array();
$join = '';
foreach ($session as $filter) {
list($key, $value) = $filter;
if ($key == 'status') {
// Note: no exploit hole as $value has already been checked
list($key, $value) = explode('-', $value, 2);
$where[] = 'n.'. $key .' = %d';
}
else {
$where[] = $filters[$key]['where'];
}
$args[] = $value;
$join .= $filters[$key]['join'];
}
$where = count($where) ? 'WHERE '. implode(' AND ', $where) : '';
Steven Wittens
committed
$result = pager_query('SELECT n.*, u.name, u.uid FROM {node} n '. $join .' INNER JOIN {users} u ON n.uid = u.uid '. $where .' ORDER BY n.changed DESC', 50, 0, NULL, $args);
// Make sure the update controls are disabled if we don't have any rows to select from.
$disabled = !db_num_rows($result);
$form = form_select(NULL, 'operation', 'approve', $options, NULL, ($disabled ? 'disabled="disabled"' : ''));
$form .= form_submit(t('Update'), 'op', ($disabled ? array('disabled' => 'disabled') : array()));
$output .= form_group(t('Update options'), "<div class=\"container-inline\">$form</div>");
$output .= '</div>';
$header = array(NULL, t('Title'), t('Type'), t('Author'), t('Status'), t('Operations'));
Dries Buytaert
committed
$destination = drupal_get_destination();
$rows[] = array(form_checkbox(NULL, 'nodes]['. $node->nid, 1, 0),
l($node->title, 'node/'. $node->nid) .' '. theme('mark', node_mark($node->nid, $node->changed)),
node_get_name($node),
Dries Buytaert
committed
theme('username', $node),
($node->status ? t('published') : t('not published')),
Dries Buytaert
committed
l(t('edit'), 'node/'. $node->nid .'/edit', array(), $destination));
$rows[] = array(array('data' => t('No posts available.'), 'colspan' => '6'));
$output .= theme('pager', NULL, 50, 0);
return form($output, 'post', url('admin/node/action'));
Dries Buytaert
committed
/**
* Menu callback; presents each node type configuration page.
*/
function node_types_configure($type = NULL) {
if (isset($type)) {
// Go to the listing page when we submit this form, system_settings_save() calls drupal_goto().
if ($_POST['op']) {
$_GET['q'] = 'admin/settings/content-types';
$options = 'options_'. $type;
if (empty($node->$options)) {
$node->$options = array();
}
Dries Buytaert
committed
}
system_settings_save();
$group = form_textarea(t('Explanation or submission guidelines'), $type .'_help', variable_get($type .'_help', ''), 60, 5, t('This text will be displayed at the top of the %type submission form. It is useful for helping or instructing your users.', array('%type' => node_get_name($type))));
$group .= form_select(t('Minimum number of words'), 'minimum_'. $type .'_size', variable_get('minimum_'. $type .'_size', 0), drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)), t('The minimum number of words a %type must be to be considered valid. This can be useful to rule out submissions that do not meet the site\'s standards, such as short test posts.', array('%type' => node_get_name($type))));
Dries Buytaert
committed
$output = form_group(t('Submission form'), $group);
$output .= form_group(t('Workflow'), implode('', node_invoke_nodeapi($node, 'settings')));
Dries Buytaert
committed
else {
$header = array(t('Type'), t('Operations'));