Index: includes/content.admin.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/includes/Attic/content.admin.inc,v retrieving revision 1.181.2.68.2.3 diff -u -r1.181.2.68.2.3 content.admin.inc --- includes/content.admin.inc 4 Jul 2009 11:58:16 -0000 1.181.2.68.2.3 +++ includes/content.admin.inc 8 Jul 2009 21:13:08 -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; } @@ -308,6 +317,9 @@ // Additional row : add new group. if (!empty($group_types)) { + $current_group_options = $group_options; + $options = fieldgroup_types(); + unset($current_group_options[_add_new_group]); $weight++; $name = '_add_new_group'; $form[$name] = array( @@ -334,9 +346,9 @@ '#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', ); @@ -650,6 +662,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( @@ -674,6 +687,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')); @@ -1629,12 +1644,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. @@ -1642,12 +1673,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.19.2.2 diff -u -r1.7.2.19.2.2 content.node_form.inc --- includes/content.node_form.inc 1 Jul 2009 18:37:26 -0000 1.7.2.19.2.2 +++ includes/content.node_form.inc 8 Jul 2009 21:19:03 -0000 @@ -468,12 +468,7 @@ 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(); @@ -490,7 +485,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'] : '') .'
'; @@ -514,3 +510,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_multigroup/content_multigroup.admin.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/content_multigroup/Attic/content_multigroup.admin.inc,v retrieving revision 1.1.2.2 diff -u -r1.1.2.2 content_multigroup.admin.inc --- modules/content_multigroup/content_multigroup.admin.inc 18 Jun 2009 19:59:00 -0000 1.1.2.2 +++ modules/content_multigroup/content_multigroup.admin.inc 8 Jul 2009 21:23:15 -0000 @@ -46,6 +46,7 @@ // See if we have fields moving into or out of a Multigroup. // Set any fields to use the new name here so they will get processed // correctly by the fieldgroup module when saved. + $group_rows = array(); foreach ($form_values as $key => $values) { if ($values['parent'] == '_add_new_group') { $values['parent'] = $new_group_name; @@ -56,6 +57,7 @@ // Gather up info about all groups. $group_name = $form_values[$key]['group']['group_name']; $groups[$group_name] = $form_values[$key]['group']; + $group_rows[$group_name] = $group_name; } if (!empty($form[$key]['#row_type']) && $form[$key]['#row_type'] == 'field') { if ($values['prev_parent'] != $values['parent']) { @@ -66,6 +68,18 @@ } $rebuild = FALSE; + + foreach ($groups as $key => $values) { + if (in_array($key, $group_rows)) { + if (!empty($groups[$key]['parent']) && $groups[$groups[$key]['parent']]['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'); + } + } + } + foreach ($fields as $field_name => $field) { $new_group = $form_values[$field_name]['parent']; $old_group = $form_values[$field_name]['prev_parent']; Index: modules/content_multigroup/content_multigroup.node_form.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/modules/content_multigroup/Attic/content_multigroup.node_form.inc,v retrieving revision 1.1.2.5 diff -u -r1.1.2.5 content_multigroup.node_form.inc --- modules/content_multigroup/content_multigroup.node_form.inc 1 Jul 2009 18:37:26 -0000 1.1.2.5 +++ modules/content_multigroup/content_multigroup.node_form.inc 13 Jul 2009 18:25:13 -0000 @@ -24,7 +24,7 @@ $group_fields = array(); foreach ($content_type['fields'] as $field_name => $field) { if (isset($group['fields'][$field_name]) && isset($form[$group_name][$field_name])) { - if (!isset($form[$group_name][$field_name]['#access']) || $form[$group_name][$field_name]['#access']) { + if ($group['group_type'] != 'multigroup' || (isset($form[$group_name]['#access']) && $form[$group_name]['#access'] != TRUE) || empty($form[$group_name])) { $group_fields[$field_name] = $field; } } @@ -107,9 +107,11 @@ } foreach ($group_deltas as $delta) { - content_multigroup_group_form($form, $form_state, $group, $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 (array_keys($group_fields) as $field_name) { unset($form[$group_name][$field_name]); @@ -126,36 +128,49 @@ * Called in form_alter and by AHAH add more. */ function content_multigroup_group_form(&$form, &$form_state, $group, $delta) { + module_load_include('inc', 'content', 'includes/content.node_form'); + $element = array(); + $type_name = $group['type_name']; + $content_type = content_types($type_name); $group_name = $group['group_name']; - if ($group['group_type'] != 'multigroup' || empty($form[$group_name]) || empty($form['#multigroups']) || empty($form['#multigroups'][$group_name])) { + + 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; } - module_load_include('inc', 'content', 'includes/content.node_form'); $node = $form['#node']; $group_fields = $form['#multigroups'][$group_name]; $group_multiple = $group['settings']['multigroup']['multiple']; foreach ($group_fields as $field_name => $field) { - if (empty($form[$group_name][$delta])) { - $form[$group_name] += array($delta => array($field_name => array())); + 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(); } - - $item_count = (isset($form_state['item_count'][$group_name]) ? $form_state['item_count'][$group_name] : $form[$group_name]['#item_count']); - $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' => $item_count, // this 'delta' is the 'weight' element's property '#default_value' => $delta, '#weight' => 100, ); - + // 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) { - $form[$group_name][$delta]['_remove'] = array( + $element[$group_name][$delta]['_remove'] = array( '#type' => 'checkbox', '#attributes' => array('class' => 'content-multiple-remove-checkbox'), '#default_value' => isset($form_state['multigroup_removed'][$group_name][$delta]) ? $form_state['multigroup_removed'][$group_name][$delta] : 0, @@ -200,7 +215,7 @@ 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 { // When the form is submitted, get the element data from the form values. @@ -224,14 +239,16 @@ $field_form[$field_name]['#element_validate'] = array(); } $field_form[$field_name]['#element_validate'][] = 'content_multigroup_fix_multivalue_fields'; - - $form[$group_name][$delta][$field_name] = $field_form[$field_name]; + + $element[$group_name][$delta][$field_name] = $field_form[$field_name]; } - $form[$group_name][$delta][$field_name]['#weight'] = $field['widget']['weight']; + $element[$group_name][$delta][$field_name]['#weight'] = $field['widget']['weight']; } // Reset the form '#node' back to its original value. $form['#node'] = $node; + + return $element; } /** @@ -696,15 +713,16 @@ // Build our new form element for the whole group, asking for one more element. $delta = max(array_keys($_POST[$group_name])) + 1; $form_state['item_count'] = array($group_name => count($_POST[$group_name]) + 1); - content_multigroup_group_form($form, $form_state, $group, $delta); - + $form_element = content_multigroup_group_form($form, $form_state, $group, $delta); // Rebuild weight deltas to make sure they all are equally dimensioned. - foreach ($form[$group_name] as $key => $item) { + foreach ($form_element[$group_name] as $key => $item) { if (is_numeric($key) && isset($item['_weight']) && is_array($item['_weight'])) { - $form[$group_name][$key]['_weight']['#delta'] = $delta; + $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); @@ -720,7 +738,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'] : '') .'
'; 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 6 Jul 2009 22:53:11 -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.48.2.2 diff -u -r1.79.2.48.2.2 fieldgroup.module --- modules/fieldgroup/fieldgroup.module 12 Jul 2009 20:50:10 -0000 1.79.2.48.2.2 +++ modules/fieldgroup/fieldgroup.module 13 Jul 2009 18:32:18 -0000 @@ -75,6 +75,9 @@ 'fieldgroup_display_overview_form' => array( 'arguments' => array('form' => NULL), ), + 'fieldgroup_content_type' => array( + 'arguments' => array('vars' => NULL, 'nid' => NULL), + ), ); } @@ -166,6 +169,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; @@ -210,12 +214,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) { @@ -227,23 +234,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()); } } @@ -256,6 +253,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); @@ -271,53 +331,159 @@ 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 { + if ($result = db_result(db_query("SELECT group_name FROM {". fieldgroup_fields_tablename() ."} WHERE type_name = '%s' AND field_name = '%s'", $content_type, $name))) { + $parents[$counter] = $result; + } + } + 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']; + if (!empty($fields)) { + 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 + if (isset($groups[$name]['fields'])) { + 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 + if (isset($field_check_off)) { + 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]); + $index = array_search($name, $group_rows); + unset($group_rows[$index]); + } + } + } + $max_depth--; } - } // The group is only added here so it will appear in the export // when using Content Copy. @@ -441,6 +607,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)) { @@ -464,15 +634,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()); } @@ -521,9 +694,150 @@ case 'view': // Prevent against invalid 'nodes' built by broken 3rd party code. if (isset($node->type)) { - // Build the node content element needed to render each fieldgroup. - foreach (fieldgroup_groups($node->type) as $group) { - fieldgroup_build_content($group, $node, $teaser, $page); + // NODE_BUILD_NORMAL is 0, and ('whatever' == 0) is TRUE, so we need a ===. + if ($node->build_mode === NODE_BUILD_NORMAL || $node->build_mode == NODE_BUILD_PREVIEW) { + $context = $teaser ? 'teaser' : 'full'; + } + else { + $context = $node->build_mode; + } + + //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'; + } + $label = $group['settings']['display']['label'] == 'above'; + $element = array( + '#title' => $label ? check_plain(t($group['label'])) : '', + '#description' => $label ? content_filter_xss(t($group['settings']['display']['description'])) : '', + ); + $format = isset($group['settings']['display'][$context]['format']) ? $group['settings']['display'][$context]['format'] : 'fieldset'; + + switch ($format) { + case 'simple': + $element['#type'] = 'fieldgroup_simple'; + $element['#group_name'] = $group_name; + $element['#node'] = $node; + break; + case 'hidden': + $element['#access'] = FALSE; + break; + + case 'fieldset_collapsed': + $element['#collapsed'] = TRUE; + case 'fieldset_collapsible': + $element['#collapsible'] = TRUE; + case 'fieldset': + $element['#type'] = 'fieldgroup_fieldset'; + $element['#attributes'] = array('class' => 'fieldgroup '. strtr($group['group_name'], '_', '-')); + break; + } + foreach ($group['fields'] as $field_name => $field) { + if (isset($node->content[$field_name])) { + $element[$field_name] = $node->content[$field_name]; + } + } + + // Allow other modules to alter the group view. + // Can't use module_invoke_all because we want + // to be able to use a reference to $node and $element. + foreach (module_implements('fieldgroup_view') as $module) { + $function = $module .'_fieldgroup_view'; + $function($node, $element, $group, $context); + } + + foreach ($group['fields'] as $field_name => $field) { + if (isset($node->content[$field_name])) { + unset($node->content[$field_name]); + } + } + + // The wrapper lets us get the themed output for the group + // to populate the $GROUP_NAME_rendered variable for node templates, + // and hide it from the $content variable if needed. + // See fieldgroup_preprocess_node(), theme_fieldgroup_wrapper(). + $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; @@ -712,7 +1026,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. @@ -770,7 +1084,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) { @@ -781,18 +1095,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; } @@ -862,6 +1177,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: theme/theme.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/cck/theme/theme.inc,v retrieving revision 1.1.2.13.2.1 diff -u -r1.1.2.13.2.1 theme.inc --- theme/theme.inc 7 Jun 2009 00:31:35 -0000 1.1.2.13.2.1 +++ theme/theme.inc 13 Jul 2009 22:32:43 -0000 @@ -28,6 +28,8 @@ $add_rows[] = $key; } } + $parent_list = array(); + $parent_list['top'] = 'top'; while ($order) { $key = reset($order); $element = &$form[$key]; @@ -46,9 +48,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 +84,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 +94,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();