Index: content.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/Attic/content.info,v retrieving revision 1.6 diff -u -r1.6 content.info --- content.info 4 Jul 2007 23:46:29 -0000 1.6 +++ content.info 25 Feb 2009 16:34:09 -0000 @@ -2,4 +2,10 @@ name = Content description = Allows administrators to define new content types. package = CCK -core = 6.x \ No newline at end of file +core = 6.x +; Information added by drupal.org packaging script on 2009-02-14 +version = "6.x-2.x-dev" +core = "6.x" +project = "cck" +datestamp = "1234612969" + Index: content.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/Attic/content.module,v retrieving revision 1.301.2.97 diff -u -r1.301.2.97 content.module --- content.module 14 Feb 2009 01:16:53 -0000 1.301.2.97 +++ content.module 25 Feb 2009 16:34:09 -0000 @@ -490,8 +490,11 @@ 'data' => t('!title: !required', array('!title' => $element['#title'], '!required' => $required)), 'colspan' => 2 ), - t('Order'), - ); + array('data' => t('Order'), 'class' => 'content-multiple-weight-header'), + ); + if ($field['multiple'] == 1) { + $header[] = array('data' => t('Remove'), 'class' => 'content-multiple-remove-header'); + } $rows = array(); // Sort items according to '_weight' (needed when the form comes back after @@ -499,24 +502,31 @@ $items = array(); foreach (element_children($element) as $key) { if ($key !== $element['#field_name'] .'_add_more') { - $items[] = &$element[$key]; + $items[$element[$key]['#delta']] = &$element[$key]; } } - usort($items, '_content_sort_items_value_helper'); + uasort($items, '_content_sort_items_value_helper'); // Add the items as table rows. - foreach ($items as $key => $item) { + foreach ($items as $delta => $item) { $item['_weight']['#attributes']['class'] = $order_class; $delta_element = drupal_render($item['_weight']); + if ($field['multiple'] == 1) { + $remove_element = drupal_render($item['_remove']); + } $cells = array( array('data' => '', 'class' => 'content-multiple-drag'), drupal_render($item), array('data' => $delta_element, 'class' => 'delta-order'), ); - $rows[] = array( - 'data' => $cells, - 'class' => 'draggable', - ); + $row_class = 'draggable'; + if ($field['multiple'] == 1) { + if (!empty($item['_remove']['#default_value'])) { + $row_class .= ' content-multiple-removed-row'; + } + $cells[] = array('data' => $remove_element, 'class' => 'content-multiple-remove-cell'); + } + $rows[] = array('data' => $cells, 'class' => $row_class); } $output .= theme('table', $header, $rows, array('id' => $table_id, 'class' => 'content-multiple-table')); @@ -524,6 +534,7 @@ $output .= drupal_render($element[$element['#field_name'] .'_add_more']); drupal_add_tabledrag($table_id, 'order', 'sibling', $order_class); + drupal_add_js(drupal_get_path('module', 'content') .'/theme/content-edit.js'); } else { foreach (element_children($element) as $key) { @@ -724,8 +735,8 @@ case 'view': $addition = array(); - // Previewed nodes bypass the 'presave' op, so we need to some massaging. - if ($node->build_mode == NODE_BUILD_PREVIEW && content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) { + // Previewed nodes bypass the 'presave' op, so we need to do some massaging. + if ($node->build_mode == NODE_BUILD_PREVIEW) { if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) { // Reorder items to account for drag-n-drop reordering. $items = _content_sort_items($field, $items); @@ -770,10 +781,10 @@ ); // Fill-in items. - foreach ($items as $delta => $item) { + foreach (array_keys($items) as $weight => $delta) { $element['items'][$delta] = array( - '#item' => $item, - '#weight' => $delta, + '#item' => $items[$delta], + '#weight' => $weight, ); } @@ -893,20 +904,22 @@ * returns filtered and adjusted item array */ function content_set_empty($field, $items) { - // Filter out empty values. + // Prepare an empty item. + $empty = array(); + foreach (array_keys($field['columns']) as $column) { + $empty[$column] = NULL; + } + + // Filter out items flagged for removal. $filtered = array(); $function = $field['module'] .'_content_is_empty'; foreach ((array) $items as $delta => $item) { - if (!$function($item, $field)) { - $filtered[] = $item; + if (empty($item['_remove'])) { + $filtered[] = ($function($item, $field) ? $empty : $item); } } // Make sure we store the right number of 'empty' values. - $empty = array(); - foreach (array_keys($field['columns']) as $column) { - $empty[$column] = NULL; - } $pad = $field['multiple'] > 1 ? $field['multiple'] : 1; $filtered = array_pad($filtered, $pad, $empty); @@ -921,7 +934,7 @@ if ($field['multiple'] >= 1 && isset($items[0]['_weight'])) { usort($items, '_content_sort_items_helper'); foreach ($items as $delta => $item) { - if (is_array($items[$delta])) { + if (is_array($item) && isset($item['_weight'])) { unset($items[$delta]['_weight']); } } @@ -1002,7 +1015,17 @@ if (!isset($additions[$field_name])) { $additions[$field_name] = array(); } - $additions[$field_name][] = $item; + // When the node is loaded and saved programatically, we need to + // ensure all items are preserved during content_set_empty(). + $item['_remove'] = 0; + + // Preserve deltas when loading items from database. + if (isset($row['delta'])) { + $additions[$field_name][$row['delta']] = $item; + } + else { + $additions[$field_name][] = $item; + } } } } Index: includes/content.admin.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/includes/Attic/content.admin.inc,v retrieving revision 1.181.2.63 diff -u -r1.181.2.63 content.admin.inc --- includes/content.admin.inc 21 Jan 2009 20:24:19 -0000 1.181.2.63 +++ includes/content.admin.inc 25 Feb 2009 16:34:06 -0000 @@ -141,6 +141,7 @@ if (module_exists('fieldgroup')) { $groups = fieldgroup_groups($type['type']); $group_types = fieldgroup_types(); + $plain_tree = _fieldgroup_plain_tree($groups); $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'; @@ -188,6 +189,8 @@ // Groups. foreach ($groups as $name => $group) { + $current_group_options = $plain_tree; + unset($current_group_options[$name]); $weight = $group['weight']; $form[$name] = array( 'label' => array('#value' => check_plain($group['label'])), @@ -196,17 +199,23 @@ '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, '#size' => 3), - 'parent' => array('#type' => 'hidden', '#default_value' => ''), + 'parent' => array('#type' => 'select', '#options' => $current_group_options, '#default_value' => ''), + 'prev_parent' => array('#type' => 'hidden', '#value' => ''), 'hidden_name' => array('#type' => 'hidden', '#default_value' => $group['group_name']), - '#root' => TRUE, '#row_type' => 'group', 'group' => array('#type' => 'value', '#value' => $group), ); // Adjust child fields rows. - foreach ($group['fields'] as $field_name => $field) { - $form[$field_name]['parent']['#default_value'] = $name; - $form[$field_name]['prev_parent']['#value'] = $name; + if (isset($group['fields'])) { + foreach ($group['fields'] as $field_name => $field) { + $form[$field_name]['parent']['#default_value'] = $name; + $form[$field_name]['prev_parent']['#value'] = $name; + } } + // Adjust child group rows + $form[$name]['parent']['#default_value'] = $group['parent_group_name']; + $form[$name]['prev_parent']['#value'] = $group['parent_group_name']; + $form['#group_rows'][] = $name; $weights[] = $weight; } @@ -303,6 +312,9 @@ // Additional row : add new group. if (module_exists('fieldgroup')) { + $current_group_options = $group_options; + $options = fieldgroup_types(); + unset($current_group_options[_add_new_group]); $weight++; $name = '_add_new_group'; $form[$name] = array( @@ -326,12 +338,20 @@ '#value' => 'standard', ), 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), - 'parent' => array('#type' => 'hidden', '#default_value' => ''), + 'parent' => array('#type' => 'select', '#options' => $current_group_options, '#default_value' => ''), + 'prev_parent' => array('#type' => 'hidden', '#value' => ''), 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), - '#root' => TRUE, '#add_new' => TRUE, '#row_type' => 'add_new_group', ); + if (count($options) > 1) { + $form['_add_new_group']['group_type'] = array( + '#type' => 'select', + '#description' => t('Type of group.'), + '#options' => $options, + '#default_value' => 'standard', + ); + } $form['#group_rows'][] = $name; } @@ -635,6 +655,7 @@ $form[$name] = array( 'human_name' => array('#value' => check_plain($group['label'])), 'weight' => array('#type' => 'value', '#value' => $weight), + 'parent' => array('#type' => 'value', '#value' => ''), ); if ($contexts_selector == 'basic') { $form[$name]['label'] = array( @@ -659,6 +680,8 @@ foreach ($group['fields'] as $field_name => $field) { $form[$field_name]['parent']['#value'] = $name; } + $form[$name]['parent']['#value'] = $group['parent_group_name']; + $form[$name]['group']['#value']['depth'] = $group['depth']; } $form['submit'] = array('#type' => 'submit', '#value' => t('Save')); @@ -1600,12 +1623,28 @@ */ function _content_overview_order(&$form, $field_rows, $group_rows) { // Put weight and parenting values into a $dummy render structure - // and let drupal_render figure out the corresponding row order. $dummy = array(); // Group rows: account for weight. if (module_exists('fieldgroup')) { + $max_depth = 0; foreach ($group_rows as $name) { - $dummy[$name] = array('#weight' => $form[$name]['weight']['#value'], '#value' => $name .' '); + $depth = $form[$name]['group']['#value']['depth']; + if ($depth > $max_depth) { + $max_depth = $depth; + } + $parent = $form[$name]['parent']['#value']; + $pre_weight = $form[$name]['weight']['#value']; + if (isset($parent) && $parent != '') { + if ($pre_weight == 0) { + $pre_weight = 0.5; + } + $weight = $dummy[$parent]['#weight'] + ($pre_weight * pow(10, (-3 * $depth))); + } + else { + $weight = $pre_weight * pow(10, (-3 * $depth)); + } + $dummy[$name] = array('#weight' => $weight, '#value' => $name .' '); + $form[$name]['#depth'] = $depth; } } // Field rows : account for weight and parenting. @@ -1613,12 +1652,40 @@ $dummy[$name] = array('#weight' => $form[$name]['weight']['#value'], '#value' => $name .' '); if (module_exists('fieldgroup')) { if ($parent = $form[$name]['parent']['#value']) { - $form[$name]['#depth'] = 1; + $pre_weight = $form[$name]['weight']['#value']; + $depth = $form[$parent]['group']['#value']['depth'] + 1; + if (isset($parent) && $parent != '') { + if ($pre_weight == 0) { + $pre_weight = 0.5; + } + $weight = $dummy[$parent]['#weight'] + ($pre_weight * pow(10, (-3 * $depth))); + } + else { + $weight = $pre_weight * pow(10, (-3 * $depth)); + } + $dummy[$name] = array('#weight' => $weight, '#value' => $name .' '); + $form[$name]['#depth'] = $depth; $dummy[$parent][$name] = $dummy[$name]; unset($dummy[$name]); } } } + //we have to do this at the end so as to cascade these moves bottom-up rather than use a recursive function top-down + if (module_exists('fieldgroup')) { + while ($max_depth >= 0) { + foreach ($group_rows as $name) { + if ($form[$name]['group']['#value']['depth'] == $max_depth) { + $parent = $form[$name]['parent']['#value']; + if (isset($parent) && $parent != '') { + $dummy[$parent][$name] = $dummy[$name]; + unset($dummy[$name]); + } + } + } + $max_depth--; + } + } + //drupal_set_message('
Dummy: ' .print_r($dummy, TRUE). '
'); return $dummy ? explode(' ', trim(drupal_render($dummy))) : array(); } Index: includes/content.node_form.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/includes/Attic/content.node_form.inc,v retrieving revision 1.7.2.18 diff -u -r1.7.2.18 content.node_form.inc --- includes/content.node_form.inc 10 Feb 2009 22:53:04 -0000 1.7.2.18 +++ includes/content.node_form.inc 25 Feb 2009 16:34:06 -0000 @@ -151,21 +151,21 @@ switch ($field['multiple']) { case 0: + $deltas = array(0); $max = 0; break; case 1: - $filled_items = content_set_empty($field, $items); - $current_item_count = isset($form_state['item_count'][$field_name]) - ? $form_state['item_count'][$field_name] - : count($items); - // We always want at least one empty icon for the user to fill in. - $max = ($current_item_count > count($filled_items)) - ? $current_item_count - 1 - : $current_item_count; - + $deltas = array_keys($items); + $current_item_count = isset($form_state['item_count'][$field_name]) ? $form_state['item_count'][$field_name] : max(1, count($deltas)); + $max = (!empty($deltas) ? max($deltas) : -1); + while (count($deltas) < $current_item_count) { + $max++; + $deltas[] = $max; + } break; default: $max = $field['multiple'] - 1; + $deltas = array_keys(array_fill(0, $field['multiple'], 0)); break; } @@ -180,7 +180,7 @@ ); $function = $field['widget']['module'] .'_widget'; - for ($delta = 0; $delta <= $max; $delta++) { + foreach ($deltas as $delta) { if ($element = $function($form, $form_state, $field, $items, $delta)) { $defaults = array( '#title' => ($field['multiple'] >= 1) ? '' : $title, @@ -205,6 +205,21 @@ '#weight' => 100, ); } + + // Add a checkbox to allow users remove a single delta item. + // See content_set_empty() and theme_content_multiple_values(). + if ($field['multiple'] == 1) { + // We name the element '_remove' to avoid clashing with column names + // defined by field modules. + $element['_remove'] = array( + '#type' => 'checkbox', + '#attributes' => array( + 'class' => 'content-multiple-remove-checkbox', + 'alt' => 'type:'. $field['widget']['type'] .',name:'. $field_name .',delta:'. $delta, + ), + '#default_value' => isset($items[$delta]['_remove']) ? $items[$delta]['_remove'] : 0, + ); + } $form_element[$delta] = array_merge($element, $defaults); } @@ -264,7 +279,6 @@ } } - /** * Menu callback for AHAH addition of new empty widgets. */ @@ -313,23 +327,20 @@ unset($form_state['values'][$field_name][$field['field_name'] .'_add_more']); foreach ($_POST[$field_name] as $delta => $item) { $form_state['values'][$field_name][$delta]['_weight'] = $item['_weight']; + $form_state['values'][$field_name][$delta]['_remove'] = isset($item['_remove']) ? $item['_remove'] : 0; } $form_state['values'][$field_name] = _content_sort_items($field, $form_state['values'][$field_name]); $_POST[$field_name] = _content_sort_items($field, $_POST[$field_name]); // Build our new form element for the whole field, asking for one more element. + $delta = max(array_keys($_POST[$field_name])) + 1; $form_state['item_count'] = array($field_name => count($_POST[$field_name]) + 1); $form_element = content_field_form($form, $form_state, $field); // Let other modules alter it. drupal_alter('form', $form_element, array(), 'content_add_more_js'); // Add the new element at the right place in the (original, unbuilt) form. - if (module_exists('fieldgroup') && ($group_name = _fieldgroup_field_get_group($type['type'], $field_name))) { - $form[$group_name][$field_name] = $form_element[$field_name]; - } - else { - $form[$field_name] = $form_element[$field_name]; - } + content_set_form_element($field_name, $type['type'], $form, $form_element[$field_name]); // Save the new definition of the form. $form_state['values'] = array(); @@ -337,7 +348,6 @@ // Build the new form against the incoming $_POST values so that we can // render the new element. - $delta = max(array_keys($_POST[$field_name])) + 1; $_POST[$field_name][$delta]['_weight'] = $delta; $form_state = array('submitted' => FALSE); $form += array( @@ -347,7 +357,8 @@ $form = form_builder($_POST['form_id'], $form, $form_state); // Render the new output. - $field_form = (!empty($group_name)) ? $form[$group_name][$field_name] : $form[$field_name]; + $field_form = content_get_form_element($field_name, $type['type'], $form); + // We add a div around the new content to receive the ahah effect. $field_form[$delta]['#prefix'] = '
'. (isset($field_form[$delta]['#prefix']) ? $field_form[$delta]['#prefix'] : ''); $field_form[$delta]['#suffix'] = (isset($field_form[$delta]['#suffix']) ? $field_form[$delta]['#suffix'] : '') .'
'; @@ -363,7 +374,6 @@ $output_js = isset($javascript['setting']) ? '' : ''; $output = theme('status_messages') . drupal_render($field_form) . $output_js; - // Using drupal_json() breaks filefield's file upload, because the jQuery // Form plugin handles file uploads in a way that is not compatible with // 'text/javascript' response type. @@ -371,3 +381,74 @@ print drupal_to_js(array('status' => TRUE, 'data' => $output)); exit; } + +/** + * Store an element into a form. + * + * @param $name + * The field name. + * @param $type + * The content type where the field instance belongs to. + * @param $form + * The form to store this field element into. + * @param $element + * The form element to store. + */ +function content_set_form_element($name, $type, &$form, $element, $is_group = FALSE) { + if (module_exists('fieldgroup') && ($parents = _fieldgroup_field_get_parents($type, $name, $is_group))) { + foreach (module_implements('fieldgroup_parents_alter') as $module) { + $parents = call_user_func($module .'_fieldgroup_parents_alter', $form, $parents, $type, $name); + } + $reference = &$form; + if ($is_group) { + array_shift($parents); + $parents = array_reverse($parents); + } + else { + $parents = array_reverse($parents); + } + + foreach (array_values($parents) as $group_name) { + $reference = &$reference[$group_name]; + } + + $reference[$name] = $element; + + } + else { + $form[$name] = $element; + } +} + +/** + * Retrieve an element from a form. + * + * @param $name + * The field name. + * @param $type + * The content type where the field instance belongs to. + * @param $form + * The form to retrieve this field element from. + */ +function content_get_form_element($name, $type, $form, $is_group = FALSE) { + if (module_exists('fieldgroup') && ($parents = _fieldgroup_field_get_parents($type, $name, $is_group))) { + foreach (module_implements('fieldgroup_parents_alter') as $module) { + $parents = call_user_func($module .'_fieldgroup_parents_alter', $form, $parents, $type, $name); + } + $reference = &$form; + if ($is_group) { + array_shift($parents); + $parents = array_reverse($parents); + } + else { + $parents = array_reverse($parents); + } + + foreach (array_values($parents) as $group_name) { + $reference = &$reference[$group_name]; + } + + return $reference[$name]; + } + return $form[$name]; +} \ No newline at end of file Index: modules/content_copy/content_copy.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/content_copy/Attic/content_copy.info,v retrieving revision 1.6 diff -u -r1.6 content_copy.info --- modules/content_copy/content_copy.info 23 Apr 2008 18:01:48 -0000 1.6 +++ modules/content_copy/content_copy.info 25 Feb 2009 16:34:07 -0000 @@ -3,4 +3,10 @@ description = Enables ability to import/export field definitions. dependencies[] = content package = CCK -core = 6.x \ No newline at end of file +core = 6.x +; Information added by drupal.org packaging script on 2009-02-14 +version = "6.x-2.x-dev" +core = "6.x" +project = "cck" +datestamp = "1234612969" + Index: modules/content_copy/content_copy.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/content_copy/Attic/content_copy.module,v retrieving revision 1.27.2.20 diff -u -r1.27.2.20 content_copy.module --- modules/content_copy/content_copy.module 30 Jan 2009 20:13:49 -0000 1.27.2.20 +++ modules/content_copy/content_copy.module 25 Feb 2009 16:34:07 -0000 @@ -149,6 +149,7 @@ $form[$name] = array( 'human_name' => array('#value' => check_plain($group['label'])), 'group_name' => array('#value' => $group['group_name']), + 'parent_group_name' => array('#value' => $group['parent_group_name']), 'weight' => array('#type' => 'value', '#value' => $weight), '#row_type' => 'group', ); @@ -303,7 +304,7 @@ $form['#prefix'] = t('This form will import field definitions exported from another content type or another database.
Note that fields cannot be duplicated within the same content type, so imported fields will be added only if they do not already exist in the selected type.'); $form['type_name'] = array( '#type' => 'select', - '#options' => array('' => t('')) + content_copy_types(), + '#options' => array('' => t('')) + node_get_types('names'), '#default_value' => $type_name, '#title' => t('Content type'), '#description' => t('Select the content type to import these fields into.
Select <Create> to create a new content type to contain the fields.'), Index: modules/content_multigroup/README.txt =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/content_multigroup/Attic/README.txt,v retrieving revision 1.1.2.2 diff -u -r1.1.2.2 README.txt --- modules/content_multigroup/README.txt 14 Oct 2008 15:28:14 -0000 1.1.2.2 +++ modules/content_multigroup/README.txt 25 Feb 2009 16:34:07 -0000 @@ -1,19 +1,5 @@ ; $Id: README.txt,v 1.1.2.2 2008/10/14 15:28:14 karens Exp $ -THIS MODULE IS STILL EXPERIMENTAL AND NOT READY FOR PRODUCTION! -=================================================================== - -To try it out, add a content_multigroup.info file to the module. -The info file should be a plain text file that looks like: -=================================================================== -name = Content Multigroup -description = Combine multiple CCK fields into repeating field collections that work in unison. This module is experimental and not ready for production. -dependencies[] = content -dependencies[] = fieldgroup -package = CCK -core = 6.x -=================================================================== - USING MULTIGROUPS The Multigroup group treats all included fields like a single field, Index: modules/content_multigroup/content_multigroup.css =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/content_multigroup/Attic/content_multigroup.css,v retrieving revision 1.1.2.1 diff -u -r1.1.2.1 content_multigroup.css --- modules/content_multigroup/content_multigroup.css 14 Oct 2008 15:16:50 -0000 1.1.2.1 +++ modules/content_multigroup/content_multigroup.css 25 Feb 2009 16:34:07 -0000 @@ -1,9 +1,42 @@ /* $Id: content_multigroup.css,v 1.1.2.1 2008/10/14 15:16:50 karens Exp $ */ label.content-multigroup { - font-weight:bold; -} + font-weight: bold; +} -/* Not styled by default, but available to style */ +/* Not styled by default, but available to style. */ hr.content-multigroup { -} \ No newline at end of file +} + +/* Inline field labels visible within the content of multigroups. */ +.content-multigroup-wrapper .field .field-label-inline { + visibility: visible; +} + +/** + * Hide field labels and description on the node edit form when the multiple + * columns option is enabled. + */ +.content-multigroup-edit-table-multiple-columns label, +.content-multigroup-edit-table-multiple-columns .description { + display: none; +} + +/* Hide field labels when using 'table-multiple' display mode. */ +.content-multigroup-display-table-multiple-columns .field .field-label, +.content-multigroup-display-table-multiple-columns .field .field-label-inline, +.content-multigroup-display-table-multiple-columns .field .field-label-inline-first { + display: none; +} + +/* Display table with a row for each subgroup and all fields in a single column. */ +.content-multigroup-display-table-single-column .content-multigroup-wrapper { + clear: both; +} +.content-multigroup-display-table-single-column .content-multigroup-wrapper label.content-multigroup { + display: block; +} +.content-multigroup-display-table-single-column .content-multigroup-wrapper .field { + float: left; + margin-right: 1em; +} Index: modules/content_multigroup/content_multigroup.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/content_multigroup/Attic/content_multigroup.module,v retrieving revision 1.1.2.4 diff -u -r1.1.2.4 content_multigroup.module --- modules/content_multigroup/content_multigroup.module 22 Oct 2008 11:02:41 -0000 1.1.2.4 +++ modules/content_multigroup/content_multigroup.module 25 Feb 2009 16:34:07 -0000 @@ -4,14 +4,11 @@ /** * @file * Create complex, repeating groups of CCK fields that work in unison. - * - * @See README.txt. - * */ function content_multigroup_help($path, $arg) { switch ($path) { - case ('admin/help#content_multigroup'): + case 'admin/help#content_multigroup': return t('The fields in a Standard group are independent of each other and each can have either single or multiple values. The fields in a Multigroup are treated as a repeating collection of single value fields.'); } } @@ -37,16 +34,31 @@ 'content_multigroup_display_simple' => array( 'arguments' => array('element' => NULL), ), + 'content_multigroup_display_fieldset' => array( + 'arguments' => array('element' => NULL), + ), 'content_multigroup_display_hr' => array( 'arguments' => array('element' => NULL), ), - 'content_multigroup_display_table' => array( + 'content_multigroup_display_table_single' => array( + 'arguments' => array('element' => NULL), + ), + 'content_multigroup_display_table_multiple' => array( 'arguments' => array('element' => NULL), ), ); } /** + * Implementation of hook_elements(). + */ +function content_multigroup_elements() { + return array( + 'content_multigroup_display_fieldset' => array('#value' => NULL), + ); +} + +/** * Implementation of hook_fieldgroup_types(). */ function content_multigroup_fieldgroup_types() { @@ -61,7 +73,7 @@ module_load_include('inc', 'content', 'includes/content.admin'); $settings = array('multigroup' => array('multiple' => 1)); foreach (array_keys(content_build_modes()) as $key) { - $settings['multigroup']['display_settings'][$key]['format'] = 'fieldset'; + $settings['display'][$key]['format'] = 'fieldset'; } return $settings; } @@ -105,30 +117,17 @@ } } elseif ($form_id == 'content_field_overview_form') { - content_multigroup_field_overview_form($form, $form_state); $form['#validate'][] = 'content_multigroup_field_overview_form_validate'; } - elseif ($form_id == 'content_display_overview_form') { + elseif ($form_id == 'content_display_overview_form' && !empty($form['#groups'])) { content_multigroup_display_overview_form($form, $form_state, $form_id); $form['#submit'] = array_merge(array('content_multigroup_display_overview_form_submit'), $form['#submit']); } elseif ($form_id == 'fieldgroup_group_edit_form') { - return content_multigroup_group_edit_form($form, $form_state, $form_id); + content_multigroup_group_edit_form($form, $form_state, $form_id); } } -function content_multigroup_field_overview_form(&$form, &$form_state) { - $options = fieldgroup_types(); - $options['standard'] = t('Standard'); - $options['multigroup'] = t('Multigroup'); - $form['_add_new_group']['group_type'] = array( - '#type' => 'select', - '#description' => t('Type of group.'), - '#options' => $options, - '#default_value' => 'standard', - ); -} - /** * Validation for creating/moving fields and groups on the * Manage Fields screen. @@ -144,7 +143,7 @@ if (array_filter(array($group['label'], $group['group_name']))) { $group['settings'] = field_group_default_settings($form_values['_add_new_group']['group_type']); - $group = $form_values['_add_new_group']; + //$group = $form_values['_add_new_group'];//why a second one that overrides the assignment above? $validation = fieldgroup_validate_name($group, $form['#type_name']); // If there's something wrong with the new group, @@ -170,7 +169,8 @@ if (!empty($form[$key]['#row_type']) && $form[$key]['#row_type'] == 'group') { // Gather up info about all groups. $group_name = $form_values[$key]['group']['group_name']; - $groups[$group_name] = $form_values[$key]['group']; + $groups[$group_name] = $form_values[$key]; + $group_rows[$group_name] = $group_name; } if (!empty($form[$key]['#row_type']) && $form[$key]['#row_type'] == 'field') { if ($values['prev_parent'] != $values['parent']) { @@ -179,40 +179,49 @@ } } } + + foreach ($groups as $key => $values) { + if (in_array($key, $group_rows)) { + if (!empty($groups[$key]['parent']) && $groups[$groups[$key]['parent']]['group']['group_type'] == 'multigroup') { + $error_message = t('You cannot place any kind of group inside a multigroup. ' . $key . ' was moved back to where it started.'); + form_set_value($form[$key]['weight'], $form[$key]['weight']['#default_value'], $form_state); + form_set_value($form[$key]['parent'], $form[$key]['parent']['#default_value'], $form_state); + drupal_set_message($error_message, 'error'); + } + } + } - if (!empty($fields)) { - foreach ($fields as $field_name => $field) { - $new_group = $form_values[$field_name]['parent']; - $old_group = $form_values[$field_name]['prev_parent']; - if (!empty($new_group) && isset($groups[$new_group]) && $groups[$new_group]['group_type'] == 'multigroup') { - $allowed_in = content_multigroup_allowed_in($field, $groups[$new_group]); - if (!$allowed_in['allowed']) { - form_set_error($field_name, $allowed_in['message']); - } - else { - if (!empty($allowed_in['message'])) { - drupal_set_message($allowed_in['message']); - } - module_load_include('inc', 'content', 'includes/content.crud'); - $content_type = content_types($type_name); - $multiple = $groups[$new_group]['settings']['multigroup']['multiple']; - $multiple_values = content_multigroup_multiple_values(); - $field = $content_type['fields'][$field_name]; - $field['multiple'] = $multiple; - $field = content_field_instance_collapse($field); - content_field_instance_update($field); - drupal_set_message(t('The field %field has been updated to use %multiple values, to match the multiple value setting of the Multigroup %group.', array( - '%field' => $field['label'], '%multiple' => $multiple_values[$multiple], '%group' => $groups[$new_group]['label']))); - } + foreach ($fields as $field_name => $field) { + $new_group = $form_values[$field_name]['parent']; + $old_group = $form_values[$field_name]['prev_parent']; + if (!empty($new_group) && isset($groups[$new_group]['group']) && $groups[$new_group]['group']['group_type'] == 'multigroup') { + $allowed_in = content_multigroup_allowed_in($field, $groups[$new_group]['group']); + if (!$allowed_in['allowed']) { + form_set_error($field_name, $allowed_in['message']); } - elseif (!empty($old_group) && isset($groups[$old_group]) && $groups[$old_group]['group_type'] == 'multigroup') { - $allowed_out = content_multigroup_allowed_out($field, $groups[$old_group]); - if (!$allowed_out['allowed']) { - form_set_error($field_name, $allowed_out['message']); - } - elseif (!empty($allowed_out['message'])) { - drupal_set_message($allowed_out['message']); + else { + if (!empty($allowed_in['message'])) { + drupal_set_message($allowed_in['message']); } + module_load_include('inc', 'content', 'includes/content.crud'); + $content_type = content_types($type_name); + $group_multiple = $groups[$new_group]['group']['settings']['multigroup']['multiple']; + $multiple_values = content_multigroup_multiple_values(); + $field = $content_type['fields'][$field_name]; + $field['multiple'] = $group_multiple; + $field = content_field_instance_collapse($field); + content_field_instance_update($field); + drupal_set_message(t('The field %field has been updated to use %multiple values, to match the multiple value setting of the Multigroup %group.', array( + '%field' => $field['label'], '%multiple' => $multiple_values[$group_multiple], '%group' => $groups[$new_group]['group']['label']))); + } + } + elseif (!empty($old_group) && isset($groups[$old_group]['group']) && $groups[$old_group]['group']['group_type'] == 'multigroup') { + $allowed_out = content_multigroup_allowed_out($field, $groups[$old_group]['group']); + if (!$allowed_out['allowed']) { + form_set_error($field_name, $allowed_out['message']); + } + elseif (!empty($allowed_out['message'])) { + drupal_set_message($allowed_out['message']); } } } @@ -230,12 +239,12 @@ // We can't allow fields with more multiple values than the group has // to be moved into it. $max_existing = content_max_delta($field['field_name']); - $group_max = $group['settings']['multigroup']['multiple']; + $group_multiple = $group['settings']['multigroup']['multiple']; $multiple_values = content_multigroup_multiple_values(); - if ($group_max != 1 && $max_existing > $group_max) { + if ($group_multiple != 1 && $max_existing > $group_multiple) { return array( 'allowed' => FALSE, - 'message' => t('This change is not allowed. The field %field already has %multiple values in the database but the group %group only allows %group_max. Making this change would result in the loss of data.', array('%field' => $field['widget']['label'], '%multiple' => $max_existing, '%group' => $group['label'], '%group_max' => $multiple_values[$group_max])) + 'message' => t('This change is not allowed. The field %field already has %multiple values in the database but the group %group only allows %group_max. Making this change would result in the loss of data.', array('%field' => $field['widget']['label'], '%multiple' => $max_existing, '%group' => $group['label'], '%group_max' => $multiple_values[$group_multiple])) ); } @@ -254,7 +263,7 @@ 'nodereference_select', 'userreference_buttons', 'userreference_select', - ); + ); $allowed_widgets = array_merge($allowed_widgets, module_invoke_all('content_multigroup_allowed_widgets')); if (!in_array($field['widget']['type'], $allowed_widgets)) { return array( @@ -295,14 +304,14 @@ $max_existing = content_max_delta($field['field_name']); $no_remove_widgets = array( - 'optionwidgets_select', - 'optionwidgets_buttons', - 'optionwidgets_onoff', - 'nodereference_buttons', - 'nodereference_select', - 'userreference_buttons', - 'userreference_select', - ); + 'optionwidgets_select', + 'optionwidgets_buttons', + 'optionwidgets_onoff', + 'nodereference_buttons', + 'nodereference_select', + 'userreference_buttons', + 'userreference_select', + ); $no_remove_widgets = array_merge($no_remove_widgets, module_invoke_all('content_multigroup_no_remove_widgets')); if (in_array($field['widget']['type'], $no_remove_widgets) && $max_existing > 0) { return array( @@ -336,14 +345,17 @@ $contexts_selector = $form['#contexts']; // Gather type information. - $type = content_types($type_name); - $field_types = _content_field_types(); - $fields = $type['fields']; + $content_type = content_types($type_name); + + // The content module stops building the form if the type has no fields. + if (empty($content_type['fields'])) { + return; + } $groups = $group_options = array(); if (module_exists('fieldgroup')) { - $groups = fieldgroup_groups($type['type']); - $group_options = _fieldgroup_groups_label($type['type']); + $groups = fieldgroup_groups($type_name); + $group_options = _fieldgroup_groups_label($type_name); } $contexts = content_build_modes($contexts_selector); @@ -353,41 +365,42 @@ 'hidden' => t(''), ); $options = array( - 'none' => t('none'), + 'simple' => t('Simple'), 'fieldset' => t('Fieldset'), 'hr' => t('Horizontal line'), - //'table' => t('Table'), // TODO add this later - 'hidden' => t(''), + 'table-single' => t('Table - Single column'), + 'table-multiple' => t('Table - Multiple columns'), ); - foreach ($groups as $name => $group) { + foreach ($groups as $group_name => $group) { if ($group['group_type'] != 'multigroup') { continue; } - $defaults = $group['settings']['multigroup']['display_settings']; + $subgroup_settings = isset($group['settings']['multigroup']['subgroup']) ? $group['settings']['multigroup']['subgroup'] : array(); - $form_name = $name .'_subgroup'; - $form['#fields'] = array_merge(array($form_name), $form['#fields']); - $form[$form_name] = array( + $subgroup_name = $group_name .'_subgroup'; + $form['#fields'] = array_merge(array($subgroup_name), $form['#fields']); + $form[$subgroup_name] = array( 'human_name' => array('#value' => t('[Subgroup format]')), 'weight' => array('#type' => 'value', '#value' => -20), - 'parent' => array('#type' => 'value', '#value' => $name), + 'parent' => array('#type' => 'value', '#value' => $group_name), + 'subgroup' => array('#type' => 'value', '#value' => 1), ); if ($contexts_selector == 'basic') { - $form[$form_name]['label'] = array( + $form[$subgroup_name]['label'] = array( '#type' => 'select', '#options' => $label_options, - '#default_value' => isset($defaults['label']) ? $defaults['label'] : 'above', + '#default_value' => isset($subgroup_settings['label']) ? $subgroup_settings['label'] : 'above', ); } foreach ($contexts as $key => $title) { - $form[$form_name][$key]['format'] = array( + $form[$subgroup_name][$key]['format'] = array( '#type' => 'select', '#options' => $options, - '#default_value' => isset($defaults[$key]) ? $defaults[$key] : 'fieldset', + '#default_value' => isset($subgroup_settings[$key]['format']) ? $subgroup_settings[$key]['format'] : 'fieldset', ); + $form[$subgroup_name][$key]['exclude'] = array('#type' => 'value', '#value' => 0); } } - return $form; } /** @@ -397,28 +410,27 @@ * tries to use our 'field'. */ function content_multigroup_display_overview_form_submit($form, &$form_state) { - $form_values = $form_state['values']; - + $groups = fieldgroup_groups($form['#type_name']); // Find any groups we inserted into the display fields form, // save our settings, and remove them from $form_state. - foreach ($form_values as $key => $values) { - if (in_array($key, $form['#fields']) && substr($key, -9) == '_subgroup') { - $group_name = str_replace('_subgroup', '', $key); - $groups = fieldgroup_groups($form['#type_name']); + foreach ($form_state['values'] as $key => $values) { + if (in_array($key, $form['#fields']) && !empty($values['parent']) && !empty($values['subgroup'])) { + $group_name = $values['parent']; $group = $groups[$group_name]; - + unset($values['subgroup'], $values['parent']); // We have some numeric keys here, so we can't use array_merge. foreach ($values as $k => $v) { - $group['settings']['multigroup']['display_settings'][$k] = $v; + $group['settings']['multigroup']['subgroup'][$k] = $v; } fieldgroup_save_group($form['#type_name'], $group); - // Make sure group information is immediately updated. - cache_clear_all('fieldgroup_data', content_cache_tablename()); - fieldgroup_groups('', FALSE, TRUE); unset($form_state['values'][$key]); } } + + // Make sure group information is immediately updated. + cache_clear_all('fieldgroup_data', content_cache_tablename()); + fieldgroup_groups('', FALSE, TRUE); } /** @@ -430,7 +442,7 @@ $group_name = $form['group_name']['#default_value']; $content_type = content_types($type_name); - $groups = fieldgroup_groups($content_type['type']); + $groups = fieldgroup_groups($type_name); $group = $groups[$group_name]; if ($group['group_type'] != 'multigroup') { @@ -442,25 +454,46 @@ $form['group_type'] = array( '#type' => 'hidden', '#value' => $group['group_type'], - ); + ); $form['settings']['multigroup'] = array( '#type' => 'fieldset', - '#title' => t('Other settings'), + '#title' => t('Multigroup settings'), '#collapsed' => FALSE, '#collapsible' => TRUE, ); - $description = t('Number of times to repeat the collection of Multigroup fields.') . ' '; - $description .= t("'Unlimited' will provide an 'Add more' button so the users can add repeat it as many times as they like.") . ' '; + if (isset($group['settings']['multigroup']['subgroup'])) { + // Preserve subgroup display settings. + $form['settings']['multigroup']['subgroup'] = array( + '#type' => 'value', + '#value' => $group['settings']['multigroup']['subgroup'], + ); + } + + $form['settings']['multigroup']['multiple-columns'] = array( + '#type' => 'checkbox', + '#title' => t('Multiple columns'), + '#default_value' => isset($group['settings']['multigroup']['multiple-columns']) ? $group['settings']['multigroup']['multiple-columns'] : 0, + '#description' => t('Enable this option to render each field on a separate column on the node edit form.'), + ); + + $form['settings']['multigroup']['required'] = array( + '#type' => 'checkbox', + '#title' => t('Required'), + '#default_value' => isset($group['settings']['multigroup']['required']) ? $group['settings']['multigroup']['required'] : 1, + '#description' => t('Enable this option to require a minimum of one collection of fields in this Multigroup.'), + ); + + $description = t('Number of times to repeat the collection of Multigroup fields.') .' '; + $description .= t("'Unlimited' will provide an 'Add more' button so the users can add items as many times as they like.") .' '; $description .= t('All fields in this group will automatically be set to allow this number of values.'); - $multiple = isset($group['settings']['multigroup']['multiple']) ? $group['settings']['multigroup']['multiple'] : 1; + $group_multiple = isset($group['settings']['multigroup']['multiple']) ? $group['settings']['multigroup']['multiple'] : 1; $form['settings']['multigroup']['multiple'] = array( - '#tree' => TRUE, '#type' => 'select', '#title' => t('Number of repeats'), '#options' => content_multigroup_multiple_values(), - '#default_value' => $multiple, + '#default_value' => $group_multiple, '#description' => $description, ); @@ -469,8 +502,8 @@ '#title' => t('Labels'), '#description' => t("Labels for each subgroup of fields. Labels can be hidden or shown in various contexts using the 'Display fields' screen."), ); - if ($multiple < 2) { - $multiple = 0; + if ($group_multiple < 2) { + $group_multiple = 0; } for ($i = 0; $i < 10; $i++) { $form['settings']['multigroup']['labels'][$i] = array( @@ -482,7 +515,6 @@ $form['#validate'][] = 'content_multigroup_group_edit_form_validate'; $form['#submit'][] = 'content_multigroup_group_edit_form_submit'; - return $form; } /** @@ -523,10 +555,9 @@ $content_type = $form['#content_type']; $groups = fieldgroup_groups($content_type['type']); $group = $groups[$form_values['group_name']]; - $multiple = $form_values['settings']['multigroup']['multiple']; - foreach ($group['fields'] as $field_name => $data) { - $field = $content_type['fields'][$field_name]; - $field['multiple'] = $multiple; + $group_fields = array_intersect_key($content_type['fields'], $group['fields']); + foreach ($group_fields as $field_name => $field) { + $field['multiple'] = $form_values['settings']['multigroup']['multiple']; $field = content_field_instance_collapse($field); content_field_instance_update($field); } @@ -541,65 +572,100 @@ * d-n-d each collection of fields as a single delta item. */ function content_multigroup_fieldgroup_form(&$form, &$form_state, $form_id, $group) { - if ($group['group_type'] != 'multigroup' || - !empty($form[$group['group_name']]['#access']) || empty($form[$group['group_name']])) { + $group_name = $group['group_name']; + if ($group['group_type'] != 'multigroup' || (isset($form[$group_name]['#access']) && $form[$group_name]['#access'] != TRUE) || empty($form[$group_name])) { return; } $node = $form['#node']; - $fields = $group['fields']; - $content_fields = content_fields(); - $group_name = $group['group_name']; - - // Use the first field in the group to get the item counts. - $first_field_name = array_shift(array_keys($group['fields'])); - $first_field = isset($content_fields[$first_field_name]) ? $content_fields[$first_field_name] : array(); - $first_field_items = isset($node->$first_field_name) ? $node->$first_field_name : array(); + $content_type = content_types($group['type_name']); + $group_fields = array_intersect_key($content_type['fields'], $group['fields']); + $group_multiple = $group['settings']['multigroup']['multiple']; - $group['multiple'] = $group['settings']['multigroup']['multiple']; - switch ($group['multiple']) { + switch ($group_multiple) { case 0: - $max = 0; + $group_deltas = array(0); + $max_delta = 0; break; + case 1: - // Is this a new node? - if (empty($first_field_items)) { - $max = 1; + // Compute deltas based on the field with the highest number of items. + $group_deltas = array(); + $max_delta = -1; + foreach ($group_fields as $field_name => $field) { + $field_items = isset($node->$field_name) ? $node->$field_name : array(); + if (!empty($field_items)) { + $field = $group_fields[$field_name]; + $field_deltas = array_keys($field_items); + $field_max = (!empty($field_deltas) ? max($field_deltas) : 0); + if ($field_max > $max_delta || empty($group_deltas)) { + $max_delta = $field_max; + $group_deltas = $field_deltas; + } + } } - else { - $filled_items = content_set_empty($first_field, $first_field_items); - $current_item_count = isset($form_state['item_count'][$group_name]) - ? $form_state['item_count'][$group_name] - : count($first_field_items); - // We always want at least one empty icon for the user to fill in. - $max = ($current_item_count > count($filled_items)) - ? $current_item_count - 1 - : count($filled_items); + $current_item_count = isset($form_state['item_count'][$group_name]) ? $form_state['item_count'][$group_name] : max(1, count($group_deltas)); + while (count($group_deltas) < $current_item_count) { + $max_delta++; + $group_deltas[$max_delta] = $max_delta; } break; + default: - $max = $group['multiple'] - 1; + $group_deltas = array_keys(array_fill(0, $group_multiple, 0)); + $max_delta = $group_multiple - 1; break; } - $form[$group_name]['#theme'] = 'content_multigroup_node_form'; - $form[$group_name]['#multiple'] = !empty($max); + $form[$group_name]['#item_count'] = count($group_deltas); $form[$group_name]['#type_name'] = $group['type_name']; $form[$group_name]['#group_name'] = $group_name; $form[$group_name]['#group_label'] = $group['label']; - $form[$group_name]['#element_validate'] = array('content_multigroup_node_form_validate'); $form[$group_name]['#tree'] = TRUE; + if (!isset($form['#multigroups'])) { + $form['#multigroups'] = array(); + } + $form['#multigroups'][$group_name] = $group_fields; + + // Attach our own after build handler to the form, used to fix posting data + // and the form structure, moving fields back to their original positions. + // That is, move them from group->delta->field back to field->delta. + if (!isset($form['#after_build'])) { + $form['#after_build'] = array(); + } + if (!in_array('content_multigroup_node_form_after_build', $form['#after_build'])) { + array_unshift($form['#after_build'], 'content_multigroup_node_form_after_build'); + } + + // Attach our own validation handler to the form, used to check for empty fields. + if (!isset($form['#validate'])) { + $form['#validate'] = array(); + } + if (!in_array('content_multigroup_node_form_validate', $form['#validate'])) { + array_unshift($form['#validate'], 'content_multigroup_node_form_validate'); + } - for ($delta = 0; $delta <= $max; $delta++) { - content_multigroup_group_form($form, $form_state, $group, $delta); + // Attach our own pre_render handler to the form, used to fix the required + // attribute of all fields in multigroups. + if (!isset($form['#pre_render'])) { + $form['#pre_render'] = array(); + } + if (!in_array('content_multigroup_node_form_pre_render', $form['#pre_render'])) { + array_unshift($form['#pre_render'], 'content_multigroup_node_form_pre_render'); + } + $elements[$group_name] = array(); + foreach ($group_deltas as $delta) { + $element = content_multigroup_group_form($form, $form_state, $group, $delta); + $elements[$group_name] = array_merge($elements[$group_name], $element[$group_name]); } + $form[$group_name] = $elements[$group_name]; // Unset the original group field values now that we've moved them. - foreach ($fields as $field_name => $field) { + foreach (array_keys($group_fields) as $field_name) { unset($form[$group_name][$field_name]); } - - if ($add_more = content_multigroup_add_more($form, $form_state, $group)) { + + if (($add_more = content_multigroup_add_more($form, $form_state, $group)) !== FALSE) { $form[$group_name] += $add_more; } } @@ -610,47 +676,59 @@ * Called in form_alter and by AHAH add more. */ function content_multigroup_group_form(&$form, &$form_state, $group, $delta) { - if ($group['group_type'] != 'multigroup' || - !empty($form[$group['group_name']]['#access']) || empty($form[$group['group_name']])) { - return; - } module_load_include('inc', 'content', 'includes/content.node_form'); - + $element = array(); $node = $form['#node']; - $fields = $group['fields']; - $content_fields = content_fields(); + $type_name = $group['type_name']; + $content_type = content_types($type_name); + $group_fields = array_intersect_key($content_type['fields'], $group['fields']); $group_name = $group['group_name']; - $group['multiple'] = $group['settings']['multigroup']['multiple']; - $form[$group_name]['#fields'] = array_keys($group['fields']); - - foreach ($fields as $field_name => $group_field) { - if (empty($form[$group_name][$delta])) { - $form[$group_name] += array($delta => array($field_name => array())); + $group_multiple = $group['settings']['multigroup']['multiple']; + + if (!isset($form[$group_name])) {//nested AHAH, not initial build + $element[$group_name] = content_get_form_element($group_name, $type_name, $form, TRUE); + } + else {//initial build (via content_multigroup_fieldgroup_form) or non-nested AHAH + $element[$group_name] = $form[$group_name]; + } + $element[$group_name]['#fields'] = array_keys($group_fields); + + if (($group['group_type'] != 'multigroup') || (!(empty($element[$group['group_name']]['#access'])) && $element[$group['group_name']]['#access'] != TRUE) || empty($element[$group['group_name']])) { + return; + } + + foreach ($group_fields as $field_name => $field) { + if (empty($element[$group_name][$delta])) { + $element[$group_name] += array($delta => array($field_name => array())); } else { - $form[$group_name][$delta][$field_name] = array(); + $element[$group_name][$delta][$field_name] = array(); } - - $form[$group_name][$delta]['_weight'] = array( + + $item_count = (isset($form_state['item_count'][$group_name]) ? $form_state['item_count'][$group_name] : $element[$group_name]['#item_count']); + $element[$group_name][$delta]['_weight'] = array( '#type' => 'weight', - '#delta' => $delta, // this 'delta' is the 'weight' element's property + '#delta' => $item_count, // this 'delta' is the 'weight' element's property '#default_value' => $delta, '#weight' => 100, ); - $form[$group_name][$delta]['_delta'] = array( - '#type' => 'hidden', - '#value' => $delta, - ); - - $field = $content_fields[$field_name]; + // Add a checkbox to allow users remove a single delta subgroup. + // See content_set_empty() and theme_content_multigroup_node_form(). + if ($group_multiple == 1) { + $element[$group_name][$delta]['_remove'] = array( + '#type' => 'checkbox', + '#attributes' => array( + 'class' => 'content-multiple-remove-checkbox', + 'alt' => 'type:multigroup,name:'. $group_name .',delta:'. $delta, + ), + '#default_value' => isset($form_state['multigroup_removed'][$group_name][$delta]) ? $form_state['multigroup_removed'][$group_name][$delta] : 0, + ); + } // Make each field into a pseudo single value field // with the right delta value. - $field['multiple'] = FALSE; - - // Make sure new fields after the first have an 'empty' option. - $field['required'] = $delta > 0 ? FALSE : $field['required']; + $field['multiple'] = 0; $form['#field_info'][$field_name] = $field; $node_copy = drupal_clone($node); @@ -665,328 +743,435 @@ $node_copy->$field_name = array($delta => $node->{$field_name}[$delta]); } else { - $node_copy->$field_name = array($delta => NULL); + $value = NULL; + if (content_callback('widget', 'default value', $field) != CONTENT_CALLBACK_NONE) { + // If a module wants to insert custom default values here, + // it should provide a hook_default_value() function to call, + // otherwise the content module's content_default_value() function + // will be used. + $callback = content_callback('widget', 'default value', $field) == CONTENT_CALLBACK_CUSTOM ? $field['widget']['module'] .'_default_value' : 'content_default_value'; + if (function_exists($callback)) { + $items = $callback($form, $form_state, $field, 0); + $value = $items[0]; + } + } + $node_copy->$field_name = array($delta => $value); } $form['#node'] = $node_copy; - $field_form = content_field_form($form, $form_state, $field, $delta); - // Place the new $field_form into the $delta position in the group form. + // Place the new element into the $delta position in the group form. if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) { + $field_form = content_field_form($form, $form_state, $field, $delta); $value = array_key_exists($delta, $field_form[$field_name]) ? $delta : 0; - $form[$group_name][$delta][$field_name] = $field_form[$field_name][$value]; + $element[$group_name][$delta][$field_name] = $field_form[$field_name][$value]; } else { - $form[$group_name][$delta][$field_name] = $field_form[$field_name]; - } - $form[$group_name][$delta][$field_name]['#weight'] = $field['widget']['weight']; + // When the form is submitted, get the element data from the form values. + if (isset($form_state['values'][$field_name])) { + $form_state_copy = $form_state; + if (isset($form_state_copy['values'][$field_name][$delta])) { + $form_state_copy['values'][$field_name] = array($delta => $form_state_copy['values'][$field_name][$delta]); + } + else { + $form_state_copy['values'][$field_name] = array($delta => NULL); + } + $field_form = content_field_form($form, $form_state_copy, $field, $delta); + } + else { + $field_form = content_field_form($form, $form_state, $field, $delta); + } + + // Multiple value fields have an additional level in the array form that + // needs to get fixed in $form_state['values']. + if (!isset($field_form[$field_name]['#element_validate'])) { + $field_form[$field_name]['#element_validate'] = array(); + } + $field_form[$field_name]['#element_validate'][] = 'content_multigroup_fix_multivalue_fields'; - // Add in our validation step, and make sure it preceeds other - // processing so we can massage the element back to the normal position. - if (empty($form[$group_name][$delta][$field_name]['#element_validate'])) { - $form[$group_name][$delta][$field_name]['#element_validate'] = array(); + $element[$group_name][$delta][$field_name] = $field_form[$field_name]; } - array_unshift($form[$group_name][$delta][$field_name]['#element_validate'], 'content_multigroup_node_item_validate'); + $element[$group_name][$delta][$field_name]['#weight'] = $field['widget']['weight']; } - + // Reset the form '#node' back to its original value. $form['#node'] = $node; + + return $element; +} +/** + * Fix required flag during form rendering stage. + * + * Required fields should display the required star in the rendered form. + */ +function content_multigroup_node_form_pre_render(&$form) { + foreach ($form['#multigroups'] as $group_name => $group_fields) { + $required_fields = array(); + foreach ($group_fields as $field_name => $field) { + if ($field['required']) { + $required_fields[] = $field_name; + } + } + if (!empty($required_fields)) { + content_multigroup_node_form_fix_required($form[$group_name], $required_fields, TRUE); + } + } return $form; } /** - * Swap transposed field/delta values back - * to their normal positions in the node. - */ -function content_multigroup_node_item_validate($element, &$form_state) { - static $weights = array(); + * Fix form and posting data when the form is submitted. + * + * FormAPI uses form_builder() during form processing to map incoming $_POST + * data to the proper elements in the form. It builds the '#parents' array, + * copies the $_POST array to the '#post' member of all form elements, and it + * also builds the $form_state['values'] array. Then the '#after_build' hook is + * invoked to allow custom processing of the form structure, and that happens + * just before validation and submit handlers are executed. + * + * During hook_form_alter(), the multigroup module altered the form structure + * moving elements from field->delta to multigroup->delta->field position, + * which is what has been processed by FormAPI to build the form structures, + * but field validation (and submit) handlers expect their data to be located + * in their original positions. + * + * We now need to move the fields back to their original positions in the form, + * and we need to do so without altering the form rendering process, which is + * now reflecting the structure the multigroup is interested in. We just need + * to fix the parts of the form that affect validation and submit processing. + */ +function content_multigroup_node_form_after_build($form, &$form_state) { + // Disable required flag during FormAPI validation, except when building the + // form for an 'Add more values' request. + $required = !empty($form_state['multigroup_add_more']); + foreach ($form['#multigroups'] as $group_name => $group_fields) { + $required_fields = array(); + foreach ($group_fields as $field_name => $field) { + if ($field['required']) { + $required_fields[] = $field_name; + } + } + if (!empty($required_fields)) { + content_multigroup_node_form_fix_required($form[$group_name], $required_fields, $required); + } + } - //dsm($form_state['values']); - $form_values = $form_state['values']; - $field_name = array_pop($element['#parents']); - $delta = array_pop($element['#parents']); - $group_name = array_pop($element['#parents']); - - // Identify the new delta value for each field. - - // Find the original delta values for this group, save as static value - // because the group will acquire and lose values while we process it. - if (!array_key_exists($group_name, $weights)) { - $items = $form_state['values'][$group_name]; - $weights[$group_name] = array(); - foreach ($items as $count => $value) { - // Allow for the possibility of matching _weights and missing deltas. - $weight = floatval($value['_weight']); - $old_delta = intval($value['_delta']); - if (empty($weights[$group_name][$weight]) || !in_array($old_delta, $weights[$group_name][$weight])) { - $weights[$group_name][$weight][] = $old_delta; - } - } - ksort($weights[$group_name]); - } - $count = 0; - foreach ($weights[$group_name] as $weight => $values) { - foreach ($values as $old_delta) { - if ($old_delta === $delta) { - $delta = $count; - //dsm('moving delta values: '.$group_name.'>'.$field_name.'>'.'from '. $old_delta .' to '. $delta); - break 2; - } - $count++; - } - } - // We figured out what the new order for the fields is, - // so set the value for the new delta. - - // We move these new values back up to the top level of the - // node and out of the group so the Content module will find and - // save the new values and so they don't get mixed into the - // remaining, unaltered, values in the group. - array_push($element['#parents'], $field_name); - array_push($element['#parents'], $delta); - - // It's very important to use $form_values instead of $element['#value'] - // here, because $element['#value'] is sometimes missing changes - // made in #element_validate processing done by other modules. - $value = isset($form_values[$group_name][$delta][$field_name]) ? $form_values[$group_name][$delta][$field_name] : NULL; - - // Fields that use optionwidgets have an extra array level in the value - // because of the optionwidgets transposition that forces a delta value - // into the result array. This works fine when a delta value is between - // the field name and the field value, as in normal nodes, but not when - // we reverse the field and the delta, so in this case we need to - // promote the nested delta value back up to the field level. - if (is_array($value) && content_multigroup_uses_optionwidgets($field_name, $element['#type_name'])) { - $value = array_shift($value); + if ($form_state['submitted']) { + // Fix value positions in $form_state for the fields in multigroups. + foreach (array_keys($form['#multigroups']) as $group_name) { + content_multigroup_node_form_fix_values($form, $form_state, $form['#node']->type, $group_name); + } + + // Fix form element parents for all fields in multigroups. + content_multigroup_node_form_fix_parents($form, $form['#multigroups']); + + // Update posting data to reflect delta changes in the form structure. + if (!empty($_POST)) { + content_multigroup_node_form_fix_post($form); + } } - //dsm('setting value of '. $field_name.'>'.$delta); - //dsm($value); - form_set_value($element, $value, $form_state); + return $form; } /** - * Helper function for identifying fields that use - * optionwidgets transpositions. + * Fix required flag for required fields. + * + * We need to let the user enter an empty set of fields for a delta subgroup, + * even if it contains required fields, which is equivalent to say a subgroup + * should be ignored, not to be stored into the database. + * So, we need to check for required fields, but only for non-empty subgroups. + * + * When the form is processed for rendering, the required flag is enabled for + * all required fields, so the user can see what's required and what's not. + * + * When the form is processed for validation, the required flag is disabled, + * so that FormAPI does not report errors for empty fields. + * + * @see content_multigroup_node_form_validate(). */ -function content_multigroup_uses_optionwidgets($field_name, $type_name) { - static $optionwidgets; - if (empty($optionwidgets)) { - $optionwidgets = array( - 'optionwidgets_select', - 'optionwidgets_buttons', - 'optionwidgets_onoff', - 'nodereference_buttons', - 'nodereference_select', - 'userreference_buttons', - 'userreference_select', - ); - // Add hook where other widgets that use optionwidgets can announce it. - $optionwidgets = array_merge($optionwidgets, module_invoke_all('content_multigroup_uses_optionwidgets')); - } +function content_multigroup_node_form_fix_required(&$elements, $required_fields, $required) { + foreach (element_children($elements) as $key) { + if (isset($elements[$key]) && $elements[$key]) { - $types = content_types($type_name); - $fields = $types['fields']; - $field = $fields[$field_name]; - if (in_array($field['widget']['type'], $optionwidgets)) { - return TRUE; + if (count($elements[$key]['#array_parents']) >= 3 && in_array($elements[$key]['#array_parents'][2], $required_fields) && isset($elements[$key]['#required'])) { + $elements[$key]['#required'] = $required; + } + + // Recurse through all children elements. + content_multigroup_node_form_fix_required($elements[$key], $required_fields, $required); + } } - return FALSE; } /** - * Validation for the whole node group. - */ -function content_multigroup_node_form_validate($element, $form_state) { - // We moved all the new field values out of the field group - // and up to the top level of the node, now get rid of the - // original group values. - form_set_value($element, NULL, $form_state); - return; + * Node form validation handler. + * + * Perform validation for empty fields ignoring subgroups flagged for removal. + * Note that FormAPI validation for required fields is disabled because we need + * to accept empty fields that are flagged for removal. + */ +function content_multigroup_node_form_validate($form, &$form_state) { + $type_name = $form['#node']->type; + $groups = fieldgroup_groups($type_name); + + foreach ($form['#multigroups'] as $group_name => $group_fields) { + $group = $groups[$group_name]; + $group_required = isset($group['settings']['multigroup']['required']) ? $group['settings']['multigroup']['required'] : 1; + + $non_empty_subgroups = array(); + foreach ($group_fields as $field_name => $field) { + foreach ($form_state['values'][$field_name] as $delta => $item) { + // Ignore subgroups flagged for removal. + if (!$form_state['multigroup_removed'][$group_name][$delta]) { + $is_empty_function = $field['module'] .'_content_is_empty'; + if ($is_empty_function($form_state['values'][$field_name][$delta], $field)) { + if ($field['required']) { + if (!empty($item['_error_element'])) { + $error_element = explode('][', $item['_error_element']); + array_shift($error_element); + array_shift($error_element); + array_shift($error_element); + array_unshift($error_element, $field_name, $delta); + $error_element = implode('][', $error_element); + } + else { + $error_element = ''; + } + form_set_error($error_element, t('!name field is required in group @group.', array( + '!name' => $form[$group_name][$delta][$field_name]['#title'], + '@group' => t($group['label']), + ))); + } + } + else { + $non_empty_subgroups[$delta] = TRUE; + } + } + } + } + + if ($group_required && count($non_empty_subgroups) == 0) { + form_set_error('', t('Group @name requires one collection of fields minimum.', array('@name' => t($group['label'])))); + } + } } /** - * Implementation of hook_fieldgroup_view(). + * Fix value positions in $form_state for the fields in a multigroup. */ -function content_multigroup_fieldgroup_view(&$node, &$element, $group, $context) { - if ($group['group_type'] != 'multigroup') { - return; +function content_multigroup_node_form_fix_values(&$form, &$form_state, $type_name, $group_name) { + $content_type = content_types($type_name); + $groups = fieldgroup_groups($type_name); + $group = $groups[$group_name]; + $group_fields = array_intersect_key($content_type['fields'], $group['fields']); + + // Save the remove state of multigroup items in the $form_state array. + if (!isset($form_state['multigroup_removed'])) { + $form_state['multigroup_removed'] = array(); + } + if (!isset($form_state['multigroup_removed'][$group_name])) { + $form_state['multigroup_removed'][$group_name] = array(); } - $group_name = $group['group_name']; - $node_copy = drupal_clone($node); - $max = $group['settings']['multigroup']['multiple']; + // Move group data from group->delta->field to field->delta. + $group_data = array(); + foreach ($form_state['values'][$group_name] as $delta => $items) { + // Skip 'add more' button. + if (!is_array($items) || !isset($items['_weight'])) { + continue; + } + foreach ($group_fields as $field_name => $field) { + if (!isset($group_data[$field_name])) { + $group_data[$field_name] = array(); + } + // Get field weight and remove state from the group and keep track of the + // current delta for each field item. + $group_data[$field_name][$delta] = array_merge($items[$field_name], array( + '_weight' => $items['_weight'], + '_remove' => $items['_remove'], + '_old_delta' => $delta, + )); + // Store the remove state in the form element as well, so we can restore + // it during content_multigroup_fix_multivalue_fields(). + $form[$group_name][$delta][$field_name]['#removed'] = $items['_remove']; + } + $form_state['multigroup_removed'][$group_name][$delta] = $items['_remove']; + } + + $form_group_sorted = FALSE; + foreach ($group_data as $field_name => $items) { + + // Sort field items according to drag-n-drop reordering. Deltas are also + // rebuilt to start counting from 0 to n. Note that since all fields in the + // group share the same weight, their deltas remain in sync. + usort($items, '_content_sort_items_helper'); + + // Now we need to apply the same ordering to the form elements. Also, + // note that deltas have changed during the sort operation, so we need + // to reflect this delta conversion in the form. + if (!$form_group_sorted) { + $form_group_items = array(); + $form_deltas = array(); + foreach ($items as $new_delta => $item) { + $form_deltas[$item['_old_delta']] = $new_delta; + $form_group_items[$new_delta] = $form[$group_name][$item['_old_delta']]; + unset($form[$group_name][$item['_old_delta']]); + } + foreach ($form_group_items as $new_delta => $form_group_item) { + $form[$group_name][$new_delta] = $form_group_item; + } + content_multigroup_node_form_fix_deltas($form[$group_name], $form_deltas); + $form_group_sorted = TRUE; + } - $count = 0; - foreach ($group['fields'] as $field_name => $field) { - $count = max($count, count($node->$field_name)); - } + // Get rid of the old delta value. + foreach (array_keys($items) as $delta) { + unset($items[$delta]['_old_delta']); + } - $group['multiple'] = isset($group['settings']['multigroup']['multiple']) ? $group['settings']['multigroup']['multiple'] : 1; - $labels = isset($group['settings']['multigroup']['labels']) ? $group['settings']['multigroup']['labels'] : array(); - $format = isset($group['settings']['multigroup']['display_settings'][$context]['format']) ? $group['settings']['multigroup']['display_settings'][$context]['format'] : 'fieldset'; - $show_label = isset($group['settings']['multigroup']['display_settings']['label']) ? $group['settings']['multigroup']['display_settings']['label'] : 'above'; + // Fix field and delta positions in the $_POST array. + if (!empty($_POST)) { + $_POST[$field_name] = array(); + foreach ($items as $new_delta => $item) { + $_POST[$field_name][$new_delta] = $item; + } + if (isset($_POST[$group_name])) { + unset($_POST[$group_name]); + } + } - switch ($group['multiple']) { - case 0: - $max = 0; - break; - case 1: - $max = $count; - break; - default: - $max = $group['multiple']; - break; + // Move field items back to their original positions. + $form_state['values'][$field_name] = $items; } - for ($delta = 0; $delta < $max; $delta++) { - $element[$delta] = array('#weight' => $delta); + // Finally, get rid of the group data in form values. + unset($form_state['values'][$group_name]); +} - $label = !empty($labels[$delta]) && $show_label == 'above' ? $labels[$delta] : ''; +/** + * Fix deltas for all affected form elements. + */ +function content_multigroup_node_form_fix_deltas(&$elements, $form_deltas) { + foreach (element_children($elements) as $key) { + if (isset($elements[$key]) && $elements[$key]) { - foreach ($group['fields'] as $field_name => $field) { + // Fix the second item, the delta value, of the element's '#parents' array. + $elements[$key]['#parents'][1] = $form_deltas[$elements[$key]['#parents'][1]]; - // Create a pseudo node that only has the value we want - // in this group and pass it to the formatter. - if (isset($node->content[$field_name])) { - $node_copy->content[$field_name]['field']['items'] = array( - $delta => isset($node->content[$field_name]['field']['items'][$delta]) ? $node->content[$field_name]['field']['items'][$delta] : NULL, - ); - $element[$delta][$field_name] = $node_copy->content[$field_name]; - $element[$delta][$field_name]['#delta'] = $delta; + // If present, fix delta value in '#delta' attribute of the element. + if (isset($elements[$key]['#delta']) && isset($form_deltas[$elements[$key]['#delta']])) { + $elements[$key]['#delta'] = $form_deltas[$elements[$key]['#delta']]; } - } - switch ($format) { - case 'table': - $element[$delta]['#theme'] = 'content_multigroup_display_table'; - $element[$delta]['#title'] = $label; - break; - case 'fieldset': - $element[$delta]['#type'] = 'fieldset'; - $element[$delta]['#title'] = $label; - break; - case 'hr': - $element[$delta]['#theme'] = 'content_multigroup_display_hr'; - $element[$delta]['#title'] = $label; - break; - default: - $element[$delta]['#theme'] = 'content_multigroup_display_simple'; - $element[$delta]['#title'] = $label; - break; - } + // Recurse through all children elements. + content_multigroup_node_form_fix_deltas($elements[$key], $form_deltas); + } } +} - foreach ($group['fields'] as $field_name => $field) { - if (isset($element[$field_name])) { - unset($element[$field_name]); +/** + * Fix form element parents for all fields in multigroups. + * + * The $element['#parents'] array needs to reflect the position of the fields + * in the $form_state['values'] array so that form_set_value() can be safely + * used by field validation handlers. + */ +function content_multigroup_node_form_fix_parents(&$elements, $multigroups) { + foreach (element_children($elements) as $key) { + if (isset($elements[$key]) && $elements[$key]) { + // Check if the current element is child of a multigroup. The #parents + // array for field values has, at least, 3 parent elements, being the + // first one the name of a multigroup. + if (count($elements[$key]['#parents']) >= 3 && isset($multigroups[$elements[$key]['#parents'][0]])) { + + // Extract group name, delta and field name from the #parents array. + array_shift($elements[$key]['#parents']); + $delta = array_shift($elements[$key]['#parents']); + $field_name = array_shift($elements[$key]['#parents']); + + // Now, insert field name and delta to the #parents array. + array_unshift($elements[$key]['#parents'], $field_name, $delta); + } + + // Recurse through all children elements. + content_multigroup_node_form_fix_parents($elements[$key], $multigroups); } } } /** - * Theme an individual form element. + * Update posting data to reflect delta changes in the form structure. * - * Combine multiple values into a table with drag-n-drop reordering. + * The $_POST array is fixed in content_multigroup_node_form_fix_values(). */ -function theme_content_multigroup_node_form($element) { - $output = ''; - if ($element['#multiple'] >= 1) { - $table_id = $element['#group_name'] .'_values'; - $order_class = $element['#group_name'] .'-delta-order'; - - $header = array( - array( - 'data' => '', - 'colspan' => 2 - ), - t('Order'), - ); - $rows = array(); - $groups = fieldgroup_groups($element['#type_name']); - $group = $groups[$element['#group_name']]; - $labels = isset($group['settings']['multigroup']['labels']) ? $group['settings']['multigroup']['labels'] : array(); - $multiple = isset($group['settings']['multigroup']['multiple']) ? $group['settings']['multigroup']['multiple'] : 1; - - $i = 0; - foreach (element_children($element) as $delta => $key) { - if ($key !== $element['#group_name'] .'_add_more') { - $label = !empty($labels[$i]) ? theme('content_multigroup_node_label', $labels[$i]) : ''; - $element[$key]['_weight']['#attributes']['class'] = $order_class; - $delta_element = drupal_render($element[$key]['_weight']); - $cells = array( - array('data' => '', 'class' => 'content-multiple-drag'), - $label . drupal_render($element[$key]), - array('data' => $delta_element, 'class' => 'delta-order'), - ); - $rows[] = array( - 'data' => $cells, - // TODO Tablesort drag n drop is not working with complex - // field validation. The fields appear to work correctly, - // but element validation seems to get missed or confused - // causing validation errors. Need to investigate why. - 'class' => 'draggable', - ); - } - $i++; - } +function content_multigroup_node_form_fix_post(&$elements) { + foreach (element_children($elements) as $key) { + if (isset($elements[$key]) && $elements[$key]) { - $output .= theme('table', $header, $rows, array('id' => $table_id, 'class' => 'content-multiple-table')); - $output .= $element['#description'] ? '
'. $element['#description'] .'
' : ''; - $output .= drupal_render($element[$element['#group_name'] .'_add_more']); + // Update the element copy of the $_POST array. + $elements[$key]['#post'] = $_POST; - drupal_add_tabledrag($table_id, 'order', 'sibling', $order_class); - } - else { - foreach (element_children($element) as $key) { - $output .= drupal_render($element[$key]); + // Recurse through all children elements. + content_multigroup_node_form_fix_post($elements[$key]); } } - return $output; + // Update the form copy of the $_POST array. + $elements['#post'] = $_POST; +} + +/** + * Fix the value for fields that deal with multiple values themselves. + */ +function content_multigroup_fix_multivalue_fields($element, &$form_state) { + $field_name = $element['#field_name']; + $delta = $element['#delta']; + $value = array_merge($form_state['values'][$field_name][$delta][0], array('_remove' => $element['#removed'])); + form_set_value($element, $value, $form_state); } +/** + * Add AHAH add more button, if not working with a programmed form. + */ function content_multigroup_add_more(&$form, &$form_state, $group) { - // Add AHAH add more button, if not working with a programmed form. - $multiple = $group['settings']['multigroup']['multiple']; - $form_element = array(); - if ($multiple != 1 || !empty($form['#programmed'])) { - return $form_element; + $group_multiple = $group['settings']['multigroup']['multiple']; + if ($group_multiple != 1 || !empty($form['#programmed'])) { + return FALSE; } - else { - // Make sure the form is cached so ahah can work. - $form['#cache'] = TRUE; - $content_type = content_types($group['type_name']); - $group_name = $group['group_name']; - $group_name_css = str_replace('_', '-', $group_name); - - $form_element[$group_name .'_add_more'] = array( - '#type' => 'submit', - '#name' => $group_name .'_add_more', - '#value' => t('Add more values'), - '#weight' => $multiple + 1, - // Submit callback for disabled JavaScript. drupal_get_form() might get - // the form from the cache, so we can't rely on content_form_alter() - // including this file. Therefore, call a proxy function to do this. - '#submit' => array('content_multigroup_add_more_submit_proxy'), - '#ahah' => array( - 'path' => 'content_multigroup/js_add_more/'. $content_type['url_str'] .'/'. $group_name, - 'wrapper' => $group_name_css .'-items', - 'method' => 'replace', - 'effect' => 'fade', - ), - // When JS is disabled, the content_add_more_submit handler will find - // the relevant field using these entries. - '#group_name' => $group_name, - '#type_name' => $group['type_name'], - ); - // Add wrappers for the group and 'more' button. - // TODO: could be simplified ? - $form_element['#prefix'] = '
'; - $form_element[$group_name .'_add_more']['#prefix'] = '
'; - $form_element[$group_name .'_add_more']['#suffix'] = '
'; - } + // Make sure the form is cached so ahah can work. + $form['#cache'] = TRUE; + $content_type = content_types($group['type_name']); + $group_name = $group['group_name']; + $group_name_css = str_replace('_', '-', $group_name); + + $form_element = array(); + $form_element[$group_name .'_add_more'] = array( + '#type' => 'submit', + '#name' => $group_name .'_add_more', + '#value' => t('Add more values'), + '#weight' => $group_multiple + 1, + '#submit' => array('content_multigroup_add_more_submit'), + '#ahah' => array( + 'path' => 'content_multigroup/js_add_more/'. $content_type['url_str'] .'/'. $group_name, + 'wrapper' => $group_name_css .'-items', + 'method' => 'replace', + 'effect' => 'fade', + ), + // When JS is disabled, the content_multigroup_add_more_submit handler will + // find the relevant group information using these entries. + '#group_name' => $group_name, + '#type_name' => $group['type_name'], + '#item_count' => $form[$group_name]['#item_count'], + ); + + // Add wrappers for the group and 'more' button. + // TODO: could be simplified ? + $form_element['#prefix'] = '
'; + $form_element[$group_name .'_add_more']['#prefix'] = '
'; + $form_element[$group_name .'_add_more']['#suffix'] = '
'; + return $form_element; } @@ -1002,8 +1187,8 @@ $type_name = $form_state['clicked_button']['#type_name']; // Make the changes we want to the form state. - if ($form_state['values'][$group_name][$group_name .'_add_more']) { - $form_state['item_count'][$group_name] = count($form_state['values'][$group_name]); + if (isset($form_state['clicked_button']['#item_count'])) { + $form_state['item_count'][$group_name] = $form_state['clicked_button']['#item_count'] + 1; } } @@ -1013,12 +1198,11 @@ * Adapted from content_add_more_js to work with groups instead of fields. */ function content_multigroup_add_more_js($type_name_url, $group_name) { - $type = content_types($type_name_url); - $groups = fieldgroup_groups($type['type']); + $content_type = content_types($type_name_url); + $groups = fieldgroup_groups($content_type['type']); $group = $groups[$group_name]; - $group['multiple'] = $group['settings']['multigroup']['multiple']; - if (($group['multiple'] != 1) || empty($_POST['form_build_id'])) { + if (($group['settings']['multigroup']['multiple'] != 1) || empty($_POST['form_build_id'])) { // Invalid request. drupal_json(array('data' => '')); exit; @@ -1033,7 +1217,6 @@ drupal_json(array('data' => '')); exit; } - // We don't simply return a new empty widget to append to existing ones, because // - ahah.js won't simply let us add a new row to a table // - attaching the 'draggable' behavior won't be easy @@ -1059,17 +1242,25 @@ unset($form_state['values'][$group_name][$group['group_name'] .'_add_more']); foreach ($_POST[$group_name] as $delta => $item) { $form_state['values'][$group_name][$delta]['_weight'] = $item['_weight']; - $form_state['values'][$group_name][$delta]['_delta'] = $item['_delta']; + $form_state['values'][$group_name][$delta]['_remove'] = isset($item['_remove']) ? $item['_remove'] : 0; } + $group['multiple'] = $group['settings']['multigroup']['multiple']; $form_state['values'][$group_name] = _content_sort_items($group, $form_state['values'][$group_name]); $_POST[$group_name] = _content_sort_items($group, $_POST[$group_name]); - + // Build our new form element for the whole group, asking for one more element. - - $form_state['item_count'] = array($group_name => count($_POST[$group_name]) + 1); $delta = max(array_keys($_POST[$group_name])) + 1; - content_multigroup_group_form($form, $form_state, $group, $delta); - + $form_state['item_count'] = array($group_name => count($_POST[$group_name]) + 1); + $form_element = content_multigroup_group_form($form, $form_state, $group, $delta); + // Rebuild weight deltas to make sure they all are equally dimensioned. + foreach ($form_element[$group_name] as $key => $item) { + if (is_numeric($key) && isset($item['_weight']) && is_array($item['_weight'])) { + $form_element[$group_name][$key]['_weight']['#delta'] = $delta; + } + } + // Add the new element at the right place in the (original, unbuilt) form. + content_set_form_element($group_name, $content_type['type'], $form, $form_element[$group_name], TRUE); + // Save the new definition of the form. $form_state['values'] = array(); form_set_cache($form_build_id, $form, $form_state); @@ -1077,7 +1268,7 @@ // Build the new form against the incoming $_POST values so that we can // render the new element. $_POST[$group_name][$delta]['_weight'] = $delta; - $form_state = array('submitted' => FALSE); + $form_state = array('submitted' => FALSE, 'multigroup_add_more' => TRUE); $form += array( '#post' => $_POST, '#programmed' => FALSE, @@ -1085,8 +1276,7 @@ $form = form_builder($_POST['form_id'], $form, $form_state); // Render the new output. - $group_form = $form[$group_name]; - + $group_form = content_get_form_element($group_name, $content_type['type'], $form, TRUE); // We add a div around the new content to receive the ahah effect. $group_form[$delta]['#prefix'] = '
'. (isset($group_form[$delta]['#prefix']) ? $group_form[$delta]['#prefix'] : ''); $group_form[$delta]['#suffix'] = (isset($group_form[$delta]['#suffix']) ? $group_form[$delta]['#suffix'] : '') .'
'; @@ -1105,46 +1295,315 @@ } /** + * Implementation of hook_fieldgroup_view(). + */ +function content_multigroup_fieldgroup_view(&$node, &$element, $group, $context) { + if ($group['group_type'] != 'multigroup') { + return; + } + + $group_name = $group['group_name']; + $node_copy = drupal_clone($node); + $content_type = content_types($group['type_name']); + $group_fields = array_intersect_key($content_type['fields'], $group['fields']); + $group_multiple = $group['settings']['multigroup']['multiple']; + $subgroup_settings = isset($group['settings']['multigroup']['subgroup']) ? $group['settings']['multigroup']['subgroup'] : array(); + $show_label = isset($subgroup_settings['label']) ? $subgroup_settings['label'] : 'above'; + $subgroup_labels = isset($group['settings']['multigroup']['labels']) ? $group['settings']['multigroup']['labels'] : array(); + $subgroup_format = isset($subgroup_settings[$context]['format']) ? $subgroup_settings[$context]['format'] : 'fieldset'; + + switch ($group_multiple) { + case 0: + $group_deltas = array(0); + break; + + case 1: + // Compute deltas based on the field with the highest number of items. + $group_deltas = array(); + $max_delta = -1; + foreach (array_keys($group_fields) as $field_name) { + $field_deltas = is_array($node->content[$field_name]['field']['items']) ? array_keys($node->content[$field_name]['field']['items']) : array(); + $field_max = (!empty($field_deltas) ? max($field_deltas) : 0); + if ($field_max > $max_delta) { + $max_delta = $field_max; + $group_deltas = $field_deltas; + } + } + break; + + default: + $group_deltas = array_keys(array_fill(0, $group_multiple - 1, 0)); + break; + } + + foreach ($group_deltas as $i => $delta) { + $element[$delta] = array( + '#title' => ($show_label == 'above' && !empty($subgroup_labels[$i]) ? check_plain(t($subgroup_labels[$i])) : ''), + '#attributes' => array('class' => 'content-multigroup-wrapper content-multigroup-'. $i), + '#weight' => $delta, + ); + + // Create a pseudo node that only has the value we want in this group and + // pass it to the formatter. + // Default implementation of content-field.tpl.php uses a different CSS + // class for inline labels when delta is zero, but this is not needed in + // the context of multigroup, so we place the field into index 1 of the + // item list. Note that CSS class "field-label-inline" is overridden in the + // multigroup stylesheet because here labels should always be visible. + foreach (array_keys($group_fields) as $field_name) { + if (isset($node->content[$field_name])) { + $node_copy->content[$field_name]['field']['items'] = array( + 1 => isset($node->content[$field_name]['field']['items'][$delta]) ? $node->content[$field_name]['field']['items'][$delta] : NULL, + ); + $element[$delta][$field_name] = $node_copy->content[$field_name]; + $element[$delta][$field_name]['#delta'] = $delta; + } + } + + switch ($subgroup_format) { + case 'simple': + $element[$delta]['#theme'] = 'content_multigroup_display_simple'; + $element[$delta]['#fields'] = $group_fields; + break; + case 'fieldset': + $element[$delta]['#type'] = 'content_multigroup_display_fieldset'; + $element[$delta]['#fields'] = $group_fields; + break; + case 'hr': + $element[$delta]['#theme'] = 'content_multigroup_display_hr'; + $element[$delta]['#fields'] = $group_fields; + break; + case 'table-single': + $element['#theme'] = 'content_multigroup_display_table_single'; + $element['#attributes']['class'] = 'content-multigroup-display-table-single-column'; + $element['#fields'] = $group_fields; + break; + case 'table-multiple': + $element['#theme'] = 'content_multigroup_display_table_multiple'; + $element['#attributes']['class'] = 'content-multigroup-display-table-multiple-columns'; + $element['#fields'] = $group_fields; + break; + } + } + + foreach (array_keys($group_fields) as $field_name) { + if (isset($element[$field_name])) { + unset($element[$field_name]); + } + } +} + +/** + * Theme an individual form element. + * + * Combine multiple values into a table with drag-n-drop reordering. + */ +function theme_content_multigroup_node_form($element) { + $groups = fieldgroup_groups($element['#type_name']); + $group_name = $element['#group_name']; + $group = $groups[$group_name]; + $group_multiple = $group['settings']['multigroup']['multiple']; + $content_type = content_types($group['type_name']); + $group_fields = array_intersect_key($content_type['fields'], $group['fields']); + + $table_id = $element['#group_name'] .'_values'; + $table_class = 'content-multiple-table'; + $order_class = $element['#group_name'] .'-delta-order'; + $subgroup_settings = isset($group['settings']['multigroup']['subgroup']) ? $group['settings']['multigroup']['subgroup'] : array(); + $show_label = isset($subgroup_settings['label']) ? $subgroup_settings['label'] : 'above'; + $subgroup_labels = isset($group['settings']['multigroup']['labels']) ? $group['settings']['multigroup']['labels'] : array(); + $multiple_columns = isset($group['settings']['multigroup']['multiple-columns']) ? $group['settings']['multigroup']['multiple-columns'] : 0; + + $header = array(); + if ($group_multiple >= 1) { + $header[] = array('data' => ''); + } + if ($multiple_columns) { + foreach ($group_fields as $field_name => $field) { + $required = !empty($field['required']) ? ' *' : ''; + $header[] = check_plain(t($field['widget']['label'])) . $required; + } + $table_class .= ' content-multigroup-edit-table-multiple-columns'; + } + else { + if ($group_multiple >= 1) { + $header[0]['colspan'] = 2; + } + $table_class .= ' content-multigroup-edit-table-one-column'; + } + if ($group_multiple >= 1) { + $header[] = array('data' => t('Order'), 'class' => 'content-multiple-weight-header'); + if ($group_multiple == 1) { + $header[] = array('data' => t('Remove'), 'class' => 'content-multiple-remove-header'); + } + } + $rows = array(); + + $i = 0; + foreach (element_children($element) as $delta => $key) { + if ($key !== $group_name .'_add_more') { + $cells = array(); + $label = ($show_label == 'above' && !empty($subgroup_labels[$i]) ? theme('content_multigroup_node_label', check_plain(t($subgroup_labels[$i]))) : ''); + $element[$key]['_weight']['#attributes']['class'] = $order_class; + if ($group_multiple >= 1) { + $cells[] = array('data' => '', 'class' => 'content-multiple-drag'); + $delta_element = drupal_render($element[$key]['_weight']); + if ($group_multiple == 1) { + $remove_element = drupal_render($element[$key]['_remove']); + } + } + else { + $element[$key]['_weight']['#type'] = 'hidden'; + } + if ($multiple_columns) { + foreach ($group_fields as $field_name => $field) { + $cells[] = drupal_render($element[$key][$field_name]); + } + } + else { + $cells[] = $label . drupal_render($element[$key]); + } + if ($group_multiple >= 1) { + $row_class = 'draggable'; + $cells[] = array('data' => $delta_element, 'class' => 'delta-order'); + if ($group_multiple == 1) { + if (!empty($element[$key]['_remove']['#value'])) { + $row_class .= ' content-multiple-removed-row'; + } + $cells[] = array('data' => $remove_element, 'class' => 'content-multiple-remove-cell'); + } + $rows[] = array('data' => $cells, 'class' => $row_class); + } + else { + $rows[] = array('data' => $cells); + } + } + $i++; + } + + $output = theme('table', $header, $rows, array('id' => $table_id, 'class' => $table_class)); + $output .= $element['#description'] ? '
'. $element['#description'] .'
' : ''; + $output .= drupal_render($element[$group_name .'_add_more']); + + // Enable drag-n-drop only if the group really allows multiple values. + if ($group_multiple >= 1) { + drupal_add_tabledrag($table_id, 'order', 'sibling', $order_class); + drupal_add_js(drupal_get_path('module', 'content') .'/theme/content-edit.js'); + + // Enable remove buttons behavior. + if ($group_multiple == 1) { + drupal_add_js(drupal_get_path('module', 'content_multigroup') .'/content_multigroup-edit.js'); + $settings = array($group_name => array()); + foreach ($group_fields as $field_name => $field) { + $settings[$group_name][$field_name] = $field['widget']['type']; + } + drupal_add_js(array('content_multigroup' => $settings), 'setting'); + } + } + + return $output; +} + +/** * Theme the sub group label in the node form. */ function theme_content_multigroup_node_label($text) { - if (!empty($text)) { - return '

'. check_plain($text) .'

'; - } + return !empty($text) ? '

'. $text .'

' : ''; } +/** + * Theme a subgroup of fields in 'simple' format. + * + * No output is generated if all fields are empty. + */ function theme_content_multigroup_display_simple($element) { - $label = ''; - if (!empty($element['#title'])) { - $label .= ''; - } - $output = $label; + $children = $output = ''; foreach (element_children($element) as $key) { - $output .= drupal_render($element[$key]); + $children .= drupal_render($element[$key]); + } + if (!empty($children)) { + $output .= ''; + if (!empty($element['#title'])) { + $output .= ''; + } + $output .= $children .''; } return $output; } -function theme_content_multigroup_display_hr($element) { - $label = ''; - if (!empty($element['#title'])) { - $label .= ''; +/** + * Theme a subgroup of fields in 'fieldset' format. + * + * No output is generated if all fields are empty. + */ +function theme_content_multigroup_display_fieldset($element) { + if (empty($element['#children']) && empty($element['#value'])) { + return ''; } - $output = '
'. $label; + return theme('fieldset', $element); +} + +/** + * Theme a subgroup of fields in 'hr' format. + * + * No output is generated if all fields are empty. + */ +function theme_content_multigroup_display_hr($element) { + $children = $output = ''; foreach (element_children($element) as $key) { - $output .= drupal_render($element[$key]); + $children .= drupal_render($element[$key]); + } + if (!empty($children)) { + $output .= '
'; + if (!empty($element['#title'])) { + $output .= ''; + } + $output .= $children .''; } return $output; } -function theme_content_multigroup_display_table($element) { - $label = ''; - if (!empty($element['#title'])) { - $label .= ''; +/** + * Theme a multigroup in 'table-single' format. + * + * Each subgroup has its own table row with a single cell for all fields. + * No output is generated if all fields are empty. + */ +function theme_content_multigroup_display_table_single($element) { + $header = array(); + $rows = array(); + foreach (element_children($element) as $delta) { + $row = array(); + if (!empty($element[$delta]['#title'])) { + $row[] = ''; + } + foreach ($element['#fields'] as $field_name => $field) { + $row[] = drupal_render($element[$delta][$field_name]); + } + $rows[] = array('data' => array(implode("\n", $row)), 'class' => $element[$delta]['#attributes']['class']); } - $output = $label; - foreach (element_children($element) as $key) { - $output .= drupal_render($element[$key]); + return count($rows) ? theme('table', $header, $rows, $element['#attributes']) : ''; +} + +/** + * Theme a multigroup in 'table-multiple' format. + * + * Each subgroup has its own table row with a separate cell for each field. + * No output is generated if all fields are empty. + */ +function theme_content_multigroup_display_table_multiple($element) { + $header = array(); + foreach ($element['#fields'] as $field_name => $field) { + $label_display = isset($field['display_settings']['label']['format']) ? $field['display_settings']['label']['format'] : 'above'; + $header[] = $label_display != 'hidden' ? check_plain(t($field['widget']['label'])) : ''; + } + $rows = array(); + foreach (element_children($element) as $delta) { + $cells = array(); + foreach ($element['#fields'] as $field_name => $field) { + $cells[] = array('data' => drupal_render($element[$delta][$field_name]), 'class' => $element[$delta]['#attributes']['class']); + } + $rows[] = $cells; } - return $output; -} \ No newline at end of file + return count($rows) ? theme('table', $header, $rows, $element['#attributes']) : ''; +} Index: modules/content_permissions/content_permissions.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/content_permissions/Attic/content_permissions.info,v retrieving revision 1.2 diff -u -r1.2 content_permissions.info --- modules/content_permissions/content_permissions.info 23 Apr 2008 18:01:52 -0000 1.2 +++ modules/content_permissions/content_permissions.info 25 Feb 2009 16:34:08 -0000 @@ -3,4 +3,10 @@ description = Set field-level permissions for CCK fields. package = CCK core = 6.x -dependencies[] = content \ No newline at end of file +dependencies[] = content +; Information added by drupal.org packaging script on 2009-02-14 +version = "6.x-2.x-dev" +core = "6.x" +project = "cck" +datestamp = "1234612969" + Index: modules/fieldgroup/fieldgroup.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/fieldgroup/fieldgroup.info,v retrieving revision 1.6.2.1 diff -u -r1.6.2.1 fieldgroup.info --- modules/fieldgroup/fieldgroup.info 22 Sep 2008 18:25:21 -0000 1.6.2.1 +++ modules/fieldgroup/fieldgroup.info 25 Feb 2009 16:34:07 -0000 @@ -3,4 +3,10 @@ description = Create display groups for CCK fields. dependencies[] = content package = CCK -core = 6.x \ No newline at end of file +core = 6.x +; Information added by drupal.org packaging script on 2009-02-14 +version = "6.x-2.x-dev" +core = "6.x" +project = "cck" +datestamp = "1234612969" + Index: modules/fieldgroup/fieldgroup.install =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/fieldgroup/fieldgroup.install,v retrieving revision 1.34.2.19 diff -u -r1.34.2.19 fieldgroup.install --- modules/fieldgroup/fieldgroup.install 26 Dec 2008 11:51:46 -0000 1.34.2.19 +++ modules/fieldgroup/fieldgroup.install 28 Jan 2009 17:18:30 -0000 @@ -52,6 +52,7 @@ 'group_type' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => 'standard'), 'type_name' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''), 'group_name' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''), + 'parent_group_name' => array('type' => 'varchar', 'length' => 32, 'not null' => FALSE, 'default' => ''), 'label' => array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''), 'settings' => array('type' => 'text', 'size' => 'medium', 'not null' => TRUE), 'weight' => array('type' => 'int', 'not null' => TRUE, 'default' => 0), @@ -313,4 +314,16 @@ $ret = array(); $ret[] = update_sql("DELETE FROM {content_group_fields} WHERE (field_name, type_name) NOT IN (SELECT field_name, type_name FROM {content_node_field_instance})"); return $ret; +} + +/** + * allow for nesting of fieldgroups + */ +function fieldgroup_update_6008() { + if ($abort = content_check_update('fieldgroup')) { + return $abort; + } + $ret = array(); + db_add_field($ret, 'content_group', 'parent_group_name', array('type' => 'varchar', 'length' => 32, 'not null' => FALSE, 'default' => '')); + return $ret; } \ No newline at end of file Index: modules/fieldgroup/fieldgroup.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/fieldgroup/fieldgroup.module,v retrieving revision 1.79.2.44 diff -u -r1.79.2.44 fieldgroup.module --- modules/fieldgroup/fieldgroup.module 14 Feb 2009 00:03:52 -0000 1.79.2.44 +++ modules/fieldgroup/fieldgroup.module 25 Feb 2009 16:37:48 -0000 @@ -163,6 +163,7 @@ $form['settings']['display']['label'] = array('#type' => 'value', '#value' => $group['settings']['display']['label']); $form['weight'] = array('#type' => 'hidden', '#default_value' => $group['weight']); $form['group_name'] = array('#type' => 'hidden', '#default_value' => $group_name); + $form['parent_group_name'] = array('#type' => 'hidden', '#default_value' => $group['parent_group_name']); $form['#content_type'] = $content_type; @@ -207,12 +208,15 @@ $form_values = $form_state['values']; $content_type = $form['#content_type']; $group_name = $form['#group_name']; - fieldgroup_delete($content_type['type'], $group_name); + $parent_group_name = db_fetch_array(db_query("SELECT parent_group_name FROM {". fieldgroup_tablename() ."} WHERE group_name = '%s' and type_name = '%s'", $group_name, $content_type['type'])); + $result = db_query("UPDATE {". fieldgroup_tablename() ."} SET parent_group_name = '%s' WHERE parent_group_name = '%s'", $parent_group_name['parent_group_name'], $group_name); + $result = db_query("UPDATE {". fieldgroup_fields_tablename() ."} SET group_name = '%s' WHERE group_name = '%s'", $parent_group_name['parent_group_name'], $group_name); + fieldgroup_delete($content_type['type'], $group_name); drupal_set_message(t('The group %group_name has been removed.', array('%group_name' => $group_name))); $form_state['redirect'] = 'admin/content/node-type/'. $content_type['url_str'] .'/fields'; } -/* +/** * Returns all groups for a content type */ function fieldgroup_groups($content_type = '', $sorted = FALSE, $reset = FALSE) { @@ -224,23 +228,13 @@ $groups_sorted = $data['groups_sorted']; } else { - $result = db_query("SELECT * FROM {". fieldgroup_tablename() ."} ORDER BY weight, group_name"); + $result = db_query("SELECT * FROM {". fieldgroup_tablename() ."} ORDER BY type_name, weight"); $groups = array(); $groups_sorted = array(); while ($group = db_fetch_array($result)) { - $group['settings'] = unserialize($group['settings']); - $group['fields'] = array(); - $groups[$group['type_name']][$group['group_name']] = $group; + $groups[$group['type_name']] = _fieldgroup_get_tree($group['type_name']); $groups_sorted[$group['type_name']][] = &$groups[$group['type_name']][$group['group_name']]; } - //load fields - $result = db_query("SELECT nfi.*, ng.group_name FROM {". fieldgroup_tablename() ."} ng ". - "INNER JOIN {". fieldgroup_fields_tablename() ."} ngf ON ngf.type_name = ng.type_name AND ngf.group_name = ng.group_name ". - "INNER JOIN {". content_instance_tablename() ."} nfi ON nfi.field_name = ngf.field_name AND nfi.type_name = ngf.type_name ". - "WHERE nfi.widget_active = 1 ORDER BY nfi.weight"); - while ($field = db_fetch_array($result)) { - $groups[$field['type_name']][$field['group_name']]['fields'][$field['field_name']] = $field; - } cache_set('fieldgroup_data', array('groups' => $groups, 'groups_sorted' => $groups_sorted), content_cache_tablename()); } } @@ -253,6 +247,69 @@ return $sorted ? $groups_sorted[$content_type] : $groups[$content_type]; } +/** + * create a tree of fieldgroups for nesting them + */ +function _fieldgroup_get_tree($type_name, $parent_group_name = '', $depth = -1, $max_depth = null) { + static $children, $parents, $groups; + + $depth++; + // We cache trees, so it's not CPU-intensive to call get_tree() on a term + // and its children, too. + if (!isset($children[$type_name])) { + $children[$type_name] = array(); + + $s = "SELECT * FROM {". fieldgroup_tablename() ."} WHERE type_name='%s' ORDER BY weight"; + $r = db_query($s, $type_name); + while ($group = db_fetch_array($r)) { + $children[$type_name][$group['parent_group_name']][] = $group['group_name']; + $parents[$type_name][$group['group_name']][] = $group['parent_group_name']; + $groups[$type_name][$group['group_name']] = $group; + $groups[$type_name][$group['group_name']]['fields'] = array(); + $groups[$type_name][$group['group_name']]['settings'] = unserialize($group['settings']); + } + //load fields + $result = db_query("SELECT nfi.*, ng.group_name FROM {". fieldgroup_tablename() ."} ng ". + "INNER JOIN {". fieldgroup_fields_tablename() ."} ngf ON ngf.type_name = ng.type_name AND ngf.group_name = ng.group_name ". + "INNER JOIN {". content_instance_tablename() ."} nfi ON nfi.field_name = ngf.field_name AND nfi.type_name = ngf.type_name ". + "WHERE nfi.widget_active = 1 ORDER BY nfi.weight"); + while ($field = db_fetch_array($result)) { + $groups[$field['type_name']][$field['group_name']]['fields'][$field['field_name']] = $field; + } + } + + $max_depth = (is_null($max_depth)) ? count($children[$type_name]) : $max_depth; + if (isset($children[$type_name][$parent_group_name])) { + foreach ($children[$type_name][$parent_group_name] as $child_group_name) { + if ($max_depth > $depth) { + $group = $groups[$type_name][$child_group_name]; + $group['depth'] = $depth; + $group['parents'] = $parents[$type_name][$child_group_name]; + $tree[$group['group_name']] = $group; + if ($children[$type_name][$child_group_name]) { + $tree = array_merge($tree, _fieldgroup_get_tree($type_name, $child_group_name, $depth, $max_depth)); + } + } + } + } + return $tree ? $tree : array(); +} + +/** + * go through a set of fieldgroups and construct a simple representation of their hierarchy + */ +function _fieldgroup_plain_tree($items) { + $rows = array(); + $rows[''] = '<'. t('none') .'>'; + foreach ($items as $item) { + $group_name = $item['group_name']; + $label = t($item['label']); + if ($group_name) { + $rows[$group_name] = str_repeat('--', $item['depth']) . ' ' . $label; + } + } + return $rows; +} function _fieldgroup_groups_label($content_type) { $groups = fieldgroup_groups($content_type); @@ -268,53 +325,148 @@ return db_result(db_query("SELECT group_name FROM {". fieldgroup_fields_tablename() ."} WHERE type_name = '%s' AND field_name = '%s'", $content_type, $field_name)); } +function _fieldgroup_field_get_parents($content_type, $name, $is_group = FALSE) { + $counter = 0; + if ($is_group) { + $parents[$counter] = $name; + } + else { + $parents[$counter] = db_result(db_query("SELECT group_name FROM {". fieldgroup_fields_tablename() ."} WHERE type_name = '%s' AND field_name = '%s'", $content_type, $name)); + } + while ($result = db_result(db_query("SELECT parent_group_name FROM {". fieldgroup_tablename() ."} WHERE type_name = '%s' AND group_name = '%s'", $content_type, $parents[$counter]))) { + $counter++; + $parents[$counter] = $result; + } + return $parents; +} + +function _fieldgroup_add_group_to_form(&$form, &$form_state, $form_id, $group_name, $group, $groups) { + $form[$group_name] = array( + '#type' => 'fieldset', + '#title' => check_plain(t($group['label'])), + '#collapsed' => $group['settings']['form']['style'] == 'fieldset_collapsed', + '#collapsible' => in_array($group['settings']['form']['style'], array('fieldset_collapsed', 'fieldset_collapsible')), + '#weight' => $group['weight'], + '#depth' => $group['depth'], + '#group_parent' => $group['parent_group_name'], + '#description' => content_filter_xss(t($group['settings']['form']['description'])), + '#attributes' => array('class' => strtr($group['group_name'], '_', '-')), + ); + $has_accessible_field = FALSE; + foreach ($group['fields'] as $field_name => $field) { + if (isset($form[$field_name])) { + $form[$field_name]['#weight'] = $field['weight']; + $form[$group_name][$field_name] = $form[$field_name]; + //Track whether this group has any accessible fields within it. + if (!isset($form[$field_name]['#access']) || $form[$field_name]['#access'] !== FALSE) { + $has_accessible_field = TRUE; + } + unset($form[$field_name]); + } + } + if (!empty($group['fields']) && !element_children($form[$group_name])) { + //hide the fieldgroup, because the fields are hidden too + unset($form[$group_name]); + } + + if (!$has_accessible_field) { + // Hide the fieldgroup, because the fields are inaccessible. + $form[$group_name]['#access'] = FALSE; + } + else { + //cascade visibility up + $form[$group_name]['#access'] = TRUE; + } + + // Allow other modules to alter the form. + // Can't use module_invoke_all because we want + // to be able to use a reference to $form and $form_state. + foreach (module_implements('fieldgroup_form') as $module) { + $function = $module .'_fieldgroup_form'; + $function($form, $form_state, $form_id, $group); + } +} + /** * Implementation of hook_form_alter() */ function fieldgroup_form_alter(&$form, $form_state, $form_id) { if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) { - foreach (fieldgroup_groups($form['type']['#value']) as $group_name => $group) { - $form[$group_name] = array( - '#type' => 'fieldset', - '#title' => check_plain(t($group['label'])), - '#collapsed' => $group['settings']['form']['style'] == 'fieldset_collapsed', - '#collapsible' => in_array($group['settings']['form']['style'], array('fieldset_collapsed', 'fieldset_collapsible')), - '#weight' => $group['weight'], - '#description' => content_filter_xss(t($group['settings']['form']['description'])), - '#attributes' => array('class' => strtr($group['group_name'], '_', '-')), - ); - - $has_accessible_field = FALSE; - foreach ($group['fields'] as $field_name => $field) { - if (isset($form[$field_name])) { - $form[$group_name][$field_name] = $form[$field_name]; - // Track whether this group has any accessible fields within it. - if (!isset($form[$field_name]['#access']) || $form[$field_name]['#access'] !== FALSE) { - $has_accessible_field = TRUE; - } - unset($form[$field_name]); - } - } - if (!empty($group['fields']) && !element_children($form[$group_name])) { - //hide the fieldgroup, because the fields are hidden too - unset($form[$group_name]); - } - - if (!$has_accessible_field) { - // Hide the fieldgroup, because the fields are inaccessible. - $form[$group_name]['#access'] = FALSE; - } - - // Allow other modules to alter the form. - // Can't use module_invoke_all because we want - // to be able to use a reference to $form and $form_state. - foreach (module_implements('fieldgroup_form') as $module) { - $function = $module .'_fieldgroup_form'; - $function($form, $form_state, $form_id, $group); + $group_rows = array(); + $field_rows = array(); + + //prepare data that will make this easier + $groups = fieldgroup_groups($form['type']['#value']); + foreach ($groups as $name => $more) { + $group_rows[] = $name; + } + $fields = $form['#field_info']; + foreach ($fields as $name => $more) { + $field_rows[] = $name; + } + + $field_check_off = $field_rows; + //now run through the groups and come up with new weights for display purposes + $max_depth = 0; + foreach ($group_rows as $name) { + $depth = $groups[$name]['depth']; + if ($depth > $max_depth) { + $max_depth = $depth; + } + $parent = $groups[$name]['parent_group_name']; + $pre_weight = $groups[$name]['weight']; + if (isset($parent) && $parent != '') { + if ($pre_weight == 0) { + $pre_weight = 0.5; + } + $weight = $groups[$parent]['weight'] + ($pre_weight * pow(10, (-3 * $depth))); + } + else { + $weight = $pre_weight * pow(10, (-3 * $depth)); + } + $groups[$name]['weight'] = $weight; + //run through the fields and come up with new weights for display purposes + foreach ($groups[$name]['fields'] as $name2 => $elements) { + $pre_weight2 = $groups[$name]['fields'][$name2]['weight']; + $depth2 = $groups[$name]['depth'] + 1; + if ($pre_weight2 == 0) { + $pre_weight2 = 0.5; + } + $weight2 = $groups[$name]['weight'] + ($pre_weight2 * pow(10, (-3 * $depth2))); + $groups[$name]['fields'][$name2]['weight'] = $weight2; + $groups[$name]['fields'][$name2]['depth'] = $depth2; + if (in_array($name2, $field_check_off)) { + $index = array_search($name2, $field_check_off); + unset($field_check_off[$index]); + } } - } - + //cover the top level fields that aren't in fieldgroups + foreach ($field_check_off as $name) { + $form[$name]['#depth'] = 0; + } + + //now that we have the order of things as we want them, let's create the fieldsets for the fieldgroups + foreach ($groups as $group_name => $group) { + _fieldgroup_add_group_to_form($form, $form_state, $form_id, $group_name, $group, $groups); + } + + //reorder the groups from the inside-out in order to avoid a recursive function + while ($max_depth >= 0) { + foreach ($group_rows as $name) { + if ($form[$name]['#depth'] == $max_depth) { + $parent = $form[$name]['#group_parent']; + if (isset($parent) && $parent != '') { + $form[$parent][$name] = $form[$name]; + if ($form[$name]['#access']) { + $form[$parent]['#access'] = TRUE; + } + unset($form[$name]); + } + } + } + $max_depth--; + } } // The group is only added here so it will appear in the export // when using Content Copy. @@ -438,6 +590,10 @@ // Parse incoming rows. $add_field_rows = array('_add_new_field', '_add_existing_field'); $field_rows = array_merge($form['#fields'], $add_field_rows); + $add_group_rows = array($new_group_name); + $group_rows = array_merge($form['#groups'], $add_group_rows); + + foreach ($form_values as $key => $values) { // If 'field' row: update field parenting. if (in_array($key, $field_rows)) { @@ -461,15 +617,18 @@ // TODO: check the parent group does exist ? fieldgroup_update_fields(array('field_name' => $key, 'group' => $parent, 'type_name' => $type_name)); } - - // If 'group' row: update groups weights + } + + foreach ($form_state['values'] as $key => $values) { + // If 'group' row: update groups weights and parent // (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); + if (in_array($key, $group_rows)) { + $parent = ($values['parent'] == '_add_new_group' && isset($new_group_name)) ? $new_group_name : $values['parent']; + $weight = $values['weight']; + db_query("UPDATE {". fieldgroup_tablename() ."} SET weight = %d, parent_group_name = '%s' WHERE type_name = '%s' AND group_name = '%s'", $weight, $parent, $type_name, $key); } } - + cache_clear_all('fieldgroup_data', content_cache_tablename()); } @@ -525,8 +684,64 @@ else { $context = $node->build_mode; } - - foreach (fieldgroup_groups($node->type) as $group_name => $group) { + + //prepare data that will make this easier + $group_rows = array(); + $field_rows = array(); + $groups = fieldgroup_groups($node->type); + foreach ($groups as $name => $more) { + $group_rows[] = $name; + } + $fields = $node->content; + foreach ($fields as $name => $more) { + if (strstr($fields, 'field_')) { + $field_rows[] = $name; + } + } + $field_check_off = $field_rows; + + //now run through the groups and come up with new weights for display purposes + $max_depth = 0; + foreach ($group_rows as $name) { + $depth = $groups[$name]['depth']; + if ($depth > $max_depth) { + $max_depth = $depth; + } + $parent = $groups[$name]['parent_group_name']; + $pre_weight = $groups[$name]['weight']; + if (isset($parent) && $parent != '') { + if ($pre_weight == 0) { + $pre_weight = 0.5; + } + $weight = $groups[$parent]['weight'] + ($pre_weight * pow(10, (-3 * $depth))); + } + else { + $weight = $pre_weight * pow(10, (-3 * $depth)); + } + $groups[$name]['weight'] = $weight; + //run through the fields and come up with new weights for display purposes + foreach ($groups[$name]['fields'] as $name2 => $elements) { + $pre_weight2 = $groups[$name]['fields'][$name2]['weight']; + $depth2 = $groups[$name]['depth'] + 1; + if ($pre_weight2 == 0) { + $pre_weight2 = 0.5; + } + $weight2 = $groups[$name]['weight'] + ($pre_weight2 * pow(10, (-3 * $depth2))); + $groups[$name]['fields'][$name2]['weight'] = $weight2; + $groups[$name]['fields'][$name2]['depth'] = $depth2; + if (in_array($name2, $field_check_off)) { + $index = array_search($name2, $field_check_off); + unset($field_check_off[$index]); + } + } + } + + //cover the top level fields that aren't in fieldgroups + foreach ($field_check_off as $name) { + $node->content[$name]['#depth'] = 0; + } + + foreach ($groups as $group_name => $group) { // Do not include group labels when indexing content. if ($context == NODE_BUILD_SEARCH_INDEX) { $group['settings']['display']['label'] = 'hidden'; @@ -583,14 +798,29 @@ $wrapper = array( 'group' => $element, '#weight' => $group['weight'], + '#depth' => $group['depth'], '#post_render' => array('fieldgroup_wrapper_post_render'), '#group_name' => $group_name, '#type_name' => $node->type, '#context' => $context, + '#group_parent' => $group['parent_group_name'], ); $node->content[$group_name] = $wrapper; } + //reorder the groups from the inside-out in order to avoid a recursive function + while ($max_depth >= 0) { + foreach ($group_rows as $name) { + if ($node->content[$name]['#depth'] == $max_depth) { + $parent = $node->content[$name]['#group_parent']; + if (isset($parent) && $parent != '') { + $node->content[$parent]['group'][$name] = $node->content[$name]; + unset($node->content[$name]); + } + } + } + $max_depth--; + } } break; } @@ -612,7 +842,7 @@ return $content; } -/* +/** * Get the group name for a field. * If the field isn't in a group, FALSE will be returned. * @return The name of the group, or FALSE. @@ -670,7 +900,7 @@ * @todo * Make this into more of a real API for groups. */ -/* +/** * Saves the given group for this content-type */ function fieldgroup_save_group($type_name, $group) { @@ -681,18 +911,19 @@ $function = $module .'_fieldgroup_save_group'; $function($group); } - + if (!isset($groups[$group['group_name']])) { // Accept group name from programmed submissions if valid. - db_query("INSERT INTO {". fieldgroup_tablename() ."} (group_type, type_name, group_name, label, settings, weight)". - " VALUES ('%s', '%s', '%s', '%s', '%s', %d)", $group['group_type'], $type_name, $group['group_name'], $group['label'], serialize($group['settings']), $group['weight']); + db_query("INSERT INTO {". fieldgroup_tablename() ."} (parent_group_name, group_type, type_name, group_name, label, settings, weight)". + " VALUES ('%s','%s', '%s', '%s', '%s', '%s', %d)", + isset($group['parent']) ? $group['parent'] : $group['parent_group_name'], $group['group_type'], $type_name, $group['group_name'], $group['label'], serialize($group['settings']), $group['weight']); cache_clear_all('fieldgroup_data', content_cache_tablename()); return SAVED_NEW; } else { - db_query("UPDATE {". fieldgroup_tablename() ."} SET group_type = '%s', label = '%s', settings = '%s', weight = %d ". - "WHERE type_name = '%s' AND group_name = '%s'", - $group['group_type'], $group['label'], serialize($group['settings']), $group['weight'], $type_name, $group['group_name']); + db_query("UPDATE {". fieldgroup_tablename() ."} SET parent_group_name = '%s', group_type = '%s', label = '%s', settings = '%s', weight = %d ". + "WHERE type_name = '%s' AND group_name = '%s'", + isset($group['parent']) ? $group['parent'] : $group['parent_group_name'], $group['group_type'], $group['label'], serialize($group['settings']), $group['weight'], $type_name, $group['group_name']); cache_clear_all('fieldgroup_data', content_cache_tablename()); return SAVED_UPDATED; } @@ -762,6 +993,7 @@ function fieldgroup_preprocess_fieldgroup_simple(&$vars) { $element = $vars['element']; + $vars['parent_group_name'] = $element['#parent_group_name']; $vars['group_name'] = $element['#group_name']; $vars['group_name_css'] = strtr($element['#group_name'], '_', '-'); $vars['label'] = isset($element['#title']) ? $element['#title'] : '';; Index: modules/nodereference/nodereference.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/nodereference/Attic/nodereference.info,v retrieving revision 1.8 diff -u -r1.8 nodereference.info --- modules/nodereference/nodereference.info 23 Apr 2008 18:02:07 -0000 1.8 +++ modules/nodereference/nodereference.info 25 Feb 2009 16:34:08 -0000 @@ -5,4 +5,10 @@ dependencies[] = text dependencies[] = optionwidgets package = CCK -core = 6.x \ No newline at end of file +core = 6.x +; Information added by drupal.org packaging script on 2009-02-14 +version = "6.x-2.x-dev" +core = "6.x" +project = "cck" +datestamp = "1234612969" + Index: modules/number/number.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/number/Attic/number.info,v retrieving revision 1.7 diff -u -r1.7 number.info --- modules/number/number.info 23 Apr 2008 18:02:16 -0000 1.7 +++ modules/number/number.info 25 Feb 2009 16:34:08 -0000 @@ -3,4 +3,10 @@ description = Defines numeric field types. dependencies[] = content package = CCK -core = 6.x \ No newline at end of file +core = 6.x +; Information added by drupal.org packaging script on 2009-02-14 +version = "6.x-2.x-dev" +core = "6.x" +project = "cck" +datestamp = "1234612969" + Index: modules/optionwidgets/optionwidgets.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/optionwidgets/Attic/optionwidgets.info,v retrieving revision 1.7 diff -u -r1.7 optionwidgets.info --- modules/optionwidgets/optionwidgets.info 23 Apr 2008 18:02:24 -0000 1.7 +++ modules/optionwidgets/optionwidgets.info 25 Feb 2009 16:34:06 -0000 @@ -3,4 +3,10 @@ description = Defines selection, check box and radio button widgets for text and numeric fields. dependencies[] = content package = CCK -core = 6.x \ No newline at end of file +core = 6.x +; Information added by drupal.org packaging script on 2009-02-14 +version = "6.x-2.x-dev" +core = "6.x" +project = "cck" +datestamp = "1234612969" + Index: modules/text/text.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/text/Attic/text.info,v retrieving revision 1.9 diff -u -r1.9 text.info --- modules/text/text.info 23 Apr 2008 18:02:31 -0000 1.9 +++ modules/text/text.info 25 Feb 2009 16:34:08 -0000 @@ -3,4 +3,10 @@ description = Defines simple text field types. dependencies[] = content package = CCK -core = 6.x \ No newline at end of file +core = 6.x +; Information added by drupal.org packaging script on 2009-02-14 +version = "6.x-2.x-dev" +core = "6.x" +project = "cck" +datestamp = "1234612969" + Index: modules/userreference/userreference.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/userreference/Attic/userreference.info,v retrieving revision 1.8 diff -u -r1.8 userreference.info --- modules/userreference/userreference.info 23 Apr 2008 18:02:38 -0000 1.8 +++ modules/userreference/userreference.info 25 Feb 2009 16:34:08 -0000 @@ -5,4 +5,10 @@ dependencies[] = text dependencies[] = optionwidgets package = CCK -core = 6.x \ No newline at end of file +core = 6.x +; Information added by drupal.org packaging script on 2009-02-14 +version = "6.x-2.x-dev" +core = "6.x" +project = "cck" +datestamp = "1234612969" + Index: theme/content-module.css =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/theme/Attic/content-module.css,v retrieving revision 1.1.2.5 diff -u -r1.1.2.5 content-module.css --- theme/content-module.css 27 Oct 2008 16:58:38 -0000 1.1.2.5 +++ theme/content-module.css 25 Feb 2009 16:34:05 -0000 @@ -27,6 +27,40 @@ margin:0; } +.content-multiple-remove-button { + display: block; + float: right; + height: 12px; + width: 16px; + margin: 2px 0 1px 0; + padding: 0; + background:transparent url(../images/button-remove.png) no-repeat 0 0; + border-bottom: #C2C9CE 1px solid; + border-right: #C2C9CE 1px solid; +} +.content-multiple-remove-button:hover { + background-position: 0 -12px; +} +.content-multiple-throbber, +.content-multiple-throbber:hover { + background-image: url(../images/button-throbber.gif); + background-position: 0 0; + border: none; +} +.content-multiple-weight-header, +.content-multiple-remove-header, +.content-multiple-remove-cell, +.content-multiple-table td.delta-order { + text-align: center; +} +html.js .content-multiple-weight-header, +html.js .content-multiple-remove-header, +html.js .content-multiple-removed-row, +html.js .content-multiple-table td.delta-order, +html.js .content-multiple-remove-checkbox { + display: none; +} + .node-form .number { display:inline; width:auto; Index: theme/theme.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/theme/theme.inc,v retrieving revision 1.1.2.12 diff -u -r1.1.2.12 theme.inc --- theme/theme.inc 28 Oct 2008 22:12:09 -0000 1.1.2.12 +++ theme/theme.inc 25 Feb 2009 16:34:05 -0000 @@ -28,10 +28,11 @@ $add_rows[] = $key; } } + $parent_list = array(); + $parent_list['top'] = 'top'; while ($order) { $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)) { @@ -46,9 +47,17 @@ } $row = new stdClass(); - // Add target classes for the tabledrag behavior. - $element['weight']['#attributes']['class'] = 'field-weight'; + if ($element['#row_type'] == 'group') { + $parent_list[$element['group_name']['#value']] = strtr($element['group_name']['#value'], '_', '-'); + } + if (empty($element['parent']['#value']) || !isset($element['parent']['#value'])) { + $element['weight']['#attributes']['class'] = 'field-weight field-weight-' . 'top'; + } + else { + $element['weight']['#attributes']['class'] = 'field-weight field-weight-' . strtr($element['parent']['#value'], '_', '-'); + } + $element['parent']['#attributes']['class'] = 'group-parent'; $element['hidden_name']['#attributes']['class'] = 'field-name'; // Add target classes for the update selects behavior. @@ -74,6 +83,8 @@ $row->class .= isset($element['#add_new']) ? ' content-add-new' : ''; $row->class .= isset($element['#leaf']) ? ' tabledrag-leaf' : ''; $row->class .= isset($element['#root']) ? ' tabledrag-root' : ''; + $row->class .= (isset($element['group_type']['#value']) && $element['group_type']['#value'] == 'Standard group') ? ' tabledrag-standardgroup' : ''; + $row->class .= (isset($element['group_type']['#value']) && $element['group_type']['#value'] == 'Multigroup') ? ' tabledrag-multigroup' : ''; $rows[] = $row; array_shift($order); @@ -82,10 +93,15 @@ $vars['submit'] = drupal_render($form); // Add tabledrag behavior. -// 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'); + //drupal_add_tabledrag('content-field-overview', 'match', 'parent', 'group-parent', 'group-parent', 'field-name', FALSE, 10); + drupal_add_tabledrag('content-field-overview', 'match', 'parent', 'group-parent', 'group-parent', 'field-name', TRUE, 10); + foreach ($parent_list as $name => $parent) { + //drupal_add_tabledrag('content-field-overview', 'order', 'sibling', 'field-weight', 'field-weight-' . $parent, NULL, FALSE); + drupal_add_tabledrag('content-field-overview', 'order', 'sibling', 'field-weight', 'field-weight-' . $parent, NULL, TRUE); + } + + // Override methods in Drupal core tabledrag.js. + drupal_add_js(drupal_get_path('module', 'fieldgroup') .'/fieldgroup.tabledrag.js'); // Add settings for the update selects behavior. $js_fields = array(); --- modules/content_multigroup/content_multigroup-edit.js +++ modules/content_multigroup/content_multigroup-edit.js @@ -0,0 +1,31 @@ +// $Id$ + +/** + * Attach onRemove handlers to content 'remove' buttons. + */ +Drupal.behaviors.contentMultigroupRemoveButtons = function(context) { + $('.content-multiple-remove-checkbox', context).not('.content-multigroup-remove-buttons-processed').each(function() { + $(this).addClass('content-multigroup-remove-buttons-processed'); + + $(this).bind('remove', function(event, settings) { + // Ignore events not targetted to multigroups. + if (settings.type != 'multigroup') { + return; + } + + // Trigger all onRemove event handlers for all widgets in the multigroup. + for (var field_name in Drupal.settings.content_multigroup[settings.name]) { + // Clone and setup the settings object for the widget. + var widget_settings = {}; + for (var i in settings) { + widget_settings[i] = settings[i]; + } + widget_settings.type = Drupal.settings.content_multigroup[settings.name][field_name]; + widget_settings.name = field_name; + + // Trigger the onRemove event handler for a single widget. + $(settings.checkbox).trigger('remove', [event, widget_settings]); + } + }); + }); +}; --- modules/content_multigroup/content_multigroup.info +++ modules/content_multigroup/content_multigroup.info @@ -0,0 +1,6 @@ +name = Content Multigroup +description = Combine multiple CCK fields into repeating field collections that work in unison. +dependencies[] = content +dependencies[] = fieldgroup +package = CCK +core = 6.x --- modules/content_multigroup/content_multigroup.install +++ modules/content_multigroup/content_multigroup.install @@ -0,0 +1,47 @@ += this.rowObject.indents) { + previousRow = previousRow.prev('tr'); + } + // If we found a row. + if (previousRow.length) { + sourceRow = previousRow[0]; + var oldGroupField = $('input.field-name', sourceRow); + var newGroupName = oldGroupField[0].value.replace(/_/g, '-'); + } + // Otherwise we went all the way to the left of the table without finding + // a parent, meaning this item has been placed at the root level. + else { + var newGroupName = "top"; + } + + var oldGroupName = oldWeightField[0].className.replace(/([^ ]+[ ]+)*field-weight-([^ ]+)([ ]+[^ ]+)*/, '$2'); + var oldClass = 'field-weight-' + oldGroupName; + var newClass = 'field-weight-' + newGroupName; + weightField.removeClass(oldClass).addClass(newClass); + + //Now that we've updated the class names, let's make sure the weight is properly set. + //First make sure that if the rows above are nested deeper, that we find their parent, which is a sibling of + //our current row. + while (siblingRowUp.length && $('.indentation', siblingRowUp).length > this.rowObject.indents) { + siblingRowUp = siblingRowUp.prev('tr'); + } + //before we set the new weight, make sure we know whether the previous row is a sibling or a parent + var siblingIndent = $('.indentation', siblingRowUp).length; + var indents = this.rowObject.indents; + + //if we found a sibling + if (siblingIndent == indents) { + //the previous row is a sibling, so let's set the new weight + var siblingWeightFieldUp = $('input.field-weight', siblingRowUp); + var siblingWeightUp = siblingWeightFieldUp[0].value; + var newWeight = parseInt(siblingWeightUp) + 1; + $('input.field-weight', tableDragObject.rowObject.element).val(newWeight); + + //now deal with the case where we moved left/right + if (oldClass != newClass) { + $(tableDragObject.rowObject.element).nextAll('tr').each(function() { + if ($('input.field-weight', this).hasClass(newClass)) { + //let's make sure we grab an actual sibling + var prevRow = $(this).prev('tr'); + while (prevRow.length && $('.indentation', prevRow).length > $('.indentation', this).length) { + prevRow = prevRow.prev('tr'); + } + var prevWeight = $('input.field-weight', prevRow).val(); + var incWeight = parseInt(prevWeight) + 1; + $('input.field-weight', this).val(incWeight); + } + }); + } + } + else { + //the previous row is a parent, which means we're at the top of this group, so set the index to zero + $('input.field-weight', tableDragObject.rowObject.element).val(0); + + //find the row's group to pass in to the each function for comparison since we only want to affect the + //weights in this same group + $(tableDragObject.rowObject.element).nextAll('tr').each(function() { + if ($('input.field-weight', this).hasClass(newClass)) { + //let's make sure we grab an actual sibling + var prevRow = $(this).prev('tr'); + while (prevRow.length && $('.indentation', prevRow).length > $('.indentation', this).length) { + prevRow = prevRow.prev('tr'); + } + var prevWeight = $('input.field-weight', prevRow).val(); + var incWeight = parseInt(prevWeight) + 1; + $('input.field-weight', this).val(incWeight); + } + }); + } +} + +/** +* Determine the valid indentations interval for the row at a given position +* in the table. +* +* @param prevRow +* DOM object for the row before the tested position +* (or null for first position in the table). +* @param nextRow +* DOM object for the row after the tested position +* (or null for last position in the table). +*/ +Drupal.tableDrag.prototype.row.prototype.validIndentInterval = function(prevRow, nextRow) { + var minIndent, maxIndent; + var previousRow = $(this.element).prev('tr'); + var previousMultiRow = previousRow; + var thisRow = $(previousRow).next('tr'); + var realNextRow = $(this.element).next('tr'); + var nextMultiRow = realNextRow; + var thisDepth = $('.indentation', thisRow).size(); + var which = 'standard'; + var rowOne, rowTwo, rowThree, rowFour; + + // Minimum indentation: + // Do not orphan the next row. + minIndent = nextRow ? $('.indentation', nextRow).size() : 0; + + if ($(this.element).is('.tabledrag-multigroup') || $(this.element).is('.tabledrag-standardgroup')) { + //find the first multigroup below where we are + while (nextMultiRow.length && (!($(nextMultiRow).is('.tabledrag-multigroup')) || ($('.indentation', nextMultiRow).size() > $('.indentation', thisRow).size()))) { + nextMultiRow = nextMultiRow.next('tr'); + } + if (!($(nextMultiRow).is('.tabledrag-multigroup'))) { + nextMultiRow = null; + } + //find the first standard group below where we are + while (realNextRow.length && !($(realNextRow).is('.tabledrag-standardgroup'))) { + realNextRow = realNextRow.next('tr'); + } + if (!($(realNextRow).is('.tabledrag-standardgroup'))) { + realNextRow = null; + } + + if (typeof (realNextRow) != 'undefined' && realNextRow != undefined) { + rowOne = realNextRow.get(0); + //alert("rowOne " + rowOne.rowIndex); + } + else { + rowOne = null; + } + if (typeof (nextMultiRow) != 'undefined' && nextMultiRow != undefined) { + rowTwo = nextMultiRow.get(0); + //alert("rowTwo " + rowTwo.rowIndex); + } + else { + rowTwo = null; + } + + if (rowOne == null && rowTwo == null) { + realNextRow = nextRow; + } + else if (rowTwo != null && rowOne == null) { + realNextRow = nextMultiRow; + which = 'multi'; + } + else if (rowTwo != null && rowOne != null && rowTwo.rowIndex < rowOne.rowIndex) { + realNextRow = nextMultiRow; + which = 'multi'; + } + + //find the first multigroup above where we are at + while (previousMultiRow.length && !($(previousMultiRow).is('.tabledrag-multigroup'))) { + previousMultiRow = previousMultiRow.prev('tr'); + } + if (!($(previousMultiRow).is('.tabledrag-multigroup'))) { + previousMultiRow = null; + } + //find the first multigroup above where we are at + while (previousRow.length && !($(previousRow).is('.tabledrag-standardgroup'))) { + previousRow = previousRow.prev('tr'); + } + if (!($(previousRow).is('.tabledrag-standardgroup'))) { + previousRow = null; + } + + if (typeof (previousRow) != 'undefined' && previousRow != undefined) { + rowThree = previousRow.get(0); + //alert("rowThree " + rowThree.rowIndex); + } + else { + rowThree = null; + } + if (typeof (previousMultiRow) != 'undefined' && previousMultiRow != undefined) { + rowFour = previousMultiRow.get(0); + //alert("rowFour " + rowFour.rowIndex); + } + else { + rowFour = null; + } + + if (rowThree == null && rowFour == null) { + previousRow = prevRow; + } + else if (rowFour != null && rowThree == null) { + previousRow = previousMultiRow; + which = 'multi'; + } + else if (rowFour != null && rowThree != null && rowFour.rowIndex > rowThree.rowIndex) { + previousRow = previousMultiRow; + which = 'multi'; + } + + if ($(this.element).is('.tabledrag-multigroup') || $(this.element).is('.tabledrag-standardgroup')) { + if (this.direction != 'down') { + if (which == 'standard') { + maxIndent = $('.indentation', previousRow).size() + 1; + } + else { + maxIndent = $('.indentation', previousRow).size(); + } + //alert("up " + maxIndent); + } + else if (this.direction == 'down') { + if (which == 'standard') { + maxIndent = $('.indentation', realNextRow).size() + 1; + } + else { + maxIndent = $('.indentation', realNextRow).size(); + } + //alert("down " + maxIndent); + } + } + } + else { + // Maximum indentation: + if (!prevRow || $(this.element).is('.tabledrag-root')) { + // Do not indent the first row in the table or 'root' rows. + maxIndent = 0; + } + else { + // Do not go deeper than as a child of the previous row. + maxIndent = $('.indentation', prevRow).size() + ($(prevRow).is('.tabledrag-leaf') ? 0 : 1); + // Limit by the maximum allowed depth for the table. + if (this.maxDepth) { + maxIndent = Math.min(maxIndent, this.maxDepth - (this.groupDepth - this.indents)); + } + } + } + //alert("min " + minIndent); + //alert("max " + maxIndent); + return { 'min': minIndent, 'max': maxIndent }; +} --- theme/content-edit.js +++ theme/content-edit.js @@ -0,0 +1,191 @@ +// $Id$ + +/** + * Manipulation of content remove buttons. + * + * TableDrag objects for multiple value fields (and multigroups) are scanned + * to find 'remove' checkboxes. These checkboxes are hidden when javascript is + * enabled (using the Global CSS Killswitch, html.js, defined in drupal.js). + * A new 'remove' button is created here in place of these checkboxes aimed to + * provide a more user-friendly method to remove items. The onClick handler of + * these buttons fire the onRemove event of the corresponding checkbox that can + * be used by field widgets to perform additional processing. + * + * Custom onRemove handlers are attached to 'remove' checkboxes as follows: + * + * + * $('.content-multiple-remove-checkbox').not('.my-widget-onremove-processed').each(function() { + * // Make sure the element is not processed more than once. + * $(this).addClass('my-widget-onremove-processed'); + * + * // Attach custom onRemove handler to each checkbox. + * $(this).bind('remove', function(event, settings) { + * if (settings.type == 'my_widget_type') { + * // Custom code to process the remove event of the widget. + * } + * }); + * }); + * + * + * Note that onRemove handlers should filter by settings.type and/or + * settings.name before processing the event. + * + * onRemove handlers are invoked with the following arguments: + * event - The onClick event object. + * settings - An object with the following properties: + * tableDrag - The tableDrag object. + * checkbox_id - The ID of the checkbox element. + * checkbox - The DOM object of the remove checkbox element. + * button - The DOM object of the remove button element. + * type - field or multigroup. + * name - The name of the field or multigroup. + * delta - The delta of the field or fields in the subgroup. + */ +Drupal.behaviors.contentRemoveButtons = function(context) { + $('table.content-multiple-table', context).not('.content-multiple-remove-buttons-processed').each(function() { + var table = this, tableDrag = Drupal.tableDrag[$(table).attr('id')]; + + // Make sure the element is not processed more than once. + $(table).addClass('content-multiple-remove-buttons-processed'); + + // Create remove buttons, only when there is more than one item. + if ($('input.content-multiple-remove-checkbox', table).length > 1) { + $('input.content-multiple-remove-checkbox', table).each(function() { + Drupal.contentRemoveButtons.createButton(tableDrag, this); + }); + } + }); +}; + +/** + * Global contentRemoveButtons object. + */ +Drupal.contentRemoveButtons = { + _isBusy: false +}; + +/** + * Create a 'remove' button. + */ +Drupal.contentRemoveButtons.createButton = function(tableDrag, checkbox) { + // Parse checkbox options passed via alt attribute. + var settings = {}, checkbox_alt = $(checkbox).attr('alt'); + if (typeof checkbox_alt != 'string' || checkbox_alt.length <= 0) { + return; + } + checkbox_alt = checkbox_alt.split(','); + for (var i = 0; i < checkbox_alt.length; i++) { + var option = checkbox_alt[i].split(':'); + if (option.length == 2) { + settings[option[0]] = option[1]; + } + } + // Ignore checkbox if required settings are not present. + if (settings.type == undefined || settings.name == undefined || settings.delta == undefined) { + return; + } + + // Create a new remove button. + var button = $(Drupal.theme('contentRemoveButton', settings.type, settings.name, settings.delta)); + + // Complete the button settings. + $.extend(settings, { + tableDrag: tableDrag, + checkbox_id: $(checkbox).attr('id'), + checkbox: checkbox, + button: button.get(0) + }); + + // Bind the onClick event to the 'remove' button. + button.bind('click', function(event) { + $(this).blur(); + + // Do not allow users click on a 'remove' button while the process of + // another one has not been finished yet. + if (!Drupal.contentRemoveButtons._isBusy) { + Drupal.contentRemoveButtons._isBusy = true; + Drupal.contentRemoveButtons.onClick(event, settings); + } + + return false; + }); + + // Attach the new button to the DOM tree. + $(checkbox).parent().append(button); +}; + +/** + * onClick handler for 'remove' buttons. + */ +Drupal.contentRemoveButtons.onClick = function(event, settings) { + // Display the throbber while remove callbacks are being processed. + $(settings.button).addClass('content-multiple-throbber'); + + // Check the checkbox related to the 'remove' button. + $('input[@id='+ settings.checkbox_id +']').attr('checked', 'checked'); + + // Trigger all onRemove event handlers attached to the checkbox. + $(settings.checkbox).trigger('remove', [event, settings]); + + // Hide the table row. + $(settings.button).parents('tr:first').fadeOut('slow', function() { + $(this).addClass('content-multiple-removed-row'); + + // When there's only one delta, we don't want to remove any more items, so + // we hide the table column where the remove checkboxes and buttons are. + if ($('input.content-multiple-remove-checkbox', settings.tableDrag.table).not(':checked').length <= 1) { + $('th.content-multiple-remove-header', settings.tableDrag.table).fadeOut(); + $('td.content-multiple-remove-cell', settings.tableDrag.table).fadeOut(); + } + + // Restripe table rows and display table changed warning. + settings.tableDrag.restripeTable(); + settings.tableDrag.displayChangedWarning(); + + // Disable the busy flag after a while. + setTimeout(function() { Drupal.contentRemoveButtons._isBusy = false; }, 500); + }); + + // Hide the throbber after all remove callbacks have been processed. + $(settings.button).removeClass('content-multiple-throbber'); +}; + +/** + * tableDrag override; Re-assign odd/even classes to all visible table rows. + */ +Drupal.tableDrag.prototype.restripeTable = function() { + var odd = true; + $('tr.draggable:visible', this.table).each(function() { + if (odd) { + $(this).filter('.even').removeClass('even').addClass('odd'); + } + else { + $(this).filter('.odd').removeClass('odd').addClass('even'); + } + odd = !odd; + }); +}; + +/** + * tableDrag extension; Display table change warning when appropriate. + */ +Drupal.tableDrag.prototype.displayChangedWarning = function() { + if (this.changed == false) { + $(Drupal.theme('tableDragChangedWarning')).insertAfter(this.table).hide().fadeIn('slow'); + this.changed = true; + } +}; + +/** + * Theme the 'remove' button. + * + * @param type + * 'field' or 'multigroup'. + * @param name + * The name of the field or multigroup. + * @param delta + * The delta of the field or fields in the subgroup. + */ +Drupal.theme.prototype.contentRemoveButton = function(type, name, delta) { + return ''; +}; --- translations/examples.de.po +++ translations/examples.de.po @@ -0,0 +1,34 @@ +# $Id: examples.de.po,v 1.1.2.1 2008/06/16 19:18:38 hass Exp $ +# German translation of CCK +# Copyright 2006 Lukas Gangoly +# Copyright 2006 Jakob Petsovits +# Generated from files: +# field.php,v 1.3 2006/04/16 13:47:13 luke +# text.module,v 1.34 2006/06/12 19:59:53 luke +# number.module,v 1.28 2006/05/02 13:52:16 luke +# content_admin.inc,v 1.16 2006/06/12 19:36:54 luke +# content.module,v 1.64 2006/06/12 19:36:54 luke +# nodereference.module,v 1.28 2006/06/12 19:36:54 luke +# optionwidgets.module,v 1.8 2006/05/01 15:45:29 luke +# userreference.module,v 1.24 2006/05/05 14:10:44 luke +# weburl.module,v 1.8 2006/06/12 19:36:54 luke +# +msgid "" +msgstr "" +"Project-Id-Version: CCK\n" +"POT-Creation-Date: 2008-06-16 20:05+0200\n" +"PO-Revision-Date: 2008-06-16 20:07+0100\n" +"Last-Translator: Alexander Hass\n" +"Language-Team: German \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"X-Poedit-Language: German\n" +"X-Poedit-Country: GERMANY\n" +"X-Poedit-SourceCharset: utf-8\n" + +#: examples/example_field.php:484 +msgid "Text area" +msgstr "Textfeld" + --- translations/examples.pot +++ translations/examples.pot @@ -0,0 +1,27 @@ +# $Id: examples.pot,v 1.1.2.2 2008/07/03 17:22:39 damz Exp $ +# +# LANGUAGE translation of Drupal (examples) +# Copyright YEAR NAME +# Generated from file: example_field.php,v 1.5.2.2 2008/07/03 12:44:51 yched +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"POT-Creation-Date: 2008-07-03 07:41+0200\n" +"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n" +"Last-Translator: NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: examples/example_field.php:178 +msgid "The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database and it must match the field storage type, %type. The label is optional and the key will be used as the label if no label is specified." +msgstr "" + +#: examples/example_field.php:484 +msgid "Text area" +msgstr "" + --- translations/general.hu.po +++ translations/general.hu.po @@ -0,0 +1,270 @@ +# Hungarian translation of cck (6.x-2.0-rc10) +# Copyright (c) 2008 by the Hungarian translation team +# Generated from files: +# content.admin.inc,v 1.181.2.49 2008/10/06 15:19:05 karens +# content.rules.inc,v 1.1.2.2 2008/10/06 15:02:03 karens +# number.module,v 1.91.2.23 2008/10/06 15:11:39 karens +# text.module,v 1.95.2.20 2008/10/01 17:04:31 karens +# userreference.module,v 1.106.2.21 2008/10/07 10:17:51 karens +# content-admin-field-overview-form.tpl.php,v 1.1.2.4 2008/09/27 14:08:09 karens +# fieldgroup.module,v 1.79.2.34 2008/10/06 15:11:39 karens +# content.module,v 1.301.2.68 2008/10/08 13:29:37 karens +# content.crud.inc,v 1.76.2.11 2008/10/07 11:55:17 karens +# content.info,v 1.6 2007/07/04 23:46:29 yched +# content.views.inc,v 1.1.2.9 2008/10/04 13:14:21 karens +# content.panels.inc,v 1.1.2.2 2008/10/04 13:14:21 karens +# fieldgroup.panels.inc,v 1.1.2.4 2008/10/06 17:21:51 karens +# text.info,v 1.9 2008/04/23 18:02:31 dww +# content_handler_field.inc,v 1.1.2.7 2008/10/04 13:14:21 karens +# content-admin-display-overview-form.tpl.php,v 1.1.2.2 2008/09/23 17:32:31 yched +# nodereference.module,v 1.138.2.38 2008/10/06 15:11:39 karens +# nodereference.rules.inc,v 1.1.2.2 2008/10/06 15:02:03 karens +# userreference.rules.inc,v 1.1.2.2 2008/10/06 15:02:03 karens +# content_copy.module,v 1.27.2.13 2008/10/08 12:55:54 karens +# content_copy.info,v 1.6 2008/04/23 18:01:48 dww +# content_permissions.info,v 1.2 2008/04/23 18:01:52 dww +# fieldgroup.info,v 1.6.2.1 2008/09/22 18:25:21 karens +# nodereference.info,v 1.8 2008/04/23 18:02:07 dww +# number.info,v 1.7 2008/04/23 18:02:16 dww +# optionwidgets.info,v 1.7 2008/04/23 18:02:24 dww +# userreference.info,v 1.8 2008/04/23 18:02:38 dww +# optionwidgets.module,v 1.69.2.18 2008/10/06 15:11:39 karens +# +msgid "" +msgstr "" +"Project-Id-Version: cck (6.x-2.0-rc10)\n" +"POT-Creation-Date: 2008-10-31 12:16-0500\n" +"PO-Revision-Date: 2008-10-26 13:06-0500\n" +"Language-Team: Hungarian\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n!=1);\n" + +#: includes/content.admin.inc:1046,1059; includes/content.rules.inc:94; modules/number/number.module:147,156; modules/text/text.module:103,112 +msgid "Code" +msgstr "Kód" + +#: modules/text/text.module:202; modules/userreference/userreference.module:209 +msgid "Default" +msgstr "Alapértelmezett" + +#: includes/content.admin.inc:16; theme/content-admin-field-overview-form.tpl.php:14 +msgid "Operations" +msgstr "Műveletek" + +#: includes/content.admin.inc:845; modules/fieldgroup/fieldgroup.module:197 +msgid "Cancel" +msgstr "Mégsem" + +#: includes/content.admin.inc:314,626; modules/fieldgroup/fieldgroup.module:165,329 +msgid "Save" +msgstr "Mentés" + +#: includes/content.admin.inc:16; modules/fieldgroup/fieldgroup.module:149 +msgid "Description" +msgstr "Leírás" + +#: content.module:530,537,0; includes/content.crud.inc:585,629 +msgid "content" +msgstr "tartalom" + +#: includes/content.admin.inc:146,172,845; modules/fieldgroup/fieldgroup.module:197 +msgid "Remove" +msgstr "Törlés" + +#: includes/content.admin.inc:16; theme/content-admin-field-overview-form.tpl.php:13 +msgid "Type" +msgstr "Típus" + +#: includes/content.admin.inc:16; theme/content-admin-field-overview-form.tpl.php:12 +msgid "Name" +msgstr "Név" + +#: content.info:0; includes/content.rules.inc:19,195; includes/views/content.views.inc:162,206 +msgid "Content" +msgstr "Tartalom" + +#: includes/content.panels.inc:39; modules/fieldgroup/fieldgroup.panels.inc:70 +msgid "Node" +msgstr "Tartalom" + +#: includes/content.admin.inc:783; includes/content.rules.inc:61 +msgid "Continue" +msgstr "Folytatás" + +#: includes/content.admin.inc:981; modules/fieldgroup/fieldgroup.module:136 +msgid "Help text" +msgstr "Súgó szöveg" + +#: modules/text/text.module:49; modules/text/text.info:0 +msgid "Text" +msgstr "Szöveg" + +#: modules/text/text.module:66,207; modules/userreference/userreference.module:214 +msgid "Plain text" +msgstr "Egyszerű szöveg" + +#: includes/content.admin.inc:219,257,288,754,935; includes/content.panels.inc:49; includes/views/handlers/content_handler_field.inc:43; modules/fieldgroup/fieldgroup.module:108; theme/content-admin-display-overview-form.tpl.php:13; theme/content-admin-field-overview-form.tpl.php:10 +msgid "Label" +msgstr "Címke" + +#: modules/nodereference/nodereference.module:126; modules/userreference/userreference.module:133 +msgid "Provide a comma separated list of arguments to pass to the view." +msgstr "" +"Vesszővel elválasztott lista, amely a nézetnek küldendő " +"paramétereket tartalmazza." + +#: includes/content.rules.inc:53,261; modules/nodereference/nodereference.rules.inc:45; modules/userreference/userreference.rules.inc:47; theme/content-admin-display-overview-form.tpl.php:11 +msgid "Field" +msgstr "Mező" + +#: content.module:82; modules/content_copy/content_copy.module:110 +msgid "Fields" +msgstr "Mezők" + +#: content.module:1771; modules/nodereference/nodereference.module:239 +msgid "Teaser" +msgstr "Bevezető" + +#: content.module:1775; modules/nodereference/nodereference.module:234 +msgid "Full node" +msgstr "Teljes tartalom" + +#: content.module:104; includes/content.admin.inc:541 +msgid "Manage fields" +msgstr "Mezők kezelése" + +#: content.info:0; modules/content_copy/content_copy.info:0; modules/content_permissions/content_permissions.info:0; modules/fieldgroup/fieldgroup.info:0; modules/nodereference/nodereference.info:0; modules/number/number.info:0; modules/optionwidgets/optionwidgets.info:0; modules/text/text.info:0; modules/userreference/userreference.info:0 +msgid "CCK" +msgstr "CCK" + +#: includes/content.admin.inc:547,588; includes/content.panels.inc:53 +msgid "Above" +msgstr "Felette" + +#: includes/content.admin.inc:548; includes/content.panels.inc:54 +msgid "Inline" +msgstr "Beágyazott" + +#: includes/content.admin.inc:1031; modules/number/number.module:140; modules/text/text.module:96 +msgid "PHP code" +msgstr "PHP kód" + +#: includes/content.admin.inc:1040,1194; includes/content.rules.inc:88 +msgid "'@column' => value for @column" +msgstr "„@column” => @column értéke" + +#: includes/content.admin.inc:1042,1196; includes/content.rules.inc:90 +msgid "" +"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" +");" +msgstr "" +"return array(\n" +" 0 => array(@columns),\n" +" // Itt gyakran vége is van. Több értéket is meg lehet adni\n" +" // ha az „alapértelmezett értéknek” több értéke is " +"lehet:\n" +" 1 => array(@columns),\n" +" 2 => ...\n" +");" + +#: includes/content.admin.inc:1060; modules/number/number.module:157; modules/text/text.module:113 +msgid "<none>" +msgstr "<nincs>" + +#: includes/content.admin.inc:1061; modules/number/number.module:158; modules/text/text.module:114 +msgid "You're not allowed to input PHP code." +msgstr "Nem engedélyezett a PHP kód bevitele." + +#: includes/content.admin.inc:1101; modules/content_copy/content_copy.module:200 +msgid "Save field settings" +msgstr "Mező beállításainak mentése" + +#: includes/content.panels.inc:40; modules/fieldgroup/fieldgroup.panels.inc:71 +msgid "Node context" +msgstr "Tartalmi környezet" + +#: modules/fieldgroup/fieldgroup.panels.inc:99; modules/fieldgroup/fieldgroup.info:0 +msgid "Fieldgroup" +msgstr "Mezőcsoport" + +#: modules/nodereference/nodereference.module:99; modules/userreference/userreference.module:106 +msgid "Default Views" +msgstr "Alapértelmezett nézetek" + +#: modules/nodereference/nodereference.module:102; modules/userreference/userreference.module:109 +msgid "Existing Views" +msgstr "Létező nézetek" + +#: modules/nodereference/nodereference.module:123; modules/userreference/userreference.module:130 +msgid "View arguments" +msgstr "A nézet paraméterei" + +#: modules/nodereference/nodereference.module:329; modules/optionwidgets/optionwidgets.module:80; modules/userreference/userreference.module:261 +msgid "Select list" +msgstr "Legördülő lista" + +#: modules/nodereference/nodereference.module:337; modules/optionwidgets/optionwidgets.module:88; modules/userreference/userreference.module:269 +msgid "Check boxes/radio buttons" +msgstr "Jelölőnégyzetek/választógombok" + +#: modules/nodereference/nodereference.module:345; modules/userreference/userreference.module:277 +msgid "Autocomplete text field" +msgstr "Automatikusan kiegészülő szöveges mező" + +#: modules/number/number.module:126; modules/text/text.module:82 +msgid "Allowed values" +msgstr "Megengedett értékek" + +#: modules/number/number.module:132; modules/text/text.module:88 +msgid "Allowed values list" +msgstr "Megengedett értékek" + +#: modules/number/number.module:136; modules/text/text.module:92 +msgid "" +"The possible values this field can contain. Enter one value per line, " +"in the format key|label. The key is the value that will be stored in " +"the database, and it must match the field storage type (%type). The " +"label is optional, and the key will be used as the label if no label " +"is specified.
Allowed HTML tags: @tags" +msgstr "" +"A mező lehetséges értékei. Egy sorban egy értéket lehet megadni " +"kulcs|címke formában. A kulcs értéke kerül az adatbázisba, és " +"ennek meg kell felelnie az adatbázisban tárolt típussal (%type). A " +"címke nem kötelező, ha nincs megadva, akkor a kulcs kerül " +"felhasználásra, mint címke.
Engedélyezett HTML elemek: @tags" + +#: modules/number/number.module:150; modules/text/text.module:106 +msgid "" +"Advanced usage only: PHP code that returns a keyed array of allowed " +"values. Should not include <?php ?> delimiters. If this field is " +"filled out, the array returned by this code will override the allowed " +"values list above." +msgstr "" +"Csak haladóknak: PHP kód, ami visszaadja a megengedett értékek " +"tömbjét. Nem szükséges <?php ?> elemek közé zárni. Ha ez " +"a mező ki van töltve, a kód által visszaadott tömb felülír " +"minden fentebb megadott értéket." + +#: modules/number/number.module:158; modules/text/text.module:114 +msgid "" +"This PHP code was set by an administrator and will override the " +"allowed values list above." +msgstr "" +"Ezt a PHP kódot egy adminisztrátor állította be, és felül fogja " +"írni a fentebb megadott elfogadható értékek listáját." + +#: modules/number/number.module:250; modules/text/text.module:162 +msgid "%name: illegal value." +msgstr "%name: érvénytelen érték." + +#: modules/number/number.module:368; modules/text/text.module:262 +msgid "Text field" +msgstr "Szöveg mező" + --- translations/modules-content.de.po +++ translations/modules-content.de.po @@ -0,0 +1,177 @@ +# $Id: modules-content.de.po,v 1.2.2.1 2008/06/16 19:18:38 hass Exp $ +# German translation of CCK +# Copyright 2006 Lukas Gangoly +# Copyright 2006 Jakob Petsovits +# Generated from files: +# field.php,v 1.3 2006/04/16 13:47:13 luke +# text.module,v 1.34 2006/06/12 19:59:53 luke +# number.module,v 1.28 2006/05/02 13:52:16 luke +# content_admin.inc,v 1.16 2006/06/12 19:36:54 luke +# content.module,v 1.64 2006/06/12 19:36:54 luke +# nodereference.module,v 1.28 2006/06/12 19:36:54 luke +# optionwidgets.module,v 1.8 2006/05/01 15:45:29 luke +# userreference.module,v 1.24 2006/05/05 14:10:44 luke +# weburl.module,v 1.8 2006/06/12 19:36:54 luke +# +msgid "" +msgstr "" +"Project-Id-Version: CCK\n" +"POT-Creation-Date: 2008-06-16 20:05+0200\n" +"PO-Revision-Date: 2008-06-16 20:11+0100\n" +"Last-Translator: Alexander Hass\n" +"Language-Team: German \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"X-Poedit-Language: German\n" +"X-Poedit-Country: GERMANY\n" +"X-Poedit-SourceCharset: utf-8\n" + +#: content.module:25 +msgid "The content module, a required component of the Content Construction Kit (CCK), allows administrators to associate custom fields with content types. In Drupal, content types are used to define the characteristics of a post, including the title and description of the fields displayed on its add and edit pages. Using the content module (and the other helper modules included in CCK), custom fields beyond the default \"Title\" and \"Body\" may be added. CCK features are accessible through tabs on the content types administration page. (See the node module help page for more information about content types.)" +msgstr "" + +#: content.module:26 +msgid "When adding a custom field to a content type, you determine its type (whether it will contain text, numbers, or references to other objects) and how it will be displayed (either as a text field or area, a select box, checkbox, radio button, or autocompleting field). A field may have multiple values (i.e., a \"person\" may have multiple e-mail addresses) or a single value (i.e., an \"employee\" has a single employee identification number). As you add and edit fields, CCK automatically adjusts the structure of the database as necessary. CCK also provides a number of other features, including intelligent caching for your custom data, an import and export facility for content type definitions, and integration with other contributed modules." +msgstr "" + +#: content.module:27 +msgid "Custom field types are provided by a set of optional modules included with CCK (each module provides a different type). The modules page allows you to enable or disable CCK components. A default installation of CCK includes:" +msgstr "" + +#: content.module:29 +msgid "number, which adds numeric field types, in integer, decimal or floating point form. You may define a set of allowed inputs, or specify an allowable range of values. A variety of common formats for displaying numeric data are available." +msgstr "" + +#: content.module:30 +msgid "text, which adds text field types. A text field may contain plain text only, or optionally, may use Drupal's input format filters to securely manage rich text input. Text input fields may be either a single line (text field), multiple lines (text area), or for greater input control, a select box, checkbox, or radio buttons. If desired, CCK can validate the input to a set of allowed values." +msgstr "" + +#: content.module:31 +msgid "nodereference, which creates custom references between Drupal nodes. By adding a nodereference field and two different content types, for instance, you can easily create complex parent/child relationships between data (multiple \"employee\" nodes may contain a nodereference field linking to an \"employer\" node)." +msgstr "" + +#: content.module:32 +msgid "userreference, which creates custom references to your sites' user accounts. By adding a userreference field, you can create complex relationships between your site's users and posts. To track user involvement in a post beyond Drupal's standard Authored by field, for instance, add a userreference field named \"Edited by\" to a content type to store a link to an editor's user account page." +msgstr "" + +#: content.module:33 +msgid "fieldgroup, which creates collapsible fieldsets to hold a group of related fields. A fieldset may either be open or closed by default. The order of your fieldsets, and the order of fields within a fieldset, is managed via a drag-and-drop interface provided by content module." +msgstr "" + +#: content.module:35 +msgid "For more information, see the online handbook entry for CCK or the CCK project page." +msgstr "" + +#: content.module:41 +msgid "Configure how this content type's fields and field labels should be displayed when it's viewed in teaser and full-page mode." +msgstr "Die Anzeige von Feldern und Bezeichnungen dieses Inhaltstyps konfigurieren, wenn dieser im Anrisstext und ganzseiten Modus angezeigt wird." + +#: content.module:44 +msgid "Configure how this content type's fields should be displayed when it's rendered in the following contexts." +msgstr "Die Anzeige von Feldern dieses Inhaltstyps konfigurieren, wenn dieser im folgenden Kontext angezeigt wird." + +#: content.module:48 +msgid "Control the order of fields in the input form." +msgstr "Die Reihenfolge der Felder im Eingabeformular steuern." + +#: content.module:456 +msgid "This field is required." +msgstr "Dieses Feld wird benötigt." + +#: content.module:460 +msgid "!title: !required" +msgstr "!title: !required" + +#: content.module:463 +msgid "Order" +msgstr "Reihenfolge" + +#: content.module:1587 +msgid "RSS Item" +msgstr "RSS-Eintrag" + +#: content.module:1589 +msgid "Search Index" +msgstr "Sucheindex" + +#: content.module:1590 +msgid "Search Result" +msgstr "Suchergebnis" + +#: content.module:1928 +msgid "Language" +msgstr "Sprache" + +#: content.module:1931 +msgid "Taxonomy" +msgstr "Taxonomie" + +#: content.module:1934 +msgid "File attachments" +msgstr "Dateianhänge" + +#: content.module:539 +msgid "Updating field type %type with module %module." +msgstr "Feldtyp %type wird mit Modul %module aktualisiert." + +#: content.module:546 +msgid "Updating widget type %type with module %module." +msgstr "Widget %type wird mit Modul %module aktualisiert." + +#: content.module:87 +msgid "Use PHP input for default and allowed values (dangerous - grant with care)" +msgstr "" + +#: content.module:119 +msgid "Manage fields" +msgstr "Felder verwalten" + +#: content.module:128 +msgid "Display fields" +msgstr "Felder anzeigen" + +#: content.module:137 +msgid "General" +msgstr "Allgemein" + +#: content.module:143 +msgid "Advanced" +msgstr "Erweitert" + +#: content.module:171 +msgid "Remove field" +msgstr "Feld löschen" + +#: content.info:0 +#: includes/content.views.inc:57;94 +msgid "Content" +msgstr "Inhalt" + +#: content.info:0 +msgid "Allows administrators to define new content types." +msgstr "Erlaubt dem Administrator, neue Inhaltstypen zu definieren." + +#: content.info:0 +#: modules/content_copy/content_copy.info:0 +#: modules/content_permissions/content_permissions.info:0 +#: modules/fieldgroup/fieldgroup.info:0 +#: modules/nodereference/nodereference.info:0 +#: modules/number/number.info:0 +#: modules/optionwidgets/optionwidgets.info:0 +#: modules/text/text.info:0 +#: modules/userreference/userreference.info:0 +msgid "CCK" +msgstr "CCK" + +#: examples/example_field.php:107 +#: examples/simple_field.php:115 +#: modules/text/text.module:49 +#: modules/text/text.info:0 +msgid "Text" +msgstr "Text" + +#~ msgid "Add another item" +#~ msgstr "Einen weiteren Eintrag hinzufügen" + --- translations/modules-content.fr.po +++ translations/modules-content.fr.po @@ -0,0 +1,280 @@ +# translation of SB-cck-6.x-2.x-dev.po to +# translation of cck-6.x-2.x-dev.po to +msgid "" +msgstr "" +"Project-Id-Version: SB-cck-6.x-2.x-dev\n" +"POT-Creation-Date: 2008-07-03 07:41+0200\n" +"PO-Revision-Date: 2008-07-03 13:24+0100\n" +"Last-Translator: Damien Tournoud \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Poedit-Language: French\n" +"X-Poedit-Country: France\n" +"X-Poedit-SourceCharset: utf-8\n" +"X-Generator: KBabel 1.11.4\n" + +#: content.module:25 +msgid "" +"The content module, a required component of the Content Construction Kit " +"(CCK), allows administrators to associate custom fields with content types. " +"In Drupal, content types are used to define the characteristics of a post, " +"including the title and description of the fields displayed on its add and " +"edit pages. Using the content module (and the other helper modules included " +"in CCK), custom fields beyond the default \"Title\" and \"Body\" may be " +"added. CCK features are accessible through tabs on the content types administration page. (See the node module help page for more information about content types.)" +msgstr "" +"Le module Content, composant obligatoire du kit CCK (Content Construction " +"Kit) permet aux administrateurs d'associer des champs personnalisés à des " +"types de contenus. Au sein de Drupal, les types de contenus servent à " +"définir les caractéristiques d'une publication, y compris le titre et la " +"description des champs affichés sur ses pages \"ajouter\" et \"éditer\". Le " +"module Content (et les modules auxiliaires inclus dans CCK) permet d'ajouter " +"des champs personnalisés en plus des champs par défaut \"Titre\" et \"Corps" +"\". Les fonctionnalités de CCK sont accessible via différents onglets sur la " +"page d'administration des types de contenus. " +"(Voir la page d'aide du module Node pour plus " +"d'informations sur les types de contenus)." + +#: content.module:26 +msgid "" +"When adding a custom field to a content type, you determine its type " +"(whether it will contain text, numbers, or references to other objects) and " +"how it will be displayed (either as a text field or area, a select box, " +"checkbox, radio button, or autocompleting field). A field may have multiple " +"values (i.e., a \"person\" may have multiple e-mail addresses) or a single " +"value (i.e., an \"employee\" has a single employee identification number). " +"As you add and edit fields, CCK automatically adjusts the structure of the " +"database as necessary. CCK also provides a number of other features, " +"including intelligent caching for your custom data, an import and export " +"facility for content type definitions, and integration with other " +"contributed modules." +msgstr "" +"Lorsque vous ajoutez un champ personnalisé à un type de contenu, vous " +"déterminez son type (c'est-à-dire s'il doit contenir du texte, des nombres " +"ou des références à d'autres objets) et la façon dont il doit être affiché " +"(en tant que champ ou zone de texte, liste de sélection, case à cocher, " +"bouton radio, ou champ à auto-complètement). Un champ peut présenter " +"plusieurs valeurs (par exemple, une \"personne\" peut disposer de plusieurs " +"adresses courriel) ou une seule (par exemple, un \"employé\" possède un " +"numéro d'identification unique). À mesure que vous ajoutez et modifiez des " +"champs, CCK ajuste automatiquement la structure de la base de données en " +"fonction. CCK propose également un certain nombre d'autres fonctionnalités, " +"par exemple un cache intelligent pour vos données personnalisées, des " +"fonctionnalités d'import et d'export pour les définitions de types de " +"contenus, ainsi qu'une intégration à d'autres modules provenant des " +"contributions." + +#: content.module:27 +msgid "" +"Custom field types are provided by a set of optional modules included with " +"CCK (each module provides a different type). The modules page allows you to enable or disable CCK components. A " +"default installation of CCK includes:" +msgstr "" +"Des types de champs personnalisés sont proposés par plusieurs modules " +"optionnels inclus dans CCK (chaque module fournissant un type différent). La " +"page des modules vous permet d'activer ou de " +"désactiver des composants CCK. Une installation par défaut de CCK inclut :" + +#: content.module:29 +msgid "" +"number, which adds numeric field types, in integer, decimal or " +"floating point form. You may define a set of allowed inputs, or specify an " +"allowable range of values. A variety of common formats for displaying " +"numeric data are available." +msgstr "" +"Number, qui ajoute des types de champs numériques (formats entier, " +"décimal ou réel à virgule flottante). Vous pouvez définir un jeu ou un " +"intervalle de valeurs autorisées. Divers formats sont disponibles pour " +"l'affichage des données numériques." + +#: content.module:30 +msgid "" +"text, which adds text field types. A text field may contain plain " +"text only, or optionally, may use Drupal's input format filters to securely " +"manage rich text input. Text input fields may be either a single line (text " +"field), multiple lines (text area), or for greater input control, a select " +"box, checkbox, or radio buttons. If desired, CCK can validate the input to a " +"set of allowed values." +msgstr "" +"Text, qui ajoute des types de champs de texte. Un champ texte peut " +"contenir du texte brut uniquement ou, optionnellement, utiliser les filtres " +"des formats d'entrée que propose Drupal pour gérer en toute sécurité des " +"textes enrichis. Les champs de saisie de texte peuvent être constitués d'une " +"seule ligne (champ texte), de plusieurs lignes (zone de texte) ou, pour un " +"meilleur contrôle des valeurs saisies, une liste de sélection, des cases à " +"cocher ou des boutons radio. Si besoin, CCK peut valider les saisies sur la " +"base d'un ensemble de valeurs autorisées." + +#: content.module:31 +msgid "" +"nodereference, which creates custom references between Drupal " +"nodes. By adding a nodereference field and two different content " +"types, for instance, you can easily create complex parent/child " +"relationships between data (multiple \"employee\" nodes may contain a " +"nodereference field linking to an \"employer\" node)." +msgstr "" +"Node Reference, qui crée des références personnalisées entre nœuds " +"de Drupal. En ajoutant, par exemple, un champ nodereference et deux " +"types de contenus différents, vous pouvez facilement créer des relations " +"complexes de type parent/enfant entre données (par exemple plusieurs nœuds " +"\"employé\" peuvent présenter un champ nodereference pointant vers " +"un même nœud \"employeur\")." + +#: content.module:32 +msgid "" +"userreference, which creates custom references to your sites' user " +"accounts. By adding a userreference field, you can create complex " +"relationships between your site's users and posts. To track user involvement " +"in a post beyond Drupal's standard Authored by field, for instance, " +"add a userreference field named \"Edited by\" to a content type to " +"store a link to an editor's user account page." +msgstr "" +"User reference, qui crée des références personnalisées vers les " +"comptes des utilisateurs de votre site. En ajoutant un champ " +"userreference, vous pouvez créer des relations complexes entre les " +"utilisateurs de votre site et des publications. Ainsi, pour tracer " +"l'implication d'un utilisateur dans une publication (au delà du champ Drupal " +"standard Écrit par), vous pouvez ajouter à un type de contenu un " +"champ userreference intitulé \"Édité par\" pour enregistrer un lien " +"vers la page du compte utilisateur ayant édité la publication." + +#: content.module:33 +msgid "" +"fieldgroup, which creates collapsible fieldsets to hold a group of " +"related fields. A fieldset may either be open or closed by default. The " +"order of your fieldsets, and the order of fields within a fieldset, is " +"managed via a drag-and-drop interface provided by content module." +msgstr "" +"Fieldgroup, qui crée des groupes de champs liés. Les groupe de " +"champ peuvent être repliés, et vous pouvez choisir qu'ils soient dépliés ou " +"repliés par défaut. L'ordre des groupes de champs, ainsi que l'ordre des " +"champs au sein d'un groupe, est géré grâce à l'interface par glisser-déposer " +"fournie par le module Content." + +#: content.module:35 +msgid "" +"For more information, see the online handbook entry for CCK or the CCK project page." +msgstr "" +"Pour plus d'informations, reportez-vous à l'entrée de manuel en ligne " +"relative à CCK ou à la page du projet CCK." + +#: content.module:41 +msgid "" +"Configure how this content type's fields and field labels should be " +"displayed when it's viewed in teaser and full-page mode." +msgstr "" +"Configurez ici la manière dont les champs et étiquettes de champs de ce type " +"de contenu doivent être affichées, lorsque le contenu est vu en mode résumé " +"ou en pleine page." + +#: content.module:44 +msgid "" +"Configure how this content type's fields should be displayed when it's " +"rendered in the following contexts." +msgstr "" +"Configurez ici la façon dont les champs de ce type de contenu doivent être " +"affichés lorsqu'il est rendu dans les contextes suivants." + +#: content.module:48 +msgid "Control the order of fields in the input form." +msgstr "Contrôlez ici l'ordre des champs dans le formulaire de saisie." + +#: content.module:474 +msgid "This field is required." +msgstr "Ce champ est obligatoire." + +#: content.module:478 +msgid "!title: !required" +msgstr "!title : !required" + +#: content.module:481 +msgid "Order" +msgstr "Ordre" + +#: content.module:1640 +msgid "RSS Item" +msgstr "Élément de flux RSS" + +#: content.module:1642 +msgid "Search Index" +msgstr "Index de recherche" + +#: content.module:1643 +msgid "Search Result" +msgstr "Résultat de recherche" + +#: content.module:1981 +msgid "Language" +msgstr "Langue" + +#: content.module:1984 +msgid "Taxonomy" +msgstr "Taxonomie" + +#: content.module:1987 +msgid "File attachments" +msgstr "Fichiers attachés" + +#: content.module:557 +msgid "Updating field type %type with module %module." +msgstr "Mise à jour du type de champ %type avec le module %module." + +#: content.module:564 +msgid "Updating widget type %type with module %module." +msgstr "Mise à jour du type de widget %widget avec le module %module." + +#: content.module:84 +msgid "Use PHP input for field settings (dangerous - grant with care)" +msgstr "" +"Utiliser du code PHP pour le paramétrage des champs (dangereux - à " +"n'autoriser qu'avec précautions)" + +#: content.module:125 +msgid "Manage fields" +msgstr "Gérer les champs" + +#: content.module:134 +msgid "Display fields" +msgstr "Afficher les champs" + +#: content.module:143 +msgid "General" +msgstr "Général" + +#: content.module:149 +msgid "Advanced" +msgstr "Avancé" + +#: content.module:177 +msgid "Remove field" +msgstr "Supprimer un champ" + +#: content.info:0 includes/content.views.inc:54;91 +msgid "Content" +msgstr "Content" + +#: content.info:0 +msgid "Allows administrators to define new content types." +msgstr "Permet aux administrateurs de définir des nouveaux types de contenu." + +#: content.info:0 modules/content_copy/content_copy.info:0 +#: modules/content_permissions/content_permissions.info:0 +#: modules/fieldgroup/fieldgroup.info:0 +#: modules/nodereference/nodereference.info:0 modules/number/number.info:0 +#: modules/optionwidgets/optionwidgets.info:0 modules/text/text.info:0 +#: modules/userreference/userreference.info:0 +msgid "CCK" +msgstr "CCK" + +#: examples/example_field.php:107 examples/simple_field.php:115 +#: modules/text/text.module:49 modules/text/text.info:0 +msgid "Text" +msgstr "Texte" --- translations/modules-content.pot +++ translations/modules-content.pot @@ -0,0 +1,165 @@ +# $Id: modules-content.pot,v 1.1.2.2 2008/07/03 17:22:39 damz Exp $ +# +# LANGUAGE translation of Drupal (root) +# Copyright YEAR NAME +# Generated from files: +# content.module,v 1.301.2.17 2008/07/03 12:44:50 yched +# content.info,v 1.6 2007/07/04 23:46:29 yched +# content.views.inc,v 1.68.2.6 2008/07/03 12:44:51 yched +# content_copy.info,v 1.6 2008/04/23 18:01:48 dww +# content_permissions.info,v 1.2 2008/04/23 18:01:52 dww +# fieldgroup.info,v 1.6 2008/04/23 18:01:58 dww +# nodereference.info,v 1.8 2008/04/23 18:02:07 dww +# number.info,v 1.7 2008/04/23 18:02:16 dww +# optionwidgets.info,v 1.7 2008/04/23 18:02:24 dww +# text.info,v 1.9 2008/04/23 18:02:31 dww +# userreference.info,v 1.8 2008/04/23 18:02:38 dww +# example_field.php,v 1.5.2.2 2008/07/03 12:44:51 yched +# simple_field.php,v 1.5.2.2 2008/07/03 12:44:51 yched +# text.module,v 1.95.2.4 2008/06/28 15:42:41 yched +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"POT-Creation-Date: 2008-07-03 07:41+0200\n" +"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\n" +"Last-Translator: NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: content.module:25 +msgid "The content module, a required component of the Content Construction Kit (CCK), allows administrators to associate custom fields with content types. In Drupal, content types are used to define the characteristics of a post, including the title and description of the fields displayed on its add and edit pages. Using the content module (and the other helper modules included in CCK), custom fields beyond the default \"Title\" and \"Body\" may be added. CCK features are accessible through tabs on the content types administration page. (See the node module help page for more information about content types.)" +msgstr "" + +#: content.module:26 +msgid "When adding a custom field to a content type, you determine its type (whether it will contain text, numbers, or references to other objects) and how it will be displayed (either as a text field or area, a select box, checkbox, radio button, or autocompleting field). A field may have multiple values (i.e., a \"person\" may have multiple e-mail addresses) or a single value (i.e., an \"employee\" has a single employee identification number). As you add and edit fields, CCK automatically adjusts the structure of the database as necessary. CCK also provides a number of other features, including intelligent caching for your custom data, an import and export facility for content type definitions, and integration with other contributed modules." +msgstr "" + +#: content.module:27 +msgid "Custom field types are provided by a set of optional modules included with CCK (each module provides a different type). The modules page allows you to enable or disable CCK components. A default installation of CCK includes:" +msgstr "" + +#: content.module:29 +msgid "number, which adds numeric field types, in integer, decimal or floating point form. You may define a set of allowed inputs, or specify an allowable range of values. A variety of common formats for displaying numeric data are available." +msgstr "" + +#: content.module:30 +msgid "text, which adds text field types. A text field may contain plain text only, or optionally, may use Drupal's input format filters to securely manage rich text input. Text input fields may be either a single line (text field), multiple lines (text area), or for greater input control, a select box, checkbox, or radio buttons. If desired, CCK can validate the input to a set of allowed values." +msgstr "" + +#: content.module:31 +msgid "nodereference, which creates custom references between Drupal nodes. By adding a nodereference field and two different content types, for instance, you can easily create complex parent/child relationships between data (multiple \"employee\" nodes may contain a nodereference field linking to an \"employer\" node)." +msgstr "" + +#: content.module:32 +msgid "userreference, which creates custom references to your sites' user accounts. By adding a userreference field, you can create complex relationships between your site's users and posts. To track user involvement in a post beyond Drupal's standard Authored by field, for instance, add a userreference field named \"Edited by\" to a content type to store a link to an editor's user account page." +msgstr "" + +#: content.module:33 +msgid "fieldgroup, which creates collapsible fieldsets to hold a group of related fields. A fieldset may either be open or closed by default. The order of your fieldsets, and the order of fields within a fieldset, is managed via a drag-and-drop interface provided by content module." +msgstr "" + +#: content.module:35 +msgid "For more information, see the online handbook entry for CCK or the CCK project page." +msgstr "" + +#: content.module:41 +msgid "Configure how this content type's fields and field labels should be displayed when it's viewed in teaser and full-page mode." +msgstr "" + +#: content.module:44 +msgid "Configure how this content type's fields should be displayed when it's rendered in the following contexts." +msgstr "" + +#: content.module:48 +msgid "Control the order of fields in the input form." +msgstr "" + +#: content.module:474 +msgid "This field is required." +msgstr "" + +#: content.module:478 +msgid "!title: !required" +msgstr "" + +#: content.module:481 +msgid "Order" +msgstr "" + +#: content.module:1640 +msgid "RSS Item" +msgstr "" + +#: content.module:1642 +msgid "Search Index" +msgstr "" + +#: content.module:1643 +msgid "Search Result" +msgstr "" + +#: content.module:1981 +msgid "Language" +msgstr "" + +#: content.module:1984 +msgid "Taxonomy" +msgstr "" + +#: content.module:1987 +msgid "File attachments" +msgstr "" + +#: content.module:557 +msgid "Updating field type %type with module %module." +msgstr "" + +#: content.module:564 +msgid "Updating widget type %type with module %module." +msgstr "" + +#: content.module:84 +msgid "Use PHP input for field settings (dangerous - grant with care)" +msgstr "" + +#: content.module:125 +msgid "Manage fields" +msgstr "" + +#: content.module:134 +msgid "Display fields" +msgstr "" + +#: content.module:143 +msgid "General" +msgstr "" + +#: content.module:149 +msgid "Advanced" +msgstr "" + +#: content.module:177 +msgid "Remove field" +msgstr "" + +#: content.info:0 includes/content.views.inc:54;91 +msgid "Content" +msgstr "" + +#: content.info:0 +msgid "Allows administrators to define new content types." +msgstr "" + +#: content.info:0 modules/content_copy/content_copy.info:0 modules/content_permissions/content_permissions.info:0 modules/fieldgroup/fieldgroup.info:0 modules/nodereference/nodereference.info:0 modules/number/number.info:0 modules/optionwidgets/optionwidgets.info:0 modules/text/text.info:0 modules/userreference/userreference.info:0 +msgid "CCK" +msgstr "" + +#: examples/example_field.php:107 examples/simple_field.php:115 modules/text/text.module:49 modules/text/text.info:0 +msgid "Text" +msgstr "" +