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