source: sipes/modules_contrib/cck/includes/content.admin.inc @ 6e81fb4

stableversion-3.0
Last change on this file since 6e81fb4 was dafc8d8, checked in by José Gregorio Puentes <jpuentes@…>, 8 años ago

se actualizo el modulo

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