tripal.fields.api.inc

Provides an application programming interface (API) for working with fields attached to TripalEntity content types (bundles).

File

tripal/api/tripal.fields.api.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Provides an application programming interface (API) for working with
  5. * fields attached to TripalEntity content types (bundles).
  6. *
  7. */
  8. /**
  9. * @defgroup tripal_fields_api Tripal Fields
  10. * @ingroup tripal_api
  11. * @{
  12. * Provides an application programming interface (API) for working with
  13. * fields attached to TripalEntity content types (bundles).
  14. *
  15. * Fields:
  16. * A field is a reusable "data container" that is attached to a Bundle.
  17. * Programmatically, each field provides one or more primitive data types, with
  18. * validators and widgets for editing and formatters for display. Each field
  19. * independently manages the data to which it assigned. Just like with Bundles,
  20. * Fields are also described using controlled vocabulary terms. For example, a
  21. * gene Bundle has a field attached that provides the name of the gene.
  22. * This field only provides the name and nothing more. Tripal uses the
  23. * schema:name vocabulary term to describe the field.
  24. *
  25. * Field Instances:
  26. * Fields describe "atomic" units of data that are associated with an entity.
  27. * For example, a "name" is an atomic unit of data about a Gene or Organism
  28. * entity. Fields can be reused for multiple Bundles. For example, gene, mRNA,
  29. * genetic markers and variants all have name data. Despite that all of these
  30. * Bundles provides a "name", we only need one field to describe that this data
  31. * is a "name". However, we may want to customize a field specific to each
  32. * bundle. Therefore, an Instance of a field is attached to a bundle, and
  33. * field instances can then be customized differently. The most important
  34. * customization is the one that defines the Chado table from which the data
  35. * for a field is retrieved. Despite that field instances are attached to
  36. * bundles, they become visible with Entities. When an entity is loaded for
  37. * display, Drupal examines all of the fields that are attached to the entity's
  38. * bundle, and then populates the fields instances with data specific to the
  39. * entity being loaded.
  40. * @}
  41. *
  42. */
  43. /**
  44. * @section
  45. * Hooks.
  46. */
  47. /**
  48. * Executes a TripalFieldQuery using the provided conditions.
  49. *
  50. * This hook is called to find the entities having certain field
  51. * conditions and sort them in the given field order.
  52. *
  53. * @param $conditions
  54. * An array of filter representing the conditions to be applied to the query.
  55. * Each filter is an associative array whose keys include the following:
  56. * - field: an array representing the field identical to the output of the
  57. * field_info_field() function.
  58. * - filter: the name of the field on which the filter should be applied.
  59. * - value: the value of the filter.
  60. * - operator: the operation to apply: '=', '<>', '>', '>=', '<', '<=',
  61. * 'STARTS_WITH', 'CONTAINS': These operators expect $value to be a
  62. * literal of the same type as the column. 'IN', 'NOT IN': These operators
  63. * expect $value to be an array of literals of the same type as the column.
  64. * @param $orderBy
  65. * An array of sorting instructions. Each sort is an associative array with
  66. * the following keys:
  67. * - field: an array representing the field identical to the output of the
  68. * field_info_field() function.
  69. * - orderBy: the name of the field on which the filter should be applied.
  70. * - direction: either the string 'ASC' (for ascending sort) or 'DESC' (for
  71. * descending).
  72. *
  73. * @ingroup tripal_fields_api
  74. */
  75. function hook_field_storage_tquery($conditions, $orderBy) {
  76. // See the tripal_chado_field_storage_tquery() function for an example.
  77. }
  78. /**
  79. * Allows a module to return a bundles field info.
  80. *
  81. * @param $entity_type
  82. * The name of the entity, like 'TripalEntity'.
  83. * @param $bundle
  84. * The bundle object.
  85. *
  86. * @ingroup tripal_fields_api
  87. */
  88. function hook_bundle_fields_info($entity_type, $bundle) {
  89. }
  90. /**
  91. * Allows a module to return the field instances of a bundle.
  92. * @param $entity_type
  93. * The name of the entity, most likely 'TripalEntity'.
  94. * @param $bundle
  95. * The bundle object.
  96. *
  97. * @ingroup tripal_fields_api
  98. */
  99. function hook_bundle_instances_info($entity_type, $bundle) {
  100. }
  101. /**
  102. * Retrieves a list of TripalField types.
  103. *
  104. * The TripalField classes can be added by a site developer and should be
  105. * placed in the [module]/includes/TripalFields directory. Tripal will support
  106. * any field as long as it is in this directory and extends the TripalField
  107. * class. To support dynamic inclusion of new fields this function
  108. * will look for TripalField class files and return a type for
  109. * each one.
  110. *
  111. * @return
  112. * A list of TripalField names.
  113. *
  114. * @ingroup tripal_fields_api
  115. */
  116. function tripal_get_field_types() {
  117. $types = array();
  118. $modules = module_list(TRUE);
  119. foreach ($modules as $module) {
  120. // Only run this for modules that are enabled.
  121. if (!module_exists($module)) {
  122. continue;
  123. }
  124. // Find all of the files in the tripal_chado/includes/TripalFields/ directory.
  125. $fields_path = drupal_get_path('module', $module) . '/includes/TripalFields';
  126. $field_files = file_scan_directory($fields_path, '/.*\.inc$/');
  127. // Iterate through the fields, include the file and run the info function.
  128. foreach ($field_files as $file) {
  129. // Ignore the formatter and widget classes for now.
  130. if (preg_match('/_formatter|_widget/', $file->name)) {
  131. continue;
  132. }
  133. $field_type = $file->name;
  134. module_load_include('inc', $module, 'includes/TripalFields/' . $field_type . '/' . $field_type);
  135. if (class_exists($field_type) and is_subclass_of($field_type, 'TripalField')) {
  136. $types[] = $field_type;
  137. }
  138. }
  139. }
  140. // If the libraries module is enabled then we want to look for a TripalFields
  141. // library folder and see if our field exist there.
  142. if (module_exists('libraries')) {
  143. $library_path = libraries_get_path('TripalFields');
  144. $fields_path = realpath(".") . '/' . $library_path;
  145. $field_files = file_scan_directory($fields_path, '/.*\.inc$/');
  146. foreach ($field_files as $file) {
  147. // Ignore the formatter and widget classes for now.
  148. if (preg_match('/_formatter|_widget/', $file->name)) {
  149. continue;
  150. }
  151. $field_type = $file->name;
  152. $file_path = realpath(".") . '/' . $library_path .'/' . $field_type . '/' . $field_type . '.inc';
  153. if (file_exists($file_path)) {
  154. require_once($file_path);
  155. if (class_exists($field_type) and is_subclass_of($field_type, 'TripalField')) {
  156. $types[] = $field_type;
  157. }
  158. }
  159. }
  160. }
  161. return $types;
  162. }
  163. /**
  164. * Retrieves a list of TripalFieldWidgets.
  165. *
  166. * The TripalFieldWidget classes can be added by a site developer and should be
  167. * placed in the [module]/includes/TripalFields directory. Tripal will support
  168. * any widget as long as it is in this directory and extends the
  169. * TripalFieldWidget class.
  170. *
  171. * @return
  172. * A list of TripalFieldWidget names.
  173. *
  174. * @ingroup tripal_fields_api
  175. */
  176. function tripal_get_field_widgets() {
  177. $widgets = array();
  178. $modules = module_list(TRUE);
  179. foreach ($modules as $module) {
  180. // Only run this for modules that are enabled.
  181. if (!module_exists($module)) {
  182. continue;
  183. }
  184. // Find all of the files in the tripal_chado/includes/fields directory.
  185. $fields_path = drupal_get_path('module', $module) . '/includes/TripalFields';
  186. $field_files = file_scan_directory($fields_path, '/.*_widget\.inc$/');
  187. // Iterate through the fields, include the file and run the info function.
  188. foreach ($field_files as $file) {
  189. $widget_type = $file->name;
  190. $field_type = preg_replace('/(^.*)_widget/', '$1', $widget_type);
  191. module_load_include('inc', $module, 'includes/TripalFields/' . $field_type . '/' . $widget_type);
  192. if (class_exists($widget_type) and is_subclass_of($widget_type, 'TripalFieldWidget')) {
  193. $widgets[] = $widget_type;
  194. }
  195. }
  196. }
  197. // If the libraries module is enabled then we want to look for a TripalFields
  198. // library folder and see if our field exist there.
  199. if (module_exists('libraries')) {
  200. $library_path = libraries_get_path('TripalFields');
  201. $fields_path = realpath(".") . '/' . $library_path;
  202. $field_files = file_scan_directory($fields_path, '/.*_widget\.inc$/');
  203. foreach ($field_files as $file) {
  204. $widget_type = $file->name;
  205. $field_type = preg_replace('/(^.*)_widget/', '$1', $widget_type);
  206. $file_path = realpath(".") . '/' . $library_path .'/' . $field_type . '/' . $widget_type . '.inc';
  207. if (file_exists($file_path)) {
  208. require_once($file_path);
  209. if (class_exists($widget_type) and is_subclass_of($widget_type, 'TripalFieldWidget')) {
  210. $widgets[] = $widget_type;
  211. }
  212. }
  213. }
  214. }
  215. return $widgets;
  216. }
  217. /**
  218. * Retrieves a list of field formatters compatible with a given field.
  219. *
  220. * @param $field
  221. * A field array as returned by the field_info_field() function.
  222. * @param $instance
  223. * A field instance array.
  224. * @return
  225. * A list of file formatter class names.
  226. */
  227. function tripal_get_field_field_formatters($field, $instance) {
  228. $field_name = $field['field_name'];
  229. $field_type = $field['type'];
  230. $downloaders = array();
  231. // If the field type is a TripalField then get information about the formatter.
  232. if (tripal_load_include_field_class($field_type)) {
  233. $formatters = $field_type::$download_formatters;
  234. foreach ($formatters as $class_name) {
  235. if (!array_key_exists($class_name, $downloaders)) {
  236. tripal_load_include_downloader_class($class_name);
  237. $downloaders[$class_name] = $class_name::$full_label;
  238. }
  239. }
  240. }
  241. else {
  242. // For non TripalFields we'll assume TAB and CSV.
  243. tripal_load_include_downloader_class('TripalTabDownloader');
  244. tripal_load_include_downloader_class('TripalCSVDownloader');
  245. $downloaders['TripalTabDownloader'] = TripalTabDownloader::$full_label;
  246. $downloaders['TripalCSVDownloader'] = TripalCSVDownloader::$full_label;
  247. }
  248. return $downloaders;
  249. }
  250. /**
  251. * Retrieves a list of TripalFieldFormatters.
  252. *
  253. * The TripalFieldFormatter classes can be added by a site developer and should
  254. * be placed in the [module]/includes/TripalFields directory. Tripal will
  255. * support any widget as long as it is in this directory and extends the
  256. * TripalFieldFormatter class.
  257. *
  258. * @return
  259. * A list of TripalFieldFormatter names.
  260. *
  261. * @ingroup tripal_fields_api
  262. */
  263. function tripal_get_field_formatters() {
  264. $formatters = array();
  265. $modules = module_list(TRUE);
  266. foreach ($modules as $module) {
  267. // Only run this for modules that are enabled.
  268. if (!module_exists($module)) {
  269. continue;
  270. }
  271. // Find all of the files in the tripal_chado/includes/fields directory.
  272. $fields_path = drupal_get_path('module', $module) . '/includes/TripalFields';
  273. $field_files = file_scan_directory($fields_path, '/.*_formatter\.inc$/');
  274. // Iterate through the fields, include the file and run the info function.
  275. foreach ($field_files as $file) {
  276. $formatter_type = $file->name;
  277. $field_type = preg_replace('/(^.*)_formatter/', '$1', $formatter_type);
  278. module_load_include('inc', $module, 'includes/TripalFields/' . $field_type . '/' . $formatter_type);
  279. if (class_exists($formatter_type) and is_subclass_of($formatter_type, 'TripalFieldFormatter')) {
  280. $formatters[] = $formatter_type;
  281. }
  282. }
  283. }
  284. // If the libraries module is enabled then we want to look for a TripalFields
  285. // library folder and see if our field exist there.
  286. if (module_exists('libraries')) {
  287. $library_path = libraries_get_path('TripalFields');
  288. $fields_path = realpath(".") . '/' . $library_path;
  289. $field_files = file_scan_directory($fields_path, '/.*_formatter\.inc$/');
  290. foreach ($field_files as $file) {
  291. $formatter_type = $file->name;
  292. $field_type = preg_replace('/(^.*)_formatter/', '$1', $formatter_type);
  293. $file_path = realpath(".") . '/' . $library_path .'/' . $field_type . '/' . $formatter_type . '.inc';
  294. if (file_exists($file_path)) {
  295. require_once($file_path);
  296. if (class_exists($formatter_type) and is_subclass_of($formatter_type, 'TripalFieldFormatter')) {
  297. $formatters[] = $formatter_type;
  298. }
  299. }
  300. }
  301. }
  302. return $formatters;
  303. }
  304. /**
  305. * Loads the TripalField class file into scope.
  306. *
  307. * @param $class
  308. * The class to include. This can be a TripalField, TripalFieldWidget or
  309. * TripalFieldFormatter class name.
  310. *
  311. * @return
  312. * TRUE if the field type class file was found, FALSE otherwise.
  313. *
  314. * @ingroup tripal_fields_api
  315. */
  316. function tripal_load_include_field_class($class) {
  317. $modules = module_list(TRUE);
  318. foreach ($modules as $module) {
  319. $field_type = preg_replace('/(^.*)_(formatter|widget)/', '$1', $class);
  320. $file_path = realpath(".") . '/' . drupal_get_path('module', $module) . '/includes/TripalFields/' . $field_type . '/' . $class . '.inc';
  321. if (file_exists($file_path)) {
  322. module_load_include('inc', $module, 'includes/TripalFields/' . $field_type . '/' . $class);
  323. if (class_exists($class)) {
  324. return TRUE;
  325. }
  326. }
  327. }
  328. // If the libraries module is enabled then we want to look for a TripalFields
  329. // library folder and see if our field exist there.
  330. if (module_exists('libraries')) {
  331. $library_path = libraries_get_path('TripalFields');
  332. $field_type = preg_replace('/(^.*)_(formatter|widget)/', '$1', $class);
  333. $file_path = realpath(".") . '/' . $library_path .'/' . $field_type . '/' . $class . '.inc';
  334. if (file_exists($file_path)) {
  335. require_once($file_path);
  336. if (class_exists($class)) {
  337. return TRUE;
  338. }
  339. }
  340. }
  341. return FALSE;
  342. }
  343. /**
  344. * Loads the TripalEntityDownloader file into scope.
  345. *
  346. * @param $class
  347. * The name of the class to include.
  348. *
  349. * @return
  350. * TRUE if the downloader class file was found, FALSE otherwise.
  351. *
  352. * @ingroup tripal_fields_api
  353. */
  354. function tripal_load_include_downloader_class($class) {
  355. $modules = module_list(TRUE);
  356. foreach ($modules as $module) {
  357. $file_path = realpath(".") . '/' . drupal_get_path('module', $module) . '/includes/TripalFieldDownloaders/' . $class . '.inc';
  358. if (file_exists($file_path)) {
  359. module_load_include('inc', $module, 'includes/TripalFieldDownloaders/' . $class);
  360. if (class_exists($class)) {
  361. return TRUE;
  362. }
  363. }
  364. }
  365. // If the libraries module is enabled then we want to look for a
  366. // TripalFieldDownloader library folder and see if our field exist there.
  367. if (module_exists('libraries')) {
  368. $library_path = libraries_get_path('TripalFieldDownloaders');
  369. $file_path = realpath(".") . '/' . $library_path .'/' . $class . '.inc';
  370. if (file_exists($file_path)) {
  371. require_once($file_path);
  372. if (class_exists($class)) {
  373. return TRUE;
  374. }
  375. }
  376. }
  377. return FALSE;
  378. }
  379. /**
  380. * More easily get the value of a single item from a field's items array.
  381. *
  382. * A Drupal field attached to an entity may have multiple items (i.e. it has
  383. * a cardinality > 1). Each of these items will always have a 'value' key that
  384. * contains the data for that field. However, fields can also have other keys
  385. * with their own values. You can easily retreive the value of these keys
  386. * using this function. What makes this function useful is that you can
  387. * provide a default value to use if the key has no value. This is useful
  388. * when developing a TripalField::widgetForm function and you need to
  389. * retreive default values and you don't want to have to always check if the
  390. * value is set.
  391. *
  392. * @param $items
  393. * The fields $items array. Compatbile with that returned by field_get_items.
  394. * @param $delta
  395. * The index of the item.
  396. * @parm $key
  397. * The name of the key within the item.
  398. * @param $default
  399. * A default value to return if the key is not set. By default the empty
  400. * string is returned.
  401. *
  402. * @return
  403. * The value assigned to the item's key; FALSE if the key doesn't exist or
  404. * the $default argument if no value is associated with the key.
  405. *
  406. * @ingroup tripal_fields_api
  407. */
  408. function tripal_get_field_item_keyval($items, $delta, $key, $default='') {
  409. if (!array_key_exists($delta, $items)) {
  410. return FALSE;
  411. }
  412. if (!array_key_exists($key, $items[$delta])) {
  413. return FALSE;
  414. }
  415. if (!$items[$delta][$key]) {
  416. return $default;
  417. }
  418. return $items[$delta][$key];
  419. }
  420. /**
  421. * Formats an element of a TripalField for use by Drupal Views.
  422. *
  423. * Sometimes the value of TripalField can be more than just a single scalar. In
  424. * this case the value is an array of key value pairs where each key is a
  425. * controlled vocabulary term. In order to support fields, filtering and
  426. * sorting by these sub elements using Drupal Views, the TripalField
  427. * implementation must provide some help to Views by describing these elements,
  428. * and then implementing a query() function to support them. However, the
  429. * naming of sub elements must follow a set convention. This function
  430. * guarantees proper naming for sub elements.
  431. *
  432. * @param $field_name
  433. * The name of the field to which the element belongs.
  434. * @param $term
  435. * The term object as returned by tripal_get_term_details();
  436. *
  437. * @ingroup tripal_fields_api
  438. */
  439. function tripal_format_views_field_element($field_name, $term) {
  440. $element_name = $term['vocabulary']['short_name'] . '__' . $term['accession'];
  441. return $field_name . '.' . $element_name;
  442. }