array( 'label' => t('Media browser'), 'field types' => array('file', 'image'), 'settings' => array( 'allowed_types' => array( 'image' => 'image', ), 'browser_plugins' => array(), 'allowed_schemes' => array( 'public' => 'public', ), ), 'behaviors' => array( 'multiple values' => FIELD_BEHAVIOR_CUSTOM, 'default value' => FIELD_BEHAVIOR_NONE, ), ), ); } /** * Implements hook_field_widget_settings_form(). */ function media_field_widget_settings_form($field, $instance) { $widget = $instance['widget']; $settings = $widget['settings']; $plugins = media_get_browser_plugin_info(); $options = array(); foreach ($plugins as $key => $plugin) { $options[$key] = check_plain($plugin['title']); } $form['browser_plugins'] = array( '#type' => 'checkboxes', '#title' => t('Enabled browser plugins'), '#options' => $options, '#default_value' => $settings['browser_plugins'], '#description' => t('Media browser plugins which are allowed for this field. If no plugins are selected, they will all be available.'), ); $form['allowed_types'] = array( '#type' => 'checkboxes', '#title' => t('Allowed file types'), '#options' => file_entity_type_get_names(), '#default_value' => $settings['allowed_types'], '#description' => t('File types which are allowed for this field. If no file types are selected, they will all be available.'), ); $visible_steam_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_VISIBLE); $options = array(); foreach ($visible_steam_wrappers as $scheme => $information) { $options[$scheme] = check_plain($information['name']); } $form['allowed_schemes'] = array( '#type' => 'checkboxes', '#title' => t('Allowed URI schemes'), '#options' => $options, '#default_value' => $settings['allowed_schemes'], '#description' => t('URI schemes which are allowed for this field. If no schemes are selected, they will all be available.'), ); return $form; } /** * Implements hook_field_widget_form(). */ function media_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { $defaults = array( 'fid' => 0, 'display' => !empty($field['settings']['display_default']), 'description' => '', ); // Load the items for form rebuilds from the field state as they might not be // in $form_state['values'] because of validation limitations. Also, they are // only passed in as $items when editing existing entities. $field_state = field_form_get_state($element['#field_parents'], $field['field_name'], $langcode, $form_state); if (isset($field_state['items'])) { $items = $field_state['items']; } $field_settings = $instance['settings']; $widget_settings = $instance['widget']['settings']; // Essentially we use the media type, extended with some enhancements. $element_info = element_info('media'); $element += array( '#type' => 'media', '#value_callback' => 'media_field_widget_value', '#process' => array_merge($element_info['#process'], array('media_field_widget_process')), '#media_options' => array( 'global' => array( 'types' => array_filter($widget_settings['allowed_types']), 'enabledPlugins' => array_filter($instance['widget']['settings']['browser_plugins']), 'schemes' => array_filter($widget_settings['allowed_schemes']), 'file_directory' => isset($field_settings['file_directory']) ? $field_settings['file_directory'] : '', 'file_extensions' => isset($field_settings['file_extensions']) ? $field_settings['file_extensions'] : variable_get('file_entity_default_allowed_extensions', 'jpg jpeg gif png txt doc docx xls xlsx pdf ppt pptx pps ppsx odt ods odp mp3 mov mp4 m4a m4v mpeg avi ogg oga ogv weba webp webm'), 'max_filesize' => isset($field_settings['max_filesize']) ? $field_settings['max_filesize'] : 0, 'uri_scheme' => !empty($field['settings']['uri_scheme']) ? $field['settings']['uri_scheme'] : file_default_scheme(), ), ), // Allows this field to return an array instead of a single value. '#extended' => TRUE, ); // Add image field specific validators. if ($field['type'] == 'image') { if ($field_settings['min_resolution'] || $field_settings['max_resolution']) { $element['#media_options']['global']['min_resolution'] = $field_settings['min_resolution']; $element['#media_options']['global']['max_resolution'] = $field_settings['max_resolution']; } } if ($field['cardinality'] == 1) { // Set the default value. $element['#default_value'] = !empty($items) ? $items[0] : $defaults; // If there's only one field, return it as delta 0. if (empty($element['#default_value']['fid'])) { $element['#description'] = theme('media_upload_help', array('description' => $element['#description'])); } $elements = array($element); } else { // If there are multiple values, add an element for each existing one. foreach ($items as $item) { $elements[$delta] = $element; $elements[$delta]['#default_value'] = $item; $elements[$delta]['#weight'] = $delta; $delta++; } // And then add one more empty row for new uploads except when this is a // programmed form as it is not necessary. if (($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta < $field['cardinality']) && empty($form_state['programmed'])) { $elements[$delta] = $element; $elements[$delta]['#default_value'] = $defaults; $elements[$delta]['#weight'] = $delta; $elements[$delta]['#required'] = ($element['#required'] && $delta == 0); } // The group of elements all-together need some extra functionality // after building up the full list (like draggable table rows). $elements['#file_upload_delta'] = $delta; $elements['#theme'] = 'media_widget_multiple'; $elements['#theme_wrappers'] = array('fieldset'); $elements['#process'] = array('media_field_widget_process_multiple'); $elements['#title'] = $element['#title']; $elements['#description'] = $element['#description']; $elements['#field_name'] = $element['#field_name']; $elements['#language'] = $element['#language']; $elements['#display_field'] = intval(!empty($field['settings']['display_field'])); // Add some properties that will eventually be added to the media upload // field. These are added here so that they may be referenced easily through // a hook_form_alter(). $elements['#file_upload_title'] = t('Attach media'); $elements['#file_upload_description'] = theme('media_upload_help', array('description' => '')); } return $elements; } /** * The #value_callback for the media field element. */ function media_field_widget_value($element, $input = FALSE, $form_state) { if ($input) { // Checkboxes lose their value when empty. // If the display field is present make sure its unchecked value is saved. $field = field_widget_field($element, $form_state); if (empty($input['display'])) { $input['display'] = intval(!empty($field['settings']['display_field'])); } } // We depend on the media element to handle uploads. $return = media_file_value($element, $input, $form_state); // Ensure that all the required properties are returned even if empty. $return += array( 'fid' => 0, 'display' => 1, 'description' => '', ); return $return; } /** * An element #process callback for the media field type. * * Expands the media type to include the description and display fields. */ function media_field_widget_process($element, &$form_state, $form) { $item = $element['#value']; $item['fid'] = $element['fid']['#value']; $field = field_widget_field($element, $form_state); $instance = field_widget_instance($element, $form_state); $settings = $instance['widget']['settings']; $element['#theme'] = 'media_widget'; // Add the display field if enabled. if (!empty($field['settings']['display_field']) && $item['fid']) { $element['display'] = array( '#type' => empty($item['fid']) ? 'hidden' : 'checkbox', '#title' => t('Include file in display'), '#value' => isset($item['display']) ? $item['display'] : $field['settings']['display_default'], '#attributes' => array('class' => array('file-display')), ); } else { $element['display'] = array( '#type' => 'hidden', '#value' => '1', ); } // Add the description field if enabled. if (!empty($instance['settings']['description_field']) && $item['fid']) { $element['description'] = array( '#type' => variable_get('file_description_type', 'textfield'), '#title' => t('Description'), '#value' => isset($item['description']) ? $item['description'] : '', '#maxlength' => variable_get('file_description_length', 128), '#description' => t('The description may be used as the label of the link to the file.'), ); } // Adjust the Ajax settings so that on upload and remove of any individual // file, the entire group of file fields is updated together. if ($field['cardinality'] != 1) { $parents = array_slice($element['#array_parents'], 0, -1); $new_path = 'media/ajax/' . implode('/', $parents) . '/' . $form['form_build_id']['#value']; $field_element = drupal_array_get_nested_value($form, $parents); $new_wrapper = $field_element['#id'] . '-ajax-wrapper'; foreach (element_children($element) as $key) { if (isset($element[$key]['#ajax'])) { $element[$key]['#ajax']['path'] = $new_path; $element[$key]['#ajax']['wrapper'] = $new_wrapper; } } unset($element['#prefix'], $element['#suffix']); } // Add another submit handler to the upload and remove buttons, to implement // functionality needed by the field widget. This submit handler, along with // the rebuild logic in media_field_widget_form() requires the entire field, // not just the individual item, to be valid. foreach (array('attach_button', 'remove_button') as $key) { $element[$key]['#submit'][] = 'media_field_widget_submit'; $element[$key]['#limit_validation_errors'] = array(array_slice($element['#parents'], 0, -1)); } return $element; } /** * An element #process callback for a group of media fields. * * Adds the weight field to each row so it can be ordered and adds a new Ajax * wrapper around the entire group so it can be replaced all at once. */ function media_field_widget_process_multiple($element, &$form_state, $form) { $element_children = element_children($element, TRUE); $count = count($element_children); foreach ($element_children as $delta => $key) { if ($key != $element['#file_upload_delta']) { $description = _media_field_get_description_from_element($element[$key]); $element[$key]['_weight'] = array( '#type' => 'weight', '#title' => $description ? t('Weight for @title', array('@title' => $description)) : t('Weight for new file'), '#title_display' => 'invisible', '#delta' => $count, '#default_value' => $delta, ); } else { // The title needs to be assigned to the attach field so that validation // errors include the correct widget label. $element[$key]['#title'] = $element['#title']; $element[$key]['_weight'] = array( '#type' => 'hidden', '#default_value' => $delta, ); } } // Add a new wrapper around all the elements for Ajax replacement. $element['#prefix'] = '