diff -Nurp ../cck.orig/includes/content.admin.inc ./includes/content.admin.inc
--- ../cck.orig/includes/content.admin.inc 2010-07-30 11:09:25.000000000 -0500
+++ ./includes/content.admin.inc 2010-07-30 11:11:04.000000000 -0500
@@ -147,6 +147,7 @@ function content_field_overview_form(&$f
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';
@@ -194,6 +195,8 @@ function content_field_overview_form(&$f
// 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'])),
@@ -202,17 +205,23 @@ function content_field_overview_form(&$f
'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;
}
@@ -317,6 +326,9 @@ function content_field_overview_form(&$f
// 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(
@@ -346,9 +358,9 @@ function content_field_overview_form(&$f
'#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',
);
@@ -662,6 +674,7 @@ function content_display_overview_form(&
$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(
@@ -686,6 +699,8 @@ function content_display_overview_form(&
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'));
@@ -1667,6 +1682,31 @@ function _content_overview_order(&$form,
}
}
}
+ // Nested fieldgroup
+ if (module_exists('fieldgroup')) {
+ // readjust the depth and parenting of fieldgroup
+ $nested = array();
+ foreach ($group_rows as $name) {
+ if ($parent = $form[$name]['parent']['#value']) {
+ $form[$name]['#depth'] = $form[$parent]['#depth'] + 1;
+ if (array_key_exists($parent, $nested)){
+ $nested[$parent][$name] = $dummy[$name];
+ $nested[$name] = & $nested[$parent][$name];
+ } else {
+ $dummy[$parent][$name] = $dummy[$name];
+ $nested[$name] = & $dummy[$parent][$name];
+ }
+ unset($dummy[$name]);
+ }
+ }
+ // readjust the depth
+ foreach ($field_rows as $name) {
+ if ($parent = $form[$name]['parent']['#value']) {
+ $form[$name]['#depth'] = $form[$parent]['#depth'] + 1;
+ }
+ }
+ }
+
return $dummy ? explode(' ', trim(drupal_render($dummy))) : array();
}
diff -Nurp ../cck.orig/includes/content.node_form.inc ./includes/content.node_form.inc
--- ../cck.orig/includes/content.node_form.inc 2010-07-30 11:09:25.000000000 -0500
+++ ./includes/content.node_form.inc 2010-07-30 11:11:04.000000000 -0500
@@ -325,12 +325,7 @@ function content_add_more_js($type_name_
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();
@@ -348,7 +343,8 @@ function content_add_more_js($type_name_
$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'] : '') .'
';
@@ -372,3 +368,74 @@ function content_add_more_js($type_name_
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];
+}
diff -Nurp ../cck.orig/modules/fieldgroup/fieldgroup.install ./modules/fieldgroup/fieldgroup.install
--- ../cck.orig/modules/fieldgroup/fieldgroup.install 2010-07-30 11:09:25.000000000 -0500
+++ ./modules/fieldgroup/fieldgroup.install 2010-07-30 11:11:04.000000000 -0500
@@ -52,6 +52,7 @@ function fieldgroup_schema() {
'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 @@ function fieldgroup_update_6007() {
$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
diff -Nurp ../cck.orig/modules/fieldgroup/fieldgroup.module ./modules/fieldgroup/fieldgroup.module
--- ../cck.orig/modules/fieldgroup/fieldgroup.module 2010-07-30 11:09:25.000000000 -0500
+++ ./modules/fieldgroup/fieldgroup.module 2010-07-30 11:13:25.000000000 -0500
@@ -166,6 +166,7 @@ function fieldgroup_group_edit_form(&$fo
$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,6 +211,9 @@ function fieldgroup_remove_group_submit(
$form_values = $form_state['values'];
$content_type = $form['#content_type'];
$group_name = $form['#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' AND type_name = '%s'", $parent_group_name['parent_group_name'], $group_name, $content_type['type']);
+ $result = db_query("UPDATE {". fieldgroup_fields_tablename() ."} SET group_name = '%s' WHERE group_name = '%s' AND type_name = '%s'", $parent_group_name['parent_group_name'], $group_name, $content_type['type']);
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';
@@ -228,45 +232,14 @@ function fieldgroup_groups($content_type
$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();
-
- // Allow external modules to translate field group strings.
- $group_strings = array(
- 'label' => $group['label'],
- 'form_description' => $group['settings']['form']['description'],
- 'display_description' => $group['settings']['display']['description'],
- );
- drupal_alter('content_fieldgroup_strings', $group_strings, $group['type_name'], $group['group_name']);
- $group['label'] = $group_strings['label'];
- $group['settings']['form']['description'] = $group_strings['form_description'];
- $group['settings']['display']['description'] = $group_strings['display_description'];
-
- $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)) {
- // Allow external modules to translate field strings.
- $field_strings = array(
- 'widget_label' => $field['label'],
- 'widget_description' => $field['description'],
- );
- drupal_alter('content_field_strings', $field_strings, $field['type_name'], $field['field_name']);
- $field['label'] = $field_strings['widget_label'];
- $field['description'] = $field_strings['widget_description'];
-
- $groups[$field['type_name']][$field['group_name']]['fields'][$field['field_name']] = $field;
- }
- cache_set('fieldgroup_data:'. $language->language, array('groups' => $groups, 'groups_sorted' => $groups_sorted), content_cache_tablename());
+ cache_set('fieldgroup_data', array('groups' => $groups, 'groups_sorted' => $groups_sorted), content_cache_tablename());
}
}
if (empty($content_type)) {
@@ -278,6 +251,89 @@ function fieldgroup_groups($content_type
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)) {
+ $group['settings'] = unserialize($group['settings']);
+ $group['fields'] = array();
+
+ // Allow external modules to translate field group strings.
+ $group_strings = array(
+ 'label' => $group['label'],
+ 'form_description' => $group['settings']['form']['description'],
+ 'display_description' => $group['settings']['display']['description'],
+ );
+ drupal_alter('content_fieldgroup_strings', $group_strings, $type_name, $group['group_name']);
+ $group['label'] = $group_strings['label'];
+ $group['settings']['form']['description'] = $group_strings['form_description'];
+ $group['settings']['display']['description'] = $group_strings['display_description'];
+
+ $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;
+ }
+ //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;
+
+ // Unserialize arrays.
+ foreach (array('widget_settings', 'display_settings', 'global_settings', 'db_columns') as $key) {
+ $groups[$field['type_name']][$field['group_name']]['fields'][$field['field_name']] = (!empty($groups[$field['type_name']][$field['group_name']]['fields'][$field['field_name']][$key])) ? (array) unserialize($groups[$field['type_name']][$field['group_name']]['fields'][$field['field_name']][$key]) : array();
+ }
+
+ // For fields inside of groups, use the weight given by fieldgroup
+ $groups[$field['type_name']][$field['group_name']]['fields'][$field['field_name']]['weight'] = $field['weight'];
+ }
+ }
+
+ $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);
@@ -293,51 +349,157 @@ function _fieldgroup_field_get_group($co
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);
+ }
+}
+
+
+/**
+ * This is function fieldgroup_order_fields_and_groups
+ *
+ * @param array $group_rows An empty array that we will fill.
+ * @param array $groups All of the info we need about all of the groups for the content type we're working on.
+ * @param array $field_check_off This contains the fields. We will unset them as we process them.
+ *
+ */
+function fieldgroup_order_fields_groups(&$group_rows, &$groups, &$field_check_off) {
+ $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'];
+
+ //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) {
+ $depth2 = $groups[$name]['depth'] + 1;
+ $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]);
+ }
+ }
+ }
+ }
+ return $max_depth;
+}
+
/**
* 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;
+ $group_rows = array();
+ $field_rows = array();
+
+ //prepare data that will make this easier
+ $groups = fieldgroup_groups($form['type']['#value']);
+ if (!empty($groups)) {
+ foreach ($groups as $name => $more) {
+ $group_rows[] = $name;
+ }
+ }
+
+ $fields = $form['#field_info'];
+ if (!empty($fields)) {
+ foreach ($fields as $name => $more) {
+ $field_rows[] = $name;
+ }
+ }
+
+ $max_depth = fieldgroup_order_fields_groups($group_rows, $groups, $field_rows);
+
+ //cover the top level fields that aren't in fieldgroups
+ if (isset($field_rows)) {
+ foreach ($field_rows 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]);
}
- 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);
- }
-
+ $max_depth--;
}
}
@@ -463,6 +625,10 @@ function fieldgroup_field_overview_form_
// 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)) {
@@ -486,12 +652,15 @@ function fieldgroup_field_overview_form_
// 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);
}
}
@@ -519,6 +688,18 @@ function fieldgroup_display_overview_for
foreach ($form_values as $key => $values) {
if (in_array($key, $form['#groups'])) {
$group = $groups[$key];
+ // Multigroup data (namely, subgroup data) ends up in the settings array as well
+ // Prevent this data from ending up inside of ['settings']['display']
+ if (is_array($values['settings']) && array_key_exists('multigroup', $values['settings'])){
+ if (is_array($group['settings']['multigroup'])){
+ foreach ($values['settings']['multigroup'] as $mg_key => $mg_values){
+ $group['settings']['multigroup'][$mg_key] = $values['settings']['multigroup'][$mg_key];
+ }
+ } else {
+ $group['settings']['multigroup'] = $values['settings']['multigroup'];
+ }
+ unset($values['settings']['multigroup']);
+ }
// We have some numeric keys here, so we can't use array_merge.
$group['settings']['display'] = $values + $group['settings']['display'];
fieldgroup_save_group($form['#type_name'], $group);
@@ -543,10 +724,51 @@ function fieldgroup_nodeapi(&$node, $op,
case 'view':
// Prevent against invalid 'nodes' built by broken 3rd party code.
if (isset($node->type)) {
+ //prepare data that will make this easier
+ $group_rows = array();
+ $field_rows = array();
+ $groups = fieldgroup_groups($node->type);
+ if (!empty($groups)) {
+ foreach ($groups as $name => $more) {
+ $group_rows[] = $name;
+ }
+ }
+
+ $fields = $node->content;
+ if (!empty($fields)) {
+ foreach ($fields as $name => $more) {
+ if (is_string($name) && strstr($name, 'field_')) {
+ $field_rows[] = $name;
+ }
+ }
+ }
+
+ $max_depth = fieldgroup_order_fields_groups($group_rows, $groups, $field_rows);
+
+ //cover the top level fields that aren't in fieldgroups
+ if (isset($field_rows)) {
+ foreach ($field_rows as $name) {
+ $node->content[$name]['#depth'] = 0;
+ }
+ }
+
// Build the node content element needed to render each fieldgroup.
- foreach (fieldgroup_groups($node->type) as $group) {
+ foreach ($groups as $group) {
fieldgroup_build_content($group, $node, $teaser, $page);
}
+ //reorder the groups from the inside-out in order to avoid writing 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;
}
@@ -611,6 +833,8 @@ function fieldgroup_build_content($group
foreach ($group['fields'] as $field_name => $field) {
if (isset($node->content[$field_name])) {
$element[$field_name] = $node->content[$field_name];
+ $element[$field_name]['#weight'] = $field['weight'];
+ $element[$field_name]['#depth'] = $field['depth'];
}
}
@@ -636,10 +860,12 @@ function fieldgroup_build_content($group
$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;
@@ -734,7 +960,7 @@ function fieldgroup_wrapper_post_render(
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.
@@ -792,7 +1018,7 @@ function fieldgroup_fields_tablename($ve
* @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) {
@@ -803,18 +1029,19 @@ function fieldgroup_save_group($type_nam
$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(), TRUE);
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(), TRUE);
return SAVED_UPDATED;
}
@@ -884,6 +1111,7 @@ function theme_fieldgroup_fieldset($elem
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'] : '';;
@@ -910,4 +1138,4 @@ function fieldgroup_preprocess_node(&$va
// '#chilren' might not be set if the group is empty.
$vars[$group_name .'_rendered'] = isset($node->content[$group_name]['#children']) ? $node->content[$group_name]['#children'] : '';
}
-}
\ No newline at end of file
+}
diff -Nurp ../cck.orig/modules/fieldgroup/fieldgroup.module.orig ./modules/fieldgroup/fieldgroup.module.orig
--- ../cck.orig/modules/fieldgroup/fieldgroup.module.orig 1969-12-31 18:00:00.000000000 -0600
+++ ./modules/fieldgroup/fieldgroup.module.orig 2010-07-30 11:11:04.000000000 -0500
@@ -0,0 +1,1133 @@
+content.
+ * - hook_fieldgroup_form: Alter the group portion of the node form.
+ * - hook_fieldgroup_types: Add additional fieldgroup group_types.
+ * - hook_fieldgroup_default_settings: Add additional fieldgroup default settings.
+ * - hook_fieldgroup_save: Do additional processing when a fieldgroup is saved.
+ */
+/**
+ * Implementation of hook_init().
+ */
+function fieldgroup_init() {
+ drupal_add_css(drupal_get_path('module', 'fieldgroup') .'/fieldgroup.css');
+}
+
+/**
+ * Implementation of hook_ctools_plugin_directory().
+ */
+function fieldgroup_ctools_plugin_directory($module, $plugin) {
+ if ($module == 'ctools' && $plugin == 'content_types') {
+ return 'panels/' . $plugin;
+ }
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function fieldgroup_menu() {
+ $items = array();
+
+ // Make sure this doesn't fire until content_types is working,
+ // needed to avoid errors on initial installation.
+ if (!defined('MAINTENANCE_MODE')) {
+ foreach (node_get_types() as $type) {
+ $type_name = $type->type;
+ $content_type = content_types($type_name);
+ $type_url_str = $content_type['url_str'];
+ $items['admin/content/node-type/'. $type_url_str .'/groups/%'] = array(
+ 'title' => 'Edit group',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('fieldgroup_group_edit_form', $type_name, 5),
+ 'access arguments' => array('administer content types'),
+ 'type' => MENU_CALLBACK,
+ );
+ $items['admin/content/node-type/'. $type_url_str .'/groups/%/remove'] = array(
+ 'title' => 'Edit group',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('fieldgroup_remove_group', $type_name, 5),
+ 'access arguments' => array('administer content types'),
+ 'type' => MENU_CALLBACK,
+ );
+ }
+ }
+ return $items;
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function fieldgroup_theme() {
+ return array(
+ 'fieldgroup_simple' => array(
+ 'template' => 'fieldgroup-simple',
+ 'arguments' => array('element' => NULL),
+ ),
+ 'fieldgroup_fieldset' => array(
+ 'arguments' => array('element' => NULL),
+ ),
+ 'fieldgroup_display_overview_form' => array(
+ 'arguments' => array('form' => NULL),
+ ),
+ );
+}
+
+/**
+ * Implementation of hook_elements().
+ */
+function fieldgroup_elements() {
+ return array(
+ 'fieldgroup_simple' => array(),
+ 'fieldgroup_fieldset' => array('#collapsible' => FALSE, '#collapsed' => FALSE, '#value' => NULL,),
+ );
+}
+
+/**
+ * Implementation of hook_fieldapi().
+ */
+function fieldgroup_content_fieldapi($op, $field) {
+ switch ($op) {
+ case 'delete instance':
+ db_query("DELETE FROM {". fieldgroup_fields_tablename() ."} WHERE field_name = '%s' AND type_name = '%s'", $field['field_name'], $field['type_name']);
+ cache_clear_all('fieldgroup_data:', content_cache_tablename(), TRUE);
+ break;
+ }
+}
+
+function fieldgroup_group_edit_form(&$form_state, $type_name, $group_name) {
+ $content_type = content_types($type_name);
+ $groups = fieldgroup_groups($content_type['type']);
+
+ if (!$group = $groups[$group_name]) {
+ drupal_not_found();
+ exit;
+ }
+
+ $form['label'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Label'),
+ '#default_value' => $group['label'],
+ '#required' => TRUE,
+ );
+
+ // Set a default value for group type early in the form so it
+ // can be overridden by subsequent form elements added by other modules.
+ $group_type = !empty($group['group_type']) ? $group['group_type'] : 'standard';
+ $form['group_type'] = array('#type' => 'hidden', '#default_value' => $group_type);
+
+ $form['settings']['#tree'] = TRUE;
+ $form['settings']['form'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Form settings'),
+ '#description' => t('These settings apply to the group in the node editing form.'),
+ );
+ $form['settings']['form']['style'] = array(
+ '#type' => 'radios',
+ '#title' => t('Style'),
+ '#default_value' => $group['settings']['form']['style'],
+ '#options' => array(
+ 'fieldset' => t('always open'),
+ 'fieldset_collapsible' => t('collapsible'),
+ 'fieldset_collapsed' => t('collapsed'),
+ )
+ );
+ $form['settings']['form']['description'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Help text'),
+ '#default_value' => $group['settings']['form']['description'],
+ '#rows' => 5,
+ '#description' => t('Instructions to present to the user on the editing form.'),
+ '#required' => FALSE,
+ );
+ $form['settings']['display'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Display settings'),
+ '#description' => t('These settings apply to the group on node display.'),
+ );
+ $form['settings']['display']['description'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Description'),
+ '#default_value' => $group['settings']['display']['description'],
+ '#rows' => 5,
+ '#description' => t('A description of the group.'),
+ '#required' => FALSE,
+ );
+
+ foreach (array_keys(content_build_modes()) as $key) {
+ $form['settings']['display'][$key]['format'] = array('#type' => 'value', '#value' => isset($group['settings']['display'][$key]['format']) ? $group['settings']['display'][$key]['format'] : 'fieldset');
+ $form['settings']['display'][$key]['exclude'] = array('#type' => 'value', '#value' => isset($group['settings']['display'][$key]['exclude']) ? $group['settings']['display'][$key]['exclude'] : 0);
+ }
+ $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;
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ '#weight' => 10,
+ );
+
+ return $form;
+}
+
+function fieldgroup_group_edit_form_submit($form, &$form_state) {
+ $form_values = $form_state['values'];
+ $content_type = $form['#content_type'];
+ fieldgroup_save_group($content_type['type'], $form_values);
+ $form_state['redirect'] = 'admin/content/node-type/'. $content_type['url_str'] .'/fields';
+}
+
+function fieldgroup_remove_group(&$form_state, $type_name, $group_name) {
+ $content_type = content_types($type_name);
+ $groups = fieldgroup_groups($content_type['type']);
+ $group = isset($groups[$group_name]) ? $groups[$group_name] : '';
+
+ if (empty($group)) {
+ drupal_not_found();
+ exit;
+ }
+
+ $form['#submit'][] = 'fieldgroup_remove_group_submit';
+ $form['#content_type'] = $content_type;
+ $form['#group_name'] = $group_name;
+
+ return confirm_form($form,
+ t('Are you sure you want to remove the group %label?',
+ array('%label' => t($group['label']))),
+ 'admin/content/node-type/'. $content_type['url_str'] .'/fields', t('This action cannot be undone.'),
+ t('Remove'), t('Cancel'));
+}
+
+function fieldgroup_remove_group_submit($form, &$form_state) {
+ $form_values = $form_state['values'];
+ $content_type = $form['#content_type'];
+ $group_name = $form['#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) {
+ global $language;
+ static $groups, $groups_sorted;
+ if (!isset($groups) || $reset) {
+ if ($cached = cache_get('fieldgroup_data:'. $language->language, content_cache_tablename())) {
+ $data = $cached->data;
+ $groups = $data['groups'];
+ $groups_sorted = $data['groups_sorted'];
+ }
+ else {
+ $result = db_query("SELECT * FROM {". fieldgroup_tablename() ."} ORDER BY type_name, weight");
+ $groups = array();
+ $groups_sorted = array();
+ while ($group = db_fetch_array($result)) {
+ $groups[$group['type_name']] = _fieldgroup_get_tree($group['type_name']);
+ $groups_sorted[$group['type_name']][] = &$groups[$group['type_name']][$group['group_name']];
+ }
+ cache_set('fieldgroup_data', array('groups' => $groups, 'groups_sorted' => $groups_sorted), content_cache_tablename());
+ }
+ }
+ if (empty($content_type)) {
+ return $groups;
+ }
+ elseif (empty($groups) || empty($groups[$content_type])) {
+ return array();
+ }
+ 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)) {
+ $group['settings'] = unserialize($group['settings']);
+ $group['fields'] = array();
+
+ // Allow external modules to translate field group strings.
+ $group_strings = array(
+ 'label' => $group['label'],
+ 'form_description' => $group['settings']['form']['description'],
+ 'display_description' => $group['settings']['display']['description'],
+ );
+ drupal_alter('content_fieldgroup_strings', $group_strings, $type_name, $group['group_name']);
+ $group['label'] = $group_strings['label'];
+ $group['settings']['form']['description'] = $group_strings['form_description'];
+ $group['settings']['display']['description'] = $group_strings['display_description'];
+
+ $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;
+ }
+ //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);
+
+ $labels[''] = '<'. t('none') .'>';
+ foreach ($groups as $group_name => $group) {
+ $labels[$group_name] = t($group['label']);
+ }
+ return $labels;
+}
+
+function _fieldgroup_field_get_group($content_type, $field_name) {
+ 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);
+ }
+}
+
+
+/**
+ * This is function fieldgroup_order_fields_and_groups
+ *
+ * @param array $group_rows An empty array that we will fill.
+ * @param array $groups All of the info we need about all of the groups for the content type we're working on.
+ * @param array $field_check_off This contains the fields. We will unset them as we process them.
+ *
+ */
+function fieldgroup_order_fields_groups(&$group_rows, &$groups, &$field_check_off) {
+ $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'];
+
+ //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) {
+ $depth2 = $groups[$name]['depth'] + 1;
+ $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]);
+ }
+ }
+ }
+ }
+ return $max_depth;
+}
+
+/**
+ * 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) {
+ $group_rows = array();
+ $field_rows = array();
+
+ //prepare data that will make this easier
+ $groups = fieldgroup_groups($form['type']['#value']);
+ if (!empty($groups)) {
+ foreach ($groups as $name => $more) {
+ $group_rows[] = $name;
+ }
+ }
+
+ $fields = $form['#field_info'];
+ if (!empty($fields)) {
+ foreach ($fields as $name => $more) {
+ $field_rows[] = $name;
+ }
+ }
+
+ $max_depth = fieldgroup_order_fields_groups($group_rows, $groups, $field_rows);
+
+ //cover the top level fields that aren't in fieldgroups
+ if (isset($field_rows)) {
+ foreach ($field_rows 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.
+ elseif ($form_id == 'content_field_edit_form' && isset($form['widget'])) {
+ $content_type = content_types($form['type_name']['#value']);
+ $form['widget']['group'] = array(
+ '#type' => 'value',
+ '#value' => _fieldgroup_field_get_group($content_type['type'], $form['field_name']['#value']),
+ );
+ }
+ elseif ($form_id == 'content_field_overview_form') {
+ $form['#validate'][] = 'fieldgroup_field_overview_form_validate';
+ $form['#submit'][] = 'fieldgroup_field_overview_form_submit';
+ }
+ elseif ($form_id == 'content_display_overview_form' && !empty($form['#groups'])) {
+ $form['#submit'][] = 'fieldgroup_display_overview_form_submit';
+ if (!isset($form['submit'])) {
+ $form['submit'] = array('#type' => 'submit', '#value' => t('Save'), '#weight' => 10);
+ }
+ }
+ elseif ($form_id == 'content_field_remove_form') {
+ $form['#submit'][] = 'fieldgroup_field_remove_form_submit';
+ }
+}
+
+/**
+ * API for group name validation.
+ *
+ * Pulled into separate function to be re-usable.
+ */
+function fieldgroup_validate_name($group, $type_name) {
+ $errors = array();
+
+ // No label.
+ if (!$group['label']) {
+ $errors['label'][] = t('You need to provide a label.');
+ }
+
+ // No group name.
+ if (!$group['group_name']) {
+ $errors['group_name'][] = t('You need to provide a group name.');
+ }
+ // Group name validation.
+ else {
+ $group_name = $group['group_name'];
+ $group['group_type'] = !empty($group['group_type']) ? $group['group_type'] : 'standard';
+
+ // Add the 'group_' prefix.
+ if (substr($group_name, 0, 6) != 'group_') {
+ $group_name = 'group_'. $group_name;
+ }
+
+ // Invalid field name.
+ if (!preg_match('!^group_[a-z0-9_]+$!', $group_name)) {
+ $errors['group_name'][] = t('The group name %group_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', array('%group_name' => $group_name));
+ }
+ if (strlen($group_name) > 32) {
+ $errors['group_name'][] = t('The group name %group_name is too long. The name is limited to 32 characters, including the \'group_\' prefix.', array('%group_name' => $group_name));
+ }
+
+ // Group name already exists.
+ $groups = fieldgroup_groups($type_name);
+ if (isset($groups[$group_name])) {
+ $errors['group_name'][] = t('The group name %group_name already exists.', array('%group_name' => $group_name));
+ }
+ if (empty($errors['group_name'])) {
+ $group['group_name'] = $group_name;
+ }
+ }
+ return array('group_name' => $group['group_name'], 'errors' => $errors);
+}
+
+function fieldgroup_field_overview_form_validate($form, &$form_state) {
+ $form_values = $form_state['values'];
+ $group = $form_values['_add_new_group'];
+
+ if (array_filter(array($group['label'], $group['group_name']))) {
+ $validation = fieldgroup_validate_name($group, $form['#type_name']);
+ if (!empty($validation['errors'])) {
+ foreach ($validation['errors'] as $type => $messages) {
+ foreach ($messages as $message) {
+ if ($type == 'label') {
+ form_set_error('_add_new_group][label', t('Add new group:') .' '. $message);
+ }
+ else {
+ form_set_error('_add_new_group][group_name', t('Add new group:') .' '. $message);
+ }
+ }
+ }
+ }
+ $group_name = $validation['group_name'];
+ form_set_value($form['_add_new_group']['group_name'], $group_name, $form_state);
+ }
+ else {
+ // Fail validation if attempt to nest fields under a new group without the
+ // proper information. Not raising an error would cause the nested fields
+ // to get weights the user doesn't expect.
+
+ foreach ($form_values as $key => $values) {
+ if ($values['parent'] == '_add_new_group') {
+ form_set_error('_add_new_group][label', t('Add new group: you need to provide a label.'));
+ form_set_error('_add_new_group][group_name', t('Add new group: you need to provide a group name.'));
+ break;
+ }
+ }
+ }
+}
+
+function fieldgroup_field_overview_form_submit($form, &$form_state) {
+ $form_values = $form_state['values'];
+ $type_name = $form['#type_name'];
+
+ // Create new group if needed.
+ if (!empty($form_values['_add_new_group']['label'])) {
+ $group = $form_values['_add_new_group'];
+ $group['settings'] = field_group_default_settings($group['group_type']);
+ fieldgroup_save_group($type_name, $group);
+ $new_group_name = $group['group_name'];
+ }
+
+ // 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)) {
+ // If newly added fields were added to a group:
+ if (in_array($key, $add_field_rows)) {
+ // We replace the '_add_*_field' key with the actual name of
+ // the field that got added.
+ // content_field_overview_form_submit() placed those
+ // in $form_state['fields_added'] for us.
+ if (isset($form_state['fields_added'][$key])) {
+ $key = $form_state['fields_added'][$key];
+ }
+ else {
+ // No field was actually created : skip to next row.
+ continue;
+ }
+ }
+ // If the field was added to the newly created group, replace the
+ // '_add_new_group' value with the actual name of the group.
+ $parent = ($values['parent'] == '_add_new_group' && isset($new_group_name)) ? $new_group_name : $values['parent'];
+ // TODO: check the parent group does exist ?
+ fieldgroup_update_fields(array('field_name' => $key, 'group' => $parent, 'type_name' => $type_name));
+ }
+ }
+
+ 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).
+ 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(), TRUE);
+}
+
+function field_group_default_settings($group_type) {
+ $settings = array(
+ 'form' => array('style' => 'fieldset', 'description' => ''),
+ 'display' => array('description' => '', 'label' => 'above'),
+ );
+ module_load_include('inc', 'content', 'includes/content.admin');
+ foreach (array_keys(content_build_modes()) as $key) {
+ $settings['display'][$key]['format'] = 'fieldset';
+ $settings['display'][$key]['exclude'] = 0;
+ }
+ // Allow other modules to add new default settings.
+ $settings = array_merge($settings, module_invoke_all('fieldgroup_default_settings', $group_type));
+ return $settings;
+}
+
+function fieldgroup_display_overview_form_submit($form, &$form_state) {
+ $form_values = $form_state['values'];
+ $groups = fieldgroup_groups($form['#type_name']);
+ foreach ($form_values as $key => $values) {
+ if (in_array($key, $form['#groups'])) {
+ $group = $groups[$key];
+ // Multigroup data (namely, subgroup data) ends up in the settings array as well
+ // Prevent this data from ending up inside of ['settings']['display']
+ if (is_array($values['settings']) && array_key_exists('multigroup', $values['settings'])){
+ if (is_array($group['settings']['multigroup'])){
+ foreach ($values['settings']['multigroup'] as $mg_key => $mg_values){
+ $group['settings']['multigroup'][$mg_key] = $values['settings']['multigroup'][$mg_key];
+ }
+ } else {
+ $group['settings']['multigroup'] = $values['settings']['multigroup'];
+ }
+ unset($values['settings']['multigroup']);
+ }
+ // We have some numeric keys here, so we can't use array_merge.
+ $group['settings']['display'] = $values + $group['settings']['display'];
+ fieldgroup_save_group($form['#type_name'], $group);
+ }
+ }
+}
+
+function fieldgroup_field_remove_form_submit($form, &$form_state) {
+ $form_values = $form_state['values'];
+ // TODO:
+ // - when a (non last) field is removed from a group, a 'ghost row' remains in the fields overview
+ // - when the last field is removed, the group disappears
+ // seems to be fixed when emptying the cache.
+ db_query("DELETE FROM {". fieldgroup_fields_tablename() ."} WHERE type_name = '%s' AND field_name = '%s'", $form_values['type_name'], $form_values['field_name']);
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ */
+function fieldgroup_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
+ switch ($op) {
+ case 'view':
+ // Prevent against invalid 'nodes' built by broken 3rd party code.
+ if (isset($node->type)) {
+ //prepare data that will make this easier
+ $group_rows = array();
+ $field_rows = array();
+ $groups = fieldgroup_groups($node->type);
+ if (!empty($groups)) {
+ foreach ($groups as $name => $more) {
+ $group_rows[] = $name;
+ }
+ }
+
+ $fields = $node->content;
+ if (!empty($fields)) {
+ foreach ($fields as $name => $more) {
+ if (is_string($fields) && strstr($fields, 'field_')) {
+ $field_rows[] = $name;
+ }
+ }
+ }
+
+ $max_depth = fieldgroup_order_fields_groups($group_rows, $groups, $field_rows);
+
+ //cover the top level fields that aren't in fieldgroups
+ if (isset($field_rows)) {
+ foreach ($field_rows as $name) {
+ $node->content[$name]['#depth'] = 0;
+ }
+ }
+
+ // Build the node content element needed to render each fieldgroup.
+ foreach ($groups as $group) {
+ fieldgroup_build_content($group, $node, $teaser, $page);
+ }
+ //reorder the groups from the inside-out in order to avoid writing 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;
+ }
+}
+
+/**
+ * Build the node content element needed to render a fieldgroup.
+ *
+ * @param $group
+ * The field group definition.
+ * @param $node
+ * The node containing the field group to display. Can be a 'pseudo-node',
+ * containing at least 'type', 'nid', 'vid', and the content for fields
+ * required for the group.
+ * @param $teaser
+ * @param $page
+ * Similar to hook_nodeapi('view').
+ *
+ * @see fieldgroup_nodeapi()
+ * @see fieldgroup_view_group()
+ */
+function 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;
+ }
+
+ $group_name = $group['group_name'];
+
+ // 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];
+ $element[$field_name]['#weight'] = $field['weight'];
+ $element[$field_name]['#depth'] = $field['depth'];
+ }
+ }
+
+ // 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);
+ }
+
+ // Unset the original field values now that we've moved them.
+ 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;
+}
+
+/**
+ * Render a single field group, fully themed with label.
+ *
+ * To be used by third-party code (Panels, ...) that needs to output an
+ * isolated field group. Do *not* use inside node templates, use the
+ * $GROUP_NAME_rendered variables instead. You can also use the 'simple'
+ * style format and override the template fieldgroup-simple.tpl.php.
+ *
+ * By default, the field group is displayed using the settings defined for the
+ * 'full node' or 'teaser' contexts (depending on the value of the $teaser param).
+ * Set $node->build_mode to a different value to use a different context.
+ *
+ * Different settings can be specified by adjusting $group['settings']['display'].
+ *
+ * @param $group
+ * The field group definition.
+ * @param $node
+ * The node containing the field group to display. Can be a 'pseudo-node',
+ * containing at least 'type', 'nid', 'vid', and the field data required
+ * for the group.
+ * @param $teaser
+ * @param $page
+ * Similar to hook_nodeapi('view').
+ * @return
+ * The themed output for the field group.
+ *
+ * @see content_view_field()
+ */
+function fieldgroup_view_group($group, &$node, $teaser = FALSE, $page = FALSE) {
+ $group_name = $group['group_name'];
+ $field_types = _content_field_types();
+
+ // Clone the node to prevent from altering the original.
+ $node_copy = drupal_clone($node);
+
+ // Use 'full'/'teaser' if not specified otherwise.
+ $node_copy->build_mode = isset($node_copy->build_mode) ? $node_copy->build_mode : NODE_BUILD_NORMAL;
+
+ // Build the content element for individual fields in the field group.
+ if (!isset($node_copy->content)) {
+ $node_copy->content = array();
+ }
+ foreach (array_keys($group['fields']) as $field_name) {
+ $field = content_fields($field_name, $node_copy->type);
+
+ if (isset($node_copy->{$field_name})) {
+ $items = $node_copy->{$field_name};
+
+ // One-field equivalent to _content_field_invoke('sanitize').
+ $module = $field_types[$field['type']]['module'];
+ $function = $module .'_field';
+ if (function_exists($function)) {
+ $function('sanitize', $node_copy, $field, $items, $teaser, $page);
+ $node_copy->{$field_name} = $items;
+ }
+
+ $field_view = content_field('view', $node_copy, $field, $items, $teaser, $page);
+ // content_field('view') adds a wrapper to handle variables and 'excluded'
+ // fields for node templates. We bypass it and get the actual field.
+ $node_copy->content[$field_name] = $field_view[$field_name];
+ }
+ }
+
+ // Build the content element of the field group itself.
+ fieldgroup_build_content($group, $node_copy, $teaser, $page);
+
+ // fieldgroup_build_content() adds a wrapper to handle variables and 'excluded'
+ // groups for node templates. We bypass it and render the actual field group.
+ $output = drupal_render($node_copy->content[$group_name]['group']);
+
+ return $output;
+}
+
+/**
+ * Hide specified fields from the $content variable in node templates.
+ */
+function fieldgroup_wrapper_post_render($content, $element) {
+ $groups = fieldgroup_groups($element['#type_name']);
+ $group = $groups[$element['#group_name']];
+
+ // The display settings are not in quite the same place in the
+ // group and the field, so create the value the theme will expect.
+ $group['display_settings'] = $group['settings']['display'];
+ if (theme('content_exclude', $content, $group, $element['#context'])) {
+ return '';
+ }
+ 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.
+ */
+function fieldgroup_get_group($content_type, $field_name) {
+ foreach (fieldgroup_groups($content_type) as $group_name => $group) {
+ if (in_array($field_name, array_keys($group['fields']))) {
+ return $group_name;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Implementation of hook_node_type()
+ * React to change in node types
+ */
+function fieldgroup_node_type($op, $info) {
+ if ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) {
+ // update the tables
+ db_query("UPDATE {". fieldgroup_tablename() ."} SET type_name='%s' WHERE type_name='%s'", array($info->type, $info->old_type));
+ db_query("UPDATE {". fieldgroup_fields_tablename() ."} SET type_name='%s' WHERE type_name='%s'", array($info->type, $info->old_type));
+ cache_clear_all('fieldgroup_data:', content_cache_tablename(), TRUE);
+ }
+ elseif ($op == 'delete') {
+ db_query("DELETE FROM {". fieldgroup_tablename() ."} WHERE type_name = '%s'", $info->type);
+ db_query("DELETE FROM {". fieldgroup_fields_tablename() ."} WHERE type_name = '%s'", $info->type);
+ }
+}
+
+function fieldgroup_types() {
+ $types = array('standard' => t('Standard group'));
+ // Allow other modules to add new group_types.
+ $types = array_merge($types, module_invoke_all('fieldgroup_types'));
+ return $types;
+}
+
+function fieldgroup_tablename($version = NULL) {
+ if (is_null($version)) {
+ $version = variable_get('fieldgroup_schema_version', 0);
+ }
+ return $version < 6000 ? 'node_group' : 'content_group';
+}
+
+function fieldgroup_fields_tablename($version = NULL) {
+ if (is_null($version)) {
+ $version = variable_get('fieldgroup_schema_version', 0);
+ }
+ return $version < 6000 ? 'node_group_fields' : 'content_group_fields';
+}
+
+/**
+ * CRUD API for fieldgroup module.
+ *
+ * @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) {
+ $groups = fieldgroup_groups($type_name);
+
+ // Allow other modules to intervene when the group is saved.
+ foreach (module_implements('fieldgroup_save_group') as $module) {
+ $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() ."} (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(), TRUE);
+ return SAVED_NEW;
+ }
+ else {
+ 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(), TRUE);
+ return SAVED_UPDATED;
+ }
+}
+
+function fieldgroup_update_fields($form_values) {
+ $default = _fieldgroup_field_get_group($form_values['type_name'], $form_values['field_name']);
+
+ if ($default != $form_values['group']) {
+ if ($form_values['group'] && !$default) {
+ db_query("INSERT INTO {". fieldgroup_fields_tablename() ."} (type_name, group_name, field_name) VALUES ('%s', '%s', '%s')", $form_values['type_name'], $form_values['group'], $form_values['field_name']);
+ }
+ elseif ($form_values['group']) {
+ db_query("UPDATE {". fieldgroup_fields_tablename() ."} SET group_name = '%s' WHERE type_name = '%s' AND field_name = '%s'", $form_values['group'], $form_values['type_name'], $form_values['field_name']);
+ }
+ else {
+ db_query("DELETE FROM {". fieldgroup_fields_tablename() ."} WHERE type_name = '%s' AND field_name = '%s'", $form_values['type_name'], $form_values['field_name']);
+ }
+ cache_clear_all('fieldgroup_data:', content_cache_tablename(), TRUE);
+ }
+}
+
+function fieldgroup_delete($content_type, $group_name) {
+ db_query("DELETE FROM {". fieldgroup_tablename() ."} WHERE type_name = '%s' AND group_name = '%s'", $content_type, $group_name);
+ db_query("DELETE FROM {". fieldgroup_fields_tablename() ."} WHERE type_name = '%s' AND group_name = '%s'", $content_type, $group_name);
+ cache_clear_all('fieldgroup_data:', content_cache_tablename(), TRUE);
+}
+
+/**
+ * Format a fieldgroup using a 'fieldset'.
+ *
+ * Derived from core's theme_fieldset, with no output if the content is empty.
+ */
+function theme_fieldgroup_fieldset($element) {
+ if (empty($element['#children']) && empty($element['#value'])) {
+ return '';
+ }
+
+ if ($element['#collapsible']) {
+ drupal_add_js('misc/collapse.js');
+
+ if (!isset($element['#attributes']['class'])) {
+ $element['#attributes']['class'] = '';
+ }
+
+ $element['#attributes']['class'] .= ' collapsible';
+ if ($element['#collapsed']) {
+ $element['#attributes']['class'] .= ' collapsed';
+ }
+ }
+ return '\n";
+}
+
+
+/**
+ * Process variables for fieldgroup.tpl.php.
+ *
+ * The $variables array contains the following arguments:
+ * - $group_name
+ * - $group_name_css
+ * - $label
+ * - $description
+ * - $content
+ *
+ * @see fieldgroup-simple.tpl.php
+ */
+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'] : '';;
+ $vars['description'] = isset($element['#description']) ? $element['#description'] : '';;
+ $vars['content'] = isset($element['#children']) ? $element['#children'] : '';
+ $vars['template_files'] = array(
+ 'fieldgroup-simple-',
+ 'fieldgroup-simple-'. $element['#group_name'],
+ 'fieldgroup-simple-'. $element['#node']->type,
+ 'fieldgroup-simple-'. $element['#group_name'] .'-'. $element['#node']->type,
+ );
+}
+
+/**
+ * Theme preprocess function for node.
+ *
+ * Adds $GROUP_NAME_rendered variables,
+ * containing the themed output for the whole group.
+ */
+function fieldgroup_preprocess_node(&$vars) {
+ $node = $vars['node'];
+
+ foreach (fieldgroup_groups($node->type) as $group_name => $group) {
+ // '#chilren' might not be set if the group is empty.
+ $vars[$group_name .'_rendered'] = isset($node->content[$group_name]['#children']) ? $node->content[$group_name]['#children'] : '';
+ }
+}
diff -Nurp ../cck.orig/modules/fieldgroup/fieldgroup.tabledrag.js ./modules/fieldgroup/fieldgroup.tabledrag.js
--- ../cck.orig/modules/fieldgroup/fieldgroup.tabledrag.js 1969-12-31 18:00:00.000000000 -0600
+++ ./modules/fieldgroup/fieldgroup.tabledrag.js 2010-07-30 11:11:04.000000000 -0500
@@ -0,0 +1,249 @@
+// $Id$
+
+/**
+ * @file
+ * Override Drupal tabledrag methods to add support for nested fieldgroups.
+ */
+
+/**
+ * Adjust the classes associated with a row's weight when it is dropped.
+ * Use the parent's name to base the change on.
+ */
+Drupal.tableDrag.prototype.onDrop = function() {
+ tableDragObject = this;
+ var siblingRowUp = $(tableDragObject.rowObject.element).prev('tr');
+ var siblingRowsDown = $(tableDragObject.rowObject.element).nextAll('tr');
+ var weightField = $('input.field-weight', tableDragObject.rowObject.element);
+ var siblingWeightFieldDown = $('input.field-weight', siblingRowsDown);
+ var oldWeightField = $('input.field-weight', tableDragObject.rowObject.element);
+ var previousRow = $(tableDragObject.rowObject.element).prev('tr');
+
+
+ while (previousRow.length && $('.indentation', previousRow).length >= 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);
+ if (siblingWeightFieldUp[0] != undefined){
+ 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 };
+}
diff -Nurp ../cck.orig/theme/theme.inc ./theme/theme.inc
--- ../cck.orig/theme/theme.inc 2010-07-30 11:09:25.000000000 -0500
+++ ./theme/theme.inc 2010-07-30 11:11:04.000000000 -0500
@@ -28,6 +28,8 @@ function template_preprocess_content_fie
$add_rows[] = $key;
}
}
+ $parent_list = array();
+ $parent_list['top'] = 'top';
while ($order) {
$key = reset($order);
$element = &$form[$key];
@@ -48,7 +50,16 @@ function template_preprocess_content_fie
$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 +85,8 @@ function template_preprocess_content_fie
$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 +95,15 @@ function template_preprocess_content_fie
$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();