node.module 67.5 KB
Newer Older
Dries's avatar
 
Dries committed
1
<?php
2
// $Id$
Dries's avatar
 
Dries committed
3

Dries's avatar
   
Dries committed
4
5
6
7
8
/**
 * @file
 * The core that allows content to be submitted to the site.
 */

9
10
define('NODE_NEW_LIMIT', time() - 30 * 24 * 60 * 60);

Dries's avatar
   
Dries committed
11
12
13
14
/**
 * Implementation of hook_help().
 */
function node_help($section) {
15
  switch ($section) {
Dries's avatar
   
Dries committed
16
    case 'admin/help#node':
Dries's avatar
   
Dries committed
17
      $output = t("
Dries's avatar
   
Dries committed
18
19
20
21
22
23
24
      <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>
25
      <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>
Dries's avatar
   
Dries committed
26
27
      <dt>Authored on</dt><dd>The date the node was written.</dd>
      <dt>Changed</dt><dd>The last time this node was changed.</dd>
28
      <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.
Dries's avatar
   
Dries committed
29
30
      <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>
Dries's avatar
   
Dries committed
31
      <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>
32
      <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>
33
      <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")));
Dries's avatar
   
Dries committed
34

Dries's avatar
   
Dries committed
35
36
37
      foreach (node_list() as $type) {
        $output .= '<h3>'. t('Node type: %module', array('%module' => node_invoke($type, 'node_name'))). '</h3>';
        $output .= implode("\n", module_invoke_all('help', 'node/add#'. $type));
Dries's avatar
   
Dries committed
38
      }
Dries's avatar
   
Dries committed
39

Dries's avatar
   
Dries committed
40
      return $output;
41

Dries's avatar
   
Dries committed
42
    case 'admin/modules#description':
Dries's avatar
   
Dries committed
43
      return t('The core that allows content to be submitted to the site.');
44
45
    case 'admin/node/configure':
    case 'admin/node/configure/settings':
46
      return t('<p>Settings for the core of Drupal. Almost everything is a node so these settings will affect most of the site.</p>');
47
    case 'admin/node':
48
      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')));
49
    case 'admin/node/search':
50
      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>');
Dries's avatar
   
Dries committed
51
  }
Dries's avatar
   
Dries committed
52
53
54
55

  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.');
  }
56
57
58
59

  if (arg(0) == 'node' && arg(1) == 'add' && $type = arg(2)) {
    return variable_get($type .'_help', '');
  }
Dries's avatar
   
Dries committed
60
61
}

Dries's avatar
   
Dries committed
62
63
64
/**
 * Implementation of hook_cron().
 */
65
function node_cron() {
Dries's avatar
   
Dries committed
66
  db_query('DELETE FROM {history} WHERE timestamp < %d', NODE_NEW_LIMIT);
67
68
}

Dries's avatar
   
Dries committed
69
70
71
72
/**
 * Gather a listing of links to nodes.
 *
 * @param $result
73
 *   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.
Dries's avatar
   
Dries committed
74
 * field to be set.
Dries's avatar
   
Dries committed
75
76
77
78
79
80
 * @param $title
 *   A heading for the resulting list.
 *
 * @return
 *   An HTML list suitable as content for a block.
 */
Dries's avatar
   
Dries committed
81
82
function node_title_list($result, $title = NULL) {
  while ($node = db_fetch_object($result)) {
Dries's avatar
   
Dries committed
83
    $items[] = l($node->title, 'node/'. $node->nid, $node->comment_count ? array('title' => format_plural($node->comment_count, '1 comment', '%count comments')) : '');
Dries's avatar
   
Dries committed
84
85
  }

Dries's avatar
   
Dries committed
86
  return theme('node_list', $items, $title);
Dries's avatar
   
Dries committed
87
88
}

Dries's avatar
   
Dries committed
89
90
91
/**
 * Format a listing of links to nodes.
 */
Dries's avatar
   
Dries committed
92
function theme_node_list($items, $title = NULL) {
Dries's avatar
   
Dries committed
93
  return theme('item_list', $items, $title);
Dries's avatar
   
Dries committed
94
95
}

Dries's avatar
   
Dries committed
96
97
98
/**
 * Update the 'last viewed' timestamp of the specified node for current user.
 */
Dries's avatar
   
Dries committed
99
100
101
102
function node_tag_new($nid) {
  global $user;

  if ($user->uid) {
Dries's avatar
   
Dries committed
103
    if (node_last_viewed($nid)) {
Dries's avatar
   
Dries committed
104
      db_query('UPDATE {history} SET timestamp = %d WHERE uid = %d AND nid = %d', time(), $user->uid, $nid);
Dries's avatar
   
Dries committed
105
106
    }
    else {
Dries's avatar
   
Dries committed
107
      @db_query('INSERT INTO {history} (uid, nid, timestamp) VALUES (%d, %d, %d)', $user->uid, $nid, time());
Dries's avatar
   
Dries committed
108
109
110
111
    }
  }
}

Dries's avatar
   
Dries committed
112
113
114
115
/**
 * Retrieves the timestamp at which the current user last viewed the
 * specified node.
 */
Dries's avatar
   
Dries committed
116
117
function node_last_viewed($nid) {
  global $user;
Dries's avatar
   
Dries committed
118
  static $history;
Dries's avatar
   
Dries committed
119

Dries's avatar
   
Dries committed
120
121
122
123
124
  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's avatar
   
Dries committed
125
126
127
}

/**
128
 * Decide on the type of marker to be displayed for a given node.
Dries's avatar
   
Dries committed
129
 *
Dries's avatar
   
Dries committed
130
131
132
133
 * @param $nid
 *   Node ID whose history supplies the "last viewed" timestamp.
 * @param $timestamp
 *   Time which is compared against node's "last viewed" timestamp.
134
135
 * @return
 *   One of the MARK constants.
Dries's avatar
   
Dries committed
136
 */
137
function node_mark($nid, $timestamp) {
Dries's avatar
   
Dries committed
138
139
140
  global $user;
  static $cache;

141
142
143
  if (!$user->uid) {
    return MARK_READ;
  }
Dries's avatar
Dries committed
144
  if (!isset($cache[$nid])) {
145
    $cache[$nid] = node_last_viewed($nid);
Dries's avatar
   
Dries committed
146
  }
147
148
149
150
151
152
153
  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;
Dries's avatar
   
Dries committed
154
155
}

Dries's avatar
   
Dries committed
156
/**
Dries's avatar
   
Dries committed
157
 * Automatically generate a teaser for the given body text.
Dries's avatar
   
Dries committed
158
 */
Dries's avatar
   
Dries committed
159
160
function node_teaser($body) {

Dries's avatar
   
Dries committed
161
  $size = variable_get('teaser_length', 600);
Dries's avatar
   
Dries committed
162

Dries's avatar
   
Dries committed
163
164
165
166
167
  // find where the delimiter is in the body
  $delimiter = strpos($body, '<!--break-->');

  // If the size is zero, and there is no delimiter, we return the entire body.
  if ($size == 0 && $delimiter == 0) {
Dries's avatar
   
Dries committed
168
169
    return $body;
  }
Dries's avatar
   
Dries committed
170

171
172
173
174
175
  // If the body contains PHP code, do not split it up to prevent parse errors.
  if (strpos($body, '<?') != false) {
    return $body;
  }

Dries's avatar
   
Dries committed
176
  // If a valid delimiter has been specified, use it to chop of the teaser.
Dries's avatar
   
Dries committed
177
  if ($delimiter > 0) {
Dries's avatar
   
Dries committed
178
179
180
    return substr($body, 0, $delimiter);
  }

Dries's avatar
   
Dries committed
181
  // If we have a short body, return the entire body.
Dries's avatar
   
Dries committed
182
183
184
185
  if (strlen($body) < $size) {
    return $body;
  }

Dries's avatar
   
Dries committed
186
187
  // 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.
Dries's avatar
   
Dries committed
188
  if ($length = strpos($body, '</p>', $size)) {
Dries's avatar
   
Dries committed
189
    return substr($body, 0, $length + 4);
Dries's avatar
   
Dries committed
190
191
  }

Dries's avatar
   
Dries committed
192
  if ($length = strpos($body, '<br />', $size)) {
Dries's avatar
   
Dries committed
193
    return substr($body, 0, $length);
Dries's avatar
   
Dries committed
194
195
  }

Dries's avatar
   
Dries committed
196
  if ($length = strpos($body, '<br>', $size)) {
Dries's avatar
   
Dries committed
197
198
199
    return substr($body, 0, $length);
  }

200
  if ($length = strpos($body, "\n", $size)) {
Dries's avatar
   
Dries committed
201
    return substr($body, 0, $length);
Dries's avatar
   
Dries committed
202
203
  }

Dries's avatar
   
Dries committed
204
205
  // When even the first paragraph is too long, try to split at the end of
  // the next sentence.
Dries's avatar
   
Dries committed
206
  if ($length = strpos($body, '. ', $size)) {
Dries's avatar
   
Dries committed
207
208
209
    return substr($body, 0, $length + 1);
  }

Dries's avatar
   
Dries committed
210
  if ($length = strpos($body, '! ', $size)) {
Dries's avatar
   
Dries committed
211
212
213
    return substr($body, 0, $length + 1);
  }

Dries's avatar
   
Dries committed
214
  if ($length = strpos($body, '? ', $size)) {
Dries's avatar
   
Dries committed
215
216
217
    return substr($body, 0, $length + 1);
  }

218
219
220
221
222
223
224
225
226
227
228
229
  if ($length = strpos($body, '。', $size)) {
    return substr($body, 0, $length + 1);
  }

  if ($length = strpos($body, '、', $size)) {
    return substr($body, 0, $length + 1);
  }

  if ($length = strpos($body, '؟ ', $size)) {
    return substr($body, 0, $length + 1);
  }

Dries's avatar
   
Dries committed
230
  // If all else fails, simply truncate the string.
231
  return truncate_utf8($body, $size);
Dries's avatar
   
Dries committed
232
233
}

Dries's avatar
   
Dries committed
234

235
/**
Dries's avatar
   
Dries committed
236
 * Determine the module that defines the node type of the given node.
Dries's avatar
   
Dries committed
237
238
 *
 * @param &$node
Dries's avatar
   
Dries committed
239
 *   Either a node object, a node array, or a string containing the node type.
Dries's avatar
   
Dries committed
240
241
242
243
 * @return
 *   A string containing the name of the defining module.
 */
function node_get_module_name($node) {
Dries's avatar
   
Dries committed
244
  if (is_array($node)) {
Dries's avatar
   
Dries committed
245
    if ($pos = strpos($node['type'], '-')) {
Dries's avatar
   
Dries committed
246
      return substr($node['type'], 0, $pos);
247
248
    }
    else {
Dries's avatar
   
Dries committed
249
      return $node['type'];
Dries's avatar
   
Dries committed
250
    }
Dries's avatar
   
Dries committed
251
252
  }
  else if (is_object($node)) {
Dries's avatar
   
Dries committed
253
    if ($pos = strpos($node->type, '-')) {
Dries's avatar
   
Dries committed
254
      return substr($node->type, 0, $pos);
255
256
    }
    else {
Dries's avatar
   
Dries committed
257
258
      return $node->type;
    }
Dries's avatar
   
Dries committed
259
260
  }
  else if (is_string($node)) {
Dries's avatar
   
Dries committed
261
    if ($pos = strpos($node, '-')) {
Dries's avatar
   
Dries committed
262
      return substr($node, 0, $pos);
263
264
    }
    else {
Dries's avatar
   
Dries committed
265
266
      return $node;
    }
Dries's avatar
   
Dries committed
267
  }
Dries's avatar
   
Dries committed
268
}
Dries's avatar
   
Dries committed
269

270
/**
Dries's avatar
   
Dries committed
271
272
273
 * Get a list of all the defined node types.
 *
 * @return
Dries's avatar
   
Dries committed
274
 *   A list of all node types.
Dries's avatar
   
Dries committed
275
276
277
278
 */
function node_list() {
  $types = array();
  foreach (module_list() as $module) {
Dries's avatar
   
Dries committed
279
280
    if (module_hook($module, 'node_name')) {
      $module_types = module_invoke($module, 'node_types');
Dries's avatar
   
Dries committed
281
282
      if ($module_types) {
        foreach ($module_types as $type) {
Dries's avatar
   
Dries committed
283
          $types[] = $type;
Dries's avatar
   
Dries committed
284
        }
285
286
      }
      else {
Dries's avatar
   
Dries committed
287
        $types[] = $module;
Dries's avatar
   
Dries committed
288
289
290
291
      }
    }
  }
  return $types;
Dries's avatar
   
Dries committed
292
}
Dries's avatar
   
Dries committed
293

294
/**
Dries's avatar
   
Dries committed
295
296
297
298
299
300
301
302
303
304
 * 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) {
305
  $function = node_get_module_name($node) ."_$hook";
Dries's avatar
   
Dries committed
306
307
308
309

  return function_exists($function);
}

310
/**
Dries's avatar
   
Dries committed
311
312
313
314
315
316
317
318
319
 * 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
Dries's avatar
   
Dries committed
320
 *   The returned value of the invoked hook.
Dries's avatar
   
Dries committed
321
322
 */
function node_invoke(&$node, $hook, $a2 = NULL, $a3 = NULL, $a4 = NULL) {
323
  $function = node_get_module_name($node) ."_$hook";
Dries's avatar
   
Dries committed
324
325

  if (function_exists($function)) {
Dries's avatar
   
Dries committed
326
    return ($function($node, $a2, $a3, $a4));
Dries's avatar
   
Dries committed
327
328
329
  }
}

Dries's avatar
   
Dries committed
330
331
332
333
334
335
336
337
338
339
340
341
/**
 * Invoke a hook_nodeapi() operation in all modules.
 *
 * @param &$node
 *   Either a node object, node array, or a string containing the node type.
 * @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.
 */
Dries's avatar
   
Dries committed
342
function node_invoke_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
Dries's avatar
   
Dries committed
343
344
  $return = array();
  foreach (module_list() as $name) {
Dries's avatar
   
Dries committed
345
    $function = $name .'_nodeapi';
Dries's avatar
   
Dries committed
346
    if (function_exists($function)) {
Dries's avatar
   
Dries committed
347
      $result = $function($node, $op, $a3, $a4);
348
      if (is_array($result)) {
Dries's avatar
   
Dries committed
349
350
        $return = array_merge($return, $result);
      }
Steven Wittens's avatar
Steven Wittens committed
351
      else if (isset($result)) {
352
353
        $return[] = $result;
      }
Dries's avatar
   
Dries committed
354
355
356
357
358
    }
  }
  return $return;
}

Dries's avatar
   
Dries committed
359
360
361
362
363
364
365
366
/**
 * Load a node object from the database.
 *
 * @param $conditions
 *   An array of conditions to match against in the database query. Most calls
 *   will simply use array('nid' => 52).
 * @param $revision
 *   Which numbered revision to load. Defaults to the current version.
Dries's avatar
   
Dries committed
367
368
 * @param $reset
 *   Whether to reset the internal node_load cache.
Dries's avatar
   
Dries committed
369
370
371
372
 *
 * @return
 *   A fully-populated node object.
 */
Dries's avatar
   
Dries committed
373
374
375
376
377
378
379
380
381
382
383
384
385
function node_load($conditions, $revision = NULL, $reset = NULL) {
  static $nodes = array();

  if ($reset) {
    $nodes = array();
  }

  $cachable = (count($conditions) == 1 && isset($conditions['nid']) && $revision == NULL);

  if ($cachable && isset($nodes[$conditions['nid']])) {
    return $nodes[$conditions['nid']];
  }

Dries's avatar
   
Dries committed
386
  // Turn the conditions into a query.
Dries's avatar
   
Dries committed
387
  foreach ($conditions as $key => $value) {
Dries's avatar
   
Dries committed
388
    $cond[] = 'n.'. db_escape_string($key) ." = '". db_escape_string($value) ."'";
Dries's avatar
   
Dries committed
389
390
  }

Dries's avatar
   
Dries committed
391
  // Retrieve the node.
Dries's avatar
   
Dries committed
392
  $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)));
Dries's avatar
   
Dries committed
393
  $node = drupal_unpack($node);
Dries's avatar
   
Dries committed
394

Dries's avatar
   
Dries committed
395
  // Unserialize the revisions and user data fields.
Dries's avatar
   
Dries committed
396
397
398
399
  if ($node->revisions) {
    $node->revisions = unserialize($node->revisions);
  }

Dries's avatar
   
Dries committed
400
401
  // Call the node specific callback (if any) and piggy-back the
  // results to the node or overwrite some values.
Dries's avatar
   
Dries committed
402
  if ($extra = node_invoke($node, 'load')) {
Dries's avatar
   
Dries committed
403
404
405
406
407
    foreach ($extra as $key => $value) {
      $node->$key = $value;
    }
  }

Dries's avatar
   
Dries committed
408
409
410
411
412
413
  if ($extra = node_invoke_nodeapi($node, 'load')) {
    foreach ($extra as $key => $value) {
      $node->$key = $value;
    }
  }

Dries's avatar
   
Dries committed
414
  // Return the desired revision.
Dries's avatar
   
Dries committed
415
  if (!is_null($revision) && is_array($node->revisions[$revision])) {
416
   $node = $node->revisions[$revision]['node'];
Dries's avatar
   
Dries committed
417
418
  }

Dries's avatar
   
Dries committed
419
420
421
422
  if ($cachable) {
    $nodes[$conditions['nid']] = $node;
  }

Dries's avatar
   
Dries committed
423
424
425
  return $node;
}

Dries's avatar
   
Dries committed
426
427
428
/**
 * Save a node object into the database.
 */
429
function node_save($node) {
Dries's avatar
   
Dries committed
430
  // Fetch fields to save to node table:
Dries's avatar
   
Dries committed
431
  $fields = node_invoke_nodeapi($node, 'fields');
Dries's avatar
   
Dries committed
432

Dries's avatar
   
Dries committed
433
  // Serialize the revisions field:
Dries's avatar
   
Dries committed
434
435
436
437
  if ($node->revisions) {
    $node->revisions = serialize($node->revisions);
  }

Dries's avatar
   
Dries committed
438
  // Apply filters to some default node fields:
Dries's avatar
   
Dries committed
439
  if (empty($node->nid)) {
Dries's avatar
   
Dries committed
440
    // Insert a new node.
Dries's avatar
   
Dries committed
441

Dries's avatar
   
Dries committed
442
    // Set some required fields:
443
444
445
    if (!$node->created) {
      $node->created = time();
    }
446
447
448
    if (!$node->changed) {
      $node->changed = time();
    }
Dries's avatar
   
Dries committed
449
    $node->nid = db_next_id('{node}_nid');
Dries's avatar
   
Dries committed
450

Dries's avatar
   
Dries committed
451
    // Prepare the query:
Dries's avatar
   
Dries committed
452
    foreach ($node as $key => $value) {
453
      if (in_array((string) $key, $fields)) {
Dries's avatar
   
Dries committed
454
        $k[] = db_escape_string($key);
Dries's avatar
   
Dries committed
455
456
        $v[] = $value;
        $s[] = "'%s'";
Dries's avatar
   
Dries committed
457
458
459
      }
    }

Dries's avatar
   
Dries committed
460
    // Insert the node into the database:
Dries's avatar
   
Dries committed
461
    db_query("INSERT INTO {node} (". implode(", ", $k) .") VALUES(". implode(", ", $s) .")", $v);
Dries's avatar
   
Dries committed
462

Dries's avatar
   
Dries committed
463
    // Call the node specific callback (if any):
Dries's avatar
   
Dries committed
464
465
    node_invoke($node, 'insert');
    node_invoke_nodeapi($node, 'insert');
Dries's avatar
   
Dries committed
466
467
  }
  else {
Dries's avatar
   
Dries committed
468
    // Update an existing node.
Dries's avatar
   
Dries committed
469

Dries's avatar
   
Dries committed
470
    // Set some required fields:
Dries's avatar
   
Dries committed
471
472
    $node->changed = time();

Dries's avatar
   
Dries committed
473
    // Prepare the query:
Dries's avatar
   
Dries committed
474
475
    foreach ($node as $key => $value) {
      if (in_array($key, $fields)) {
Dries's avatar
   
Dries committed
476
        $q[] = db_escape_string($key) ." = '%s'";
Dries's avatar
   
Dries committed
477
        $v[] = $value;
Dries's avatar
   
Dries committed
478
479
      }
    }
Dries's avatar
   
Dries committed
480

Dries's avatar
   
Dries committed
481
    // Update the node in the database:
Dries's avatar
   
Dries committed
482
    db_query("UPDATE {node} SET ". implode(', ', $q) ." WHERE nid = '$node->nid'", $v);
Dries's avatar
   
Dries committed
483

Dries's avatar
   
Dries committed
484
    // Call the node specific callback (if any):
Dries's avatar
   
Dries committed
485
486
    node_invoke($node, 'update');
    node_invoke_nodeapi($node, 'update');
Dries's avatar
   
Dries committed
487
488
  }

Dries's avatar
   
Dries committed
489
  // Clear the cache so an anonymous poster can see the node being added or updated.
Dries's avatar
   
Dries committed
490
491
  cache_clear_all();

Dries's avatar
   
Dries committed
492
  // Return the node ID:
Dries's avatar
   
Dries committed
493
494
495
  return $node->nid;
}

Dries's avatar
   
Dries committed
496
497
498
499
500
501
502
503
504
/**
 * Generate a display of the given node.
 *
 * @param $node
 *   A node array or node object.
 * @param $teaser
 *   Whether to display only the teaser for the node.
 * @param $page
 *   Whether the node is being displayed by itself as a page.
505
506
 * @param $links
 *   Whether or not to display node links. Links are omitted for node previews.
Dries's avatar
   
Dries committed
507
508
509
510
 *
 * @return
 *   An HTML representation of the themed node.
 */
511
function node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE) {
Dries's avatar
   
Dries committed
512
  $node = array2object($node);
Dries's avatar
   
Dries committed
513

514
  // Remove the delimiter (if any) that separates the teaser from the body.
Dries's avatar
   
Dries committed
515
  // TODO: this strips legitimate uses of '<!--break-->' also.
Dries's avatar
   
Dries committed
516
  $node->body = str_replace('<!--break-->', '', $node->body);
Dries's avatar
   
Dries committed
517

Dries's avatar
   
Dries committed
518
519
  // The 'view' hook can be implemented to overwrite the default function
  // to display nodes.
Dries's avatar
   
Dries committed
520
  if (node_hook($node, 'view')) {
Dries's avatar
   
Dries committed
521
    node_invoke($node, 'view', $teaser, $page);
Dries's avatar
   
Dries committed
522
523
  }
  else {
Dries's avatar
   
Dries committed
524
    $node = node_prepare($node, $teaser);
Dries's avatar
   
Dries committed
525
  }
Dries's avatar
   
Dries committed
526
527
  // Allow modules to change $node->body before viewing.
  node_invoke_nodeapi($node, 'view', $teaser, $page);
528
529
530
  if ($links) {
    $node->links = module_invoke_all('link', 'node', $node, !$page);
  }
Dries's avatar
   
Dries committed
531
532

  return theme('node', $node, $teaser, $page);
Dries's avatar
   
Dries committed
533
}
Dries's avatar
   
Dries committed
534

Dries's avatar
   
Dries committed
535
536
537
/**
 * Apply filters to a node in preparation for theming.
 */
Dries's avatar
   
Dries committed
538
function node_prepare($node, $teaser = FALSE) {
Dries's avatar
   
Dries committed
539
  $node->readmore = (strlen($node->teaser) < strlen($node->body));
Dries's avatar
   
Dries committed
540
  if ($teaser == FALSE) {
541
    $node->body = check_output($node->body, $node->format);
Dries's avatar
   
Dries committed
542
543
  }
  else {
544
    $node->teaser = check_output($node->teaser, $node->format);
Dries's avatar
   
Dries committed
545
  }
Dries's avatar
   
Dries committed
546
  return $node;
Dries's avatar
   
Dries committed
547
548
}

Dries's avatar
   
Dries committed
549
550
551
/**
 * Generate a page displaying a single node, along with its comments.
 */
Dries's avatar
   
Dries committed
552
function node_show($node, $cid) {
Dries's avatar
   
Dries committed
553
  $output = node_view($node, FALSE, TRUE);
Dries's avatar
   
Dries committed
554

Dries's avatar
   
Dries committed
555
556
  if (function_exists('comment_render') && $node->comment) {
    $output .= comment_render($node, $cid);
Dries's avatar
   
Dries committed
557
558
  }

Dries's avatar
   
Dries committed
559
560
  // Update the history table, stating that this user viewed this node.
  node_tag_new($node->nid);
Dries's avatar
   
Dries committed
561

Dries's avatar
   
Dries committed
562
  return $output;
Dries's avatar
   
Dries committed
563
564
}

Dries's avatar
   
Dries committed
565
566
567
/**
 * Implementation of hook_perm().
 */
Dries's avatar
   
Dries committed
568
function node_perm() {
Dries's avatar
   
Dries committed
569
  return array('administer nodes', 'access content');
Dries's avatar
   
Dries committed
570
571
}

Dries's avatar
   
Dries committed
572
573
574
/**
 * Implementation of hook_search().
 */
575
576
577
578
function node_search($op = 'search', $keys = null) {
  switch ($op) {
    case 'name':
      return t('content');
Dries's avatar
Dries committed
579
580
581
    case 'reset':
      variable_del('node_cron_last');
      return;
582
583
584
    case 'status':
      $last = variable_get('node_cron_last', 0);
      $total = db_result(db_query('SELECT COUNT(*) FROM {node} WHERE status = 1 AND moderate = 0'));
Steven Wittens's avatar
Steven Wittens committed
585
      $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));
586
      return array('remaining' => $remaining, 'total' => $total);
587
    case 'search':
588
      list($join, $where) = _db_rewrite_sql();
589
      $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));
590
591
592
      $results = array();
      foreach ($find as $item) {
        $node = node_load(array('nid' => $item));
Dries's avatar
Dries committed
593
        $extra = node_invoke_nodeapi($node, 'search result');
594
595
596
597
598
        $results[] = array('link' => url('node/'. $item),
                           'type' => node_invoke($node, 'node_name'),
                           'title' => $node->title,
                           'user' => format_name($node),
                           'date' => $node->changed,
Dries's avatar
Dries committed
599
                           'extra' => $extra,
600
601
602
603
                           'snippet' => search_excerpt($keys, check_output($node->body, $node->format)));
      }
      return $results;
  }
Dries's avatar
   
Dries committed
604
605
}

Dries's avatar
   
Dries committed
606
/**
Dries's avatar
   
Dries committed
607
 * Menu callback; presents general node configuration options.
Dries's avatar
   
Dries committed
608
609
610
611
612
613
 */
function node_configure() {
  if ($_POST) {
    system_settings_save();
  }

Dries's avatar
   
Dries committed
614
  $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.'));
615
  $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."));
Dries's avatar
   
Dries committed
616
  $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?'));
Dries's avatar
   
Dries committed
617

Dries's avatar
   
Dries committed
618
  print theme('page', system_settings_form($output));
Dries's avatar
   
Dries committed
619
620
}

Dries's avatar
   
Dries committed
621
622
623
/**
 * Retrieve the comment mode for the given node ID (none, read, or read/write).
 */
Dries's avatar
   
Dries committed
624
function node_comment_mode($nid) {
Dries's avatar
   
Dries committed
625
626
  static $comment_mode;
  if (!isset($comment_mode[$nid])) {
Dries's avatar
   
Dries committed
627
    $comment_mode[$nid] = db_result(db_query('SELECT comment FROM {node} WHERE nid = %d', $nid));
Dries's avatar
   
Dries committed
628
629
  }
  return $comment_mode[$nid];
Dries's avatar
   
Dries committed
630
631
}

Dries's avatar
   
Dries committed
632
633
634
/**
 * Implementation of hook_link().
 */
635
function node_link($type, $node = 0, $main = 0) {
Dries's avatar
   
Dries committed
636
637
  $links = array();

Dries's avatar
   
Dries committed
638
  if ($type == 'node') {
Dries's avatar
   
Dries committed
639
    if (array_key_exists('links', $node)) {
Kjartan's avatar
Kjartan committed
640
641
      $links = $node->links;
    }
Dries's avatar
   
Dries committed
642

Dries's avatar
   
Dries committed
643
    if ($main == 1 && $node->teaser && $node->readmore) {
Dries's avatar
   
Dries committed
644
      $links[] = l(t('read more'), "node/$node->nid", array('title' => t('Read the rest of this posting.'), 'class' => 'read-more'));
Dries's avatar
   
Dries committed
645
    }
Dries's avatar
   
Dries committed
646
647
  }

Dries's avatar
   
Dries committed
648
  return $links;
Dries's avatar
   
Dries committed
649
650
}

Dries's avatar
   
Dries committed
651
652
653
/**
 * Implementation of hook_menu().
 */
Dries's avatar
   
Dries committed
654
function node_menu($may_cache) {
Dries's avatar
   
Dries committed
655
656
  $items = array();

Dries's avatar
   
Dries committed
657
658
  if ($may_cache) {
    $items[] = array('path' => 'admin/node', 'title' => t('content'),
Dries's avatar
   
Dries committed
659
      'callback' => 'node_admin',
Dries's avatar
   
Dries committed
660
      'access' => user_access('administer nodes'));
661
662
    $items[] = array('path' => 'admin/node/action', 'title' => t('content'),
      'type' => MENU_CALLBACK);
Dries's avatar
   
Dries committed
663
664
665
666
    $items[] = array('path' => 'admin/node/overview', 'title' => t('list'),
      'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
    $items[] = array('path' => 'admin/node/configure', 'title' => t('configure'),
      'callback' => 'node_configure',
Dries's avatar
   
Dries committed
667
668
      'access' => user_access('administer nodes'),
      'type' => MENU_LOCAL_TASK);
Dries's avatar
   
Dries committed
669
670
    $items[] = array('path' => 'admin/node/configure/settings', 'title' => t('settings'),
      'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
671
    $items[] = array('path' => 'admin/node/configure/types', 'title' => t('content types'),
672
      'callback' => 'node_types_configure',
Dries's avatar
   
Dries committed
673
674
675
676
677
678
679
680
      'access' => user_access('administer nodes'),
      'type' => MENU_LOCAL_TASK);
    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);
    }
Dries's avatar
   
Dries committed
681

Dries's avatar
   
Dries committed
682
    $items[] = array('path' => 'node', 'title' => t('content'),
Dries's avatar
   
Dries committed
683
      'callback' => 'node_page',
Dries's avatar
   
Dries committed
684
685
686
      'access' => user_access('access content'),
      'type' => MENU_SUGGESTED_ITEM);
    $items[] = array('path' => 'node/add', 'title' => t('create content'),
Dries's avatar
   
Dries committed
687
      'callback' => 'node_page',
Dries's avatar
   
Dries committed
688
689
690
691
692
693
694
      'access' => user_access('access content'),
      'type' => MENU_ITEM_GROUPING,
      'weight' => 1);
  }
  else {
    if (arg(0) == 'node' && is_numeric(arg(1))) {
      $node = node_load(array('nid' => arg(1)));
695
696
      if ($node->nid) {
        $items[] = array('path' => 'node/'. arg(1), 'title' => t('view'),
Dries's avatar
   
Dries committed
697
          'callback' => 'node_page',
698
699
700
701
702
703
704
705
          '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,
Dries's avatar
   
Dries committed
706
          'type' => MENU_LOCAL_TASK);
707
708
709
710
711
        $items[] = array('path' => 'node/'. arg(1) .'/delete', 'title' => t('delete'),
          'callback' => 'node_page',
          'access' => node_access('delete', $node),
          'weight' => 1,
          'type' => MENU_CALLBACK);
712
713
714
715
716
717
718
719

        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);
        }
Dries's avatar
   
Dries committed
720
      }
721
    }
722
723
724
725
726
    else if (arg(0) == 'admin' && arg(1) == 'node' && arg(2) == 'configure' && arg(3) == 'types' && is_string(arg(4))) {
      $items[] = array('path' => 'admin/node/configure/types/'. arg(4),
        'title' => t("'%name' content type", array('%name' => node_invoke(arg(4), 'node_name'))),
        'type' => MENU_CALLBACK);
    }
Dries's avatar
   
Dries committed
727
728
729
730
731
  }

  return $items;
}

732
733
734
735
736
function node_last_changed($nid) {
  $node = db_fetch_object(db_query('SELECT changed FROM {node} WHERE nid = %d', $nid));
  return ($node->changed);
}

Dries's avatar
   
Dries committed
737
/**
Dries's avatar
   
Dries committed
738
 * Generate the content administration overview.
Dries's avatar
   
Dries committed
739
 */
Dries's avatar
   
Dries committed
740
function node_admin_nodes() {
741
742
743
  /*
  ** Operations
  */
Dries's avatar
   
Dries committed
744
  $operations = array(
745
746
747
748
749
750
    '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'), '')
Dries's avatar
   
Dries committed
751
  );
Dries's avatar
   
Dries committed
752

753
  // Handle operations
754
755
  $op = $_POST['op'];

756
  $edit = $_POST['edit'];
757
  if (($op == t('Update') || $op == t('Delete all')) && isset($edit['operation']) && isset($edit['nodes'])) {
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
    $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 {
782
          $extra = '<ul>';
783
784
785
          foreach ($edit['nodes'] as $nid => $value) {
            if ($value) {
              $title = db_result(db_query('SELECT title FROM {node} WHERE nid = %d', $nid));
786
              $extra .= '<li>'. form_hidden('nodes]['. $nid, 1) . $title .'</li>';
787
788
            }
          }
789
790
791
792
793
794
795
796
797
798
          $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);
799
800
          return $output;
        }
Dries's avatar
   
Dries committed
801
802
      }
    }
803
  }
Dries's avatar
   
Dries committed
804

805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
  /*
  ** Filters
  */
  $node_types = drupal_map_assoc(node_list());
  foreach ($node_types as $k => $v) {
    $node_types[$k] = node_invoke($v, 'node_name');
  }
  // Merge all vocabularies into one for retrieving $value below
  $taxonomy = taxonomy_form_all();
  $terms = array();
  foreach ($taxonomy as $key => $value) {
    $terms = $terms + $value;
  }
  // Regular filters
  $filters = array(
820
    'status'   => array('title' => t('status'),
821
822
823
824
825
826
827
828
829
830
                        '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_types),
    'category' => array('title' => t('category'),          'where' => 'tn.tid = %d',
                        'options' => $terms, 'join' => 'INNER JOIN {term_node} tn ON n.nid = tn.nid'));

  // Initialize/reset filters
831
  if (!isset($_SESSION['node_overview_filter']) || !is_array($_SESSION['node_overview_filter']) || $op == t('Reset')) {
832
833
834
835
836
837
838
839
840
841
842
843
844
845
    $_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');
Dries's avatar
   
Dries committed
846
847
  }

848
849
850
851
852
853
854
855
856
857
858
859
  /*
  ** 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>';
  }
Dries's avatar
Dries committed
860

861
862
863
  // New filter form
  $filters['category']['options'] = $taxonomy;
  $values = '';
Dries's avatar
   
Dries committed
864
865
  $options = array();
  foreach ($filters as $key => $value) {
866
867
    $options[$key] = $value['title'];
    $b .= form_select('', $key, 1, $filters[$key]['options']);
Steven Wittens's avatar
Steven Wittens committed
868
  }
Dries's avatar
   
Dries committed
869

870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
  $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>';
  $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) : '';
  $result = pager_query('SELECT n.*, u.name, u.uid FROM {node} n '. $join .' INNER JOIN {users} u ON n.uid = u.uid '. $where, 50, 0, NULL, $args);
911
912
913
914

  // Make sure the update controls are disabled if we don't have any rows to select from.
  $disabled = !db_num_rows($result);

Dries's avatar
   
Dries committed
915
916
  $options = array();
  foreach ($operations as $key => $value) {
917
    $options[$key] = $value[0];
Dries's avatar
   
Dries committed
918
  }
919

920
  $form = form_select(NULL, 'operation', 'approve', $options, NULL, ($disabled ? 'disabled="disabled"' : ''));
921
  $form .= form_submit(t('Update'), 'op', ($disabled ? array('disabled' => 'disabled') : array()));
Dries's avatar
   
Dries committed
922

923
924
  $output .= form_group(t('Update options'), "<div class=\"container-inline\">$form</div>");
  $output .= '</div>';
Dries's avatar
   
Dries committed
925

Dries's avatar
   
Dries committed
926
  // Overview table:
Dries's avatar
Dries committed
927
  $header = array(NULL, t('Title'), t('Type'), t('Author'), t('Status'), t('Operations'));
Dries's avatar
   
Dries committed
928

929
  $destination = drupal_get_destination();
Dries's avatar
   
Dries committed
930
  while ($node = db_fetch_object($result)) {
931
932
933
934
935
    $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_invoke($node, 'node_name'),
                    format_name($node),
                    ($node->status ? t('published') : t('not published')),
936
                    l(t('edit'), 'node/'. $node->nid .'/edit', array(), $destination));
Dries's avatar
   
Dries committed
937
  }
Dries's avatar
   
Dries committed
938

Dries's avatar
   
Dries committed
939
  if ($pager = theme('pager', NULL, 50, 0)) {
Dries's avatar
   
Dries committed
940
941
942
943
    $rows[] = array(array('data' => $pager, 'colspan' => '7'));
  }

  if (!$rows) {
944
    $rows[] = array(array('data' => t('No posts available.'), 'colspan' => '6'));
Dries's avatar
   
Dries committed
945
  }
Dries's avatar
   
Dries committed
946

Dries's avatar
   
Dries committed
947
  $output .= theme('table', $header, $rows);
948
  return form($output, 'post', url('admin/node/action'));
Dries's avatar
Dries committed
949
950
}

951
952
953
954
955
956
957
958
959
960
/**
 * 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/node/configure/types';
    }
    system_settings_save();
Kjartan's avatar
Kjartan committed
961

962
963
964
965
    $group = form_textarea(t('Explanation or submission guidelines'), $type .'_help', variable_get($type .'_help', ''), 70, 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_invoke($type, 'node_name'))));
    $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_invoke($type, 'node_name'))));
    $output = form_group(t('Submission form'), $group);
    $output .= form_group(t('Workflow'), implode('', node_invoke_nodeapi($type, 'settings')));
966

967
    print theme('page', system_settings_form($output));
968
  }
969
970
  else {
    $header = array(t('Type'), t('Operations'));
971

972
973
974
975
    $rows = array();
    foreach (node_list() as $type) {
      $rows[] = array(node_invoke($type, 'node_name'), l(t('configure'), 'admin/node/configure/types/'. $type));
    }
976

977
978
    print theme('page', theme('table', $header, $rows));
  }
979
980
}

Dries's avatar
   
Dries committed
981
/**
Dries's avatar
   
Dries committed
982
 * Generate an overview table of older revisions of a node.
Dries's avatar
   
Dries committed
983
 */
Dries's avatar
   
Dries committed
984
function node_revision_overview($nid) {
Dries's avatar
   
Dries committed
985
986
  if (user_access('administer nodes')) {
    $node = node_load(array('nid' => $nid));
Dries's avatar
   
Dries committed
987