Code coverage for /20081101/modules/node/node.admin.inc

Line #Times calledCode
1
<?php
2
// $Id: node.admin.inc,v 1.27 2008/10/12 04:30:06 webchick Exp $
3
4
/**
5
 * @file
6
 * Content administration and module settings UI.
7
 */
8
9
/**
10
 * Menu callback; presents general node configuration options.
11
 */
12178
function node_configure() {
13
  // Only show rebuild button if there are either 0, or 2 or more, rows
14
  // in the {node_access} table, or if there are modules that
15
  // implement hook_node_grants().
160
  if (db_result(db_query('SELECT COUNT(*) FROM {node_access}')) != 1 ||
count(module_implements('node_grants')) > 0) {
170
    $status = '<p>' . t('If the site is experiencing problems with
permissions to content, you may have to rebuild the permissions cache.
Possible causes for permission problems are disabling modules or
configuration changes to permissions. Rebuilding will remove all privileges
to posts, and replace them with permissions based on the current modules
and settings.') . '</p>';
180
    $status .= '<p>' . t('Rebuilding may take some time if there is a lot
of content or complex permission settings. After rebuilding has completed
posts will automatically use the new permissions.') . '</p>';
19
200
    $form['access'] = array(
210
      '#type' => 'fieldset',
220
      '#title' => t('Node access status'),
23
    );
240
    $form['access']['status'] = array('#markup' => $status);
250
    $form['access']['rebuild'] = array(
260
      '#type' => 'submit',
270
      '#value' => t('Rebuild permissions'),
28
    );
290
  }
30
310
  $form['default_nodes_main'] = array(
320
    '#type' => 'select', '#title' => t('Number of posts on main page'),
'#default_value' => variable_get('default_nodes_main', 10),
330
    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15,
20, 25, 30)),
340
    '#description' => t('The default maximum number of posts to display per
page on overview pages such as the main page.')
350
  );
360
  $form['teaser_length'] = array(
370
    '#type' => 'select', '#title' => t('Length of trimmed posts'),
'#default_value' => variable_get('teaser_length', 600),
380
    '#options' => drupal_map_assoc(array(0, 200, 400, 600, 800, 1000, 1200,
1400, 1600, 1800, 2000), '_node_characters'),
390
    '#description' => 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.")
400
  );
41
420
  $form['node_preview'] = array(
430
    '#type' => 'radios',
440
    '#title' => t('Preview post'),
450
    '#default_value' => variable_get('node_preview', 0),
460
    '#options' => array(t('Optional'), t('Required')),
470
    '#description' => t('Must users preview posts before submitting?'),
48
  );
49
500
  $form['#validate'] = array('node_configure_validate');
51
520
  return system_settings_form($form);
530
}
54
55
/**
56
 * Helper function for teaser length choices.
57
 */
58178
function _node_characters($length) {
590
  return ($length == 0) ? t('Unlimited') : format_plural($length, '1
character', '@count characters');
600
}
61
62
/**
63
 * Form validate callback.
64
 */
65178
function node_configure_validate($form, &$form_state) {
660
  if ($form_state['values']['op'] == t('Rebuild permissions')) {
670
    drupal_goto('admin/content/node-settings/rebuild');
680
  }
690
}
70
71
/**
72
 * Menu callback: confirm rebuilding of permissions.
73
 */
74178
function node_configure_rebuild_confirm() {
750
  return confirm_form(array(), t('Are you sure you want to rebuild the
permissions on site content?'),
760
                  'admin/content/node-settings', t('This action rebuilds
all permissions on site content, and may be a lengthy process. This action
cannot be undone.'), t('Rebuild permissions'), t('Cancel'));
770
}
78
79
/**
80
 * Handler for wipe confirmation
81
 */
82178
function node_configure_rebuild_confirm_submit($form, &$form_state) {
830
  node_access_rebuild(TRUE);
840
  $form_state['redirect'] = 'admin/content/node-settings';
850
  return;
860
}
87
88
/**
89
 * Implementation of hook_node_operations().
90
 */
91178
function node_node_operations() {
92
  $operations = array(
93
    'publish' => array(
940
      'label' => t('Publish'),
950
      'callback' => 'node_mass_update',
960
      'callback arguments' => array('updates' => array('status' => 1)),
970
    ),
98
    'unpublish' => array(
990
      'label' => t('Unpublish'),
1000
      'callback' => 'node_mass_update',
1010
      'callback arguments' => array('updates' => array('status' => 0)),
1020
    ),
103
    'promote' => array(
1040
      'label' => t('Promote to front page'),
1050
      'callback' => 'node_mass_update',
1060
      'callback arguments' => array('updates' => array('status' => 1,
'promote' => 1)),
1070
    ),
108
    'demote' => array(
1090
      'label' => t('Demote from front page'),
1100
      'callback' => 'node_mass_update',
1110
      'callback arguments' => array('updates' => array('promote' => 0)),
1120
    ),
113
    'sticky' => array(
1140
      'label' => t('Make sticky'),
1150
      'callback' => 'node_mass_update',
1160
      'callback arguments' => array('updates' => array('status' => 1,
'sticky' => 1)),
1170
    ),
118
    'unsticky' => array(
1190
      'label' => t('Remove stickiness'),
1200
      'callback' => 'node_mass_update',
1210
      'callback arguments' => array('updates' => array('sticky' => 0)),
1220
    ),
123
    'delete' => array(
1240
      'label' => t('Delete'),
1250
      'callback' => NULL,
1260
    ),
1270
  );
1280
  return $operations;
1290
}
130
131
/**
132
 * List node administration filters that can be applied.
133
 */
134178
function node_filters() {
135
  // Regular filters
1360
  $filters['status'] = array(
1370
    'title' => t('status'),
138
    'options' => array(
1390
      'status-1' => t('published'),
1400
      'status-0' => t('not published'),
1410
      'promote-1' => t('promoted'),
1420
      'promote-0' => t('not promoted'),
1430
      'sticky-1' => t('sticky'),
1440
      'sticky-0' => t('not sticky'),
1450
    ),
146
  );
147
  // Include translation states if we have this module enabled
1480
  if (module_exists('translation')) {
1490
    $filters['status']['options'] += array(
1500
      'translate-0' => t('Up to date translation'),
1510
      'translate-1' => t('Outdated translation'),
152
    );
1530
  }
154
1550
  $filters['type'] = array('title' => t('type'), 'options' =>
node_get_types('names'));
156
157
  // The taxonomy filter
1580
  if ($taxonomy = module_invoke('taxonomy', 'form_all', 1)) {
1590
    $filters['category'] = array('title' => t('category'), 'options' =>
$taxonomy);
1600
  }
161
  // Language filter if there is a list of languages
1620
  if ($languages = module_invoke('locale', 'language_list')) {
1630
    $languages = array('' => t('Language neutral')) + $languages;
1640
    $filters['language'] = array('title' => t('language'), 'options' =>
$languages);
1650
  }
1660
  return $filters;
1670
}
168
169
/**
170
 * Build query for node administration filters based on session.
171
 */
172178
function node_build_filter_query() {
1730
  $filters = node_filters();
174
175
  // Build query
1760
  $where = $args = array();
1770
  $join = '';
1780
  foreach ($_SESSION['node_overview_filter'] as $index => $filter) {
1790
    list($key, $value) = $filter;
180
    switch ($key) {
1810
      case 'status':
182
        // Note: no exploitable hole as $key/$value have already been
checked when submitted
1830
        list($key, $value) = explode('-', $value, 2);
1840
        $where[] = 'n.' . $key . ' = %d';
1850
        break;
1860
      case 'category':
1870
        $table = "tn$index";
1880
        $where[] = "$table.tid = %d";
1890
        $join .= "INNER JOIN {term_node} $table ON n.nid = $table.nid ";
1900
        break;
1910
      case 'type':
1920
        $where[] = "n.type = '%s'";
1930
        break;
1940
      case 'language':
1950
        $where[] = "n.language = '%s'";
1960
        break;
1970
    }
1980
    $args[] = $value;
1990
  }
2000
  $where = count($where) ? 'WHERE ' . implode(' AND ', $where) : '';
201
2020
  return array('where' => $where, 'join' => $join, 'args' => $args);
2030
}
204
205
/**
206
 * Return form for node administration filters.
207
 */
208178
function node_filter_form() {
2090
  $session = &$_SESSION['node_overview_filter'];
2100
  $session = is_array($session) ? $session : array();
2110
  $filters = node_filters();
212
2130
  $i = 0;
2140
  $form['filters'] = array(
2150
    '#type' => 'fieldset',
2160
    '#title' => t('Show only items where'),
2170
    '#theme' => 'node_filters',
218
  );
2190
  $form['#submit'][] = 'node_filter_form_submit';
2200
  foreach ($session as $filter) {
2210
    list($type, $value) = $filter;
2220
    if ($type == 'category') {
223
      // Load term name from DB rather than search and parse options array.
2240
      $value = module_invoke('taxonomy', 'get_term', $value);
2250
      $value = $value->name;
2260
    }
2270
    elseif ($type == 'language') {
2280
      $value = empty($value) ? t('Language neutral') :
module_invoke('locale', 'language_name', $value);
2290
    }
230
    else {
2310
      $value = $filters[$type]['options'][$value];
232
    }
2330
    if ($i++) {
2340
      $form['filters']['current'][] = array('#markup' => t('<em>and</em>
where <strong>%a</strong> is <strong>%b</strong>', array('%a' =>
$filters[$type]['title'], '%b' => $value)));
2350
    }
236
    else {
2370
      $form['filters']['current'][] = array('#markup' =>
t('<strong>%a</strong> is <strong>%b</strong>', array('%a' =>
$filters[$type]['title'], '%b' => $value)));
238
    }
2390
    if (in_array($type, array('type', 'language'))) {
240
      // Remove the option if it is already being filtered on.
2410
      unset($filters[$type]);
2420
    }
2430
  }
244
2450
  foreach ($filters as $key => $filter) {
2460
    $names[$key] = $filter['title'];
2470
    $form['filters']['status'][$key] = array('#type' => 'select',
'#options' => $filter['options']);
2480
  }
249
2500
  $form['filters']['filter'] = array('#type' => 'radios', '#options' =>
$names, '#default_value' => 'status');
2510
  $form['filters']['buttons']['submit'] = array('#type' => 'submit',
'#value' => (count($session) ? t('Refine') : t('Filter')));
2520
  if (count($session)) {
2530
    $form['filters']['buttons']['undo'] = array('#type' => 'submit',
'#value' => t('Undo'));
2540
    $form['filters']['buttons']['reset'] = array('#type' => 'submit',
'#value' => t('Reset'));
2550
  }
256
2570
  drupal_add_js('misc/form.js', 'core');
258
2590
  return $form;
2600
}
261
262
/**
263
 * Theme node administration filter form.
264
 *
265
 * @ingroup themeable
266
 */
267178
function theme_node_filter_form($form) {
2680
  $output = '';
2690
  $output .= '<div id="node-admin-filter">';
2700
  $output .= drupal_render($form['filters']);
2710
  $output .= '</div>';
2720
  $output .= drupal_render($form);
2730
  return $output;
2740
}
275
276
/**
277
 * Theme node administration filter selector.
278
 *
279
 * @ingroup themeable
280
 */
281178
function theme_node_filters($form) {
2820
  $output = '';
2830
  $output .= '<ul class="clear-block">';
2840
  if (!empty($form['current'])) {
2850
    foreach (element_children($form['current']) as $key) {
2860
      $output .= '<li>' . drupal_render($form['current'][$key]) . '</li>';
2870
    }
2880
  }
289
2900
  $output .= '<li><dl class="multiselect">' . (!empty($form['current']) ?
'<dt><em>' . t('and') . '</em> ' . t('where') . '</dt>' : '') . '<dd
class="a">';
2910
  foreach (element_children($form['filter']) as $key) {
2920
    $output .= drupal_render($form['filter'][$key]);
2930
  }
2940
  $output .= '</dd>';
295
2960
  $output .= '<dt>' . t('is') . '</dt><dd class="b">';
297
2980
  foreach (element_children($form['status']) as $key) {
2990
    $output .= drupal_render($form['status'][$key]);
3000
  }
3010
  $output .= '</dd>';
302
3030
  $output .= '</dl>';
3040
  $output .= '<div class="container-inline" id="node-admin-buttons">' .
drupal_render($form['buttons']) . '</div>';
3050
  $output .= '</li></ul>';
306
3070
  return $output;
3080
}
309
310
/**
311
 * Process result from node administration filter form.
312
 */
313178
function node_filter_form_submit($form, &$form_state) {
3140
  $filters = node_filters();
3150
  switch ($form_state['values']['op']) {
3160
    case t('Filter'):
3170
    case t('Refine'):
3180
      if (isset($form_state['values']['filter'])) {
3190
        $filter = $form_state['values']['filter'];
320
321
        // Flatten the options array to accommodate hierarchical/nested
options.
3220
        $flat_options = form_options_flatten($filters[$filter]['options']);
323
3240
        if (isset($flat_options[$form_state['values'][$filter]])) {
3250
          $_SESSION['node_overview_filter'][] = array($filter,
$form_state['values'][$filter]);
3260
        }
3270
      }
3280
      break;
3290
    case t('Undo'):
3300
      array_pop($_SESSION['node_overview_filter']);
3310
      break;
3320
    case t('Reset'):
3330
      $_SESSION['node_overview_filter'] = array();
3340
      break;
3350
  }
3360
}
337
338
/**
339
 * Make mass update of nodes, changing all nodes in the $nodes array
340
 * to update them with the field values in $updates.
341
 *
342
 * IMPORTANT NOTE: This function is intended to work when called
343
 * from a form submit handler. Calling it outside of the form submission
344
 * process may not work correctly.
345
 *
346
 * @param array $nodes
347
 *   Array of node nids to update.
348
 * @param array $updates
349
 *   Array of key/value pairs with node field names and the
350
 *   value to update that field to.
351
 */
352178
function node_mass_update($nodes, $updates) {
353
  // We use batch processing to prevent timeout when updating a large
number
354
  // of nodes.
3550
  if (count($nodes) > 10) {
356
    $batch = array(
357
      'operations' => array(
3580
        array('_node_mass_update_batch_process', array($nodes, $updates))
3590
      ),
3600
      'finished' => '_node_mass_update_batch_finished',
3610
      'title' => t('Processing'),
362
      // We use a single multi-pass operation, so the default
363
      // 'Remaining x of y operations' message will be confusing here.
3640
      'progress_message' => '',
3650
      'error_message' => t('The update has encountered an error.'),
366
      // The operations do not live in the .module file, so we need to
367
      // tell the batch engine which file to load before calling them.
3680
      'file' => drupal_get_path('module', 'node') . '/node.admin.inc',
3690
    );
3700
    batch_set($batch);
3710
  }
372
  else {
3730
    foreach ($nodes as $nid) {
3740
      _node_mass_update_helper($nid, $updates);
3750
    }
3760
    drupal_set_message(t('The update has been performed.'));
377
  }
3780
}
379
380
/**
381
 * Node Mass Update - helper function.
382
 */
383178
function _node_mass_update_helper($nid, $updates) {
3840
  $node = node_load($nid, NULL, TRUE);
3850
  foreach ($updates as $name => $value) {
3860
    $node->$name = $value;
3870
  }
3880
  node_save($node);
3890
  return $node;
3900
}
391
392
/**
393
 * Node Mass Update Batch operation
394
 */
395178
function _node_mass_update_batch_process($nodes, $updates, &$context) {
3960
  if (!isset($context['sandbox']['progress'])) {
3970
    $context['sandbox']['progress'] = 0;
3980
    $context['sandbox']['max'] = count($nodes);
3990
    $context['sandbox']['nodes'] = $nodes;
4000
  }
401
402
  // Process nodes by groups of 5.
4030
  $count = min(5, count($context['sandbox']['nodes']));
4040
  for ($i = 1; $i <= $count; $i++) {
405
    // For each nid, load the node, reset the values, and save it.
4060
    $nid = array_shift($context['sandbox']['nodes']);
4070
    $node = _node_mass_update_helper($nid, $updates);
408
409
    // Store result for post-processing in the finished callback.
4100
    $context['results'][] = l($node->title, 'node/' . $node->nid);
411
412
    // Update our progress information.
4130
    $context['sandbox']['progress']++;
4140
  }
415
416
  // Inform the batch engine that we are not finished,
417
  // and provide an estimation of the completion level we reached.
4180
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
4190
    $context['finished'] = $context['sandbox']['progress'] /
$context['sandbox']['max'];
4200
  }
4210
}
422
423
/**
424
 * Node Mass Update Batch 'finished' callback.
425
 */
426178
function _node_mass_update_batch_finished($success, $results, $operations)
{
4270
  if ($success) {
4280
    drupal_set_message(t('The update has been performed.'));
4290
  }
430
  else {
4310
    drupal_set_message(t('An error occurred and processing did not
complete.'), 'error');
4320
    $message = format_plural(count($results), '1 item successfully
processed:', '@count items successfully processed:');
4330
    $message .= theme('item_list', $results);
4340
    drupal_set_message($message);
435
  }
4360
}
437
438
/**
439
 * Menu callback: content administration.
440
 */
441178
function node_admin_content($form_state) {
4420
  if (isset($form_state['values']['operation']) &&
$form_state['values']['operation'] == 'delete') {
4430
    return node_multiple_delete_confirm($form_state,
array_filter($form_state['values']['nodes']));
4440
  }
4450
  $form = node_filter_form();
446
4470
  $form['#theme'] = 'node_filter_form';
4480
  $form['admin'] = node_admin_nodes();
449
4500
  return $form;
4510
}
452
453
/**
454
 * Form builder: Builds the node administration overview.
455
 */
456178
function node_admin_nodes() {
457
  // Enable language column if translation module is enabled
458
  // or if we have any node with language.
4590
  $multilanguage = (module_exists('translation') ||
db_result(db_query("SELECT COUNT(*) FROM {node} WHERE language != ''")));
460
461
  // Build the sortable table header.
4620
  $header = array();
4630
  $header[] = theme('table_select_header_cell');
4640
  $header[] = array('data' => t('Title'), 'field' => 'n.title');
4650
  $header[] = array('data' => t('Type'), 'field' => 'n.type');
4660
  $header[] = array('data' => t('Author'), 'field' => 'u.name');
4670
  $header[] = array('data' => t('Status'), 'field' => 'n.status');
4680
  $header[] = array('data' => t('Updated'), 'field' => 'n.changed', 'sort'
=> 'desc');
4690
  if ($multilanguage) {
4700
    $header[] = array('data' => t('Language'), 'field' => 'n.language');
4710
  }
4720
  $header[] = array('data' => t('Operations'));
473
4740
  $form['header'] = array(
4750
   '#type' => 'value',
4760
   '#value' => $header,
477
  );
478
479
  // Build the query and load the nodes we want to display.
4800
  $filter = node_build_filter_query();
481
4820
  $sort = tablesort_sql($header, '', 'n.changed DESC');
4830
  $result = pager_query(db_rewrite_sql('SELECT n.*, u.name FROM {node} n '.
$filter['join'] .' INNER JOIN {users} u ON n.uid = u.uid '.
$filter['where'] . $sort), 50, 0, NULL, $filter['args']);
484
485
  // Build the 'Update options' form.
4860
  $form['options'] = array(
4870
    '#type' => 'fieldset',
4880
    '#title' => t('Update options'),
4890
    '#prefix' => '<div class="container-inline">',
4900
    '#suffix' => '</div>',
491
  );
4920
  $options = array();
4930
  foreach (module_invoke_all('node_operations') as $operation => $array) {
4940
    $options[$operation] = $array['label'];
4950
  }
4960
  $form['options']['operation'] = array(
4970
    '#type' => 'select',
4980
    '#options' => $options,
4990
    '#default_value' => 'approve',
500
  );
5010
  $form['options']['submit'] = array(
5020
    '#type' => 'submit',
5030
    '#value' => t('Update'),
5040
    '#submit' => array('node_admin_nodes_submit'),
505
  );
506
5070
  $languages = language_list();
5080
  $destination = drupal_get_destination();
5090
  $nodes = array();
5100
  while ($node = db_fetch_object($result)) {
5110
    $nodes[$node->nid] = '';
5120
    $options = empty($node->language) ? array() : array('language' =>
$languages[$node->language]);
5130
    $form['title'][$node->nid] = array('#markup' => l($node->title, 'node/'
. $node->nid, $options) . ' ' . theme('mark', node_mark($node->nid,
$node->changed)));
5140
    $form['name'][$node->nid] =  array('#markup' =>
check_plain(node_get_types('name', $node)));
5150
    $form['username'][$node->nid] = array('#markup' => theme('username',
$node));
5160
    $form['status'][$node->nid] =  array('#markup' => ($node->status ?
t('published') : t('not published')));
5170
    $form['changed'][$node->nid] = array('#markup' =>
format_date($node->changed, 'small'));
5180
    if ($multilanguage) {
5190
      $form['language'][$node->nid] = array('#markup' =>
empty($node->language) ? t('Language neutral') :
t($languages[$node->language]->name));
5200
    }
5210
    $form['operations'][$node->nid] = array('#markup' => l(t('edit'),
'node/' . $node->nid . '/edit', array('query' => $destination)));
5220
  }
5230
  $form['nodes'] = array(
5240
    '#type' => 'checkboxes',
5250
    '#options' => $nodes,
526
  );
5270
  $form['pager'] = array('#markup' => theme('pager', NULL, 50, 0));
5280
  $form['#theme'] = 'node_admin_nodes';
5290
  return $form;
5300
}
531
532
/**
533
 * Validate node_admin_nodes form submissions.
534
 *
535
 * Check if any nodes have been selected to perform the chosen
536
 * 'Update option' on.
537
 */
538178
function node_admin_nodes_validate($form, &$form_state) {
5390
  $nodes = array_filter($form_state['values']['nodes']);
5400
  if (count($nodes) == 0) {
5410
    form_set_error('', t('No items selected.'));
5420
  }
5430
}
544
545
/**
546
 * Process node_admin_nodes form submissions.
547
 *
548
 * Execute the chosen 'Update option' on the selected nodes.
549
 */
550178
function node_admin_nodes_submit($form, &$form_state) {
5510
  $operations = module_invoke_all('node_operations');
5520
  $operation = $operations[$form_state['values']['operation']];
553
  // Filter out unchecked nodes
5540
  $nodes = array_filter($form_state['values']['nodes']);
5550
  if ($function = $operation['callback']) {
556
    // Add in callback arguments if present.
5570
    if (isset($operation['callback arguments'])) {
5580
      $args = array_merge(array($nodes), $operation['callback arguments']);
5590
    }
560
    else {
5610
      $args = array($nodes);
562
    }
5630
    call_user_func_array($function, $args);
564
5650
    cache_clear_all();
5660
  }
567
  else {
568
    // We need to rebuild the form to go to a second step.  For example, to
569
    // show the confirmation form for the deletion of nodes.
5700
    $form_state['rebuild'] = TRUE;
571
  }
5720
}
573
574
575
/**
576
 * Theme node administration overview.
577
 *
578
 * @ingroup themeable
579
 */
580178
function theme_node_admin_nodes($form) {
5810
  $output = '';
5820
  $output .= drupal_render($form['options']);
583
5840
  $header = $form['header']['#value'];
585
5860
  $has_posts = isset($form['title']) && is_array($form['title']);
5870
  if ($has_posts) {
5880
    $rows = array();
5890
    foreach (element_children($form['title']) as $key) {
5900
      $row = array();
5910
      $row[] = drupal_render($form['nodes'][$key]);
5920
      $row[] = drupal_render($form['title'][$key]);
5930
      $row[] = drupal_render($form['name'][$key]);
5940
      $row[] = drupal_render($form['username'][$key]);
5950
      $row[] = drupal_render($form['status'][$key]);
5960
      $row[] = drupal_render($form['changed'][$key]);
5970
      if (isset($form['language'])) {
5980
        $row[] = drupal_render($form['language'][$key]);
5990
      }
6000
      $row[] = drupal_render($form['operations'][$key]);
6010
      $rows[] = $row;
6020
    }
6030
  }
604
  else {
6050
    $rows[] = array(
6060
      array('data' => t('No posts available.'), 'colspan' =>
count($header)),
607
    );
608
  }
609
6100
  $output .= theme('table', $header, $rows);
611
6120
  if ($form['pager']['#markup']) {
6130
    $output .= drupal_render($form['pager']);
6140
  }
615
6160
  $output .= drupal_render($form);
617
6180
  return $output;
6190
}
620
621178
function node_multiple_delete_confirm(&$form_state, $nodes) {
622
6230
  $form['nodes'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree'
=> TRUE);
624
  // array_filter returns only elements with TRUE values
6250
  foreach ($nodes as $nid => $value) {
6260
    $title = db_result(db_query('SELECT title FROM {node} WHERE nid = %d',
$nid));
6270
    $form['nodes'][$nid] = array(
6280
      '#type' => 'hidden',
6290
      '#value' => $nid,
6300
      '#prefix' => '<li>',
6310
      '#suffix' => check_plain($title) . "</li>\n",
632
    );
6330
  }
6340
  $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
6350
  $form['#submit'][] = 'node_multiple_delete_confirm_submit';
6360
  return confirm_form($form,
6370
                      t('Are you sure you want to delete these items?'),
6380
                      'admin/content/node', t('This action cannot be
undone.'),
6390
                      t('Delete all'), t('Cancel'));
6400
}
641
642178
function node_multiple_delete_confirm_submit($form, &$form_state) {
6430
  if ($form_state['values']['confirm']) {
6440
    foreach ($form_state['values']['nodes'] as $nid => $value) {
6450
      node_delete($nid);
6460
    }
6470
    drupal_set_message(t('The items have been deleted.'));
6480
  }
6490
  $form_state['redirect'] = 'admin/content/node';
6500
  return;
6510
}
652178