| [ Index ] |
PHP Cross Reference of Drupal 6 (yi-drupal) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: fieldgroup.module,v 1.79.2.53 2011/01/03 11:03:47 yched Exp $ 3 4 /** 5 * @file 6 * Create field groups for CCK fields. 7 * 8 * Hooks for other modules to intervene include: 9 * - hook_fieldgroup_view: Alter the group $element added to $node->content. 10 * - hook_fieldgroup_form: Alter the group portion of the node form. 11 * - hook_fieldgroup_types: Add additional fieldgroup group_types. 12 * - hook_fieldgroup_default_settings: Add additional fieldgroup default settings. 13 * - hook_fieldgroup_save: Do additional processing when a fieldgroup is saved. 14 */ 15 /** 16 * Implementation of hook_init(). 17 */ 18 function fieldgroup_init() { 19 drupal_add_css(drupal_get_path('module', 'fieldgroup') .'/fieldgroup.css'); 20 } 21 22 /** 23 * Implementation of hook_ctools_plugin_directory(). 24 */ 25 function fieldgroup_ctools_plugin_directory($module, $plugin) { 26 if ($module == 'ctools' && $plugin == 'content_types') { 27 return 'panels/' . $plugin; 28 } 29 } 30 31 /** 32 * Implementation of hook_menu(). 33 */ 34 function fieldgroup_menu() { 35 $items = array(); 36 37 // Make sure this doesn't fire until content_types is working, 38 // needed to avoid errors on initial installation. 39 if (!defined('MAINTENANCE_MODE')) { 40 foreach (node_get_types() as $type) { 41 $type_name = $type->type; 42 $content_type = content_types($type_name); 43 $type_url_str = $content_type['url_str']; 44 $items['admin/content/node-type/'. $type_url_str .'/groups/%'] = array( 45 'title' => 'Edit group', 46 'page callback' => 'drupal_get_form', 47 'page arguments' => array('fieldgroup_group_edit_form', $type_name, 5), 48 'access arguments' => array('administer content types'), 49 'type' => MENU_CALLBACK, 50 ); 51 $items['admin/content/node-type/'. $type_url_str .'/groups/%/remove'] = array( 52 'title' => 'Edit group', 53 'page callback' => 'drupal_get_form', 54 'page arguments' => array('fieldgroup_remove_group', $type_name, 5), 55 'access arguments' => array('administer content types'), 56 'type' => MENU_CALLBACK, 57 ); 58 } 59 } 60 return $items; 61 } 62 63 /** 64 * Implementation of hook_theme(). 65 */ 66 function fieldgroup_theme() { 67 return array( 68 'fieldgroup_simple' => array( 69 'template' => 'fieldgroup-simple', 70 'arguments' => array('element' => NULL), 71 ), 72 'fieldgroup_fieldset' => array( 73 'arguments' => array('element' => NULL), 74 ), 75 'fieldgroup_display_overview_form' => array( 76 'arguments' => array('form' => NULL), 77 ), 78 ); 79 } 80 81 /** 82 * Implementation of hook_elements(). 83 */ 84 function fieldgroup_elements() { 85 return array( 86 'fieldgroup_simple' => array(), 87 'fieldgroup_fieldset' => array('#collapsible' => FALSE, '#collapsed' => FALSE, '#value' => NULL,), 88 ); 89 } 90 91 /** 92 * Implementation of hook_fieldapi(). 93 */ 94 function fieldgroup_content_fieldapi($op, $field) { 95 switch ($op) { 96 case 'delete instance': 97 db_query("DELETE FROM {". fieldgroup_fields_tablename() ."} WHERE field_name = '%s' AND type_name = '%s'", $field['field_name'], $field['type_name']); 98 cache_clear_all('fieldgroup_data:', content_cache_tablename(), TRUE); 99 break; 100 } 101 } 102 103 function fieldgroup_group_edit_form(&$form_state, $type_name, $group_name) { 104 $content_type = content_types($type_name); 105 $groups = fieldgroup_groups($content_type['type']); 106 107 if (!$group = $groups[$group_name]) { 108 drupal_not_found(); 109 exit; 110 } 111 112 $form['label'] = array( 113 '#type' => 'textfield', 114 '#title' => t('Label'), 115 '#default_value' => $group['label'], 116 '#required' => TRUE, 117 ); 118 119 // Set a default value for group type early in the form so it 120 // can be overridden by subsequent form elements added by other modules. 121 $group_type = !empty($group['group_type']) ? $group['group_type'] : 'standard'; 122 $form['group_type'] = array('#type' => 'hidden', '#default_value' => $group_type); 123 124 $form['settings']['#tree'] = TRUE; 125 $form['settings']['form'] = array( 126 '#type' => 'fieldset', 127 '#title' => t('Form settings'), 128 '#description' => t('These settings apply to the group in the node editing form.'), 129 ); 130 $form['settings']['form']['style'] = array( 131 '#type' => 'radios', 132 '#title' => t('Style'), 133 '#default_value' => $group['settings']['form']['style'], 134 '#options' => array( 135 'fieldset' => t('always open'), 136 'fieldset_collapsible' => t('collapsible'), 137 'fieldset_collapsed' => t('collapsed'), 138 ) 139 ); 140 $form['settings']['form']['description'] = array( 141 '#type' => 'textarea', 142 '#title' => t('Help text'), 143 '#default_value' => $group['settings']['form']['description'], 144 '#rows' => 5, 145 '#description' => t('Instructions to present to the user on the editing form.'), 146 '#required' => FALSE, 147 ); 148 $form['settings']['display'] = array( 149 '#type' => 'fieldset', 150 '#title' => t('Display settings'), 151 '#description' => t('These settings apply to the group on node display.'), 152 ); 153 $form['settings']['display']['description'] = array( 154 '#type' => 'textarea', 155 '#title' => t('Description'), 156 '#default_value' => $group['settings']['display']['description'], 157 '#rows' => 5, 158 '#description' => t('A description of the group.'), 159 '#required' => FALSE, 160 ); 161 162 foreach (array_keys(content_build_modes()) as $key) { 163 $form['settings']['display'][$key]['format'] = array('#type' => 'value', '#value' => isset($group['settings']['display'][$key]['format']) ? $group['settings']['display'][$key]['format'] : 'fieldset'); 164 $form['settings']['display'][$key]['exclude'] = array('#type' => 'value', '#value' => isset($group['settings']['display'][$key]['exclude']) ? $group['settings']['display'][$key]['exclude'] : 0); 165 } 166 $form['settings']['display']['label'] = array('#type' => 'value', '#value' => $group['settings']['display']['label']); 167 $form['weight'] = array('#type' => 'hidden', '#default_value' => $group['weight']); 168 $form['group_name'] = array('#type' => 'hidden', '#default_value' => $group_name); 169 170 $form['#content_type'] = $content_type; 171 172 $form['submit'] = array( 173 '#type' => 'submit', 174 '#value' => t('Save'), 175 '#weight' => 10, 176 ); 177 178 return $form; 179 } 180 181 function fieldgroup_group_edit_form_submit($form, &$form_state) { 182 $form_values = $form_state['values']; 183 $content_type = $form['#content_type']; 184 fieldgroup_save_group($content_type['type'], $form_values); 185 $form_state['redirect'] = 'admin/content/node-type/'. $content_type['url_str'] .'/fields'; 186 } 187 188 function fieldgroup_remove_group(&$form_state, $type_name, $group_name) { 189 $content_type = content_types($type_name); 190 $groups = fieldgroup_groups($content_type['type']); 191 $group = isset($groups[$group_name]) ? $groups[$group_name] : ''; 192 193 if (empty($group)) { 194 drupal_not_found(); 195 exit; 196 } 197 198 $form['#submit'][] = 'fieldgroup_remove_group_submit'; 199 $form['#content_type'] = $content_type; 200 $form['#group_name'] = $group_name; 201 202 return confirm_form($form, 203 t('Are you sure you want to remove the group %label?', 204 array('%label' => t($group['label']))), 205 'admin/content/node-type/'. $content_type['url_str'] .'/fields', t('This action cannot be undone.'), 206 t('Remove'), t('Cancel')); 207 } 208 209 function fieldgroup_remove_group_submit($form, &$form_state) { 210 $form_values = $form_state['values']; 211 $content_type = $form['#content_type']; 212 $group_name = $form['#group_name']; 213 fieldgroup_delete($content_type['type'], $group_name); 214 drupal_set_message(t('The group %group_name has been removed.', array('%group_name' => $group_name))); 215 $form_state['redirect'] = 'admin/content/node-type/'. $content_type['url_str'] .'/fields'; 216 } 217 218 /** 219 * Returns all groups for a content type 220 */ 221 function fieldgroup_groups($content_type = '', $sorted = FALSE, $reset = FALSE) { 222 global $language; 223 static $groups, $groups_sorted; 224 if (!isset($groups) || $reset) { 225 if ($cached = cache_get('fieldgroup_data:'. $language->language, content_cache_tablename())) { 226 $data = $cached->data; 227 $groups = $data['groups']; 228 $groups_sorted = $data['groups_sorted']; 229 } 230 else { 231 $result = db_query("SELECT * FROM {". fieldgroup_tablename() ."} ORDER BY weight, group_name"); 232 $groups = array(); 233 $groups_sorted = array(); 234 while ($group = db_fetch_array($result)) { 235 $group['settings'] = unserialize($group['settings']); 236 $group['fields'] = array(); 237 238 // Allow external modules to translate field group strings. 239 $group_strings = array( 240 'label' => $group['label'], 241 'form_description' => $group['settings']['form']['description'], 242 'display_description' => $group['settings']['display']['description'], 243 ); 244 drupal_alter('content_fieldgroup_strings', $group_strings, $group['type_name'], $group['group_name']); 245 $group['label'] = $group_strings['label']; 246 $group['settings']['form']['description'] = $group_strings['form_description']; 247 $group['settings']['display']['description'] = $group_strings['display_description']; 248 249 $groups[$group['type_name']][$group['group_name']] = $group; 250 $groups_sorted[$group['type_name']][] = &$groups[$group['type_name']][$group['group_name']]; 251 } 252 //load fields 253 $result = db_query("SELECT nfi.*, ng.group_name FROM {". fieldgroup_tablename() ."} ng ". 254 "INNER JOIN {". fieldgroup_fields_tablename() ."} ngf ON ngf.type_name = ng.type_name AND ngf.group_name = ng.group_name ". 255 "INNER JOIN {". content_instance_tablename() ."} nfi ON nfi.field_name = ngf.field_name AND nfi.type_name = ngf.type_name ". 256 "WHERE nfi.widget_active = 1 ORDER BY nfi.weight"); 257 while ($field = db_fetch_array($result)) { 258 // Allow external modules to translate field strings. 259 $field_strings = array( 260 'widget_label' => $field['label'], 261 'widget_description' => $field['description'], 262 ); 263 drupal_alter('content_field_strings', $field_strings, $field['type_name'], $field['field_name']); 264 $field['label'] = $field_strings['widget_label']; 265 $field['description'] = $field_strings['widget_description']; 266 267 $groups[$field['type_name']][$field['group_name']]['fields'][$field['field_name']] = $field; 268 } 269 cache_set('fieldgroup_data:'. $language->language, array('groups' => $groups, 'groups_sorted' => $groups_sorted), content_cache_tablename()); 270 } 271 } 272 if (empty($content_type)) { 273 return $groups; 274 } 275 elseif (empty($groups) || empty($groups[$content_type])) { 276 return array(); 277 } 278 return $sorted ? $groups_sorted[$content_type] : $groups[$content_type]; 279 } 280 281 282 function _fieldgroup_groups_label($content_type) { 283 $groups = fieldgroup_groups($content_type); 284 285 $labels[''] = '<'. t('none') .'>'; 286 foreach ($groups as $group_name => $group) { 287 $labels[$group_name] = t($group['label']); 288 } 289 return $labels; 290 } 291 292 function _fieldgroup_field_get_group($content_type, $field_name) { 293 return db_result(db_query("SELECT group_name FROM {". fieldgroup_fields_tablename() ."} WHERE type_name = '%s' AND field_name = '%s'", $content_type, $field_name)); 294 } 295 296 /** 297 * Implementation of hook_form_alter() 298 */ 299 function fieldgroup_form_alter(&$form, $form_state, $form_id) { 300 if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) { 301 foreach (fieldgroup_groups($form['type']['#value']) as $group_name => $group) { 302 $form[$group_name] = array( 303 '#type' => 'fieldset', 304 '#title' => check_plain(t($group['label'])), 305 '#collapsed' => $group['settings']['form']['style'] == 'fieldset_collapsed', 306 '#collapsible' => in_array($group['settings']['form']['style'], array('fieldset_collapsed', 'fieldset_collapsible')), 307 '#weight' => $group['weight'], 308 '#description' => content_filter_xss(t($group['settings']['form']['description'])), 309 '#attributes' => array('class' => strtr($group['group_name'], '_', '-')), 310 ); 311 312 $has_accessible_field = FALSE; 313 foreach ($group['fields'] as $field_name => $field) { 314 if (isset($form[$field_name])) { 315 $form[$group_name][$field_name] = $form[$field_name]; 316 // Track whether this group has any accessible fields within it. 317 if (!isset($form[$field_name]['#access']) || $form[$field_name]['#access'] !== FALSE) { 318 $has_accessible_field = TRUE; 319 } 320 unset($form[$field_name]); 321 } 322 } 323 if (!empty($group['fields']) && !element_children($form[$group_name])) { 324 //hide the fieldgroup, because the fields are hidden too 325 unset($form[$group_name]); 326 } 327 328 if (!$has_accessible_field) { 329 // Hide the fieldgroup, because the fields are inaccessible. 330 $form[$group_name]['#access'] = FALSE; 331 } 332 333 // Allow other modules to alter the form. 334 // Can't use module_invoke_all because we want 335 // to be able to use a reference to $form and $form_state. 336 foreach (module_implements('fieldgroup_form') as $module) { 337 $function = $module .'_fieldgroup_form'; 338 $function($form, $form_state, $form_id, $group); 339 } 340 341 } 342 343 } 344 // The group is only added here so it will appear in the export 345 // when using Content Copy. 346 elseif ($form_id == 'content_field_edit_form' && isset($form['widget'])) { 347 $content_type = content_types($form['type_name']['#value']); 348 $form['widget']['group'] = array( 349 '#type' => 'value', 350 '#value' => _fieldgroup_field_get_group($content_type['type'], $form['field_name']['#value']), 351 ); 352 } 353 elseif ($form_id == 'content_field_overview_form') { 354 $form['#validate'][] = 'fieldgroup_field_overview_form_validate'; 355 $form['#submit'][] = 'fieldgroup_field_overview_form_submit'; 356 } 357 elseif ($form_id == 'content_display_overview_form' && !empty($form['#groups'])) { 358 $form['#submit'][] = 'fieldgroup_display_overview_form_submit'; 359 if (!isset($form['submit'])) { 360 $form['submit'] = array('#type' => 'submit', '#value' => t('Save'), '#weight' => 10); 361 } 362 } 363 elseif ($form_id == 'content_field_remove_form') { 364 $form['#submit'][] = 'fieldgroup_field_remove_form_submit'; 365 } 366 } 367 368 /** 369 * API for group name validation. 370 * 371 * Pulled into separate function to be re-usable. 372 */ 373 function fieldgroup_validate_name($group, $type_name) { 374 $errors = array(); 375 376 // No label. 377 if (!$group['label']) { 378 $errors['label'][] = t('You need to provide a label.'); 379 } 380 381 // No group name. 382 if (!$group['group_name']) { 383 $errors['group_name'][] = t('You need to provide a group name.'); 384 } 385 // Group name validation. 386 else { 387 $group_name = $group['group_name']; 388 $group['group_type'] = !empty($group['group_type']) ? $group['group_type'] : 'standard'; 389 390 // Add the 'group_' prefix. 391 if (substr($group_name, 0, 6) != 'group_') { 392 $group_name = 'group_'. $group_name; 393 } 394 395 // Invalid field name. 396 if (!preg_match('!^group_[a-z0-9_]+$!', $group_name)) { 397 $errors['group_name'][] = t('The group name %group_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', array('%group_name' => $group_name)); 398 } 399 if (strlen($group_name) > 32) { 400 $errors['group_name'][] = t('The group name %group_name is too long. The name is limited to 32 characters, including the \'group_\' prefix.', array('%group_name' => $group_name)); 401 } 402 403 // Group name already exists. 404 $groups = fieldgroup_groups($type_name); 405 if (isset($groups[$group_name])) { 406 $errors['group_name'][] = t('The group name %group_name already exists.', array('%group_name' => $group_name)); 407 } 408 if (empty($errors['group_name'])) { 409 $group['group_name'] = $group_name; 410 } 411 } 412 return array('group_name' => $group['group_name'], 'errors' => $errors); 413 } 414 415 function fieldgroup_field_overview_form_validate($form, &$form_state) { 416 $form_values = $form_state['values']; 417 $group = $form_values['_add_new_group']; 418 419 if (array_filter(array($group['label'], $group['group_name']))) { 420 $validation = fieldgroup_validate_name($group, $form['#type_name']); 421 if (!empty($validation['errors'])) { 422 foreach ($validation['errors'] as $type => $messages) { 423 foreach ($messages as $message) { 424 if ($type == 'label') { 425 form_set_error('_add_new_group][label', t('Add new group:') .' '. $message); 426 } 427 else { 428 form_set_error('_add_new_group][group_name', t('Add new group:') .' '. $message); 429 } 430 } 431 } 432 } 433 $group_name = $validation['group_name']; 434 form_set_value($form['_add_new_group']['group_name'], $group_name, $form_state); 435 } 436 else { 437 // Fail validation if attempt to nest fields under a new group without the 438 // proper information. Not raising an error would cause the nested fields 439 // to get weights the user doesn't expect. 440 441 foreach ($form_values as $key => $values) { 442 if ($values['parent'] == '_add_new_group') { 443 form_set_error('_add_new_group][label', t('Add new group: you need to provide a label.')); 444 form_set_error('_add_new_group][group_name', t('Add new group: you need to provide a group name.')); 445 break; 446 } 447 } 448 } 449 } 450 451 function fieldgroup_field_overview_form_submit($form, &$form_state) { 452 $form_values = $form_state['values']; 453 $type_name = $form['#type_name']; 454 455 // Create new group if needed. 456 if (!empty($form_values['_add_new_group']['label'])) { 457 $group = $form_values['_add_new_group']; 458 $group['settings'] = field_group_default_settings($group['group_type']); 459 fieldgroup_save_group($type_name, $group); 460 $new_group_name = $group['group_name']; 461 } 462 463 // Parse incoming rows. 464 $add_field_rows = array('_add_new_field', '_add_existing_field'); 465 $field_rows = array_merge($form['#fields'], $add_field_rows); 466 foreach ($form_values as $key => $values) { 467 // If 'field' row: update field parenting. 468 if (in_array($key, $field_rows)) { 469 // If newly added fields were added to a group: 470 if (in_array($key, $add_field_rows)) { 471 // We replace the '_add_*_field' key with the actual name of 472 // the field that got added. 473 // content_field_overview_form_submit() placed those 474 // in $form_state['fields_added'] for us. 475 if (isset($form_state['fields_added'][$key])) { 476 $key = $form_state['fields_added'][$key]; 477 } 478 else { 479 // No field was actually created : skip to next row. 480 continue; 481 } 482 } 483 // If the field was added to the newly created group, replace the 484 // '_add_new_group' value with the actual name of the group. 485 $parent = ($values['parent'] == '_add_new_group' && isset($new_group_name)) ? $new_group_name : $values['parent']; 486 // TODO: check the parent group does exist ? 487 fieldgroup_update_fields(array('field_name' => $key, 'group' => $parent, 'type_name' => $type_name)); 488 } 489 490 // If 'group' row: update groups weights 491 // (possible newly created group has already been taken care of). 492 elseif (in_array($key, $form['#groups'])) { 493 db_query("UPDATE {". fieldgroup_tablename() ."} SET weight = %d WHERE type_name = '%s' AND group_name = '%s'", 494 $values['weight'], $type_name, $key); 495 } 496 } 497 498 cache_clear_all('fieldgroup_data:', content_cache_tablename(), TRUE); 499 } 500 501 function field_group_default_settings($group_type) { 502 $settings = array( 503 'form' => array('style' => 'fieldset', 'description' => ''), 504 'display' => array('description' => '', 'label' => 'above'), 505 ); 506 module_load_include('inc', 'content', 'includes/content.admin'); 507 foreach (array_keys(content_build_modes()) as $key) { 508 $settings['display'][$key]['format'] = 'fieldset'; 509 $settings['display'][$key]['exclude'] = 0; 510 } 511 // Allow other modules to add new default settings. 512 $settings = array_merge($settings, module_invoke_all('fieldgroup_default_settings', $group_type)); 513 return $settings; 514 } 515 516 function fieldgroup_display_overview_form_submit($form, &$form_state) { 517 $form_values = $form_state['values']; 518 $groups = fieldgroup_groups($form['#type_name']); 519 foreach ($form_values as $key => $values) { 520 if (in_array($key, $form['#groups'])) { 521 $group = $groups[$key]; 522 // We have some numeric keys here, so we can't use array_merge. 523 $group['settings']['display'] = $values + $group['settings']['display']; 524 fieldgroup_save_group($form['#type_name'], $group); 525 } 526 } 527 } 528 529 function fieldgroup_field_remove_form_submit($form, &$form_state) { 530 $form_values = $form_state['values']; 531 // TODO: 532 // - when a (non last) field is removed from a group, a 'ghost row' remains in the fields overview 533 // - when the last field is removed, the group disappears 534 // seems to be fixed when emptying the cache. 535 db_query("DELETE FROM {". fieldgroup_fields_tablename() ."} WHERE type_name = '%s' AND field_name = '%s'", $form_values['type_name'], $form_values['field_name']); 536 } 537 538 /** 539 * Implementation of hook_nodeapi(). 540 */ 541 function fieldgroup_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { 542 switch ($op) { 543 case 'view': 544 // Prevent against invalid 'nodes' built by broken 3rd party code. 545 if (isset($node->type)) { 546 // Build the node content element needed to render each fieldgroup. 547 foreach (fieldgroup_groups($node->type) as $group) { 548 fieldgroup_build_content($group, $node, $teaser, $page); 549 } 550 } 551 break; 552 } 553 } 554 555 /** 556 * Build the node content element needed to render a fieldgroup. 557 * 558 * @param $group 559 * The field group definition. 560 * @param $node 561 * The node containing the field group to display. Can be a 'pseudo-node', 562 * containing at least 'type', 'nid', 'vid', and the content for fields 563 * required for the group. 564 * @param $teaser 565 * @param $page 566 * Similar to hook_nodeapi('view'). 567 * 568 * @see fieldgroup_nodeapi() 569 * @see fieldgroup_view_group() 570 */ 571 function fieldgroup_build_content($group, &$node, $teaser, $page) { 572 // NODE_BUILD_NORMAL is 0, and ('whatever' == 0) is TRUE, so we need a ===. 573 if ($node->build_mode === NODE_BUILD_NORMAL || $node->build_mode == NODE_BUILD_PREVIEW) { 574 $context = $teaser ? 'teaser' : 'full'; 575 } 576 else { 577 $context = $node->build_mode; 578 } 579 580 $group_name = $group['group_name']; 581 582 // Do not include group labels when indexing content. 583 if ($context == NODE_BUILD_SEARCH_INDEX) { 584 $group['settings']['display']['label'] = 'hidden'; 585 } 586 $label = $group['settings']['display']['label'] == 'above'; 587 $element = array( 588 '#title' => $label ? check_plain(t($group['label'])) : '', 589 '#description' => $label ? content_filter_xss(t($group['settings']['display']['description'])) : '', 590 ); 591 $format = isset($group['settings']['display'][$context]['format']) ? $group['settings']['display'][$context]['format'] : 'fieldset'; 592 593 switch ($format) { 594 case 'simple': 595 $element['#type'] = 'fieldgroup_simple'; 596 $element['#group_name'] = $group_name; 597 $element['#node'] = $node; 598 break; 599 case 'hidden': 600 $element['#access'] = FALSE; 601 break; 602 case 'fieldset_collapsed': 603 $element['#collapsed'] = TRUE; 604 case 'fieldset_collapsible': 605 $element['#collapsible'] = TRUE; 606 case 'fieldset': 607 $element['#type'] = 'fieldgroup_fieldset'; 608 $element['#attributes'] = array('class' => 'fieldgroup '. strtr($group['group_name'], '_', '-')); 609 break; 610 } 611 foreach ($group['fields'] as $field_name => $field) { 612 if (isset($node->content[$field_name])) { 613 $element[$field_name] = $node->content[$field_name]; 614 } 615 } 616 617 // Allow other modules to alter the group view. 618 // Can't use module_invoke_all because we want 619 // to be able to use a reference to $node and $element. 620 foreach (module_implements('fieldgroup_view') as $module) { 621 $function = $module .'_fieldgroup_view'; 622 $function($node, $element, $group, $context); 623 } 624 625 // Unset the original field values now that we've moved them. 626 foreach ($group['fields'] as $field_name => $field) { 627 if (isset($node->content[$field_name])) { 628 unset($node->content[$field_name]); 629 } 630 } 631 632 // The wrapper lets us get the themed output for the group 633 // to populate the $GROUP_NAME_rendered variable for node templates, 634 // and hide it from the $content variable if needed. 635 // See fieldgroup_preprocess_node(), theme_fieldgroup_wrapper(). 636 $wrapper = array( 637 'group' => $element, 638 '#weight' => $group['weight'], 639 '#post_render' => array('fieldgroup_wrapper_post_render'), 640 '#group_name' => $group_name, 641 '#type_name' => $node->type, 642 '#context' => $context, 643 ); 644 645 $node->content[$group_name] = $wrapper; 646 } 647 648 /** 649 * Render a single field group, fully themed with label. 650 * 651 * To be used by third-party code (Panels, ...) that needs to output an 652 * isolated field group. Do *not* use inside node templates, use the 653 * $GROUP_NAME_rendered variables instead. You can also use the 'simple' 654 * style format and override the template fieldgroup-simple.tpl.php. 655 * 656 * By default, the field group is displayed using the settings defined for the 657 * 'full node' or 'teaser' contexts (depending on the value of the $teaser param). 658 * Set $node->build_mode to a different value to use a different context. 659 * 660 * Different settings can be specified by adjusting $group['settings']['display']. 661 * 662 * @param $group 663 * The field group definition. 664 * @param $node 665 * The node containing the field group to display. Can be a 'pseudo-node', 666 * containing at least 'type', 'nid', 'vid', and the field data required 667 * for the group. 668 * @param $teaser 669 * @param $page 670 * Similar to hook_nodeapi('view'). 671 * @return 672 * The themed output for the field group. 673 * 674 * @see content_view_field() 675 */ 676 function fieldgroup_view_group($group, &$node, $teaser = FALSE, $page = FALSE) { 677 $group_name = $group['group_name']; 678 $field_types = _content_field_types(); 679 680 // Clone the node to prevent from altering the original. 681 $node_copy = drupal_clone($node); 682 683 // Use 'full'/'teaser' if not specified otherwise. 684 $node_copy->build_mode = isset($node_copy->build_mode) ? $node_copy->build_mode : NODE_BUILD_NORMAL; 685 686 // Build the content element for individual fields in the field group. 687 if (!isset($node_copy->content)) { 688 $node_copy->content = array(); 689 } 690 foreach (array_keys($group['fields']) as $field_name) { 691 $field = content_fields($field_name, $node_copy->type); 692 693 if (isset($node_copy->{$field_name})) { 694 $items = $node_copy->{$field_name}; 695 696 // One-field equivalent to _content_field_invoke('sanitize'). 697 $module = $field_types[$field['type']]['module']; 698 $function = $module .'_field'; 699 if (function_exists($function)) { 700 $function('sanitize', $node_copy, $field, $items, $teaser, $page); 701 $node_copy->{$field_name} = $items; 702 } 703 704 $field_view = content_field('view', $node_copy, $field, $items, $teaser, $page); 705 // content_field('view') adds a wrapper to handle variables and 'excluded' 706 // fields for node templates. We bypass it and get the actual field. 707 $node_copy->content[$field_name] = $field_view[$field_name]; 708 } 709 } 710 711 // Build the content element of the field group itself. 712 fieldgroup_build_content($group, $node_copy, $teaser, $page); 713 714 // fieldgroup_build_content() adds a wrapper to handle variables and 'excluded' 715 // groups for node templates. We bypass it and render the actual field group. 716 $output = drupal_render($node_copy->content[$group_name]['group']); 717 718 return $output; 719 } 720 721 /** 722 * Hide specified fields from the $content variable in node templates. 723 */ 724 function fieldgroup_wrapper_post_render($content, $element) { 725 $groups = fieldgroup_groups($element['#type_name']); 726 $group = $groups[$element['#group_name']]; 727 728 // The display settings are not in quite the same place in the 729 // group and the field, so create the value the theme will expect. 730 $group['display_settings'] = $group['settings']['display']; 731 if (theme('content_exclude', $content, $group, $element['#context'])) { 732 return ''; 733 } 734 return $content; 735 } 736 737 /* 738 * Get the group name for a field. 739 * If the field isn't in a group, FALSE will be returned. 740 * @return The name of the group, or FALSE. 741 */ 742 function fieldgroup_get_group($content_type, $field_name) { 743 foreach (fieldgroup_groups($content_type) as $group_name => $group) { 744 if (in_array($field_name, array_keys($group['fields']))) { 745 return $group_name; 746 } 747 } 748 return FALSE; 749 } 750 751 /** 752 * Implementation of hook_node_type() 753 * React to change in node types 754 */ 755 function fieldgroup_node_type($op, $info) { 756 if ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) { 757 // update the tables 758 db_query("UPDATE {". fieldgroup_tablename() ."} SET type_name='%s' WHERE type_name='%s'", array($info->type, $info->old_type)); 759 db_query("UPDATE {". fieldgroup_fields_tablename() ."} SET type_name='%s' WHERE type_name='%s'", array($info->type, $info->old_type)); 760 cache_clear_all('fieldgroup_data:', content_cache_tablename(), TRUE); 761 } 762 elseif ($op == 'delete') { 763 db_query("DELETE FROM {". fieldgroup_tablename() ."} WHERE type_name = '%s'", $info->type); 764 db_query("DELETE FROM {". fieldgroup_fields_tablename() ."} WHERE type_name = '%s'", $info->type); 765 } 766 } 767 768 function fieldgroup_types() { 769 $types = array('standard' => t('Standard group')); 770 // Allow other modules to add new group_types. 771 $types = array_merge($types, module_invoke_all('fieldgroup_types')); 772 return $types; 773 } 774 775 function fieldgroup_tablename($version = NULL) { 776 if (is_null($version)) { 777 $version = variable_get('fieldgroup_schema_version', 0); 778 } 779 return $version < 6000 ? 'node_group' : 'content_group'; 780 } 781 782 function fieldgroup_fields_tablename($version = NULL) { 783 if (is_null($version)) { 784 $version = variable_get('fieldgroup_schema_version', 0); 785 } 786 return $version < 6000 ? 'node_group_fields' : 'content_group_fields'; 787 } 788 789 /** 790 * CRUD API for fieldgroup module. 791 * 792 * @todo 793 * Make this into more of a real API for groups. 794 */ 795 /* 796 * Saves the given group for this content-type 797 */ 798 function fieldgroup_save_group($type_name, $group) { 799 $groups = fieldgroup_groups($type_name); 800 801 // Allow other modules to intervene when the group is saved. 802 foreach (module_implements('fieldgroup_save_group') as $module) { 803 $function = $module .'_fieldgroup_save_group'; 804 $function($group); 805 } 806 807 if (!isset($groups[$group['group_name']])) { 808 // Accept group name from programmed submissions if valid. 809 db_query("INSERT INTO {". fieldgroup_tablename() ."} (group_type, type_name, group_name, label, settings, weight)". 810 " VALUES ('%s', '%s', '%s', '%s', '%s', %d)", $group['group_type'], $type_name, $group['group_name'], $group['label'], serialize($group['settings']), $group['weight']); 811 cache_clear_all('fieldgroup_data:', content_cache_tablename(), TRUE); 812 return SAVED_NEW; 813 } 814 else { 815 db_query("UPDATE {". fieldgroup_tablename() ."} SET group_type = '%s', label = '%s', settings = '%s', weight = %d ". 816 "WHERE type_name = '%s' AND group_name = '%s'", 817 $group['group_type'], $group['label'], serialize($group['settings']), $group['weight'], $type_name, $group['group_name']); 818 cache_clear_all('fieldgroup_data:', content_cache_tablename(), TRUE); 819 return SAVED_UPDATED; 820 } 821 } 822 823 function fieldgroup_update_fields($form_values) { 824 $default = _fieldgroup_field_get_group($form_values['type_name'], $form_values['field_name']); 825 826 if ($default != $form_values['group']) { 827 if ($form_values['group'] && !$default) { 828 db_query("INSERT INTO {". fieldgroup_fields_tablename() ."} (type_name, group_name, field_name) VALUES ('%s', '%s', '%s')", $form_values['type_name'], $form_values['group'], $form_values['field_name']); 829 } 830 elseif ($form_values['group']) { 831 db_query("UPDATE {". fieldgroup_fields_tablename() ."} SET group_name = '%s' WHERE type_name = '%s' AND field_name = '%s'", $form_values['group'], $form_values['type_name'], $form_values['field_name']); 832 } 833 else { 834 db_query("DELETE FROM {". fieldgroup_fields_tablename() ."} WHERE type_name = '%s' AND field_name = '%s'", $form_values['type_name'], $form_values['field_name']); 835 } 836 cache_clear_all('fieldgroup_data:', content_cache_tablename(), TRUE); 837 } 838 } 839 840 function fieldgroup_delete($content_type, $group_name) { 841 db_query("DELETE FROM {". fieldgroup_tablename() ."} WHERE type_name = '%s' AND group_name = '%s'", $content_type, $group_name); 842 db_query("DELETE FROM {". fieldgroup_fields_tablename() ."} WHERE type_name = '%s' AND group_name = '%s'", $content_type, $group_name); 843 cache_clear_all('fieldgroup_data:', content_cache_tablename(), TRUE); 844 } 845 846 /** 847 * Format a fieldgroup using a 'fieldset'. 848 * 849 * Derived from core's theme_fieldset, with no output if the content is empty. 850 */ 851 function theme_fieldgroup_fieldset($element) { 852 if (empty($element['#children']) && empty($element['#value'])) { 853 return ''; 854 } 855 856 if ($element['#collapsible']) { 857 drupal_add_js('misc/collapse.js'); 858 859 if (!isset($element['#attributes']['class'])) { 860 $element['#attributes']['class'] = ''; 861 } 862 863 $element['#attributes']['class'] .= ' collapsible'; 864 if ($element['#collapsed']) { 865 $element['#attributes']['class'] .= ' collapsed'; 866 } 867 } 868 return '<fieldset'. drupal_attributes($element['#attributes']) .'>'. ($element['#title'] ? '<legend>'. $element['#title'] .'</legend>' : '') . (isset($element['#description']) && $element['#description'] ? '<div class="description">'. $element['#description'] .'</div>' : '') . (!empty($element['#children']) ? $element['#children'] : '') . (isset($element['#value']) ? $element['#value'] : '') ."</fieldset>\n"; 869 } 870 871 872 /** 873 * Process variables for fieldgroup.tpl.php. 874 * 875 * The $variables array contains the following arguments: 876 * - $group_name 877 * - $group_name_css 878 * - $label 879 * - $description 880 * - $content 881 * 882 * @see fieldgroup-simple.tpl.php 883 */ 884 function template_preprocess_fieldgroup_simple(&$vars) { 885 $element = $vars['element']; 886 887 $vars['group_name'] = $element['#group_name']; 888 $vars['group_name_css'] = strtr($element['#group_name'], '_', '-'); 889 $vars['label'] = isset($element['#title']) ? $element['#title'] : '';; 890 $vars['description'] = isset($element['#description']) ? $element['#description'] : '';; 891 $vars['content'] = isset($element['#children']) ? $element['#children'] : ''; 892 $vars['template_files'] = array( 893 'fieldgroup-simple-', 894 'fieldgroup-simple-'. $element['#group_name'], 895 'fieldgroup-simple-'. $element['#node']->type, 896 'fieldgroup-simple-'. $element['#group_name'] .'-'. $element['#node']->type, 897 ); 898 } 899 900 /** 901 * Theme preprocess function for node. 902 * 903 * Adds $GROUP_NAME_rendered variables, 904 * containing the themed output for the whole group. 905 */ 906 function fieldgroup_preprocess_node(&$vars) { 907 $node = $vars['node']; 908 909 foreach (fieldgroup_groups($node->type) as $group_name => $group) { 910 // '#chilren' might not be set if the group is empty. 911 $vars[$group_name .'_rendered'] = isset($node->content[$group_name]['#children']) ? $node->content[$group_name]['#children'] : ''; 912 } 913 }
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 |