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