Newer
Older
<?php
// $Id$
/**
* @file
* Create field groups for CCK fields.
*/
/**
* Implementation of hook_init().
*/
Karen Stevenson
committed
function fieldgroup_init() {
drupal_add_css(drupal_get_path('module', 'fieldgroup') .'/fieldgroup.css');
}
Karen Stevenson
committed
/**
* Implementation of hook_menu().
*/
function fieldgroup_menu() {
$items = array();
foreach (node_get_types() as $type) {
$type_name = $type->type;
$content_type = content_types($type_name);
Karen Stevenson
committed
// Make sure this doesn't fire until content_types is working,
// needed to avoid errors on initial installation.
if (!empty($content_type)) {
$type_url_str = $content_type['url_str'];
$items['admin/content/node-type/'. $type_url_str .'/add_group'] = array(
Karen Stevenson
committed
'title' => 'Add group',
'page callback' => 'drupal_get_form',
'page arguments' => array('fieldgroup_edit_group_form', $content_type, '', 'add'),
'access arguments' => array('administer content types'),
'type' => MENU_LOCAL_TASK,
'weight' => 3,
);
$items['admin/content/node-type/'. $type_url_str .'/groups/%/edit'] = array(
Karen Stevenson
committed
'title' => 'Edit group',
'page callback' => 'drupal_get_form',
'page arguments' => array('fieldgroup_edit_group_form', $content_type, 5, 'edit'),
'access arguments' => array('administer content types'),
'type' => MENU_CALLBACK,
);
$items['admin/content/node-type/'. $type_url_str .'/groups/%/remove'] = array(
Karen Stevenson
committed
'title' => 'Edit group',
'page callback' => 'drupal_get_form',
'page arguments' => array('fieldgroup_remove_group', $content_type, 5),
Karen Stevenson
committed
'access arguments' => array('administer content types'),
'type' => MENU_CALLBACK,
);
}
Karen Stevenson
committed
}
Karen Stevenson
committed
/**
* Implementation of hook_theme().
*/
function fieldgroup_theme() {
return array(
'fieldgroup_simple' => array(
Karen Stevenson
committed
'arguments' => array('element' => NULL),
Karen Stevenson
committed
'fieldgroup_display_overview_form' => array(
Karen Stevenson
committed
'arguments' => array('form' => NULL),
Karen Stevenson
committed
);
/**
* Implementation of hook_elements().
*/
function fieldgroup_elements() {
return array(
'fieldgroup_simple' => array(),
);
}
Karen Stevenson
committed
/**
* Implementation of hook_fieldapi().
*/
Karen Stevenson
committed
function fieldgroup_content_fieldapi($op, $field) {
Karen Stevenson
committed
switch ($op) {
case 'delete':
db_query("DELETE FROM {". fieldgroup_fields_tablename() ." WHERE field_name = '%s'", $field['field_name']);
Karen Stevenson
committed
break;
}
Karen Stevenson
committed
cache_clear_all('fieldgroup_data', content_cache_tablename());
Karen Stevenson
committed
}
function fieldgroup_edit_group_form(&$form_state, $content_type, $group_name, $action) {
$groups = fieldgroup_groups($content_type['type']);
Karen Stevenson
committed
if ($action == 'add') {
//adding a new one
$group = array();
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Add'),
'#weight' => 10,
);
}
elseif ($group = $groups[$group_name]) {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
'#weight' => 10,
);
}
else {
drupal_not_found();
exit;
}
$form['label'] = array(
'#type' => 'textfield',
'#title' => t('Label'),
'#default_value' => isset($group['label']) ? $group['label'] : '',
$form['settings']['#tree'] = TRUE;
$form['settings']['form'] = array(
'#type' => 'fieldset',
'#title' => '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' => isset($group['settings']['form']['style']) ? $group['settings']['form']['style'] : 'fieldset',
'#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' => isset($group['settings']['form']['description']) ? $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' => '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' => isset($group['settings']['display']['description']) ? $group['settings']['display']['description'] : '',
'#rows' => 5,
'#description' => t('A description of the group.'),
'#required' => FALSE,
);
module_load_include('inc', 'content', 'content_admin');
foreach (array_merge(array_keys(_content_admin_display_contexts(CONTENT_CONTEXTS_SIMPLE)), array('label')) as $key) {
$form['settings']['display'][$key] = array('#type' => 'value', '#value' => isset($group['settings']['display'][$key]) ? $group['settings']['display'][$key] : 'fieldset');
$form['weight'] = array('#type' => 'hidden', '#default_value' => isset($group['weight']) ? $group['weight'] : 0);
$form['group_name'] = array('#type' => 'hidden', '#default_value' => $group_name);
$form['#content_type'] = $content_type;
$form['#submit'][] = 'fieldgroup_edit_group_submit';
Karen Stevenson
committed
/**
* Group name validation for programmatic group addition.
*
* @todo
* Come back here and do the same thing for groups as we've done for fields,
* present a machine-readable name field as well as a label instead of
* trying to create a machine-readable name from the label.
Karen Stevenson
committed
*/
function fieldgroup_edit_group_validate($form, &$form_state) {
$form_values = $form_state['values'];
$content_type = $form['#content_type'];
Karen Stevenson
committed
if (!empty($form_values['group_name']) && $action == 'add') {
Karen Stevenson
committed
$groups = fieldgroup_groups($content_type['type']);
$group = $groups[$form_values['group_name']];
if (isset($group[$form_values['group_name']])) {
form_set_error('group_name', t('The group name %name already exists.', array(
'%group_name' => $form_values['group_name'])));
}
if (!preg_match('!^[a-z0-9_]+$!', $form_values['group_name'])) {
form_set_error('group_name', t('The group name %name is invalid.', array(
'%group_name' => $form_values['group_name'])));
}
}
}
function fieldgroup_edit_group_submit($form, &$form_state) {
$form_values = $form_state['values'];
$content_type = $form['#content_type'];
// $groups = fieldgroup_groups($content_type['type']);
// $group = $groups[$form_values['group_name']];
// TODO : when a group is added, it is displayed as a fieldset *with no label*
// whereas the 'display field' overview states 'Label : above'
// (the description is not displayed either)
// fixed when the 'display field' form is submitted.
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, $content_type, $group_name) {
$groups = fieldgroup_groups($content_type['type']);
$group = isset($groups[$group_name]) ? $groups[$group_name] : '';
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'];
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
*/
Karen Stevenson
committed
function fieldgroup_groups($content_type = '', $sorted = FALSE, $reset = FALSE) {
Wolfgang Ziegler
committed
static $groups, $groups_sorted;
if (!isset($groups) || $reset) {
Karen Stevenson
committed
if ($cached = cache_get('fieldgroup_data', content_cache_tablename())) {
Karen Stevenson
committed
$data = $cached->data;
Wolfgang Ziegler
committed
$groups = $data['groups'];
$groups_sorted = $data['groups_sorted'];
$result = db_query("SELECT * FROM {". fieldgroup_tablename() ."} ORDER BY weight, group_name");
Wolfgang Ziegler
committed
$groups = array();
$groups_sorted = array();
while ($group = db_fetch_array($result)) {
$group['settings'] = unserialize($group['settings']);
$group['fields'] = array();
$groups[$group['type_name']][$group['group_name']] = $group;
$groups_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 ".
Karen Stevenson
committed
"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;
}
Karen Stevenson
committed
cache_set('fieldgroup_data', array('groups' => $groups, 'groups_sorted' => $groups_sorted), content_cache_tablename());
Karen Stevenson
committed
if (empty($content_type)) {
return $groups;
}
elseif (empty($groups) || empty($groups[$content_type])) {
return array();
}
return $sorted ? $groups_sorted[$content_type] : $groups[$content_type];
}
function _fieldgroup_groups_label($content_type) {
$groups = fieldgroup_groups($content_type);
Yves Chedemois
committed
$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));
}
/*
* Implementation of hook_form_alter()
*/
function fieldgroup_form_alter(&$form, $form_state, $form_id) {
Yves Chedemois
committed
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' => t($group['label']),
'#collapsed' => $group['settings']['form']['style'] == 'fieldset_collapsed',
'#collapsible' => in_array($group['settings']['form']['style'], array('fieldset_collapsed', 'fieldset_collapsible')),
'#description' => t($group['settings']['form']['description']),
'#attributes' => array('class' => strtr($group['group_name'], '_', '-')),
Yves Chedemois
committed
Karen Stevenson
committed
$has_accessible_field = FALSE;
foreach ($group['fields'] as $field_name => $field) {
if (isset($form[$field_name])) {
$form[$group_name][$field_name] = $form[$field_name];
Karen Stevenson
committed
// Track whether this group has any accessible fields within it.
if ($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]);
}
Yves Chedemois
committed
Karen Stevenson
committed
if (!$has_accessible_field) {
// Hide the fieldgroup, because the fields are inaccessible.
$form[$group_name]['#access'] = FALSE;
}
elseif ($form_id == '_content_admin_field') {
Karen Stevenson
committed
$content_type = content_types($form['type_name']['#value']);
$form['widget']['group'] = array(
'#type' => 'select',
'#title' => t('Display in group'),
'#options' => _fieldgroup_groups_label($content_type['type']),
Karen Stevenson
committed
'#default_value' => _fieldgroup_field_get_group($content_type['type'], $form['field_name']['#value']),
'#description' => t('Select a group, in which the field will be displayed on the editing form.'),
'#weight' => 5,
);
$form['widget']['weight']['#weight'] = 5;
$form['widget']['description']['#weight'] = 7;
$form['#submit'][] = 'fieldgroup_content_admin_form_submit';
elseif ($form_id == 'content_admin_field_overview_form' && !empty($form['#groups'])) {
$form['#submit'][] = 'fieldgroup_content_overview_form_submit';
}
elseif ($form_id == 'content_admin_display_overview_form' && !empty($form['#groups'])) {
$form['#submit'][] = 'fieldgroup_display_overview_form_submit';
if (!isset($form['submit'])) {
$form['submit'] = array('#type' => 'submit', '#value' => t('Submit'), '#weight' => 10);
}
elseif ($form_id == '_content_admin_field_remove') {
$form['#submit'][] = 'fieldgroup_content_admin_field_remove_submit';
function fieldgroup_content_admin_form_submit($form, &$form_state) {
$form_values = $form_state['values'];
Karen Stevenson
committed
fieldgroup_update_fields($form_values);
Karen Stevenson
committed
}
function fieldgroup_content_overview_form_submit($form, &$form_state) {
$form_values = $form_state['values'];
$type_name = $form['#type_name'];
foreach ($form_values as $key => $values) {
// Update field parenting.
if (in_array($key, $form['#fields'])) {
// TODO : check the parent group does exist ?
fieldgroup_update_fields(array('field_name' => $key, 'group' => $values['parent'], 'type_name' => $type_name));
Karen Stevenson
committed
}
// Update groups weights.
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);
Karen Stevenson
committed
}
}
Karen Stevenson
committed
cache_clear_all('fieldgroup_data', content_cache_tablename());
Karen Stevenson
committed
}
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];
// We have 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_content_admin_field_remove_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, $page) {
switch ($op) {
case 'view':
$context = $teaser ? 'teaser' : 'full';
foreach (fieldgroup_groups($node->type) as $group_name => $group) {
$label = $group['settings']['display']['label'] == 'above';
$element = array(
'#title' => $label ? t($group['label']) : '',
'#description' => $label ? t($group['settings']['display']['description']) : '',
'#weight' => $group['weight'],
);
switch ($group['settings']['display'][$context]['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'] = 'fieldset';
$element['#attributes'] = array('class' => 'fieldgroup '. strtr($group['group_name'], '_', '-'));
break;
}
$node->content[$group_name] = $element;
foreach ($group['fields'] as $field_name => $field) {
if (isset($node->content[$field_name])) {
$node->content[$group_name][$field_name] = $node->content[$field_name];
unset($node->content[$field_name]);
}
}
}
Yves Chedemois
committed
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
case 'alter':
// Add back the formatted values in the 'view' element,
// so that node templates can use it.
$context = $teaser ? 'teaser' : 'full';
foreach (fieldgroup_groups($node->type) as $group_name => $group) {
foreach ($group['fields'] as $field_name => $field) {
if (isset($node->content[$group_name][$field['field_name']])) {
$element = $node->content[$group_name][$field['field_name']];
if ($element['#single']) {
// Single value formatter.
foreach (element_children($element['items']) as $delta) {
// Use isset() to avoid undefined index message on #children when field values are empty.
$node->{$field_name}[$delta]['view'] = isset($element['items'][$delta]['#children']) ? $element['items'][$delta]['#children'] : '';
}
}
else {
// Multiple values formatter.
$node->{$field_name}[0]['view'] = $element['items']['#children'];
}
}
else {
$node->{$field_name}[0]['view'] = '';
}
}
}
break;
/**
* 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 template_preprocess_fieldgroup_simple(&$variables) {
$element = $variables['element'];
$variables['group_name'] = $element['#group_name'];
$variables['group_name_css'] = strtr($element['#group_name'], '_', '-');
$variables['label'] = isset($element['#title']) ? $element['#title'] : '';;
$variables['description'] = isset($element['#description']) ? $element['#description'] : '';;
$variables['content'] = isset($element['#children']) ? $element['#children'] : '';
* 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) {
Karen Stevenson
committed
if ($op == 'update' && !empty($info->old_type) && $info->type != $info->old_type) {
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));
Karen Stevenson
committed
cache_clear_all('fieldgroup_data', content_cache_tablename());
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_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';
Karen Stevenson
committed
}
/**
* CRUD API for fieldgroup module.
*
* @todo
* Make this into more of a real API for groups.
Karen Stevenson
committed
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
*/
/*
* Saves the given group for this content-type
*/
function fieldgroup_save_group($type_name, $group) {
$groups = fieldgroup_groups($type_name);
if (!isset($groups[$group['group_name']])) {
// Accept group name from programmed submissions if valid.
if (!empty($group['group_name'])) {
$group_name = $group['group_name'];
}
else {
// Otherwise, find a valid, computer-friendly name.
$group_name = trim($group['label']);
$group_name = drupal_strtolower($group_name);
$group_name = str_replace(array(' ', '-'), '_', $group_name);
$group_name = preg_replace('/[^a-z0-9_]/', '', $group_name);
$group_name = 'group_'. $group_name;
$group_name = substr($group_name, 0, 30);
if (isset($groups[$group_name])) {
$group_name_base = $group_name;
$counter = 0;
while (isset($groups[$group_name])) {
$group_name = $group_name_base .'_'. $counter++;
}
}
}
db_query("INSERT INTO {". fieldgroup_tablename() ."} (type_name, group_name, label, settings, weight)
Karen Stevenson
committed
VALUES ('%s', '%s', '%s', '%s', %d)", $type_name, $group_name, $group['label'], serialize($group['settings']), $group['weight']);
cache_clear_all('fieldgroup_data', content_cache_tablename());
return SAVED_NEW;
}
else {
db_query("UPDATE {". fieldgroup_tablename() ."} SET label = '%s', settings = '%s', weight = %d ".
Karen Stevenson
committed
"WHERE type_name = '%s' AND group_name = '%s'",
$group['label'], serialize($group['settings']), $group['weight'], $type_name, $group['group_name']);
cache_clear_all('fieldgroup_data', content_cache_tablename());
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')",
Karen Stevenson
committed
$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'",
Karen Stevenson
committed
$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']);
Karen Stevenson
committed
}
cache_clear_all('fieldgroup_data', content_cache_tablename());
}
}
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);
Karen Stevenson
committed
cache_clear_all('fieldgroup_data', content_cache_tablename());
}