123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- <?php
- /**
- * @file
- * Provides an application programming interface (API) for working with
- * fields attached to TripalEntity content types (bundles).
- *
- */
- /**
- * @defgroup tripal_fields_api Tripal Fields
- * @ingroup tripal_api
- * @{
- * Provides an application programming interface (API) for working with
- * fields attached to TripalEntity content types (bundles).
- *
- * Fields:
- * A field is a reusable "data container" that is attached to a Bundle.
- * Programmatically, each field provides one or more primitive data types, with
- * validators and widgets for editing and formatters for display. Each field
- * independently manages the data to which it assigned. Just like with Bundles,
- * Fields are also described using controlled vocabulary terms. For example, a
- * gene Bundle has a field attached that provides the name of the gene.
- * This field only provides the name and nothing more. Tripal uses the
- * schema:name vocabulary term to describe the field.
- *
- * Field Instances:
- * Fields describe "atomic" units of data that are associated with an entity.
- * For example, a "name" is an atomic unit of data about a Gene or Organism
- * entity. Fields can be reused for multiple Bundles. For example, gene, mRNA,
- * genetic markers and variants all have name data. Despite that all of these
- * Bundles provides a "name", we only need one field to describe that this data
- * is a "name". However, we may want to customize a field specific to each
- * bundle. Therefore, an Instance of a field is attached to a bundle, and
- * field instances can then be customized differently. The most important
- * customization is the one that defines the Chado table from which the data
- * for a field is retrieved. Despite that field instances are attached to
- * bundles, they become visible with Entities. When an entity is loaded for
- * display, Drupal examines all of the fields that are attached to the entity's
- * bundle, and then populates the fields instances with data specific to the
- * entity being loaded.
- * @}
- *
- */
- /**
- * @section
- * Hooks.
- */
- /**
- * Executes a TripalFieldQuery using the provided conditions.
- *
- * This hook is called to find the entities having certain field
- * conditions and sort them in the given field order.
- *
- * @param $conditions
- * An array of filter representing the conditions to be applied to the query.
- * Each filter is an associative array whose keys include the following:
- * - field: an array representing the field identical to the output of the
- * field_info_field() function.
- * - filter: the name of the field on which the filter should be applied.
- * - value: the value of the filter.
- * - operator: the operation to apply: '=', '<>', '>', '>=', '<', '<=',
- * 'STARTS_WITH', 'CONTAINS': These operators expect $value to be a
- * literal of the same type as the column. 'IN', 'NOT IN': These operators
- * expect $value to be an array of literals of the same type as the column.
- * @param $orderBy
- * An array of sorting instructions. Each sort is an associative array with
- * the following keys:
- * - field: an array representing the field identical to the output of the
- * field_info_field() function.
- * - orderBy: the name of the field on which the filter should be applied.
- * - direction: either the string 'ASC' (for ascending sort) or 'DESC' (for
- * descending).
- *
- * @ingroup tripal_fields_api
- */
- function hook_field_storage_tquery($conditions, $orderBy) {
- // See the tripal_chado_field_storage_tquery() function for an example.
- }
- /**
- * Allows a module to return a bundles field info.
- *
- * @param $entity_type
- * The name of the entity, like 'TripalEntity'.
- * @param $bundle
- * The bundle object.
- *
- * @ingroup tripal_fields_api
- */
- function hook_bundle_fields_info($entity_type, $bundle) {
- }
- /**
- * Allows a module to return the field instances of a bundle.
- *
- * @param $entity_type
- * The name of the entity, most likely 'TripalEntity'.
- * @param $bundle
- * The bundle object.
- *
- * @ingroup tripal_fields_api
- */
- function hook_bundle_instances_info($entity_type, $bundle) {
- }
- /**
- * Indicate if a field has an empty value.
- *
- * By default, all field values are attached to an entity in the form
- * $entity->{field_name}[{language}][{delta}]. Typically a field will then
- * have a 'value' element: $entity->{field_name}[{language}][{delta}]['value']
- * and if that value is empty then the field is considered empty by Tripal.
- * By default the tripal_field_is_empty() function is used to check all
- * fields to see if they are empty. However, this hook can be implemented by
- * any module to override that behavior.
- *
- * @param $field
- * A field array.
- * @param $items
- * The array of items attached to entity.
- * @param $delta
- * The specific value to check. For fields with cardinality greater than
- * 1 then each value can be checked. Defaults to 0 indicating it will check
- * the first value.
- *
- * @return
- * TRUE if the field value is empty for the given delta, and FALSE if not
- * empty.
- *
- * @ingroup tripal_fields_api
- */
- function tripal_field_is_empty($field, $items, $delta = 0) {
- // If the $items argument is empty then return TRUE.
- if (!$items) {
- return TRUE;
- }
- // If the field is a tripal field storage API field and there
- // is no value field then the field is empty.
- if (array_key_exists('storage', $field) and
- array_key_exists('tripal_storage_api', $field['storage']['settings']) and
- !array_key_exists('value', $items[$delta])) {
- return TRUE;
- }
- // If there is a value field but there's nothing in it, the the field is
- // empty.
- if (array_key_exists($delta, $items) and
- array_key_exists('value', $items[$delta]) and
- empty($items[$delta]['value'])) {
- return TRUE;
- }
- // Otherwise, the field is not empty.
- return FALSE;
- }
- /**
- * Retrieves a list of all fields implementing Tripal Fields/Formatters/Widgets.
- *
- * The TripalField classes can be added by the site developer and should be
- * placed in the [module]/includes/TripalFields directory. Tripal will support
- * any field as long as:
- * - it's in the [modules]/includes/TripalFields directory,
- * - extends TripalField, TripalWidget or TripalFormatter,
- * - matches the [cvname]__[termname] convention.
- *
- * @return
- * An array of files containing Tripal fields, widgets or formatters where
- * each element has a name, filename, and uri. The URI is relative to your
- * drupal root.
- */
- function tripal_get_tripalfield_files() {
- $field_files = &drupal_static(__FUNCTION__);
- if (!isset($field_files)) {
- $field_files = [];
- // Check module directories.
- $modules = module_list(TRUE);
- foreach ($modules as $module) {
- // Only run this for modules that are enabled.
- if (!module_exists($module)) {
- continue;
- }
- // Find all of the files in the tripal_chado/includes/fields directory.
- $fields_path = drupal_get_path('module', $module) . '/includes/TripalFields';
- $tmp = file_scan_directory($fields_path, '/.*__.*\.inc$/');
- if (!empty($tmp)) {
- $field_files = array_merge($field_files, $tmp);
- }
- }
- // Check the library directory.
- if (module_exists('libraries')) {
- $library_path = libraries_get_path('TripalFields');
- $fields_path = $library_path;
- $tmp = file_scan_directory($fields_path, '/.*__.*\.inc$/');
- if (!empty($tmp)) {
- $field_files = array_merge($field_files, $tmp);
- }
- }
- }
- return $field_files;
- }
- /**
- * Retrieves a list of TripalField types.
- *
- * The TripalField classes can be added by a site developer and should be
- * placed in the [module]/includes/TripalFields directory. Tripal will support
- * any field as long as it is in this directory and extends the TripalField
- * class. To support dynamic inclusion of new fields this function
- * will look for TripalField class files and return a type for
- * each one.
- *
- * @return
- * A list of TripalField names.
- *
- * @ingroup tripal_fields_api
- */
- function tripal_get_field_types() {
- $types = [];
- $field_files = tripal_get_tripalfield_files();
- // Iterate through the fields, include the file and run the info function.
- foreach ($field_files as $file) {
- // Ignore the formatter and widget classes for now.
- if (preg_match('/_formatter|_widget/', $file->name)) {
- continue;
- }
- $field_type = $file->name;
- require_once($file->uri);
- if (class_exists($field_type) and is_subclass_of($field_type, 'TripalField')) {
- $types[] = $field_type;
- }
- }
- return $types;
- }
- /**
- * Retrieves a list of TripalFieldWidgets.
- *
- * The TripalFieldWidget classes can be added by a site developer and should be
- * placed in the [module]/includes/TripalFields directory. Tripal will support
- * any widget as long as it is in this directory and extends the
- * TripalFieldWidget class.
- *
- * @return
- * A list of TripalFieldWidget names.
- *
- * @ingroup tripal_fields_api
- */
- function tripal_get_field_widgets() {
- $widgets = [];
- $field_files = tripal_get_tripalfield_files();
- // Iterate through the fields, include the file and run the info function.
- foreach ($field_files as $file) {
- if (preg_match('/_widget/', $file->name)) {
- $widget_type = $file->name;
- require_once($file->uri);
- if (class_exists($widget_type) and is_subclass_of($widget_type, 'TripalFieldWidget')) {
- $widgets[] = $widget_type;
- }
- }
- }
- return $widgets;
- }
- /**
- * Retrieves a list of field formatters compatible with a given field.
- *
- * @param $field
- * A field array as returned by the field_info_field() function.
- * @param $instance
- * A field instance array.
- *
- * @return
- * A list of file formatter class names.
- */
- function tripal_get_field_field_formatters($field, $instance) {
- $field_name = $field['field_name'];
- $field_type = $field['type'];
- $downloaders = [];
- // If the field type is a TripalField then get information about the formatter.
- if (tripal_load_include_field_class($field_type)) {
- $formatters = $field_type::$download_formatters;
- foreach ($formatters as $class_name) {
- if (!array_key_exists($class_name, $downloaders)) {
- tripal_load_include_downloader_class($class_name);
- $downloaders[$class_name] = $class_name::$full_label;
- }
- }
- }
- else {
- // For non TripalFields we'll assume TAB and CSV.
- tripal_load_include_downloader_class('TripalTabDownloader');
- tripal_load_include_downloader_class('TripalCSVDownloader');
- $downloaders['TripalTabDownloader'] = TripalTabDownloader::$full_label;
- $downloaders['TripalCSVDownloader'] = TripalCSVDownloader::$full_label;
- }
- return $downloaders;
- }
- /**
- * Retrieves a list of TripalFieldFormatters.
- *
- * The TripalFieldFormatter classes can be added by a site developer and should
- * be placed in the [module]/includes/TripalFields directory. Tripal will
- * support any widget as long as it is in this directory and extends the
- * TripalFieldFormatter class.
- *
- * @return
- * A list of TripalFieldFormatter names.
- *
- * @ingroup tripal_fields_api
- */
- function tripal_get_field_formatters() {
- $formatters = [];
- $field_files = tripal_get_tripalfield_files();
- // Iterate through the fields, include the file and run the info function.
- foreach ($field_files as $file) {
- if (preg_match('/_formatter/', $file->name)) {
- $formatter_type = $file->name;
- require_once($file->uri);
- if (class_exists($formatter_type) and is_subclass_of($formatter_type, 'TripalFieldFormatter')) {
- $formatters[] = $formatter_type;
- }
- }
- }
- return $formatters;
- }
- /**
- * Loads the TripalField class file into scope.
- *
- * @param $class
- * The class to include. This can be a TripalField, TripalFieldWidget or
- * TripalFieldFormatter class name.
- *
- * @return
- * TRUE if the field type class file was found, FALSE otherwise.
- *
- * @ingroup tripal_fields_api
- */
- function tripal_load_include_field_class($class) {
- $modules = module_list(TRUE);
- foreach ($modules as $module) {
- $field_type = preg_replace('/(^.*)_(formatter|widget)/', '$1', $class);
- $file_path = realpath(".") . '/' . drupal_get_path('module', $module) . '/includes/TripalFields/' . $field_type . '/' . $class . '.inc';
- if (file_exists($file_path)) {
- module_load_include('inc', $module, 'includes/TripalFields/' . $field_type . '/' . $class);
- if (class_exists($class)) {
- return TRUE;
- }
- }
- }
- // If the libraries module is enabled then we want to look for a TripalFields
- // library folder and see if our field exist there.
- if (module_exists('libraries')) {
- $library_path = libraries_get_path('TripalFields');
- $field_type = preg_replace('/(^.*)_(formatter|widget)/', '$1', $class);
- $file_path = realpath(".") . '/' . $library_path . '/' . $field_type . '/' . $class . '.inc';
- if (file_exists($file_path)) {
- require_once($file_path);
- if (class_exists($class)) {
- return TRUE;
- }
- }
- }
- return FALSE;
- }
- /**
- * Loads the TripalEntityDownloader file into scope.
- *
- * @param $class
- * The name of the class to include.
- *
- * @return
- * TRUE if the downloader class file was found, FALSE otherwise.
- *
- * @ingroup tripal_fields_api
- */
- function tripal_load_include_downloader_class($class) {
- $modules = module_list(TRUE);
- foreach ($modules as $module) {
- $file_path = realpath(".") . '/' . drupal_get_path('module', $module) . '/includes/TripalFieldDownloaders/' . $class . '.inc';
- if (file_exists($file_path)) {
- module_load_include('inc', $module, 'includes/TripalFieldDownloaders/' . $class);
- if (class_exists($class)) {
- return TRUE;
- }
- }
- }
- // If the libraries module is enabled then we want to look for a
- // TripalFieldDownloader library folder and see if our field exist there.
- if (module_exists('libraries')) {
- $library_path = libraries_get_path('TripalFieldDownloaders');
- $file_path = realpath(".") . '/' . $library_path . '/' . $class . '.inc';
- if (file_exists($file_path)) {
- require_once($file_path);
- if (class_exists($class)) {
- return TRUE;
- }
- }
- }
- return FALSE;
- }
- /**
- * More easily get the value of a single item from a field's items array.
- *
- * A Drupal field attached to an entity may have multiple items (i.e. it has
- * a cardinality > 1). Each of these items will always have a 'value' key that
- * contains the data for that field. However, fields can also have other keys
- * with their own values. You can easily retreive the value of these keys
- * using this function. What makes this function useful is that you can
- * provide a default value to use if the key has no value. This is useful
- * when developing a TripalField::widgetForm function and you need to
- * retreive default values and you don't want to have to always check if the
- * value is set.
- *
- * @param $items
- * The fields $items array. Compatbile with that returned by field_get_items.
- * @param $delta
- * The index of the item.
- * @parm $key
- * The name of the key within the item.
- * @param $default
- * A default value to return if the key is not set. By default the empty
- * string is returned.
- *
- * @return
- * The value assigned to the item's key; FALSE if the key doesn't exist or
- * the $default argument if no value is associated with the key.
- *
- * @ingroup tripal_fields_api
- */
- function tripal_get_field_item_keyval($items, $delta, $key, $default = '') {
- if (!array_key_exists($delta, $items)) {
- return FALSE;
- }
- if (!array_key_exists($key, $items[$delta])) {
- return FALSE;
- }
- if (!$items[$delta][$key]) {
- return $default;
- }
- return $items[$delta][$key];
- }
- /**
- * Formats an element of a TripalField for use by Drupal Views.
- *
- * Sometimes the value of TripalField can be more than just a single scalar. In
- * this case the value is an array of key value pairs where each key is a
- * controlled vocabulary term. In order to support fields, filtering and
- * sorting by these sub elements using Drupal Views, the TripalField
- * implementation must provide some help to Views by describing these elements,
- * and then implementing a query() function to support them. However, the
- * naming of sub elements must follow a set convention. This function
- * guarantees proper naming for sub elements.
- *
- * @param $field_name
- * The name of the field to which the element belongs.
- * @param $term
- * The term object as returned by tripal_get_term_details();
- *
- * @ingroup tripal_fields_api
- */
- function tripal_format_views_field_element($field_name, $term) {
- $element_name = $term['vocabulary']['short_name'] . '__' . $term['accession'];
- return $field_name . '.' . $element_name;
- }
|