2020-08-14 13:36:36 +02:00
< ? php
/**
* @ file
* Extend core fields with some helper functions to reduce code complexity within views and ctools plugins .
*/
/**
* Fake an instance of a field .
*
* @ param $field_name
* The unique name for this field no matter what entity / bundle it may be used on .
* @ param $view_mode
* We ' re building a new view mode for this function . Defaults to ctools , but we expect developers to actually name this something meaningful .
* @ param $formatter
* The formatter key selected from the options provided by field_ui_formatter_options () .
* @ param $formatter_settings
* An array of key value pairs . These will be used as #default_value for the form elements generated by a call to hook_field_formatter_settings_form() for this field type.
* Typically we ' ll pass an empty array to begin with and then pass this information back to ourselves on form submit so that we can set the values for later edit sessions .
*/
function ctools_fields_fake_field_instance ( $field_name , $view_mode = 'ctools' , $formatter , $formatter_settings ) {
$field = field_read_field ( $field_name );
$field_type = field_info_field_types ( $field [ 'type' ]);
return array (
// Build a fake entity type and bundle.
'field_name' => $field_name ,
'entity_type' => 'ctools' ,
'bundle' => 'ctools' ,
// Use the default field settings for settings and widget.
'settings' => field_info_instance_settings ( $field [ 'type' ]),
'widget' => array (
'type' => $field_type [ 'default_widget' ],
'settings' => array (),
),
// Build a dummy display mode.
'display' => array (
$view_mode => array (
'type' => $formatter ,
'settings' => $formatter_settings ,
),
),
// Set the other fields to their default values.
// @see _field_write_instance().
'required' => FALSE ,
'label' => $field_name ,
'description' => '' ,
'deleted' => 0 ,
);
}
/**
* Helper function for calling hook_field_formatter_settings_form () without needing to load an instance of the field .
*
* @ param $field
* A fully loaded field .
* @ param $formatter_type
* The formatter key selected from the options provided by field_ui_formatter_options () .
* @ param $form
* The full form from the function that ' s calling this function .
* @ param $form_state
* The full form_state from the function that ' s calling this function .
* @ param $view_mode
* We 're passing a view mode from this function to the fake instance we' re creating . Defaults to ctools , but we expect developers to actually name this something meaningful .
*/
function ctools_fields_get_field_formatter_settings_form ( $field , $formatter_type , & $form , $form_state , $view_mode = 'ctools' ) {
$conf = $form_state [ 'conf' ];
$formatter = field_info_formatter_types ( $formatter_type );
if ( isset ( $formatter [ 'settings' ])) {
$conf [ 'formatter_settings' ] += $formatter [ 'settings' ];
}
$function = $formatter [ 'module' ] . '_field_formatter_settings_form' ;
$instance = ctools_fields_fake_field_instance ( $field [ 'field_name' ], $view_mode , $formatter_type , $conf [ 'formatter_settings' ]);
if ( function_exists ( $function )) {
$settings_form = $function ( $field , $instance , $view_mode , $form , $form_state );
}
if ( empty ( $settings_form )) {
$settings_form = array ();
}
// Allow other modules to alter the formatter settings form.
$context = array (
'module' => $formatter [ 'module' ],
'formatter' => $formatter ,
'field' => $field ,
'instance' => $instance ,
'view_mode' => $view_mode ,
'form' => $form ,
'form_state' => $form_state ,
);
drupal_alter ( 'field_formatter_settings_form' , $settings_form , $context );
$settings_form [ '#tree' ] = TRUE ;
$form [ 'ctools_field_list' ][ '#value' ][] = $field ;
$form += $settings_form ;
if ( isset ( $field [ 'cardinality' ]) && $field [ 'cardinality' ] != 1 ) {
list ( $prefix , $suffix ) = explode ( '@count' , t ( 'Skip the first @count item(s)' ));
$form [ 'delta_offset' ] = array (
'#type' => 'textfield' ,
'#size' => 5 ,
'#field_prefix' => $prefix ,
'#field_suffix' => $suffix ,
'#default_value' => isset ( $conf [ 'delta_offset' ]) ? $conf [ 'delta_offset' ] : 0 ,
);
list ( $prefix , $suffix ) = explode ( '@count' , t ( 'Then display at most @count item(s)' ));
$form [ 'delta_limit' ] = array (
'#type' => 'textfield' ,
'#size' => 5 ,
'#field_prefix' => $prefix ,
'#field_suffix' => $suffix ,
'#description' => t ( 'Enter 0 to display all items.' ),
'#default_value' => isset ( $conf [ 'delta_limit' ]) ? $conf [ 'delta_limit' ] : 0 ,
);
$form [ 'delta_reversed' ] = array (
'#title' => t ( 'Display in reverse order' ),
'#type' => 'checkbox' ,
'#default_value' => ! empty ( $conf [ 'delta_reversed' ]),
'#description' => t ( '(start from last values)' ),
);
}
}
/**
* Helper function for generating all the formatter information associated with
* any fields .
* Especially useful for determining the fields that will be added to form that
* executes hook_field_formatter_settings_form () .
*
* @ param $fields
* An array of fully loaded fields .
*/
function ctools_fields_get_field_formatter_info ( $fields ) {
$info = array ();
$field_info = field_info_formatter_types ();
foreach ( $fields as $field ) {
foreach ( $field_info as $format_name => $formatter_info ) {
if ( in_array ( $field [ 'type' ], $formatter_info [ 'field types' ])) {
$info += array ( $format_name => $formatter_info );
}
}
}
return $info ;
}
/**
* Returns the label of a certain field .
*
* Cribbed from Views .
*/
function ctools_field_label ( $field_name ) {
$label_counter = array ();
// Count the amount of instances per label per field.
$instances = field_info_instances ();
foreach ( $instances as $entity_type ) {
foreach ( $entity_type as $bundle ) {
if ( isset ( $bundle [ $field_name ])) {
$label_counter [ $bundle [ $field_name ][ 'label' ]] = isset ( $label_counter [ $bundle [ $field_name ][ 'label' ]]) ? ++ $label_counter [ $bundle [ $field_name ][ 'label' ]] : 1 ;
}
}
}
if ( empty ( $label_counter )) {
return $field_name ;
}
// Sort the field lables by it most used label and return the most used one.
arsort ( $label_counter );
$label_counter = array_keys ( $label_counter );
return $label_counter [ 0 ];
}
/**
* Replacement for core _field_invoke () to invoke on a single field .
*
* Core only allows invoking field hooks via a private function for all fields
* on an entire entity . However , we very often need to invoke our hooks on
* a single field as we take things apart and only use little bits .
*
* @ param $field_name
* Either a field instance object or the name of the field .
* If the 'field' key is populated it will be used as the field
* settings .
* @ param $op
* Possible operations include :
* - form
* - validate
* - presave
* - insert
* - update
* - delete
* - delete revision
* - view
* - prepare translation
* @ param $entity_type
* The type of $entity ; e . g . 'node' or 'user' .
* @ param $entity
* The fully formed $entity_type entity .
* @ param $a
* - The $form in the 'form' operation .
* - The value of $view_mode in the 'view' operation .
* - Otherwise NULL .
* @ param $b
* - The $form_state in the 'submit' operation .
* - Otherwise NULL .
* @ param $options
* An associative array of additional options , with the following keys :
* - 'field_name' : The name of the field whose operation should be
* invoked . By default , the operation is invoked on all the fields
* in the entity ' s bundle . NOTE : This option is not compatible with
* the 'deleted' option ; the 'field_id' option should be used
* instead .
* - 'field_id' : The id of the field whose operation should be
* invoked . By default , the operation is invoked on all the fields
* in the entity 's' bundles .
* - 'default' : A boolean value , specifying which implementation of
* the operation should be invoked .
* - if FALSE ( default ), the field types implementation of the operation
* will be invoked ( hook_field_ [ op ])
* - If TRUE , the default field implementation of the field operation
* will be invoked ( field_default_ [ op ])
* Internal use only . Do not explicitely set to TRUE , but use
* _field_invoke_default () instead .
* - 'deleted' : If TRUE , the function will operate on deleted fields
* as well as non - deleted fields . If unset or FALSE , only
* non - deleted fields are operated on .
* - 'language' : A language code or an array of language codes keyed by field
* name . It will be used to narrow down to a single value the available
* languages to act on .
*
* @ see _field_invoke ()
*/
function ctools_field_invoke_field ( $field_name , $op , $entity_type , $entity , & $a = NULL , & $b = NULL , $options = array ()) {
if ( is_array ( $field_name )) {
$instance = $field_name ;
}
else {
list (, , $bundle ) = entity_extract_ids ( $entity_type , $entity );
$instance = field_info_instance ( $entity_type , $field_name , $bundle );
}
if ( empty ( $instance )) {
return ;
}
2021-02-04 12:25:21 +01:00
// Keep the variables consistent regardless if we retrieve the field instance
// ourself, or if one is provided to us via the $field_name variable.
$field = field_info_field ( $instance [ 'field_name' ]);
$field_name = $instance [ 'field_name' ];
2020-08-14 13:36:36 +02:00
// Merge default options.
$default_options = array (
'default' => FALSE ,
'deleted' => FALSE ,
'language' => NULL ,
);
$options += $default_options ;
$return = array ();
// Everything from here is unmodified code from _field_invoke() formerly
// inside a foreach loop over the instances.
$function = $options [ 'default' ] ? 'field_default_' . $op : $field [ 'module' ] . '_field_' . $op ;
if ( function_exists ( $function )) {
// Determine the list of languages to iterate on.
$available_languages = field_available_languages ( $entity_type , $field );
$languages = _field_language_suggestion ( $available_languages , $options [ 'language' ], $field_name );
foreach ( $languages as $langcode ) {
$items = isset ( $entity -> { $field_name }[ $langcode ]) ? $entity -> { $field_name }[ $langcode ] : array ();
$result = $function ( $entity_type , $entity , $field , $instance , $langcode , $items , $a , $b );
if ( isset ( $result )) {
// For hooks with array results, we merge results together.
// For hooks with scalar results, we collect results in an array.
if ( is_array ( $result )) {
$return = array_merge ( $return , $result );
}
else {
$return [] = $result ;
}
}
// Populate $items back in the field values, but avoid replacing missing
// fields with an empty array (those are not equivalent on update).
if ( $items !== array () || isset ( $entity -> { $field_name }[ $langcode ])) {
$entity -> { $field_name }[ $langcode ] = $items ;
}
}
}
return $return ;
}
/**
* Replacement for core _field_invoke_default () to invoke on a single field .
*
* @ see ctools_field_invoke_field ()
* @ see _field_invoke_default ()
*/
function ctools_field_invoke_field_default ( $field_name , $op , $entity_type , $entity , & $a = NULL , & $b = NULL , $options = array ()) {
$options [ 'default' ] = TRUE ;
return ctools_field_invoke_field ( $field_name , $op , $entity_type , $entity , $a , $b , $options );
}
/**
* Returns a list of field definitions of a specified type .
*
* @ param string $field_type
* A field type name ; e . g . 'text' or 'date' .
*
* @ return array
* An array of field definitions of the specified type , keyed by field name .
*/
function ctools_fields_get_fields_by_type ( $field_type ) {
$fields = array ();
foreach ( field_info_fields () as $field_name => $field_info ) {
if ( $field_info [ 'type' ] == $field_type ) {
$fields [ $field_name ] = $field_info ;
}
}
return $fields ;
}
/**
* Derive the foreign keys that a field provides .
*
* @ param $field_name
* The name of the field .
*
* @ return
* An array of foreign keys according to Schema API .
*/
function ctools_field_foreign_keys ( $field_name ) {
$foreign_keys = & drupal_static ( __FUNCTION__ , array ());
if ( ! isset ( $foreign_keys [ $field_name ])) {
$foreign_keys [ $field_name ] = array ();
$field = field_info_field ( $field_name );
if ( ! empty ( $field [ 'foreign keys' ])) {
$foreign_keys [ $field_name ] = $field [ 'foreign keys' ];
}
else {
// Try to fetch foreign keys from schema, as not everything
// stores foreign keys properly in the field info.
$module = $field [ 'module' ];
module_load_install ( $module );
$schema = module_invoke ( $module , 'field_schema' , $field );
if ( ! empty ( $schema [ 'foreign keys' ])) {
$foreign_keys [ $field_name ] = $schema [ 'foreign keys' ];
}
}
}
return $foreign_keys [ $field_name ];
}