/[drupal]/drupal/modules/node/node.admin.inc
ViewVC logotype

Contents of /drupal/modules/node/node.admin.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.76 - (show annotations) (download) (as text)
Tue Nov 3 05:27:18 2009 UTC (3 weeks, 3 days ago) by webchick
Branch: MAIN
CVS Tags: DRUPAL-7-0-UNSTABLE-10
Changes since 1.75: +18 -3 lines
File MIME type: text/x-php
#602522 by effulgentsia, sun, and moshe weitzman: Make links in renderable arrays and forms (e.g. 'Operations') alterable.
1 <?php
2 // $Id: node.admin.inc,v 1.75 2009/11/03 04:35:01 webchick Exp $
3
4 /**
5 * @file
6 * Content administration and module settings UI.
7 */
8
9 /**
10 * Menu callback: confirm rebuilding of permissions.
11 */
12 function node_configure_rebuild_confirm() {
13 return confirm_form(array(), t('Are you sure you want to rebuild the permissions on site content?'),
14 'admin/reports/status', 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'));
15 }
16
17 /**
18 * Handler for wipe confirmation
19 */
20 function node_configure_rebuild_confirm_submit($form, &$form_state) {
21 node_access_rebuild(TRUE);
22 $form_state['redirect'] = 'admin/reports/status';
23 }
24
25 /**
26 * Implement hook_node_operations().
27 */
28 function node_node_operations() {
29 $operations = array(
30 'publish' => array(
31 'label' => t('Publish'),
32 'callback' => 'node_mass_update',
33 'callback arguments' => array('updates' => array('status' => NODE_PUBLISHED)),
34 ),
35 'unpublish' => array(
36 'label' => t('Unpublish'),
37 'callback' => 'node_mass_update',
38 'callback arguments' => array('updates' => array('status' => NODE_NOT_PUBLISHED)),
39 ),
40 'promote' => array(
41 'label' => t('Promote to front page'),
42 'callback' => 'node_mass_update',
43 'callback arguments' => array('updates' => array('status' => NODE_PUBLISHED, 'promote' => NODE_PROMOTED)),
44 ),
45 'demote' => array(
46 'label' => t('Demote from front page'),
47 'callback' => 'node_mass_update',
48 'callback arguments' => array('updates' => array('promote' => NODE_NOT_PROMOTED)),
49 ),
50 'sticky' => array(
51 'label' => t('Make sticky'),
52 'callback' => 'node_mass_update',
53 'callback arguments' => array('updates' => array('status' => NODE_PUBLISHED, 'sticky' => NODE_STICKY)),
54 ),
55 'unsticky' => array(
56 'label' => t('Remove stickiness'),
57 'callback' => 'node_mass_update',
58 'callback arguments' => array('updates' => array('sticky' => NODE_NOT_STICKY)),
59 ),
60 'delete' => array(
61 'label' => t('Delete'),
62 'callback' => NULL,
63 ),
64 );
65 return $operations;
66 }
67
68 /**
69 * List node administration filters that can be applied.
70 */
71 function node_filters() {
72 // Regular filters
73 $filters['status'] = array(
74 'title' => t('status'),
75 'options' => array(
76 '[any]' => t('any'),
77 'status-1' => t('published'),
78 'status-0' => t('not published'),
79 'promote-1' => t('promoted'),
80 'promote-0' => t('not promoted'),
81 'sticky-1' => t('sticky'),
82 'sticky-0' => t('not sticky'),
83 ),
84 );
85 // Include translation states if we have this module enabled
86 if (module_exists('translation')) {
87 $filters['status']['options'] += array(
88 'translate-0' => t('Up to date translation'),
89 'translate-1' => t('Outdated translation'),
90 );
91 }
92
93 $filters['type'] = array(
94 'title' => t('type'),
95 'options' => array(
96 '[any]' => t('any'),
97 ) + node_type_get_names(),
98 );
99
100 // The taxonomy filter
101 if ($taxonomy = module_invoke('taxonomy', 'form_all', 1)) {
102 $filters['term'] = array(
103 'title' => t('term'),
104 'options' => array(
105 '[any]' => t('any'),
106 ) + $taxonomy,
107 );
108 }
109 // Language filter if there is a list of languages
110 if ($languages = module_invoke('locale', 'language_list')) {
111 $languages = array('' => t('Language neutral')) + $languages;
112 $filters['language'] = array(
113 'title' => t('language'),
114 'options' => array(
115 '[any]' => t('any'),
116 ) + $languages,
117 );
118 }
119 return $filters;
120 }
121
122 /**
123 * Apply filters for node administration filters based on session.
124 *
125 * @param $query
126 * A SelectQuery to which the filters should be applied.
127 */
128 function node_build_filter_query(SelectQueryInterface $query) {
129 // Build query
130 $filter_data = isset($_SESSION['node_overview_filter']) ? $_SESSION['node_overview_filter'] : array();
131 $counter = 0;
132 foreach ($filter_data as $index => $filter) {
133 list($key, $value) = $filter;
134 switch ($key) {
135 case 'term':
136 $index = 'tn' . $counter++;
137 $query->join('taxonomy_term_node', $index, "n.nid = $index.nid");
138 $query->condition($index . '.tid', $value);
139 break;
140 case 'status':
141 // Note: no exploitable hole as $key/$value have already been checked when submitted
142 list($key, $value) = explode('-', $value, 2);
143 case 'type':
144 case 'language':
145 $query->condition('n.' . $key, $value);
146 break;
147 }
148 }
149 }
150
151 /**
152 * Return form for node administration filters.
153 */
154 function node_filter_form() {
155 $session = isset($_SESSION['node_overview_filter']) ? $_SESSION['node_overview_filter'] : array();
156 $filters = node_filters();
157
158 $i = 0;
159 $form['filters'] = array(
160 '#type' => 'fieldset',
161 '#title' => t('Show only items where'),
162 '#theme' => 'node_filters',
163 );
164 foreach ($session as $filter) {
165 list($type, $value) = $filter;
166 if ($type == 'term') {
167 // Load term name from DB rather than search and parse options array.
168 $value = module_invoke('taxonomy', 'term_load', $value);
169 $value = $value->name;
170 }
171 elseif ($type == 'language') {
172 $value = empty($value) ? t('Language neutral') : module_invoke('locale', 'language_name', $value);
173 }
174 else {
175 $value = $filters[$type]['options'][$value];
176 }
177 if ($i++) {
178 $form['filters']['current'][] = array('#markup' => t('<em>and</em> where <strong>%type</strong> is <strong>%value</strong>', array('%type' => $filters[$type]['title'], '%value' => $value)));
179 }
180 else {
181 $form['filters']['current'][] = array('#markup' => t('<strong>%type</strong> is <strong>%value</strong>', array('%type' => $filters[$type]['title'], '%value' => $value)));
182 }
183 if (in_array($type, array('type', 'language'))) {
184 // Remove the option if it is already being filtered on.
185 unset($filters[$type]);
186 }
187 }
188
189 foreach ($filters as $key => $filter) {
190 $names[$key] = $filter['title'];
191 $form['filters']['status'][$key] = array(
192 '#type' => 'select',
193 '#options' => $filter['options'],
194 '#title' => $filter['title'],
195 '#default_value' => '[any]',
196 );
197 }
198
199 $form['filters']['buttons']['submit'] = array('#type' => 'submit', '#value' => (count($session) ? t('Refine') : t('Filter')));
200 if (count($session)) {
201 $form['filters']['buttons']['undo'] = array('#type' => 'submit', '#value' => t('Undo'));
202 $form['filters']['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset'));
203 }
204
205 drupal_add_js('misc/form.js');
206
207 return $form;
208 }
209
210 /**
211 * Theme node administration filter form.
212 *
213 * @ingroup themeable
214 */
215 function theme_node_filter_form($variables) {
216 $form = $variables['form'];
217 $output = '';
218
219 $output .= '<div id="node-admin-filter">';
220 $output .= drupal_render($form['filters']);
221 $output .= '</div>';
222 $output .= drupal_render_children($form);
223 return $output;
224 }
225
226 /**
227 * Theme node administration filter selector.
228 *
229 * @ingroup themeable
230 */
231 function theme_node_filters($variables) {
232 $form = $variables['form'];
233 $output = '';
234
235 $output .= '<ul class="clearfix">';
236 if (!empty($form['current'])) {
237 foreach (element_children($form['current']) as $key) {
238 $output .= '<li>' . drupal_render($form['current'][$key]) . '</li>';
239 }
240 }
241 $output .= '</ul>';
242
243 $output .= '<dl class="multiselect">' . (!empty($form['current']) ? '<dt><em>' . t('and') . '</em> ' . t('where') . '</dt>' : '');
244
245 $output .= '<dd class="b">';
246
247 foreach (element_children($form['status']) as $key) {
248 $output .= drupal_render($form['status'][$key]);
249 }
250 $output .= '</dd>';
251
252 $output .= '</dl>';
253 $output .= '<div class="container-inline" id="node-admin-buttons">' . drupal_render($form['buttons']) . '</div>';
254
255 return $output;
256 }
257
258 /**
259 * Process result from node administration filter form.
260 */
261 function node_filter_form_submit($form, &$form_state) {
262 $filters = node_filters();
263 switch ($form_state['values']['op']) {
264 case t('Filter'):
265 case t('Refine'):
266 // Apply every filter that has a choice selected other than 'any'.
267 foreach ($filters as $filter => $options) {
268 if (isset($form_state['values'][$filter]) && $form_state['values'][$filter] != '[any]') {
269 // Flatten the options array to accommodate hierarchical/nested options.
270 $flat_options = form_options_flatten($filters[$filter]['options']);
271 // Only accept valid selections offered on the dropdown, block bad input.
272 if (isset($flat_options[$form_state['values'][$filter]])) {
273 $_SESSION['node_overview_filter'][] = array($filter, $form_state['values'][$filter]);
274 }
275 }
276 }
277 break;
278 case t('Undo'):
279 array_pop($_SESSION['node_overview_filter']);
280 break;
281 case t('Reset'):
282 $_SESSION['node_overview_filter'] = array();
283 break;
284 }
285 }
286
287 /**
288 * Make mass update of nodes, changing all nodes in the $nodes array
289 * to update them with the field values in $updates.
290 *
291 * IMPORTANT NOTE: This function is intended to work when called
292 * from a form submit handler. Calling it outside of the form submission
293 * process may not work correctly.
294 *
295 * @param array $nodes
296 * Array of node nids to update.
297 * @param array $updates
298 * Array of key/value pairs with node field names and the
299 * value to update that field to.
300 */
301 function node_mass_update($nodes, $updates) {
302 // We use batch processing to prevent timeout when updating a large number
303 // of nodes.
304 if (count($nodes) > 10) {
305 $batch = array(
306 'operations' => array(
307 array('_node_mass_update_batch_process', array($nodes, $updates))
308 ),
309 'finished' => '_node_mass_update_batch_finished',
310 'title' => t('Processing'),
311 // We use a single multi-pass operation, so the default
312 // 'Remaining x of y operations' message will be confusing here.
313 'progress_message' => '',
314 'error_message' => t('The update has encountered an error.'),
315 // The operations do not live in the .module file, so we need to
316 // tell the batch engine which file to load before calling them.
317 'file' => drupal_get_path('module', 'node') . '/node.admin.inc',
318 );
319 batch_set($batch);
320 }
321 else {
322 foreach ($nodes as $nid) {
323 _node_mass_update_helper($nid, $updates);
324 }
325 drupal_set_message(t('The update has been performed.'));
326 }
327 }
328
329 /**
330 * Node Mass Update - helper function.
331 */
332 function _node_mass_update_helper($nid, $updates) {
333 $node = node_load($nid, NULL, TRUE);
334 foreach ($updates as $name => $value) {
335 $node->$name = $value;
336 }
337 node_save($node);
338 return $node;
339 }
340
341 /**
342 * Node Mass Update Batch operation
343 */
344 function _node_mass_update_batch_process($nodes, $updates, &$context) {
345 if (!isset($context['sandbox']['progress'])) {
346 $context['sandbox']['progress'] = 0;
347 $context['sandbox']['max'] = count($nodes);
348 $context['sandbox']['nodes'] = $nodes;
349 }
350
351 // Process nodes by groups of 5.
352 $count = min(5, count($context['sandbox']['nodes']));
353 for ($i = 1; $i <= $count; $i++) {
354 // For each nid, load the node, reset the values, and save it.
355 $nid = array_shift($context['sandbox']['nodes']);
356 $node = _node_mass_update_helper($nid, $updates);
357
358 // Store result for post-processing in the finished callback.
359 $context['results'][] = l($node->title[FIELD_LANGUAGE_NONE][0]['value'], 'node/' . $node->nid);
360
361 // Update our progress information.
362 $context['sandbox']['progress']++;
363 }
364
365 // Inform the batch engine that we are not finished,
366 // and provide an estimation of the completion level we reached.
367 if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
368 $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
369 }
370 }
371
372 /**
373 * Node Mass Update Batch 'finished' callback.
374 */
375 function _node_mass_update_batch_finished($success, $results, $operations) {
376 if ($success) {
377 drupal_set_message(t('The update has been performed.'));
378 }
379 else {
380 drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
381 $message = format_plural(count($results), '1 item successfully processed:', '@count items successfully processed:');
382 $message .= theme('item_list', array('items' => $results));
383 drupal_set_message($message);
384 }
385 }
386
387 /**
388 * Menu callback: content administration.
389 */
390 function node_admin_content($form, $form_state) {
391 if (isset($form_state['values']['operation']) && $form_state['values']['operation'] == 'delete') {
392 return node_multiple_delete_confirm($form, $form_state, array_filter($form_state['values']['nodes']));
393 }
394 // Show the 'add new content' link.
395 $form['add_content'] = array(
396 '#access' => _node_add_access(),
397 '#markup' => theme('links', array('links' => array(array('title' => t('Add new content'), 'href' => 'node/add')), 'attributes' => array('class' => array('action-links')))),
398 );
399 $form[] = node_filter_form();
400 $form['#submit'][] = 'node_filter_form_submit';
401 $form['#theme'] = 'node_filter_form';
402 $form['admin'] = node_admin_nodes();
403
404 return $form;
405 }
406
407 /**
408 * Form builder: Builds the node administration overview.
409 */
410 function node_admin_nodes() {
411 // Enable language column if translation module is enabled
412 // or if we have any node with language.
413 $multilanguage = (module_exists('translation') || db_query("SELECT COUNT(*) FROM {node} WHERE language <> ''")->fetchField());
414
415 // Build the sortable table header.
416 $header = array(
417 'title' => array('data' => t('Title'), 'field' => 'n.title'),
418 'type' => array('data' => t('Type'), 'field' => 'n.type'),
419 'author' => array('data' => t('Author'), 'field' => 'u.name'),
420 'status' => array('data' => t('Status'), 'field' => 'n.status'),
421 'changed' => array('data' => t('Updated'), 'field' => 'n.changed', 'sort' => 'desc')
422 );
423 if ($multilanguage) {
424 $header['language'] = array('data' => t('Language'), 'field' => 'n.language');
425 }
426 $header['operations'] = array('data' => t('Operations'));
427
428 $query = db_select('node', 'n')->extend('PagerDefault')->extend('TableSort');
429 $query->join('users', 'u', 'n.uid = u.uid');
430 node_build_filter_query($query);
431
432 $result = $query
433 ->fields('n')
434 ->fields('u', array('name'))
435 ->limit(50)
436 ->orderByHeader($header)
437 ->execute();
438
439 // Build the 'Update options' form.
440 $form['options'] = array(
441 '#type' => 'fieldset',
442 '#title' => t('Update options'),
443 '#prefix' => '<div class="container-inline">',
444 '#suffix' => '</div>',
445 );
446 $options = array();
447 foreach (module_invoke_all('node_operations') as $operation => $array) {
448 $options[$operation] = $array['label'];
449 }
450 $form['options']['operation'] = array(
451 '#type' => 'select',
452 '#options' => $options,
453 '#default_value' => 'approve',
454 );
455 $options = array();
456 $form['options']['submit'] = array(
457 '#type' => 'submit',
458 '#value' => t('Update'),
459 '#submit' => array('node_admin_nodes_submit'),
460 '#validate' => array('node_admin_nodes_validate'),
461 );
462
463 $languages = language_list();
464 $destination = drupal_get_destination();
465 $nodes = array();
466 foreach ($result as $node) {
467 $l_options = empty($node->language) ? array() : array('language' => $languages[$node->language]);
468 $options[$node->nid] = array(
469 'title' => array(
470 'data' => array(
471 '#type' => 'link',
472 '#title' => $node->title,
473 '#href' => 'node/' . $node->nid,
474 '#options' => $l_options,
475 '#suffix' => ' ' . theme('mark', array('type' => node_mark($node->nid, $node->changed))),
476 ),
477 ),
478 'type' => check_plain(node_type_get_name($node)),
479 'author' => theme('username', array('account' => $node)),
480 'status' => $node->status ? t('published') : t('not published'),
481 'changed' => format_date($node->changed, 'short'),
482 );
483 if ($multilanguage) {
484 $options[$node->nid]['language'] = empty($node->language) ? t('Language neutral') : t($languages[$node->language]->name);
485 }
486 $options[$node->nid]['operations'] = array(
487 'data' => array(
488 '#type' => 'link',
489 '#title' => t('edit'),
490 '#href' => 'node/' . $node->nid . '/edit',
491 '#options' => array('query' => $destination),
492 ),
493 );
494 }
495 $form['nodes'] = array(
496 '#type' => 'tableselect',
497 '#header' => $header,
498 '#options' => $options,
499 '#empty' => t('No content available.'),
500 );
501 $form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
502 return $form;
503 }
504
505 /**
506 * Validate node_admin_nodes form submissions.
507 *
508 * Check if any nodes have been selected to perform the chosen
509 * 'Update option' on.
510 */
511 function node_admin_nodes_validate($form, &$form_state) {
512 // Error if there are no items to select.
513 if (!is_array($form_state['values']['nodes']) || !count(array_filter($form_state['values']['nodes']))) {
514 form_set_error('', t('No items selected.'));
515 }
516 }
517
518 /**
519 * Process node_admin_nodes form submissions.
520 *
521 * Execute the chosen 'Update option' on the selected nodes.
522 */
523 function node_admin_nodes_submit($form, &$form_state) {
524 $operations = module_invoke_all('node_operations');
525 $operation = $operations[$form_state['values']['operation']];
526 // Filter out unchecked nodes
527 $nodes = array_filter($form_state['values']['nodes']);
528 if ($function = $operation['callback']) {
529 // Add in callback arguments if present.
530 if (isset($operation['callback arguments'])) {
531 $args = array_merge(array($nodes), $operation['callback arguments']);
532 }
533 else {
534 $args = array($nodes);
535 }
536 call_user_func_array($function, $args);
537
538 cache_clear_all();
539 }
540 else {
541 // We need to rebuild the form to go to a second step. For example, to
542 // show the confirmation form for the deletion of nodes.
543 $form_state['rebuild'] = TRUE;
544 }
545 }
546
547 function node_multiple_delete_confirm($form, &$form_state, $nodes) {
548 $form['nodes'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
549 // array_filter returns only elements with TRUE values
550 foreach ($nodes as $nid => $value) {
551 $title = db_query('SELECT title FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchField();
552 $form['nodes'][$nid] = array(
553 '#type' => 'hidden',
554 '#value' => $nid,
555 '#prefix' => '<li>',
556 '#suffix' => check_plain($title) . "</li>\n",
557 );
558 }
559 $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
560 $form['#submit'][] = 'node_multiple_delete_confirm_submit';
561 $confirm_question = format_plural(count($nodes),
562 'Are you sure you want to delete this item?',
563 'Are you sure you want to delete these items?');
564 return confirm_form($form,
565 $confirm_question,
566 'admin/content', t('This action cannot be undone.'),
567 t('Delete'), t('Cancel'));
568 }
569
570 function node_multiple_delete_confirm_submit($form, &$form_state) {
571 if ($form_state['values']['confirm']) {
572 node_delete_multiple(array_keys($form_state['values']['nodes']));
573 $count = count($form_state['values']['nodes']);
574 watchdog('content', 'Deleted @count posts.', array('@count' => $count));
575 drupal_set_message(t('Deleted @count posts.', array('@count' => $count)));
576 }
577 $form_state['redirect'] = 'admin/content';
578 }
579
580 /**
581 * Implement hook_modules_installed()
582 */
583 function node_modules_installed($modules) {
584 // Clear node type cache for node permissions.
585 drupal_static_reset('_node_types_build');
586 }

  ViewVC Help
Powered by ViewVC 1.1.2