summaryrefslogtreecommitdiff
path: root/modules/field_ui/field_ui.module
blob: 00db4681c397879d64d0883415963d22ef9483e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
<?php
// $Id$

/**
 * @file
 * Allows administrators to associate custom fields to fieldable types.
 */

/**
 * Implement hook_help().
 */
function field_ui_help($path, $arg) {
  switch ($path) {
    case 'admin/help#field_ui':
       $output = '';
       $output .= '<p>' . t('The Field UI module provides an administrative interface for adding custom fields to content types, users, comments, and other types of data. In the case of content types, a few fields are provided by default, such as the "Summary and Body" field. The Field UI module lets administrators edit or delete the default fields attached to content, as well as create new fields for storing any additional information. Field configuration is accessible through tabs on the <a href="@content-types">content types administration page</a>. (See the <a href="@node-help">node module help page</a> for more information about content types.)', array('@content-types' => url('admin/content/types'), '@node-help' => url('admin/help/node'))) . '</p>';
       $output .= '<p>' . t('When adding a custom field to a content type, you determine its type (whether it will contain text, numbers, lists, etc.) and how it will be displayed (either as a text field or text area, a select box, checkboxes, radio buttons, or an auto-complete text field). A field may have multiple values (i.e., a "person" may have multiple e-mail addresses) or a single value (i.e., an "employee" has a single employee identification number).') . '</p>';
       $output .= '<p>' . t('Custom field types may be provided by additional modules. Drupal core includes the following field types:') . '</p>';
       $output .= '<ul>';
       $output .= '<li>' . t('<em>Number</em>: Adds numeric field types, in integer, decimal or floating point form. You may define a set of allowed inputs, or specify an allowable range of values. A variety of common formats for displaying numeric data are available.') . '</li>';
       $output .= '<li>' . t("<em>Text</em>: Adds text field types. A text field may contain plain text only, or optionally, may use Drupal's input format filters to securely manage HTML output. Text input fields may be either a single line (text field), multiple lines (text area), or for greater input control, a select box, checkbox, or radio buttons. If desired, CCK can validate the input to a set of allowed values.") . '</li>';
       $output .= '<li>' . t('<em>List</em>: Provides storage mechanisms to store a list of items. Usually these items are input through a select list, checkboxes, or radio buttons.') . '</li>';
       $output .= '</ul>';
      return $output;

    case 'admin/reports/fields':
      return '<p>' . t('This list shows all fields currently in use for easy reference.') . '</p>';
  }
}

/**
 * Implement hook_menu().
 */
function field_ui_menu() {
  $items['admin/reports/fields'] = array(
    'title' => 'Field list',
    'description' => 'Overview of fields on all object types.',
    'page callback' => 'field_ui_fields_list',
    'access arguments' => array('administer content types'),
    'type' => MENU_NORMAL_ITEM,
  );

  // Ensure the following is not executed until field_bundles is working and
  // tables are updated. Needed to avoid errors on initial installation.
  if (defined('MAINTENANCE_MODE')) {
    return $items;
  }
  // Create tabs for all possible bundles.
  foreach (field_info_fieldable_types() as $obj_type => $info) {
    foreach ($info['bundles'] as $bundle_name => $bundle_info) {
      if (isset($bundle_info['admin'])) {
        // Extract informations from the bundle description.
        $path = $bundle_info['admin']['path'];
        $bundle_arg = isset($bundle_info['admin']['bundle argument']) ? $bundle_info['admin']['bundle argument'] : $bundle_name;
        $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));

        $items["$path/fields"] = array(
          'title' => 'Manage fields',
          'page callback' => 'drupal_get_form',
          'page arguments' => array('field_ui_field_overview_form', $obj_type, $bundle_arg),
          'type' => MENU_LOCAL_TASK,
          'weight' => 1,
        ) + $access;
        $instance_position = count(explode('/', $path)) + 1;
        $items["$path/fields/%field_ui_menu"] = array(
          'title callback' => 'field_ui_menu_label',
          'title arguments' => array($instance_position),
          'load arguments' => array($bundle_name),
          'page callback' => 'drupal_get_form',
          'page arguments' => array('field_ui_field_edit_form', $obj_type, $bundle_arg, $instance_position),
          'type' => MENU_LOCAL_TASK,
        ) + $access;
        $items["$path/fields/%field_ui_menu/edit"] = array(
          'title' => 'Edit instance settings',
          'load arguments' => array($bundle_name),
          'page callback' => 'drupal_get_form',
          'page arguments' => array('field_ui_field_edit_form', $obj_type, $bundle_arg, $instance_position),
          'type' => MENU_DEFAULT_LOCAL_TASK,
        ) + $access;
        $items["$path/fields/%field_ui_menu/field-settings"] = array(
          'title' => 'Edit field settings',
          'load arguments' => array($bundle_name),
          'page callback' => 'drupal_get_form',
          'page arguments' => array('field_ui_field_settings_form', $obj_type, $bundle_arg, $instance_position),
          'type' => MENU_LOCAL_TASK,
        ) + $access;
        $items["$path/fields/%field_ui_menu/widget-type"] = array(
          'title' => 'Change widget type',
          'load arguments' => array($bundle_name),
          'page callback' => 'drupal_get_form',
          'page arguments' => array('field_ui_widget_type_form', $obj_type, $bundle_arg, $instance_position),
          'type' => MENU_LOCAL_TASK,
        ) + $access;
        $items["$path/fields/%field_ui_menu/delete"] = array(
          'title' => 'Delete instance',
          'load arguments' => array($bundle_name),
          'page callback' => 'drupal_get_form',
          'page arguments' => array('field_ui_field_delete_form', $obj_type, $bundle_arg, $instance_position),
          'type' => MENU_LOCAL_TASK,
        ) + $access;

        // 'Display fields' tab and context secondary tabs.
        $items["$path/display"] = array(
          'title' => 'Display fields',
          'page callback' => 'drupal_get_form',
          'page arguments' => array('field_ui_display_overview_form', $obj_type, $bundle_arg),
          'type' => MENU_LOCAL_TASK,
          'weight' => 2,
        ) + $access;
        $tabs = field_ui_build_modes_tabs($obj_type);
        foreach ($tabs as $key => $tab) {
          $items["$path/display/$key"] = array(
            'title' => $tab['title'],
            'page arguments' => array('field_ui_display_overview_form', $obj_type, $bundle_arg, $key),
            'type' => $key == 'basic' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
            'weight' => $key == 'basic' ? 0 : 1,
          ) + $access;
        }
      }
    }
  }
  return $items;
}

/**
 * Menu loader; Load a field instance based on its name.
 */
function field_ui_menu_load($field_name, $bundle_name) {
  if ($instance = field_info_instance($field_name, $bundle_name)) {
    return $instance;
  }
  return FALSE;
}

/**
 * Menu title callback; Return a field label based on its instance.
 */
function field_ui_menu_label($instance) {
  return t($instance['label']);
}

/**
 * Implement hook_theme().
 */
function field_ui_theme() {
  return array(
    'field_ui_field_overview_form' => array(
      'arguments' => array('form' => NULL),
      'file' => 'field_ui.admin.inc',
      'template' => 'field_ui-field-overview-form',
    ),
    'field_ui_display_overview_form' => array(
      'arguments' => array('form' => NULL),
      'file' => 'field_ui.admin.inc',
      'template' => 'field_ui-display-overview-form',
    ),
  );
}

/**
 * Group available build modes on tabs on the 'Display fields' page.
 *
 * @todo Remove this completely and use vertical tabs?
 */
function field_ui_build_modes_tabs($obj_type, $tab_selector = NULL) {
  $info = &drupal_static(__FUNCTION__);

  if (!isset($info[$obj_type])) {
    $info[$obj_type] = module_invoke_all('field_ui_build_modes_tabs');
    // Collect titles, and filter out non active modes.
    $active_modes = field_build_modes($obj_type);
    foreach ($info[$obj_type] as $tab => $values) {
      $modes = array();
      foreach ($info[$obj_type][$tab]['build modes'] as $mode) {
        if (isset($active_modes[$mode])) {
          $modes[$mode] = $active_modes[$mode];
        }
      }
      if ($modes) {
        $info[$obj_type][$tab]['build modes'] = $modes;
      }
      else {
        unset($info[$obj_type][$tab]);
      }
    }
  }
  if ($tab_selector) {
    return isset($info[$obj_type][$tab_selector]) ? $info[$obj_type][$tab_selector]['build modes'] : array();
  }
  return $info[$obj_type];
}

/**
 * Implement hook_field_ui_build_modes_tabs() on behalf of other core modules.
 *
 * @return
 *   An array describing the build modes defined by the module, grouped by tabs.
 *
 * A module can add its render modes to a tab defined by another module.
 * Expected format:
 * @code
 *   array(
 *     'tab1' => array(
 *       'title' => t('The human-readable title of the tab'),
 *       'build modes' => array('mymodule_mode1', 'mymodule_mode2'),
 *     ),
 *     'tab2' => array(
 *       // ...
 *     ),
 *   );
 * @endcode
 */
function field_ui_field_ui_build_modes_tabs() {
  $modes = array(
    'basic' => array(
      'title' => t('Basic'),
      'build modes' => array('teaser', 'full'),
    ),
    'rss' => array(
      'title' => t('RSS'),
      'build modes' => array('rss'),
    ),
    'print' => array(
      'title' => t('Print'),
      'build modes' => array('print'),
    ),
    'search' => array(
      'title' => t('Search'),
      'build modes' => array('search_index', 'search_result'),
    ),
  );
  return $modes;
}

/**
 * Updates a field.
 *
 * Field API does not allow field updates, so we create a method here to
 * update a field if no data is created yet.
 *
 * @see field_create_field()
 */
function field_ui_update_field($field) {
  $field_types = field_info_field_types();
  $module = $field_types[$field['type']]['module'];

  $defaults = field_info_field_settings($field['type']);
  $field['settings'] = array_merge($defaults, (array) $field['settings']);
  $data = $field;
  unset($data['id'], $data['columns'], $data['field_name'], $data['type'], $data['locked'], $data['module'], $data['cardinality'], $data['active'], $data['deleted']);
  $field['data'] = $data;

  drupal_write_record('field_config', $field, array('field_name'));

  // Clear caches.
  field_cache_clear(TRUE);
}

/**
 * Implement hook_field_attach_create_bundle().
 */
function field_ui_field_attach_create_bundle($bundle) {
  // When a new bundle is created, the menu needs to be rebuilt to add our
  // menu item tabs.
  menu_rebuild();
}

/**
 * Implement hook_field_attach_rename_bundle().
 */
function field_ui_field_attach_rename_bundle($bundle_old, $bundle_new) {
  if ($bundle_old !== $bundle_new && $extra = variable_get("field_extra_weights_$bundle_old", array())) {
    variable_set("field_extra_weights_$bundle_new", $extra);
    variable_del("field_extra_weights_$bundle_old");
  }
}

/**
 * Implement hook_field_attach_delete_bundle().
 */
function field_ui_field_attach_delete_bundle($bundle) {
  variable_del('field_extra_weights_' . $bundle);
}

/**
 * Helper function to create the right administration path for a bundle.
 */
function _field_ui_bundle_admin_path($bundle_name) {
  $bundles = field_info_bundles();
  $bundle_info = $bundles[$bundle_name];
  return isset($bundle_info['admin']['real path']) ? $bundle_info['admin']['real path'] : $bundle_info['admin']['path'];
}

/**
 * Helper function to identify inactive fields within a bundle.
 */
function field_ui_inactive_instances($bundle_name = NULL) {
  if (!empty($bundle_name)) {
    $inactive = array($bundle_name => array());
    $params = array('bundle' => $bundle_name);
  }
  else {
    $inactive = array();
    $params = array();
  }
  $active_instances = field_info_instances();
  $all_instances = field_read_instances($params, array('include_inactive' => TRUE));
  foreach ($all_instances as $instance) {
    if (!isset($active_instances[$instance['bundle']][$instance['field_name']])) {
      $inactive[$instance['bundle']][$instance['field_name']] = $instance;
    }
  }
  if (!empty($bundle_name)) {
    return $inactive[$bundle_name];
  }
  return $inactive;
}