flexifield.module

Tracking 6.x-1.x branch
  1. drupal
    1. 6 contributions/flexifield/flexifield.module

Defines a field type where each field item can be a combination of fields.

Functions & methods

NameDescription
flexifield_content_is_emptyImplementation of hook_content_is_empty().
flexifield_default_widget_processProcess a widget element for an individual field item.
flexifield_elementsImplementation of FAPI hook_elements().
flexifield_fieldImplementation of hook_field().
flexifield_field_formatter_infoImplementation of hook_field_formatter_info().
flexifield_field_infoImplementation of hook_field_info().
flexifield_field_settingsImplementation of hook_field_settings().
flexifield_get_element_propertyflexifield_get_element_property
flexifield_items_invokeflexifield_items_invoke()
flexifield_item_invokeflexifield_item_invoke()
flexifield_menuImplementation of hook_menu().
flexifield_themeImplementation of hook_theme().
flexifield_views_apiImplementation of hook_views_api().
flexifield_widgetImplementation of hook_widget().
flexifield_widget_infoImplementation of hook_widget_info().
flexifield_widget_settingsImplementation of hook_widget_settings().
template_preprocess_flexifield_item_contentPreprocess function for item content
theme_flexifield_formatter_defaultTheme function for default flexifield formatter.
_flexifield_item_invoke_cck_flexifield_item_invoke_cck()
_flexifield_item_invoke_flexifield_flexifield_item_invoke_flexifield()

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * Defines a field type where each field item can be a combination of fields.
  5. */
  6. /**
  7. * Implementation of hook_theme().
  8. */
  9. function flexifield_theme($aExisting) {
  10. $aResult = array(
  11. 'flexifield_default_widget' => array(
  12. 'file' => 'flexifield-widget.inc',
  13. 'arguments' => array('element' => NULL),
  14. ),
  15. 'flexifield_fieldset' => array(
  16. 'file' => 'flexifield-widget.inc',
  17. 'arguments' => array('element' => NULL),
  18. ),
  19. 'flexifield_formatter_default' => array(
  20. 'arguments' => array('element' => NULL),
  21. ),
  22. 'flexifield_multiple_values' => array(
  23. 'file' => 'flexifield-cck-overrides.inc',
  24. 'arguments' => array('element' => NULL),
  25. ),
  26. );
  27. // If element_themehook module is enabled, allow extra theming control
  28. // of each item's type.
  29. if (isset($aExisting['element'])) {
  30. $aResult['flexifield_item_content'] = array(
  31. 'pattern' => 'flexifield_item_content__',
  32. 'template' => 'flexifield-item-content',
  33. 'original hook' => 'element',
  34. 'arguments' => $aExisting['element']['arguments'],
  35. );
  36. }
  37. return $aResult;
  38. }
  39. /**
  40. * Implementation of hook_menu().
  41. */
  42. function flexifield_menu() {
  43. $aItems = array();
  44. $aItems['flexifield/ahah/changetype'] = array(
  45. 'file' => 'flexifield-widget.inc',
  46. 'page callback' => 'flexifield_ahah_changetype',
  47. 'access arguments' => array('access content'),
  48. 'type' => MENU_CALLBACK,
  49. );
  50. $aItems['flexifield/ahah/addmore'] = array(
  51. 'file' => 'flexifield-widget.inc',
  52. 'page callback' => 'flexifield_ahah_addmore',
  53. 'access arguments' => array('access content'),
  54. 'type' => MENU_CALLBACK,
  55. );
  56. $aItems['flexifield/test'] = array(
  57. 'file' => 'flexifield-test.inc',
  58. 'page callback' => 'flexifield_test',
  59. 'access arguments' => array('access devel information'),
  60. 'type' => MENU_CALLBACK,
  61. );
  62. return $aItems;
  63. }
  64. /**
  65. * Implementation of hook_views_api().
  66. */
  67. function flexifield_views_api() {
  68. return array(
  69. 'api' => 2.0,
  70. 'path' => drupal_get_path('module', 'flexifield') . '/views',
  71. );
  72. }
  73. /**
  74. * Implementation of hook_field_info().
  75. */
  76. function flexifield_field_info() {
  77. return array(
  78. 'flexifield' => array(
  79. 'label' => t('Flexi-Field'),
  80. 'description' => t('Store one or more field combinations as items within a single field.'),
  81. ),
  82. );
  83. }
  84. /**
  85. * Implementation of hook_field_settings().
  86. */
  87. function flexifield_field_settings($sOperation, $aFieldSettings) {
  88. switch ($sOperation) {
  89. // The settings form for the field
  90. case 'form':
  91. $aSettingsForm = array();
  92. $aSettingsForm['item_types'] = array(
  93. '#type' => 'checkboxes',
  94. '#title' => t('Content types that can be used as field items'),
  95. '#multiple' => TRUE,
  96. '#default_value' => is_array($aFieldSettings['item_types']) ? $aFieldSettings['item_types'] : array(),
  97. '#options' => node_get_types('names'),
  98. );
  99. return $aSettingsForm;
  100. break;
  101. // The elements from the settings form to save in the database
  102. case 'save':
  103. return array('item_types');
  104. break;
  105. // The database columns to create for storing field item data
  106. case 'database columns':
  107. $aColumns = array();
  108. $aColumns['type'] = array('type' => 'varchar', 'length' => 32, 'not null' => FALSE, 'sortable' => TRUE, 'views' => FALSE);
  109. $aColumns['value'] = array('type' => 'text', 'size' => 'big', 'not null' => FALSE, 'sortable' => FALSE, 'serialize' => TRUE, 'views' => FALSE);
  110. $aColumns['item_id'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'views' => TRUE);
  111. return $aColumns;
  112. break;
  113. // Views Integration
  114. case 'views data':
  115. $aViewsData = content_views_field_views_data($aFieldSettings);
  116. $aDbInfo = content_database_info($aFieldSettings);
  117. $sTableAlias = content_views_tablename($aFieldSettings);
  118. // Relationship: add a relationship for child fields
  119. $aViewsData[$sTableAlias][$aFieldSettings['field_name'] .'_item_id']['relationship'] = array(
  120. 'base' => 'flexifield_items',
  121. 'field' => $aDbInfo['columns']['item_id']['column'],
  122. 'handler' => 'content_handler_relationship',
  123. 'label' => t($aFieldSettings['widget']['label']),
  124. 'content_field_name' => $aFieldSettings['field_name'],
  125. );
  126. return $aViewsData;
  127. break;
  128. }
  129. }
  130. /**
  131. * Implementation of hook_field().
  132. */
  133. function flexifield_field($sOperation, &$oNode, $aFieldSettings, &$aItems, $bTeaser, $bPage) {
  134. switch ($sOperation) {
  135. // Additional loading code, executed after content.module's content_storage() function
  136. case 'load':
  137. if (is_array($aItems)) {
  138. foreach ($aItems as &$aItem) {
  139. // This module is currently transitioning from storing child field data
  140. // as a serialized value in the flexifield's 'value' column to delegating
  141. // the storage to the child fields themselves, using a relationship between
  142. // the "vid" column of the child field and the "item_id" column of the
  143. // flexifield item. Eventually, we'll get rid of the serialized
  144. // storage entirely, but that will require implementing an appropriate
  145. // hook_update_N() function, so that people who used older versions of this
  146. // module don't lose their data. For now, we check to see if we should use
  147. // the serialized storage or the child field storage.
  148. if (!empty($aItem['item_id'])) {
  149. $aItem['value'] = array();
  150. flexifield_item_invoke($sOperation, $aItem, $oNode);
  151. }
  152. elseif (is_string($aItem['value'])) {
  153. // Content_storage does serialize each field item's columns (if the
  154. // 'serialize' key for that column is set) upon writing to the db,
  155. // but does not unserialize when reading from the db. Hopefully,
  156. // this will be fixed in the future, but for now, we have to
  157. // explicitly unserialize.
  158. $aItem['value'] = unserialize($aItem['value']);
  159. }
  160. }
  161. return (array($aFieldSettings['field_name'] => $aItems));
  162. }
  163. break;
  164. // Storage operations that simply cascade to the child fields.
  165. case 'insert':
  166. case 'delete':
  167. case 'delete revision':
  168. flexifield_items_invoke($sOperation, $aItems, $oNode);
  169. break;
  170. // 'update' operation is trickier
  171. // @todo: find a way to know when there's no change, and in those
  172. // cases, don't do a insert/delete.
  173. case 'update':
  174. $oOldNode = node_load($oNode->nid, $oNode->vid);
  175. $sFieldName = $aFieldSettings['field_name'];
  176. $aOldItems = $oOldNode->$sFieldName;
  177. // Insert new before deleting old, so that filefield doesn't delete
  178. // files that only temporarily reach a reference count of 0.
  179. flexifield_items_invoke('insert', $aItems, $oNode);
  180. flexifield_items_invoke('delete revision', $aOldItems, $oOldNode);
  181. break;
  182. // Validate user-submitted data
  183. case 'validate':
  184. flexifield_items_invoke($sOperation, $aItems, $oNode);
  185. break;
  186. // Code to run before the field is saved to the database.
  187. case 'presave':
  188. // Let each field within each item presave itself.
  189. flexifield_items_invoke($sOperation, $aItems, $oNode);
  190. // Make sure the items are properly sorted. We can't rely
  191. // on content.module to do this, since our widget takes over
  192. // multiple values processing.
  193. // TODO: This seems like an improper mixing of field and widget
  194. // code. We should probably relegate this to the widget's validate
  195. // function.
  196. $aItems = _content_sort_items($aFieldSettings, $aItems);
  197. break;
  198. // Sanitization code
  199. case 'sanitize':
  200. // @todo: CCK doesn't invoke the "load" operation on fields loaded into
  201. // a view. Is this a bug or a feature? If a bug, is it with CCK or with Views?
  202. // What's the best way to solve it? In the meantime, this hack takes care of it.
  203. if (!isset($aItems[0]['value']) || !is_array($aItems[0]['value'])) {
  204. flexifield_field('load', $oNode, $aFieldSettings, $aItems, $bTeaser, $bPage);
  205. }
  206. break;
  207. }
  208. }
  209. /**
  210. * flexifield_items_invoke()
  211. *
  212. * Wrapper function to call flexifield_item_invoke() on all items within the field.
  213. */
  214. function flexifield_items_invoke($sOperation, &$aItems, $oNode, $bTeaser = NULL, $bPage = NULL) {
  215. foreach ($aItems as &$aItem) {
  216. flexifield_item_invoke($sOperation, $aItem, $oNode, $bTeaser, $bPage);
  217. }
  218. }
  219. /**
  220. * flexifield_item_invoke()
  221. *
  222. * Invoke a field operation on a flexifield item's child fields. This consists of three parts:
  223. * 1. An implementation specific to the needs of the flexifield container.
  224. * 2. An implementation that allows the child fields to respond as they normally do within CCK.
  225. * 3. Allowing other modules to alter the result of the above two steps.
  226. */
  227. function flexifield_item_invoke($sOperation, &$aItem, $oNode, $bTeaser = NULL, $bPage = NULL) {
  228. if ($sOperation === 'load') {
  229. _flexifield_item_invoke_flexifield($sOperation, $aItem, $oNode, $bTeaser, $bPage);
  230. _flexifield_item_invoke_cck($sOperation, $aItem, $oNode, $bTeaser, $bPage);
  231. }
  232. else {
  233. _flexifield_item_invoke_cck($sOperation, $aItem, $oNode, $bTeaser, $bPage);
  234. _flexifield_item_invoke_flexifield($sOperation, $aItem, $oNode, $bTeaser, $bPage);
  235. }
  236. drupal_alter('flexifield_item', $aItem, $sOperation, $oNode, $bTeaser, $bPage);
  237. }
  238. /**
  239. * _flexifield_item_invoke_cck()
  240. *
  241. * Operate on a flexifield item by letting the child fields behave as though they were in a node.
  242. */
  243. function _flexifield_item_invoke_cck($sOperation, &$aItem, $oNode, $bTeaser = NULL, $bPage = NULL) {
  244. // Sometimes, the "add another item" item gets passed into here, and we don't want
  245. // to do anything with that.
  246. if (!is_array($aItem) || empty($aItem['type'])) {
  247. return;
  248. }
  249. // For inserts, we need a new item_id for the item. See the comment a few lines
  250. // below, before we call the content_<op> function.
  251. if ($sOperation === 'insert') {
  252. $aNodeRevision = array(
  253. 'nid' => $oNode->nid,
  254. 'uid' => $oNode->uid,
  255. 'title' => '',
  256. 'body' => '',
  257. 'teaser' => '',
  258. 'log' => '',
  259. );
  260. $oNodeRevision = (object) $aNodeRevision;
  261. drupal_write_record('node_revisions', $oNodeRevision);
  262. $aItem['item_id'] = $oNodeRevision->vid;
  263. db_query("DELETE FROM {node_revisions} WHERE nid = %d AND vid = %d", $oNodeRevision->nid, $oNodeRevision->vid);
  264. }
  265. // There might be legacy data from before we added a item_id column to items.
  266. // To be safe, we do not want to call 'delete revision' on those items.
  267. if ($sOperation === 'delete revision' && empty($aItem['item_id'])) {
  268. return;
  269. }
  270. // Leverage the content_<op> function from content.module. For the most part,
  271. // this lets the child fields do what they need to do without even being aware
  272. // that they are in a flexifield item. For this to work, we need to create a pseudo-node,
  273. // which does not reflect any actual node. Instead, it has a nid that's the same as
  274. // the node that contains the top-level flexifield, a type that matches the type of
  275. // the flexifield item, and a vid that doesn't exist in the node_revisions table,
  276. // but matches the parent flexifield item's item_id. This is a hack that enables
  277. // the heirarchy to work without requiring changes to the CCK storage tables.
  278. $f = 'content_' . str_replace(' ', '_', $sOperation);
  279. if (function_exists($f)) {
  280. $aPseudoNode = array(
  281. 'nid' => $oNode->nid,
  282. 'vid' => isset($aItem['item_id']) ? $aItem['item_id'] : NULL,
  283. 'type' => $aItem['type'],
  284. 'build_mode' => isset($aItem['build_mode']) ? $aItem['build_mode'] : (
  285. isset($oNode->build_mode) ? $oNode->build_mode : NODE_BUILD_NORMAL
  286. ),
  287. );
  288. if (is_array($aItem['value'])) {
  289. $aPseudoNode = array_merge($aPseudoNode, $aItem['value']);
  290. }
  291. $oPseudoNode = (object) $aPseudoNode;
  292. // The view operation needs a content array to populate.
  293. if ($sOperation === 'view') {
  294. $oPseudoNode->content = array();
  295. }
  296. // Call the appropriate function from content.module.
  297. $f($oPseudoNode, $bTeaser, $bPage);
  298. // It's possible that the operation changed the field data in the pseudo-node.
  299. // We want to have that change reflected in $aItem.
  300. $aNewValue = array();
  301. foreach ($oPseudoNode as $sFieldName => $aFieldItems) {
  302. if (substr($sFieldName, 0, 6) == 'field_') {
  303. $aNewValue[$sFieldName] = $aFieldItems;
  304. }
  305. }
  306. if ($sOperation === 'view') {
  307. $aNewValue['content'] = $oPseudoNode->content;
  308. }
  309. $aItem['value'] = $aNewValue;
  310. }
  311. }
  312. /**
  313. * _flexifield_item_invoke_flexifield()
  314. *
  315. * Adjust the item after it went through _flexifield_item_invoke_cck() to take into
  316. * account special needs of flexifield.
  317. *
  318. * For the "load" operation, and only for that operation, this function runs before
  319. * _flexifield_item_invoke_cck().
  320. */
  321. function _flexifield_item_invoke_flexifield($sOperation, &$aItem, $oNode, $bTeaser = NULL, $bPage = NULL) {
  322. switch ($sOperation) {
  323. case 'insert':
  324. if (!empty($aItem['item_id'])) {
  325. $oRecord = (object) array('item_id' => $aItem['item_id']);
  326. drupal_write_record('flexifield_items', $oRecord);
  327. }
  328. // Fields within a flexifield may end up invoking a premature node_load()
  329. // causing premature data to be cached. An example is filefield.module.
  330. // See http://drupal.org/node/583852.
  331. if ($oNode->nid) {
  332. cache_clear_all('content:'. $oNode->nid .':', content_cache_tablename(), TRUE);
  333. }
  334. break;
  335. case 'delete':
  336. case 'delete revision':
  337. if (!empty($aItem['item_id'])) {
  338. db_query("DELETE FROM {flexifield_items} WHERE item_id = %d", $aItem['item_id']);
  339. }
  340. break;
  341. }
  342. }
  343. /**
  344. * Implementation of hook_content_is_empty().
  345. */
  346. function flexifield_content_is_empty($aItem, $aFieldSettings) {
  347. // Note: There seems to be a bug with content.module where if the first field item is empty,
  348. // CCK doesn't remove it from the db. This needs more investigation, but is not a bug with
  349. // this module.
  350. // If no content type is specified, or if it's not a valid type, treat the item as empty.
  351. $sItemType = $aItem['type'];
  352. if (!$sItemType) {
  353. return TRUE;
  354. }
  355. $aContentTypeInfo = content_types($sItemType);
  356. if (!$aContentTypeInfo) {
  357. return TRUE;
  358. }
  359. // If the content type has no fields, treat the item as not empty.
  360. if (!count($aContentTypeInfo['fields'])) {
  361. return FALSE;
  362. }
  363. // Easy check for if the item value is literally empty.
  364. $aValue = $aItem['value'];
  365. if (!is_array($aValue) || empty($aValue)) {
  366. return TRUE;
  367. }
  368. // If the item value isn't literally empty, check to see if any field is not empty by its own standards
  369. // of what that means.
  370. $bEmpty = TRUE;
  371. foreach ($aValue as $sChildFieldName => $aChildFieldItems) {
  372. $aChildFieldSettings = $aContentTypeInfo['fields'][$sChildFieldName];
  373. if (is_array($aChildFieldSettings) && is_array($aChildFieldItems)) {
  374. foreach ($aChildFieldItems as $aChildFieldItem) {
  375. if (is_array($aChildFieldItem) && !module_invoke($aChildFieldSettings['module'], 'content_is_empty', $aChildFieldItem, $aChildFieldSettings)) {
  376. $bEmpty = FALSE;
  377. break;
  378. }
  379. }
  380. }
  381. if (!$bEmpty) {
  382. break;
  383. }
  384. }
  385. return $bEmpty;
  386. }
  387. /**
  388. * Implementation of hook_field_formatter_info().
  389. */
  390. function flexifield_field_formatter_info() {
  391. return array(
  392. 'default' => array(
  393. 'label' => t('Default'),
  394. 'field types' => array('flexifield'),
  395. 'multiple values' => CONTENT_HANDLE_CORE,
  396. ),
  397. );
  398. }
  399. /**
  400. * Theme function for default flexifield formatter.
  401. */
  402. function theme_flexifield_formatter_default($aElement) {
  403. // Invoke 'view' on the item, and let the item and content element created by
  404. // the view operation have access to the node.
  405. $aItem = $aElement['#item'];
  406. if (!isset($aItem['#node'])) {
  407. $aItem['#node'] = $aElement['#node'];
  408. }
  409. flexifield_item_invoke('view', $aItem, $aElement['#node']);
  410. $aContentElement = $aItem['value']['content'];
  411. if (!isset($aContentElement['#node'])) {
  412. $aContentElement['#node'] = $aItem['#node'];
  413. }
  414. // Allow extra theming control. If the flexifield_item_content theme hook isn't
  415. // defined (because the element_themehook module isn't enabled), then Drupal's
  416. // default structured content renderer gets used.
  417. if (!isset($aContentElement['#theme'])) {
  418. $aContentElement['#flexifield_item'] = $aItem;
  419. $aContentElement['#theme'] = array('flexifield_item_content__' . $aItem['type'], 'flexifield_item_content');
  420. }
  421. return drupal_render($aContentElement);
  422. }
  423. /**
  424. * Implementation of hook_widget_info().
  425. */
  426. function flexifield_widget_info() {
  427. return array(
  428. 'flexifield_default_widget' => array(
  429. 'label' => t('Default Widget for Flexi-Field'),
  430. 'field types' => array('flexifield'),
  431. 'multiple values' => CONTENT_HANDLE_MODULE,
  432. ),
  433. );
  434. }
  435. /**
  436. * Implementation of FAPI hook_elements().
  437. */
  438. function flexifield_elements() {
  439. return array(
  440. 'flexifield_default_widget' => array(
  441. '#input' => TRUE,
  442. '#columns' => array('type', 'value'),
  443. '#delta' => 0,
  444. '#process' => array('flexifield_default_widget_process'),
  445. ),
  446. 'flexifield_fieldset' => array(
  447. '#input' => TRUE,
  448. '#process' => array('flexifield_fieldset_process'),
  449. ),
  450. );
  451. }
  452. /**
  453. * Implementation of hook_widget_settings().
  454. */
  455. function flexifield_widget_settings($sOperation, $aWidgetSettings) {
  456. switch ($sOperation) {
  457. // The settings form for the widget
  458. case 'form':
  459. $aSettingsForm = array();
  460. $aSettingsForm['disable_tabledrag'] = array(
  461. '#type' => 'checkbox',
  462. '#title' => t('Disable drag-and-drop for this field'),
  463. '#default_value' => isset($aWidgetSettings['disable_tabledrag']) ? $aWidgetSettings['disable_tabledrag'] : FALSE,
  464. '#description' => t('By default, if this field has its <em>Number of values</em> set to more than 1, the editing form will allow you to use drag-and-drop for ordering the items. However, in cases where this field uses item types with child fields that use drag-and-drop, then due to a bug with nested drag-and-drop behavior, you should disable drag-and-drop for this field.'),
  465. );
  466. /* This is still experimental.
  467. $aSettingsForm['disable_tabledrag_children'] = array(
  468. '#type' => 'checkbox',
  469. '#title' => t('Disable drag-and-drop for child fields'),
  470. '#default_value' => isset($aWidgetSettings['disable_tabledrag_children']) ? $aWidgetSettings['disable_tabledrag_children'] : FALSE,
  471. '#description' => t('If you do not want to disable drag-and-drop on this field, but are using item types with child fields that normally use drag-and-drop, you should select this option in order to get around the bug with nested drag-and-drop behavior.'),
  472. );
  473. */
  474. return $aSettingsForm;
  475. break;
  476. // Validation for the widget settings form
  477. case 'validate':
  478. break;
  479. // The elements from the widget settings form to save in the database
  480. case 'save':
  481. return array('disable_tabledrag', 'disable_tabledrag_children');
  482. break;
  483. }
  484. }
  485. /**
  486. * Implementation of hook_widget().
  487. */
  488. function flexifield_widget(&$form, &$form_state, $field, $items, $delta = NULL) {
  489. static $bReturnSingleItem = FALSE;
  490. // When called for a single item's widget.
  491. if ($bReturnSingleItem) {
  492. $element = array(
  493. '#type' => $field['widget']['type'],
  494. '#default_value' => isset($items[$delta]) ? $items[$delta] : '',
  495. );
  496. }
  497. // When called for the container of multiple widgets. This happens because
  498. // in hook_widget_info(), we specfy that we want to handle multiple values
  499. // ourselves instead of using content.module's default. We do this in order
  500. // to make adjustments to what content_multiple_value_form() does.
  501. else {
  502. $bReturnSingleItem = TRUE;
  503. $element = content_multiple_value_form($form, $form_state, $field, $items);
  504. $bReturnSingleItem = FALSE;
  505. module_load_include('inc', 'flexifield', 'flexifield-widget');
  506. _flexifield_alter_multiple_values_form($element, $form, $form_state, $field, $items);
  507. }
  508. return $element;
  509. }
  510. /**
  511. * Process a widget element for an individual field item.
  512. *
  513. * This needs to be in this file instead of flexifield-widget.inc, because
  514. * when the form is submitted, it is retrieved from cache and then processed
  515. * with no opportunity to do an include of another file. However, here, we can
  516. * include the flexifield-widget.inc file, so the other process functions can
  517. * be in that file.
  518. */
  519. function flexifield_default_widget_process($aElement, $aSubmittedElementData, $aFormState, $aForm) {
  520. module_load_include('inc', 'flexifield', 'flexifield-widget');
  521. $aFieldSettings = $aForm['#field_info'][$aElement['#field_name']];
  522. $nDelta = $aElement['#delta'];
  523. $sItemType = isset($aElement['#value']['type']) ? $aElement['#value']['type'] : NULL;
  524. $aItemValue = isset($aElement['#value']['value']) ? $aElement['#value']['value'] : array();
  525. if (!is_array($aItemValue)) {
  526. $aItemValue = array();
  527. }
  528. // The element for choosing which type
  529. $aAllowedItemTypes = is_array($aFieldSettings['item_types']) ? array_filter($aFieldSettings['item_types']) : array();
  530. foreach (array_keys($aAllowedItemTypes) as $sType) {
  531. $aAllowedItemTypes[$sType] = node_get_types('name', $sType);
  532. }
  533. $nTypes = count($aAllowedItemTypes);
  534. if ($nTypes < 1) {
  535. return $aElement;
  536. }
  537. elseif ($nTypes == 1) {
  538. $sItemType = key($aAllowedItemTypes);
  539. $aElement['type'] = array(
  540. '#type' => 'value',
  541. '#value' => $sItemType,
  542. );
  543. }
  544. else {
  545. $aOptions = array_merge(array('' => t('Please select')), $aAllowedItemTypes);
  546. if ($sItemType && !$aAllowedItemTypes[$sItemType]) {
  547. $sItemType = '';
  548. }
  549. $aElement['type'] = array(
  550. '#type' => 'select',
  551. '#default_value' => $sItemType,
  552. '#options' => $aOptions,
  553. '#ahah' => array(
  554. 'path' => 'flexifield/ahah/changetype/' . join(':', $aElement['#array_parents']),
  555. 'wrapper' => $aElement['#id'] . '-value-wrapper',
  556. 'method' => 'replace',
  557. 'effect' => 'fade',
  558. ),
  559. );
  560. }
  561. // The element that contains the child fields
  562. $sFieldKey = 'value';
  563. $aElement[$sFieldKey] = array(
  564. '#type' => 'flexifield_fieldset',
  565. '#flexifield_item_type' => $sItemType,
  566. '#default_value' => $aItemValue,
  567. // The following values were set by the content module and need
  568. // to be passed down to the nested element.
  569. //'#title' => $aElement['#title'],
  570. //'#description' => $aElement['#description'],
  571. '#required' => $aElement['#required'],
  572. '#field_name' => $aElement['#field_name'],
  573. '#type_name' => $aElement['#type_name'],
  574. '#delta' => $aElement['#delta'],
  575. '#columns' => $aElement['#columns'],
  576. );
  577. // @todo: This is a temporary hack. Solve this better or document it!!
  578. if (($nTypes > 1) && module_exists('wysiwyg')) {
  579. foreach (filter_formats() as $format => $object) {
  580. wysiwyg_get_profile($format);
  581. }
  582. }
  583. return $aElement;
  584. }
  585. /**
  586. * flexifield_get_element_property
  587. *
  588. * Helper function to return an element's property, its type's property, or a default value.
  589. */
  590. function flexifield_get_element_property($aElement, $sProperty, $vDefault = NULL) {
  591. if (isset($aElement[$sProperty])) {
  592. return $aElement[$sProperty];
  593. }
  594. $aTypeInfo = _element_info(isset($aElement['#type']) ? $aElement['#type'] : 'markup');
  595. if (isset($aTypeInfo[$sProperty])) {
  596. return $aTypeInfo[$sProperty];
  597. }
  598. return $vDefault;
  599. }
  600. /**
  601. * Preprocess function for item content
  602. */
  603. function template_preprocess_flexifield_item_content(&$aVars) {
  604. // For each fully rendered child field that's exposed as a variable, also
  605. // expose each of its rendered items and the first rendered item as variables.
  606. // See content.module's content_field(op='alter') for reference on
  607. // relationship between #single and #children.
  608. foreach (element_children($aVars['element']) as $sFieldName) {
  609. if (!isset($aVars[$sFieldName . '_items']) && isset($aVars['element'][$sFieldName]['field']['#single'])) {
  610. $aVars[$sFieldName . '_items'] = array();
  611. $aChildFieldElement = $aVars['element'][$sFieldName]['field'];
  612. if ($aChildFieldElement['#single']) {
  613. foreach (element_children($aChildFieldElement['items']) as $i) {
  614. $aVars[$sFieldName . '_items'][$i] = isset($aChildFieldElement['items'][$i]['#children']) ? $aChildFieldElement['items'][$i]['#children'] : '';
  615. }
  616. }
  617. else {
  618. $aVars[$sFieldName . '_items'][0] = $aChildFieldElement['items']['#children'];
  619. }
  620. if (!isset($aVars[$sFieldName . '_item'])) {
  621. $aVars[$sFieldName . '_item'] = $aVars[$sFieldName . '_items'][0];
  622. }
  623. }
  624. }
  625. }