bundle_inherit.module in Bundle Inherit 7
Bundle Inherit module.
File
bundle_inherit.moduleView source
<?php
/**
* @file
* Bundle Inherit module.
*/
/**
* Perform necesary inherit operations.
*/
function bundle_inherit_perform($entity_type, $bundle, $bundle_parent, $strict = TRUE) {
// Get fields from parent bundle.
$instances = field_info_instances($entity_type, $bundle_parent);
foreach ($instances as $instance) {
$new_instance = $instance;
$new_instance['bundle'] = $bundle;
if ($strict) {
$new_instance['locked'] = TRUE;
}
$new_instance = field_create_instance($new_instance);
$query = db_select('field_config_instance', 'fci');
$query
->addField('fci', 'id');
$query
->condition('fci.bundle', $bundle);
$new_instance['id'] = $query
->execute()
->fetchField();
}
// Check if we perform strict inheritance.
if ($strict) {
$exists = db_query('SELECT 1 FROM {bundle_inherit} WHERE entity_type = :entity_type AND bundle = :bundle', array(
':entity_type' => $entity_type,
':bundle' => $bundle,
))
->fetchField();
if ($exists) {
db_update('bundle_inherit')
->fields(array(
'bundle_parent' => $bundle_parent,
))
->condition('entity_type', $entity_type)
->condition('bundle', $bundle)
->execute();
}
else {
db_insert('bundle_inherit')
->fields(array(
'entity_type' => $entity_type,
'bundle' => $bundle,
'bundle_parent' => $bundle_parent,
))
->execute();
}
watchdog('bundle_inherit', 'The %bundle bundle of the entity %type was STRICTLY inherited from %parent_bundle bundle.', array(
'%bundle' => $bundle,
'%bundle_parent' => $bundle_parent,
'%type' => $entity_type,
));
drupal_static_reset('bundle_inherit_bundle_get_children');
}
else {
watchdog('bundle_inherit', 'The %bundle bundle of the entity %type was SOFTLY inherited from %parent_bundle bundle.', array(
'%bundle' => $bundle,
'%bundle_parent' => $bundle_parent,
'%type' => $entity_type,
));
}
// Allow third party modules to implement there own inherit logic.
module_invoke_all('bundle_inherit_perform', $entity_type, $bundle, $bundle_parent, $strict = TRUE);
}
/**
* Implements hook_field_create_instance().
*/
function bundle_inherit_field_create_instance($instance) {
$children = bundle_inherit_bundle_get_children($instance['entity_type'], $instance['bundle']);
foreach ($children as $bundle) {
$new_instance = $instance;
unset($new_instance['id']);
$new_instance['bundle'] = $bundle['type'];
$new_instance['locked'] = TRUE;
field_create_instance($new_instance);
}
}
/**
* Implements hook_field_update_instance().
*/
function bundle_inherit_field_update_instance($instance, $prior_instance) {
$children = bundle_inherit_bundle_get_children($prior_instance['entity_type'], $prior_instance['bundle']);
foreach ($children as $bundle) {
$old_instance = field_info_instance($instance['entity_type'], $instance['field_name'], $bundle['type']);
$new_instance = array(
'id' => $old_instance['id'],
'bundle' => $old_instance['bundle'],
'locked' => TRUE,
);
$new_instance += $instance;
field_update_instance($new_instance);
}
}
/**
* Implements hook_field_delete_instance().
*/
function bundle_inherit_field_delete_instance($instance) {
$children = bundle_inherit_bundle_get_children($instance['entity_type'], $instance['bundle']);
foreach ($children as $bundle) {
$new_instance = $instance;
$new_instance['bundle'] = $bundle['type'];
$new_instance['locked'] = FALSE;
try {
field_update_instance($new_instance);
} catch (Exception $e) {
drupal_set_message($e
->getMessage(), 'error');
}
}
}
/**
* Implements hook_form_FORMID_alter().
*
* Attach additional validation callback to the field_ui_field_overview_form.
* When adding new field instance to the parent we should check that all of it
* childrens hase not that field instances.
*/
function bundle_inherit_form_field_ui_field_overview_form_alter(&$form, &$form_instance, $form_id) {
$form['#validate'][] = 'bundle_inherit_validate_field_instance_creation';
$bundle = $form['#bundle'];
$entity_type = $form['#entity_type'];
// Remove links for inherited fields.
foreach ($form['fields'] as $field_name => &$value) {
if (isset($value['#row_type']) && $value['#row_type'] === 'field') {
// Check if this field is inherited.
$parent = bundle_inherit_bundle_get_parent($entity_type, $bundle);
if (!empty($parent)) {
// Check if parent bundle has this field attached
if (field_info_instance($entity_type, $field_name, $parent)) {
$value['type'] = array(
'#markup' => $value['type']['#title'],
);
$value['widget_type'] = array(
'#markup' => $value['widget_type']['#title'],
);
}
}
}
}
}
/**
* Additional validation function to the field_ui_field_overview_form.
*
* While adding existing field instance, get this form is created for and set
* form error if any of this children has instance of this field.
*/
function bundle_inherit_validate_field_instance_creation($form, &$form_state) {
$form_values = $form_state['values']['fields'];
if (!empty($form_values['_add_existing_field']['field_name'])) {
$children = bundle_inherit_bundle_get_children_all($form['#entity_type'], $form['#bundle']);
$bundles_with_instance = array();
foreach ($children as $child) {
$prior_instance = field_info_instance($form['#entity_type'], $form_values['_add_existing_field']['field_name'], $child);
if (!empty($prior_instance)) {
$bundles_with_instance[] = $prior_instance['bundle'];
}
}
if (count($bundles_with_instance) > 0) {
$string = implode(", ", $bundles_with_instance);
form_set_error('fields][_add_existing_field', t("Instance of the field %field can't be attached to %bundle bundle because this field instances are already attached to some of this bundle children bundles: %children", array(
'%bundle' => $form['#bundle'],
'%field' => $form_values['_add_existing_field']['field_name'],
'%children' => $string,
)));
}
}
}
/**
* Get direct children bundles of the selected entity bundle.
*/
function bundle_inherit_bundle_get_children($entity_type, $bundle_parent) {
$children =& drupal_static(__FUNCTION__);
if (!isset($children[$entity_type])) {
$children[$entity_type] = array();
}
if (!isset($children[$entity_type][$bundle_parent])) {
$children[$entity_type][$bundle_parent] = array();
$entity_type_info = entity_get_info($entity_type);
$_children = db_select('bundle_inherit', 'bi')
->fields('bi', array(
'bundle',
))
->condition('bundle_parent', $bundle_parent)
->condition('entity_type', $entity_type)
->execute()
->fetchCol();
foreach ($_children as $child) {
$children[$entity_type][$bundle_parent][$child] = array(
'type' => $child,
'label' => $entity_type_info['bundles'][$child]['label'],
);
}
}
return $children[$entity_type][$bundle_parent];
}
/**
* Get all children bundles of the selected entity bundle.
*/
function bundle_inherit_bundle_get_children_all($entity_type, $bundle_parent) {
$children = array();
$children = bundle_inherit_bundle_get_children($entity_type, $bundle_parent);
foreach ($children as $child) {
$children = array_merge($children, bundle_inherit_bundle_get_children_all($entity_type, $child['type']));
}
return $children;
}
/**
* Get parent of the selected entity bundle.
*
* @return
* Entity type parent type.
*/
function bundle_inherit_bundle_get_parent($entity_type, $bundle) {
$parent =& drupal_static(__FUNCTION__);
if (!isset($parent[$entity_type][$bundle])) {
$parent[$entity_type][$bundle] = db_select('bundle_inherit', 'bi')
->fields('bi', array(
'bundle_parent',
))
->condition('bi.bundle', $bundle)
->execute()
->fetchField();
if (!$parent[$entity_type][$bundle]) {
$parent[$entity_type][$bundle] = '';
}
}
return $parent[$entity_type][$bundle];
}
/**
* Attach ineritance form to selected form element.
*
* @param $form
* Parent form element to attach inheritance form to.
* @param $form_state
* From state from the parent form.
* @param $entity_type
* Entity for which bundle is creating for.
* @param $bundle
* If editing existing bundle value for this argument should be provided.
*/
function bundle_inherit_attach_inherit_form(&$form, &$form_state, $entity_type, $bundle = '') {
$entity = entity_get_info($entity_type);
if (count($entity['bundles']) > 0) {
if (empty($bundle)) {
$form['bundle_inherit'] = array(
'#type' => 'fieldset',
'#tree' => TRUE,
'#title' => t('Inheritance'),
);
$form['bundle_inherit']['entity_type'] = array(
'#type' => 'value',
'#value' => $entity_type,
);
$form['bundle_inherit']['#parents'] = array(
'bundle_inherit',
);
$form['bundle_inherit']['inherit'] = array(
'#type' => 'checkbox',
'#title' => t('Inherit from other'),
);
foreach ($entity['bundles'] as $bundle_name => $bundle) {
$options[$bundle_name] = $bundle['label'];
}
$form['bundle_inherit']['parent_type'] = array(
'#type' => 'select',
'#options' => $options,
'#title' => t('Parent'),
'#states' => array(
// Hide the inheritance settings when inherit checkbox is disabled.
'invisible' => array(
'input[name="bundle_inherit[inherit]"]' => array(
'checked' => FALSE,
),
),
),
);
$form['bundle_inherit']['mode'] = array(
'#type' => 'radios',
'#options' => array(
'strict' => t('Strict inherit'),
'soft' => t('Soft inherit'),
),
'#default_value' => 'strict',
'#required' => TRUE,
'#states' => array(
// Hide the inheritance settings when inherit checkbox is disabled.
'invisible' => array(
'input[name="bundle_inherit[inherit]"]' => array(
'checked' => FALSE,
),
),
),
'#title' => t('Inheritance mode'),
);
}
else {
$parent_bundle_name = bundle_inherit_bundle_get_parent($entity_type, $bundle);
if (!empty($parent_bundle_name)) {
$form['bundle_inherit'] = array(
'#type' => 'fieldset',
'#tree' => TRUE,
'#title' => t('Inheritance'),
);
$form['bundle_inherit']['message'] = array(
'#markup' => t('This bundle was inherited from !parent_bundle bundle.', array(
'!parent_bundle' => l($entity['bundles'][$parent_bundle_name]['label'], $entity['bundles'][$parent_bundle_name]['admin']['real path'] . '/fields'),
)),
);
}
}
}
}
/**
* Should be executed when entity creation form is submiting.
*
* @param $bundle
* Newly created bundle name.
*/
function bundle_inherit_attach_inherit_form_submit($bundle, &$form, &$form_state) {
if (isset($form_state['values']['bundle_inherit']) && $form_state['values']['bundle_inherit']['inherit']) {
$bundle_inherit_values = $form_state['values']['bundle_inherit'];
bundle_inherit_perform($bundle_inherit_values['entity_type'], $bundle, $bundle_inherit_values['parent_type'], $bundle_inherit_values['mode'] == 'strict' ? TRUE : FALSE);
}
}
/**
* Implements hook_field_attach_create_bundle().
*/
function bundle_inherit_field_attach_create_bundle($entity_type, $bundle) {
$exists = db_query('SELECT 1 FROM {bundle_inherit} WHERE entity_type = :entity_type AND bundle = :bundle', array(
':entity_type' => $entity_type,
':bundle' => $bundle,
))
->fetchField();
if (!$exists) {
db_insert('bundle_inherit')
->fields(array(
'entity_type' => $entity_type,
'bundle' => $bundle,
'bundle_parent' => '',
))
->execute();
}
}
/**
* Implements hook_field_attach_delete_bundle().
*/
function bundle_inherit_field_attach_delete_bundle($entity_type, $bundle, $instances) {
db_delete('bundle_inherit')
->condition('entity_type', $entity_type)
->condition(db_or()
->condition('bundle_parent', $bundle)
->condition('bundle', $bundle))
->execute();
}
/**
* Implements hook_field_attach_rename_bundle().
*/
function bundle_inherit_field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) {
db_update('bundle_inherit')
->condition('entity_type', $entity_type)
->condition('bundle', $bundle_old)
->fields(array(
'bundle' => $bundle_new,
))
->execute();
db_update('bundle_inherit')
->condition('entity_type', $entity_type)
->condition('bundle_parent', $bundle_old)
->fields(array(
'bundle_parent' => $bundle_new,
))
->execute();
}
/**
* Sort rows hierarchycly and allow to indent them.
* @param $entity_type
* Entity type.
* @param $rows
* Rows.
* @param $apply_indention
* Whether ty apply indention or not. Default to TRUE.
* @param $indention_options
* (optional) Indention options. Structured array with following keys:
* - 'indention callback': The function to call to indent row.
* Takes $row, $indention_options and $level as arguments. $level is the
* bundle level in hierarchy started from zero. Default callback is
* bundle_inherit_sort_rows_default_indent.
* - 'name col index': Used in default indention callback. If each row is an
* array you can specify the column to indent. If not you cen set 'name col
* index' to 'self' and entire row will be indented.
* - 'indenter': Used in default indention callback. It is like a delimiter
* in implode() function. Will be multiplied on the bundle level and prepend
* the row value.
*/
function bundle_inherit_sort_rows($entity_type, &$rows, $apply_indention = TRUE, $indention_options = array()) {
$indention_options += array(
'name col index' => 0,
'indenter' => '- - ',
'indention callback' => 'bundle_inherit_sort_rows_default_indent',
'apply indention' => $apply_indention,
);
$tree = bundle_inherit_get_tree($entity_type);
$new_rows = array();
foreach ($tree as $bundle_type => $bundle) {
if (isset($rows[$bundle_type])) {
$new_row = $rows[$bundle_type];
if ($indention_options['apply indention']) {
$indention_options['indention callback']($new_row, $indention_options, $bundle['depth']);
}
$new_rows[] = $new_row;
}
}
$rows = $new_rows;
}
/**
* Default indention function for bundle_inherit_sort_rows.
*/
function bundle_inherit_sort_rows_default_indent(&$row, $indention_options, $level) {
if ($indention_options['name col index'] === 'self') {
if (!is_array($row)) {
$row['data'] = str_repeat($indention_options['indenter'], $level) . $row['data'];
}
elseif (isset($row['data'])) {
$row = str_repeat($indention_options['indenter'], $level) . $row;
}
}
else {
if (!is_array($row[$indention_options['name col index']])) {
$row[$indention_options['name col index']] = str_repeat($indention_options['indenter'], $level) . $row[$indention_options['name col index']];
}
elseif (isset($row[$indention_options['name col index']]['data'])) {
$row[$indention_options['name col index']]['data'] = str_repeat($indention_options['indenter'], $level) . $row[$indention_options['name col index']]['data'];
}
}
}
/**
* Get bundles tree.
*/
function bundle_inherit_get_tree($entity_type, $parent_bundle_type = '', $depth = 0) {
if ($depth == 0) {
$tree =& drupal_static(__FUNCTION__ . ':' . $parent_bundle_type);
}
if (!isset($tree)) {
$tree = array();
$entity_type_info = entity_get_info($entity_type);
$children = bundle_inherit_bundle_get_children($entity_type, $parent_bundle_type);
foreach ($children as $child) {
$tree[$child['type']] = array(
'type' => $child['type'],
'label' => $child['label'],
'depth' => $depth,
);
$tree = array_merge($tree, bundle_inherit_get_tree($entity_type, $child['type'], $depth + 1));
}
}
return $tree;
}
/**
* Implements hook_menu_alter().
*/
function bundle_inherit_menu_alter(&$items) {
$access_callback = 'bundle_inherit_access_gate';
foreach (entity_get_info() as $entity_type => $entity_info) {
if ($entity_info['fieldable']) {
foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
if (isset($bundle_info['admin'])) {
// Extract path information from the bundle.
$path = $bundle_info['admin']['path'];
// This is the position of the %field_ui_menu placeholder in the
// items below.
$field_position = count(explode('/', $path)) + 1;
$items["{$path}/fields/%field_ui_menu"]['access arguments'] = array(
$items["{$path}/fields/%field_ui_menu"]['access callback'],
$items["{$path}/fields/%field_ui_menu"]['access arguments'],
$field_position,
);
$items["{$path}/fields/%field_ui_menu"]['access callback'] = $access_callback;
$items["{$path}/fields/%field_ui_menu/edit"]['access arguments'] = array(
$items["{$path}/fields/%field_ui_menu/edit"]['access callback'],
$items["{$path}/fields/%field_ui_menu/edit"]['access arguments'],
$field_position,
);
$items["{$path}/fields/%field_ui_menu/edit"]['access callback'] = $access_callback;
$items["{$path}/fields/%field_ui_menu/delete"]['access arguments'] = array(
$items["{$path}/fields/%field_ui_menu/delete"]['access callback'],
$items["{$path}/fields/%field_ui_menu/delete"]['access arguments'],
$field_position,
);
$items["{$path}/fields/%field_ui_menu/delete"]['access callback'] = $access_callback;
$items["{$path}/fields/%field_ui_menu/field-settings"]['access arguments'] = array(
$items["{$path}/fields/%field_ui_menu/field-settings"]['access callback'],
$items["{$path}/fields/%field_ui_menu/field-settings"]['access arguments'],
$field_position,
);
$items["{$path}/fields/%field_ui_menu/field-settings"]['access callback'] = $access_callback;
$items["{$path}/fields/%field_ui_menu/widget-type"]['access arguments'] = array(
$items["{$path}/fields/%field_ui_menu/widget-type"]['access callback'],
$items["{$path}/fields/%field_ui_menu/widget-type"]['access arguments'],
$field_position,
);
$items["{$path}/fields/%field_ui_menu/widget-type"]['access callback'] = $access_callback;
$items["{$path}/fields/%field_ui_menu/delete"]['access arguments'] = array(
$items["{$path}/fields/%field_ui_menu/delete"]['access callback'],
$items["{$path}/fields/%field_ui_menu/delete"]['access arguments'],
$field_position,
);
$items["{$path}/fields/%field_ui_menu/delete"]['access callback'] = $access_callback;
}
}
}
}
}
/**
* Prevent user from editing\deleting inherited field instance.
*/
function bundle_inherit_access_gate($callback, $arguments, $instance) {
// First check parent restrictions
$access = call_user_func_array($callback, $arguments);
if (false == $access) {
return false;
}
// Check if this field was inherited
$bundle = $instance['bundle'];
$entity_type = $instance['entity_type'];
$field_name = $instance['field_name'];
$parent = bundle_inherit_bundle_get_parent($entity_type, $bundle);
if (!empty($parent)) {
// Check if parent bundle has this field attached
if (field_info_instance($entity_type, $field_name, $parent)) {
return false;
}
}
return true;
}