| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: content.admin.inc,v 1.181.2.76 2009/11/02 21:21:24 markuspetrux Exp $ 3 4 /** 5 * @file 6 * Administrative interface for content type creation. 7 */ 8 9 10 /** 11 * Menu callback; replacement for node_overview_types(). 12 */ 13 function content_types_overview() { 14 $types = node_get_types(); 15 $names = node_get_types('names'); 16 $header = array(t('Name'), t('Type'), t('Description'), array('data' => t('Operations'), 'colspan' => '4'),); 17 $rows = array(); 18 19 foreach ($names as $key => $name) { 20 $type = $types[$key]; 21 if (node_hook($type, 'form')) { 22 $type_url_str = str_replace('_', '-', $type->type); 23 $row = array( 24 check_plain($name), 25 check_plain($type->type), 26 ); 27 // Make the description smaller 28 $row[] = array('data' => filter_xss_admin($type->description), 'class' => 'description'); 29 // Set the edit column. 30 $row[] = array('data' => l(t('edit'), 'admin/content/node-type/'. $type_url_str)); 31 // Set links for managing fields. 32 // TODO: a hook to allow other content modules to add more stuff? 33 $row[] = array('data' => l(t('manage fields'), 'admin/content/node-type/'. $type_url_str .'/fields')); 34 // Set the delete column. 35 if ($type->custom) { 36 $row[] = array('data' => l(t('delete'), 'admin/content/node-type/'. $type_url_str .'/delete')); 37 } 38 else { 39 $row[] = array('data' => ''); 40 } 41 42 $rows[] = $row; 43 } 44 } 45 46 // Allow external modules alter the table headers and rows. 47 foreach (module_implements('content_types_overview_alter') as $module) { 48 $function = $module .'_content_types_overview_alter'; 49 $function($header, $rows); 50 } 51 52 if (empty($rows)) { 53 $rows[] = array(array('data' => t('No content types available.'), 'colspan' => '7', 'class' => 'message')); 54 } 55 56 return theme('table', $header, $rows) .theme('content_overview_links'); 57 } 58 59 function theme_content_overview_links() { 60 return '<div class="content-overview-links">'. l(t('ยป Add a new content type'), 'admin/content/types/add') .'</div>'; 61 } 62 63 /** 64 * Menu callback; lists all defined fields for quick reference. 65 */ 66 function content_fields_list() { 67 $fields = content_fields(); 68 $field_types = _content_field_types(); 69 70 // Sort fields by field name. 71 ksort($fields); 72 73 $header = array(t('Field name'), t('Field type'), t('Used in')); 74 $rows = array(); 75 foreach ($fields as $field) { 76 $row = array(); 77 $row[] = $field['locked'] ? t('@field_name (Locked)', array('@field_name' => $field['field_name'])) : $field['field_name']; 78 $row[] = t($field_types[$field['type']]['label']); 79 80 $types = array(); 81 $result = db_query("SELECT nt.name, nt.type FROM {". content_instance_tablename() ."} nfi ". 82 "LEFT JOIN {node_type} nt ON nt.type = nfi.type_name ". 83 "WHERE nfi.field_name = '%s' ". 84 // Keep disabled modules out of table. 85 "AND nfi.widget_active = 1 ". 86 "ORDER BY nt.name ASC", $field['field_name']); 87 while ($type = db_fetch_array($result)) { 88 $content_type = content_types($type['type']); 89 $types[] = l($type['name'], 'admin/content/node-type/'. $content_type['url_str'] .'/fields'); 90 } 91 $row[] = implode(', ', $types); 92 93 $rows[] = array('data' => $row, 'class' => $field['locked'] ? 'menu-disabled' : ''); 94 } 95 if (empty($rows)) { 96 $output = t('No fields have been defined for any content type yet.'); 97 } 98 else { 99 $output = theme('table', $header, $rows); 100 } 101 return $output; 102 } 103 104 /** 105 * Helper function to display a message about inactive fields. 106 */ 107 function content_inactive_message($type_name) { 108 $inactive_fields = content_inactive_fields($type_name); 109 if (!empty($inactive_fields)) { 110 $field_types = _content_field_types(); 111 $widget_types = _content_widget_types($type_name); 112 drupal_set_message(t('This content type has inactive fields. Inactive fields are not included in lists of available fields until their modules are enabled.'), 'error'); 113 foreach ($inactive_fields as $field_name => $field) { 114 drupal_set_message(t('!field (!field_name) is an inactive !field_type field that uses a !widget_type widget.', array( 115 '!field' => $field['widget']['label'], 116 '!field_name' => $field['field_name'], 117 '!field_type' => array_key_exists($field['type'], $field_types) ? $field_types[$field['type']]['label'] : $field['type'], 118 '!widget_type' => array_key_exists($field['widget']['type'], $widget_types) ? $widget_types[$field['widget']['type']]['label'] : $field['widget']['type'], 119 ))); 120 } 121 } 122 } 123 124 /** 125 * Menu callback; listing of fields for a content type. 126 * 127 * Allows fields to be reordered and nested in fieldgroups using 128 * JS drag-n-drop. Non-CCK form elements can also be moved around. 129 */ 130 function content_field_overview_form(&$form_state, $type_name) { 131 132 content_inactive_message($type_name); 133 134 // When displaying the form, make sure the list of fields 135 // is up-to-date. 136 if (empty($form_state['post'])) { 137 content_clear_type_cache(); 138 } 139 140 // Gather type information. 141 $type = content_types($type_name); 142 $fields = $type['fields']; 143 $field_types = _content_field_types(); 144 145 $extra = $type['extra']; 146 $groups = $group_options = $group_types = array(); 147 if (module_exists('fieldgroup')) { 148 $groups = fieldgroup_groups($type['type']); 149 $group_types = fieldgroup_types(); 150 $group_options = _fieldgroup_groups_label($type['type']); 151 // Add the ability to group under the newly created row. 152 $group_options['_add_new_group'] = '_add_new_group'; 153 } 154 155 // Store the default weights as we meet them, to be able to put the 156 //'add new' rows after them. 157 $weights = array(); 158 159 $form = array( 160 '#tree' => TRUE, 161 '#type_name' => $type['type'], 162 '#fields' => array_keys($fields), 163 '#groups' => array_keys($groups), 164 '#extra' => array_keys($extra), 165 '#field_rows' => array(), 166 '#group_rows' => array(), 167 ); 168 169 // Fields. 170 foreach ($fields as $name => $field) { 171 $weight = $field['widget']['weight']; 172 $form[$name] = array( 173 'label' => array('#value' => check_plain($field['widget']['label'])), 174 'field_name' => array('#value' => $field['field_name']), 175 'type' => array('#value' => t($field_types[$field['type']]['label'])), 176 'configure' => array('#value' => l(t('Configure'), 'admin/content/node-type/'. $type['url_str'] .'/fields/'. $field['field_name'])), 177 'remove' => array('#value' => l(t('Remove'), 'admin/content/node-type/'. $type['url_str'] .'/fields/'. $field['field_name'] .'/remove')), 178 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), 179 'parent' => array('#type' => 'select', '#options' => $group_options, '#default_value' => ''), 180 'prev_parent' => array('#type' => 'hidden', '#value' => ''), 181 'hidden_name' => array('#type' => 'hidden', '#default_value' => $field['field_name']), 182 '#leaf' => TRUE, 183 '#row_type' => 'field', 184 'field' => array('#type' => 'value', '#value' => $field), 185 ); 186 if ($field['locked']) { 187 $form[$name]['configure'] = array('#value' => t('Locked')); 188 $form[$name]['remove'] = array(); 189 $form[$name]['#disabled_row'] = TRUE; 190 } 191 $form['#field_rows'][] = $name; 192 $weights[] = $weight; 193 } 194 195 // Groups. 196 foreach ($groups as $name => $group) { 197 $weight = $group['weight']; 198 $form[$name] = array( 199 'label' => array('#value' => check_plain($group['label'])), 200 'group_name' => array('#value' => $group['group_name']), 201 'group_type' => array('#value' => t($group_types[$group['group_type']])), 202 'configure' => array('#value' => l(t('Configure'), 'admin/content/node-type/'. $type['url_str'] .'/groups/'. $group['group_name'])), 203 'remove' => array('#value' => l(t('Remove'), 'admin/content/node-type/'. $type['url_str'] .'/groups/'. $group['group_name'] .'/remove')), 204 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), 205 'parent' => array('#type' => 'hidden', '#default_value' => ''), 206 'hidden_name' => array('#type' => 'hidden', '#default_value' => $group['group_name']), 207 '#root' => TRUE, 208 '#row_type' => 'group', 209 'group' => array('#type' => 'value', '#value' => $group), 210 ); 211 // Adjust child fields rows. 212 foreach ($group['fields'] as $field_name => $field) { 213 $form[$field_name]['parent']['#default_value'] = $name; 214 $form[$field_name]['prev_parent']['#value'] = $name; 215 } 216 $form['#group_rows'][] = $name; 217 $weights[] = $weight; 218 } 219 220 // Non-CCK 'fields'. 221 foreach ($extra as $name => $label) { 222 $weight = $extra[$name]['weight']; 223 $form[$name] = array( 224 'label' => array('#value' => check_plain(t($extra[$name]['label']))), 225 'description' => array('#value' => isset($extra[$name]['description']) ? $extra[$name]['description'] : ''), 226 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), 227 'parent' => array('#type' => 'hidden', '#default_value' => ''), 228 'configure' => array('#value' => isset($extra[$name]['configure']) ? $extra[$name]['configure'] : ''), 229 'remove' => array('#value' => isset($extra[$name]['remove']) ? $extra[$name]['remove'] : ''), 230 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), 231 '#leaf' => TRUE, 232 '#root' => TRUE, 233 '#disabled_row' => TRUE, 234 '#row_type' => 'extra', 235 ); 236 $form['#field_rows'][] = $name; 237 $weights[] = $weight; 238 } 239 240 // Additional row : add new field. 241 $weight = max($weights) + 1; 242 $field_type_options = content_field_type_options(); 243 $widget_type_options = content_widget_type_options(NULL, TRUE); 244 if ($field_type_options && $widget_type_options) { 245 array_unshift($field_type_options, t('- Select a field type -')); 246 array_unshift($widget_type_options, t('- Select a widget -')); 247 $name = '_add_new_field'; 248 $form[$name] = array( 249 'label' => array( 250 '#type' => 'textfield', 251 '#size' => 15, 252 '#description' => t('Label'), 253 ), 254 'field_name' => array( 255 '#type' => 'textfield', 256 // This field should stay LTR even for RTL languages. 257 '#field_prefix' => '<span dir="ltr">field_', 258 '#field_suffix' => '</span>‎', 259 '#attributes' => array('dir'=>'ltr'), 260 '#size' => 15, 261 // Field names are limited to 32 characters including the 'field_' 262 // prefix which is 6 characters long. 263 '#maxlength' => 26, 264 '#description' => t('Field name (a-z, 0-9, _)'), 265 ), 266 'type' => array( 267 '#type' => 'select', 268 '#options' => $field_type_options, 269 '#description' => theme('advanced_help_topic', 'content', 'fields') . t('Type of data to store.'), 270 ), 271 'widget_type' => array( 272 '#type' => 'select', 273 '#options' => $widget_type_options, 274 '#description' => t('Form element to edit the data.'), 275 ), 276 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), 277 'parent' => array('#type' => 'select', '#options' => $group_options, '#default_value' => ''), 278 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), 279 '#leaf' => TRUE, 280 '#add_new' => TRUE, 281 '#row_type' => 'add_new_field', 282 ); 283 $form['#field_rows'][] = $name; 284 } 285 286 // Additional row : add existing field. 287 $existing_field_options = content_existing_field_options($type_name); 288 if ($existing_field_options && $widget_type_options) { 289 $weight++; 290 array_unshift($existing_field_options, t('- Select an existing field -')); 291 $name = '_add_existing_field'; 292 $form[$name] = array( 293 'label' => array( 294 '#type' => 'textfield', 295 '#size' => 15, 296 '#description' => t('Label'), 297 ), 298 'field_name' => array( 299 '#type' => 'select', 300 '#options' => $existing_field_options, 301 '#description' => t('Field to share'), 302 ), 303 'widget_type' => array( 304 '#type' => 'select', 305 '#options' => $widget_type_options, 306 '#description' => t('Form element to edit the data.'), 307 ), 308 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), 309 'parent' => array('#type' => 'select', '#options' => $group_options, '#default_value' => ''), 310 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), 311 '#leaf' => TRUE, 312 '#add_new' => TRUE, 313 '#row_type' => 'add_existing_field', 314 ); 315 $form['#field_rows'][] = $name; 316 } 317 318 // Additional row : add new group. 319 if (!empty($group_types)) { 320 $weight++; 321 $name = '_add_new_group'; 322 $form[$name] = array( 323 'label' => array( 324 '#type' => 'textfield', 325 '#size' => 15, 326 '#description' => t('Label'), 327 ), 328 'group_name' => array( 329 '#type' => 'textfield', 330 // This field should stay LTR even for RTL languages. 331 '#field_prefix' => '<span dir="ltr">group_', 332 '#field_suffix' => '</span>‎', 333 '#attributes' => array('dir'=>'ltr'), 334 '#size' => 15, 335 // Group names are limited to 32 characters including the 'group_' 336 // prefix which is 6 characters long. 337 '#maxlength' => 26, 338 '#description' => t('Group name (a-z, 0-9, _)'), 339 ), 340 'group_option' => array( 341 '#type' => 'hidden', 342 '#value' => '', 343 ), 344 'group_type' => array( 345 '#type' => 'hidden', 346 '#value' => 'standard', 347 ), 348 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), 349 'parent' => array('#type' => 'hidden', '#default_value' => ''), 350 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), 351 '#root' => TRUE, 352 '#add_new' => TRUE, 353 '#row_type' => 'add_new_group', 354 ); 355 if (count($group_types) > 1) { 356 $form[$name]['group_type'] = array( 357 '#type' => 'select', 358 '#description' => t('Type of group.'), 359 '#options' => $group_types, 360 '#default_value' => 'standard', 361 ); 362 } 363 $form['#group_rows'][] = $name; 364 } 365 366 $form['submit'] = array('#type' => 'submit', '#value' => t('Save')); 367 return $form; 368 } 369 370 function content_field_overview_form_validate($form, &$form_state) { 371 _content_field_overview_form_validate_add_new($form, $form_state); 372 _content_field_overview_form_validate_add_existing($form, $form_state); 373 } 374 375 /** 376 * Helper function for content_field_overview_form_validate. 377 * 378 * Validate the 'add new field' row. 379 */ 380 function _content_field_overview_form_validate_add_new($form, &$form_state) { 381 $field = $form_state['values']['_add_new_field']; 382 383 // Validate if any information was provided in the 'add new field' row. 384 if (array_filter(array($field['label'], $field['field_name'], $field['type'], $field['widget_type']))) { 385 // No label. 386 if (!$field['label']) { 387 form_set_error('_add_new_field][label', t('Add new field: you need to provide a label.')); 388 } 389 390 // No field name. 391 if (!$field['field_name']) { 392 form_set_error('_add_new_field][field_name', t('Add new field: you need to provide a field name.')); 393 } 394 // Field name validation. 395 else { 396 $field_name = $field['field_name']; 397 398 // Add the 'field_' prefix. 399 if (substr($field_name, 0, 6) != 'field_') { 400 $field_name = 'field_'. $field_name; 401 form_set_value($form['_add_new_field']['field_name'], $field_name, $form_state); 402 } 403 404 // Invalid field name. 405 if (!preg_match('!^field_[a-z0-9_]+$!', $field_name)) { 406 form_set_error('_add_new_field][field_name', t('Add new field: the field name %field_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', array('%field_name' => $field_name))); 407 } 408 if (strlen($field_name) > 32) { 409 form_set_error('_add_new_field][field_name', t('Add new field: the field name %field_name is too long. The name is limited to 32 characters, including the \'field_\' prefix.', array('%field_name' => $field_name))); 410 } 411 // A field named 'field_instance' would cause a tablename clash with {content_field_instance} 412 if ($field_name == 'field_instance') { 413 form_set_error('_add_new_field][field_name', t("Add new field: the name 'field_instance' is a reserved name.")); 414 } 415 416 // Field name already exists. 417 // We need to check inactive fields as well, so we can't use content_fields(). 418 module_load_include('inc', 'content', 'includes/content.crud'); 419 $fields = content_field_instance_read(array(), TRUE); 420 $used = FALSE; 421 foreach ($fields as $existing_field) { 422 $used |= ($existing_field['field_name'] == $field_name); 423 } 424 if ($used) { 425 form_set_error('_add_new_field][field_name', t('Add new field: the field name %field_name already exists.', array('%field_name' => $field_name))); 426 } 427 } 428 429 // No field type. 430 if (!$field['type']) { 431 form_set_error('_add_new_field][type', t('Add new field: you need to select a field type.')); 432 } 433 434 // No widget type. 435 if (!$field['widget_type']) { 436 form_set_error('_add_new_field][widget_type', t('Add new field: you need to select a widget.')); 437 } 438 // Wrong widget type. 439 elseif ($field['type']) { 440 $widget_types = content_widget_type_options($field['type']); 441 if (!isset($widget_types[$field['widget_type']])) { 442 form_set_error('_add_new_field][widget_type', t('Add new field: invalid widget.')); 443 } 444 } 445 } 446 } 447 448 /** 449 * Helper function for content_field_overview_form_validate. 450 * 451 * Validate the 'add existing field' row. 452 */ 453 function _content_field_overview_form_validate_add_existing($form, &$form_state) { 454 // The form element might be absent if no existing fields can be added to 455 // this content type 456 if (isset($form_state['values']['_add_existing_field'])) { 457 $field = $form_state['values']['_add_existing_field']; 458 459 // Validate if any information was provided in the 'add existing field' row. 460 if (array_filter(array($field['label'], $field['field_name'], $field['widget_type']))) { 461 // No label. 462 if (!$field['label']) { 463 form_set_error('_add_existing_field][label', t('Add existing field: you need to provide a label.')); 464 } 465 466 // No existing field. 467 if (!$field['field_name']) { 468 form_set_error('_add_existing_field][field_name', t('Add existing field: you need to select a field.')); 469 } 470 471 // No widget type. 472 if (!$field['widget_type']) { 473 form_set_error('_add_existing_field][widget_type', t('Add existing field: you need to select a widget.')); 474 } 475 // Wrong widget type. 476 elseif ($field['field_name'] && ($existing_field = content_fields($field['field_name']))) { 477 $widget_types = content_widget_type_options($existing_field['type']); 478 if (!isset($widget_types[$field['widget_type']])) { 479 form_set_error('_add_existing_field][widget_type', t('Add existing field: invalid widget.')); 480 } 481 } 482 } 483 } 484 } 485 486 function content_field_overview_form_submit($form, &$form_state) { 487 $form_values = $form_state['values']; 488 489 $type_name = $form['#type_name']; 490 $type = content_types($type_name); 491 492 // Update field weights. 493 $extra = array(); 494 foreach ($form_values as $key => $values) { 495 // Groups are handled in fieldgroup_content_overview_form_submit(). 496 if (in_array($key, $form['#fields'])) { 497 db_query("UPDATE {". content_instance_tablename() ."} SET weight = %d WHERE type_name = '%s' AND field_name = '%s'", 498 $values['weight'], $type_name, $key); 499 } 500 elseif (in_array($key, $form['#extra'])) { 501 $extra[$key] = $values['weight']; 502 } 503 } 504 505 if ($extra) { 506 variable_set('content_extra_weights_'. $type_name, $extra); 507 } 508 else { 509 variable_del('content_extra_weights_'. $type_name); 510 } 511 512 content_clear_type_cache(); 513 514 $destinations = array(); 515 516 // Create new field. 517 if (!empty($form_values['_add_new_field']['field_name'])) { 518 $field = $form_values['_add_new_field']; 519 $field['type_name'] = $type_name; 520 521 module_load_include('inc', 'content', 'includes/content.crud'); 522 if (content_field_instance_create($field)) { 523 // Store new field information for fieldgroup submit handler. 524 $form_state['fields_added']['_add_new_field'] = $field['field_name']; 525 $destinations[] = 'admin/content/node-type/'. $type['url_str'] .'/fields/'. $field['field_name']; 526 } 527 else { 528 drupal_set_message(t('There was a problem creating field %label.', array( 529 '%label' => $field['label']))); 530 } 531 } 532 533 // Add existing field. 534 if (!empty($form_values['_add_existing_field']['field_name'])) { 535 $field = $form_values['_add_existing_field']; 536 $field['type_name'] = $type_name; 537 $existing_field = content_fields($field['field_name']); 538 539 if ($existing_field['locked']) { 540 drupal_set_message(t('The field %label cannot be added to a content type because it is locked.', array('%label' => $field['field_name']))); 541 } 542 else { 543 module_load_include('inc', 'content', 'includes/content.crud'); 544 if (content_field_instance_create($field)) { 545 // Store new field information for fieldgroup submit handler. 546 $form_state['fields_added']['_add_existing_field'] = $field['field_name']; 547 $destinations[] = 'admin/content/node-type/'. $type['url_str'] .'/fields/'. $field['field_name']; 548 } 549 else { 550 drupal_set_message(t('There was a problem adding field %label.', array('%label' => $field['field_name']))); 551 } 552 } 553 } 554 555 if ($destinations) { 556 $destinations[] = urldecode(substr(drupal_get_destination(), 12)); 557 unset($_REQUEST['destination']); 558 $form_state['redirect'] = content_get_destinations($destinations); 559 } 560 561 } 562 563 /** 564 * Menu callback; presents a listing of fields display settings for a content type. 565 * 566 * Form includes form widgets to select which fields appear for teaser, full node 567 * and how the field labels should be rendered. 568 */ 569 function content_display_overview_form(&$form_state, $type_name, $contexts_selector = 'basic') { 570 content_inactive_message($type_name); 571 572 // Gather type information. 573 $type = content_types($type_name); 574 $field_types = _content_field_types(); 575 $fields = $type['fields']; 576 577 $groups = array(); 578 if (module_exists('fieldgroup')) { 579 $groups = fieldgroup_groups($type['type']); 580 } 581 $contexts = content_build_modes($contexts_selector); 582 583 $form = array( 584 '#tree' => TRUE, 585 '#type_name' => $type['type'], 586 '#fields' => array_keys($fields), 587 '#groups' => array_keys($groups), 588 '#contexts' => $contexts_selector, 589 ); 590 591 if (empty($fields)) { 592 drupal_set_message(t('There are no fields configured for this content type. You can add new fields on the <a href="@link">Manage fields</a> page.', array( 593 '@link' => url('admin/content/node-type/'. $type['url_str'] .'/fields'))), 'warning'); 594 return $form; 595 } 596 597 // Fields. 598 $label_options = array( 599 'above' => t('Above'), 600 'inline' => t('Inline'), 601 'hidden' => t('<Hidden>'), 602 ); 603 foreach ($fields as $name => $field) { 604 $field_type = $field_types[$field['type']]; 605 $defaults = $field['display_settings']; 606 $weight = $field['widget']['weight']; 607 608 $form[$name] = array( 609 'human_name' => array('#value' => check_plain($field['widget']['label'])), 610 'weight' => array('#type' => 'value', '#value' => $weight), 611 'parent' => array('#type' => 'value', '#value' => ''), 612 ); 613 614 // Label 615 if ($contexts_selector == 'basic') { 616 $form[$name]['label']['format'] = array( 617 '#type' => 'select', 618 '#options' => $label_options, 619 '#default_value' => isset($defaults['label']['format']) ? $defaults['label']['format'] : 'above', 620 ); 621 } 622 623 // Formatters. 624 $options = array(); 625 foreach ($field_type['formatters'] as $formatter_name => $formatter_info) { 626 $options[$formatter_name] = $formatter_info['label']; 627 } 628 $options['hidden'] = t('<Hidden>'); 629 630 foreach ($contexts as $key => $value) { 631 $form[$name][$key]['format'] = array( 632 '#type' => 'select', 633 '#options' => $options, 634 '#default_value' => isset($defaults[$key]['format']) ? $defaults[$key]['format'] : 'default', 635 ); 636 // exclude from $content 637 $form[$name][$key]['exclude'] = array( 638 '#type' => 'checkbox', 639 '#options' => array(0 => t('Include'), 1 => t('Exclude')), 640 '#default_value' => isset($defaults[$key]['exclude']) ? $defaults[$key]['exclude'] : 0, 641 ); 642 } 643 } 644 645 // Groups. 646 $label_options = array( 647 'above' => t('Above'), 648 'hidden' => t('<Hidden>'), 649 ); 650 $options = array( 651 'no_style' => t('no styling'), 652 'simple' => t('simple'), 653 'fieldset' => t('fieldset'), 654 'fieldset_collapsible' => t('fieldset - collapsible'), 655 'fieldset_collapsed' => t('fieldset - collapsed'), 656 'hidden' => t('<Hidden>'), 657 ); 658 foreach ($groups as $name => $group) { 659 $defaults = $group['settings']['display']; 660 $weight = $group['weight']; 661 662 $form[$name] = array( 663 'human_name' => array('#value' => check_plain($group['label'])), 664 'weight' => array('#type' => 'value', '#value' => $weight), 665 ); 666 if ($contexts_selector == 'basic') { 667 $form[$name]['label'] = array( 668 '#type' => 'select', 669 '#options' => $label_options, 670 '#default_value' => isset($defaults['label']) ? $defaults['label'] : 'above', 671 ); 672 } 673 foreach ($contexts as $key => $title) { 674 $form[$name][$key]['format'] = array( 675 '#type' => 'select', 676 '#options' => $options, 677 '#default_value' => isset($defaults[$key]['format']) ? $defaults[$key]['format'] : 'fieldset', 678 ); 679 // exclude in $content 680 $form[$name][$key]['exclude'] = array( 681 '#type' => 'checkbox', 682 '#options' => array(0 => t('Include'), 1 => t('Exclude')), 683 '#default_value' => isset($defaults[$key]['exclude']) ? $defaults[$key]['exclude'] : 0, 684 ); 685 } 686 foreach ($group['fields'] as $field_name => $field) { 687 $form[$field_name]['parent']['#value'] = $name; 688 } 689 } 690 691 $form['submit'] = array('#type' => 'submit', '#value' => t('Save')); 692 return $form; 693 } 694 695 /** 696 * Submit handler for the display overview form. 697 */ 698 function content_display_overview_form_submit($form, &$form_state) { 699 module_load_include('inc', 'content', 'includes/content.crud'); 700 $form_values = $form_state['values']; 701 foreach ($form_values as $key => $values) { 702 // Groups are handled in fieldgroup_display_overview_form_submit(). 703 if (in_array($key, $form['#fields'])) { 704 $field = content_fields($key, $form['#type_name']); 705 // We have some numeric keys here, so we can't use array_merge. 706 $field['display_settings'] = $values + $field['display_settings']; 707 content_field_instance_update($field, FALSE); 708 } 709 } 710 711 // Clear caches and rebuild menu. 712 content_clear_type_cache(TRUE); 713 menu_rebuild(); 714 715 drupal_set_message(t('Your settings have been saved.')); 716 } 717 718 /** 719 * Return an array of field_type options. 720 */ 721 function content_field_type_options() { 722 static $options; 723 724 if (!isset($options)) { 725 $options = array(); 726 $field_types = _content_field_types(); 727 $field_type_options = array(); 728 foreach ($field_types as $field_type_name => $field_type) { 729 // skip field types which have no widget types. 730 if (content_widget_type_options($field_type_name)) { 731 $options[$field_type_name] = t($field_type['label']); 732 } 733 } 734 asort($options); 735 } 736 return $options; 737 } 738 739 /** 740 * Return an array of widget type options for a field type. 741 * 742 * If no field type is provided, returns a nested array of 743 * all widget types, keyed by field type human name 744 */ 745 function content_widget_type_options($field_type = NULL, $by_label = FALSE) { 746 static $options; 747 748 if (!isset($options)) { 749 $options = array(); 750 foreach (_content_widget_types() as $widget_type_name => $widget_type) { 751 foreach ($widget_type['field types'] as $widget_field_type) { 752 $options[$widget_field_type][$widget_type_name] = t($widget_type['label']); 753 } 754 } 755 } 756 757 if ($field_type) { 758 return !empty($options[$field_type]) ? $options[$field_type] : array(); 759 } 760 elseif ($by_label) { 761 $field_types = _content_field_types(); 762 $options_by_label = array(); 763 foreach ($options as $field_type => $widgets) { 764 $options_by_label[t($field_types[$field_type]['label'])] = $widgets; 765 } 766 return $options_by_label; 767 } 768 else { 769 return $options; 770 } 771 } 772 773 /** 774 * Return an array of existing field to be added to a node type. 775 */ 776 function content_existing_field_options($type_name) { 777 $type = content_types($type_name); 778 $fields = content_fields(); 779 $field_types = _content_field_types(); 780 781 $options = array(); 782 foreach ($fields as $field) { 783 if (!isset($type['fields'][$field['field_name']]) && !$field['locked']) { 784 $field_type = $field_types[$field['type']]; 785 $text = t('@type: @field (@label)', array('@type' => t($field_type['label']), '@label' => t($field['widget']['label']), '@field' => $field['field_name'])); 786 $options[$field['field_name']] = (drupal_strlen($text) > 80) ? truncate_utf8($text, 77) . '...' : $text; 787 } 788 } 789 // Sort the list by type, then by field name, then by label. 790 asort($options); 791 792 return $options; 793 } 794 795 /** 796 * A form element for selecting field, widget, and label. 797 */ 798 function content_field_basic_form(&$form_state, $form_values) { 799 module_load_include('inc', 'content', 'includes/content.crud'); 800 801 $type_name = $form_values['type_name']; 802 $type = content_types($form_values['type_name']); 803 $field_name = $form_values['field_name']; 804 $field_type = $form_values['type']; 805 $label = $form_values['label']; 806 807 $form = array(); 808 809 $form['basic'] = array( 810 '#type' => 'fieldset', 811 '#title' => t('Edit basic information'), 812 ); 813 $form['basic']['field_name'] = array( 814 '#title' => t('Field name'), 815 '#type' => 'textfield', 816 '#value' => $field_name, 817 '#description' => t("The machine-readable name of the field. This name cannot be changed."), 818 '#disabled' => TRUE, 819 ); 820 $form['basic']['label'] = array( 821 '#type' => 'textfield', 822 '#title' => t('Label'), 823 '#default_value' => $label, 824 '#required' => TRUE, 825 '#description' => t('A human-readable name to be used as the label for this field in the %type content type.', array('%type' => $type['name'])), 826 ); 827 $form['basic']['type'] = array( 828 '#type' => 'select', 829 '#title' => t('Field type'), 830 '#options' => content_field_type_options(), 831 '#default_value' => $field_type, 832 '#description' => t('The type of data you would like to store in the database with this field. This option cannot be changed.'), 833 '#disabled' => TRUE, 834 ); 835 $form['basic']['widget_type'] = array( 836 '#type' => 'select', 837 '#title' => t('Widget type'), 838 '#required' => TRUE, 839 '#options' => content_widget_type_options($field_type), 840 '#default_value' => $form_values['widget_type'], 841 '#description' => t('The type of form element you would like to present to the user when creating this field in the %type content type.', array('%type' => $type['name'])), 842 ); 843 844 $form['type_name'] = array( 845 '#type' => 'value', 846 '#value' => $type_name, 847 ); 848 849 $form['submit'] = array( 850 '#type' => 'submit', 851 '#value' => t('Continue'), 852 ); 853 854 $form['#validate'] = array(); 855 $form['#submit'] = array('content_field_basic_form_submit'); 856 857 return $form; 858 } 859 860 /** 861 * Create a new field for a content type. 862 */ 863 function content_field_basic_form_submit($form, &$form_state) { 864 $form_values = $form_state['values']; 865 866 $label = $form_values['label']; 867 868 // Set the right module information 869 $field_types = _content_field_types(); 870 $widget_types = _content_widget_types(); 871 $form_values['module'] = $field_types[$form_values['type']]['module']; 872 $form_values['widget_module'] = $widget_types[$form_values['widget_type']]['module']; 873 874 // Make sure we retain previous values and only over-write changed values. 875 module_load_include('inc', 'content', 'includes/content.crud'); 876 $instances = content_field_instance_read(array('field_name' => $form_values['field_name'], 'type_name' => $form_values['type_name'])); 877 $field = array_merge(content_field_instance_collapse($instances[0]), $form_values); 878 if (content_field_instance_update($field)) { 879 drupal_set_message(t('Updated basic settings for field %label.', array( 880 '%label' => $label))); 881 } 882 else { 883 drupal_set_message(t('There was a problem updating the basic settings for field %label.', array( 884 '%label' => $label))); 885 } 886 887 $type = content_types($form_values['type_name']); 888 $form_state['redirect'] = 'admin/content/node-type/'. $type['url_str'] .'/fields/'. $form_values['field_name']; 889 $form_state['rebuild'] = FALSE; 890 } 891 892 /** 893 * Menu callback; present a form for removing a field from a content type. 894 */ 895 function content_field_remove_form(&$form_state, $type_name, $field_name) { 896 $type = content_types($type_name); 897 $field = $type['fields'][$field_name]; 898 899 $form = array(); 900 $form['type_name'] = array( 901 '#type' => 'value', 902 '#value' => $type_name, 903 ); 904 $form['field_name'] = array( 905 '#type' => 'value', 906 '#value' => $field_name, 907 ); 908 909 $output = confirm_form($form, 910 t('Are you sure you want to remove the field %field?', array('%field' => $field['widget']['label'])), 911 'admin/content/node-type/'. $type['url_str'] .'/fields', 912 t('If you have any content left in this field, it will be lost. This action cannot be undone.'), 913 t('Remove'), t('Cancel'), 914 'confirm' 915 ); 916 917 if ($field['locked']) { 918 unset($output['actions']['submit']); 919 $output['description']['#value'] = t('This field is <strong>locked</strong> and cannot be removed.'); 920 } 921 922 return $output; 923 } 924 925 /** 926 * Remove a field from a content type. 927 */ 928 function content_field_remove_form_submit($form, &$form_state) { 929 module_load_include('inc', 'content', 'includes/content.crud'); 930 $form_values = $form_state['values']; 931 932 $type = content_types($form_values['type_name']); 933 $field = $type['fields'][$form_values['field_name']]; 934 if ($field['locked']) { 935 return; 936 } 937 938 if ($type && $field && $form_values['confirm']) { 939 if (content_field_instance_delete($form_values['field_name'], $form_values['type_name'])) { 940 drupal_set_message(t('Removed field %field from %type.', array( 941 '%field' => $field['widget']['label'], 942 '%type' => $type['name']))); 943 } 944 else { 945 drupal_set_message(t('There was a problem deleting %field from %type.', array( 946 '%field' => $field['widget']['label'], 947 '%type' => $type['name']))); 948 } 949 $form_state['redirect'] = 'admin/content/node-type/'. $type['url_str'] .'/fields'; 950 } 951 } 952 953 /** 954 * Menu callback; presents the field editing page. 955 */ 956 function content_field_edit_form(&$form_state, $type_name, $field_name) { 957 $output = ''; 958 $type = content_types($type_name); 959 $field = $type['fields'][$field_name]; 960 961 if ($field['locked']) { 962 $output = array(); 963 $output['locked'] = array( 964 '#value' => t('The field %field is locked and cannot be edited.', array('%field' => $field['widget']['label'])), 965 ); 966 return $output; 967 } 968 969 $field_types = _content_field_types(); 970 $field_type = $field_types[$field['type']]; 971 $widget_types = _content_widget_types(); 972 $widget_type = $widget_types[$field['widget']['type']]; 973 974 $title = isset($field['widget']['label']) ? $field['widget']['label'] : $field['field_name']; 975 drupal_set_title(check_plain($title)); 976 977 // See if we need to change the widget type or label. 978 if (isset($form_state['change_basic'])) { 979 module_load_include('inc', 'content', 'includes/content.crud'); 980 $field_values = content_field_instance_collapse($field); 981 return content_field_basic_form($form_state, $field_values); 982 } 983 984 $add_new_sequence = isset($_REQUEST['destinations']); 985 986 // Remove menu tabs when we are in an 'add new' sequence. 987 if ($add_new_sequence) { 988 menu_set_item(NULL, menu_get_item('node')); 989 } 990 991 $form = array(); 992 $form['#field'] = $field; 993 $form['#type'] = $type; 994 995 // Basic iformation : hide when we are in an 'add new' sequence. 996 $form['basic'] = array( 997 '#type' => 'fieldset', 998 '#title' => t('%type basic information', array('%type' => $type['name'])), 999 '#access' => !$add_new_sequence, 1000 ); 1001 $form['basic']['label'] = array( 1002 '#type' => 'textfield', 1003 '#title' => t('Label'), 1004 '#value' => $field['widget']['label'], 1005 '#disabled' => TRUE, 1006 ); 1007 $form['basic']['field_name'] = array( 1008 '#type' => 'hidden', 1009 '#title' => t('Field name'), 1010 '#value' => $field['field_name'], 1011 '#disabled' => TRUE, 1012 ); 1013 $form['basic']['type'] = array( 1014 '#type' => 'hidden', 1015 '#title' => t('Field type'), 1016 '#value' => $field['type'], 1017 '#disabled' => TRUE, 1018 ); 1019 $widget_options = content_widget_type_options($field['type']); 1020 $form['basic']['widget_type'] = array( 1021 '#type' => 'select', 1022 '#title' => t('Widget type'), 1023 '#options' => $widget_options, 1024 '#default_value' => $field['widget']['type'] ? $field['widget']['type'] : key($widget_options), 1025 '#disabled' => TRUE, 1026 ); 1027 $form['basic']['change'] = array( 1028 '#type' => 'submit', 1029 '#value' => t('Change basic information'), 1030 '#submit' => array('content_field_edit_form_submit_update_basic'), 1031 ); 1032 1033 $form['widget'] = array( 1034 '#type' => 'fieldset', 1035 '#title' => t('%type settings', array('%type' => $type['name'])), 1036 '#description' => t('These settings apply only to the %field field as it appears in the %type content type.', array( 1037 '%field' => $field['widget']['label'], 1038 '%type' => $type['name'])), 1039 ); 1040 $form['widget']['weight'] = array( 1041 '#type' => 'hidden', 1042 '#default_value' => $field['widget']['weight'], 1043 ); 1044 1045 $additions = (array) module_invoke($widget_type['module'], 'widget_settings', 'form', $field['widget']); 1046 drupal_alter('widget_settings', $additions, 'form', $field['widget']); 1047 $form['widget'] = array_merge($form['widget'], $additions); 1048 1049 $form['widget']['description'] = array( 1050 '#type' => 'textarea', 1051 '#title' => t('Help text'), 1052 '#default_value' => $field['widget']['description'], 1053 '#rows' => 5, 1054 '#description' => t('Instructions to present to the user below this field on the editing form.<br />Allowed HTML tags: @tags', array('@tags' => _content_filter_xss_display_allowed_tags())), 1055 '#required' => FALSE, 1056 ); 1057 1058 // Add handling for default value if not provided by field. 1059 if (content_callback('widget', 'default value', $field) == CONTENT_CALLBACK_DEFAULT) { 1060 1061 // Store the original default value for use in programmed forms. 1062 // Set '#default_value' instead of '#value' so programmed values 1063 // can override whatever we set here. 1064 $default_value = isset($field['widget']['default_value']) ? $field['widget']['default_value'] : array(); 1065 $default_value_php = isset($field['widget']['default_value_php']) ? $field['widget']['default_value_php'] : ''; 1066 $form['widget']['default_value'] = array( 1067 '#type' => 'value', 1068 '#default_value' => $default_value, 1069 ); 1070 $form['widget']['default_value_php'] = array( 1071 '#type' => 'value', 1072 '#default_value' => $default_value_php, 1073 ); 1074 1075 // We can't tell at the time we build the form if this is a programmed 1076 // form or not, so we always end up adding the default value widget 1077 // even if we won't use it. 1078 $form['widget']['default_value_fieldset'] = array( 1079 '#type' => 'fieldset', 1080 '#title' => t('Default value'), 1081 '#collapsible' => TRUE, 1082 '#collapsed' => TRUE, 1083 ); 1084 1085 // Default value widget. 1086 $widget_form = array('#node' => (object) array('type' => $type_name)); 1087 $widget_form_state = array('values' => array($field['field_name'] => $default_value)); 1088 // Make sure the default value is not a required field. 1089 $widget_field = $field; 1090 $widget_field['required'] = FALSE; 1091 module_load_include('inc', 'content', 'includes/content.node_form'); 1092 $form_element = content_field_form($widget_form, $widget_form_state, $widget_field, 0); 1093 $form['widget']['default_value_fieldset']['default_value_widget'] = $form_element; 1094 $form['widget']['default_value_fieldset']['default_value_widget']['#tree'] = TRUE; 1095 // Set up form info that the default value widget will need to find in the form. 1096 $form['#field_info'] = array($widget_field['field_name'] => $widget_field); 1097 1098 // Advanced: PHP code. 1099 $form['widget']['default_value_fieldset']['advanced_options'] = array( 1100 '#type' => 'fieldset', 1101 '#title' => t('PHP code'), 1102 '#collapsible' => TRUE, 1103 '#collapsed' => empty($field['widget']['default_value_php']), 1104 ); 1105 1106 if (user_access('Use PHP input for field settings (dangerous - grant with care)')) { 1107 $db_info = content_database_info($field); 1108 $columns = array_keys($db_info['columns']); 1109 foreach ($columns as $key => $column) { 1110 $columns[$key] = t("'@column' => value for @column", array('@column' => $column)); 1111 } 1112 $sample = t("return array(\n 0 => array(@columns),\n // You'll usually want to stop here. Provide more values\n // if you want your 'default value' to be multi-valued:\n 1 => array(@columns),\n 2 => ...\n);", array('@columns' => implode(', ', $columns))); 1113 1114 $form['widget']['default_value_fieldset']['advanced_options']['default_value_php'] = array( 1115 '#type' => 'textarea', 1116 '#title' => t('Code'), 1117 '#default_value' => isset($field['widget']['default_value_php']) ? $field['widget']['default_value_php'] : '', 1118 '#rows' => 6, 1119 '#tree' => TRUE, 1120 '#description' => t('Advanced usage only: PHP code that returns a default value. Should not include <?php ?> delimiters. If this field is filled out, the value returned by this code will override any value specified above. Expected format: <pre>!sample</pre>To figure out the expected format, you can use the <em>devel load</em> tab provided by <a href="@link_devel">devel module</a> on a %type content page.', array( 1121 '!sample' => $sample, 1122 '@link_devel' => 'http://www.drupal.org/project/devel', 1123 '%type' => $type_name)), 1124 ); 1125 } 1126 else { 1127 $form['widget']['default_value_fieldset']['advanced_options']['markup_default_value_php'] = array( 1128 '#type' => 'item', 1129 '#title' => t('Code'), 1130 '#value' => !empty($field['widget']['default_value_php']) ? '<code>'. check_plain($field['widget']['default_value_php']) .'</code>' : t('<none>'), 1131 '#description' => empty($field['widget']['default_value_php']) ? t("You're not allowed to input PHP code.") : t('This PHP code was set by an administrator and will override any value specified above.'), 1132 ); 1133 } 1134 } 1135 1136 $form['field'] = array( 1137 '#type' => 'fieldset', 1138 '#title' => t('Global settings'), 1139 '#description' => t('These settings apply to the %field field in every content type in which it appears.', array('%field' => $field['widget']['label'])), 1140 ); 1141 $form['field']['required'] = array( 1142 '#type' => 'checkbox', 1143 '#title' => t('Required'), 1144 '#default_value' => $field['required'], 1145 ); 1146 $description = t('Maximum number of values users can enter for this field.'); 1147 if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) { 1148 $description .= '<br/>'. t("'Unlimited' will provide an 'Add more' button so the users can add as many values as they like."); 1149 } 1150 $description .= '<br/><strong>'. t('Warning! Changing this setting after data has been created could result in the loss of data!') .'</strong>'; 1151 $form['field']['multiple'] = array( 1152 '#type' => 'select', 1153 '#title' => t('Number of values'), 1154 '#options' => array(1 => t('Unlimited'), 0 => 1) + drupal_map_assoc(range(2, 10)), 1155 '#default_value' => $field['multiple'], 1156 '#description' => $description, 1157 ); 1158 1159 $form['field']['previous_field'] = array( 1160 '#type' => 'hidden', 1161 '#value' => serialize($field), 1162 ); 1163 1164 $additions = (array) module_invoke($field_type['module'], 'field_settings', 'form', $field); 1165 drupal_alter('field_settings', $additions, 'form', $field); 1166 $form['field'] = array_merge($form['field'], $additions); 1167 1168 $form['submit'] = array( 1169 '#type' => 'submit', 1170 '#value' => t('Save field settings'), 1171 ); 1172 $form['type_name'] = array( 1173 '#type' => 'value', 1174 '#value' => $type_name, 1175 ); 1176 $form['field_name'] = array( 1177 '#type' => 'value', 1178 '#value' => $field_name, 1179 ); 1180 $form['type'] = array( 1181 '#type' => 'value', 1182 '#value' => $field['type'], 1183 ); 1184 $form['module'] = array( 1185 '#type' => 'value', 1186 '#value' => $field['module'], 1187 ); 1188 $form['widget']['label'] = array( 1189 '#type' => 'value', 1190 '#value' => $field['widget']['label'], 1191 ); 1192 $form['widget_module'] = array( 1193 '#type' => 'value', 1194 '#value' => $field['widget']['module'], 1195 ); 1196 $form['columns'] = array( 1197 '#type' => 'value', 1198 '#value' => $field['columns'], 1199 ); 1200 return $form; 1201 } 1202 1203 /** 1204 * Validate a field's settings. 1205 */ 1206 function content_field_edit_form_validate($form, &$form_state) { 1207 $form_values = $form_state['values']; 1208 if (isset($form_state['change_basic']) || $form_values['op'] == t('Change basic information')) { 1209 return; 1210 } 1211 1212 module_load_include('inc', 'content', 'includes/content.crud'); 1213 $previous_field = unserialize($form_values['previous_field']); 1214 $field = content_field_instance_expand($form_values); 1215 $field['db_storage'] = content_storage_type($field); 1216 1217 $field_types = _content_field_types(); 1218 $field_type = $field_types[$field['type']]; 1219 $widget_types = _content_widget_types(); 1220 $widget_type = $widget_types[$field['widget']['type']]; 1221 1222 if ($dropped_data = content_alter_db_analyze($previous_field, $field)) { 1223 // @TODO 1224 // This is a change that might result in loss of data. 1225 // Add a confirmation form here. 1226 // dsm($dropped_data); 1227 } 1228 1229 module_invoke($widget_type['module'], 'widget_settings', 'validate', array_merge($field, $form_values)); 1230 module_invoke($field_type['module'], 'field_settings', 'validate', array_merge($field, $form_values)); 1231 1232 // If content.module is handling the default value, 1233 // validate the result using the field validation. 1234 if (content_callback('widget', 'default value', $field) == CONTENT_CALLBACK_DEFAULT) { 1235 1236 // If this is a programmed form, get rid of the default value widget, 1237 // we have the default values already. 1238 if ($form['#programmed']) { 1239 form_set_value(array('#parents' => array('default_value_widget')), NULL, $form_state); 1240 return; 1241 } 1242 1243 if (isset($form_values['default_value_php']) && 1244 ($php = trim($form_values['default_value_php']))) { 1245 $error = FALSE; 1246 ob_start(); 1247 $return = eval($php); 1248 ob_end_clean(); 1249 if (!is_array($return)) { 1250 $error = TRUE; 1251 } 1252 else { 1253 foreach ($return as $item) { 1254 if (!is_array($item)) { 1255 $error = TRUE; 1256 break; 1257 } 1258 } 1259 } 1260 if ($error) { 1261 $db_info = content_database_info($field); 1262 $columns = array_keys($db_info['columns']); 1263 foreach ($columns as $key => $column) { 1264 $columns[$key] = t("'@column' => value for @column", array('@column' => $column)); 1265 } 1266 $sample = t("return array(\n 0 => array(@columns),\n // You'll usually want to stop here. Provide more values\n // if you want your 'default value' to be multi-valued:\n 1 => array(@columns),\n 2 => ...\n);", array('@columns' => implode(', ', $columns))); 1267 1268 form_set_error('default_value_php', t('The default value PHP code returned an incorrect value.<br/>Expected format: <pre>!sample</pre> Returned value: @value', array( 1269 '!sample' => $sample, 1270 '@value' => print_r($return, TRUE)))); 1271 return; 1272 } 1273 else { 1274 $default_value = $return; 1275 $is_code = TRUE; 1276 form_set_value(array('#parents' => array('default_value_php')), $php, $form_state); 1277 form_set_value(array('#parents' => array('default_value')), array(), $form_state); 1278 } 1279 } 1280 elseif (!empty($form_values['default_value_widget'])) { 1281 // Fields that handle their own multiple values may use an expected 1282 // value as the top-level key, so just pop off the top element. 1283 $key = array_shift(array_keys($form_values['default_value_widget'])); 1284 $default_value = $form_values['default_value_widget'][$key]; 1285 $is_code = FALSE; 1286 form_set_value(array('#parents' => array('default_value_php')), '', $form_state); 1287 form_set_value(array('#parents' => array('default_value')), $default_value, $form_state); 1288 } 1289 if (isset($default_value)) { 1290 $node = array(); 1291 $node[$form_values['field_name']] = $default_value; 1292 $field['required'] = FALSE; 1293 $field_function = $field_type['module'] .'_field'; 1294 1295 $errors_before = form_get_errors(); 1296 1297 // Widget now does its own validation, should be no need 1298 // to add anything for widget validation here. 1299 if (function_exists($field_function)) { 1300 $field_function('validate', $node, $field, $default_value, $form, NULL); 1301 } 1302 // The field validation routine won't set an error on the right field, 1303 // so set it here. 1304 $errors_after = form_get_errors(); 1305 if (count($errors_after) > count($errors_before)) { 1306 if (trim($form_values['default_value_php'])) { 1307 form_set_error('default_value_php', t("The PHP code for 'default value' returned @value, which is invalid.", array( 1308 '@value' => print_r($default_value, TRUE)))); 1309 } 1310 else { 1311 form_set_error('default_value', t('The default value is invalid.')); 1312 } 1313 } 1314 } 1315 } 1316 } 1317 1318 /** 1319 * Button submit handler. 1320 */ 1321 function content_field_edit_form_submit_update_basic($form, &$form_state) { 1322 $form_state['change_basic'] = TRUE; 1323 $form_state['rebuild'] = TRUE; 1324 } 1325 1326 /** 1327 * Save a field's settings after editing. 1328 */ 1329 function content_field_edit_form_submit($form, &$form_state) { 1330 module_load_include('inc', 'content', 'includes/content.crud'); 1331 $form_values = $form_state['values']; 1332 content_field_instance_update($form_values); 1333 1334 if (isset($_REQUEST['destinations'])) { 1335 drupal_set_message(t('Added field %label.', array('%label' => $form_values['label']))); 1336 $form_state['redirect'] = content_get_destinations($_REQUEST['destinations']); 1337 } 1338 else { 1339 drupal_set_message(t('Saved field %label.', array('%label' => $form_values['label']))); 1340 $type = content_types($form_values['type_name']); 1341 $form_state['redirect'] = 'admin/content/node-type/'. $type['url_str'] .'/fields'; 1342 } 1343 } 1344 1345 /** 1346 * Helper function to handle multipage redirects. 1347 */ 1348 function content_get_destinations($destinations) { 1349 $query = array(); 1350 $path = array_shift($destinations); 1351 if ($destinations) { 1352 $query['destinations'] = $destinations; 1353 } 1354 return array($path, $query); 1355 } 1356 1357 /** 1358 * Content Schema Alter 1359 * 1360 * Alter the database schema. 1361 * 1362 * TODO figure out an API-safe way to use batching to update the nodes that 1363 * will be affected by this change so the node_save() hooks will fire. 1364 * 1365 */ 1366 function content_alter_schema($previous_field, $new_field) { 1367 content_alter_db($previous_field, $new_field); 1368 } 1369 1370 /** 1371 * Schema Alter Analyze 1372 * 1373 * Analyze if changes will remove columns or delta values, thus losing data. 1374 * Do this so we can delete the data and fire the necessary hooks, before 1375 * we actually alter the schema. 1376 */ 1377 function content_alter_db_analyze($previous_field, $new_field) { 1378 $dropped = array(); 1379 // There is no loss of data if there was no previous data. 1380 if (empty($previous_field)) { 1381 return $dropped; 1382 } 1383 1384 // Analyze possible data loss from changes in storage type. 1385 if (!empty($previous_field) && !empty($new_field)) { 1386 // Changing from multiple to not multiple data, will cause loss of all 1387 // values greater than zero. 1388 if ($previous_field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD && 1389 $new_field['db_storage'] == CONTENT_DB_STORAGE_PER_CONTENT_TYPE) { 1390 $dropped['delta'] = 0; 1391 } 1392 // Changing from one multiple value to another will cause loss of all 1393 // values for deltas greater than or equal to the new multiple value. 1394 elseif (isset($previous_field['multiple']) && isset($new_field['multiple'])) { 1395 if ($previous_field['multiple'] > $new_field['multiple'] && 1396 $new_field['multiple'] > 1) { 1397 $dropped['delta'] = $new_field['multiple']; 1398 } 1399 } 1400 } 1401 1402 // Analyze possible data loss from changes in field columns. 1403 $previous_schema = !empty($previous_field) ? content_table_schema($previous_field) : array('fields' => array()); 1404 $new_schema = !empty($new_field) ? content_table_schema($new_field) : array('fields' => array()); 1405 $dropped_columns = array_diff(array_keys($previous_schema['fields']), array_keys($new_schema['fields'])); 1406 if ($dropped_columns) { 1407 $dropped['columns'] = $dropped_columns; 1408 } 1409 // if (empty($new_schema['fields'])) { 1410 // // No new columns, will lose all columns for a field. 1411 // foreach ($previous_schema['fields'] as $column => $attributes) { 1412 // $dropped['columns'][] = $column; 1413 // } 1414 // } 1415 // else { 1416 // // Check both old and new columns to see if we are deleting some columns for a field. 1417 // foreach ($previous_schema['fields'] as $column => $attributes) { 1418 // if (!isset($new_schema['fields'][$column])) { 1419 // $dropped['columns'][] = $column; 1420 // } 1421 // } 1422 // } 1423 1424 return $dropped; 1425 } 1426 1427 /** 1428 * Perform adds, alters, and drops as needed to synchronize the database with 1429 * new field definitions. 1430 */ 1431 function content_alter_db($previous_field, $new_field) { 1432 $ret = array(); 1433 1434 // One or the other of these must be valid. 1435 if (empty($previous_field) && empty($new_field)) { 1436 return $ret; 1437 } 1438 1439 // Gather relevant information : schema, table name... 1440 $previous_schema = !empty($previous_field) ? content_table_schema($previous_field) : array(); 1441 $new_schema = !empty($new_field) ? content_table_schema($new_field) : array(); 1442 if (!empty($previous_field)) { 1443 $previous_db_info = content_database_info($previous_field); 1444 $previous_table = $previous_db_info['table']; 1445 } 1446 if (!empty($new_field)) { 1447 $new_db_info = content_database_info($new_field); 1448 $new_table = $new_db_info['table']; 1449 } 1450 1451 // Deletion of a field instance: drop relevant columns and tables and return. 1452 if (empty($new_field)) { 1453 if ($previous_field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) { 1454 db_drop_table($ret, $previous_table); 1455 } 1456 else { 1457 foreach ($previous_schema['fields'] as $column => $attributes) { 1458 if (!in_array($column, array('nid', 'vid', 'delta'))) { 1459 db_drop_field($ret, $previous_table, $column); 1460 } 1461 } 1462 } 1463 content_alter_db_cleanup(); 1464 return $ret; 1465 } 1466 1467 // Check that content types that have fields do have a per-type table. 1468 if (!empty($new_field)) { 1469 $base_tablename = _content_tablename($new_field['type_name'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE); 1470 if (!db_table_exists($base_tablename)) { 1471 db_create_table($ret, $base_tablename, content_table_schema()); 1472 } 1473 } 1474 1475 // Create new table and columns, if not already created. 1476 if (!db_table_exists($new_table)) { 1477 db_create_table($ret, $new_table, $new_schema); 1478 } 1479 else { 1480 // Or add fields and/or indexes to an existing table. 1481 foreach ($new_schema['fields'] as $column => $attributes) { 1482 if (!in_array($column, array('nid', 'vid', 'delta'))) { 1483 // Create the column if it does not exist. 1484 if (!db_column_exists($new_table, $column)) { 1485 db_add_field($ret, $new_table, $column, $attributes); 1486 } 1487 // Create the index if requested to, and it does not exist. 1488 if (isset($new_schema['indexes'][$column]) && !content_db_index_exists($new_table, $column)) { 1489 db_add_index($ret, $new_table, $column, $new_schema['indexes'][$column]); 1490 } 1491 } 1492 } 1493 } 1494 1495 // If this is a new field, we're done. 1496 if (empty($previous_field)) { 1497 content_alter_db_cleanup(); 1498 return $ret; 1499 } 1500 1501 // If the previous table doesn't exist, we're done. 1502 // Could happen if someone tries to run a schema update from an 1503 // content.install update function more than once. 1504 if (!db_table_exists($previous_table)) { 1505 content_alter_db_cleanup(); 1506 return $ret; 1507 } 1508 1509 // If changing data from one schema to another, see if changes require that 1510 // we drop multiple values or migrate data from one storage type to another. 1511 $migrate_columns = array_intersect_assoc($new_schema['fields'], $previous_schema['fields']); 1512 unset($migrate_columns['nid'], $migrate_columns['vid'], $migrate_columns['delta']); 1513 1514 // If we're going from one multiple value a smaller one or to single, 1515 // drop all delta values higher than the new maximum delta value. 1516 // Not needed if the new multiple is unlimited or if the new table is the content table. 1517 if ($new_table != $base_tablename && $new_field['multiple'] < $previous_field['multiple'] && $new_field['multiple'] != 1) { 1518 db_query("DELETE FROM {". $new_table ."} WHERE delta >= ". max(1, $new_field['multiple'])); 1519 } 1520 1521 // If going from multiple to non-multiple, make sure the field tables have 1522 // the right database structure to accept migrated data. 1523 if ($new_field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) { 1524 if ($previous_field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD && count($previous_schema['fields'])) { 1525 // Already using per-field storage; change multiplicity if needed. 1526 if ($previous_field['multiple'] > 0 && $new_field['multiple'] == 0) { 1527 db_drop_field($ret, $new_table, 'delta'); 1528 db_drop_primary_key($ret, $new_table); 1529 db_add_primary_key($ret, $new_table, array('vid')); 1530 } 1531 else if ($previous_field['multiple'] == 0 && $new_field['multiple'] > 0) { 1532 db_add_field($ret, $new_table, 'delta', array( 1533 'type' => 'int', 1534 'unsigned' => TRUE, 1535 'not null' => TRUE, 1536 'default' => 0)); 1537 db_drop_primary_key($ret, $new_table); 1538 db_add_primary_key($ret, $new_table, array('vid', 'delta')); 1539 } 1540 } 1541 } 1542 1543 // Migrate data from per-content-type storage. 1544 if ($previous_field['db_storage'] == CONTENT_DB_STORAGE_PER_CONTENT_TYPE && 1545 $new_field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) { 1546 $columns = array_keys($migrate_columns); 1547 if ($new_field['multiple']) { 1548 db_query('INSERT INTO {'. $new_table .'} (vid, nid, delta, '. implode(', ', $columns) .') '. 1549 ' SELECT vid, nid, 0, '. implode(', ', $columns) .' FROM {'. $previous_table .'}'); 1550 } 1551 else { 1552 db_query('INSERT INTO {'. $new_table .'} (vid, nid, '. implode(', ', $columns) .') '. 1553 ' SELECT vid, nid, '. implode(', ', $columns) .' FROM {'. $previous_table .'}'); 1554 } 1555 foreach ($columns as $column_name) { 1556 db_drop_field($ret, $previous_table, $column_name); 1557 } 1558 } 1559 1560 // Migrate data from per-field storage, and drop per-field table. 1561 if ($previous_field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD && 1562 $new_field['db_storage'] == CONTENT_DB_STORAGE_PER_CONTENT_TYPE) { 1563 // In order to be able to use drupal_write_record, we need to 1564 // rebuild the schema now. 1565 content_alter_db_cleanup(); 1566 if ($previous_field['multiple']) { 1567 $result = db_query("SELECT * FROM {". $previous_table ."} c JOIN {node} n ON c.nid = n.nid WHERE delta = 0 AND n.type = '%s'", $new_field['type_name']); 1568 } 1569 else { 1570 $result = db_query("SELECT * FROM {". $previous_table ."} c JOIN {node} n ON c.nid = n.nid WHERE n.type = '%s'", $new_field['type_name']); 1571 } 1572 $record = array(); 1573 while ($data = db_fetch_array($result)) { 1574 $record['nid'] = $data['nid']; 1575 $record['vid'] = $data['vid']; 1576 if ($previous_field['multiple']) { 1577 $record['delta'] = $data['delta']; 1578 } 1579 foreach ($migrate_columns as $column => $attributes) { 1580 if (is_null($data[$column])) { 1581 $record[$column] = NULL; 1582 } 1583 else { 1584 $record[$column] = $data[$column]; 1585 // Prevent double serializtion in drupal_write_record. 1586 if (isset($attributes['serialize']) && $attributes['serialize']) { 1587 $record[$column] = unserialize($record[$column]); 1588 } 1589 } 1590 } 1591 if (db_result(db_query('SELECT COUNT(*) FROM {'. $new_table . 1592 '} WHERE vid = %d AND nid = %d', $data['vid'], $data['nid']))) { 1593 $keys = $new_field['multiple'] ? array('vid', 'delta') : array('vid'); 1594 drupal_write_record($new_table, $record, $keys); 1595 } 1596 else { 1597 drupal_write_record($new_table, $record); 1598 } 1599 } 1600 db_drop_table($ret, $previous_table); 1601 } 1602 1603 // Change modified columns that don't involve storage changes. 1604 foreach ($new_schema['fields'] as $column => $attributes) { 1605 if (isset($previous_schema['fields'][$column]) && 1606 $previous_field['db_storage'] == $new_field['db_storage']) { 1607 if ($attributes != $previous_schema['fields'][$column]) { 1608 if (!in_array($column, array('nid', 'vid', 'delta'))) { 1609 db_change_field($ret, $new_table, $column, $column, $attributes); 1610 } 1611 } 1612 } 1613 } 1614 1615 // Remove obsolete columns. 1616 foreach ($previous_schema['fields'] as $column => $attributes) { 1617 if (!isset($new_schema['fields'][$column])) { 1618 if (!in_array($column, array('nid', 'vid', 'delta'))) { 1619 db_drop_field($ret, $previous_table, $column); 1620 } 1621 } 1622 } 1623 1624 // TODO: debugging stuff - should be removed 1625 if (module_exists('devel')) { 1626 //dsm($ret); 1627 } 1628 return $ret; 1629 } 1630 1631 /** 1632 * Helper function for handling cleanup operations when schema changes are made. 1633 */ 1634 function content_alter_db_cleanup() { 1635 // Rebuild the whole database schema. 1636 // TODO: this could be optimized. We don't need to rebuild in *every case*... 1637 // Or do we? This affects the schema and menu and may have unfortunate 1638 // delayed effects if we don't clear everything out at this point. 1639 content_clear_type_cache(TRUE); 1640 } 1641 1642 /** 1643 * Helper function to order fields and groups when theming (preprocessing) 1644 * overview forms. 1645 * 1646 * The $form is passed by reference because we assign depths as parenting 1647 * relationships are sorted out. 1648 */ 1649 function _content_overview_order(&$form, $field_rows, $group_rows) { 1650 // Put weight and parenting values into a $dummy render structure 1651 // and let drupal_render figure out the corresponding row order. 1652 $dummy = array(); 1653 // Group rows: account for weight. 1654 if (module_exists('fieldgroup')) { 1655 foreach ($group_rows as $name) { 1656 $dummy[$name] = array('#weight' => $form[$name]['weight']['#value'], '#value' => $name .' '); 1657 } 1658 } 1659 // Field rows : account for weight and parenting. 1660 foreach ($field_rows as $name) { 1661 $dummy[$name] = array('#weight' => $form[$name]['weight']['#value'], '#value' => $name .' '); 1662 if (module_exists('fieldgroup')) { 1663 if ($parent = $form[$name]['parent']['#value']) { 1664 $form[$name]['#depth'] = 1; 1665 $dummy[$parent][$name] = $dummy[$name]; 1666 unset($dummy[$name]); 1667 } 1668 } 1669 } 1670 return $dummy ? explode(' ', trim(drupal_render($dummy))) : array(); 1671 } 1672 1673 /** 1674 * Batching process for changing the field schema, 1675 * running each affected node through node_save() first, to 1676 * fire all hooks. 1677 * 1678 * TODO This is just a placeholder for now because batching can't be safely 1679 * used with API hooks. Need to come back and figure out how to incorporate 1680 * this and get it working properly when the fields are altered via the API. 1681 */ 1682 function content_alter_fields($previous_field, $new_field) { 1683 // See what values need to be updated in the field data. 1684 $mask = content_alter_db_mask($previous_field, $new_field); 1685 1686 // We use batch processing to prevent timeout when updating a large number 1687 // of nodes. If there is no previous data to adjust, we can just go straight 1688 // to altering the schema, otherwise use batch processing to update 1689 // the database one node at a time, then update the schema. 1690 if (empty($mask)) { 1691 return content_alter_db($previous_field, $new_field); 1692 } 1693 $updates = array( 1694 'mask' => $mask['mask'], 1695 'alt_mask' => $mask['alt_mask'], 1696 'delta' => $mask['delta'], 1697 ); 1698 $batch = array( 1699 'operations' => array( 1700 array('content_field_batch_update', array($previous_field['field_name'] => $updates)), 1701 array('content_alter_db', array($previous_field, $new_field)) 1702 ), 1703 'finished' => '_content_alter_fields_finished', 1704 'title' => t('Processing'), 1705 'error_message' => t('The update has encountered an error.'), 1706 'file' => './'. drupal_get_path('module', 'content') .'/includes/content.admin.inc', 1707 ); 1708 batch_set($batch); 1709 if (!empty($url)) { 1710 batch_process($url, $url); 1711 } 1712 } 1713 1714 /** 1715 * Content Replace Fields 'finished' callback. 1716 */ 1717 function _content_alter_fields_finished($success, $results, $operations) { 1718 if ($success) { 1719 drupal_set_message(t('The database has been altered and data has been migrated or deleted.')); 1720 } 1721 else { 1722 drupal_set_message(t('An error occurred and database alteration did not complete.'), 'error'); 1723 $message = format_plural(count($results), '1 item successfully processed:', '@count items successfully processed:'); 1724 $message .= theme('item_list', $results); 1725 drupal_set_message($message); 1726 } 1727 } 1728 1729 /** 1730 * Create a mask for the column data that should be deleted in each field. 1731 * 1732 * This is a bit tricky. We could theoretically have some columns 1733 * that should be set to empty and others with valid info that should 1734 * not be emptied out. But if delta values > X are to be wiped out, they 1735 * need to wipe out even columns that still have values. And the NULL 1736 * values in these columns after the alteration may be enough to make 1737 * the item 'empty', as defined by hook_content_is_empty(), even if 1738 * some columns still have values, so all these things need to be tested. 1739 */ 1740 function content_alter_db_mask($previous_field, $new_field) { 1741 // Get an array of column values that will be dropped by this 1742 // schema change and create a mask to feed to content_batch_update. 1743 1744 $dropped = content_alter_db_analyze($previous_field, $new_field); 1745 if (empty($dropped)) { 1746 return array(); 1747 } 1748 $mask = array('mask' => array()); 1749 foreach (array_keys($previous_field['columns']) as $column_name) { 1750 // The basic mask will empty the dropped columns. 1751 if (isset($dropped['columns']) && in_array($column_name, $dropped['columns'])) { 1752 $mask['mask'][$column_name] = NULL; 1753 } 1754 // Over the delta we'll empty all columns. 1755 if (isset($dropped['delta'])) { 1756 $mask['alt_mask'][$column_name] = NULL; 1757 } 1758 } 1759 if (isset($dropped['delta'])) { 1760 $mask['delta'] = $dropped['delta']; 1761 } 1762 return $mask; 1763 } 1764 1765 /** 1766 * Content Field Batch Update Operation 1767 * 1768 * Find all nodes that contain a field and update their values. 1769 * 1770 * @param $updates 1771 * an array like: 1772 * 'field_name' => array( 1773 * 'mask' => array() 1774 * // Keyed array of column names and replacement values for use 1775 * // below delta, or for all values if no delta is supplied. 1776 * 'alt_mask' => array() 1777 * // Optional, keyed array of column names and replacement values for use 1778 * // at or above delta, if a delta is supplied. 1779 * 'delta' => # 1780 * // Optional, the number to use as the delta value where you switch from 1781 * // one mask to the other. 1782 * ), 1783 */ 1784 function content_field_batch_update($updates, &$context) { 1785 if (empty($field)) { 1786 $context['finished'] = 1; 1787 return; 1788 } 1789 $field_name = $updates['field_name']; 1790 $field = content_fields($field_name); 1791 1792 if (!isset($context['sandbox']['progress'])) { 1793 $db_info = content_database_info($field); 1794 1795 // Might run into non-existent tables when cleaning up a corrupted 1796 // database, like some of the old content storage changes in the 1797 // .install files. 1798 if (!db_table_exists($db_info['table'])) { 1799 return $context['finished'] = 1; 1800 } 1801 $nodes = array(); 1802 $result = db_query("SELECT nid FROM {". $db_info['table'] ."}"); 1803 while ($node = db_fetch_array($result)) { 1804 $nodes[] = $node['nid']; 1805 } 1806 $context['sandbox']['progress'] = 0; 1807 $context['sandbox']['max'] = count($nodes); 1808 $context['sandbox']['nodes'] = $nodes; 1809 } 1810 1811 // Process nodes by groups of 5. 1812 $count = min(5, count($context['sandbox']['nodes'])); 1813 1814 for ($i = 1; $i <= $count; $i++) { 1815 // For each nid, load the node, empty the column values 1816 // or the whole field, and re-save it. 1817 $nid = array_shift($context['sandbox']['nodes']); 1818 $node = content_field_replace($nid, array($updates)); 1819 1820 // Store result for post-processing in the finished callback. 1821 $context['results'][] = l($node->title, 'node/'. $node->nid); 1822 1823 // Update our progress information. 1824 $context['sandbox']['progress']++; 1825 $context['message'] = t('Processing %title', array('%title' => $node->title)); 1826 } 1827 1828 // Inform the batch engine that we are not finished, 1829 // and provide an estimation of the completion level we reached. 1830 if ($context['sandbox']['progress'] != $context['sandbox']['max']) { 1831 $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max']; 1832 } 1833 } 1834 1835 /** 1836 * Content Field Replace 1837 * 1838 * Replace field values in a node from an array of update values. 1839 * 1840 * Supply an array of one or more fields and masks of field column values 1841 * to be replaced into field values, one mask for basic values and an optional 1842 * different mask for values in field items equal to or higher than a 1843 * specified delta. 1844 * 1845 * The masks should contain only the column values to be substituted in. 1846 * The supplied values will be merged into the existing values to replace 1847 * only the values in the mask, leaving all other values unchanged. 1848 * 1849 * The ability to set different masks starting at a delta allows the 1850 * possibility of setting values above a certain delta to NULL prior 1851 * to altering the database schema. 1852 * 1853 * @param $nid 1854 * @param $updates 1855 * an array like: 1856 * 'field_name' => array( 1857 * 'mask' => array() 1858 * // Keyed array of column names and replacement values for use 1859 * // below delta, or for all values if no delta is supplied. 1860 * 'alt_mask' => array() 1861 * // Optional, keyed array of column names and replacement values for use 1862 * // at or above delta, if a delta is supplied. 1863 * 'delta' => # 1864 * // Optional, the number to use as the delta value where you switch from 1865 * // one mask to the other. 1866 * ), 1867 */ 1868 function content_field_replace($nid, $updates) { 1869 $node = node_load($nid, NULL, TRUE); 1870 foreach ($updates as $field_name => $update) { 1871 $items = isset($node->$field_name) ? $node->$field_name : array(); 1872 foreach ($items as $delta => $value) { 1873 $field_mask = (isset($update['delta']) && isset($update['alt_mask']) && $delta >= $update['delta']) ? $update['alt_mask'] : $mask['mask']; 1874 // Merge the mask into the field values to do the replacements. 1875 $items[$delta] = array_merge($items[$delta], $field_mask); 1876 } 1877 // Test if the new values will make items qualify as empty. 1878 $items = content_set_empty($field, $items); 1879 $node->$field_name = $items; 1880 } 1881 node_save($node); 1882 return $node; 1883 } 1884 1885 /** 1886 * Helper form element validator : integer. 1887 */ 1888 function _element_validate_integer($element, &$form_state) { 1889 $value = $element['#value']; 1890 if ($value !== '' && (!is_numeric($value) || intval($value) != $value)) { 1891 form_error($element, t('%name must be an integer.', array('%name' => $element['#title']))); 1892 } 1893 } 1894 1895 /** 1896 * Helper form element validator : integer > 0. 1897 */ 1898 function _element_validate_integer_positive($element, &$form_state) { 1899 $value = $element['#value']; 1900 if ($value !== '' && (!is_numeric($value) || intval($value) != $value || $value <= 0)) { 1901 form_error($element, t('%name must be a positive integer.', array('%name' => $element['#title']))); 1902 } 1903 } 1904 1905 /** 1906 * Helper form element validator : number. 1907 */ 1908 function _element_validate_number($element, &$form_state) { 1909 $value = $element['#value']; 1910 if ($value != '' && !is_numeric($value)) { 1911 form_error($element, t('%name must be a number.', array('%name' => $element['#title']))); 1912 } 1913 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |