Parent Directory
|
Revision Log
|
Revision Graph
#158598 by JirkaRybka: a massive collection of bug fixes, D6 upgrades, and performance / usability improvements and other tasks for the category package. This commit includes patches from the following threads: - #370641: Port category_views to D6 - #370633: Port category_breadcrumb to D6 - #484624: Fix all broken category/container previews, and add category_display defaults - #481280: Generated menu items vs. menu administration and weights - #457688: Get rid of Menu wrapper, moving functionality to category_menu - #484084: Update README.txt and friends - #483978: Remove t() from database schema descriptions - #501378: PERFORMANCE! Central caching for category API functions - #521680: Missing argument error on category/### paths - #521714: Missing JavaScript file in Taxonomy wrapper Lots and lots of thanks go to JirkaRybka for this monumental cleanup effort.
| 1 | <?php |
| 2 | // $Id: category.module,v 1.161 2009/05/31 10:06:26 jaza Exp $ |
| 3 | |
| 4 | /** |
| 5 | * @file |
| 6 | * Allows users to structure their site and organize content with categories. |
| 7 | */ |
| 8 | |
| 9 | /** |
| 10 | * Implementation of hook_perm(). |
| 11 | */ |
| 12 | function category_perm() { |
| 13 | return array('administer categories'); |
| 14 | } |
| 15 | |
| 16 | /** |
| 17 | * Implementation of hook_theme() |
| 18 | */ |
| 19 | function category_theme() { |
| 20 | return array( |
| 21 | 'category_category_select' => array( |
| 22 | 'arguments' => array('element' => NULL), |
| 23 | ), |
| 24 | 'category_page' => array( |
| 25 | 'arguments' => array('cids' => array(), 'result' => NULL), |
| 26 | ), |
| 27 | 'category_overview_containers' => array( |
| 28 | 'arguments' => array('form' => array()), |
| 29 | ), |
| 30 | 'category_overview_categories' => array( |
| 31 | 'arguments' => array('form' => array()), |
| 32 | ), |
| 33 | 'category_wrapper_status' => array( |
| 34 | 'arguments' => array('type' => NULL, 'status' => NULL), |
| 35 | ), |
| 36 | ); |
| 37 | } |
| 38 | |
| 39 | /** |
| 40 | * Implementation of hook_flush_caches() |
| 41 | */ |
| 42 | function category_flush_caches() { |
| 43 | return array('cache_category'); |
| 44 | } |
| 45 | |
| 46 | /** |
| 47 | * Implementation of hook_link(). |
| 48 | * |
| 49 | * This hook is extended with $type = 'categories' to allow themes to |
| 50 | * print lists of categories associated with a node. Themes can print category |
| 51 | * links with: |
| 52 | * |
| 53 | * if (module_exists('category')) { |
| 54 | * $categories = category_link('categories', $node); |
| 55 | * print theme('links', $categories); |
| 56 | * } |
| 57 | */ |
| 58 | function category_link($type, $node = NULL, $teaser = FALSE) { |
| 59 | $links = array(); |
| 60 | |
| 61 | // 'Add child' links, a la book.module. |
| 62 | if ($type == 'node' && isset($node->category) && !$teaser) { |
| 63 | if ($node->status == 1 && $node->category['depth'] < MENU_MAX_DEPTH) { |
| 64 | foreach (node_get_types() as $node_type) { |
| 65 | $behavior = variable_get('category_behavior_'. $node_type->type, 0); |
| 66 | if (!empty($behavior) && node_access('create', $node_type->type)) { |
| 67 | $allowed_containers = variable_get( |
| 68 | 'category_allowed_containers_'. $node_type->type, array()); |
| 69 | $print_link = FALSE; |
| 70 | if ($node->category['behavior'] == 'container') { |
| 71 | // If the current node is a container, and if the node type of the |
| 72 | // link is a container, then always print a link. |
| 73 | if ($behavior == 'container') { |
| 74 | $print_link = TRUE; |
| 75 | } |
| 76 | else { |
| 77 | // If the current node is a container, but the node type of the |
| 78 | // link is a category, then print a link only if the current node |
| 79 | // is an allowed container for the node type in question. |
| 80 | if (empty($allowed_containers) || in_array($node->nid, $allowed_containers)) { |
| 81 | $print_link = TRUE; |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | else { |
| 86 | // If the current node is a category, and if the node type of the |
| 87 | // link is a container, then always print a link. |
| 88 | if ($behavior == 'container') { |
| 89 | $print_link = TRUE; |
| 90 | } |
| 91 | else { |
| 92 | $container = category_get_container($node->category['cnid']); |
| 93 | if (!$container->tags) { |
| 94 | // If the current node is a category, and the node type of the |
| 95 | // link is also a category, then print a link only if the |
| 96 | // container of the current node is an allowed container for |
| 97 | // the node type in question. |
| 98 | if ($container->hierarchy && (empty($allowed_containers) || in_array($node->category['cnid'], $allowed_containers))) { |
| 99 | $print_link = TRUE; |
| 100 | } |
| 101 | // If the current node and the node type of the link are both |
| 102 | // categories, but the container of the current node is NOT an |
| 103 | // allowed container for the node type, then print a link only |
| 104 | // if one of the allowed containers for the node type in |
| 105 | // question has the container of the current node as an allowed |
| 106 | // parent. |
| 107 | |
| 108 | // Now say that backwards. ;-) |
| 109 | else { |
| 110 | foreach ($allowed_containers as $allowed_cnid) { |
| 111 | $allowed_container = category_get_container($allowed_cnid); |
| 112 | if (!empty($allowed_container->allowed_parent) && $allowed_container->allowed_parent == $container->cid) { |
| 113 | $print_link = TRUE; |
| 114 | break; |
| 115 | } |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | if ($print_link) { |
| 123 | $links['category_add_'. $node_type->type] = array( |
| 124 | 'title' => t('Add child @type', array('@type' => drupal_strtolower($node_type->name))), |
| 125 | 'href' => 'node/add/'. str_replace('_', '-', check_plain($node_type->type)), |
| 126 | 'query' => 'parent='. $node->nid, |
| 127 | ); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | return $links; |
| 134 | } |
| 135 | |
| 136 | // Assigned category links, a la taxonomy.module. |
| 137 | if ($type == 'categories' && $node != NULL) { |
| 138 | // If previewing, the categories must be converted to objects first. |
| 139 | if ($node->build_mode == NODE_BUILD_PREVIEW) { |
| 140 | $node->categories = category_preview_categories($node); |
| 141 | } |
| 142 | if (isset($node->categories) && is_array($node->categories)) { |
| 143 | foreach ($node->categories as $category) { |
| 144 | // During preview the free tagging categories are in an array unlike the |
| 145 | // other categories which are objects. So we have to check if a $category |
| 146 | // is an object or not. |
| 147 | if (is_object($category)) { |
| 148 | $category_display = NULL; |
| 149 | if (module_exists('category_display')) { |
| 150 | $category_display = category_display_get_container($category->cnid); |
| 151 | } |
| 152 | if (!isset($category_display) || !empty($category_display->nodelinks)) { |
| 153 | $links['category_cat_'. $category->cid] = array( |
| 154 | 'title' => $category->title, |
| 155 | 'href' => category_category_path($category), |
| 156 | 'attributes' => array( |
| 157 | 'rel' => 'tag', |
| 158 | 'title' => check_plain(strip_tags($category->description)), |
| 159 | ), |
| 160 | ); |
| 161 | } |
| 162 | } |
| 163 | // Previewing free tagging categories; we don't link them because the |
| 164 | // category-page might not exist yet. |
| 165 | else { |
| 166 | foreach ($category as $free_typed) { |
| 167 | $typed_categories = drupal_explode_tags($free_typed); |
| 168 | $types_categories_count = 0; |
| 169 | foreach ($typed_categories as $typed_category) { |
| 170 | $links['category_preview_cat_'. $types_categories_count] = array( |
| 171 | 'title' => $typed_category, |
| 172 | ); |
| 173 | $types_categories_count++; |
| 174 | } |
| 175 | } |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | // We call this hook again because some modules and themes |
| 181 | // call category_link('categories') directly. |
| 182 | drupal_alter('link', $links, $node); |
| 183 | |
| 184 | return $links; |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | /** |
| 189 | * For containers not maintained by category.module, give the maintaining |
| 190 | * module a chance to provide a path for categories in that container. |
| 191 | * |
| 192 | * @param $category |
| 193 | * A category object. |
| 194 | * @return |
| 195 | * An internal Drupal path. |
| 196 | */ |
| 197 | |
| 198 | function category_category_path($category) { |
| 199 | if (function_exists('taxonomy_term_path')) { |
| 200 | $node = $category; |
| 201 | $node->category = (array) $category; |
| 202 | return taxonomy_term_path((object) _taxonomy_category_into_term($node)); |
| 203 | } |
| 204 | return 'node/'. $category->cid; |
| 205 | } |
| 206 | |
| 207 | /** |
| 208 | * Implementation of hook_menu(). |
| 209 | */ |
| 210 | function category_menu() { |
| 211 | $items = array(); |
| 212 | |
| 213 | $items['admin/content/category'] = array( |
| 214 | 'title' => 'Categories', |
| 215 | 'description' => 'Manage category hierarchies and classification of your content.', |
| 216 | 'page callback' => 'drupal_get_form', |
| 217 | 'page arguments' => array('category_overview_containers'), |
| 218 | 'access arguments' => array('administer categories'), |
| 219 | 'file' => 'category.admin.inc', |
| 220 | ); |
| 221 | $items['admin/content/category/list'] = array( |
| 222 | 'title' => 'List', |
| 223 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
| 224 | 'weight' => -10, |
| 225 | ); |
| 226 | $items['admin/content/category/add'] = array( |
| 227 | 'title' => 'Add container', |
| 228 | 'page callback' => 'category_add_container_page', |
| 229 | 'access arguments' => array('administer categories'), |
| 230 | 'type' => MENU_LOCAL_TASK, |
| 231 | 'weight' => -9, |
| 232 | 'file' => 'category.admin.inc', |
| 233 | ); |
| 234 | $items['admin/content/category/wrappers'] = array( |
| 235 | 'title' => 'Wrapper modules', |
| 236 | 'page callback' => 'drupal_get_form', |
| 237 | 'page arguments' => array('category_wrapper_admin_page'), |
| 238 | 'access arguments' => array('administer categories'), |
| 239 | 'type' => MENU_LOCAL_TASK, |
| 240 | 'weight' => -8, |
| 241 | 'file' => 'category.admin.inc', |
| 242 | ); |
| 243 | $items['admin/content/category/%'] = array( |
| 244 | 'title' => 'List categories', |
| 245 | 'page callback' => 'drupal_get_form', |
| 246 | 'page arguments' => array('category_overview_categories', 3), |
| 247 | 'access arguments' => array('administer categories'), |
| 248 | 'type' => MENU_CALLBACK, |
| 249 | 'file' => 'category.admin.inc', |
| 250 | ); |
| 251 | |
| 252 | $items['admin/content/category/%/list'] = array( |
| 253 | 'title' => 'List', |
| 254 | 'type' => MENU_DEFAULT_LOCAL_TASK, |
| 255 | 'weight' => -10, |
| 256 | ); |
| 257 | $items['admin/content/category/%/add'] = array( |
| 258 | 'title' => 'Add category', |
| 259 | 'page callback' => 'category_add_category_page', |
| 260 | 'page arguments' => array(3), |
| 261 | 'access arguments' => array('administer categories'), |
| 262 | 'type' => MENU_LOCAL_TASK, |
| 263 | 'weight' => -9, |
| 264 | 'file' => 'category.admin.inc', |
| 265 | ); |
| 266 | |
| 267 | $items['category/%'] = array( |
| 268 | 'title' => 'Category listing', |
| 269 | 'page callback' => 'category_page', |
| 270 | 'page arguments' => array(1), |
| 271 | 'access arguments' => array('access content'), |
| 272 | 'type' => MENU_CALLBACK, |
| 273 | 'file' => 'category.pages.inc', |
| 274 | ); |
| 275 | $items['node/%/feed'] = array( |
| 276 | 'title' => 'RSS feed', |
| 277 | 'page callback' => 'category_feed', |
| 278 | 'page arguments' => array(1), |
| 279 | 'access arguments' => array('access content'), |
| 280 | 'type' => MENU_CALLBACK, |
| 281 | 'file' => 'category.pages.inc', |
| 282 | ); |
| 283 | |
| 284 | $items['category/wrapper'] = array( |
| 285 | 'title' => 'Category wrapper', |
| 286 | 'page callback' => 'category_wrapper', |
| 287 | 'access arguments' => array('administer categories'), |
| 288 | 'type' => MENU_CALLBACK, |
| 289 | 'file' => 'category.pages.inc', |
| 290 | ); |
| 291 | $items['category/autocomplete'] = array( |
| 292 | 'title' => 'Autocomplete category', |
| 293 | 'page callback' => 'category_autocomplete', |
| 294 | 'access arguments' => array('access content'), |
| 295 | 'type' => MENU_CALLBACK, |
| 296 | 'file' => 'category.pages.inc', |
| 297 | ); |
| 298 | $items['category/js/form'] = array( |
| 299 | 'page callback' => 'category_form_update', |
| 300 | 'access arguments' => array('access content'), |
| 301 | 'type' => MENU_CALLBACK, |
| 302 | 'file' => 'category.pages.inc', |
| 303 | ); |
| 304 | $items['category/js/distant/%/%'] = array( |
| 305 | 'page callback' => 'category_distant_update', |
| 306 | 'page arguments' => array(3, 4), |
| 307 | 'access arguments' => array('access content'), |
| 308 | 'type' => MENU_CALLBACK, |
| 309 | 'file' => 'category.pages.inc', |
| 310 | ); |
| 311 | |
| 312 | return $items; |
| 313 | } |
| 314 | |
| 315 | /** |
| 316 | * Implementation of hook_init(). Add's the category module's CSS. |
| 317 | */ |
| 318 | function category_init() { |
| 319 | drupal_add_css(drupal_get_path('module', 'category') .'/category.css'); |
| 320 | require_once dirname(drupal_get_filename('module', 'category')) .'/category.inc'; |
| 321 | } |
| 322 | |
| 323 | /** |
| 324 | * Implementation of hook_form_alter(). |
| 325 | * |
| 326 | * Adds the category or container fieldset to the node form. |
| 327 | * |
| 328 | * Also generates a form for selecting categories to associate with a node. |
| 329 | * We check for category_override_selector before loading the full |
| 330 | * category list, so contrib modules can intercept before hook_form_alter |
| 331 | * and provide scalable alternatives. |
| 332 | */ |
| 333 | function category_form_alter(&$form, $form_state, $form_id) { |
| 334 | // Category node type behavior settings. |
| 335 | if ($form_id == 'node_type_form' && isset($form['identity']['type'])) { |
| 336 | $default_behavior = variable_get('category_behavior_'. $form['#node_type']->type, 0); |
| 337 | if ($default_behavior === 'container' || $default_behavior === 0) { |
| 338 | drupal_add_js("if (Drupal.jsEnabled) { $(document).ready(function() { $('div.form-item:has(div.category-allowed-containers)').css('display', 'none'); }); }", 'inline'); |
| 339 | } |
| 340 | drupal_add_js("if (Drupal.jsEnabled) { $(document).ready(function() { $('input.category-behavior').click(function() {if ( this.value == 'category') { $('div.form-item:has(div.category-allowed-containers)').show('fast'); } else { $('div.form-item:has(div.category-allowed-containers)').hide('fast'); } }); }); }", 'inline'); |
| 341 | $form['category'] = array( |
| 342 | '#type' => 'fieldset', |
| 343 | '#title' => t('Category settings'), |
| 344 | '#collapsible' => TRUE, |
| 345 | '#collapsed' => TRUE, |
| 346 | ); |
| 347 | $behavior_disabled = FALSE; |
| 348 | $existing_nodes = 0; |
| 349 | if (!empty($default_behavior)) { |
| 350 | if ($existing_nodes = db_result(db_query("SELECT COUNT(c.cid) AS cid_count FROM {category} c INNER JOIN {node} n ON c.cid = n.nid WHERE n.type = '%s'", $form['#node_type']->type))) { |
| 351 | $behavior_disabled = TRUE; |
| 352 | } |
| 353 | } |
| 354 | $behavior_options = array( |
| 355 | 0 => t('None'), |
| 356 | 'category' => t('Category'), |
| 357 | 'container' => t('Container'), |
| 358 | ); |
| 359 | $count_message = format_plural($existing_nodes, t(' <strong>Note:</strong> this node type is already a @type, and there is %count node of this type that has corresponding @type information. You cannot change the behavior of this node type unless you delete the existing node.', array('@type' => drupal_strtolower($behavior_options[$default_behavior]), '%count' => $existing_nodes)), t(' <strong>Note:</strong> this node type is already a @type, and there are %count nodes of this type that have corresponding @type information. You cannot change the behavior of this node type unless you delete all of these nodes.', array('@type' => drupal_strtolower($behavior_options[$default_behavior]), '%count' => $existing_nodes))); |
| 360 | $form['category']['category_behavior'] = array( |
| 361 | '#type' => 'radios', |
| 362 | '#title' => t('Behavior'), |
| 363 | '#default_value' => $default_behavior, |
| 364 | '#required' => TRUE, |
| 365 | '#options' => $behavior_options, |
| 366 | '#description' => t('Attaches category or container behavior to this node type.') . ($behavior_disabled ? $count_message : ''), |
| 367 | '#attributes' => array('class' => 'category-behavior'), |
| 368 | '#disabled' => $behavior_disabled, |
| 369 | ); |
| 370 | $form['category']['category_allowed_containers'] = array( |
| 371 | '#type' => 'checkboxes', |
| 372 | '#title' => t('Allowed containers'), |
| 373 | '#default_value' => variable_get('category_allowed_containers_'. $form['#node_type']->type, array()), |
| 374 | '#options' => _category_allowed_containers_options(), |
| 375 | '#description' => t('Applies only if this content type has its behavior set to \'category\'. If so, this specifies the containers that categories of this content type may belong to. Leave all options un-checked to allow all containers.'), |
| 376 | '#attributes' => array('class' => 'category-allowed-containers'), |
| 377 | ); |
| 378 | } |
| 379 | |
| 380 | // Category / container node add / edit form elements. |
| 381 | if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) { |
| 382 | // Add elements to the node form |
| 383 | $node = $form['#node']; |
| 384 | |
| 385 | if (isset($node->category)) { |
| 386 | $form['#submit'][] = 'category_node_form_submit'; |
| 387 | _category_add_form_elements($form, $node); |
| 388 | $form['category']['hierarchy']['pick-container'] = array( |
| 389 | '#type' => 'submit', |
| 390 | '#value' => t('Change container (update list of parents)'), |
| 391 | // Submit the node form so the parent select options get updated. |
| 392 | // This is typically only used when JS is disabled. Since the parent options |
| 393 | // won't be changed via AJAX, a button is provided in the node form to submit |
| 394 | // the form and generate options in the parent select corresponding to the |
| 395 | // selected book. This is similar to what happens during a node preview. |
| 396 | '#submit' => array('node_form_submit_build_node'), |
| 397 | '#weight' => 5, |
| 398 | ); |
| 399 | } |
| 400 | } |
| 401 | |
| 402 | // Category selection form elements. |
| 403 | if (isset($form['type']) && isset($form['#node']) && (!variable_get('category_override_selector', FALSE)) && $form['type']['#value'] .'_node_form' == $form_id) { |
| 404 | $node = $form['#node']; |
| 405 | |
| 406 | if (!isset($node->categories)) { |
| 407 | $categories = array(); |
| 408 | if (!empty($node->nid)) { |
| 409 | $categories = category_node_get_categories($node); |
| 410 | } |
| 411 | else if (arg(0) == 'node' && arg(1) == 'add' && is_numeric(arg(3))) { |
| 412 | $categories[arg(3)] = category_get_category(arg(3)); |
| 413 | } |
| 414 | } |
| 415 | else { |
| 416 | // After preview the categories must be converted to objects. |
| 417 | if (isset($form_state['node_preview'])) { |
| 418 | $node->categories = category_preview_categories($node); |
| 419 | } |
| 420 | $categories = $node->categories; |
| 421 | } |
| 422 | |
| 423 | $c = db_query(db_rewrite_sql("SELECT n.nid, n.title, cn.*, c.weight FROM {category_cont} cn INNER JOIN {category_cont_node_types} nt ON cn.cid = nt.cid INNER JOIN {category} c ON cn.cid = c.cid INNER JOIN {node} n ON cn.cid = n.nid WHERE nt.type = '%s' ORDER BY c.weight, n.title", 'n', 'nid'), $node->type); |
| 424 | $containers = array(); |
| 425 | $container_map = array(); |
| 426 | $weight_offset = 0; |
| 427 | |
| 428 | // Build list of non-free-tagging containers, all of which are |
| 429 | // candidates for AJAX distant-parent behavior. |
| 430 | while ($container = db_fetch_object($c)) { |
| 431 | $containers[] = $container; |
| 432 | end($containers); |
| 433 | $container_map[$container->cid] = key($containers); |
| 434 | } |
| 435 | |
| 436 | foreach ($containers as $key => $container) { |
| 437 | if ($container->tags) { |
| 438 | if (isset($form_state['node_preview'])) { |
| 439 | // Typed string can be changed by the user before preview, |
| 440 | // so we just insert the tags directly as provided in the form. |
| 441 | $typed_string = $node->categories['tags'][$container->cid]; |
| 442 | } |
| 443 | else { |
| 444 | $typed_string = category_implode_tags($categories, $container->cid) . (array_key_exists('tags', $categories) ? $categories['tags'][$container->cid] : NULL); |
| 445 | } |
| 446 | if ($container->help) { |
| 447 | $help = $container->help; |
| 448 | } |
| 449 | else { |
| 450 | $help = t('A comma-separated list of categories describing this content. Example: funny, bungee jumping, "Company, Inc.".'); |
| 451 | } |
| 452 | $form['categories']['tags'][$container->cid] = array( |
| 453 | '#type' => 'textfield', |
| 454 | '#title' => $container->title, |
| 455 | '#description' => $help, |
| 456 | '#required' => $container->required, |
| 457 | '#default_value' => $typed_string, |
| 458 | '#autocomplete_path' => 'category/autocomplete/'. $container->cid, |
| 459 | '#weight' => $container->weight, |
| 460 | '#maxlength' => 255, |
| 461 | ); |
| 462 | } |
| 463 | else { |
| 464 | // Extract categories belonging to the container in question. |
| 465 | $default_categories = array(); |
| 466 | foreach ($categories as $category) { |
| 467 | // Free tagging has no default categories and also no container id after preview. |
| 468 | if (isset($category->cnid) && $category->cnid == $container->cid) { |
| 469 | $default_categories[$category->cid] = $category; |
| 470 | } |
| 471 | } |
| 472 | if (empty($default_categories)) { |
| 473 | $default_categories = array(0); |
| 474 | } |
| 475 | |
| 476 | $parents = NULL; |
| 477 | // Find the default categories for the parent of this container - |
| 478 | // needed only for AJAX distant parent selection. |
| 479 | if (!empty($container->allowed_parent)) { |
| 480 | if (isset($containers[$container_map[$container->allowed_parent]])) { |
| 481 | $parent_cnid = $containers[$container_map[$container->allowed_parent]]->cid; |
| 482 | $parents_array = array(); |
| 483 | foreach ($categories as $category) { |
| 484 | if (isset($category->cnid) && $category->cnid == $parent_cnid) { |
| 485 | $parents_array[] = $category->cid; |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | $parents = $parents_array; |
| 490 | } |
| 491 | } |
| 492 | |
| 493 | $form['categories'][$container->cid] = category_form($container->cid, array_keys($default_categories), $container->help, 'category', $parents); |
| 494 | // Weight offset is used to leave 'extra room' between select elements, |
| 495 | // so that submit buttons can be added where necessary (for AJAX |
| 496 | // distant parent containers). |
| 497 | if ($weight_offset) { |
| 498 | $weight_offset *= 2; |
| 499 | } |
| 500 | $form['categories'][$container->cid]['#weight'] = (!empty($container->weight) ? $container->weight : 0) + $weight_offset; |
| 501 | if (!$weight_offset) { |
| 502 | $weight_offset = 1; |
| 503 | } |
| 504 | $form['categories'][$container->cid]['#required'] = TRUE; |
| 505 | } |
| 506 | } |
| 507 | if (!empty($form['categories']) && is_array($form['categories'])) { |
| 508 | if (count($form['categories']) > 1) { |
| 509 | // Add fieldset only if form has more than 1 element. |
| 510 | $form['categories'] += array( |
| 511 | '#type' => 'fieldset', |
| 512 | '#title' => t('Categories'), |
| 513 | '#collapsible' => TRUE, |
| 514 | '#collapsed' => FALSE, |
| 515 | ); |
| 516 | } |
| 517 | $form['categories']['#weight'] = -3; |
| 518 | $form['categories']['#tree'] = TRUE; |
| 519 | } |
| 520 | |
| 521 | for ($curr = 0; $curr < count($containers); $curr++) { |
| 522 | for ($cand = 0; $cand < count($containers); $cand++) { |
| 523 | if (!empty($containers[$cand]->allowed_parent) && $containers[$cand]->allowed_parent == $containers[$curr]->cid) { |
| 524 | // For any pair of container select lists on this form where one |
| 525 | // container has another as its distant parent, add an AHAH callback |
| 526 | // to the parent, and add a wrapper div to the child. |
| 527 | // NOTE: this will fail in cases where multiple containers share a |
| 528 | // single distant parent. In such cases, the AHAH behavior will only |
| 529 | // apply to the first child container. This is because Drupal's AHAH |
| 530 | // framework does not support affecting multiple elements in a single |
| 531 | // callback. |
| 532 | if (!isset($form['categories'][$containers[$curr]->cid]['#ahah'])) { |
| 533 | $form['categories'][$containers[$curr]->cid] += array( |
| 534 | '#ahah' => array( |
| 535 | 'path' => 'category/js/distant/'. $containers[$curr]->cid .'/'. $containers[$cand]->cid, |
| 536 | 'wrapper' => 'edit-category-'. $containers[$cand]->cid .'-wrapper', |
| 537 | 'effect' => 'slide', |
| 538 | ), |
| 539 | ); |
| 540 | |
| 541 | $form['categories'][$containers[$cand]->cid] += array( |
| 542 | '#prefix' => '<div id="edit-category-'. $containers[$cand]->cid .'-wrapper" class="category-distant-wrapper">', |
| 543 | '#suffix' => '</div>', |
| 544 | ); |
| 545 | |
| 546 | $form['categories'][$containers[$curr]->cid .'-pick'] = array( |
| 547 | '#type' => 'submit', |
| 548 | '#value' => t('Update children of @parent', array( |
| 549 | '@parent' => $containers[$curr]->title) |
| 550 | ), |
| 551 | // Submit the node form so the child select options get updated. |
| 552 | // This is typically only used when JS is disabled. Since the child options |
| 553 | // won't be changed via AJAX, a button is provided in the node form to submit |
| 554 | // the form and generate options in the child select corresponding to the |
| 555 | // appropriate container. This is similar to what happens during a node preview. |
| 556 | '#submit' => array('node_form_submit_build_node'), |
| 557 | '#weight' => $form['categories'][$containers[$curr]->cid]['#weight'] + 1, |
| 558 | ); |
| 559 | |
| 560 | // Need this for AJAX. |
| 561 | if (!isset($form['categories']['#cache'])) { |
| 562 | $form['categories']['#cache'] = TRUE; |
| 563 | } |
| 564 | drupal_add_js("if (Drupal.jsEnabled) { $(document).ready(function() { $('#edit-categories-". $containers[$curr]->cid ."-pick').css('display', 'none'); }); }", 'inline'); |
| 565 | } |
| 566 | } |
| 567 | } |
| 568 | } |
| 569 | } |
| 570 | } |
| 571 | |
| 572 | /** |
| 573 | * Additional form submit handler for category/container node forms. |
| 574 | * |
| 575 | * Flattens and processes the $form_state['values']['category'] array |
| 576 | * on both submits and previews. This is needed because |
| 577 | * the 'category' fieldset on the node form has to be 'tree = TRUE', but |
| 578 | * we don't want to deal with the fieldset hierarchy all the time. |
| 579 | */ |
| 580 | function category_node_form_submit($form, &$form_state) { |
| 581 | $fieldsets = array( |
| 582 | 'hierarchy' => TRUE, |
| 583 | 'advanced' => TRUE, |
| 584 | 'identification' => TRUE, |
| 585 | 'content_types' => TRUE, |
| 586 | 'tagging' => TRUE, |
| 587 | 'distant' => TRUE, |
| 588 | ); |
| 589 | foreach ($form_state['values']['category'] as $key => $value) { |
| 590 | if (isset($fieldsets[$key]) && is_array($value)) { |
| 591 | $form_state['values']['category'] += $value; |
| 592 | unset($form_state['values']['category'][$key]); |
| 593 | } |
| 594 | } |
| 595 | |
| 596 | if (!empty($form_state['values']['category']['cid']) && $form_state['values']['category']['cid'] == 'new') { |
| 597 | unset($form_state['values']['category']['cid']); |
| 598 | } |
| 599 | |
| 600 | // Load the new parent for distant-parent category selection. |
| 601 | if (!empty($form_state['values']['op'])) { |
| 602 | $containers = category_get_containers($node->type); |
| 603 | $is_valid_submission = FALSE; |
| 604 | foreach ($containers as $container) { |
| 605 | if ($form_state['values']['op'] == t('Update children of @parent', array('@parent' => $container->title))) { |
| 606 | $is_valid_submission = TRUE; |
| 607 | } |
| 608 | } |
| 609 | |
| 610 | if ($is_valid_submission) { |
| 611 | foreach ($containers as $container) { |
| 612 | if (!empty($form_state['values']['categories'][$container->cid])) { |
| 613 | $post_categories = $form_state['values']['categories'][$container->cid]; |
| 614 | if (!is_array($post_categories)) { |
| 615 | if (is_numeric($post_categories)) { |
| 616 | $post_categories = (int) $post_categories; |
| 617 | $node->categories[$post_categories] = category_get_category($post_categories); |
| 618 | } |
| 619 | } |
| 620 | else { |
| 621 | foreach ($post_categories as $post_category) { |
| 622 | if (is_numeric($post_category)) { |
| 623 | $post_category = (int) $post_category; |
| 624 | $node->categories[$post_category] = category_get_category($post_category); |
| 625 | } |
| 626 | } |
| 627 | } |
| 628 | } |
| 629 | } |
| 630 | } |
| 631 | } |
| 632 | } |
| 633 | |
| 634 | /** |
| 635 | * Generate a form element for selecting categories from a container. |
| 636 | */ |
| 637 | function category_form($cnid, $value = 0, $help = NULL, $name = 'category', |
| 638 | $parents = NULL) { |
| 639 | $container = category_get_container($cnid); |
| 640 | $help = ($help) ? $help : $container->help; |
| 641 | |
| 642 | if (!$container->multiple) { |
| 643 | $blank = ($container->required) ? t('- Please choose -') : t('- None selected -'); |
| 644 | } |
| 645 | else { |
| 646 | $blank = ($container->required) ? 0 : t('- None -'); |
| 647 | } |
| 648 | |
| 649 | return _category_category_select(check_plain($container->title), $name, $value, $cnid, $help, intval($container->multiple), $blank, array(), $parents); |
| 650 | } |
| 651 | |
| 652 | /** |
| 653 | * Helper function to convert categories after a preview. |
| 654 | * |
| 655 | * After preview the tags are an array instead of proper objects. This function |
| 656 | * converts them back to objects with the exception of 'free tagging' categories, |
| 657 | * because new tags can be added by the user before preview and those do not |
| 658 | * yet exist in the database. We therefore save those tags as a string so |
| 659 | * we can fill the form again after the preview. |
| 660 | */ |
| 661 | function category_preview_categories($node) { |
| 662 | $categories = array(); |
| 663 | if (isset($node->categories)) { |
| 664 | foreach ($node->categories as $key => $category) { |
| 665 | unset($node->categories[$key]); |
| 666 | // A 'Multiple select' and a 'Free tagging' field returns an array. |
| 667 | if (is_array($category)) { |
| 668 | foreach ($category as $cid) { |
| 669 | if ($key == 'tags') { |
| 670 | // Free tagging; the values will be saved for later as strings |
| 671 | // instead of objects to fill the form again. |
| 672 | $categories['tags'] = $category; |
| 673 | } |
| 674 | else { |
| 675 | $categories[$cid] = category_get_category($cid); |
| 676 | } |
| 677 | } |
| 678 | } |
| 679 | // A 'Single select' field returns the category id. |
| 680 | elseif ($category) { |
| 681 | $categories[$category] = category_get_category($category); |
| 682 | } |
| 683 | } |
| 684 | } |
| 685 | return $categories; |
| 686 | } |
| 687 | |
| 688 | /** |
| 689 | * Provides category information for rss feeds. |
| 690 | */ |
| 691 | function category_rss_item($node) { |
| 692 | $output = array(); |
| 693 | foreach ($node->categories as $cat) { |
| 694 | $is_included = TRUE; |
| 695 | if (module_exists('category_display')) { |
| 696 | $cont = category_display_get_container($cat->cnid); |
| 697 | if (!$cont->nodelinks) { |
| 698 | $is_included = FALSE; |
| 699 | } |
| 700 | } |
| 701 | |
| 702 | if ($is_included) { |
| 703 | $output[] = array( |
| 704 | 'key' => 'category', |
| 705 | 'value' => check_plain($cat->title), |
| 706 | 'attributes' => array( |
| 707 | 'domain' => url('node/'. $cat->cid, array('absolute' => TRUE)) |
| 708 | ), |
| 709 | ); |
| 710 | } |
| 711 | } |
| 712 | return $output; |
| 713 | } |
| 714 | |
| 715 | /** |
| 716 | * Make sure incoming cnids are free tagging enabled. |
| 717 | */ |
| 718 | function category_node_validate(&$node) { |
| 719 | if (!empty($node->categories)) { |
| 720 | $categories = $node->categories; |
| 721 | if (!empty($categories['tags'])) { |
| 722 | foreach ($categories['tags'] as $cnid => $cnid_value) { |
| 723 | $container = category_get_container($cnid); |
| 724 | if (empty($container->tags)) { |
| 725 | // see form_get_error $key = implode('][', $element['#parents']); |
| 726 | // on why this is the key |
| 727 | form_set_error("categories][tags][$cnid", t('The %title container can not be modified in this way.', array('%title' => $container->title))); |
| 728 | } |
| 729 | } |
| 730 | } |
| 731 | else { |
| 732 | foreach ($categories as $cnid => $category) { |
| 733 | if (empty($category)) { |
| 734 | $container = category_get_container($cnid); |
| 735 | if ($container->required) { |
| 736 | form_set_error("categories][$cnid", t('You must choose a category from %container.', array('%container' => $container->title))); |
| 737 | } |
| 738 | } |
| 739 | } |
| 740 | } |
| 741 | } |
| 742 | } |
| 743 | |
| 744 | /** |
| 745 | * Save category associations for a given node. |
| 746 | */ |
| 747 | function category_node_save($node, $categories) { |
| 748 | global $user; |
| 749 | |
| 750 | category_node_delete_revision($node); |
| 751 | |
| 752 | // Free tagging containers do not send their cids in the form, |
| 753 | // so we'll detect them here and process them independently. |
| 754 | if (isset($categories['tags'])) { |
| 755 | $typed_input = $categories['tags']; |
| 756 | unset($categories['tags']); |
| 757 | |
| 758 | foreach ($typed_input as $cnid => $cnid_value) { |
| 759 | $typed_cats = drupal_explode_tags($cnid_value); |
| 760 | |
| 761 | $inserted = array(); |
| 762 | foreach ($typed_cats as $typed_cat) { |
| 763 | // See if the term exists in the chosen container |
| 764 | // and return the cid, otherwise, add a new record. |
| 765 | $possibilities = category_get_category_by_name($typed_cat); |
| 766 | $typed_cat_cid = NULL; // cid match if any. |
| 767 | foreach ($possibilities as $possibility) { |
| 768 | if ($possibility->cnid == $cnid) { |
| 769 | $typed_cat_cid = $possibility->cid; |
| 770 | } |
| 771 | } |
| 772 | |
| 773 | if (!$typed_cat_cid) { |
| 774 | $tag_node = new stdClass(); |
| 775 | $tag_node->title = $typed_cat; |
| 776 | $tag_node->type = 'category'; |
| 777 | $tag_node->category['cnid'] = $cnid; |
| 778 | $tag_node->category['parents'][0] = $cnid; |
| 779 | $node_options = variable_get('node_options_'. $tag_node->type, array('status', 'promote')); |
| 780 | $tag_node->status = in_array('status', $node_options); |
| 781 | $tag_node->promote = in_array('promote', $node_options); |
| 782 | $tag_node->sticky = in_array('sticky', $node_options); |
| 783 | $tag_node->revision = in_array('revision', $node_options); |
| 784 | $tag_node->name = $user->name ? $user->name : 0; |
| 785 | $tag_node->date = date('j M Y H:i:s'); |
| 786 | $tag_node = node_submit($tag_node); |
| 787 | node_save($tag_node); |
| 788 | $typed_cat_cid = $tag_node->nid; |
| 789 | } |
| 790 | |
| 791 | // Defend against duplicate, differently cased tags |
| 792 | if (!isset($inserted[$typed_cat_cid])) { |
| 793 | db_query('INSERT INTO {category_node} (nid, vid, cid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $typed_cat_cid); |
| 794 | $inserted[$typed_cat_cid] = TRUE; |
| 795 | } |
| 796 | } |
| 797 | } |
| 798 | } |
| 799 | |
| 800 | if (is_array($categories)) { |
| 801 | foreach ($categories as $key => $cat) { |
| 802 | if ($key != 'tags') { |
| 803 | if (is_array($cat)) { |
| 804 | foreach ($cat as $cid) { |
| 805 | if (!empty($cid)) { |
| 806 | db_query('INSERT INTO {category_node} (nid, vid, cid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $cid); |
| 807 | } |
| 808 | } |
| 809 | } |
| 810 | else if (isset($cat->cid)) { |
| 811 | db_query('INSERT INTO {category_node} (nid, vid, cid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $cat->cid); |
| 812 | } |
| 813 | else if ($cat) { |
| 814 | db_query('INSERT INTO {category_node} (nid, vid, cid) VALUES (%d, %d, %d)', $node->nid, $node->vid, $cat); |
| 815 | } |
| 816 | } |
| 817 | } |
| 818 | } |
| 819 | } |
| 820 | |
| 821 | /** |
| 822 | * Remove associations of a node to its categories. |
| 823 | */ |
| 824 | function category_node_delete($node) { |
| 825 | db_query('DELETE FROM {category_node} WHERE nid = %d', $node->nid); |
| 826 | category_cache_op('flush', $node->nid); |
| 827 | } |
| 828 | |
| 829 | /** |
| 830 | * Remove associations of a node to its terms. |
| 831 | */ |
| 832 | function category_node_delete_revision($node) { |
| 833 | db_query('DELETE FROM {category_node} WHERE vid = %d', $node->vid); |
| 834 | category_cache_op('flush', $node->nid); |
| 835 | } |
| 836 | |
| 837 | /** |
| 838 | * Implementation of hook_node_type(). |
| 839 | */ |
| 840 | function category_node_type($op, $info) { |
| 841 | if ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) { |
| 842 | db_query("UPDATE {category_cont_node_types} SET type = '%s' WHERE type = '%s'", $info->type, $info->old_type); |
| 843 | |
| 844 | $allowed_containers = variable_get('category_allowed_containers_'. $info->old_type, array()); |
| 845 | variable_del('category_allowed_containers_'. $info->old_type); |
| 846 | if (!empty($allowed_containers)) { |
| 847 | variable_set('category_allowed_containers_'. $info->type, $allowed_containers); |
| 848 | } |
| 849 | |
| 850 | $behavior = variable_get('category_behavior_'. $info->old_type, 0); |
| 851 | variable_del('category_behavior_'. $info->old_type); |
| 852 | if (!empty($behavior)) { |
| 853 | variable_set('category_behavior_'. $info->type, $behavior); |
| 854 | } |
| 855 | } |
| 856 | elseif ($op == 'delete') { |
| 857 | db_query("DELETE FROM {category_cont_node_types} WHERE type = '%s'", $info->type); |
| 858 | variable_del('category_allowed_containers_'. $info->type); |
| 859 | variable_del('category_behavior_'. $info->type); |
| 860 | } |
| 861 | } |
| 862 | |
| 863 | /** |
| 864 | * Implementation of hook_nodeapi(). |
| 865 | * |
| 866 | * Handles category loading, inserting and updating. |
| 867 | */ |
| 868 | function category_nodeapi(&$node, $op, $teaser, $page) { |
| 869 | switch ($op) { |
| 870 | case 'load': |
| 871 | $output = array(); |
| 872 | $behavior = variable_get('category_behavior_'. $node->type, 0); |
| 873 | if (!empty($behavior)) { |
| 874 | if ($behavior == 'category') { |
| 875 | $output['category'] = (array) category_get_category($node->nid); |
| 876 | } |
| 877 | else { |
| 878 | $output['category'] = (array) category_get_container($node->nid); |
| 879 | } |
| 880 | $output['category']['parents'] = category_get_parents($node->nid); |
| 881 | $output['category']['behavior'] = $behavior; |
| 882 | } |
| 883 | $output['categories'] = category_node_get_categories($node); |
| 884 | return $output; |
| 885 | |
| 886 | case 'view': |
| 887 | if (!$teaser) { |
| 888 | $behavior = variable_get('category_behavior_'. $node->type, 0); |
| 889 | if (isset($behavior) && $node->build_mode == NODE_BUILD_NORMAL && ($listing = category_node_listing($node))) { |
| 890 | $node->content['category_listing'] = array( |
| 891 | '#value' => $listing, |
| 892 | '#weight' => 100, |
| 893 | ); |
| 894 | } |
| 895 | } |
| 896 | break; |
| 897 | |
| 898 | case 'prepare': |
| 899 | $behavior = variable_get('category_behavior_'. $node->type, 0); |
| 900 | if (!empty($behavior)) { |
| 901 | // Prepare defaults for the add/edit form. |
| 902 | if (empty($node->category)) { |
| 903 | $node->category = array(); |
| 904 | if (empty($node->nid) && isset($_GET['parent']) && |
| 905 | is_numeric($_GET['parent'])) { |
| 906 | // Handle "Add child page" links: |
| 907 | $parent = category_get_category($_GET['parent']); |
| 908 | if (!empty($parent)) { |
| 909 | $cnid = !empty($parent->cnid) ? $parent->cnid : $parent->cid; |
| 910 | $parent_container = category_get_container($cnid); |
| 911 | $allowed_containers = variable_get('category_allowed_containers_'. $node->type, array()); |
| 912 | if (empty($parent->category['allowed_parent'])) { |
| 913 | $node->category['container'] = $cnid; |
| 914 | } |
| 915 | else { |
| 916 | $allowed_key = array_search( |
| 917 | $parent->category['allowed_parent'], $allowed_containers); |
| 918 | $node->category['container'] = ($allowed_key !== FALSE) ? $allowed_containers[$allowed_key] : $cnid; |
| 919 | } |
| 920 | $node->category['parents'] = array($parent->nid => $parent->nid); |
| 921 | } |
| 922 | } |
| 923 | if (isset($node->category['container']) && !isset($node->category['original_container'])) { |
| 924 | $node->category['original_container'] = $node->category['container']; |
| 925 | } |
| 926 | // Set defaults. |
| 927 | $node->category += _category_defaults(!empty($node->nid) ? $node->nid : 'new'); |
| 928 | } |
| 929 | else { |
| 930 | $node->category['container'] = empty($node->category['container']) ? $node->category['cnid'] : $node->category['container']; |
| 931 | if (empty($node->category['container']) && !empty($node->category['parents']) && $behavior == 'container') { |
| 932 | reset($node->category['parents']); |
| 933 | $first_parent = current($node->category['parents']); |
| 934 | $node->category['container'] = (!empty($first_parent->cnid) ? $first_parent->cnid : $first_parent->cid); |
| 935 | } |
| 936 | if (isset($node->category['container']) && !isset($node->category['original_container'])) { |
| 937 | $node->category['original_container'] = $node->category['container']; |
| 938 | } |
| 939 | } |
| 940 | // Find the depth limit for the parent select. |
| 941 | if (isset($node->category['container']) && !isset($node->category['parent_depth_limit'])) { |
| 942 | $node->category['parent_depth_limit'] = (MENU_MAX_DEPTH - 1); |
| 943 | } |
| 944 | $node->category['behavior'] = $behavior; |
| 945 | } |
| 946 | break; |
| 947 | |
| 948 | case 'insert': |
| 949 | case 'update': |
| 950 | $behavior = variable_get('category_behavior_'. $node->type, 0); |
| 951 | if (!empty($behavior)) { |
| 952 | if ($behavior == 'category') { |
| 953 | category_save_category($node); |
| 954 | } |
| 955 | else { |
| 956 | category_save_container($node); |
| 957 | } |
| 958 | } |
| 959 | |
| 960 | if (!empty($node->categories)) { |
| 961 | category_node_save($node, _category_filter_pick_elements($node->categories)); |
| 962 | } |
| 963 | break; |
| 964 | |
| 965 | case 'delete': |
| 966 | $behavior = variable_get('category_behavior_'. $node->type, 0); |
| 967 | if (!empty($behavior)) { |
| 968 | if ($behavior == 'category') { |
| 969 | category_del_category($node->nid); |
| 970 | } |
| 971 | else { |
| 972 | category_del_container($node->nid); |
| 973 | } |
| 974 | } |
| 975 | |
| 976 | category_node_delete($node); |
| 977 | break; |
| 978 | |
| 979 | case 'delete revision': |
| 980 | category_node_delete_revision($node); |
| 981 | break; |
| 982 | |
| 983 | case 'validate': |
| 984 | category_node_validate($node); |
| 985 | break; |
| 986 | |
| 987 | case 'rss item': |
| 988 | return category_rss_item($node); |
| 989 | |
| 990 | case 'update index': |
| 991 | return category_node_update_index($node); |
| 992 | } |
| 993 | } |
| 994 | |
| 995 | /** |
| 996 | * Implementation of hook_nodeapi('update_index'). |
| 997 | */ |
| 998 | function category_node_update_index(&$node) { |
| 999 | $output = array(); |
| 1000 | if (!empty($node->category)) { |
| 1001 | foreach ($node->category as $cat) { |
| 1002 | $output[] = $cat->title; |
| 1003 | } |
| 1004 | } |
| 1005 | if (count($output)) { |
| 1006 | return '<strong>('. implode(', ', $output) .')</strong>'; |
| 1007 | } |
| 1008 | } |
| 1009 | |
| 1010 | /** |
| 1011 | * A recursive helper function for category_toc(). |
| 1012 | */ |
| 1013 | function _category_toc_recurse($tree, $indent, &$toc, $exclude, $depth_limit) { |
| 1014 | foreach ($tree as $data) { |
| 1015 | if (($data->depth + 1) > $depth_limit) { |
| 1016 | // Don't iterate through any links on this level. |
| 1017 | break; |
| 1018 | } |
| 1019 | if (!in_array($data->cid, $exclude)) { |
| 1020 | $toc[$data->cid] = str_repeat($indent, $data->depth + 1) .' '. truncate_utf8($data->title, 30, TRUE, TRUE); |
| 1021 | } |
| 1022 | } |
| 1023 | } |
| 1024 | |
| 1025 | /** |
| 1026 | * Returns an array of container children in table of contents order. |
| 1027 | * |
| 1028 | * @param $container |
| 1029 | * The ID of the container whose children are to be listed. |
| 1030 | * @param $exclude |
| 1031 | * Optional array of nid values. Any category whose nid is in this array |
| 1032 | * will be excluded (along with its children). |
| 1033 | * @param $depth_limit |
| 1034 | * Any category deeper than this value will be excluded (along with its children). |
| 1035 | * @return |
| 1036 | * An array of nid, title pairs for use as options for selecting a category. |
| 1037 | */ |
| 1038 | function category_toc($container, $exclude = array(), $depth_limit) { |
| 1039 | $tree = category_get_tree($container); |
| 1040 | $container = category_get_container($container); |
| 1041 | if (!in_array($container->cid, $exclude)) { |
| 1042 | $toc[$container->cid] = $container->title; |
| 1043 | } |
| 1044 | _category_toc_recurse($tree, '--', $toc, $exclude, $depth_limit); |
| 1045 | |
| 1046 | return $toc; |
| 1047 | } |
| 1048 | |
| 1049 | /** |
| 1050 | * Saves a container to the database. This function is called when node_save() |
| 1051 | * is run on a node that has container behavior. |
| 1052 | */ |
| 1053 | function category_save_container(&$node) { |
| 1054 | $new = empty($node->category['cid']); |
| 1055 | $node->category['cid'] = $node->nid; |
| 1056 | $node->category['cnid'] = 0; |
| 1057 | $node->category['nodes'] = empty($node->category['nodes']) ? array() : $node->category['nodes']; |
| 1058 | |
| 1059 | if (!isset($node->category['module'])) { |
| 1060 | $node->category['module'] = 'category'; |
| 1061 | } |
| 1062 | if (!empty($node->category['parents']) && !is_array($node->category['parents'])) { |
| 1063 | $node->category['parents'] = array($node->category['parents'] => TRUE); |
| 1064 | } |
| 1065 | if (empty($node->category['parents'])) { |
| 1066 | $node->category['parents'] = array(0 => TRUE); |
| 1067 | } |
| 1068 | |
| 1069 | if ($new) { |
| 1070 | // Insert new. |
| 1071 | drupal_write_record('category', $node->category); |
| 1072 | drupal_write_record('category_cont', $node->category); |
| 1073 | $status = SAVED_NEW; |
| 1074 | } |
| 1075 | else { |
| 1076 | drupal_write_record('category', $node->category, 'cid'); |
| 1077 | drupal_write_record('category_cont', $node->category, |