[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/cck/includes/ -> content.admin.inc (source)

   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>&lrm;',
 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>&lrm;',
 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 &lt;?php ?&gt; 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('&lt;none&gt;'),
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  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7