'. $info[$field['type']]['label'] .': '; + $description .= $info[$field['type']]['description'] .'
'; + $form['#prefix'] = ''. t('These settings apply to the %field field everywhere it is used. These settings impact the way that data is stored in the database and cannot be changed once data has been created.', array( + '%field' => $instance['label'])) .'
'; + + // Create a form structure for the field values. + $form['field'] = array( + '#type' => 'fieldset', + '#title' => t('%field field settings', array('%field' => $instance['label'])), + '#description' => $description, + '#tree' => TRUE, + ); + + // See if data already exists for this field. + // If so, prevent changes to the field settings. + $has_data = cck_field_has_data($field_name); + if ($has_data) { + $form['field']['#description'] = '!sampleTo figure out the expected format, you can use the devel load tab provided by devel module on a %type content page. The option to embed PHP code in the field definition is provided for backwards compatibility and could be deprecated in the future. It is strongly recommended that you move this code to a custom function in a custom module and simply identify the custom function in the box above!', array( + '!sample' => $sample, + '@link_devel' => 'http://www.drupal.org/project/devel', + '%type' => $instance['bundle'])), + ); + } + else { + $form['instance']['widget']['default_value_widget']['advanced_options']['markup_default_value_php'] = array( + '#type' => 'item', + '#title' => t('Code'), + '#value' => !empty($instance['widget']['default_value_php']) ? '
'. check_plain($instance['widget']['default_value_php']) .'
' : t('<none>'),
+ '#description' => empty($instance['widget']['default_value_php']) ? t("You're not allowed to input PHP code.") : t('This PHP code was set by an administrator and will override any value specified above.'),
+ );
+ }
+}
+
+/**
+ * Validate a field's settings.
+ */
+function cck_field_edit_form_validate($form, &$form_state) {
+
+ $form_values = $form_state['values'];
+ $instance = $form_values['instance'];
+ $field = field_info_field($instance['field_name']);
+
+ $field_type = field_info_field_types($field['type']);
+ $widget_type = field_info_widget_types($instance['widget']['type']);
+
+ // TODO D7 : deprecate hook_*_settings_form_validate in favor of regular FAPI validation ?
+ module_invoke($widget_type['module'], 'widget_settings_form_validate', array_merge($instance, $form_values));
+ module_invoke($field_type['module'], 'field_settings_form_validate', array_merge($instance, $form_values));
+
+ // If field.module is handling the default value,
+ // validate the result using the field validation.
+ if (field_behaviors_widget('default value', $instance) == FIELD_BEHAVIOR_DEFAULT) {
+
+ // If this is a programmed form, get rid of the default value widget,
+ // we have the default values already.
+ if ($form['#programmed']) {
+ form_set_value(array('#parents' => array('instance', 'widget', 'default_value_widget')), NULL, $form_state);
+ return;
+ }
+
+ if (isset($form_values['instance']['widget']['default_value_php']) &&
+ ($php = trim($form_values['instance']['widget']['default_value_php']))) {
+ ob_start();
+ $return = eval($php);
+ ob_end_clean();
+ if (!is_array($return)) {
+ $error = TRUE;
+ }
+ else {
+ foreach ($return as $item) {
+ if (!is_array($item)) {
+ $error = TRUE;
+ break;
+ }
+ }
+ }
+ if ($error) {
+ foreach (array_keys($field['columns']) as $column) {
+ $columns[] = t("'@column' => value for @column", array('@column' => $column));
+ }
+ $sample = t("return array(\n 0 => array(@columns),\n You'll usually want to stop here. Provide more values\n if you want your 'default value' to be multi-valued:\n 1 => array(@columns),\n 2 => ...\n);", array('@columns' => implode(', ', $columns)));
+
+ form_set_error('default_value_php', t('The default value PHP code returned an incorrect value.!sampleReturned value: @value', array( + '!sample' => $sample, + '@value' => print_r($return, TRUE)))); + return; + } + else { + $default_value = $return; + $is_code = TRUE; + form_set_value(array('#parents' => array('instance', 'widget', 'default_value_php')), $php, $form_state); + form_set_value(array('#parents' => array('instance', 'widget', 'default_value')), array(), $form_state); + } + } + elseif (!empty($form_values['instance']['widget']['default_value_widget'])) { + // Fields that handle their own multiple values may use an expected + // value as the top-level key, so just pop off the top element. + $key = array_shift(array_keys($form_values['instance']['widget']['default_value_widget'])); + $default_value = $form_values['instance']['widget']['default_value_widget'][$key]; + $is_code = FALSE; + form_set_value(array('#parents' => array('instance', 'widget', 'default_value_php')), '', $form_state); + form_set_value(array('#parents' => array('instance', 'widget', 'default_value')), $default_value, $form_state); + } + if (isset($default_value)) { + $node = array(); + $node[$field['field_name']] = $default_value; + $field['required'] = FALSE; + $field_function = $field_type['module'] .'_field'; + + $errors_before = form_get_errors(); + + // Widget now does its own validation, should be no need + // to add anything for widget validation here. + if (drupal_function_exists($field_function)) { + $field_function('validate', $node, $field, $default_value, $form, NULL); + } + // The field validation routine won't set an error on the right field, + // so set it here. + $errors_after = form_get_errors(); + if (count($errors_after) > count($errors_before)) { + if (trim($form_values['default_value_php'])) { + form_set_error('default_value_php', t("The PHP code for 'default value' returned @value, which is invalid.", array( + '@value' => print_r($default_value, TRUE)))); + } + else { + form_set_error('default_value', t('The default value is invalid.')); + } + } + } + } +} + +/** + * Save instance settings after editing. + */ +function cck_field_edit_form_submit($form, &$form_state) { + $form_values = $form_state['values']; + $instance = $form_values['instance']; + + // Make sure the default value widget does not get stored. + if (isset($instance['widget']['default_value_widget'])) { + unset($instance['widget']['default_value_widget']); + } + + // TODO, move this to cck.text.inc and cck.number.inc?? + // Make sure the allowed values fieldset does not get stored. + if (isset($instance['settings']['allowed_values_fieldset'])) { + $instance['settings'] += $instance['settings']['allowed_values_fieldset']; + unset($instance['settings']['allowed_values_fieldset']); + } + + // Update the instance. + module_load_include('inc', 'field', 'includes/field.crud'); + field_update_instance($instance); + + drupal_set_message(t('Saved %label configuration.', array('%label' => $instance['label']))); + + $form_state['redirect'] = cck_next_destination($form_state, $instance['bundle']); +} + +/** + * Helper functions to handle multipage redirects. + */ +function cck_get_destinations($destinations) { + $query = array(); + $path = array_shift($destinations); + if ($destinations) { + $query['destinations'] = $destinations; + } + return array($path, $query); +} + +function cck_next_destination(&$form_state, $bundle) { + $destinations = !empty($_REQUEST['destinations']) ? $_REQUEST['destinations'] : array(); + if (!empty($destinations)) { + unset($_REQUEST['destinations']); + return cck_get_destinations($destinations); + } + else { + $admin_path = cck_bundle_admin_path($bundle); + return $admin_path .'/fields'; + } +} + +/** + * Dummy function to force a page refresh so + * menu_rebuild() will work right when creating a new field + * that creates a new menu item. + */ +function cck_field_menu_refresh(&$form_state, $bundle) { + menu_rebuild(); + $destinations = cck_next_destination($form_state, $bundle); + if (is_array($destinations)) { + $path = array_shift($destinations); + drupal_goto($path, $destinations); + } + else { + drupal_goto($destinations); + } +} + +/** + * Helper function to order fields and groups when theming (preprocessing) + * overview forms. + * + * The $form is passed by reference because we assign depths as parenting + * relationships are sorted out. + */ +function _cck_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')) { + foreach ($group_rows as $name) { + $dummy[$name] = array('#markup' => $name .' ', '#weight' => $form[$name]['weight']['#value']); + } + } + // Field rows : account for weight and parenting. + foreach ($field_rows as $name) { + $dummy[$name] = array('#markup' => $name .' ', '#type' => 'markup', '#weight' => $form[$name]['weight']['#value']); + if (module_exists('fieldgroup')) { + if ($parent = $form[$name]['parent']['#value']) { + $form[$name]['#depth'] = 1; + $dummy[$parent][$name] = $dummy[$name]; + unset($dummy[$name]); + } + } + } + return $dummy ? explode(' ', trim(drupal_render($dummy))) : array(); +} + +/** + * Helper form element validator : integer. + */ +function _element_validate_integer($element, &$form_state) { + $value = $element['#value']; + if ($value !== '' && (!is_numeric($value) || intval($value) != $value)) { + form_error($element, t('%name must be an integer.', array('%name' => $element['#title']))); + } +} + +/** + * Helper form element validator : integer > 0. + */ +function _element_validate_integer_positive($element, &$form_state) { + $value = $element['#value']; + if ($value !== '' && (!is_numeric($value) || intval($value) != $value || $value <= 0)) { + form_error($element, t('%name must be a positive integer.', array('%name' => $element['#title']))); + } +} + +/** + * Helper form element validator : number. + */ +function _element_validate_number($element, &$form_state) { + $value = $element['#value']; + if ($value != '' && !is_numeric($value)) { + form_error($element, t('%name must be a number.', array('%name' => $element['#title']))); + } +} \ No newline at end of file diff --git a/includes/cck.devel.inc b/includes/cck.devel.inc new file mode 100644 index 0000000000000000000000000000000000000000..856a7b1dc66d5cd8952b07d9cd3a3687cce9fcaa --- /dev/null +++ b/includes/cck.devel.inc @@ -0,0 +1,197 @@ +{$field['field_name']} = $object_field; + } +} + +/** + * A simple function to return multiple values for fields that use + * custom multiple value widgets but don't need any other special multiple + * values handling. This will call the field generation function + * a random number of times and compile the results into a node array. + */ +function cck_devel_multiple($function, $object, $field, $instance, $bundle) { + $object_field = array(); + if (function_exists($function)) { + switch ($field['cardinality']) { + case FIELD_CARDINALITY_UNLIMITED: + $max = rand(0, 3); //just an arbitrary number for 'unlimited' + break; + default: + $max = $field['cardinality'] - 1; + break; + } + for ($i = 0; $i <= $max; $i++) { + $object_field[$i] = $function($object, $field, $instance, $bundle); + } + } + return $object_field; +} + +if (module_exists('text')) { + function text_cck_generate($object, $field, $instance, $bundle) { + if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_CUSTOM) { + return cck_devel_multiple('_text_cck_generate', $object, $field, $instance, $bundle); + } + else { + return _text_cck_generate($object, $field, $instance, $bundle); + } + } + + function _text_cck_generate($object, $field, $instance, $bundle) { + $object_field = array(); + if ($instance['widget']['type'] == 'text_textarea') { + $object_field['value'] = devel_create_content($format); + } + else { + // Generate a value that respects max_length. + if (empty($field['max_length'])) { + $field['max_length'] = 12; + } + $object_field['value'] = user_password($field['max_length']); + } + $format = isset($field['text_processing']) ? rand(0, 3) : 0; + $object_field['format'] = $format; + return $object_field; + } +} + +if (module_exists('number')) { + function number_cck_generate($object, $field, $instance, $bundle) { + if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_CUSTOM) { + return cck_devel_multiple('_number_cck_generate', $object, $field, $instance, $bundle); + } + else { + return _number_cck_generate($object, $field, $instance, $bundle); + } + } + + function _number_cck_generate($object, $field, $instance, $bundle) { + $object_field = array(); + // Make sure the field settings are all set. + foreach (array('min', 'max', 'precision', 'scale') as $key) { + if (empty($field[$key])) { + $field[$key] = NULL; + } + } + $min = is_numeric($field['min']) ? $field['min'] : 0; + switch ($field['type']) { + case 'number_integer': + $max = is_numeric($field['max']) ? $field['max'] : 10000; + $decimal = 0; + $scale = 0; + break; + + case 'number_decimal': + $precision = is_numeric($field['precision']) ? $field['precision'] : 10; + $scale = is_numeric($field['scale']) ? $field['scale'] : 2; + $max = is_numeric($field['max']) ? $field['max'] : pow(10, ($precision - $scale)); + $decimal = rand(0, (10 * $scale)) / 100; + break; + + case 'number_float': + $precision = rand(10, 32); + $scale = rand(0, 2); + $decimal = rand(0, (10 * $scale)) / 100; + $max = is_numeric($field['max']) ? $field['max'] : pow(10, ($precision - $scale)); + break; + } + $object_field['value'] = round((rand($min, $max) + $decimal), $scale); + return $object_field; + } +} + +if (module_exists('nodereference')) { + function nodereference_cck_generate($object, $field, $instance, $bundle) { + if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_CUSTOM) { + return cck_devel_multiple('_nodereference_cck_generate', $object, $field, $instance, $bundle); + } + else { + return _nodereference_cck_generate($object, $field, $instance, $bundle); + } + } + + function _nodereference_cck_generate($object, $field, $instance, $bundle) { + $object_field = array(); + $allowed_values = nodereference_allowed_values($field); + unset($allowed_values[0]); + if (!empty($allowed_values)) { + // Just pick one of the specified allowed values. + $object_field['nid'] = array_rand($allowed_values); + } + return $object_field; + } +} + +if (module_exists('userreference')) { + function userreference_cck_generate($object, $field) { + if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_CUSTOM) { + return cck_devel_multiple('_userreference_cck_generate', $object, $field); + } + else { + return _userreference_cck_generate($object, $field); + } + } + + function _userreference_cck_generate($object, $field) { + $object_field = array(); + $allowed_values = userreference_allowed_values($field); + if (isset($allowed_values['none'])) { + unset($allowed_values['none']); + } + if (!empty($allowed_values)) { + // Just pick one of the specified allowed values. + $object_field['uid'] = array_rand($allowed_values); + } + return $object_field; + } +} \ No newline at end of file diff --git a/includes/cck.list.inc b/includes/cck.list.inc new file mode 100644 index 0000000000000000000000000000000000000000..5987b88e8f39df7084d04e9624a80134afb894da --- /dev/null +++ b/includes/cck.list.inc @@ -0,0 +1,103 @@ + $field['field_name']))->fetchField(); +} + +/** + * A custom function to return allowed values from PHP code. + */ +function cck_list_allowed_values_php($field) { + $allowed_values = array(); + $php = cck_list_get_allowed_values_php($field); + if (!empty($php)) { + ob_start(); + $result = eval($php); + if (is_array($result)) { + $allowed_values = $result; + } + ob_end_clean(); + } + return $allowed_values; +} + +/** + * Implementation of hook_field_settings_form() + * on behalf of core List module. + */ +function list_field_settings_form($field, $instance) { + + $form = array( + '#element_validate' => array('list_field_settings_form_validate'), + ); + + $defaults = list_field_settings($field['type']); + $settings = array_merge($defaults, $field['settings']); + $settings['allowed_values_php'] = cck_list_get_allowed_values_php($field); + + $form['allowed_values'] = array( + '#type' => 'textarea', + '#title' => t('Allowed values list'), + '#default_value' => $settings['allowed_values'], + '#required' => FALSE, + '#rows' => 10, + '#description' => t('The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database, and must be a %type value. The label is optional, and the key will be used as the label if no label is specified.
'. check_plain($settings['allowed_values_php']) .'
' : t('<none>'),
+ '#description' => empty($settings['allowed_values_php']) ? t("You're not allowed to input PHP code.") : t('This PHP code was set by an administrator and will override the allowed values list and allowed values functions shown above.'),
+ );
+ }
+ return $form;
+}
+
+/**
+ * Handle Allowed values PHP code.
+ */
+function list_field_settings_form_validate($form, &$form_state) {
+ $form_values = $form_state['values'];
+ $field = $form_values['field'];
+ $field_name = $field['field_name'];
+ $option = $form_values['field']['settings']['advanced_options']['allowed_values_php'];
+ db_query("DELETE FROM {cck_field_settings} WHERE setting='field' AND setting_type='allowed_values_php' AND field_name = ':field_name'", array(':field_name' => $field['field_name']));
+ if (!empty($option)) {
+ $record = array(
+ 'field_name' => $field_name,
+ 'bundle' => NULL,
+ 'setting_type' => 'field',
+ 'setting' => 'allowed_values_php',
+ 'setting_option' => $option,
+ );
+ drupal_write_record('cck_field_settings', $record, array());
+ form_set_value($form['allowed_values_function'], 'cck_list_allowed_values_php', $form_state);
+ form_set_value($form['advanced_options'], NULL, $form_state);
+ }
+}
diff --git a/includes/cck.number.inc b/includes/cck.number.inc
new file mode 100644
index 0000000000000000000000000000000000000000..37770f3f88be5b43ec62837c7fd0af53dfed717e
--- /dev/null
+++ b/includes/cck.number.inc
@@ -0,0 +1,71 @@
+ 'select',
+ '#options' => drupal_map_assoc(range(10, 32)),
+ '#title' => t('Precision'),
+ '#description' => t('The total number of digits to store in the database, including those to the right of the decimal.'),
+ '#default_value' => $field['settings']['precision'],
+ );
+ $form['scale'] = array(
+ '#type' => 'select',
+ '#options' => drupal_map_assoc(range(0, 6)),
+ '#title' => t('Scale'),
+ '#description' => t('The number of digits to the right of the decimal.'),
+ '#default_value' => $field['settings']['scale'],
+ );
+ $form['decimal'] = array(
+ '#type' => 'select',
+ '#options' => array('.' => 'decimal point', ',' => 'comma', ' ' => 'space'),
+ '#title' => t('Decimal marker'),
+ '#description' => t('The character users will input to mark the decimal point in forms.'),
+ '#default_value' => $field['settings']['decimal'],
+ );
+ }
+ return $form;
+}
+
+function number_field_instance_settings_form($instance) {
+ $widget = $instance['widget'];
+ $field = field_info_field($instance['field_name']);
+ $field_type = field_info_field_types($field['type']);
+ $defaults = field_info_instance_settings($field['type']);
+ $settings = array_merge($defaults, $instance['settings']);
+
+ $form['min'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Minimum'),
+ '#element_validate' => array('_element_validate_number'),
+ '#default_value' => $instance['settings']['min'],
+ '#description' => t('The minimum value that should be allowed in this field. Leave blank for no minimum.')
+ );
+ $form['max'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Maximum'),
+ '#element_validate' => array('_element_validate_number'),
+ '#default_value' => $instance['settings']['max'],
+ '#description' => t('The maximum value that should be allowed in this field. Leave blank for no maximum.')
+ );
+ $form['prefix'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Prefix'),
+ '#size' => 60,
+ '#default_value' => $instance['settings']['prefix'],
+ '#description' => t('Define a string that should be prefixed to the value, like $ or €. Leave blank for none. Separate singular and plural values with a pipe (pound|pounds).'),
+ );
+ $form['suffix'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Suffix'),
+ '#size' => 60,
+ '#default_value' => $instance['settings']['suffix'],
+ '#description' => t('Define a string that should suffixed to the value, like m², m/s², kb/s. Leave blank for none. Separate singular and plural values with a pipe (pound|pounds).'),
+ );
+ return $form;
+}
\ No newline at end of file
diff --git a/includes/cck.text.inc b/includes/cck.text.inc
new file mode 100644
index 0000000000000000000000000000000000000000..f27944379be53119d1a216d448e244287fcbd288
--- /dev/null
+++ b/includes/cck.text.inc
@@ -0,0 +1,72 @@
+ 'textfield',
+ '#title' => t('Maximum length'),
+ '#default_value' => !empty($settings['max_length']) && is_numeric($settings['max_length']) ? $settings['max_length'] : $defaults['max_length'],
+ '#required' => FALSE,
+ '#element_validate' => array('_element_validate_integer_positive'),
+ '#description' => t('The maximum length of the field in characters. Leave blank for an unlimited size.'),
+ );
+
+ return $form;
+}
+
+/**
+ * Implementation of hook_widget_settings_form()
+ * on behalf of core Text module.
+ */
+function text_widget_settings_form($instance) {
+ $widget = $instance['widget'];
+ $defaults = field_info_widget_settings($widget['type']);
+ $settings = array_merge($defaults, $widget['settings']);
+ $field = field_info_field($instance['field_name']);
+ if ($widget['type'] == 'text_textfield') {
+ $form['size'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Size of textfield'),
+ '#default_value' => $settings['size'],
+ '#element_validate' => array('_element_validate_integer_positive'),
+ '#required' => TRUE,
+ );
+ }
+ else {
+ $form['rows'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Rows'),
+ '#default_value' => $settings['rows'],
+ '#element_validate' => array('_element_validate_integer_positive'),
+ '#required' => TRUE,
+ );
+ }
+ return $form;
+}
+
+/**
+ * Implementation of hook_field_instance_settings_form()
+ * on behalf of core Text module.
+ */
+function text_field_instance_settings_form($instance) {
+ $widget = $instance['widget'];
+ $field = field_info_field($instance['field_name']);
+ $field_type = field_info_field_types($field['type']);
+ $defaults = field_info_instance_settings($field['type']);
+ $settings = array_merge($defaults, $instance['settings']);
+ $options = array(0 => t('Plain text'), 1 => t('Filtered text (user selects input format)'));
+ $form['text_processing'] = array(
+ '#type' => 'radios',
+ '#title' => t('Text processing'),
+ '#default_value' => !empty($settings['text_processing']) && is_numeric($settings['text_processing']) ? $settings['text_processing'] : $defaults['text_processing'],
+ '#options' => $options,
+ );
+ return $form;
+}
\ No newline at end of file
diff --git a/modules/fieldgroup/fieldgroup.css b/modules/fieldgroup/fieldgroup.css
new file mode 100644
index 0000000000000000000000000000000000000000..32454a0be1b42f4f525314b40ce8b62ffafb7cf5
--- /dev/null
+++ b/modules/fieldgroup/fieldgroup.css
@@ -0,0 +1,6 @@
+div.fieldgroup {
+ margin:.5em 0 1em 0;
+}
+div.fieldgroup .content {
+ padding-left:1em;
+}
diff --git a/modules/fieldgroup/fieldgroup.info b/modules/fieldgroup/fieldgroup.info
new file mode 100644
index 0000000000000000000000000000000000000000..901e0f0b78e90739e5c9dcfbd0fa6cf1a673e1f9
--- /dev/null
+++ b/modules/fieldgroup/fieldgroup.info
@@ -0,0 +1,7 @@
+; $Id$
+name = Fieldgroup
+description = Create display groups for CCK fields.
+dependencies[] = cck
+package = CCK
+core = 7.x
+files[]=fieldgroup.module
\ No newline at end of file
diff --git a/modules/fieldgroup/fieldgroup.install b/modules/fieldgroup/fieldgroup.install
new file mode 100644
index 0000000000000000000000000000000000000000..280b58c9ab8f854bb043248e3ea7e069b6effef2
--- /dev/null
+++ b/modules/fieldgroup/fieldgroup.install
@@ -0,0 +1,46 @@
+ array(
+ 'group_type' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => 'standard'),
+ 'bundle' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''),
+ 'group_name' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, '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),
+ ),
+ 'primary key' => array('bundle', 'group_name'),
+ );
+
+ $schema['field_group_fields'] = array(
+ 'fields' => array(
+ 'bundle' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''),
+ 'group_name' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''),
+ 'field_name' => array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''),
+ ),
+ 'primary key' => array('bundle', 'group_name', 'field_name'),
+ );
+
+ return $schema;
+}
\ No newline at end of file
diff --git a/modules/fieldgroup/fieldgroup.module b/modules/fieldgroup/fieldgroup.module
new file mode 100644
index 0000000000000000000000000000000000000000..8eef761e94f8093aa569f07afd61a65509b70876
--- /dev/null
+++ b/modules/fieldgroup/fieldgroup.module
@@ -0,0 +1,768 @@
+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_menu().
+ */
+function fieldgroup_menu() {
+ $items = array();
+
+ // Make sure this doesn't fire until field_info_fieldable_types() is working,
+ // needed to avoid errors on initial installation.
+ if (!defined('MAINTENANCE_MODE')) {
+ // Create tabs for all possible bundles.
+ $bundles = field_info_bundles();
+ foreach ($bundles as $bundle_name => $bundle_label) {
+ $admin_path = cck_bundle_admin_path($bundle_name);
+ $items[$admin_path .'/groups/%'] = array(
+ 'title' => 'Edit group',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('fieldgroup_group_edit_form', $bundle_name, 5),
+ 'access arguments' => array('administer content types'),
+ 'type' => MENU_CALLBACK,
+ );
+ $items[$admin_path .'/groups/%/remove'] = array(
+ 'title' => 'Edit group',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('fieldgroup_remove_group', $bundle_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',
+ '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_field_fieldapi($op, $field) {
+ switch ($op) {
+ case 'delete instance':
+ db_query("DELETE FROM {field_group_fields} WHERE field_name = '%s'", $field['field_name']);
+ break;
+ }
+ cache_clear_all('fieldgroup_data', 'cache_field');
+}
+
+function fieldgroup_group_edit_form(&$form_state, $bundle, $group_name) {
+ $groups = fieldgroup_groups($bundle);
+
+ 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 the 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(field_build_modes(field_info_bundle_entity($bundle))) 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['#bundle'] = $bundle;
+
+ $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'];
+ $bundle = $form['#bundle'];
+ fieldgroup_save_group($bundle, $form_values);
+ $form_state['redirect'] = cck_bundle_admin_path($bundle) .'/fields';
+}
+
+function fieldgroup_remove_group(&$form_state, $bundle, $group_name) {
+ $groups = fieldgroup_groups($bundle);
+ $group = isset($groups[$group_name]) ? $groups[$group_name] : '';
+
+ if (empty($group)) {
+ drupal_not_found();
+ exit;
+ }
+
+ $form['#submit'][] = 'fieldgroup_remove_group_submit';
+ $form['#bundle'] = $bundle;
+ $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']))),
+ cck_bundle_admin_path($bundle) .'/fields', t('This action cannot be undone.'),
+ t('Remove'), t('Cancel'));
+}
+
+function fieldgroup_remove_group_submit($form, &$form_state) {
+ $form_values = $form_state['values'];
+ $bundle = $form['#bundle'];
+ $group_name = $form['#group_name'];
+ fieldgroup_delete($bundle, $group_name);
+ drupal_set_message(t('The group %group_name has been removed.', array('%group_name' => $group_name)));
+ $form_state['redirect'] = cck_bundle_admin_path($bundle) .'/fields';
+}
+
+/*
+ * Returns all groups for a content type
+ */
+function fieldgroup_groups($bundle = '', $sorted = FALSE, $reset = FALSE) {
+ static $groups, $groups_sorted;
+ if (!isset($groups) || $reset) {
+ if ($cached = cache_get('fieldgroup_data', 'cache_field')) {
+ $data = $cached->data;
+ $groups = $data['groups'];
+ $groups_sorted = $data['groups_sorted'];
+ }
+ else {
+ $result = db_query("SELECT * FROM {field_group} ORDER BY weight, group_name");
+ $groups = array();
+ $groups_sorted = array();
+ while ($group = db_fetch_array($result)) {
+ $group['settings'] = unserialize($group['settings']);
+ $group['fields'] = array();
+ $groups[$group['bundle']][$group['group_name']] = $group;
+ $groups_sorted[$group['bundle']][] = &$groups[$group['bundle']][$group['group_name']];
+ }
+ //load fields
+ $result = db_query("SELECT nfi.*, ng.group_name FROM {field_group} ng ".
+ "INNER JOIN {field_group_fields} ngf ON ngf.bundle = ng.bundle AND ngf.group_name = ng.group_name ".
+ "INNER JOIN {field_config_instance} nfi ON nfi.field_name = ngf.field_name AND nfi.bundle = ngf.bundle ".
+ "WHERE nfi.widget_active = 1 ORDER BY nfi.weight");
+ while ($field = db_fetch_array($result)) {
+ $groups[$field['bundle']][$field['group_name']]['fields'][$field['field_name']] = $field;
+ }
+ cache_set('fieldgroup_data', array('groups' => $groups, 'groups_sorted' => $groups_sorted), 'cache_field');
+ }
+ }
+ if (empty($bundle)) {
+ return $groups;
+ }
+ elseif (empty($groups) || empty($groups[$bundle])) {
+ return array();
+ }
+ return $sorted ? $groups_sorted[$bundle] : $groups[$bundle];
+}
+
+
+function _fieldgroup_groups_label($bundle) {
+ $groups = fieldgroup_groups($bundle);
+
+ $labels[''] = '<'. t('none') .'>';
+ foreach ($groups as $group_name => $group) {
+ $labels[$group_name] = t($group['label']);
+ }
+ return $labels;
+}
+
+function _fieldgroup_field_get_group($bundle, $field_name) {
+ return db_result(db_query("SELECT group_name FROM {field_group_fields} WHERE bundle = '%s' AND field_name = '%s'", $bundle, $field_name));
+}
+
+/**
+ * Implementation of hook_form_alter()
+ */
+function fieldgroup_form_alter(&$form, $form_state, $form_id) {
+ // TODO This only works right on a node form, need to make it more general.
+ if (!empty($form['#fields']) && !empty($form['type'])) {
+ 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' => field_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])) {
+ // 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;
+ }
+ // Move the form element.
+ $form[$group_name][$field_name] = $form[$field_name];
+ unset($form[$field_name]);
+ $form['#fields'][$field_name]['form_path'] = array($group_name, $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);
+ }
+
+ }
+
+ }
+ // The group is only added here so it will appear in the export
+ // when using Content Copy.
+ elseif ($form_id == 'cck_field_edit_form' && isset($form['widget'])) {
+ $bundle = $form['bundle']['#value'];
+ $form['widget']['group'] = array(
+ '#type' => 'value',
+ '#value' => _fieldgroup_field_get_group($bundle, $form['field_name']['#value']),
+ );
+ }
+ elseif ($form_id == 'cck_field_overview_form') {
+ $form['#validate'][] = 'fieldgroup_field_overview_form_validate';
+ $form['#submit'][] = 'fieldgroup_field_overview_form_submit';
+ }
+ elseif ($form_id == 'cck_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 == 'cck_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, $bundle) {
+ $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($bundle);
+ 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['#bundle']);
+ 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'];
+ $bundle = $form['#bundle'];
+
+ // 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($bundle, $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);
+ 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.
+ // field_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, 'bundle' => $bundle));
+ }
+
+ // If 'group' row: update groups weights
+ // (possible newly created group has already been taken care of).
+ elseif (in_array($key, $form['#groups'])) {
+ db_query("UPDATE {field_group} SET weight = %d WHERE bundle = '%s' AND group_name = '%s'",
+ $values['weight'], $bundle, $key);
+ }
+ }
+
+ cache_clear_all('fieldgroup_data', 'cache_field');
+}
+
+function field_group_default_settings($group_type) {
+ $settings = array(
+ 'form' => array('style' => 'fieldset', 'description' => ''),
+ 'display' => array('description' => '', 'label' => 'above'),
+ );
+ module_load_include('inc', 'field', 'includes/field.admin');
+ foreach (array_keys(field_build_modes(field_info_bundle_entity($bundle))) 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['#bundle']);
+ foreach ($form_values as $key => $values) {
+ if (in_array($key, $form['#groups'])) {
+ $group = $groups[$key];
+ // We have some numeric keys here, so we can't use array_merge.
+ $group['settings']['display'] = $values + $group['settings']['display'];
+ fieldgroup_save_group($form['#bundle'], $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 {field_group_fields} WHERE bundle = '%s' AND field_name = '%s'", $form_values['bundle'], $form_values['field_name']);
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ */
+function fieldgroup_nodeapi(&$node, $op, $teaser, $page) {
+ // TODO This will only work on a node, need to make it general enough to work on users.
+ switch ($op) {
+ case 'view':
+ // Prevent against invalid 'nodes' built by broken 3rd party code.
+ if (isset($node->type)) {
+ // 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;
+ }
+
+ foreach (fieldgroup_groups($node->type) 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 ? field_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;
+ 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'],
+ '#post_render' => array('fieldgroup_wrapper_post_render'),
+ '#group_name' => $group_name,
+ '#bundle' => $node->type,
+ '#context' => $context,
+ );
+
+ $node->content[$group_name] = $wrapper;
+ }
+ }
+ break;
+ }
+}
+
+/**
+ * Hide specified fields from the $content variable in node templates.
+ */
+function fieldgroup_wrapper_post_render($content, $element) {
+ $groups = fieldgroup_groups($element['#bundle']);
+ $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('field_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($bundle, $field_name) {
+ foreach (fieldgroup_groups($bundle) 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 {field_group} SET bundle='%s' WHERE bundle='%s'", array($info->type, $info->old_type));
+ db_query("UPDATE {field_group_fields} SET bundle='%s' WHERE bundle='%s'", array($info->type, $info->old_type));
+ cache_clear_all('fieldgroup_data', 'cache_field');
+ }
+ elseif ($op == 'delete') {
+ db_query("DELETE FROM {field_group} WHERE bundle = '%s'", $info->type);
+ db_query("DELETE FROM {field_group_fields} WHERE bundle = '%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;
+}
+
+/**
+ * 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($bundle, $group) {
+ $groups = fieldgroup_groups($bundle);
+
+ // 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 {field_group} (group_type, bundle, group_name, label, settings, weight)".
+ " VALUES ('%s', '%s', '%s', '%s', '%s', %d)", $group['group_type'], $bundle, $group['group_name'], $group['label'], serialize($group['settings']), $group['weight']);
+ cache_clear_all('fieldgroup_data', 'cache_field');
+ return SAVED_NEW;
+ }
+ else {
+ db_query("UPDATE {field_group} SET group_type = '%s', label = '%s', settings = '%s', weight = %d ".
+ "WHERE bundle = '%s' AND group_name = '%s'",
+ $group['group_type'], $group['label'], serialize($group['settings']), $group['weight'], $bundle, $group['group_name']);
+ cache_clear_all('fieldgroup_data', 'cache_field');
+ return SAVED_UPDATED;
+ }
+}
+
+function fieldgroup_update_fields($form_values) {
+ $default = _fieldgroup_field_get_group($form_values['bundle'], $form_values['field_name']);
+
+ if ($default != $form_values['group']) {
+ if ($form_values['group'] && !$default) {
+ db_query("INSERT INTO {field_group_fields} (bundle, group_name, field_name) VALUES ('%s', '%s', '%s')", $form_values['bundle'], $form_values['group'], $form_values['field_name']);
+ }
+ elseif ($form_values['group']) {
+ db_query("UPDATE {field_group_fields} SET group_name = '%s' WHERE bundle = '%s' AND field_name = '%s'", $form_values['group'], $form_values['bundle'], $form_values['field_name']);
+ }
+ else {
+ db_query("DELETE FROM {field_group_fields} WHERE bundle = '%s' AND field_name = '%s'", $form_values['bundle'], $form_values['field_name']);
+ }
+ cache_clear_all('fieldgroup_data', 'cache_field');
+ }
+}
+
+function fieldgroup_delete($bundle, $group_name) {
+ db_query("DELETE FROM {field_group} WHERE bundle = '%s' AND group_name = '%s'", $bundle, $group_name);
+ db_query("DELETE FROM {field_group_fields} WHERE bundle = '%s' AND group_name = '%s'", $bundle, $group_name);
+ cache_clear_all('fieldgroup_data', 'cache_field');
+}
+
+/**
+ * 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.tpl.php
+ */
+function fieldgroup_preprocess_fieldgroup_simple(&$vars) {
+ $element = $vars['element'];
+
+ $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'] : '';
+}
+
+/**
+ * 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'] : '';
+ }
+}
\ No newline at end of file
diff --git a/modules/fieldgroup/fieldgroup.tpl.php b/modules/fieldgroup/fieldgroup.tpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..9bc88e758a1ada52c113ee214a53e398cf53347b
--- /dev/null
+++ b/modules/fieldgroup/fieldgroup.tpl.php
@@ -0,0 +1,33 @@
+
+
++ $value): ?> + | + + | ||
---|---|---|---|
+ $value): ?> + | + | + | + + |
indentation; ?>human_name; ?> | + $title): ?> +{$context}->label; ?> | +{$context}->type; ?> | +{$context}->exclude; ?> | + +
+ | + | + | + | + | + | ||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
+ indentation; ?> + label; ?> + | +weight . $row->parent . $row->hidden_name; ?> | +field_name; ?> | +type; ?> | +widget_type; ?> | +configure; ?> remove; ?> | + ++ indentation; ?> + label; ?> + | +weight . $row->parent . $row->hidden_name; ?> | +group_name; ?> | +group_type; ?> | +configure; ?> remove; ?> | + ++ indentation; ?> + label; ?> + | +weight . $row->parent . $row->hidden_name; ?> | +description; ?> | + ++ + |
+ indentation; ?>
+
+
+ label; ?>
+
+ |
+
+ indentation; ?>
+
+
+ label; ?>
+
+ |
+
+ indentation; ?>
+
+
+ label; ?>
+
+ |
+