diff options
Diffstat (limited to 'modules/field')
-rw-r--r-- | modules/field/field.api.php | 57 | ||||
-rw-r--r-- | modules/field/field.attach.inc | 30 | ||||
-rw-r--r-- | modules/field/field.crud.inc | 2 | ||||
-rw-r--r-- | modules/field/field.default.inc | 7 | ||||
-rw-r--r-- | modules/field/field.form.inc | 10 | ||||
-rw-r--r-- | modules/field/field.info.inc | 6 | ||||
-rw-r--r-- | modules/field/field.module | 95 | ||||
-rw-r--r-- | modules/field/modules/list/list.module | 159 | ||||
-rw-r--r-- | modules/field/modules/number/number.module | 76 | ||||
-rw-r--r-- | modules/field/modules/text/text.module | 74 |
10 files changed, 466 insertions, 50 deletions
diff --git a/modules/field/field.api.php b/modules/field/field.api.php index 39618ea08..f8d2137d9 100644 --- a/modules/field/field.api.php +++ b/modules/field/field.api.php @@ -101,6 +101,63 @@ function hook_fieldable_info_alter(&$info) { } /** + * Expose "pseudo-field" components on fieldable objects. + * + * Field UI's 'Manage fields' page lets users re-order fields, but also + * non-field components. For nodes, that would be title, menu settings, or + * other elements exposed by contributed modules through hook_form() or + * hook_form_alter(). + * + * Fieldable entities or contributed modules that want to have their components + * supported should expose them using this hook, and use + * field_attach_extra_weight() to retrieve the user-defined weight when + * inserting the component. + * + * @param $bundle + * The name of the bundle being considered. + * @return + * An array of 'pseudo-field' components. The keys are the name of the element + * as it appears in the form structure. The values are arrays with the + * following key/value pairs: + * - label: The human readable name of the component. + * - description: A short description of the component contents. + * - weight: The default weight of the element. + * - view: (optional) The name of the element as it appears in the rendered + * structure, if different from the name in the form. + */ +function hook_field_extra_fields($bundle) { + $extra = array(); + + if ($type = node_type_get_type($bundle)) { + if ($type->has_title) { + $extra['title'] = array( + 'label' => $type->title_label, + 'description' => t('Node module element.'), + 'weight' => -5, + ); + } + if ($bundle == 'poll' && module_exists('poll')) { + $extra['title'] = array( + 'label' => t('Poll title'), + 'description' => t('Poll module title.'), + 'weight' => -5, + ); + $extra['choice_wrapper'] = array( + 'label' => t('Poll choices'), + 'description' => t('Poll module choices.'), + 'weight' => -4, + ); + $extra['settings'] = array( + 'label' => t('Poll settings'), + 'description' => t('Poll module settings.'), + 'weight' => -3, + ); + } + } + return $extra; +} + +/** * @} End of "ingroup field_fieldable_type" */ diff --git a/modules/field/field.attach.inc b/modules/field/field.attach.inc index 55241f5af..838c6d93c 100644 --- a/modules/field/field.attach.inc +++ b/modules/field/field.attach.inc @@ -457,6 +457,11 @@ function _field_invoke_multiple_default($op, $obj_type, $objects, &$a = NULL, &$ function field_attach_form($obj_type, $object, &$form, &$form_state) { $form += (array) _field_invoke_default('form', $obj_type, $object, $form, $form_state); + // Add custom weight handling. + list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object); + $form['#pre_render'][] = '_field_extra_weights_pre_render'; + $form['#extra_fields'] = field_extra_fields($bundle); + // Let other modules make changes to the form. foreach (module_implements('field_attach_form') as $module) { $function = $module . '_field_attach_form'; @@ -1043,6 +1048,11 @@ function field_attach_view($obj_type, $object, $build_mode = 'full') { $output = _field_invoke_default('view', $obj_type, $object, $build_mode); + // Add custom weight handling. + list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object); + $output['#pre_render'][] = '_field_extra_weights_pre_render'; + $output['#extra_fields'] = field_extra_fields($bundle); + // Let other modules make changes after rendering the view. drupal_alter('field_attach_view', $output, $obj_type, $object, $build_mode); @@ -1051,6 +1061,24 @@ function field_attach_view($obj_type, $object, $build_mode = 'full') { } /** + * Retrieve the user-defined weight for a 'pseudo-field' component. + * + * @param $bundle + * The bundle name. + * @param $pseudo_field + * The name of the 'pseudo-field'. + * @return + * The weight for the 'pseudo-field', respecting the user settings stored by + * field.module. + */ +function field_attach_extra_weight($bundle, $pseudo_field) { + $extra = field_extra_fields($bundle); + if (isset($extra[$pseudo_field])) { + return $extra[$pseudo_field]['weight']; + } +} + +/** * Implement hook_node_prepare_translation. * * TODO D7: We do not yet know if this really belongs in Field API. @@ -1084,7 +1112,7 @@ function field_attach_create_bundle($bundle) { // Clear the cache. field_cache_clear(); - + menu_rebuild(); foreach (module_implements('field_attach_create_bundle') as $module) { $function = $module . '_field_attach_create_bundle'; $function($bundle); diff --git a/modules/field/field.crud.inc b/modules/field/field.crud.inc index 08afcb695..cea858d53 100644 --- a/modules/field/field.crud.inc +++ b/modules/field/field.crud.inc @@ -637,8 +637,8 @@ function field_read_instances($params = array(), $include_additional = array()) foreach ($params as $key => $value) { $query->condition('fci.' . $key, $value); } - $query->condition('fc.active', 1); if (!isset($include_additional['include_inactive']) || !$include_additional['include_inactive']) { + $query->condition('fc.active', 1); $query->condition('fci.widget_active', 1); } if (!isset($include_additional['include_deleted']) || !$include_additional['include_deleted']) { diff --git a/modules/field/field.default.inc b/modules/field/field.default.inc index 927571e84..2c5e175b4 100644 --- a/modules/field/field.default.inc +++ b/modules/field/field.default.inc @@ -43,11 +43,8 @@ function field_default_submit($obj_type, $object, $field, $instance, &$items, $f function field_default_insert($obj_type, $object, $field, $instance, &$items) { // _field_invoke() populates $items with an empty array if the $object has no // entry for the field, so we check on the $object itself. - if (!property_exists($object, $field['field_name']) && !empty($instance['default_value_function'])) { - $function = $instance['default_value_function']; - if (drupal_function_exists($function)) { - $items = $function($obj_type, $object, $field, $instance); - } + if (empty($object) || !property_exists($object, $field['field_name'])) { + $items = field_get_default_value($obj_type, $object, $field, $instance); } } /** diff --git a/modules/field/field.form.inc b/modules/field/field.form.inc index 10f687a56..0bc155bc4 100644 --- a/modules/field/field.form.inc +++ b/modules/field/field.form.inc @@ -37,13 +37,9 @@ function field_default_form($obj_type, $object, $field, $instance, $items, &$for 'instance' => $instance, ); - // Populate widgets with default values if we're creating a new object. - if (empty($items) && empty($id) && !empty($instance['default_value_function'])) { - $items = array(); - $function = $instance['default_value_function']; - if (drupal_function_exists($function)) { - $items = $function($obj_type, $object, $field, $instance); - } + // Populate widgets with default values when creating a new object. + if (empty($items) && empty($id)) { + $items = field_get_default_value($obj_type, $object, $field, $instance); } $form_element = array(); diff --git a/modules/field/field.info.inc b/modules/field/field.info.inc index 5507f548d..685edd2d4 100644 --- a/modules/field/field.info.inc +++ b/modules/field/field.info.inc @@ -25,6 +25,7 @@ */ function _field_info_cache_clear() { _field_info_collate_types(TRUE); + drupal_static_reset('field_build_modes'); _field_info_collate_fields(TRUE); } @@ -263,6 +264,11 @@ function _field_info_prepare_instance($instance, $field) { // Make sure all expected instance settings are present. $instance['settings'] += field_info_instance_settings($field['type']); + // Set a default value for the instance. + if (field_behaviors_widget('default value', $instance) == FIELD_BEHAVIOR_DEFAULT && !isset($instance['default_value'])) { + $instance['default_value'] = NULL; + } + // Fallback to default widget if widget type is not available. if (!field_info_widget_types($instance['widget']['type'])) { $instance['widget']['type'] = $field_type['default_widget']; diff --git a/modules/field/field.module b/modules/field/field.module index 2be05058d..e1e03b554 100644 --- a/modules/field/field.module +++ b/modules/field/field.module @@ -266,6 +266,32 @@ function field_associate_fields($module) { } /** + * Helper function to get the default value for a field on an object. + * + * @param $obj_type + * The type of $object; e.g. 'node' or 'user'. + * @param $object + * The object for the operation. + * @param $field + * The field structure. + * @param $instance + * The instance structure. + */ +function field_get_default_value($obj_type, $object, $field, $instance) { + $items = array(); + if (!empty($instance['default_value_function'])) { + $function = $instance['default_value_function']; + if (drupal_function_exists($function)) { + $items = $function($obj_type, $object, $field, $instance); + } + } + elseif (!empty($instance['default_value'])) { + $items = $instance['default_value']; + } + return $items; +} + +/** * Helper function to filter out empty values. * * On order to keep marker rows in the database, the function ensures @@ -279,15 +305,15 @@ function field_associate_fields($module) { * TODO D7: poorly named... */ function field_set_empty($field, $items) { - // Filter out empty values. - $filtered = array(); $function = $field['module'] . '_field_is_empty'; + // We ensure the function is loaded, but explicitly break if it is missing. + drupal_function_exists($function); foreach ((array) $items as $delta => $item) { - if (!$function($item, $field)) { - $filtered[] = $item; + if ($function($item, $field)) { + unset($items[$delta]); } } - return $filtered; + return array_values($items); } /** @@ -335,7 +361,7 @@ function _field_sort_items_value_helper($a, $b) { * Registry of available build modes. */ function field_build_modes($obj_type) { - static $info; + $info = &drupal_static(__FUNCTION__, array()); if (!isset($info[$obj_type])) { $info[$obj_type] = module_invoke_all('field_build_modes', $obj_type); @@ -344,6 +370,63 @@ function field_build_modes($obj_type) { } /** + * Registry of pseudo-field components in a given bundle. + * + * @param $bundle_name + * The bundle name. + * @return + * The array of pseudo-field elements in the bundle. + */ +function field_extra_fields($bundle_name) { + $info = &drupal_static(__FUNCTION__, array()); + + if (empty($info)) { + $info = array(); + $bundles = field_info_bundles(); + foreach ($bundles as $bundle => $bundle_label) { + // Gather information about non-field object additions. + $extra = module_invoke_all('field_extra_fields', $bundle); + drupal_alter('field_extra_fields', $extra, $bundle); + + // Add saved weights. + foreach (variable_get("field_extra_weights_$bundle", array()) as $key => $value) { + // Some stored entries might not exist anymore, for instance if uploads + // have been disabled or vocabularies were deleted. + if (isset($extra[$key])) { + $extra[$key]['weight'] = $value; + } + } + $info[$bundle] = $extra; + } + } + if (array_key_exists($bundle_name, $info)) { + return $info[$bundle_name]; + } + else { + return array(); + } +} + +/** + * Pre-render callback to adjust weights of non-field elements on objects. + */ +function _field_extra_weights_pre_render($elements) { + if (isset($elements['#extra_fields'])) { + foreach ($elements['#extra_fields'] as $key => $value) { + // Some core 'fields' use a different key in node forms and in 'view' + // render arrays. Ensure that we are not on a form first. + if (!isset($elements['#build_id']) && isset($value['view']) && isset($elements[$value['view']])) { + $elements[$value['view']]['#weight'] = $value['weight']; + } + elseif (isset($elements[$key])) { + $elements[$key]['#weight'] = $value['weight']; + } + } + } + return $elements; +} + +/** * Clear the cached information; called in several places when field * information is changed. */ diff --git a/modules/field/modules/list/list.module b/modules/field/modules/list/list.module index 409cec782..f7401b837 100644 --- a/modules/field/modules/list/list.module +++ b/modules/field/modules/list/list.module @@ -28,28 +28,28 @@ function list_field_info() { 'list' => array( 'label' => t('List'), 'description' => t('This field stores numeric keys from key/value lists of allowed values where the key is a simple alias for the position of the value, i.e. 0|First option, 1|Second option, 2|Third option.'), - 'settings' => array('allowed_values_function' => ''), + 'settings' => array('allowed_values' => '', 'allowed_values_function' => ''), 'default_widget' => 'options_select', 'default_formatter' => 'list_default', ), 'list_boolean' => array( 'label' => t('Boolean'), 'description' => t('This field stores simple on/off or yes/no options.'), - 'settings' => array('allowed_values_function' => ''), + 'settings' => array('allowed_values' => '', 'allowed_values_function' => ''), 'default_widget' => 'options_select', 'default_formatter' => 'list_default', ), 'list_number' => array( 'label' => t('List (numeric)'), 'description' => t('This field stores keys from key/value lists of allowed numbers where the stored numeric key has significance and must be preserved, i.e. \'Lifetime in days\': 1|1 day, 7|1 week, 31|1 month.'), - 'settings' => array('allowed_values_function' => ''), + 'settings' => array('allowed_values' => '', 'allowed_values_function' => ''), 'default_widget' => 'options_select', 'default_formatter' => 'list_default', ), 'list_text' => array( 'label' => t('List (text)'), 'description' => t('This field stores keys from key/value lists of allowed values where the stored key has significance and must be a varchar, i.e. \'US States\': IL|Illinois, IA|Iowa, IN|Indiana'), - 'settings' => array('allowed_values_function' => ''), + 'settings' => array('allowed_values' => '', 'allowed_values_function' => ''), 'default_widget' => 'options_select', 'default_formatter' => 'list_default', ), @@ -98,6 +98,131 @@ function list_field_schema($field) { } /** + * Implement hook_field_settings_form(). + */ +function list_field_settings_form($field, $instance) { + $settings = $field['settings']; + + $form['allowed_values'] = array( + '#type' => 'textarea', + '#title' => t('Allowed values list'), + '#default_value' => $settings['allowed_values'], + '#required' => FALSE, + '#rows' => 10, + '#description' => '<p>' . t('The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database, and must be a %type value. The label is optional, and the key will be used as the label if no label is specified.', array('%type' => $field['type'] == 'list_text' ? t('text') : t('numeric'))) . '</p>', + '#element_validate' => array('list_allowed_values_validate'), + '#list_field_type' => $field['type'], + '#access' => empty($settings['allowed_values_function']), + ); + + // Alter the description for allowed values depending on the widget type. + if ($instance['widget']['type'] == 'options_onoff') { + $form['allowed_values']['#description'] .= '<p>' . t("For a 'single on/off checkbox' widget, define the 'off' value first, then the 'on' value in the <strong>Allowed values</strong> section. Note that the checkbox will be labeled with the label of the 'on' value.") . '</p>'; + } + elseif ($instance['widget']['type'] == 'options_buttons') { + $form['allowed_values']['#description'] .= '<p>' . t("The 'checkboxes/radio buttons' widget will display checkboxes if the <em>Number of values</em> option is greater than 1 for this field, otherwise radios will be displayed.") . '</p>'; + } + $form['allowed_values']['#description'] .= t('Allowed HTML tags in labels: @tags', array('@tags' => _field_filter_xss_display_allowed_tags())); + + $form['allowed_values_function'] = array( + '#type' => 'value', + '#value' => $settings['allowed_values_function'], + ); + $form['allowed_values_function_display'] = array( + '#type' => 'item', + '#title' => t('Allowed values list'), + '#markup' => t('The value of this field is being determined by the %function function and may not be changed.', array('%function' => $settings['allowed_values_function'])), + '#access' => !empty($settings['allowed_values_function']), + ); + + return $form; +} + +/** + * Create an array of allowed values for this field. + */ +function list_allowed_values($field) { + $allowed_values = drupal_static(__FUNCTION__, array()); + + if (isset($allowed_values[$field['field_name']])) { + return $allowed_values[$field['field_name']]; + } + + $allowed_values[$field['field_name']] = array(); + + $function = $field['settings']['allowed_values_function']; + if (!empty($function) && drupal_function_exists($function)) { + $allowed_values[$field['field_name']] = $function($field); + } + elseif (!empty($field['settings']['allowed_values'])) { + $allowed_values[$field['field_name']] = list_allowed_values_list($field['settings']['allowed_values'], $field['type'] == 'list'); + } + + return $allowed_values[$field['field_name']]; +} + +/** + * Create an array of the allowed values for this field. + * + * Explode a string with keys and labels separated with '|' and with each new + * value on its own line. + * + * @param $string_values + * The list of choices as a string. + * @param $position_keys + * Boolean value indicating whether to generate keys based on the position of + * the value if a key is not manually specified, effectively generating + * integer-based keys. This should only be TRUE for fields that have a type of + * "list". Otherwise the value will be used as the key if not specified. + */ +function list_allowed_values_list($string_values, $position_keys = FALSE) { + $allowed_values = array(); + + $list = explode("\n", $string_values); + $list = array_map('trim', $list); + $list = array_filter($list, 'strlen'); + foreach ($list as $key => $value) { + // Sanitize the user input with a permissive filter. + $value = field_filter_xss($value); + + // Check for a manually specified key. + if (strpos($value, '|') !== FALSE) { + list($key, $value) = explode('|', $value); + } + // Otherwise see if we need to use the value as the key. The "list" type + // will automatically convert non-keyed lines to integers. + elseif (!$position_keys) { + $key = $value; + } + $allowed_values[$key] = (isset($value) && $value !== '') ? $value : $key; + } + + return $allowed_values; +} + +/** + * Element validate callback; check that the entered values are valid. + */ +function list_allowed_values_validate($element, &$form_state) { + $values = list_allowed_values_list($element['#value'], $element['#list_field_type'] == 'list'); + $field_type = $element['#list_field_type']; + foreach ($values as $key => $value) { + if ($field_type == 'list_number' && !is_numeric($key)) { + form_error($element, t('The entered available values are not valid. Each key must be a valid integer or decimal.')); + break; + } + elseif ($field_type == 'list_text' && strlen($key) > 255) { + form_error($element, t('The entered available values are not valid. Each key must be a string less than 255 characters.')); + break; + } + elseif ($field_type == 'list' && (!preg_match('/^-?\d+$/', $key))) { + form_error($element, t('The entered available values are not valid. All specified keys must be integers.')); + break; + } + } +} + +/** * Implement hook_field_validate(). * * Possible error codes: @@ -161,29 +286,3 @@ function theme_field_formatter_list_default($element) { function theme_field_formatter_list_key($element) { return $element['#item']['safe']; } - -/** - * Create an array of the allowed values for this field. - * - * Call the allowed_values_function to retrieve the allowed - * values array. - * - * TODO Rework this to create a method of selecting plugable allowed values lists. - */ -function list_allowed_values($field) { - static $allowed_values; - - if (isset($allowed_values[$field['field_name']])) { - return $allowed_values[$field['field_name']]; - } - - $allowed_values[$field['field_name']] = array(); - - if (isset($field['settings']['allowed_values_function'])) { - $function = $field['settings']['allowed_values_function']; - if (drupal_function_exists($function)) { - $allowed_values[$field['field_name']] = $function($field); - } - } - return $allowed_values[$field['field_name']]; -} diff --git a/modules/field/modules/number/number.module b/modules/field/modules/number/number.module index eb98aed5b..52b3685f3 100644 --- a/modules/field/modules/number/number.module +++ b/modules/field/modules/number/number.module @@ -88,6 +88,82 @@ function number_field_schema($field) { } /** + * Implement hook_field_settings_form(). + */ +function number_field_settings_form($field, $instance) { + $settings = $field['settings']; + $form = array(); + + if ($field['type'] == 'number_decimal') { + $form['precision'] = array( + '#type' => 'select', + '#title' => t('Precision'), + '#options' => drupal_map_assoc(range(10, 32)), + '#default_value' => $settings['precision'], + '#description' => t('The total number of digits to store in the database, including those to the right of the decimal.'), + ); + $form['scale'] = array( + '#type' => 'select', + '#title' => t('Scale'), + '#options' => drupal_map_assoc(range(0, 10)), + '#default_value' => $settings['scale'], + '#description' => t('The number of digits to the right of the decimal.'), + ); + $form['decimal'] = array( + '#type' => 'select', + '#title' => t('Decimal marker'), + '#options' => array( + '.' => 'decimal point', + ',' => 'comma', + ' ' => 'space', + ), + '#default_value' => $settings['decimal'], + '#description' => t('The character users will input to mark the decimal point in forms.'), + ); + } + + return $form; +} + +/** + * Implement hook_field_instance_settings_form(). + */ +function number_field_instance_settings_form($field, $instance) { + $settings = $instance['settings']; + + $form['min'] = array( + '#type' => 'textfield', + '#title' => t('Minimum'), + '#default_value' => $settings['min'], + '#description' => t('The minimum value that should be allowed in this field. Leave blank for no minimum.'), + '#element_validate' => array('_element_validate_number'), + ); + $form['max'] = array( + '#type' => 'textfield', + '#title' => t('Maximum'), + '#default_value' => $settings['max'], + '#description' => t('The maximum value that should be allowed in this field. Leave blank for no maximum.'), + '#element_validate' => array('_element_validate_number'), + ); + $form['prefix'] = array( + '#type' => 'textfield', + '#title' => t('Prefix'), + '#default_value' => $settings['prefix'], + '#size' => 60, + '#description' => t("Define a string that should be prefixed to the value, like '$ ' or '€ '. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."), + ); + $form['suffix'] = array( + '#type' => 'textfield', + '#title' => t('Suffix'), + '#default_value' => $settings['suffix'], + '#size' => 60, + '#description' => t("Define a string that should suffixed to the value, like ' m', ' kb/s'. Leave blank for none. Separate singular and plural values with a pipe ('pound|pounds')."), + ); + + return $form; +} + +/** * Implement hook_field_validate(). * * Possible error codes: diff --git a/modules/field/modules/text/text.module b/modules/field/modules/text/text.module index b123602fd..ba69c310b 100644 --- a/modules/field/modules/text/text.module +++ b/modules/field/modules/text/text.module @@ -129,6 +129,51 @@ function text_field_schema($field) { } /** + * Implement hook_field_settings_form(). + */ +function text_field_settings_form($field, $instance) { + $settings = $field['settings']; + + $form['max_length'] = array( + '#type' => 'textfield', + '#title' => t('Maximum length'), + '#default_value' => $settings['max_length'], + '#required' => FALSE, + '#description' => t('The maximum length of the field in characters. Leave blank for an unlimited size.'), + '#element_validate' => array('_element_validate_integer_positive'), + ); + + return $form; +} + +/** + * Implement hook_field_instance_settings_form(). + */ +function text_field_instance_settings_form($field, $instance) { + $settings = $instance['settings']; + + $form['text_processing'] = array( + '#type' => 'radios', + '#title' => t('Text processing'), + '#default_value' => $settings['text_processing'], + '#options' => array( + t('Plain text'), + t('Filtered text (user selects input format)'), + ), + ); + if ($field['type'] == 'text_with_summary') { + $form['display_summary'] = array( + '#type' => 'checkbox', + '#title' => t('Summary input'), + '#default_value' => $settings['display_summary'], + '#description' => t('This allows authors to input an explicit summary, to be displayed instead of the automatically trimmed text when using the "Summary or trimmed" display format.'), + ); + } + + return $form; +} + +/** * Implement hook_field_validate(). * * Possible error codes: @@ -467,6 +512,35 @@ function text_field_widget_info() { } /** + * Implement hook_field_widget_settings_form(). + */ +function text_field_widget_settings_form($field, $instance) { + $widget = $instance['widget']; + $settings = $widget['settings']; + + if ($widget['type'] == 'text_textfield') { + $form['size'] = array( + '#type' => 'textfield', + '#title' => t('Size of textfield'), + '#default_value' => $settings['size'], + '#required' => TRUE, + '#element_validate' => array('_element_validate_integer_positive'), + ); + } + else { + $form['rows'] = array( + '#type' => 'textfield', + '#title' => t('Rows'), + '#default_value' => $settings['rows'], + '#required' => TRUE, + '#element_validate' => array('_element_validate_integer_positive'), + ); + } + + return $form; +} + +/** * Implement FAPI hook_elements(). * * Any FAPI callbacks needed for individual widgets can be declared here, |