field_conditional_state.module in Field Conditional States 7
Same filename and directory in other branches
Main functions for field_conditional_state
File
field_conditional_state.moduleView source
<?php
/**
* @file
* Main functions for field_conditional_state
*/
/**
* Implements hook_menu().
*/
function field_conditional_state_menu() {
$items = array();
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'];
if (isset($bundle_info['admin']['bundle argument'])) {
$bundle_arg = $bundle_info['admin']['bundle argument'];
$bundle_pos = (string) $bundle_arg;
}
else {
$bundle_arg = $bundle_name;
$bundle_pos = '0';
}
$field_position = count(explode('/', $path)) + 1;
$items["{$path}/fields/%field_ui_menu/field-conditional-states"] = array(
'load arguments' => array(
$entity_type,
$bundle_arg,
$bundle_pos,
'%map',
),
'title' => 'Conditional states',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'field_conditional_state_settings_form',
$field_position,
),
'type' => MENU_LOCAL_TASK,
'access arguments' => array(
'administer field conditional states',
),
'weight' => 4,
'file' => 'field_conditional_state.admin.inc',
);
}
}
}
}
return $items;
}
/**
* Implements hook_permission().
*/
function field_conditional_state_permission() {
return array(
'administer field conditional states' => array(
'title' => t('Administer field conditional states'),
),
);
}
/**
* Implements hook_theme().
*/
function field_conditional_state_theme($existing, $type, $theme, $path) {
return array(
'field_conditional_state_settings_form' => array(
'render element' => 'form',
),
);
}
/**
* Implements hook_field_delete_instance().
*/
function field_conditional_state_field_delete_instance($instance) {
$bundle = $instance['bundle'];
$instance_name = $instance['field_name'];
$query_args = array(
':bundle' => $bundle,
':field_name' => $instance_name,
':control_field' => $instance_name,
);
$result = db_query("SELECT count(*) AS count FROM {field_conditional_state} WHERE bundle = :bundle AND (field_name = :field_name OR control_field = :control_field)", $query_args)
->fetchObject();
// Delete conditions for deleted instance.
if ($result->count > 0) {
$delete_trigger_instance = db_delete('field_conditional_state')
->condition('field_name', $instance_name)
->condition('bundle', $bundle)
->execute();
$delete_control_instance = db_delete('field_conditional_state')
->condition('control_field', $instance_name)
->condition('bundle', $bundle)
->execute();
}
}
/**
* Implements hook_field_attach_delete_bundle().
*/
function field_conditional_state_field_attach_delete_bundle($entity_type, $bundle, $instances) {
$query_args = array(
':bundle' => $bundle,
);
$result = db_query("SELECT count(*) AS count FROM {field_conditional_state} WHERE bundle = :bundle", $query_args)
->fetchObject();
// Delete conditions for deleted bundle.
if ($result->count > 0) {
$delete_trigger_field = db_delete('field_conditional_state')
->condition('bundle', $bundle, '=')
->execute();
}
}
/**
* Implements hook_field_state().
*/
function field_conditional_state_field_state() {
return array(
'visible' => array(
'state_handler' => 'field_conditional_state_handle_visibility',
'validate_handler' => 'field_conditional_state_element_validate',
),
'invisible' => array(
'state_handler' => 'field_conditional_state_handle_visibility',
'validate_handler' => 'field_conditional_state_element_validate',
),
'enabled' => array(
'state_handler' => 'field_conditional_state_handle_availability',
'validate_handler' => 'field_conditional_state_element_validate',
),
'disabled' => array(
'state_handler' => 'field_conditional_state_handle_availability',
'validate_handler' => 'field_conditional_state_element_validate',
),
'required' => array(
'state_handler' => 'field_conditional_state_handle_requirements',
),
);
}
/**
* Implements hook_entity_view().
*
* Hide field in entity view if state for controlled field
* is set to visible or infisible based on the value from control field.
*/
function field_conditional_state_entity_view($entity, $type, $view_mode, $langcode) {
if (!empty($entity->content)) {
foreach ($entity->content as $field_name => $field_content) {
if (is_array($field_content) && isset($field_content['#bundle'])) {
$field_conditions = field_conditional_state_get_field_conditions($field_name, $field_content['#bundle']);
$data = array(
$type,
$view_mode,
$langcode,
);
drupal_alter('field_conditional_state_entity_view', $field_conditions, $entity, $data);
if ($field_conditions) {
foreach ($field_conditions as $condition) {
$lang = $field_content['#language'];
$control_field_name = $condition['control_field'];
$control_values = $entity->{$control_field_name};
$trigger_value_exist = field_conditional_state_trigger_value_in_field_value($control_values[$lang], $condition['trigger_values'], $condition['condition_type']);
if ($condition['state'] == 'visible' && !$trigger_value_exist || $condition['state'] == 'invisible' && $trigger_value_exist) {
$entity->content[$field_name]['#access'] = FALSE;
}
}
}
}
}
}
}
/**
* Implements hook_module_implements_alter().
*
* Ensures the call to field_conditional_state_link_form_field_ui_field_overview_form_alter()
* function runs after any invocation of the form_alter() by other modules, e.g.
* Field Group module.
*/
function field_conditional_state_module_implements_alter(&$implementations, $hook) {
if ($hook == 'form_alter' && array_key_exists('field_conditional_state', $implementations)) {
$group = $implementations['field_conditional_state'];
unset($implementations['field_conditional_state']);
$implementations['field_conditional_state'] = $group;
}
}
/**
* Implements hook_form_FORM_ID_alter().
* Using hook_form_field_ui_field_overview_form_alter.
*/
function field_conditional_state_form_field_ui_field_overview_form_alter(&$form, &$form_state) {
$entity_type = $form['#entity_type'];
$bundle = $form['#bundle'];
$bundle = field_extract_bundle($entity_type, $bundle);
$admin_path = _field_ui_bundle_admin_path($entity_type, $bundle);
$table =& $form['fields'];
// Find the operations column and number of existing operations, because
// other modules may alter the form to add operations before or after us.
foreach ($table['#header'] as $key => $header) {
if (is_array($header) && !empty($header['data'])) {
$op_col = $key;
$op_count = $header['colspan'];
continue;
}
}
// Increment the colspan.
$table['#header'][$op_col]['colspan'] = $op_count + 1;
$instances = field_info_instances($entity_type, $bundle);
foreach (element_children($table) as $key) {
if (array_key_exists($key, $instances)) {
$field = field_info_field($instances[$key]['field_name']);
$admin_field_path = $admin_path . '/fields/' . $instances[$key]['field_name'];
$table[$key]['field-conditional-states'] = array(
'#type' => 'link',
'#title' => t('States'),
'#href' => $admin_field_path . '/field-conditional-states',
'#options' => array(
'attributes' => array(
'title' => t('Manage field conditional state rules.'),
),
),
);
}
else {
$table[$key]['field-conditional-states'] = array(
'#markup' => '',
);
}
}
}
/**
* Implements hook_element_info_alter().
*/
function field_conditional_state_element_info_alter(&$type) {
foreach ($type as $key => $value) {
if (isset($value['#input']) && $value['#input'] == TRUE) {
$type[$key]['#process'][] = 'field_conditional_state_element_process';
}
}
}
/**
* @todo.
*/
function field_conditional_state_element_process($element, &$form_state, $form) {
/*
* $processed_fields is a "memory" of already processed field names.
* Each occurence of a field name will get an index appended to the control_field name
* so if there are multiple instances of one field (field collections) they can be handled separately
*/
static $processed_fields = array();
if (isset($element['#field_name']) && isset($element['#bundle']) && $form['#form_id'] != 'field_ui_field_edit_form') {
$indexed_field_name = $element['#field_name'];
if (!isset($processed_fields[$indexed_field_name]) || count($processed_fields[$indexed_field_name]) == 0) {
$processed_fields[$indexed_field_name] = array(
0,
);
}
$element_field_name_index = max($processed_fields[$indexed_field_name]) + 1;
$processed_fields[$indexed_field_name][] = $element_field_name_index;
$indexed_field_name .= '_' . $element_field_name_index;
$path = drupal_get_path('module', 'field_conditional_state');
$element['#attached']['js'][] = "{$path}/js/conditional_state.js";
$element['#attached']['js'][] = "{$path}/js/required_conditional_state.js";
$control_field = field_conditional_state_is_control_field($element['#field_name'], $element['#bundle']);
// Check if element is control element.
if ($control_field) {
$element['#prefix'] = '<div class="conditional_state_control_field_' . $indexed_field_name . '">';
$element['#suffix'] = '</div>';
}
// Get element conditions.
$element_conditions = field_conditional_state_get_field_conditions($element['#field_name'], $element['#bundle'], $element_field_name_index);
if ($element_conditions) {
$element_states = field_conditional_state_set_field_state($element_conditions);
$conditional_states = field_conditional_state_module_invoke();
$form_element = drupal_array_get_nested_value($form, (array) $element['#field_parents']);
foreach ($element_states as $state => $condition) {
if (isset($form_element[$element['#field_name']])) {
$function = $conditional_states[$state]['state_handler'];
$form_element[$element['#field_name']] = $function($state, $condition, $form_element[$element['#field_name']], $element);
$form_element[$element['#field_name']]['#bundle'] = $element['#bundle'];
}
}
foreach ($element_conditions as $key => $condition) {
if (isset($conditional_states[$state]['validate_handler'])) {
$validate_function = $conditional_states[$state]['validate_handler'];
$form_element[$element['#field_name']]['#element_validate'][] = $validate_function;
$form_element[$element['#field_name']]['#conditional_state'] = $condition;
}
if ($form_state['input']) {
field_conditional_state_set_requirements($element, $form_state, $condition);
}
}
}
}
return $element;
}
/**
* @todo.
* set empty value for invisible field and requirements to false
*/
function field_conditional_state_element_validate(&$element, &$form_state) {
$lang = $element['#language'];
$element_status = field_conditional_state_element_status($element, $form_state['input'], $element['#conditional_state']);
if ($element_status == 'not_available') {
form_set_value($element[$lang], array(), $form_state);
}
}
/**
* @todo.
* if field is not visible or disabled set requirements to false
*/
function field_conditional_state_set_requirements(&$element, $form_state, $condition) {
$element_status = field_conditional_state_element_status($element, $form_state['input'], $condition);
if ($element_status == 'not_available') {
$element['#required'] = FALSE;
}
elseif ($element_status == 'required') {
$element['#required'] = TRUE;
}
}
/**
* @todo.
* check the element status when form is being validated, add @param description
*
* @param array $element
* Description goes here.
* @param array $input
* Description goes here.
*
* @condition array $condition
* Description goes here.
*/
function field_conditional_state_element_status(&$element, $input, $condition) {
$lang = $element['#language'];
$field_name = isset($element['#field_name']) ? $element['#field_name'] : $element[$lang]['#field_name'];
$element_parents = isset($element['#field_parents']) ? $element['#field_parents'] : $element[$lang]['#field_parents'];
$field_info = field_info_field($field_name);
if ($element_parents) {
$input = drupal_array_get_nested_value($input, $element_parents);
}
$control_field_name = $condition['control_field'];
// if checkbox is not checked.
if (!isset($input[$control_field_name])) {
if ($condition['state'] == 'visible' || $condition['state'] == 'enabled') {
return 'not_available';
}
}
else {
$trigger_value_exist = field_conditional_state_trigger_value_in_field_value($input[$control_field_name][$lang], $condition['trigger_values'], $condition['condition_type']);
if (($condition['state'] == 'visible' || $condition['state'] == 'enabled') && !$trigger_value_exist || ($condition['state'] == 'invisible' || $condition['state'] == 'disabled') && $trigger_value_exist) {
return 'not_available';
}
elseif ($condition['state'] == 'required' && $trigger_value_exist) {
$value = isset($element['fid']['#value']) ? $element['fid']['#value'] : $element['#value'];
// Single value - simple check, if value is empty return required.
if ($field_info['cardinality'] == 1) {
// Support for date field.
if ($element['#type'] == 'date_combo') {
$granularity = $field_info['settings']['granularity'];
$empty_date = field_conditional_state_check_date_value($granularity, $element['#value']['value']);
if ($empty_date) {
$element['value']['#required'] = TRUE;
}
}
if (empty($value) || $value == '_none') {
return 'required';
}
}
elseif ($field_info['cardinality'] > 1) {
if (is_array($value)) {
if (array_key_exists('_none', $value)) {
unset($element['#value']['_none']);
}
if (empty($value) && $element['#type'] != 'date_combo') {
return 'required';
}
}
elseif (empty($value)) {
static $values;
$values[$element['#field_name']][] = $value;
if ($field_info['cardinality'] == count($values[$element['#field_name']]) && empty($value)) {
foreach ($values[$element['#field_name']] as $value) {
if ($value) {
return FALSE;
}
}
return 'required';
}
elseif (empty($value) && isset($element['#value']['_weight']) && $element['#value']['_weight'] == 0) {
return 'required';
}
}
}
if (isset($element['#type']) && $element['#type'] == 'date_combo') {
static $values;
$granularity = $field_info['settings']['granularity'];
$values[$element['#field_name']][] = field_conditional_state_check_date_value($granularity, $element['#value']['value']);
if ($field_info['cardinality'] == count($values[$element['#field_name']])) {
foreach ($values[$element['#field_name']] as $empty_value) {
if (!$empty_value) {
return FALSE;
}
}
$element['value']['#required'] = TRUE;
}
}
}
}
return FALSE;
}
/**
* @todo.
* check if one of the date parts is empty
* (year, month, day, hour, minute or second)
*
* @param array $granularity
* date format
* @param array $values
* Submitted values.
*/
function field_conditional_state_check_date_value($granularity, $values) {
foreach ($granularity as $date_part) {
if (empty($values[$date_part])) {
return TRUE;
}
}
return FALSE;
}
/**
* @todo.
* Search condition target values in field values.
*
* @param array $values
* Current values for the field.
* @param array $trigger_values
* Target values for the condition.
*/
function field_conditional_state_trigger_value_in_field_value($values, $trigger_values, $condition_type) {
if ($condition_type == 'or') {
// If one of the submitted values is in trigger values, return true.
if (is_array($values)) {
foreach ($values as $delta => $value) {
foreach ($trigger_values as $trigger_value) {
if (is_array($value)) {
$search = array_search($trigger_value, $value);
if ($search) {
// Target value is set in field value - return TRUE.
return TRUE;
}
}
elseif ($value == $trigger_value) {
return TRUE;
}
}
}
}
else {
if (in_array($values, $trigger_values)) {
return TRUE;
}
}
}
else {
$return = TRUE;
foreach ($trigger_values as $key => $trigger_value) {
if (!in_array($trigger_value, $values)) {
$return = FALSE;
}
}
return $return;
}
return FALSE;
}
/**
* @todo.
* Set states for target field.
*
* @param array $conditions
* An array of state conditions.
*/
function field_conditional_state_set_field_state($conditions) {
$conditional_states = field_conditional_state_module_invoke();
$states = array();
foreach ($conditions as $condition_settings) {
$selector = '.conditional_state_control_field_' . $condition_settings['control_field_indexed'] . ' :input';
$condition_values = array();
if ($condition_settings['trigger_values']) {
foreach ($condition_settings['trigger_values'] as $c_value) {
$condition_values[] = $c_value;
}
}
else {
$condition_values = $condition_settings['trigger_values'];
}
$states[$condition_settings['state']][$selector] = array(
'value' => $condition_values,
'condition_type' => $condition_settings['condition_type'],
);
}
return $states;
}
/**
* @todo.
*/
function field_conditional_state_module_invoke() {
$conditional_states = array();
foreach (module_implements('field_state') as $module) {
$function = $module . '_field_state';
$conditional_states += $function();
}
return $conditional_states;
}
/**
* @todo.
* get all the conditions bundle types
*
* @return array
* An array of bundle types.
*/
function field_conditional_state_get_bundle_types() {
$bundle_types = array();
$result = db_query("SELECT bundle FROM {field_conditional_state}");
foreach ($result as $res) {
$bundle_types[] = $res->bundle;
}
return $bundle_types;
}
/**
* @todo.
* Check if field is control field.
*
* @param string $field_name
* The name of the field.
* @param string $bundle
* The name of the bundle for this field.
*
* @return bool
* Returns true or false if the requested field is a control field.
*/
function field_conditional_state_is_control_field($field_name, $bundle) {
$placeholders = array(
':field_name' => $field_name,
':bundle' => $bundle,
);
$sql = db_query("SELECT COUNT(id) as count FROM {field_conditional_state} WHERE control_field = :field_name AND bundle = :bundle", $placeholders)
->fetchObject();
if (isset($sql->count) && $sql->count > 0) {
return TRUE;
}
return FALSE;
}
/**
* @todo.
* get conditions for the field
*
* @param string $field_name
* The name of the field.
* @param string $bundle
* The name of the bundle.
*
* @return array
* An array of conditions.
*/
function field_conditional_state_get_field_conditions($field_name, $bundle, $control_field_index = null) {
$conditions = array();
$result = db_query("SELECT * FROM {field_conditional_state} WHERE field_name = :field_name AND bundle = :bundle", array(
':field_name' => $field_name,
':bundle' => $bundle,
));
foreach ($result as $res) {
$conditions["condition_{$res->id}"] = array(
'id' => $res->id,
'control_field' => $res->control_field,
'control_field_indexed' => $res->control_field,
'state' => $res->state,
'trigger_values' => unserialize($res->trigger_values),
'condition_type' => $res->condition_type,
);
if (!empty($control_field_index)) {
$conditions["condition_{$res->id}"]['control_field_indexed'] .= '_' . $control_field_index;
}
}
return $conditions;
}
/**
* @todo.
* handler for 'disable', 'enable' states
*
* @param string $state
* The current state.
* @param string $condition
* The current condition.
* @param array $form_element
* An associative array for the form element.
* @param array $element
* The element array.
*
* @return array
* Returns the $form_element.
*/
function field_conditional_state_handle_availability($state, $condition, $form_element, &$element) {
$field_info = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
switch ($field_info['widget']['type']) {
case 'options_buttons':
foreach ($element['#options'] as $option_id => $option) {
$element[$option_id]['#states'][$state] = $condition;
}
break;
case 'file_generic':
case 'image_image':
$element['upload']['#states'][$state] = $condition;
$element['upload_button']['#states'][$state] = $condition;
$element['remove_button']['#states'][$state] = $condition;
break;
case 'date_popup':
case 'date_popup_repeat':
case 'date_select':
case 'date_select_repeat':
case 'date_text':
case 'date_text_repeat':
$form_element['#attributes'] = array(
'class' => array(
'date-conditional-state',
),
'state' => $state,
);
$element['#states'][$state] = $condition;
break;
default:
$element['#states'][$state] = $condition;
}
return $form_element;
}
/**
* @todo.
* handler for 'required' state
*
* @param string $state
* The current state.
* @param string $condition
* The current condition.
* @param array $form_element
* An associative array for the form element.
* @param array $element
* The element array.
*
* @return array
* Returns the $form_element.
*/
function field_conditional_state_handle_requirements($state, $condition, $form_element, &$element) {
$form_element['#states'][$state] = $condition;
return $form_element;
}
/**
* @todo.
* handler for 'visible', 'invisible' state
*
* @param string $state
* The current state.
* @param string $condition
* The current condition.
* @param array $form_element
* An associative array for the form element.
* @param array $element
* The element array.
*
* @return array
* Returns the $form_element.
*/
function field_conditional_state_handle_visibility($state, $condition, $form_element, &$element) {
$form_element['#states'][$state] = $condition;
return $form_element;
}