Index: theme/content.css =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/theme/content.css,v retrieving revision 1.12.2.3 diff -u -r1.12.2.3 content.css --- theme/content.css 22 Aug 2008 22:10:57 -0000 1.12.2.3 +++ theme/content.css 1 Sep 2008 18:19:40 -0000 @@ -51,14 +51,13 @@ } /* 'Manage fields' overview */ -table.content-field-overview, table.content-field-overview fieldset table { - width:100%; +tr.content-add-new .tabledrag-changed { + display: none; } -table.content-field-overview td { - width:14%; +tr.content-add-new .description { + margin-bottom:0; } -.content-field-overview-empty { - text-align:center; +.content-default-value, .form-item input.content-default-value.error { + color: #999; } - Index: modules/fieldgroup/fieldgroup.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/fieldgroup/fieldgroup.module,v retrieving revision 1.79.2.15 diff -u -r1.79.2.15 fieldgroup.module --- modules/fieldgroup/fieldgroup.module 1 Sep 2008 13:05:59 -0000 1.79.2.15 +++ modules/fieldgroup/fieldgroup.module 1 Sep 2008 18:19:40 -0000 @@ -351,8 +351,9 @@ '#value' => _fieldgroup_field_get_group($content_type['type'], $form['field_name']['#value']), ); } - elseif ($form_id == 'content_admin_field_overview_form' && !empty($form['#groups'])) { - $form['#submit'][] = 'fieldgroup_content_overview_form_submit'; + elseif ($form_id == 'content_admin_field_overview_form') { + $form['#validate'][] = 'fieldgroup_field_overview_form_validate'; + $form['#submit'][] = 'fieldgroup_field_overview_form_submit'; } elseif ($form_id == 'content_admin_display_overview_form' && !empty($form['#groups'])) { $form['#submit'][] = 'fieldgroup_display_overview_form_submit'; @@ -365,16 +366,61 @@ } } -function fieldgroup_content_overview_form_submit($form, &$form_state) { +function fieldgroup_field_overview_form_validate($form, &$form_state) { + $form_values = $form_state['values']; + $group = $form_values['_add_new_group']; + + // Fail validation if attempt to nest fields under a new group without a label. + if (!$group['label']) { + foreach ($form_values as $key => $values) { + if ($values['parent'] == '_add_new_group') { + form_set_error('_add_new_group][label', t('Add new group: you need to provide a label.')); + break; + } + } + } +} + +function fieldgroup_field_overview_form_submit($form, &$form_state) { $form_values = $form_state['values']; $type_name = $form['#type_name']; + + // Create new group if needed. + if (!empty($form_values['_add_new_group']['label'])) { + $group = $form_values['_add_new_group']; + $group['settings'] = _field_group_default_settings(); + $new_group_name = fieldgroup_save_group($type_name, $group); + } + + // Parse incoming rows. + $add_field_rows = array('_add_new_field', '_add_existing_field'); + $field_rows = array_merge($form['#fields'], $add_field_rows); foreach ($form_values as $key => $values) { - // Update field parenting. - if (in_array($key, $form['#fields'])) { + // If 'field' row: update field parenting. + if (in_array($key, $field_rows)) { + // If newly added fields were added to a group: + if (in_array($key, $add_field_rows)) { + // We replace the '_add_*_field' key with the actual name of + // the field that got added. + // content_admin_field_overview_form_submit() placed those + // in $form_state['fields_added'] for us. + if (isset($form_state['fields_added'][$key])) { + $key = $form_state['fields_added'][$key]; + } + else { + // No field was actually created : skip to next row. + continue; + } + } + // If the field was added to the newly created group, replace the + // '_add_new_group' value with the actual name of the group. + $parent = ($values['parent'] == '_add_new_group' && isset($new_group_name)) ? $new_group_name : $values['parent']; // TODO: check the parent group does exist ? - fieldgroup_update_fields(array('field_name' => $key, 'group' => $values['parent'], 'type_name' => $type_name)); + fieldgroup_update_fields(array('field_name' => $key, 'group' => $parent, 'type_name' => $type_name)); } - // Update groups weights. + + // If 'group' row: update groups weights + // (possible newly created group has already been taken care of). elseif (in_array($key, $form['#groups'])) { db_query("UPDATE {". fieldgroup_tablename() ."} SET weight = %d WHERE type_name = '%s' AND group_name = '%s'", $values['weight'], $type_name, $key); @@ -384,6 +430,17 @@ cache_clear_all('fieldgroup_data', content_cache_tablename()); } +function _field_group_default_settings() { + $settings = array( + 'form' => array('style' => 'fieldset', 'description' => ''), + 'display' => array('description' => '', 'label' => 'above'), + ); + foreach (array_keys(_content_admin_display_contexts(CONTENT_CONTEXTS_SIMPLE)) as $key) { + $settings['display'][$key] = 'fieldset'; + } + return $settings; +} + function fieldgroup_display_overview_form_submit($form, &$form_state) { $form_values = $form_state['values']; $groups = fieldgroup_groups($form['#type_name']); @@ -526,7 +583,7 @@ function fieldgroup_save_group($type_name, $group) { $groups = fieldgroup_groups($type_name); - if (!isset($groups[$group['group_name']])) { + if (!isset($group['group_name']) || !isset($groups[$group['group_name']])) { // Accept group name from programmed submissions if valid. if (!empty($group['group_name'])) { $group_name = $group['group_name']; @@ -550,14 +607,15 @@ db_query("INSERT INTO {". fieldgroup_tablename() ."} (type_name, group_name, label, settings, weight) VALUES ('%s', '%s', '%s', '%s', %d)", $type_name, $group_name, $group['label'], serialize($group['settings']), $group['weight']); cache_clear_all('fieldgroup_data', content_cache_tablename()); - return SAVED_NEW; + return $group_name; } else { + $group_name = $group['group_name']; db_query("UPDATE {". fieldgroup_tablename() ."} SET label = '%s', settings = '%s', weight = %d ". "WHERE type_name = '%s' AND group_name = '%s'", - $group['label'], serialize($group['settings']), $group['weight'], $type_name, $group['group_name']); + $group['label'], serialize($group['settings']), $group['weight'], $type_name, $group_name); cache_clear_all('fieldgroup_data', content_cache_tablename()); - return SAVED_UPDATED; + return $group_name; } } Index: includes/content.admin.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/includes/content.admin.inc,v retrieving revision 1.181.2.34 diff -u -r1.181.2.34 content.admin.inc --- includes/content.admin.inc 1 Sep 2008 14:38:12 -0000 1.181.2.34 +++ includes/content.admin.inc 1 Sep 2008 18:19:40 -0000 @@ -111,22 +111,18 @@ $fields = $type['fields']; $field_types = _content_field_types(); - if (empty($fields) && empty($form_state['post'])) { - drupal_set_message(t('There are no fields configured for this content type. You can !link.', array( - '!link' => l(t('Add a new field'), str_replace('/fields', '/add_field', $_GET['q'])))), 'warning'); - } - $extra = $type['extra']; $groups = $group_options = array(); if (module_exists('fieldgroup')) { $groups = fieldgroup_groups($type['type']); $group_options = _fieldgroup_groups_label($type['type']); + // Add the ability to group under the newly created row. + $group_options['_add_new_group'] = '_add_new_group'; } - // Rows in this table are essentially nested, but for the simplicity of - // theme and submit functions, we keep them in a flat array, and use a - // $dummy render structure to figure the right display order. - $dummy = array(); + // Store the default weights as we meet them, to be able to put the + //'add new' rows after them. + $weights = array(); $form = array( '#tree' => TRUE, @@ -142,8 +138,8 @@ foreach ($fields as $name => $field) { $weight = $field['widget']['weight']; $form[$name] = array( - 'human_name' => array('#value' => check_plain($field['widget']['label'])), - 'name' => array('#value' => $field['field_name']), + 'label' => array('#value' => check_plain($field['widget']['label'])), + 'field_name' => array('#value' => $field['field_name']), 'type' => array('#value' => t($field_types[$field['type']]['label'])), 'configure' => array('#value' => l(t('Configure'), 'admin/content/node-type/'. $type['url_str'] .'/fields/'. $field['field_name'])), 'remove' => array('#value' => l(t('Remove'), 'admin/content/node-type/'. $type['url_str'] .'/fields/'. $field['field_name'] .'/remove')), @@ -151,114 +147,289 @@ 'parent' => array('#type' => 'select', '#options' => $group_options, '#default_value' => ''), 'hidden_name' => array('#type' => 'hidden', '#default_value' => $field['field_name']), '#leaf' => TRUE, + '#row_type' => 'field', ); - $dummy[$name] = array('#weight' => $weight, '#value' => $name .' '); if ($field['locked']) { $form[$name]['configure'] = array('#value' => t('Locked')); $form[$name]['remove'] = array(); $form[$name]['#disabled_row'] = true; } + $weights[] = $weight; } // Groups. foreach ($groups as $name => $group) { $weight = $group['weight']; $form[$name] = array( - 'human_name' => array('#value' => check_plain($group['label'])), - 'name' => array('#value' => $group['group_name']), - 'type' => array(), + 'label' => array('#value' => check_plain($group['label'])), + 'group_name' => array('#value' => $group['group_name']), 'configure' => array('#value' => l(t('Configure'), 'admin/content/node-type/'. $type['url_str'] .'/groups/'. $group['group_name'])), 'remove' => array('#value' => l(t('Remove'), 'admin/content/node-type/'. $type['url_str'] .'/groups/'. $group['group_name'] .'/remove')), 'weight' => array('#type' => 'textfield', '#default_value' => $weight), 'parent' => array('#type' => 'hidden', '#default_value' => ''), 'hidden_name' => array('#type' => 'hidden', '#default_value' => $group['group_name']), '#root' => TRUE, + '#row_type' => 'group', ); - $dummy[$name] = array('#weight' => $weight, '#value' => $name .' '); // Adjust child fields rows. foreach ($group['fields'] as $field_name => $field) { - $form[$field_name]['#depth'] = 1; $form[$field_name]['parent']['#default_value'] = $name; - $dummy[$name][$field_name] = $dummy[$field_name]; - unset($dummy[$field_name]); } + $weights[] = $weight; } // Non-CCK 'fields'. foreach ($extra as $name => $label) { $weight = $extra[$name]['weight']; $form[$name] = array( - 'human_name' => array('#value' => t($extra[$name]['label'])), - 'name' => array('#value' => isset($extra[$name]['description']) ? $extra[$name]['description'] : ''), - 'type' => array(), - 'configure' => array(), - 'remove' => array(), + 'label' => array('#value' => t($extra[$name]['label'])), + 'description' => array('#value' => isset($extra[$name]['description']) ? $extra[$name]['description'] : ''), 'weight' => array('#type' => 'textfield', '#default_value' => $weight), 'parent' => array('#type' => 'hidden', '#default_value' => ''), 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), '#leaf' => TRUE, '#root' => TRUE, '#disabled_row' => TRUE, - '#extra_field' => TRUE, + '#row_type' => 'extra', + ); + $weights[] = $weight; + } + + // Additional row : add new field. + $weight = max($weights) + 1; + $field_type_options = content_field_type_options(); + $widget_type_options = content_widget_type_options(NULL, TRUE); + array_unshift($field_type_options, t('- Select a field type -')); + array_unshift($widget_type_options, t('- Select a widget -')); + if ($field_type_options && $widget_type_options) { + $name = '_add_new_field'; + $form[$name] = array( + 'label' => array( + '#type' => 'textfield', + '#size' => 15, + '#description' => t('Human-readable name.'), + ), + 'field_name' => array( + '#type' => 'textfield', + '#field_prefix' => 'field_', + '#size' => 15, + '#description' => t('Machine-readable name.'), + // '#description' => t('Machine-readable name, cannot be changed.'), + ), + 'type' => array( + '#type' => 'select', + '#options' => $field_type_options, + '#description' => t('Type of data to store.'), + ), + 'widget_type' => array( + '#type' => 'select', + '#options' => $widget_type_options, + '#description' => t('Form element to edit the data.'), + ), + 'weight' => array('#type' => 'textfield', '#default_value' => $weight), + 'parent' => array('#type' => 'select', '#options' => $group_options, '#default_value' => ''), + 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), + '#leaf' => TRUE, + '#add_new' => TRUE, + '#row_type' => $name, + ); + } + + // Additional row : add existing field. + $existing_field_options = content_existing_field_options($type_name); + if ($existing_field_options && $widget_type_options) { + $weight++; + array_unshift($existing_field_options, t('- Select an existing field -')); + $name = '_add_existing_field'; + $form[$name] = array( + 'label' => array( + '#type' => 'textfield', + '#size' => 15, + '#description' => t('Human-readable name.'), + ), + 'field_name' => array( + '#type' => 'select', + '#options' => $existing_field_options, + '#description' => t('Field to share.'), + ), + 'widget_type' => array( + '#type' => 'select', + '#options' => $widget_type_options, + '#description' => t('Form element to edit the data.'), + ), + 'weight' => array('#type' => 'textfield', '#default_value' => $weight), + 'parent' => array('#type' => 'select', '#options' => $group_options, '#default_value' => ''), + 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), + '#leaf' => TRUE, + '#add_new' => TRUE, + '#row_type' => $name, ); - $dummy[$name] = array('#weight' => $weight, '#value' => $name .' '); } - // Let drupal_render figure out the right order for the rows. - $form['#order'] = explode(' ', trim(drupal_render($dummy))); + // Additional row : add new group. + if (module_exists('fieldgroup')) { + $weight++; + $name = '_add_new_group'; + $form[$name] = array( + 'label' => array( + '#type' => 'textfield', + '#size' => 15, + '#description' => t('Human-readable name.'), + ), + 'weight' => array('#type' => 'textfield', '#default_value' => $weight), + 'parent' => array('#type' => 'hidden', '#default_value' => ''), + 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), + '#root' => TRUE, + '#add_new' => TRUE, + '#row_type' => $name, + ); + } $form['submit'] = array('#type' => 'submit', '#value' => t('Save')); return $form; } /** - * Theme the field overview table. + * Theme the field overview table. */ function theme_content_admin_field_overview_form($form) { - if (empty($form['#order'])) { - return; + // Put weight and parenting values into a $dummy render structure + // that will figure out the corresponding row order. + $dummy = array(); + $field_rows = array_merge($form['#fields'], $form['#extra'], array('_add_new_field', '_add_existing_field')); + $group_rows = array_merge($form['#groups'], array('_add_new_group')); + // Group rows: account for weight. + if (module_exists('fieldgroup')) { + foreach ($group_rows as $name) { + $dummy[$name] = array('#weight' => $form[$name]['weight']['#value'], '#value' => $name .' '); + } } + // Field rows : account for weight and parenting. + foreach ($field_rows as $name) { + $dummy[$name] = array('#weight' => $form[$name]['weight']['#value'], '#value' => $name .' '); + if (module_exists('fieldgroup')) { + if ($parent = $form[$name]['parent']['#value']) { + $form[$name]['#depth'] = 1; + $dummy[$parent][$name] = $dummy[$name]; + unset($dummy[$name]); + } + } + } + $order = explode(' ', trim(drupal_render($dummy))); + + // Fill the actual table rows. $header = array(t('Label'), t('Weight'), t('Name'), t('Type'), t('Operations')); $rows = array(); - foreach ($form['#order'] as $key) { + while ($order) { $row = array(); + $key = reset($order); $element = &$form[$key]; + + // Only display the 'Add' separator if the 'add' rows are still + // at the end of the table. + if (!isset($added_separator)) { + $remaining_rows = array_diff($order, array('_add_new_field', '_add_existing_field', '_add_new_group')); + if (empty($remaining_rows) && empty($element['#depth'])) { + $rows[] = array('data' => array(array('data' => t('Add'), 'colspan' => 4, 'class' => 'region')), 'class' => 'tabledrag-leaf region'); + $added_separator = TRUE; + } + } + $element['weight']['#attributes']['class'] = 'field-weight'; $element['parent']['#attributes']['class'] = 'group-parent'; $element['hidden_name']['#attributes']['class'] = 'field-name'; - if (in_array($key, $form['#groups'])) { - $element['human_name']['#prefix'] = ''; - $element['human_name']['#suffix'] = ''; - } - $row[] = theme('indentation', isset($element['#depth']) ? $element['#depth'] : 0) . drupal_render($element['human_name']); - // tabledrag.js cell hiding doesn't work well with colspans in table rows, - // so we add the cell to hide *before* the ones with colspans. - $row[] = drupal_render($element['weight']) . drupal_render($element['parent']) . drupal_render($element['hidden_name']); - if (isset($element['#extra_field'])) { - $row[] = array('data' => drupal_render($element['name']), 'colspan' => 3); - } - else { - $row[] = drupal_render($element['name']); - $row[] = drupal_render($element['type']); - $row[] = drupal_render($element['configure']) .'  '. drupal_render($element['remove']); + $indent = theme('indentation', isset($element['#depth']) ? $element['#depth'] : 0); + switch ($element['#row_type']) { + case 'field': + $row[] = $indent . drupal_render($element['label']); + // tabledrag.js cell hiding doesn't work well with colspans in table rows, + // so we add the cell to hide *before* the ones with colspans. + $row[] = drupal_render($element['weight']) . drupal_render($element['parent']) . drupal_render($element['hidden_name']); + $row[] = drupal_render($element['field_name']); + $row[] = drupal_render($element['type']); + $row[] = drupal_render($element['configure']) .'  '. drupal_render($element['remove']); + break; + case 'group': + $element['label']['#prefix'] = ''; + $element['label']['#suffix'] = ''; + $row[] = $indent . drupal_render($element['label']); + $row[] = drupal_render($element['weight']) . drupal_render($element['parent']) . drupal_render($element['hidden_name']); + $row[] = drupal_render($element['group_name']); + $row[] = ''; + $row[] = drupal_render($element['configure']) .'  '. drupal_render($element['remove']); + break; + case 'extra': + $row[] = $indent . drupal_render($element['label']); + $row[] = drupal_render($element['weight']) . drupal_render($element['parent']) . drupal_render($element['hidden_name']); + $row[] = array('data' => drupal_render($element['description']), 'colspan' => 3); + break; + case '_add_new_field'; + $element['type']['#attributes'] = array('class' => 'content-field-type-select'); + $element['widget_type']['#attributes'] = array('class' => 'content-widget-type-select'); + if (empty($element['label']['#value'])) { + $element['label']['#value'] = t('New field'); + $element['label']['#attributes'] = array('class' => 'content-default-value'); + } + $row[] = $indent . drupal_render($element['label']); + $row[] = drupal_render($element['weight']) . drupal_render($element['parent']) . drupal_render($element['hidden_name']); + $row[] = drupal_render($element['field_name']); + $row[] = drupal_render($element['type']); + $row[] = drupal_render($element['widget_type']); + break; + case '_add_existing_field': + $element['field_name']['#attributes'] = array('class' => 'content-field-select'); + $element['widget_type']['#attributes'] = array('class' => 'content-widget-type-select'); + $element['label']['#attributes'] = array('class' => 'content-label-textfield'); + if (empty($element['label']['#value'])) { + $element['label']['#value'] = t('Existing field'); + $element['label']['#attributes']['class'] .= ' content-default-value'; + } + $row[] = $indent . drupal_render($element['label']); + $row[] = drupal_render($element['weight']) . drupal_render($element['parent']) . drupal_render($element['hidden_name']); + $row[] = array('data' => drupal_render($element['field_name']), 'colspan' => 2); + $row[] = drupal_render($element['widget_type']); + break; + case '_add_new_group': + if (empty($element['label']['#value'])) { + $element['label']['#value'] = t('New group'); + $element['label']['#attributes'] = array('class' => 'content-default-value'); + } + $row[] = $indent . drupal_render($element['label']); + $row[] = drupal_render($element['weight']) . drupal_render($element['parent']) . drupal_render($element['hidden_name']); + $row[] = ''; + $row[] = ''; + $row[] = ''; + break; } $class = 'draggable'; $class .= isset($element['#disabled_row']) ? ' menu-disabled' : ''; + $class .= isset($element['#add_new']) ? ' content-add-new' : ''; $class .= isset($element['#leaf']) ? ' tabledrag-leaf' : ''; $class .= isset($element['#root']) ? ' tabledrag-root' : ''; $rows[] = array('data' => $row, 'class' => $class); + + array_shift($order); } $output = theme('table', $header, $rows, array('id' => 'content-field-overview')); - +// drupal_add_tabledrag('content-field-overview', 'match', 'parent', 'group-parent', 'group-parent', 'field-name', FALSE, 1); drupal_add_tabledrag('content-field-overview', 'match', 'parent', 'group-parent', 'group-parent', 'field-name', TRUE, 1); +// drupal_add_tabledrag('content-field-overview', 'order', 'sibling', 'field-weight', NULL, NULL, FALSE); drupal_add_tabledrag('content-field-overview', 'order', 'sibling', 'field-weight'); - // Hide the 'Save' button, show it when fields are swapped. - $form['submit']['#attributes']['class'] = 'content-admin-field-overview-submit'; - drupal_add_js('if (Drupal.jsEnabled) { $(document).ready(function() { $(".content-admin-field-overview-submit").hide(); }); }', 'inline'); + $js_fields = array(); + foreach(array_keys(content_existing_field_options($form['#type_name'])) as $field_name) { + $field = content_fields($field_name); + $js_fields[$field_name] = array('label' => $field['widget']['label'], 'type' => $field['type']); + } + + $js_setting = array( + 'contentWidgetTypes' => content_widget_type_options(), + 'contentFields' => $js_fields, + ); + drupal_add_js($js_setting, 'setting'); drupal_add_js(drupal_get_path('module', 'content') .'/content.js'); $output .= drupal_render($form); @@ -266,6 +437,118 @@ return $output; } +function content_admin_field_overview_form_validate($form, &$form_state) { + _content_admin_field_overview_form_validate_add_new($form, &$form_state); + _content_admin_field_overview_form_validate_add_existing($form, &$form_state); +// form_set_error('', 'foo'); +} + +/** + * Helper function for content_admin_field_overview_form_validate. + * + * Validate the 'add new field' row. + */ +function _content_admin_field_overview_form_validate_add_new($form, &$form_state) { + $field = $form_state['values']['_add_new_field']; + + // Validate if any information was provided in the 'add new field' row. + if (array_filter(array($field['label'], $field['field_name'], $field['type'], $field['widget_type']))) { + // No label. + if (!$field['label']) { + form_set_error('_add_new_field][label', t('Add new field: you need to provide a label.')); + } + + // No field name. + if (!$field['field_name']) { + form_set_error('_add_new_field][field_name', t('Add new field: you need to provide a field name.')); + } + // Field name validation. + else { + $field_name = $field['field_name']; + + // Add the 'field_' prefix. + if (substr($field_name, 0, 6) != 'field_') { + $field_name = 'field_'. $field_name; + form_set_value($form['_add_new_field']['field_name'], $field_name, $form_state); + } + + // Invalid field name. + if (!preg_match('!^field_[a-z0-9_]+$!', $field_name)) { + 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))); + } + if (strlen($field_name) > 32) { + 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))); + } + if ($field_name == 'field_instance') { + form_set_error('_add_new_field][field_name', t("Add new field: the name 'field_instance' is a reserved name.")); + } + + // Field name already exists. + // We need to check inactive fields as well, so we can't use content_fields(). + module_load_include('inc', 'content', 'includes/content.crud'); + $fields = content_field_instance_read(array(), TRUE); + $used = FALSE; + foreach ($fields as $existing_field) { + $used |= ($existing_field['field_name'] == $field_name); + } + if ($used) { + form_set_error('_add_new_field][field_name', t('Add new field: the field name %field_name already exists.', array('%field_name' => $field_name))); + } + } + + // No field type. + if (!$field['type']) { + form_set_error('_add_new_field][type', t('Add new field: you need to select a field type.')); + } + + // No widget type. + if (!$field['widget_type']) { + form_set_error('_add_new_field][widget_type', t('Add new field: you need to select a widget.')); + } + // Wrong widget type. + elseif ($field['type']) { + $widget_types = content_widget_type_options($field['type']); + if (!isset($widget_types[$field['widget_type']])) { + form_set_error('_add_new_field][widget_type', t('Add new field: invalid widget.')); + } + } + } +} + +/** + * Helper function for content_admin_field_overview_form_validate. + * + * Validate the 'add existing field' row. + */ +function _content_admin_field_overview_form_validate_add_existing($form, &$form_state) { + $field = $form_state['values']['_add_existing_field']; + + // Validate if any information was provided in the 'add existing field' row. + if (array_filter(array($field['label'], $field['field_name'], $field['widget_type']))) { + // No label. + if (!$field['label']) { + form_set_error('_add_existing_field][label', t('Add existing field: you need to provide a label.')); + } + + // No existing field. + if (!$field['field_name']) { + form_set_error('_add_existing_field][field_name', t('Add existing field: you need to select a field.')); + } + + // No widget type. + if (!$field['widget_type']) { + form_set_error('_add_existing_field][widget_type', t('Add existing field: you need to select a widget.')); + } + // Wrong widget type. + elseif ($field['field_name'] && ($existing_field = content_fields($field['field_name']))) { + $widget_types = content_widget_type_options($existing_field['type']); + if (!isset($widget_types[$field['widget_type']])) { + form_set_error('_add_existing_field][widget_type', t('Add existing field: invalid widget.')); + } + } + } +} + function content_admin_field_overview_form_submit($form, &$form_state) { $form_values = $form_state['values']; @@ -290,6 +573,49 @@ } content_clear_type_cache(); + + // TODO : weights > 1000 + + // Create new field. + if (!empty($form_values['_add_new_field']['field_name'])) { + $field = $form_values['_add_new_field']; + $field['type_name'] = $form['#type_name']; + + module_load_include('inc', 'content', 'includes/content.crud'); + if (content_field_instance_create($field)) { + drupal_set_message(t('Created field %label.', array( + '%label' => $field['label']))); + $form_state['fields_added']['_add_new_field'] = $field['field_name']; + } + else { + drupal_set_message(t('There was a problem creating field %label.', array( + '%label' => $field['label']))); + } + } + + // Add existing field. + if (!empty($form_values['_add_existing_field']['field_name'])) { + $field = $form_values['_add_existing_field']; + $field['type_name'] = $form['#type_name']; + $existing_field = content_fields($field['field_name']); + + if ($existing_field['locked']) { + drupal_set_message(t('The field %label cannot be added to a content type because it is locked.', + array('%label' => $field['field_name']))); + } + else { + module_load_include('inc', 'content', 'includes/content.crud'); + if (content_field_instance_create($field)) { + drupal_set_message(t('Added field %label.', array( + '%label' => $field['field_name']))); + $form_state['fields_added']['_add_existing_field'] = $field['field_name']; + } + else { + drupal_set_message(t('There was a problem adding field %label.', array( + '%label' => $field['field_name']))); + } + } + } } /** @@ -316,11 +642,6 @@ } $contexts = _content_admin_display_contexts($contexts_selector); - // Rows in this table are essentially nested, but for the simplicity of - // theme and submit functions, we keep them in a flat array, and use a - // $dummy render structure to figure the right display order. - $dummy = array(); - $form = array( '#tree' => TRUE, '#type_name' => $type['type'], @@ -342,7 +663,9 @@ $weight = $field['widget']['weight']; $form[$name] = array( - 'human_name' => array('#value' => check_plain($field['widget']['label'])), // OK + 'human_name' => array('#value' => check_plain($field['widget']['label'])), + 'weight' => array('#type' => 'value', '#value' => $weight), + 'parent' => array('#type' => 'value', '#value' => ''), ); // Label @@ -367,8 +690,6 @@ '#default_value' => isset($defaults[$key]['format']) ? $defaults[$key]['format'] : 'default', ); } - - $dummy[$name] = array('#weight' => $weight, '#value' => $name .' '); } // Groups. @@ -390,6 +711,7 @@ $form[$name] = array( 'human_name' => array('#value' => check_plain($group['label'])), + 'weight' => array('#type' => 'value', '#value' => $weight), ); if ($contexts_selector == CONTENT_CONTEXTS_SIMPLE) { $form[$name]['label'] = array( @@ -405,18 +727,11 @@ '#default_value' => isset($defaults[$key]) ? $defaults[$key] : 'fieldset', ); } - $dummy[$name] = array('#weight' => $weight, '#value' => $name .' '); - // Adjust child fields rows. foreach ($group['fields'] as $field_name => $field) { - $form[$field_name]['#depth'] = 1; - $dummy[$name][$field_name] = $dummy[$field_name]; - unset($dummy[$field_name]); + $form[$field_name]['parent']['#value'] = $name; } } - // Let drupal_render figure out the right order for the rows. - $form['#order'] = explode(' ', trim(drupal_render($dummy))); - $form['submit'] = array('#type' => 'submit', '#value' => t('Save')); return $form; } @@ -425,8 +740,32 @@ * Theme the display overview table. */ function theme_content_admin_display_overview_form($form) { + // Put weight and parenting values into a $dummy render structure + // that will figure out the corresponding row order. + $dummy = array(); + $field_rows = $form['#fields']; + $group_rows = $form['#groups']; + // Group rows: account for weight. + if (module_exists('fieldgroup')) { + foreach ($group_rows as $name) { + $dummy[$name] = array('#weight' => $form[$name]['weight']['#value'], '#value' => $name .' '); + } + } + // Field rows : account for weight and parenting. + foreach ($field_rows as $name) { + $dummy[$name] = array('#weight' => $form[$name]['weight']['#value'], '#value' => $name .' '); + if (module_exists('fieldgroup')) { + if ($parent = $form[$name]['parent']['#value']) { + $form[$name]['#depth'] = 1; + $dummy[$parent][$name] = $dummy[$name]; + unset($dummy[$name]); + } + } + } + $order = explode(' ', trim(drupal_render($dummy))); + $output = ''; - if (isset($form['#order'])) { + if (isset($order)) { $contexts_selector = $form['#contexts']; $contexts = _content_admin_display_contexts($contexts_selector); $header = array(t('Field')); @@ -438,7 +777,7 @@ } $rows = array(); - foreach ($form['#order'] as $key) { + foreach ($order as $key) { $row = array(); $element = &$form[$key]; if (in_array($key, $form['#groups'])) { @@ -524,36 +863,59 @@ /** * Return an array of widget type options for a field type. + * + * If no field type is provided, returns a nested array of + * all widget types, keyed by field type human name */ -function content_widget_type_options($field_type) { +function content_widget_type_options($field_type = NULL, $by_label = FALSE) { static $options; if (!isset($options)) { - $widget_types = _content_widget_types(); $options = array(); - foreach ($widget_types as $widget_type_name => $widget_type) { + foreach (_content_widget_types() as $widget_type_name => $widget_type) { foreach($widget_type['field types'] as $widget_field_type) { $options[$widget_field_type][$widget_type_name] = t($widget_type['label']); } } } - return !empty($options[$field_type]) ? $options[$field_type] : array(); + if ($field_type) { + return !empty($options[$field_type]) ? $options[$field_type] : array(); + } + elseif ($by_label) { + $field_types = _content_field_types(); + $options_by_label = array(); + foreach ($options as $field_type => $widgets) { + $options_by_label[t($field_types[$field_type]['label'])] = $widgets; + } + return $options_by_label; + } + else { + return $options; + } } -function _content_admin_field_add_existing(&$form_state, $type_name) { - $output = ''; +/** + * Return an array of existing field to be added to a node type. + */ +function content_existing_field_options($type_name) { $type = content_types($type_name); $fields = content_fields(); - $form = array(); + $field_types = _content_field_types(); $options = array(); foreach ($fields as $field) { if (!isset($type['fields'][$field['field_name']]) && !$field['locked']) { - $options[$field['field_name']] = t($field['widget']['label']) .' ('. $field['field_name'] .')'; + $field_type = $field_types[$field['type']]; + $options[$field['field_name']] = t('@type: @label (@field)', array('@type' => t($field_type['label']), '@label' => t($field['widget']['label']), '@field' => $field['field_name'])); } } - if ($options) { + return $options; +} + +function _content_admin_field_add_existing(&$form_state, $type_name) { + $form = array(); + if ($options = content_existing_field_options($type_name)) { $form['existing'] = array( '#type' => 'fieldset', '#title' => t('Add existing field'), Index: content.js =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/content.js,v retrieving revision 1.1 diff -u -r1.1 content.js --- content.js 2 Mar 2008 23:18:08 -0000 1.1 +++ content.js 1 Sep 2008 18:19:38 -0000 @@ -1,20 +1,101 @@ // $Id: content.js,v 1.1 2008/03/02 23:18:08 yched Exp $ -/** - * Show the Save button when fields are swapped. - * - * This behavior is dependent on the tableDrag behavior, since it uses the - * objects initialized in that behavior to update the row. - */ -Drupal.behaviors.cckFieldDrag = function(context) { - var tableDrag = Drupal.tableDrag['content-field-overview']; // Get the tableDrag object. - - // Add a handler for when a row is dropped, - // show Save button if the table has been changed. - tableDrag.onDrop = function() { - if (tableDrag.changed) { - $('.content-admin-field-overview-submit').fadeIn('slow'); - } - } + +Drupal.behaviors.cckManageFields = function(context) { + attachDefaultValues(context); + attachUpdateSelects(context); +}; + +/* (borrowed from quicksketch's webforms.module) */ +function attachDefaultValues(context) { + var fields = $('#content-field-overview .content-default-value', context); + var forms = fields.parents('form:first'); + fields.each(function() { + this.defaultValue = this.value; + $(this).focus(function() { + if (this.value == this.defaultValue) { + this.value = ''; + } + $(this).removeClass('content-default-value'); + }); + $(this).blur(function() { + if (this.value == '') { + $(this).addClass('content-default-value'); + this.value = this.defaultValue; + } + }); + }); + + // Clear all the form elements before submission. + forms.submit(function() { + fields.focus(); + }); }; +function attachUpdateSelects(context) { + var widgetTypes = Drupal.settings.contentWidgetTypes; + var fields = Drupal.settings.contentFields; + console.log + + // Store the default text of widget selects. + $('#content-field-overview .content-widget-type-select', context).each(function() { + this.initialValue = this.options[0].text; + }); + + // Field type select updates its widget select. + $('#content-field-overview .content-field-type-select', context).each(function() { + this.targetSelect = $('.content-widget-type-select', $(this).parents('tr')); + + $(this).change(function() { + selectedFieldType = this.options[this.selectedIndex].value; + this.targetSelect.contentOptions((selectedFieldType in widgetTypes) ? widgetTypes[selectedFieldType] : [ ]); + }); + + $(this).trigger('change'); + }); + + // Existing field select updates its widget select and label textfield. + $('#content-field-overview .content-field-select', context).each(function() { + this.targetSelect = $('.content-widget-type-select', $(this).parents('tr')); + this.targetTextfield = $('.content-label-textfield', $(this).parents('tr')); + + $(this).change(function(e, updateText) { + updateText = (typeof(updateText) == 'undefined') ? true : updateText; + selectedField = this.options[this.selectedIndex].value; + selectedFieldType = (selectedField in fields) ? fields[selectedField].type : null; + // TODO : we should preseect the widget used by the original field. + this.targetSelect.contentOptions((selectedFieldType && (selectedFieldType in widgetTypes)) ? widgetTypes[selectedFieldType] : [ ]); + + if (updateText) { + $(this.targetTextfield) + .attr('value', (selectedField in fields) ? fields[selectedField].label : '') + .focus().blur(); + $(this).focus(); + } + }); + + $(this).trigger('change', false); + }); +} + +jQuery.fn.contentOptions = function(options) { + return this.each(function() { + disabled = false; + if (options.length == 0) { + options = [this.initialValue]; + disabled = true; + } + + selectedValue = this.options[this.selectedIndex].value; + + html = ''; + jQuery.each(options, function(value, text) { + selected = (value == selectedValue) ? ' selected="selected"' : ''; + html += ''; + }); + + $(this) + .html(html) + .attr('disabled', disabled ? 'disabled' : ''); + }); +} \ No newline at end of file