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

4 5
define('NODE_NEW_LIMIT', time() - 30 * 24 * 60 * 60);

Dries's avatar
 
Dries committed
6
function node_help($section = 'admin/help#node') {
Dries's avatar
 
Dries committed
7
  global $mod;
Dries's avatar
 
Dries committed
8
  $output = '';
9 10 11

  switch ($section) {

Dries's avatar
 
Dries committed
12
    case 'admin/help#node':
Dries's avatar
 
Dries committed
13 14 15 16 17 18 19 20
      $output .= t("
      <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>
21
      <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
22 23
      <dt>Authored on</dt><dd>The date the node was written.</dd>
      <dt>Changed</dt><dd>The last time this node was changed.</dd>
Dries's avatar
 
Dries committed
24
      <dt>Sticky at top of lists</dt><dd>In listings such as the frontpage or a taxonomy overview the teasers of a slected 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
25 26
      <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
27
      <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>
Dries's avatar
 
Dries committed
28 29 30 31
      <dt>In moderation queue</dt><dd>Drupal has a moderation system. If it is active, a node is in one of three states: approved and published, approved and unpublished, and awaiting approval. If you are moderating a node it should be in the moderation queue.</dd>
      <dt>Votes</dt><dd>If you are moderating a node this counts how many votes the node has gotten. Once a node gets a certain number of vote it will either be approved or dropped.
      <dt>Score</dt><dd>The score of the node is gotten by the votes it is given.</dd>
      <dt>Users</dt><dd>The list of users who have voted on a moderated node.</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
      if ($mod == 'admin') {
Dries's avatar
 
Dries committed
36
        foreach (node_list() as $type) {
Dries's avatar
 
Dries committed
37
          $output .= '<h3>'. t('Node type: %module', array('%module' => node_invoke($type, 'node_name'))). '</h3>';
38
          $output .= implode("\n", module_invoke_all('help', 'node/add#'. $type));
39
        }
Dries's avatar
 
Dries committed
40
      }
41 42
      break;

Dries's avatar
 
Dries committed
43
    case 'admin/modules#description':
Dries's avatar
 
Dries committed
44
      $output = t('The core that allows content to be submitted to the site.');
45
      break;
46 47
    case 'admin/node/configure':
    case 'admin/node/configure/settings':
Dries's avatar
 
Dries committed
48
      $output = t('Settings for the core of Drupal. Almost everything is a node so these settings will affect most of the site.');
49 50
      break;
    case 'admin/node':
51
      $output = t("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>).<br />Clicking a title views the post, while clicking an author's name edits their user information.<br />Other post-related tasks are available from the menu.", array('%comments' => url('admin/comment')));
52 53
      break;
    case 'admin/node/search':
Dries's avatar
 
Dries committed
54
      $output = t("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\".");
55
      break;
56
    case 'admin/node/configure/defaults':
57
      $output = t('This page lets you set the defaults used during creation of nodes for all the different node types.<br /><em>comment:</em> Read/write setting for comments.<br /><em>publish:</em> Is this post publicly viewable, has it been published?<br /><em>promote:</em> Is this post to be promoted to the front page?<br /><em>moderate:</em> Does this post need approval before it can be viewed?<br /><em>sticky:</em> Is this post always visible at the top of lists?<br /><em>revision:</em> Will this post go into the revision system allowing multiple versions to be saved?');
58 59
      break;

Dries's avatar
 
Dries committed
60
  }
Dries's avatar
 
Dries committed
61 62

  return $output;
Dries's avatar
 
Dries committed
63 64
}

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
function node_help_page() {
Dries's avatar
 
Dries committed
70
  print theme('page', node_help());
Dries's avatar
 
Dries committed
71 72 73
}


Dries's avatar
 
Dries committed
74 75 76 77
/*
** Accepts a DB result object which can be used to fetch node objects.
** Returns an HTML list suitable as content for a block.
*/
Dries's avatar
 
Dries committed
78 79
function node_title_list($result, $title = NULL) {
  while ($node = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
80
    $number = module_invoke('comment', 'num_all', $node->nid);
Dries's avatar
 
Dries committed
81
    $items[] = l($node->title, "node/$node->nid", array('title' => format_plural($number, '1 comment', '%count comments')));
Dries's avatar
 
Dries committed
82 83
  }

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

Dries's avatar
 
Dries committed
87
function theme_node_list($items, $title = NULL) {
Dries's avatar
 
Dries committed
88
  return theme('item_list', $items, $title);
Dries's avatar
 
Dries committed
89 90
}

Dries's avatar
 
Dries committed
91 92 93 94 95
// Update the 'last viewed' timestamp of the specified node for current user.
function node_tag_new($nid) {
  global $user;

  if ($user->uid) {
Dries's avatar
 
Dries committed
96
    $result = db_query('SELECT timestamp FROM {history} WHERE uid = %d AND nid = %d', $user->uid, $nid);
Dries's avatar
 
Dries committed
97
    if (db_fetch_object($result)) {
Dries's avatar
 
Dries committed
98
      db_query('UPDATE {history} SET timestamp = %d WHERE uid = %d AND nid = %d', time(), $user->uid, $nid);
Dries's avatar
 
Dries committed
99 100
    }
    else {
Dries's avatar
 
Dries committed
101
      db_query('INSERT INTO {history} (uid, nid, timestamp) VALUES (%d, %d, %d)', $user->uid, $nid, time());
Dries's avatar
 
Dries committed
102 103 104 105 106 107 108 109 110 111 112
    }
  }
}

/*
** Retrieves the timestamp at which the current user last viewed the
** specified node.
*/
function node_last_viewed($nid) {
  global $user;

Dries's avatar
 
Dries committed
113
  $history = db_fetch_object(db_query("SELECT timestamp FROM {history} WHERE uid = '$user->uid' AND nid = %d", $nid));
114
  return ($history->timestamp ? $history->timestamp : NODE_NEW_LIMIT);
Dries's avatar
 
Dries committed
115 116 117
}

/**
Dries's avatar
 
Dries committed
118 119
 * Determines whether the supplied timestamp is newer than the user's last view
 * of a given node
Dries's avatar
 
Dries committed
120
 *
121 122
 * @param $nid node-id whose history supplies the 'last viewed' timestamp
 * @param $timestamp time which is compared against node's 'last viewed'
Dries's avatar
 
Dries committed
123 124
 *   timestamp
 */
Dries's avatar
 
Dries committed
125 126 127 128
function node_is_new($nid, $timestamp) {
  global $user;
  static $cache;

Dries's avatar
Dries committed
129
  if (!isset($cache[$nid])) {
Dries's avatar
 
Dries committed
130
    if ($user->uid) {
Dries's avatar
 
Dries committed
131
      $history = db_fetch_object(db_query('SELECT timestamp FROM {history} WHERE uid = %d AND nid = %d', $user->uid, $nid));
Dries's avatar
 
Dries committed
132 133 134 135 136 137 138
      $cache[$nid] = $history->timestamp ? $history->timestamp : 0;
    }
    else {
      $cache[$nid] = time();
    }
  }

139
  return ($timestamp > $cache[$nid] && $timestamp > NODE_NEW_LIMIT);
Dries's avatar
 
Dries committed
140 141
}

Dries's avatar
 
Dries committed
142 143
function node_teaser($body) {

Dries's avatar
 
Dries committed
144
  $size = variable_get('teaser_length', 600);
Dries's avatar
 
Dries committed
145 146 147 148 149 150 151 152 153

  /*
  ** If the size is zero, teasers are disabled so we
  ** return the entire body.
  */

  if ($size == 0) {
    return $body;
  }
Dries's avatar
 
Dries committed
154 155

  /*
Dries's avatar
 
Dries committed
156
  ** If a valid delimiter has been specified, use it to
Dries's avatar
 
Dries committed
157 158
  ** chop of the teaser.  The delimiter can be outside
  ** the allowed range but no more than a factor two.
Dries's avatar
 
Dries committed
159 160
  */

Dries's avatar
 
Dries committed
161
  $delimiter = strpos($body, '<!--break-->');
Dries's avatar
 
Dries committed
162
  if ($delimiter > 0) {
Dries's avatar
 
Dries committed
163 164 165
    return substr($body, 0, $delimiter);
  }

Dries's avatar
 
Dries committed
166 167 168 169 170 171 172 173
  /*
  ** If we have a short body, return the entire body:
  */

  if (strlen($body) < $size) {
    return $body;
  }

Dries's avatar
 
Dries committed
174 175 176 177
  /*
  ** In some cases no delimiter has been specified (eg.
  ** when posting using the Blogger API) in which case
  ** we try to split at paragraph boundaries.
Dries's avatar
 
Dries committed
178 179
  */

Dries's avatar
 
Dries committed
180
  if ($length = strpos($body, '</p>', $size)) {
Dries's avatar
 
Dries committed
181
    return substr($body, 0, $length + 4);
Dries's avatar
 
Dries committed
182 183
  }

Dries's avatar
 
Dries committed
184
  if ($length = strpos($body, '<br />', $size)) {
Dries's avatar
 
Dries committed
185
    return substr($body, 0, $length);
Dries's avatar
 
Dries committed
186 187
  }

Dries's avatar
 
Dries committed
188
  if ($length = strpos($body, '<br>', $size)) {
Dries's avatar
 
Dries committed
189 190 191
    return substr($body, 0, $length);
  }

192
  if ($length = strpos($body, "\n", $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
  /*
Dries's avatar
 
Dries committed
197
  ** When even the first paragraph is too long, try to
Dries's avatar
 
Dries committed
198
  ** split at the end of the next sentence.
Dries's avatar
 
Dries committed
199 200
  */

Dries's avatar
 
Dries committed
201
  if ($length = strpos($body, '. ', $size)) {
Dries's avatar
 
Dries committed
202 203 204
    return substr($body, 0, $length + 1);
  }

Dries's avatar
 
Dries committed
205
  if ($length = strpos($body, '! ', $size)) {
Dries's avatar
 
Dries committed
206 207 208
    return substr($body, 0, $length + 1);
  }

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

213 214 215 216 217 218 219 220 221 222 223 224
  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
225 226 227
  /*
  ** Nevermind, we split it the hard way ...
  */
Dries's avatar
 
Dries committed
228

229
  return truncate_utf8($body, $size);
Dries's avatar
 
Dries committed
230 231
}

Dries's avatar
 
Dries committed
232

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

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

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

  return function_exists($function);
}

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

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

Dries's avatar
 
Dries committed
328
function node_invoke_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
Dries's avatar
 
Dries committed
329 330
  $return = array();
  foreach (module_list() as $name) {
Dries's avatar
 
Dries committed
331
    $function = $name .'_nodeapi';
Dries's avatar
 
Dries committed
332
    if (function_exists($function)) {
Dries's avatar
 
Dries committed
333
      $result = $function($node, $op, $a3, $a4);
Dries's avatar
 
Dries committed
334 335 336 337 338 339 340 341
      if (isset($result)) {
        $return = array_merge($return, $result);
      }
    }
  }
  return $return;
}

Dries's avatar
 
Dries committed
342
function node_load($conditions, $revision = -1) {
Dries's avatar
 
Dries committed
343 344 345 346 347 348

  /*
  ** Turn the conditions into a query:
  */

  foreach ($conditions as $key => $value) {
Dries's avatar
 
Dries committed
349
    $cond[] = 'n.'. check_query($key) ." = '". check_query($value) ."'";
Dries's avatar
 
Dries committed
350 351 352 353 354 355
  }

  /*
  ** Retrieve the node:
  */

Dries's avatar
 
Dries committed
356
  $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
357
  $node = drupal_unpack($node);
Dries's avatar
 
Dries committed
358 359

  /*
Dries's avatar
 
Dries committed
360
  ** Unserialize the revisions and user data fields:
Dries's avatar
 
Dries committed
361 362 363 364 365 366 367 368 369 370 371
  */

  if ($node->revisions) {
    $node->revisions = unserialize($node->revisions);
  }

  /*
  ** Call the node specific callback (if any) and piggy-back the
  ** results to the node or overwrite some values:
  */

Dries's avatar
 
Dries committed
372
  if ($extra = node_invoke($node, 'load')) {
Dries's avatar
 
Dries committed
373 374 375 376 377
    foreach ($extra as $key => $value) {
      $node->$key = $value;
    }
  }

Dries's avatar
 
Dries committed
378 379 380 381 382 383
  if ($extra = node_invoke_nodeapi($node, 'load')) {
    foreach ($extra as $key => $value) {
      $node->$key = $value;
    }
  }

Dries's avatar
 
Dries committed
384 385 386 387
  /*
  ** Return the desired revision
  */
  if ($revision != -1 && isset($node->revisions[$revision])) {
388
   $node = $node->revisions[$revision]['node'];
Dries's avatar
 
Dries committed
389 390
  }

Dries's avatar
 
Dries committed
391 392 393
  return $node;
}

394
function node_save($node) {
Dries's avatar
 
Dries committed
395

396 397 398
  /*
  ** Fetch fields to save to node table:
  */
Dries's avatar
 
Dries committed
399
  $fields = node_invoke_nodeapi($node, 'fields');
Dries's avatar
 
Dries committed
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

  /*
  ** Serialize the revisions field:
  */

  if ($node->revisions) {
    $node->revisions = serialize($node->revisions);
  }

  /*
  ** Apply filters to some default node fields:
  */

  if (empty($node->nid)) {

    /*
    ** Insert a new node:
    */

Dries's avatar
 
Dries committed
419
    // Set some required fields:
420 421 422
    if (!$node->created) {
      $node->created = time();
    }
423 424 425
    if (!$node->changed) {
      $node->changed = time();
    }
Dries's avatar
 
Dries committed
426
    $node->nid = db_next_id('{node}_nid');
Dries's avatar
 
Dries committed
427

Dries's avatar
 
Dries committed
428
    // Prepare the query:
Dries's avatar
 
Dries committed
429 430 431
    foreach ($node as $key => $value) {
      if (in_array($key, $fields)) {
        $k[] = check_query($key);
Dries's avatar
 
Dries committed
432 433
        $v[] = $value;
        $s[] = "'%s'";
Dries's avatar
 
Dries committed
434 435 436
      }
    }

Dries's avatar
 
Dries committed
437
    $keysfmt = implode(', ', $s);
Dries's avatar
 
Dries committed
438 439 440
    // need to quote the placeholders for the values
    $valsfmt = "'". implode("', '", $s) ."'";

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

Dries's avatar
 
Dries committed
444
    // Call the node specific callback (if any):
Dries's avatar
 
Dries committed
445 446
    node_invoke($node, 'insert');
    node_invoke_nodeapi($node, 'insert');
Dries's avatar
 
Dries committed
447 448 449 450 451 452 453
  }
  else {

    /*
    ** Update an existing node:
    */

Dries's avatar
 
Dries committed
454
    // Set some required fields:
Dries's avatar
 
Dries committed
455 456
    $node->changed = time();

Dries's avatar
 
Dries committed
457
    // Prepare the query:
Dries's avatar
 
Dries committed
458 459
    foreach ($node as $key => $value) {
      if (in_array($key, $fields)) {
Dries's avatar
 
Dries committed
460 461
        $q[] = check_query($key) ." = '%s'";
        $v[] = $value;
Dries's avatar
 
Dries committed
462 463 464
      }
    }

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

Dries's avatar
 
Dries committed
468
    // Call the node specific callback (if any):
Dries's avatar
 
Dries committed
469 470
    node_invoke($node, 'update');
    node_invoke_nodeapi($node, 'update');
Dries's avatar
 
Dries committed
471 472
  }

Dries's avatar
 
Dries committed
473
  /*
Dries's avatar
 
Dries committed
474 475
  ** Clear the cache so an anonymous poster can see the node being
  ** added or updated.
Dries's avatar
 
Dries committed
476 477 478 479
  */

  cache_clear_all();

Dries's avatar
 
Dries committed
480 481 482 483 484 485 486 487
  /*
  ** Return the node ID:
  */

  return $node->nid;

}

Dries's avatar
 
Dries committed
488
function node_view($node, $teaser = FALSE, $page = FALSE) {
Dries's avatar
 
Dries committed
489

Dries's avatar
 
Dries committed
490
  $node = array2object($node);
Dries's avatar
 
Dries committed
491

Dries's avatar
 
Dries committed
492 493
  /*
  ** Remove the delimiter (if any) that seperates the teaser from the
Dries's avatar
Dries committed
494
  ** body. TODO: this strips legitimate uses of '<!--break-->' also.
Dries's avatar
 
Dries committed
495 496
  */

Dries's avatar
 
Dries committed
497
  $node->body = str_replace('<!--break-->', '', $node->body);
Dries's avatar
 
Dries committed
498

Dries's avatar
 
Dries committed
499
  // Allow modules to change $node->body before viewing.
Dries's avatar
 
Dries committed
500
  node_invoke_nodeapi($node, 'view', $teaser, $page);
Dries's avatar
 
Dries committed
501

Dries's avatar
 
Dries committed
502
  /*
Dries's avatar
 
Dries committed
503
  ** The 'view' hook can be implemented to overwrite the default function
Dries's avatar
 
Dries committed
504 505 506
  ** to display nodes.
  */

Dries's avatar
 
Dries committed
507
  if (node_hook($node, 'view')) {
Dries's avatar
 
Dries committed
508
    return node_invoke($node, 'view', $teaser, $page);
Dries's avatar
 
Dries committed
509 510 511 512 513 514 515
  }
  else {

    /*
    ** Default behavior:
    */

Dries's avatar
 
Dries committed
516
    return theme('node', node_prepare($node, $teaser), $teaser, $page);
Dries's avatar
 
Dries committed
517 518
  }
}
Dries's avatar
 
Dries committed
519

Dries's avatar
 
Dries committed
520
function node_prepare($node, $teaser = FALSE) {
Dries's avatar
 
Dries committed
521
  $node->readmore = (strlen($node->teaser) < strlen($node->body));
Dries's avatar
 
Dries committed
522
  if ($teaser == FALSE) {
Dries's avatar
 
Dries committed
523
    $node->body = check_output($node->body);
Dries's avatar
 
Dries committed
524 525 526
  }
  else {
    $node->teaser = check_output($node->teaser);
Dries's avatar
 
Dries committed
527
  }
Dries's avatar
 
Dries committed
528
  return $node;
Dries's avatar
 
Dries committed
529 530
}

Dries's avatar
 
Dries committed
531 532
function node_show($node, $cid) {

Dries's avatar
 
Dries committed
533
  if (node_access('view', $node)) {
Dries's avatar
 
Dries committed
534

Dries's avatar
 
Dries committed
535
    $output = node_view($node, 0, 1);
Dries's avatar
 
Dries committed
536

Dries's avatar
 
Dries committed
537
    if (function_exists('comment_render') && $node->comment) {
Dries's avatar
 
Dries committed
538
      $output .= comment_render($node, $cid);
Dries's avatar
 
Dries committed
539
    }
Dries's avatar
 
Dries committed
540 541 542 543 544 545

    /*
    ** Update the history table, stating that this user viewed this node.
    */

    node_tag_new($node->nid);
Dries's avatar
 
Dries committed
546 547

    return $output;
Dries's avatar
 
Dries committed
548
  }
Dries's avatar
 
Dries committed
549 550 551
  else {
    drupal_set_message(message_access());
  }
Dries's avatar
 
Dries committed
552 553
}

Dries's avatar
 
Dries committed
554 555
function node_access($op, $node = 0) {

Dries's avatar
 
Dries committed
556
  if (user_access('administer nodes')) {
Dries's avatar
 
Dries committed
557
    return 1;
Dries's avatar
 
Dries committed
558 559
  }

Dries's avatar
 
Dries committed
560 561 562
  /*
  ** Convert the node to an object if necessary:
  */
Dries's avatar
 
Dries committed
563

Dries's avatar
 
Dries committed
564
  $node = array2object($node);
Dries's avatar
 
Dries committed
565

Dries's avatar
 
Dries committed
566 567
  // Can't use node_invoke:
  // the access hook takes the $op parameter before the $node parameter.
Dries's avatar
 
Dries committed
568
  return module_invoke(node_get_module_name($node), 'access', $op, $node);
Dries's avatar
 
Dries committed
569 570
}

Dries's avatar
 
Dries committed
571
function node_perm() {
Dries's avatar
 
Dries committed
572
  return array('administer nodes', 'access content');
Dries's avatar
 
Dries committed
573 574
}

Dries's avatar
 
Dries committed
575 576
function node_search($keys) {

Kjartan's avatar
Kjartan committed
577 578 579
  // Return the results of performing a search using the indexed search
  // for this particular type of node.
  //
Dries's avatar
 
Dries committed
580
  // Pass an array to the 'do_search' function which dictates what it
Kjartan's avatar
Kjartan committed
581 582 583 584 585 586 587 588
  // will search through, and what it will search for
  //
  // "keys"'s value is the keywords entered by the user
  //
  // "type"'s value is used to identify the node type in the search
  // index.
  //
  // "select"'s value is used to relate the data from the specific nodes
Dries's avatar
 
Dries committed
589
  // table to the data that the search_index table has in it, and the the
Kjartan's avatar
Kjartan committed
590 591
  // do_search functino will rank it.
  //
Dries's avatar
 
Dries committed
592
  // The select must always provide the following fields - lno, title,
Kjartan's avatar
Kjartan committed
593 594
  // created, uid, name, count
  //
Dries's avatar
 
Dries committed
595
  $find = do_search(array('keys' => $keys, 'type' => 'node', 'select' => "select s.lno as lno, n.title as title, n.created as created, u.uid as uid, u.name as name, s.count as count FROM {search_index} s, {node} n INNER JOIN {users} u ON n.uid = u.uid WHERE s.lno = n.nid AND s.type = 'node' AND s.word like '%' AND n.status = 1"));
Dries's avatar
 
Dries committed
596

Dries's avatar
 
Dries committed
597
  return array(t('Matching nodes ranked in order of relevance'), $find);
Dries's avatar
 
Dries committed
598 599
}

Dries's avatar
 
Dries committed
600 601 602 603 604 605 606 607
/**
 * Page callback; presents general node configuration options.
 */
function node_configure() {
  if ($_POST) {
    system_settings_save();
  }

Dries's avatar
 
Dries committed
608
  $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.'));
609
  $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
610
  $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
611

Dries's avatar
 
Dries committed
612
  print theme('page', system_settings_form($output));
Dries's avatar
 
Dries committed
613 614
}

Dries's avatar
 
Dries committed
615
function node_comment_mode($nid) {
Dries's avatar
 
Dries committed
616 617
  static $comment_mode;
  if (!isset($comment_mode[$nid])) {
Dries's avatar
 
Dries committed
618
    $comment_mode[$nid] = db_result(db_query('SELECT comment FROM {node} WHERE nid = %d', $nid));
Dries's avatar
 
Dries committed
619 620
  }
  return $comment_mode[$nid];
Dries's avatar
 
Dries committed
621 622
}

Dries's avatar
 
Dries committed
623 624 625
/**
 * Implementation of hook_link().
 */
626
function node_link($type, $node = 0, $main = 0) {
Dries's avatar
 
Dries committed
627

Dries's avatar
 
Dries committed
628 629
  $links = array();

Dries's avatar
 
Dries committed
630
  if ($type == 'node') {
Kjartan's avatar
Kjartan committed
631 632 633
    if ($node->links) {
      $links = $node->links;
    }
Dries's avatar
 
Dries committed
634

Dries's avatar
 
Dries committed
635
    if ($main == 1 && $node->teaser && $node->readmore) {
Dries's avatar
 
Dries committed
636
      $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
637 638
    }

Dries's avatar
 
Dries committed
639 640
    if (user_access('administer nodes') && $node->revisions) {
      $links[] = l(t('revisions'), "node/revisions/$node->nid", array('title' => t('Administer revisions.')));
Dries's avatar
 
Dries committed
641
    }
Dries's avatar
 
Dries committed
642 643
  }

Dries's avatar
 
Dries committed
644
  return $links;
Dries's avatar
 
Dries committed
645 646
}

Dries's avatar
 
Dries committed
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
/**
 * Implementation of hook_menu().
 */
function node_menu() {
  $items = array();

  $items[] = array('path' => 'admin/node', 'title' => t('content'),
    'callback' => 'node_admin',
    'access' => user_access('administer nodes'));

  // Tabs:
  $items[] = array('path' => 'admin/node/configure', 'title' => t('configure'),
    'callback' => 'node_configure',
    '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);
  }

  // Subtabs:
  $items[] = array('path' => 'admin/node/configure/settings', 'title' => t('settings'),
    'callback' => 'node_configure',
    'access' => user_access('administer nodes'),
    'type' => MENU_LOCAL_SUBTASK);
  $items[] = array('path' => 'admin/node/configure/defaults', 'title' => t('default workflow'),
    'callback' => 'node_default_settings',
    'access' => user_access('administer nodes'),
    'type' => MENU_LOCAL_SUBTASK);

  $items[] = array('path' => 'node', 'title' => t('content'),
    'callback' => 'node_page',
    'access' => user_access('access content'),
    'type' => MENU_SUGGESTED_ITEM);
  $items[] = array('path' => 'node/add', 'title' => t('create content'),
    'callback' => 'node_page',
    'access' => user_access('access content'),
    'type' => MENU_ITEM_GROUPING,
    'weight' => 1);

  if (arg(0) == 'node' && is_numeric(arg(1))) {
    $items[] = array('path' => 'node/'. arg(1), 'title' => t('view'),
      'callback' => 'node_page',
      'access' => user_access('access content'),
      'type' => MENU_CALLBACK);
    $items[] = array('path' => 'node/'. arg(1) .'/edit', 'title' => t('edit'),
      'callback' => 'node_page',
      'access' => user_access('administer nodes'),
      'type' => MENU_LOCAL_TASK);
  }

  // Legacy handler
  $items[] = array('path' => 'node/view', 'title' => t('view'),
    'callback' => 'node_old_url',
    'access' => user_access('access content'),
    'type' => MENU_CALLBACK);

  return $items;
}

Dries's avatar
 
Dries committed
709
function node_admin_edit($node) {
Dries's avatar
 
Dries committed
710

Dries's avatar
 
Dries committed
711
  if (is_numeric($node)) {
Dries's avatar
 
Dries committed
712
    $node = node_load(array('nid' => $node));
Dries's avatar
 
Dries committed
713
  }
Dries's avatar
 
Dries committed
714

Dries's avatar
 
Dries committed
715
  $output .= node_form($node);
Dries's avatar
 
Dries committed
716

Dries's avatar
 
Dries committed
717
  /*
Dries's avatar
 
Dries committed
718
  ** Display the node form extensions:
Dries's avatar
 
Dries committed
719
  */
720
  $output .= implode("\n", module_invoke_all('node_link', $node));
Dries's avatar
Dries committed
721

Dries's avatar
 
Dries committed
722
  return $output;
Dries's avatar
 
Dries committed
723 724 725

}

Dries's avatar
 
Dries committed
726
function node_admin_nodes() {
Dries's avatar
 
Dries committed
727
  $filters = array(
Dries's avatar
 
Dries committed
728 729 730 731
    array(t('View posts that are new or updated'), 'ORDER BY n.changed DESC'),
    array(t('View posts that need approval'), 'WHERE n.status = 0 OR n.moderate = 1 ORDER BY n.changed DESC'),
    array(t('View posts that are promoted'), 'WHERE n.status = 1 AND n.promote = 1 ORDER BY n.changed DESC'),
    array(t('View posts that are not promoted'), 'WHERE n.status = 1 AND n.promote = 0 ORDER BY n.changed DESC'),
Dries's avatar
 
Dries committed
732
    array(t('View posts that are sticky'), 'WHERE n.status = 1 AND n.sticky = 1 ORDER BY n.changed DESC'),
Dries's avatar
 
Dries committed
733
    array(t('View posts that are unpublished'), 'WHERE n.status = 0 AND n.moderate = 0 ORDER BY n.changed DESC')
Dries's avatar
 
Dries committed
734 735 736
   );

  $operations = array(
Dries's avatar
 
Dries committed
737 738
    array(t('Approve the selected posts'), 'UPDATE {node} SET status = 1, moderate = 0 WHERE nid = %d'),
    array(t('Promote the selected posts'), 'UPDATE {node} SET status = 1, promote = 1 WHERE nid = %d'),
Dries's avatar
 
Dries committed
739
    array(t('Make the selected posts sticky'), 'UPDATE {node} SET status = 1, sticky = 1 WHERE nid = %d'),
Dries's avatar
 
Dries committed
740 741
    array(t('Demote the selected posts'), 'UPDATE {node} SET promote = 0 WHERE nid = %d'),
    array(t('Unpublish the selected posts'), 'UPDATE {node} SET status = 0 WHERE nid = %d')
Dries's avatar
 
Dries committed
742
  );
Dries's avatar
 
Dries committed
743

Dries's avatar
 
Dries committed
744 745 746 747
  /*
  ** Handle operations:
  */

Dries's avatar
 
Dries committed
748 749
  if (empty($_SESSION['node_overview_filter'])) {
    $_SESSION['node_overview_filter'] = 0;
Dries's avatar
 
Dries committed
750 751
  }

752 753 754
  $op = $_POST['op'];

  if ($op == t('Filter') && isset($_POST['edit']['filter'])) {
Dries's avatar
 
Dries committed
755
    $_SESSION['node_overview_filter'] = $_POST['edit']['filter'];
Dries's avatar
 
Dries committed
756 757
  }

758
  if ($op == t('Update') && isset($_POST['edit']['operation']) && isset($_POST['edit']['status'])) {
Dries's avatar
 
Dries committed
759 760
    $operation = $operations[$_POST['edit']['operation']][1];
    foreach ($_POST['edit']['status'] as $nid => $value) {
Dries's avatar
 
Dries committed
761
      if ($value) {
Dries's avatar
Dries committed
762
        db_query($operation, $nid);
Dries's avatar
 
Dries committed
763 764 765
      }
    }

Dries's avatar
 
Dries committed
766
    drupal_set_message(t('the update has been performed.'));
Dries's avatar
 
Dries committed
767 768
  }

Dries's avatar
 
Dries committed
769
  $filter = $_SESSION['node_overview_filter'];
Dries's avatar
Dries committed
770

Dries's avatar
 
Dries committed
771 772 773 774 775 776 777 778
  /*
  ** Render filter form:
  */

  $options = array();
  foreach ($filters as $key => $value) {
    $options[] = $value[0];
  }
Dries's avatar
 
Dries committed
779

Dries's avatar
 
Dries committed
780
  $form  = form_select(NULL, 'filter', $filter, $options);
781
  $form .= form_submit(t('Filter'));
Dries's avatar
 
Dries committed
782

Dries's avatar
 
Dries committed
783
  $output .= '<h3>'. t('Filter options') .'</h3>';
Dries's avatar
 
Dries committed
784 785 786 787 788 789
  $output .= "<div class=\"container-inline\">$form</div>";

  /*
  ** Render operations form:
  */

790 791 792 793 794
  $result = pager_query('SELECT n.*, u.name, u.uid FROM {node} n INNER JOIN {users} u ON n.uid = u.uid '. $filters[$filter][1], 50);

  // 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
795 796 797 798 799
  $options = array();
  foreach ($operations as $key => $value) {
    $options[] = $value[0];
  }

800 801
  $form = form_select(NULL, 'operation', 0, $options, NULL, ($disabled ? 'disabled="disabled"' : ''));
  $form .= form_submit(t('Update'), 'op', ($disabled ? array('disabled' => 'disabled') : array()));
Dries's avatar
 
Dries committed
802

Dries's avatar
 
Dries committed
803
  $output .= '<h3>'. t('Update options') .'</h3>';
Dries's avatar
 
Dries committed
804 805 806 807 808 809
  $output .= "<div class=\"container-inline\">$form</div>";

  /*
  ** Overview table:
  */

Dries's avatar
 
Dries committed
810
  $header = array(NULL, t('title'), t('type'), t('author'), t('status'), array('data' => t('operations'), 'colspan' => 2));
Dries's avatar
 
Dries committed
811

Dries's avatar
 
Dries committed
812
  while ($node = db_fetch_object($result)) {
813
    $rows[] = array(form_checkbox(NULL, "status][$node->nid", 1, 0), l($node->title, "node/$node->nid") .' '. (node_is_new($node->nid, $node->changed) ? theme_mark() : ''), node_invoke($node, 'node_name'), format_name($node), ($node->status ? t('published') : t('not published')), l(t('edit %post', array('%post' => t($node->type))), "node/$node->nid/edit"), l(t('delete %post', array('%post' => t($node->type))), "admin/node/delete/$node->nid"));
Dries's avatar
 
Dries committed
814
  }
Dries's avatar
 
Dries committed
815

Dries's avatar
 
Dries committed
816 817
  if ($pager = theme('pager', NULL, 50, 0)) {
    $rows[] = array(array('data' => $pager, 'colspan' => 7));
Dries's avatar
 
Dries committed
818
  }
Dries's avatar
 
Dries committed
819

Dries's avatar
 
Dries committed
820 821
  $output .= '<h3>'. $filters[$filter][0] .'</h3>';
  $output .= theme('table', $header, $rows);
Dries's avatar
 
Dries committed
822
  return form($output);
Dries's avatar
Dries committed
823 824
}

Dries's avatar
 
Dries committed
825
function node_default_settings() {
Dries's avatar
 
Dries committed
826
  $op = $_POST['op'];
Dries's avatar
 
Dries committed
827
  $edit = $_POST['edit'];
Kjartan's avatar
Kjartan committed
828

Dries's avatar
 
Dries committed
829
  if ($op == t('Save configuration')) {
830 831 832 833 834 835 836
    /*
    ** Save the configuration options:
    */

    foreach ($edit as $name => $value) {
      variable_set($name, $value);
    }
Dries's avatar
 
Dries committed
837
    drupal_set_message(t('the content settings have been saved.'));
838 839
  }

Dries's avatar
 
Dries committed
840
  if ($op == t('Reset to defaults')) {
841 842 843 844 845 846 847
    /*
    ** Reset the configuration options to their default value:
    */

    foreach ($edit as $name => $value) {
      variable_del($name);
    }
Dries's avatar
 
Dries committed
848
    drupal_set_message(t('the content settings have been reset to their default values.'));
849 850
  }

Dries's avatar
 
Dries committed
851
  $header = array_merge(array(t('type')), array_keys(node_invoke_nodeapi($node, 'settings')));
Dries's avatar
 
Dries committed
852
  foreach (node_list() as $type) {
Dries's avatar
 
Dries committed
853 854
    $node->type = $type;
    $cols = array();
Dries's avatar
 
Dries committed
855 856
    foreach (node_invoke_nodeapi($node, 'settings') as $setting) {
      $cols[] = array('data' => $setting, 'align' => 'center', 'width' => 55);
857
    }
Dries's avatar
 
Dries committed
858
    $rows[] = array_merge(array(node_invoke($node, 'node_name')), $cols);
859
  }
Kjartan's avatar
Kjartan committed
860

Dries's avatar
 
Dries committed
861
  $output .= theme('table', $header, $rows);
862

Dries's avatar
 
Dries committed
863 864
  $output .= form_submit(t('Save configuration'));
  $output .= form_submit(t('Reset to defaults'));
Kjartan's avatar
Kjartan committed
865

Dries's avatar
 
Dries committed
866
  print theme('page', form($output));
867 868
}

Dries's avatar
 
Dries committed
869 870
function node_revision_overview($nid) {

Dries's avatar
 
Dries committed
871 872
  if (user_access('administer nodes')) {
    $node = node_load(array('nid' => $nid));
Dries's avatar
 
Dries committed
873 874

    if ($node->revisions) {
Dries's avatar
 
Dries committed
875
      $header = array(t('older revisions'), array('colspan' => '3', 'data' => t('operations')));
Dries's avatar
 
Dries committed
876 877

      foreach ($node->revisions as $key => $revision) {