source: sipei/modules/cck/includes/content.admin.inc @ fc0b1f8

drupal-6.x
Last change on this file since fc0b1f8 was ffa4103, checked in by Luis Peña <lpena@…>, 12 años ago

Cambiando el nombre de modulos a modules

  • Propiedad mode establecida a 100755
File size: 70.6 KB
Línea 
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 */
13function 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
59function 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 */
66function 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 */
107function 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 */
130function 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
370function 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 */
380function _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 */
453function _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
486function 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 */
569function 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 */
698function 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 */
721function 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 */
745function 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 */
776function 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 */
798function 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 */
863function 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 */
895function 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 */
928function 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 */
956function 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 */
1206function 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 */
1321function 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 */
1329function 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 */
1348function 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 */
1366function 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 */
1377function 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 */
1431function 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 */
1634function 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 */
1649function _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 */
1682function 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 */
1717function _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 */
1740function 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 */
1784function 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 */
1868function 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 */
1888function _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 */
1898function _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 */
1908function _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}
Nota: Vea TracBrowser para ayuda de uso del navegador del repositorio.