PageRenderTime 53ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/sites/all/modules/contrib/conditional_fields/conditional_fields.module

https://bitbucket.org/antisocnet/drupal
Unknown | 1993 lines | 1760 code | 233 blank | 0 comment | 0 complexity | aa76a8d242b83c6a6dc0deb1b9ed14e4 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * @file
  4. * Define dependencies between fields based on their states and values.
  5. *
  6. * Conditional Fields for Drupal 7 is basically an user interface for the States
  7. * API, plus the ability to hide fields on certain conditions when viewing
  8. * content.
  9. */
  10. /**
  11. * Dependency is triggered if the dependee has a certain value.
  12. */
  13. define('CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET', 1);
  14. /**
  15. * Dependency is triggered if the dependee has all values.
  16. */
  17. define('CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND', 2);
  18. /**
  19. * Dependency is triggered if the dependee has any of the values.
  20. */
  21. define('CONDITIONAL_FIELDS_DEPENDENCY_VALUES_OR', 3);
  22. /**
  23. * Dependency is triggered if the dependee has only one of the values.
  24. */
  25. define('CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR', 4);
  26. /**
  27. * Dependency is triggered if the dependee does not have any of the values.
  28. */
  29. define('CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT', 5);
  30. /**
  31. * Dependency is triggered if the dependee values match a regular expression.
  32. */
  33. define('CONDITIONAL_FIELDS_DEPENDENCY_VALUES_REGEX', 6);
  34. /**
  35. * Field view setting. Dependent is shown only if the dependency is triggered.
  36. */
  37. define('CONDITIONAL_FIELDS_FIELD_VIEW_EVALUATE', 1);
  38. /**
  39. * Field view setting. Dependent is shown only if the dependee is shown as well.
  40. */
  41. define('CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_ORPHAN', 2);
  42. /**
  43. * Field view setting. Dependent is highlighted if the dependency is not
  44. * triggered.
  45. */
  46. define('CONDITIONAL_FIELDS_FIELD_VIEW_HIGHLIGHT', 3);
  47. /**
  48. * Field view setting. Dependent has a textual description of the dependency.
  49. */
  50. define('CONDITIONAL_FIELDS_FIELD_VIEW_DESCRIBE', 4);
  51. /**
  52. * Field view setting. Dependent is shown only if the dependee is shown as well
  53. * and the dependency evaluates to TRUE.
  54. */
  55. define('CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_UNTRIGGERED_ORPHAN', 5);
  56. /**
  57. * Field edit setting. Dependent is shown only if the dependee is shown as well.
  58. */
  59. define('CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN', 1);
  60. /**
  61. * Field edit setting. Dependent is shown only if the dependee is shown as well
  62. * and the dependency evaluates to TRUE.
  63. */
  64. define('CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN', 2);
  65. /**
  66. * Field edit setting. Dependent is reset to its default values if the
  67. * dependency was not triggered when the form is submitted.
  68. */
  69. define('CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED', 3);
  70. /**
  71. * Implements hook_permission().
  72. */
  73. function conditional_fields_permission() {
  74. return array(
  75. 'administer dependencies' => array(
  76. 'title' => t('Administer dependencies'),
  77. 'description' => t('View, edit and delete field dependencies.'),
  78. ),
  79. );
  80. }
  81. /**
  82. * Implements hook_menu().
  83. */
  84. function conditional_fields_menu() {
  85. $items = array();
  86. // Ensure the following is not executed until field_bundles is working and
  87. // tables are updated. Needed to avoid errors on initial installation.
  88. if (defined('MAINTENANCE_MODE')) {
  89. return $items;
  90. }
  91. $items['admin/structure/dependencies'] = array(
  92. 'title' => 'Field dependencies',
  93. 'description' => 'Administer field dependencies for the site.',
  94. 'page callback' => 'conditional_fields_dependencies_overview_page',
  95. 'access arguments' => array('administer dependencies'),
  96. 'file' => 'includes/conditional_fields.admin.inc',
  97. );
  98. $items['admin/structure/dependencies/overview'] = array(
  99. 'title' => 'Overview',
  100. 'type' => MENU_DEFAULT_LOCAL_TASK,
  101. 'weight' => 1,
  102. );
  103. $items['admin/structure/dependencies/edit/%conditional_fields_dependency'] = array(
  104. 'title' => 'Edit dependency',
  105. 'page callback' => 'drupal_get_form',
  106. 'page arguments' => array('conditional_fields_dependency_edit_form', 4),
  107. 'access arguments' => array('administer dependencies'),
  108. 'file' => 'includes/conditional_fields.admin.inc',
  109. );
  110. $items['admin/structure/dependencies/delete/%conditional_fields_dependency'] = array(
  111. 'title' => 'Delete dependency',
  112. 'page callback' => 'drupal_get_form',
  113. 'page arguments' => array('conditional_fields_dependency_delete_form', 4),
  114. 'access arguments' => array('administer dependencies'),
  115. 'file' => 'includes/conditional_fields.admin.inc',
  116. );
  117. // Some of the following code is copied from field_ui_menu().
  118. // Create tabs for all possible bundles.
  119. foreach (entity_get_info() as $entity_type => $entity_info) {
  120. if ($entity_info['fieldable']) {
  121. $items["admin/structure/dependencies/$entity_type"] = array(
  122. 'title' => $entity_info['label'],
  123. 'page arguments' => array(NULL, 3),
  124. 'access arguments' => array('administer dependencies'),
  125. 'type' => MENU_LOCAL_TASK,
  126. 'weight' => 2,
  127. );
  128. foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
  129. if (module_exists('field_ui') && isset($bundle_info['admin'])) {
  130. // Extract path information from the bundle and replace any "magic"
  131. // wildcard with a normal one.
  132. $path = preg_replace('/(%[a-z0-9_]*)/', '%', $bundle_info['admin']['path']);
  133. if (isset($bundle_info['admin']['bundle argument'])) {
  134. $bundle_pos = $bundle_info['admin']['bundle argument'];
  135. }
  136. else {
  137. $bundle_pos = $bundle_name;
  138. }
  139. $items["$path/dependencies"] = array(
  140. 'title' => $entity_type == 'comment' ? 'Comment dependencies' : 'Manage dependencies',
  141. 'page callback' => 'conditional_fields_dependencies_overview_page',
  142. 'page arguments' => array($bundle_pos, $entity_type),
  143. 'type' => MENU_LOCAL_TASK,
  144. 'weight' => $entity_type == 'comment' ? 4 : 2,
  145. 'file' => 'includes/conditional_fields.admin.inc',
  146. 'access arguments' => array('administer dependencies'),
  147. );
  148. }
  149. }
  150. }
  151. }
  152. return $items;
  153. }
  154. /**
  155. * Implements hook_forms().
  156. *
  157. * Maps all dependency add forms to the same callback.
  158. */
  159. function conditional_fields_forms() {
  160. foreach (entity_get_info() as $entity_type => $entity_info) {
  161. if ($entity_info['fieldable']) {
  162. foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
  163. $forms['conditional_fields_dependency_add_form_' . $entity_type . '_' . $bundle_name] = array(
  164. 'callback' => 'conditional_fields_dependency_add_form',
  165. 'callback arguments' => array($entity_type, $bundle_name),
  166. );
  167. }
  168. }
  169. }
  170. return $forms;
  171. }
  172. /**
  173. * Implements hook_js_alter().
  174. *
  175. * Overrides core states API with a patched version that allows multiple
  176. * conditions and OR/XOR logic.
  177. */
  178. function conditional_fields_js_alter(&$javascript) {
  179. // Since Drupal 7.14, states.js includes the OR/XOR patch.
  180. if (isset($javascript['misc/states.js']) && version_compare(VERSION, '7.14', '<')) {
  181. $javascript['misc/states.js']['data'] = drupal_get_path('module', 'conditional_fields') . '/js/states.js';
  182. }
  183. }
  184. /**
  185. * Implements hook_element_info_alter().
  186. * Adds an #after_build function to all form elements.
  187. */
  188. function conditional_fields_element_info_alter(&$types) {
  189. foreach ($types as $type => $info) {
  190. $types[$type]['#after_build'][] = 'conditional_fields_element_after_build';
  191. }
  192. }
  193. /**
  194. * Processes form elements with dependencies.
  195. *
  196. * Just adds a #conditional_fields property to the form with the needed
  197. * data, which is used later in conditional_fields_form_after_build():
  198. * - The fields #parents property.
  199. * - Field dependencies data.
  200. */
  201. function conditional_fields_element_after_build($element, &$form_state) {
  202. // Ensure that the element is a field.
  203. if (isset($element['#field_name'])) {
  204. $field = $element;
  205. }
  206. elseif (isset($element['#language'], $element[$element['#language']], $element[$element['#language']]['#field_name'])) {
  207. // Some fields are wrapped in containers before processing.
  208. $field = $element[$element['#language']];
  209. }
  210. else {
  211. return $element;
  212. }
  213. $form = &$form_state['complete form'];
  214. // Avoid processing fields in fields_ui administration pages.
  215. if (drupal_substr($form['#form_id'], 0, 9) == 'field_ui_') {
  216. return $element;
  217. }
  218. // Some fields do not have entity type and bundle properties. In this case we
  219. // try to use the properties from the form. This is not an optimal solution,
  220. // since in case of fields in entities within entities they might not correspond,
  221. // and their dependencies will not be loaded.
  222. if (isset($field['#entity_type'], $field['#bundle'])) {
  223. $entity_type = $field['#entity_type'];
  224. $bundle = $field['#bundle'];
  225. }
  226. elseif (isset($form['#entity_type'], $form['#bundle'])) {
  227. $entity_type = $form['#entity_type'];
  228. $bundle = $form['#bundle'];
  229. }
  230. else {
  231. return $element;
  232. }
  233. $dependencies = conditional_fields_load_dependencies($entity_type, $bundle);
  234. if (!$dependencies) {
  235. return $element;
  236. }
  237. // Attach dependent.
  238. if (isset($dependencies['dependents'][$field['#field_name']])) {
  239. foreach ($dependencies['dependents'][$field['#field_name']] as $id => $dependency) {
  240. if (!isset($form['#conditional_fields'][$field['#field_name']]['dependees'][$id])) {
  241. conditional_fields_attach_dependency($form, array('#field_name' => $dependency['dependee']), $field, $dependency['options'], $id);
  242. }
  243. }
  244. }
  245. // Attach dependee.
  246. // TODO: collect information about every element of the dependee widget, not
  247. // just the first encountered. This bottom-up approach would allow us to
  248. // define per-element sets of dependency values.
  249. if (isset($dependencies['dependees'][$field['#field_name']])) {
  250. foreach ($dependencies['dependees'][$field['#field_name']] as $id => $dependency) {
  251. if (!isset($form['#conditional_fields'][$field['#field_name']]['dependents'][$id])) {
  252. conditional_fields_attach_dependency($form, $field, array('#field_name' => $dependency['dependent']), $dependency['options'], $id);
  253. }
  254. }
  255. }
  256. return $element;
  257. }
  258. /**
  259. * Attaches a single dependency to a form.
  260. *
  261. * Call this function when defining or altering a form to create dependencies
  262. * dynamically.
  263. *
  264. * @param $form
  265. * The form where the dependency is attached.
  266. * @param $dependee
  267. * The dependee field form element. Either a string identifying the element
  268. * key in the form, or a fully built field array. Actually used properties of
  269. * the array are #field_name and #parents.
  270. * @param $dependent
  271. * The dependent field form element. Either a string identifying the element
  272. * key in the form, or a fully built field array. Actually used properties of
  273. * the array are #field_name and #field_parents.
  274. * @param $options
  275. * An array of dependency options with the following key/value pairs:
  276. * - state: The state applied to the dependent when the dependency is
  277. * triggered. See conditional_fields_states() for available states.
  278. * - condition: The condition for the dependency to be triggered. See
  279. * conditional_fields_conditions() for available conditions.
  280. * - values_set: One of the following constants:
  281. * - CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET: Dependency is
  282. * triggered if the dependee has a certain value defined in 'value'.
  283. * - CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND: Dependency is triggered if
  284. * the dependee has all the values defined in 'values'.
  285. * - CONDITIONAL_FIELDS_DEPENDENCY_VALUES_OR: Dependency is triggered if the
  286. * dependee has any of the values defined in 'values'.
  287. * - CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR: Dependency is triggered if
  288. * the dependee has only one of the values defined in 'values'.
  289. * - CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT: Dependency is triggered if
  290. * the dependee does not have any of the values defined in 'values'.
  291. * - value: The value to be tested when 'values_set' is
  292. * CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET. An associative array with
  293. * the same structure of the dependee field values as found in
  294. * $form_states['values] when the form is submitted. You can use
  295. * field_default_extract_form_values() to extract this array.
  296. * - values: The array of values to be tested when 'values_set' is not
  297. * CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET.
  298. * - value_form: An associative array with the same structure of the dependee
  299. * field values as found in $form_state['input']['value']['field'] when the
  300. * form is submitted.
  301. * - effect: The jQuery effect associated to the state change. See
  302. * conditional_fields_effects() for available effects and options.
  303. * - effect_options: The options for the active effect.
  304. * - element_view: An associative array of field view behaviors with
  305. * CONDITIONAL_FIELDS_FIELD_VIEW_* constants as keys and the same constants
  306. * as values for enabled behaviors and 0 for disabled behaviors.
  307. * See conditional_fields_behaviors() for descriptions.
  308. * - element_view_per_role: Set to 1 to activate field view settings per role.
  309. * - element_view_roles: An associative array of field view settings per role
  310. * where the keys are role ids and the values are arrays with the same
  311. * structure of 'element_view'.
  312. * - element_edit: An associative array of field edit behaviors with
  313. * CONDITIONAL_FIELDS_FIELD_EDIT_* constants as keys and the same constants
  314. * as values for enabled behaviors and 0 for disabled behaviors.
  315. * See conditional_fields_behaviors() for descriptions.
  316. * - element_edit_per_role: Set to 1 to activate field edit settings per role.
  317. * - element_edit_roles: An associative array of field edit settings per role
  318. * where the keys are role ids and the values are arrays with the same
  319. * structure of 'element_edit'.
  320. * - selector: (optional) Custom jQuery selector for the dependee.
  321. * @param $id
  322. * (internal use) The identifier for the dependency. Omit this parameter when
  323. * attaching a custom dependency.
  324. *
  325. * Note that you don't need to manually set all these options, since default
  326. * settings are always provided.
  327. */
  328. function conditional_fields_attach_dependency(&$form, $dependee, $dependent, $options, $id = 0) {
  329. $options += conditional_fields_dependency_default_options();
  330. // The absence of the $id parameter identifies a custom dependency.
  331. if (!$id) {
  332. // String values are accepted to simplify usage of this function with custom
  333. // forms.
  334. if (is_string($dependee) && is_string($dependent)) {
  335. $dependee = array(
  336. '#field_name' => $dependee,
  337. '#parents' => array($dependee),
  338. );
  339. $dependent = array(
  340. '#field_name' => $dependent,
  341. '#field_parents' => array($dependent),
  342. );
  343. // Custom dependencies have automatically assigned a progressive id.
  344. static $current_id;
  345. if (!$current_id) {
  346. $current_id = 1;
  347. }
  348. $id = $current_id;
  349. $current_id++;
  350. }
  351. }
  352. // Attach dependee.
  353. // Use the #parents property of the dependee instead of #field_parents since
  354. // we will need access to the full structure of the widget.
  355. if (isset($dependee['#parents'])) {
  356. $form['#conditional_fields'][$dependee['#field_name']]['parents'] = $dependee['#parents'];
  357. $form['#conditional_fields'][$dependee['#field_name']]['dependents'][$id] = array(
  358. 'dependent' => $dependent['#field_name'],
  359. 'options' => $options,
  360. );
  361. }
  362. // Attach dependent.
  363. if (isset($dependent['#field_parents'])) {
  364. $dependent_parents = $dependent['#field_parents'];
  365. }
  366. elseif (isset($dependent['#parents'])) {
  367. $dependent_parents = $dependent['#parents'];
  368. }
  369. if (isset($dependent_parents)) {
  370. $form['#conditional_fields'][$dependent['#field_name']]['field_parents'] = $dependent_parents;
  371. $form['#conditional_fields'][$dependent['#field_name']]['dependees'][$id] = array(
  372. 'dependee' => $dependee['#field_name'],
  373. 'options' => $options,
  374. );
  375. }
  376. // Actual processing is done in conditional_fields_form_after_build().
  377. // Append the property so the callback runs last.
  378. _conditional_fields_element_add_property($form, '#after_build', 'conditional_fields_form_after_build', 'append');
  379. }
  380. /**
  381. * after_build callback for forms with dependencies.
  382. *
  383. * Builds and attaches #states properties to dependent fields, adds additional
  384. * visual effects handling to the States API and attaches a validation callback
  385. * to the form that handles validation of dependent fields.
  386. */
  387. function conditional_fields_form_after_build($form, &$form_state) {
  388. // Dependencies data is attached in conditional_fields_element_after_build().
  389. if (empty($form['#conditional_fields'])) {
  390. return $form;
  391. }
  392. $effects = array();
  393. $state_handlers = conditional_fields_states_handlers();
  394. // Cycle all dependents.
  395. foreach ($form['#conditional_fields'] as $dependent => $dependent_info) {
  396. $states = array();
  397. if (empty($dependent_info['dependees'])) {
  398. continue;
  399. }
  400. $dependent_location = array_merge($dependent_info['field_parents'], array($dependent));
  401. $dependent_form_field = drupal_array_get_nested_value($form, $dependent_location);
  402. // Cycle the dependant's dependees.
  403. foreach ($dependent_info['dependees'] as $dependency) {
  404. $dependee = $dependency['dependee'];
  405. if (empty($form['#conditional_fields'][$dependee])) {
  406. continue;
  407. }
  408. $dependee_info = $form['#conditional_fields'][$dependee];
  409. $dependee_form_field = drupal_array_get_nested_value($form, $dependee_info['parents']);
  410. $options = $dependency['options'];
  411. // Load field edit behaviors.
  412. // If this dependent has multiple dependees, only the logic of the first
  413. // dependency will be taken into account.
  414. if (!isset($behaviors)) {
  415. $behaviors = conditional_fields_field_behaviors('edit', $options);
  416. }
  417. // Determine if the dependee is in the form.
  418. if (empty($dependee_form_field) || (isset($dependee_form_field['#access']) && $dependee_form_field['#access'] == FALSE)) {
  419. // Apply orphan dependent behaviors.
  420. /*
  421. if (in_array(CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN, $behaviors)) {
  422. // TODO
  423. $is_triggered = TRUE;
  424. if ($is_orphan && !$is_triggered) {
  425. $form[$dependent]['#access'] = FALSE;
  426. }
  427. }
  428. */
  429. if (in_array(CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN, $behaviors)) {
  430. $dependent_form_field['#access'] = FALSE;
  431. }
  432. unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN]);
  433. unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN]);
  434. continue;
  435. }
  436. unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN]);
  437. unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN]);
  438. // Build a jQuery selector if it was not overridden by a custom value.
  439. // Note that this may be overridden later by a state handler.
  440. if (!$options['selector']) {
  441. $options['selector'] = conditional_fields_field_selector($dependee_form_field);
  442. }
  443. else {
  444. // Replace the language placeholder in the selector with current language.
  445. $options['selector'] = str_replace('%lang', $dependee_form_field['#language'], $options['selector']);
  446. }
  447. if ($options['condition'] != 'value') {
  448. // Conditions different than "value" are always evaluated against TRUE.
  449. $state = array($options['state'] => array($options['selector'] => array($options['condition'] => TRUE)));
  450. }
  451. else {
  452. // Build the values that trigger the dependency.
  453. $values = array();
  454. if ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET) {
  455. $values[$options['condition']] = $options['value_form'];
  456. }
  457. elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_REGEX) {
  458. $values[$options['condition']] = $options['value'];
  459. }
  460. elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND) {
  461. $values[$options['condition']] = count($options['values']) == 1 ? $options['values'][0] : $options['values'];
  462. }
  463. else {
  464. if ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR) {
  465. // XOR behaves like OR with added 'xor' element.
  466. $values[] = 'xor';
  467. }
  468. elseif ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT) {
  469. // NOT behaves like OR with switched state.
  470. $options['state'] = strpos($options['state'], '!') === 0 ? drupal_substr($options['state'], 1) : '!' . $options['state'];
  471. }
  472. // OR, NOT and XOR conditions are obtained with a nested array.
  473. foreach ($options['values'] as $value) {
  474. $values[] = array($options['condition'] => $value);
  475. }
  476. }
  477. $state = array($options['state'] => array($options['selector'] => $values));
  478. $dependee_form_state = isset($dependee_form_field['#field_parents'], $dependee_form_field['#field_name'], $dependee_form_field['#language']) ? field_form_get_state($dependee_form_field['#field_parents'], $dependee_form_field['#field_name'], $dependee_form_field['#language'], $form_state) : NULL;
  479. // Execute special handler for fields that need further processing.
  480. // The handler has no return value. Modify the $state parameter by
  481. // reference if needed.
  482. foreach ($state_handlers as $handler => $handler_conditions) {
  483. if (array_intersect_assoc($handler_conditions, $dependee_form_field) == $handler_conditions) {
  484. $handler($dependee_form_field, $dependee_form_state, $options, $state);
  485. }
  486. }
  487. // Add validation callback to element.
  488. _conditional_fields_element_add_property($dependent_form_field, '#element_validate', 'conditional_fields_dependent_validate', 'append');
  489. }
  490. // Add the $state into the correct logic group in $states.
  491. foreach ($state as $key => $constraints) {
  492. if (empty($states[$key][$options['grouping']])) {
  493. $states[$key][$options['grouping']] = $constraints;
  494. }
  495. else {
  496. $states[$key][$options['grouping']] = array_merge($states[$key][$options['grouping']], $constraints);
  497. }
  498. }
  499. // Build effect settings for effects with options.
  500. // TODO: add dependee key to allow different effects on the same selector.
  501. if ($options['effect'] && $options['effect'] != 'show') {
  502. $selector = conditional_fields_field_selector(drupal_array_get_nested_value($form, array($dependent_location[0])));
  503. // Convert numeric strings to numbers.
  504. foreach ($options['effect_options'] as &$effect_option) {
  505. if (is_numeric($effect_option)) {
  506. $effect_option += 0;
  507. }
  508. }
  509. $effects[$selector] = array(
  510. 'effect' => $options['effect'],
  511. 'options' => $options['effect_options'],
  512. );
  513. }
  514. // Apply reset dependent to default if untriggered behavior.
  515. if (in_array(CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED, $behaviors)) {
  516. // Add property to element so conditional_fields_dependent_validate() can
  517. // pick it up.
  518. $dependent_form_field['#conditional_fields_reset_if_untriggered'] = TRUE;
  519. unset($behaviors[CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED]);
  520. }
  521. }
  522. // Execute custom behaviors.
  523. if (!empty($behaviors)) {
  524. foreach ($behaviors as $behavior) {
  525. // Custom behaviors are callbacks.
  526. if (function_exists($$behavior)) {
  527. $$behavior('edit', $form, $form_state, $dependent, $dependencies);
  528. }
  529. }
  530. }
  531. unset($behaviors);
  532. if (empty($states)) {
  533. continue;
  534. }
  535. // Save the modified field back into the form.
  536. drupal_array_set_nested_value($form, $dependent_location, $dependent_form_field);
  537. // Map the states based on the conjunctions.
  538. $states_new = array();
  539. foreach ($states as $state_key => $value) {
  540. // As the main object is ANDed together we can add the AND items directly.
  541. if (!empty($states[$state_key]['AND'])) {
  542. $states_new[$state_key] = $states[$state_key]['AND'];
  543. }
  544. // The OR and XOR groups are moved into a sub-array that has numeric keys
  545. // so that we get a JSON array and not an object, as required by the States
  546. // API for OR and XOR groupings.
  547. if (!empty($states[$state_key]['OR'])) {
  548. $or = array();
  549. foreach ($states[$state_key]['OR'] as $constraint_key => $constraint_value) {
  550. $or[] = array($constraint_key => $constraint_value);
  551. }
  552. // '1' as a string so that we get an object (which means logic groups
  553. // are ANDed together).
  554. $states_new[$state_key]['1'] = $or;
  555. }
  556. if (!empty($states[$state_key]['XOR'])) {
  557. $xor = array('xor');
  558. foreach ($states[$state_key]['XOR'] as $constraint_key => $constraint_value) {
  559. $xor[] = array($constraint_key => $constraint_value);
  560. }
  561. // '2' as a string so that we get an object.
  562. $states_new[$state_key]['2'] = $xor;
  563. }
  564. }
  565. $states = $states_new;
  566. // Add the #states property to the dependent field.
  567. drupal_array_set_nested_value($form, array_merge($dependent_location, array('#states')), $states);
  568. $has_states = TRUE;
  569. }
  570. if (empty($has_states)) {
  571. return $form;
  572. }
  573. $form['#attached']['js'][] = drupal_get_path('module', 'conditional_fields') . '/js/conditional_fields.js';
  574. // Add effect settings to the form.
  575. if ($effects) {
  576. $form['#attached']['js'][] = array(
  577. 'data' => array(
  578. 'conditionalFields' => array(
  579. 'effects' => $effects,
  580. ),
  581. ),
  582. 'type' => 'setting',
  583. );
  584. }
  585. // Validation callback to manage dependent fields validation.
  586. $form['#validate'][] = 'conditional_fields_form_validate';
  587. // Initialize validation information every time the form is rendered to avoid
  588. // stale data after a failed submission.
  589. $form_state['conditional_fields_untriggered_dependents'] = array();
  590. return $form;
  591. }
  592. /**
  593. * Dependent field validation callback.
  594. *
  595. * If the dependencies of a dependent field are not triggered, the validation
  596. * errors that it might have thrown must be removed, together with its submitted
  597. * values. This will simulate the field not being present in the form at all.
  598. * In this field-level callback we just collect needed information and store it
  599. * in $form_state. Values and errors will be removed in a single sweep in
  600. * conditional_fields_form_validate(), which runs at the end of the validation
  601. * cycle.
  602. *
  603. * @see conditional_fields_form_validate()
  604. */
  605. function conditional_fields_dependent_validate($element, &$form_state, $form) {
  606. $dependent = $element[$element['#language']];
  607. // Check if this field's dependencies were triggered.
  608. if (conditional_fields_evaluate_dependencies($dependent, $form, $form_state)) {
  609. return;
  610. }
  611. // Mark submitted values for removal. We have to remove them after all fields
  612. // have been validated to avoid collision between dependencies.
  613. $form_state_addition['parents'] = $dependent['#array_parents'];
  614. // Optional behavior: reset the field to its default values.
  615. // Default values are always valid, so it's safe to skip validation.
  616. if (!empty($element['#conditional_fields_reset_if_untriggered'])) {
  617. $form_state_addition['reset'] = TRUE;
  618. }
  619. // Tag validation errors previously set on this field for removal in
  620. // conditional_fields_form_validate().
  621. $errors = form_get_errors();
  622. if ($errors) {
  623. $error_key = implode('][', $dependent['#parents']);
  624. foreach ($errors as $name => $error) {
  625. // An error triggered by this field might have been set on a descendant
  626. // element. This also means that so there can be multiple errors on the
  627. // same field (even though Drupal doesn't support multiple errors on the
  628. // same element).
  629. if (strpos($name, $error_key) === 0) {
  630. $field_errors[$name] = $error;
  631. }
  632. }
  633. }
  634. if (!empty($field_errors)) {
  635. $form_state_addition['errors'] = $field_errors;
  636. }
  637. $form_state['conditional_fields_untriggered_dependents'][] = $form_state_addition;
  638. }
  639. /**
  640. * Extracts submitted field values during form validation.
  641. *
  642. * @return
  643. * The requested field values parent. Actual field vales are stored under the
  644. * key $element['#field_name'].
  645. */
  646. function conditional_fields_form_field_get_values($element, $form_state) {
  647. // Fall back to #parents to support custom dependencies.
  648. $parents = isset($element['#field_parents']) ? $element['#field_parents'] : $element['#parents'];
  649. return drupal_array_get_nested_value($form_state['values'], $parents);
  650. }
  651. /**
  652. * Validation callback for any form with conditional fields.
  653. *
  654. * This validation callback is added to all forms that contain fields with
  655. * dependencies. It removes all validation errors from dependent fields whose
  656. * dependencies are not triggered, which were collected at field-level
  657. * validation in conditional_fields_dependent_validate().
  658. *
  659. * @see conditional_fields_dependent_validate()
  660. */
  661. function conditional_fields_form_validate($form, &$form_state) {
  662. if (empty($form_state['conditional_fields_untriggered_dependents'])) {
  663. return;
  664. }
  665. $untriggered_dependents_errors = array();
  666. foreach ($form_state['conditional_fields_untriggered_dependents'] as $field) {
  667. $dependent = drupal_array_get_nested_value($form, $field['parents']);
  668. $field_values_location = conditional_fields_form_field_get_values($dependent, $form_state);
  669. // If we couldn't find a location for the field's submitted values, let the
  670. // validation errors pass through to avoid security holes.
  671. if (!isset($field_values_location[$dependent['#field_name']])) {
  672. continue;
  673. }
  674. if (empty($field['reset'])) {
  675. unset($field_values_location[$dependent['#field_name']]);
  676. }
  677. else {
  678. $dependent_info = field_form_get_state($dependent['#field_parents'], $dependent['#field_name'], $dependent['#language'], $form_state);
  679. $field_values_location[$dependent['#field_name']][$dependent['#language']] = field_get_default_value($dependent_info['instance']['entity_type'], NULL, $dependent_info['field'], $dependent_info['instance'], $dependent['#language']);
  680. }
  681. // Save the changed array back in place.
  682. // Do not use form_set_value() since it assumes that the values are located at
  683. // $form_state['values'][ ... $element['#parents'] ... ], while the
  684. // documentation of hook_field_widget_form() states that field values are
  685. // $form_state['values'][ ... $element['#field_parents'] ... ].
  686. drupal_array_set_nested_value($form_state['values'], $dependent['#field_parents'], $field_values_location);
  687. if (!empty($field['errors'])) {
  688. $untriggered_dependents_errors = array_merge($untriggered_dependents_errors, $field['errors']);
  689. }
  690. }
  691. if (!empty($untriggered_dependents_errors)) {
  692. // Since Drupal provides no clean way to selectively remove error messages,
  693. // we have to store all current form errors and error messages, clear them,
  694. // filter out from our stored values the errors originating from untriggered
  695. // dependent fields, and then reinstate remaining errors and messages.
  696. $errors = array_diff_assoc((array) form_get_errors(), $untriggered_dependents_errors);
  697. form_clear_error();
  698. $error_messages = drupal_get_messages('error');
  699. $removed_messages = array_values($untriggered_dependents_errors);
  700. // Reinstate remaining errors.
  701. foreach ($errors as $name => $error) {
  702. form_set_error($name, $error);
  703. // form_set_error() calls drupal_set_message(), so we have to filter out
  704. // these from the messages to avoid duplicates.
  705. $removed_messages[] = $error;
  706. }
  707. // Reinstate remaining error messages (which, at this point, are messages that
  708. // were originated outside of the validation process).
  709. foreach (array_diff($error_messages['error'], $removed_messages) as $message) {
  710. drupal_set_message($message, 'error');
  711. }
  712. }
  713. }
  714. /**
  715. * Helper function to add a property/value pair to a render array safely without
  716. * overriding any pre-existing value.
  717. *
  718. * @param $position
  719. * 'append' if $value should be inserted at the end of the $element[$property]
  720. * array, any other value to insert it at the beginning.
  721. *
  722. */
  723. function _conditional_fields_element_add_property(&$element, $property, $value, $position = 'prepend') {
  724. // Avoid overriding default element properties that might not yet be set.
  725. if (!isset($element[$property])) {
  726. $element[$property] = isset($element['#type']) ? element_info_property($element['#type'], $property, array()) : array();
  727. }
  728. if (in_array($value, $element[$property])) {
  729. return;
  730. }
  731. switch ($position) {
  732. case 'append':
  733. $element[$property] = array_merge($element[$property], (array) $value);
  734. break;
  735. case 'prepend':
  736. default:
  737. $element[$property] = array_merge((array) $value, $element[$property]);
  738. break;
  739. }
  740. }
  741. /**
  742. * Implements hook_entity_view_alter().
  743. *
  744. * Applies entity view logic to conditional fields.
  745. */
  746. function conditional_fields_entity_view_alter(&$build, $type) {
  747. if (!(isset($build['#entity_type'], $build['#bundle']) && $dependencies = conditional_fields_load_dependencies($build['#entity_type'], $build['#bundle']))) {
  748. return;
  749. }
  750. $evaluated_dependents = array();
  751. foreach ($dependencies['dependents'] as $dependent => $dependency) {
  752. if (empty($build[$dependent]['#access'])) {
  753. continue;
  754. }
  755. foreach ($dependency as $dependency_options) {
  756. $dependee = $dependency_options['dependee'];
  757. $options = $dependency_options['options'];
  758. // We can interface with the States API only through the Value condition.
  759. if ($options['condition'] != 'value') {
  760. continue;
  761. }
  762. // Determine field view behaviors.
  763. // If this dependent has multiple dependencies, only the logic of the
  764. // first dependency will be taken into account.
  765. if (!isset($behaviors)) {
  766. $behaviors = conditional_fields_field_behaviors('view', $options);
  767. }
  768. // Manage orphan fields (dependents with no dependees).
  769. $evaluate = in_array(CONDITIONAL_FIELDS_FIELD_VIEW_EVALUATE, $behaviors);
  770. $hide_orphan = in_array(CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_ORPHAN, $behaviors);
  771. $hide_untriggered_orphan = in_array(CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_UNTRIGGERED_ORPHAN, $behaviors);
  772. $is_orphan = empty($build[$dependee]['#access']);
  773. if ($is_orphan) {
  774. // Hide the dependent. No need to evaluate the dependency.
  775. if ($hide_orphan) {
  776. $build[$dependent]['#access'] = FALSE;
  777. continue;
  778. }
  779. if ($hide_untriggered_orphan) {
  780. $evaluate = TRUE;
  781. }
  782. if ($evaluate) {
  783. // We have to look for the dependee in the entity object.
  784. // TODO: Is it possible to avoid hardcoding this?
  785. switch ($type) {
  786. case 'node':
  787. $entity_property = '#node';
  788. break;
  789. case 'user':
  790. $entity_property = '#account';
  791. break;
  792. case 'term':
  793. $entity_property = '#term';
  794. break;
  795. case 'field_collection_item':
  796. case 'profile2':
  797. default:
  798. $entity_property = '#entity';
  799. }
  800. // If we didn't find the field, there is nothing more we can do.
  801. if (!isset($build[$entity_property]->$dependee)) {
  802. continue;
  803. }
  804. $items = $build[$entity_property]->$dependee;
  805. // Values are keyed by language here, remove it.
  806. $items = array_shift($items);
  807. }
  808. }
  809. else {
  810. $items = $build[$dependee]['#items'];
  811. }
  812. if ($evaluate) {
  813. $evaluated_dependents[$dependent][$options['grouping']][] = conditional_fields_evaluate_dependency('view', $items, $options);
  814. }
  815. }
  816. if (isset($evaluated_dependents[$dependent])) {
  817. $is_triggered = conditional_fields_evaluate_grouping($evaluated_dependents[$dependent]);
  818. foreach ($behaviors as $behavior) {
  819. switch ($behavior) {
  820. case CONDITIONAL_FIELDS_FIELD_VIEW_EVALUATE:
  821. // Hide the dependent if it is not triggered.
  822. if (!$is_triggered) {
  823. $build[$dependent]['#access'] = FALSE;
  824. }
  825. break;
  826. case CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_ORPHAN:
  827. // This case was handled while looking for the field.
  828. break;
  829. case CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_UNTRIGGERED_ORPHAN:
  830. // Hide the dependent if the dependee is not viewable and the dependency is not triggered.
  831. if ($is_orphan && !$is_triggered) {
  832. $build[$dependent]['#access'] = FALSE;
  833. }
  834. break;
  835. case CONDITIONAL_FIELDS_FIELD_VIEW_HIGHLIGHT:
  836. // Show the dependent themed like an error message if it is not triggered.
  837. if (!$is_triggered) {
  838. $build[$dependent]['#prefix'] = isset($build[$dependent]['#prefix']) ? '<div class="messages error">' . $build[$dependent]['#prefix'] : '<div class="messages error">';
  839. $build[$dependent]['#suffix'] = isset($build[$dependent]['#suffix']) ? $build[$dependent]['#suffix'] . '</div>' : '</div>';
  840. }
  841. break;
  842. case CONDITIONAL_FIELDS_FIELD_VIEW_DESCRIBE:
  843. // Show a textual description of the dependency under the dependent field.
  844. if ($build[$dependent]['#access']) {
  845. $dependee_title = isset($build[$dependee]['#title']) ? $build[$dependee]['#title'] : $dependee;
  846. $dependent_title = isset($build[$dependent]['#title']) ? $build[$dependent]['#title'] : $dependent;
  847. $description = conditional_fields_dependency_description($dependee_title, $dependent_title, $options);
  848. if (isset($build[$dependent]['#suffix'])) {
  849. $description = $build[$dependent]['#suffix'] . $description;
  850. }
  851. $build[$dependent]['#suffix'] = $description;
  852. }
  853. break;
  854. default:
  855. // Custom behaviors are callbacks.
  856. $$behavior('view', $dependee, $dependent, $is_triggered, $dependencies, $build, $type);
  857. break;
  858. }
  859. if (empty($build[$dependent]['#access'])) {
  860. break;
  861. }
  862. }
  863. }
  864. unset($behaviors);
  865. }
  866. }
  867. /**
  868. * Evaluates an array with 'AND', 'OR' and 'XOR' groupings,
  869. * each containing a list of boolean values.
  870. */
  871. function conditional_fields_evaluate_grouping($groups) {
  872. $or = $and = $xor = TRUE;
  873. if (!empty($groups['OR'])) {
  874. $or = in_array(TRUE, $groups['OR']);
  875. }
  876. if (!empty($groups['AND'])) {
  877. $and = !in_array(FALSE, $groups['AND']);
  878. }
  879. if (!empty($groups['XOR'])) {
  880. $xor = array_sum($groups['XOR']) == 1;
  881. }
  882. return $or && $and && $xor;
  883. }
  884. /**
  885. * Evaluate a set of dependencies for a dependent field.
  886. *
  887. * @param $dependent
  888. * The field form element in the current language.
  889. */
  890. function conditional_fields_evaluate_dependencies($dependent, $form, $form_state) {
  891. $dependencies = $form['#conditional_fields'][$dependent['#field_name']]['dependees'];
  892. $evaluated_dependees = array();
  893. foreach ($dependencies as $dependency_id => $dependency) {
  894. // Extract field values from submitted values.
  895. $dependee = $dependency['dependee'];
  896. $dependee_parents = $form['#conditional_fields'][$dependee]['parents'];
  897. // We have the parents of the field, but depending on the entity type and
  898. // the widget type, they may include additional elements that are actually
  899. // part of the value. So we find the depth of the field inside the form
  900. // structure and use the parents only up to that depth.
  901. $dependee_parents_keys = array_flip($dependee_parents);
  902. $dependee_parent = drupal_array_get_nested_value($form, array_slice($dependee_parents, 0, $dependee_parents_keys[$dependee]));
  903. $values = conditional_fields_form_field_get_values($dependee_parent[$dependee], $form_state);
  904. // Remove the language key.
  905. if (isset($dependee_parent[$dependee]['#language'], $values[$dependee_parent[$dependee]['#language']])) {
  906. $values = $values[$dependee_parent[$dependee]['#language']];
  907. }
  908. $evaluated_dependees[$dependent['#field_name']][$dependency['options']['grouping']][] = conditional_fields_evaluate_dependency('edit', $values, $dependency['options']);
  909. }
  910. return conditional_fields_evaluate_grouping($evaluated_dependees[$dependent['#field_name']]);
  911. }
  912. /**
  913. * Evaluate if a dependency meets the requirements to be triggered.
  914. *
  915. * @param $context
  916. * 'edit' if $values are extracted from $form_state or 'view' if
  917. * $values are extracted from an entity.
  918. */
  919. function conditional_fields_evaluate_dependency($context, $values, $options) {
  920. if ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET) {
  921. $dependency_values = $context == 'view' ? $options['value'] : $options['value_form'];
  922. // Simple case: both values are strings or integers. Should never happen in
  923. // view context, but does no harm to check anyway.
  924. if (!is_array($values)) {
  925. // Options elements consider "_none" value same as empty.
  926. $values = $values === '_none' ? '' : $values;
  927. if (!is_array($dependency_values)) {
  928. // Some widgets store integers, but values saved in $dependency_values
  929. // are always strings. Convert them to integers because we want to do a
  930. // strict equality check to differentiate empty strings from '0'.
  931. if (is_int($values) && is_numeric($dependency_values)) {
  932. $dependency_values = (int) $dependency_values;
  933. }
  934. return $dependency_values === $values;
  935. }
  936. // If $values is a string and $dependency_values an array, convert $values
  937. // to the standard field array form format. This covers cases like single
  938. // value textfields.
  939. $values = array(array('value' => $values));
  940. }
  941. // If we are in form context, we are almost done.
  942. if ($context == 'edit') {
  943. // If $dependency_values is not an array, we can only assume that it
  944. // should map to the first key of the first value of $values.
  945. if (!is_array($dependency_values)) {
  946. $key = current(array_keys((array) current($values)));
  947. $dependency_values = array(array($key => $dependency_values));
  948. }
  949. // Compare arrays recursively ignoring keys, since multiple select widgets
  950. // values have numeric keys in form format and string keys in storage
  951. // format.
  952. return array_values($dependency_values) == array_values($values);
  953. }
  954. // $values, when viewing fields, may contain all sort of additional
  955. // information, so filter out from $values the keys that are not present in
  956. // $dependency_values.
  957. // Values here are alway keyed by delta (regardless of multiple value
  958. // settings).
  959. foreach ($values as $delta => &$value) {
  960. if (isset($dependency_values[$delta])) {
  961. $value = array_intersect_key($value, $dependency_values[$delta]);
  962. foreach ($value as $key => &$element_value) {
  963. if (isset($dependency_values[$delta][$key]) && is_int($dependency_values[$delta][$key]) && is_numeric($element_value)) {
  964. $element_value = (int) $element_value;
  965. }
  966. }
  967. }
  968. }
  969. // Compare values.
  970. foreach ($dependency_values as $delta => $dependency_value) {
  971. if (!isset($values[$delta])) {
  972. return FALSE;
  973. }
  974. foreach ($dependency_value as $key => $dependency_element_value) {
  975. // Ignore keys set in the field and not in the dependency.
  976. if (isset($values[$delta][$key]) && $values[$delta][$key] !== $dependency_element_value) {
  977. return FALSE;
  978. }
  979. }
  980. }
  981. return TRUE;
  982. }
  983. // Flatten array of values.
  984. $reference_values = array();
  985. foreach ((array) $values as $value) {
  986. // TODO: support multiple values.
  987. $reference_values[] = is_array($value) ? array_shift($value) : $value;
  988. }
  989. // Regular expression method.
  990. if ($options['values_set'] == CONDITIONAL_FIELDS_DEPENDENCY_VALUES_REGEX) {
  991. foreach ($reference_values as $reference_value) {
  992. if (!preg_match('/' . $options['value']['RegExp'] . '/', $reference_value)) {
  993. return FALSE;
  994. }
  995. }
  996. return TRUE;
  997. }
  998. switch ($options['values_set']) {
  999. case CONDITIONAL_FIELDS_DEPENDENCY_VALUES_AND:
  1000. $diff = array_diff($options['values'], $reference_values);
  1001. return empty($diff);
  1002. case CONDITIONAL_FIELDS_DEPENDENCY_VALUES_OR:
  1003. $intersect = array_intersect($options['values'], $reference_values);
  1004. return !empty($intersect);
  1005. case CONDITIONAL_FIELDS_DEPENDENCY_VALUES_XOR:
  1006. $intersect = array_intersect($options['values'], $reference_values);
  1007. return count($intersect) == 1;
  1008. case CONDITIONAL_FIELDS_DEPENDENCY_VALUES_NOT:
  1009. $intersect = array_intersect($options['values'], $reference_values);
  1010. return empty($intersect);
  1011. }
  1012. }
  1013. /**
  1014. * Determine which dependency behaviors should be used in forms and content
  1015. * display, depending on dependency options and user roles.
  1016. *
  1017. * @param $op
  1018. * 'view' or 'edit'.
  1019. * @param $options
  1020. * Dependency options.
  1021. *
  1022. * @return
  1023. * An array of behaviors.
  1024. *
  1025. */
  1026. function conditional_fields_field_behaviors($op, $options) {
  1027. global $user;
  1028. if ($options['element_' . $op . '_per_role']) {
  1029. foreach ($options['element_' . $op . '_roles'] as $rid => $role_behaviors) {
  1030. if (isset($user->roles[$rid])) {
  1031. $behaviors = $role_behaviors;
  1032. break;
  1033. }
  1034. }
  1035. }
  1036. else {
  1037. $behaviors = $options['element_' . $op];
  1038. }
  1039. // Filter out inactive behaviors.
  1040. $behaviors = array_filter($behaviors);
  1041. return $behaviors;
  1042. }
  1043. /**
  1044. * Builds a jQuery selector from the name attribute of a field.
  1045. * TODO: support custom selectors with %lang and %key placeholders.
  1046. */
  1047. function conditional_fields_field_selector($field) {
  1048. if (isset($field['#attributes']['name'])) {
  1049. return '[name="' . $field['#attributes']['name'] . '"]';
  1050. }
  1051. if (isset($field['#name'])) {
  1052. return '[name="' . $field['#name'] . '"]';
  1053. }
  1054. // Try with id if name is not found.
  1055. if (isset($field['#attributes']['id'])) {
  1056. return '#' . $field['#attributes']['id'];
  1057. }
  1058. if (isset($field['#id'])) {
  1059. return '#' . $field['#id'];
  1060. }
  1061. return FALSE;
  1062. }
  1063. /**
  1064. * Provides default options for a dependency.
  1065. *
  1066. * For an explanation of available options,
  1067. * @see conditional_fields_field_attach_dependency()
  1068. */
  1069. function conditional_fields_dependency_default_options() {
  1070. return array(
  1071. 'state' => 'visible',
  1072. 'condition' => 'value',
  1073. 'grouping' => 'AND',
  1074. 'values_set' => CONDITIONAL_FIELDS_DEPENDENCY_VALUES_WIDGET,
  1075. 'value' => array(),
  1076. 'values' => array(),
  1077. 'value_form' => array(),
  1078. 'effect' => 'show',
  1079. 'effect_options' => array(),
  1080. 'element_view' => array(
  1081. CONDITIONAL_FIELDS_FIELD_VIEW_EVALUATE => CONDITIONAL_FIELDS_FIELD_VIEW_EVALUATE,
  1082. CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_ORPHAN => CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_ORPHAN,
  1083. CONDITIONAL_FIELDS_FIELD_VIEW_HIDE_UNTRIGGERED_ORPHAN => 0,
  1084. CONDITIONAL_FIELDS_FIELD_VIEW_HIGHLIGHT => 0,
  1085. CONDITIONAL_FIELDS_FIELD_VIEW_DESCRIBE => 0,
  1086. ),
  1087. 'element_view_per_role' => 0,
  1088. 'element_view_roles' => array(),
  1089. 'element_edit' => array(
  1090. CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN => CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_ORPHAN,
  1091. CONDITIONAL_FIELDS_FIELD_EDIT_HIDE_UNTRIGGERED_ORPHAN => 0,
  1092. CONDITIONAL_FIELDS_FIELD_EDIT_RESET_UNTRIGGERED => 0,
  1093. ),
  1094. 'element_edit_per_role' => 0,
  1095. 'element_edit_roles' => array(),
  1096. 'selector' => '',
  1097. );
  1098. }
  1099. /**
  1100. * Loads all dependencies from the database.
  1101. *
  1102. * The result can be filtered by providing an entity type and a bundle name.
  1103. */
  1104. function conditional_fields_load_dependencies($entity_type = NULL, $bundle = NULL) {
  1105. // Use the advanced drupal_static() pattern.
  1106. static $dependencies;
  1107. if (!isset($dependencies)) {
  1108. $dependencies = &drupal_static(__FUNCTION__);
  1109. }
  1110. if (!$dependencies) {
  1111. $dependencies = array();
  1112. }
  1113. if (!isset($dependencies[$entity_type][$bundle])) {
  1114. if (!empty($entity_type) && !empty($bundle)) {
  1115. $dependencies[$entity_type][$bundle] = array();
  1116. }
  1117. $default_options = conditional_fields_dependency_default_options();
  1118. $select = db_select('conditional_fields', 'cf')
  1119. ->fields('cf', array('id', 'options'))
  1120. ->orderBy('cf.dependent');
  1121. $fci_depende = $select->join('field_config_instance', 'fci_dependee', 'cf.dependee = fci_dependee.id');
  1122. $fci_dependent = $select->join('field_config_instance', 'fci_dependent', 'cf.dependent = fci_dependent.id');
  1123. $select->addField($fci_depende, 'field_name', 'dependee');
  1124. $select->addField($fci_dependent, 'field_name', 'dependent');
  1125. $select->addField($fci_depende, 'entity_type');
  1126. $select->addField($fci_depende, 'bundle');
  1127. if ($entity_type) {
  1128. $select->condition(
  1129. db_and()
  1130. ->condition('fci_dependee.entity_type', $entity_type)
  1131. ->condition('fci_dependent.entity_type', $entity_type)
  1132. );
  1133. }
  1134. if ($bundle) {
  1135. $select->condition(
  1136. db_and()
  1137. ->condition('fci_dependee.bundle', $bundle)
  1138. ->condition('fci_dependent.bundle', $bundle)
  1139. );
  1140. }
  1141. $result = $select->execute();
  1142. foreach ($result as $dependency) {
  1143. $result_entity_type = $entity_type ? $entity_type : $dependency->entity_type;
  1144. $result_bundle = $bundle ? $bundle : $dependency->bundle;
  1145. $options = unserialize($dependency->options);
  1146. $options += $default_options

Large files files are truncated, but you can click here to view the full file