1 | <?php |
---|
2 | |
---|
3 | /** |
---|
4 | * @file |
---|
5 | * Administrative interface for content type creation. |
---|
6 | */ |
---|
7 | |
---|
8 | |
---|
9 | /** |
---|
10 | * Menu callback; replacement for node_overview_types(). |
---|
11 | */ |
---|
12 | function content_types_overview() { |
---|
13 | $types = node_get_types(); |
---|
14 | $names = node_get_types('names'); |
---|
15 | $header = array(t('Name'), t('Type'), t('Description'), array('data' => t('Operations'), 'colspan' => '4'),); |
---|
16 | $rows = array(); |
---|
17 | |
---|
18 | foreach ($names as $key => $name) { |
---|
19 | $type = $types[$key]; |
---|
20 | if (node_hook($type, 'form')) { |
---|
21 | $type_url_str = str_replace('_', '-', $type->type); |
---|
22 | $row = array( |
---|
23 | check_plain($name), |
---|
24 | check_plain($type->type), |
---|
25 | ); |
---|
26 | // Make the description smaller |
---|
27 | $row[] = array('data' => filter_xss_admin($type->description), 'class' => 'description'); |
---|
28 | // Set the edit column. |
---|
29 | $row[] = array('data' => l(t('edit'), 'admin/content/node-type/'. $type_url_str)); |
---|
30 | // Set links for managing fields. |
---|
31 | // TODO: a hook to allow other content modules to add more stuff? |
---|
32 | $row[] = array('data' => l(t('manage fields'), 'admin/content/node-type/'. $type_url_str .'/fields')); |
---|
33 | // Set the delete column. |
---|
34 | if ($type->custom) { |
---|
35 | $row[] = array('data' => l(t('delete'), 'admin/content/node-type/'. $type_url_str .'/delete')); |
---|
36 | } |
---|
37 | else { |
---|
38 | $row[] = array('data' => ''); |
---|
39 | } |
---|
40 | |
---|
41 | $rows[] = $row; |
---|
42 | } |
---|
43 | } |
---|
44 | |
---|
45 | // Allow external modules alter the table headers and rows. |
---|
46 | foreach (module_implements('content_types_overview_alter') as $module) { |
---|
47 | $function = $module .'_content_types_overview_alter'; |
---|
48 | $function($header, $rows); |
---|
49 | } |
---|
50 | |
---|
51 | if (empty($rows)) { |
---|
52 | $rows[] = array(array('data' => t('No content types available.'), 'colspan' => '7', 'class' => 'message')); |
---|
53 | } |
---|
54 | |
---|
55 | return theme('table', $header, $rows) .theme('content_overview_links'); |
---|
56 | } |
---|
57 | |
---|
58 | function theme_content_overview_links() { |
---|
59 | return '<div class="content-overview-links">'. l(t('» Add a new content type'), 'admin/content/types/add') .'</div>'; |
---|
60 | } |
---|
61 | |
---|
62 | /** |
---|
63 | * Menu callback; lists all defined fields for quick reference. |
---|
64 | */ |
---|
65 | function content_fields_list() { |
---|
66 | $fields = content_fields(); |
---|
67 | $field_types = _content_field_types(); |
---|
68 | |
---|
69 | // Sort fields by field name. |
---|
70 | ksort($fields); |
---|
71 | |
---|
72 | $header = array(t('Field name'), t('Field type'), t('Used in')); |
---|
73 | $rows = array(); |
---|
74 | foreach ($fields as $field) { |
---|
75 | $row = array(); |
---|
76 | $row[] = $field['locked'] ? t('@field_name (Locked)', array('@field_name' => $field['field_name'])) : $field['field_name']; |
---|
77 | $row[] = t($field_types[$field['type']]['label']); |
---|
78 | |
---|
79 | $types = array(); |
---|
80 | $result = db_query("SELECT nt.name, nt.type FROM {". content_instance_tablename() ."} nfi ". |
---|
81 | "LEFT JOIN {node_type} nt ON nt.type = nfi.type_name ". |
---|
82 | "WHERE nfi.field_name = '%s' ". |
---|
83 | // Keep disabled modules out of table. |
---|
84 | "AND nfi.widget_active = 1 ". |
---|
85 | "ORDER BY nt.name ASC", $field['field_name']); |
---|
86 | while ($type = db_fetch_array($result)) { |
---|
87 | $content_type = content_types($type['type']); |
---|
88 | $types[] = l($type['name'], 'admin/content/node-type/'. $content_type['url_str'] .'/fields'); |
---|
89 | } |
---|
90 | $row[] = implode(', ', $types); |
---|
91 | |
---|
92 | $rows[] = array('data' => $row, 'class' => $field['locked'] ? 'menu-disabled' : ''); |
---|
93 | } |
---|
94 | if (empty($rows)) { |
---|
95 | $output = t('No fields have been defined for any content type yet.'); |
---|
96 | } |
---|
97 | else { |
---|
98 | $output = theme('table', $header, $rows); |
---|
99 | } |
---|
100 | return $output; |
---|
101 | } |
---|
102 | |
---|
103 | /** |
---|
104 | * Helper function to display a message about inactive fields. |
---|
105 | */ |
---|
106 | function content_inactive_message($type_name) { |
---|
107 | $inactive_fields = content_inactive_fields($type_name); |
---|
108 | if (!empty($inactive_fields)) { |
---|
109 | $field_types = _content_field_types(); |
---|
110 | $widget_types = _content_widget_types($type_name); |
---|
111 | drupal_set_message(t('This content type has inactive fields. Inactive fields are not included in lists of available fields until their modules are enabled.'), 'error'); |
---|
112 | foreach ($inactive_fields as $field_name => $field) { |
---|
113 | drupal_set_message(t('!field (!field_name) is an inactive !field_type field that uses a !widget_type widget.', array( |
---|
114 | '!field' => $field['widget']['label'], |
---|
115 | '!field_name' => $field['field_name'], |
---|
116 | '!field_type' => array_key_exists($field['type'], $field_types) ? $field_types[$field['type']]['label'] : $field['type'], |
---|
117 | '!widget_type' => array_key_exists($field['widget']['type'], $widget_types) ? $widget_types[$field['widget']['type']]['label'] : $field['widget']['type'], |
---|
118 | ))); |
---|
119 | } |
---|
120 | } |
---|
121 | } |
---|
122 | |
---|
123 | /** |
---|
124 | * Menu callback; listing of fields for a content type. |
---|
125 | * |
---|
126 | * Allows fields to be reordered and nested in fieldgroups using |
---|
127 | * JS drag-n-drop. Non-CCK form elements can also be moved around. |
---|
128 | */ |
---|
129 | function content_field_overview_form(&$form_state, $type_name) { |
---|
130 | |
---|
131 | content_inactive_message($type_name); |
---|
132 | |
---|
133 | // When displaying the form, make sure the list of fields |
---|
134 | // is up-to-date. |
---|
135 | if (empty($form_state['post'])) { |
---|
136 | content_clear_type_cache(); |
---|
137 | } |
---|
138 | |
---|
139 | // Gather type information. |
---|
140 | $type = content_types($type_name); |
---|
141 | $fields = $type['fields']; |
---|
142 | $field_types = _content_field_types(); |
---|
143 | |
---|
144 | $extra = $type['extra']; |
---|
145 | $groups = $group_options = $group_types = array(); |
---|
146 | if (module_exists('fieldgroup')) { |
---|
147 | $groups = fieldgroup_groups($type['type']); |
---|
148 | $group_types = fieldgroup_types(); |
---|
149 | $plain_tree = _fieldgroup_plain_tree($groups); |
---|
150 | $group_options = _fieldgroup_groups_label($type['type']); |
---|
151 | // Add the ability to group under the newly created row. |
---|
152 | $group_options['_add_new_group'] = '_add_new_group'; |
---|
153 | } |
---|
154 | |
---|
155 | // Store the default weights as we meet them, to be able to put the |
---|
156 | //'add new' rows after them. |
---|
157 | $weights = array(); |
---|
158 | |
---|
159 | $form = array( |
---|
160 | '#tree' => TRUE, |
---|
161 | '#type_name' => $type['type'], |
---|
162 | '#fields' => array_keys($fields), |
---|
163 | '#groups' => array_keys($groups), |
---|
164 | '#extra' => array_keys($extra), |
---|
165 | '#field_rows' => array(), |
---|
166 | '#group_rows' => array(), |
---|
167 | ); |
---|
168 | |
---|
169 | // Fields. |
---|
170 | foreach ($fields as $name => $field) { |
---|
171 | $weight = $field['widget']['weight']; |
---|
172 | $form[$name] = array( |
---|
173 | 'label' => array('#value' => check_plain($field['widget']['label'])), |
---|
174 | 'field_name' => array('#value' => $field['field_name']), |
---|
175 | 'type' => array('#value' => t($field_types[$field['type']]['label'])), |
---|
176 | 'configure' => array('#value' => l(t('Configure'), 'admin/content/node-type/'. $type['url_str'] .'/fields/'. $field['field_name'])), |
---|
177 | 'remove' => array('#value' => l(t('Remove'), 'admin/content/node-type/'. $type['url_str'] .'/fields/'. $field['field_name'] .'/remove')), |
---|
178 | 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), |
---|
179 | 'parent' => array('#type' => 'select', '#options' => $group_options, '#default_value' => ''), |
---|
180 | 'prev_parent' => array('#type' => 'hidden', '#value' => ''), |
---|
181 | 'hidden_name' => array('#type' => 'hidden', '#default_value' => $field['field_name']), |
---|
182 | '#leaf' => TRUE, |
---|
183 | '#row_type' => 'field', |
---|
184 | 'field' => array('#type' => 'value', '#value' => $field), |
---|
185 | ); |
---|
186 | if ($field['locked']) { |
---|
187 | $form[$name]['configure'] = array('#value' => t('Locked')); |
---|
188 | $form[$name]['remove'] = array(); |
---|
189 | $form[$name]['#disabled_row'] = TRUE; |
---|
190 | } |
---|
191 | $form['#field_rows'][] = $name; |
---|
192 | $weights[] = $weight; |
---|
193 | } |
---|
194 | |
---|
195 | // Groups. |
---|
196 | foreach ($groups as $name => $group) { |
---|
197 | $current_group_options = $plain_tree; |
---|
198 | unset($current_group_options[$name]); |
---|
199 | $weight = $group['weight']; |
---|
200 | $form[$name] = array( |
---|
201 | 'label' => array('#value' => check_plain($group['label'])), |
---|
202 | 'group_name' => array('#value' => $group['group_name']), |
---|
203 | 'group_type' => array('#value' => t($group_types[$group['group_type']])), |
---|
204 | 'configure' => array('#value' => l(t('Configure'), 'admin/content/node-type/'. $type['url_str'] .'/groups/'. $group['group_name'])), |
---|
205 | 'remove' => array('#value' => l(t('Remove'), 'admin/content/node-type/'. $type['url_str'] .'/groups/'. $group['group_name'] .'/remove')), |
---|
206 | 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), |
---|
207 | 'parent' => array('#type' => 'select', '#options' => $current_group_options, '#default_value' => ''), |
---|
208 | 'prev_parent' => array('#type' => 'hidden', '#value' => ''), |
---|
209 | 'hidden_name' => array('#type' => 'hidden', '#default_value' => $group['group_name']), |
---|
210 | '#row_type' => 'group', |
---|
211 | 'group' => array('#type' => 'value', '#value' => $group), |
---|
212 | ); |
---|
213 | // Adjust child fields rows. |
---|
214 | if (isset($group['fields'])) { |
---|
215 | foreach ($group['fields'] as $field_name => $field) { |
---|
216 | $form[$field_name]['parent']['#default_value'] = $name; |
---|
217 | $form[$field_name]['prev_parent']['#value'] = $name; |
---|
218 | } |
---|
219 | } |
---|
220 | // Adjust child group rows |
---|
221 | $form[$name]['parent']['#default_value'] = $group['parent']; |
---|
222 | $form[$name]['prev_parent']['#value'] = $group['parent']; |
---|
223 | |
---|
224 | $form['#group_rows'][] = $name; |
---|
225 | $weights[] = $weight; |
---|
226 | } |
---|
227 | |
---|
228 | // Non-CCK 'fields'. |
---|
229 | foreach ($extra as $name => $label) { |
---|
230 | $weight = $extra[$name]['weight']; |
---|
231 | $form[$name] = array( |
---|
232 | 'label' => array('#value' => check_plain(t($extra[$name]['label']))), |
---|
233 | 'description' => array('#value' => isset($extra[$name]['description']) ? $extra[$name]['description'] : ''), |
---|
234 | 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), |
---|
235 | 'parent' => array('#type' => 'hidden', '#default_value' => ''), |
---|
236 | 'configure' => array('#value' => isset($extra[$name]['configure']) ? $extra[$name]['configure'] : ''), |
---|
237 | 'remove' => array('#value' => isset($extra[$name]['remove']) ? $extra[$name]['remove'] : ''), |
---|
238 | 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), |
---|
239 | '#leaf' => TRUE, |
---|
240 | '#root' => TRUE, |
---|
241 | '#disabled_row' => TRUE, |
---|
242 | '#row_type' => 'extra', |
---|
243 | ); |
---|
244 | $form['#field_rows'][] = $name; |
---|
245 | $weights[] = $weight; |
---|
246 | } |
---|
247 | |
---|
248 | // Additional row : add new field. |
---|
249 | $weight = max($weights) + 1; |
---|
250 | $field_type_options = content_field_type_options(); |
---|
251 | $widget_type_options = content_widget_type_options(NULL, TRUE); |
---|
252 | if ($field_type_options && $widget_type_options) { |
---|
253 | array_unshift($field_type_options, t('- Select a field type -')); |
---|
254 | array_unshift($widget_type_options, t('- Select a widget -')); |
---|
255 | $name = '_add_new_field'; |
---|
256 | $form[$name] = array( |
---|
257 | 'label' => array( |
---|
258 | '#type' => 'textfield', |
---|
259 | '#size' => 15, |
---|
260 | '#description' => t('Label'), |
---|
261 | ), |
---|
262 | 'field_name' => array( |
---|
263 | '#type' => 'textfield', |
---|
264 | // This field should stay LTR even for RTL languages. |
---|
265 | '#field_prefix' => '<span dir="ltr">field_', |
---|
266 | '#field_suffix' => '</span>‎', |
---|
267 | '#attributes' => array('dir'=>'ltr'), |
---|
268 | '#size' => 15, |
---|
269 | // Field names are limited to 32 characters including the 'field_' |
---|
270 | // prefix which is 6 characters long. |
---|
271 | '#maxlength' => 26, |
---|
272 | '#description' => t('Field name (a-z, 0-9, _)'), |
---|
273 | ), |
---|
274 | 'type' => array( |
---|
275 | '#type' => 'select', |
---|
276 | '#options' => $field_type_options, |
---|
277 | '#description' => theme('advanced_help_topic', 'content', 'fields') . t('Type of data to store.'), |
---|
278 | ), |
---|
279 | 'widget_type' => array( |
---|
280 | '#type' => 'select', |
---|
281 | '#options' => $widget_type_options, |
---|
282 | '#description' => t('Form element to edit the data.'), |
---|
283 | ), |
---|
284 | 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), |
---|
285 | 'parent' => array('#type' => 'select', '#options' => $group_options, '#default_value' => ''), |
---|
286 | 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), |
---|
287 | '#leaf' => TRUE, |
---|
288 | '#add_new' => TRUE, |
---|
289 | '#row_type' => 'add_new_field', |
---|
290 | ); |
---|
291 | $form['#field_rows'][] = $name; |
---|
292 | } |
---|
293 | |
---|
294 | // Additional row : add existing field. |
---|
295 | $existing_field_options = content_existing_field_options($type_name); |
---|
296 | if ($existing_field_options && $widget_type_options) { |
---|
297 | $weight++; |
---|
298 | array_unshift($existing_field_options, t('- Select an existing field -')); |
---|
299 | $name = '_add_existing_field'; |
---|
300 | $form[$name] = array( |
---|
301 | 'label' => array( |
---|
302 | '#type' => 'textfield', |
---|
303 | '#size' => 15, |
---|
304 | '#description' => t('Label'), |
---|
305 | ), |
---|
306 | 'field_name' => array( |
---|
307 | '#type' => 'select', |
---|
308 | '#options' => $existing_field_options, |
---|
309 | '#description' => t('Field to share'), |
---|
310 | ), |
---|
311 | 'widget_type' => array( |
---|
312 | '#type' => 'select', |
---|
313 | '#options' => $widget_type_options, |
---|
314 | '#description' => t('Form element to edit the data.'), |
---|
315 | ), |
---|
316 | 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), |
---|
317 | 'parent' => array('#type' => 'select', '#options' => $group_options, '#default_value' => ''), |
---|
318 | 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), |
---|
319 | '#leaf' => TRUE, |
---|
320 | '#add_new' => TRUE, |
---|
321 | '#row_type' => 'add_existing_field', |
---|
322 | ); |
---|
323 | $form['#field_rows'][] = $name; |
---|
324 | } |
---|
325 | |
---|
326 | // Additional row : add new group. |
---|
327 | if (!empty($group_types)) { |
---|
328 | $current_group_options = $group_options; |
---|
329 | $options = fieldgroup_types(); |
---|
330 | unset($current_group_options['_add_new_group']); |
---|
331 | $weight++; |
---|
332 | $name = '_add_new_group'; |
---|
333 | $form[$name] = array( |
---|
334 | 'label' => array( |
---|
335 | '#type' => 'textfield', |
---|
336 | '#size' => 15, |
---|
337 | '#description' => t('Label'), |
---|
338 | ), |
---|
339 | 'group_name' => array( |
---|
340 | '#type' => 'textfield', |
---|
341 | // This field should stay LTR even for RTL languages. |
---|
342 | '#field_prefix' => '<span dir="ltr">group_', |
---|
343 | '#field_suffix' => '</span>‎', |
---|
344 | '#attributes' => array('dir'=>'ltr'), |
---|
345 | '#size' => 15, |
---|
346 | // Group names are limited to 32 characters including the 'group_' |
---|
347 | // prefix which is 6 characters long. |
---|
348 | '#maxlength' => 26, |
---|
349 | '#description' => t('Group name (a-z, 0-9, _)'), |
---|
350 | ), |
---|
351 | 'group_option' => array( |
---|
352 | '#type' => 'hidden', |
---|
353 | '#value' => '', |
---|
354 | ), |
---|
355 | 'group_type' => array( |
---|
356 | '#type' => 'hidden', |
---|
357 | '#value' => 'standard', |
---|
358 | ), |
---|
359 | 'weight' => array('#type' => 'textfield', '#default_value' => $weight, '#size' => 3), |
---|
360 | 'parent' => array('#type' => 'select', '#options' => $current_group_options, '#default_value' => ''), |
---|
361 | 'prev_parent' => array('#type' => 'hidden', '#value' => ''), |
---|
362 | 'hidden_name' => array('#type' => 'hidden', '#default_value' => $name), |
---|
363 | '#add_new' => TRUE, |
---|
364 | '#row_type' => 'add_new_group', |
---|
365 | ); |
---|
366 | if (count($group_types) > 1) { |
---|
367 | $form[$name]['group_type'] = array( |
---|
368 | '#type' => 'select', |
---|
369 | '#description' => t('Type of group.'), |
---|
370 | '#options' => $group_types, |
---|
371 | '#default_value' => 'standard', |
---|
372 | ); |
---|
373 | } |
---|
374 | $form['#group_rows'][] = $name; |
---|
375 | } |
---|
376 | |
---|
377 | $form['submit'] = array('#type' => 'submit', '#value' => t('Save')); |
---|
378 | return $form; |
---|
379 | } |
---|
380 | |
---|
381 | function content_field_overview_form_validate($form, &$form_state) { |
---|
382 | _content_field_overview_form_validate_add_new($form, $form_state); |
---|
383 | _content_field_overview_form_validate_add_existing($form, $form_state); |
---|
384 | } |
---|
385 | |
---|
386 | /** |
---|
387 | * Helper function for content_field_overview_form_validate. |
---|
388 | * |
---|
389 | * Validate the 'add new field' row. |
---|
390 | */ |
---|
391 | function _content_field_overview_form_validate_add_new($form, &$form_state) { |
---|
392 | $field = $form_state['values']['_add_new_field']; |
---|
393 | |
---|
394 | // Validate if any information was provided in the 'add new field' row. |
---|
395 | if (array_filter(array($field['label'], $field['field_name'], $field['type'], $field['widget_type']))) { |
---|
396 | // No label. |
---|
397 | if (!$field['label']) { |
---|
398 | form_set_error('_add_new_field][label', t('Add new field: you need to provide a label.')); |
---|
399 | } |
---|
400 | |
---|
401 | // No field name. |
---|
402 | if (!$field['field_name']) { |
---|
403 | form_set_error('_add_new_field][field_name', t('Add new field: you need to provide a field name.')); |
---|
404 | } |
---|
405 | // Field name validation. |
---|
406 | else { |
---|
407 | $field_name = $field['field_name']; |
---|
408 | |
---|
409 | // Add the 'field_' prefix. |
---|
410 | if (substr($field_name, 0, 6) != 'field_') { |
---|
411 | $field_name = 'field_'. $field_name; |
---|
412 | form_set_value($form['_add_new_field']['field_name'], $field_name, $form_state); |
---|
413 | } |
---|
414 | |
---|
415 | // Invalid field name. |
---|
416 | if (!preg_match('!^field_[a-z0-9_]+$!', $field_name)) { |
---|
417 | form_set_error('_add_new_field][field_name', t('Add new field: the field name %field_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', array('%field_name' => $field_name))); |
---|
418 | } |
---|
419 | if (strlen($field_name) > 32) { |
---|
420 | form_set_error('_add_new_field][field_name', t('Add new field: the field name %field_name is too long. The name is limited to 32 characters, including the \'field_\' prefix.', array('%field_name' => $field_name))); |
---|
421 | } |
---|
422 | // A field named 'field_instance' would cause a tablename clash with {content_field_instance} |
---|
423 | if ($field_name == 'field_instance') { |
---|
424 | form_set_error('_add_new_field][field_name', t("Add new field: the name 'field_instance' is a reserved name.")); |
---|
425 | } |
---|
426 | |
---|
427 | // Field name already exists. |
---|
428 | // We need to check inactive fields as well, so we can't use content_fields(). |
---|
429 | module_load_include('inc', 'content', 'includes/content.crud'); |
---|
430 | $fields = content_field_instance_read(array(), TRUE); |
---|
431 | $used = FALSE; |
---|
432 | foreach ($fields as $existing_field) { |
---|
433 | $used |= ($existing_field['field_name'] == $field_name); |
---|
434 | } |
---|
435 | if ($used) { |
---|
436 | form_set_error('_add_new_field][field_name', t('Add new field: the field name %field_name already exists.', array('%field_name' => $field_name))); |
---|
437 | } |
---|
438 | } |
---|
439 | |
---|
440 | // No field type. |
---|
441 | if (!$field['type']) { |
---|
442 | form_set_error('_add_new_field][type', t('Add new field: you need to select a field type.')); |
---|
443 | } |
---|
444 | |
---|
445 | // No widget type. |
---|
446 | if (!$field['widget_type']) { |
---|
447 | form_set_error('_add_new_field][widget_type', t('Add new field: you need to select a widget.')); |
---|
448 | } |
---|
449 | // Wrong widget type. |
---|
450 | elseif ($field['type']) { |
---|
451 | $widget_types = content_widget_type_options($field['type']); |
---|
452 | if (!isset($widget_types[$field['widget_type']])) { |
---|
453 | form_set_error('_add_new_field][widget_type', t('Add new field: invalid widget.')); |
---|
454 | } |
---|
455 | } |
---|
456 | } |
---|
457 | } |
---|
458 | |
---|
459 | /** |
---|
460 | * Helper function for content_field_overview_form_validate. |
---|
461 | * |
---|
462 | * Validate the 'add existing field' row. |
---|
463 | */ |
---|
464 | function _content_field_overview_form_validate_add_existing($form, &$form_state) { |
---|
465 | // The form element might be absent if no existing fields can be added to |
---|
466 | // this content type |
---|
467 | if (isset($form_state['values']['_add_existing_field'])) { |
---|
468 | $field = $form_state['values']['_add_existing_field']; |
---|
469 | |
---|
470 | // Validate if any information was provided in the 'add existing field' row. |
---|
471 | if (array_filter(array($field['label'], $field['field_name'], $field['widget_type']))) { |
---|
472 | // No label. |
---|
473 | if (!$field['label']) { |
---|
474 | form_set_error('_add_existing_field][label', t('Add existing field: you need to provide a label.')); |
---|
475 | } |
---|
476 | |
---|
477 | // No existing field. |
---|
478 | if (!$field['field_name']) { |
---|
479 | form_set_error('_add_existing_field][field_name', t('Add existing field: you need to select a field.')); |
---|
480 | } |
---|
481 | |
---|
482 | // No widget type. |
---|
483 | if (!$field['widget_type']) { |
---|
484 | form_set_error('_add_existing_field][widget_type', t('Add existing field: you need to select a widget.')); |
---|
485 | } |
---|
486 | // Wrong widget type. |
---|
487 | elseif ($field['field_name'] && ($existing_field = content_fields($field['field_name']))) { |
---|
488 | $widget_types = content_widget_type_options($existing_field['type']); |
---|
489 | if (!isset($widget_types[$field['widget_type']])) { |
---|
490 | form_set_error('_add_existing_field][widget_type', t('Add existing field: invalid widget.')); |
---|
491 | } |
---|
492 | } |
---|
493 | } |
---|
494 | } |
---|
495 | } |
---|
496 | |
---|
497 | function content_field_overview_form_submit($form, &$form_state) { |
---|
498 | $form_values = $form_state['values']; |
---|
499 | |
---|
500 | $type_name = $form['#type_name']; |
---|
501 | $type = content_types($type_name); |
---|
502 | |
---|
503 | // Update field weights. |
---|
504 | $extra = array(); |
---|
505 | foreach ($form_values as $key => $values) { |
---|
506 | // Groups are handled in fieldgroup_content_overview_form_submit(). |
---|
507 | if (in_array($key, $form['#fields'])) { |
---|
508 | db_query("UPDATE {". content_instance_tablename() ."} SET weight = %d WHERE type_name = '%s' AND field_name = '%s'", |
---|
509 | $values['weight'], $type_name, $key); |
---|
510 | } |
---|
511 | elseif (in_array($key, $form['#extra'])) { |
---|
512 | $extra[$key] = $values['weight']; |
---|
513 | } |
---|
514 | } |
---|
515 | |
---|
516 | if ($extra) { |
---|
517 | variable_set('content_extra_weights_'. $type_name, $extra); |
---|
518 | } |
---|
519 | else { |
---|
520 | variable_del('content_extra_weights_'. $type_name); |
---|
521 | } |
---|
522 | |
---|
523 | content_clear_type_cache(); |
---|
524 | |
---|
525 | $destinations = array(); |
---|
526 | |
---|
527 | // Create new field. |
---|
528 | if (!empty($form_values['_add_new_field']['field_name'])) { |
---|
529 | $field = $form_values['_add_new_field']; |
---|
530 | $field['type_name'] = $type_name; |
---|
531 | |
---|
532 | module_load_include('inc', 'content', 'includes/content.crud'); |
---|
533 | if (content_field_instance_create($field)) { |
---|
534 | // Store new field information for fieldgroup submit handler. |
---|
535 | $form_state['fields_added']['_add_new_field'] = $field['field_name']; |
---|
536 | $destinations[] = 'admin/content/node-type/'. $type['url_str'] .'/fields/'. $field['field_name']; |
---|
537 | } |
---|
538 | else { |
---|
539 | drupal_set_message(t('There was a problem creating field %label.', array( |
---|
540 | '%label' => $field['label']))); |
---|
541 | } |
---|
542 | } |
---|
543 | |
---|
544 | // Add existing field. |
---|
545 | if (!empty($form_values['_add_existing_field']['field_name'])) { |
---|
546 | $field = $form_values['_add_existing_field']; |
---|
547 | $field['type_name'] = $type_name; |
---|
548 | $existing_field = content_fields($field['field_name']); |
---|
549 | |
---|
550 | if ($existing_field['locked']) { |
---|
551 | drupal_set_message(t('The field %label cannot be added to a content type because it is locked.', array('%label' => $field['field_name']))); |
---|
552 | } |
---|
553 | else { |
---|
554 | module_load_include('inc', 'content', 'includes/content.crud'); |
---|
555 | if (content_field_instance_create($field)) { |
---|
556 | // Store new field information for fieldgroup submit handler. |
---|
557 | $form_state['fields_added']['_add_existing_field'] = $field['field_name']; |
---|
558 | $destinations[] = 'admin/content/node-type/'. $type['url_str'] .'/fields/'. $field['field_name']; |
---|
559 | } |
---|
560 | else { |
---|
561 | drupal_set_message(t('There was a problem adding field %label.', array('%label' => $field['field_name']))); |
---|
562 | } |
---|
563 | } |
---|
564 | } |
---|
565 | |
---|
566 | if ($destinations) { |
---|
567 | $destinations[] = urldecode(substr(drupal_get_destination(), 12)); |
---|
568 | unset($_REQUEST['destination']); |
---|
569 | $form_state['redirect'] = content_get_destinations($destinations); |
---|
570 | } |
---|
571 | |
---|
572 | } |
---|
573 | |
---|
574 | /** |
---|
575 | * Menu callback; presents a listing of fields display settings for a content type. |
---|
576 | * |
---|
577 | * Form includes form widgets to select which fields appear for teaser, full node |
---|
578 | * and how the field labels should be rendered. |
---|
579 | */ |
---|
580 | function content_display_overview_form(&$form_state, $type_name, $contexts_selector = 'basic') { |
---|
581 | content_inactive_message($type_name); |
---|
582 | |
---|
583 | // Gather type information. |
---|
584 | $type = content_types($type_name); |
---|
585 | $field_types = _content_field_types(); |
---|
586 | $fields = $type['fields']; |
---|
587 | |
---|
588 | $groups = array(); |
---|
589 | if (module_exists('fieldgroup')) { |
---|
590 | $groups = fieldgroup_groups($type['type']); |
---|
591 | } |
---|
592 | $contexts = content_build_modes($contexts_selector); |
---|
593 | |
---|
594 | $form = array( |
---|
595 | '#tree' => TRUE, |
---|
596 | '#type_name' => $type['type'], |
---|
597 | '#fields' => array_keys($fields), |
---|
598 | '#groups' => array_keys($groups), |
---|
599 | '#contexts' => $contexts_selector, |
---|
600 | ); |
---|
601 | |
---|
602 | if (empty($fields)) { |
---|
603 | drupal_set_message(t('There are no fields configured for this content type. You can add new fields on the <a href="@link">Manage fields</a> page.', array( |
---|
604 | '@link' => url('admin/content/node-type/'. $type['url_str'] .'/fields'))), 'warning'); |
---|
605 | return $form; |
---|
606 | } |
---|
607 | |
---|
608 | // Fields. |
---|
609 | $label_options = array( |
---|
610 | 'above' => t('Above'), |
---|
611 | 'inline' => t('Inline'), |
---|
612 | 'hidden' => t('<Hidden>'), |
---|
613 | ); |
---|
614 | foreach ($fields as $name => $field) { |
---|
615 | $field_type = $field_types[$field['type']]; |
---|
616 | $defaults = $field['display_settings']; |
---|
617 | $weight = $field['widget']['weight']; |
---|
618 | |
---|
619 | $form[$name] = array( |
---|
620 | 'human_name' => array('#value' => check_plain($field['widget']['label'])), |
---|
621 | 'weight' => array('#type' => 'value', '#value' => $weight), |
---|
622 | 'parent' => array('#type' => 'value', '#value' => ''), |
---|
623 | ); |
---|
624 | |
---|
625 | // Label |
---|
626 | if ($contexts_selector == 'basic') { |
---|
627 | $form[$name]['label']['format'] = array( |
---|
628 | '#type' => 'select', |
---|
629 | '#options' => $label_options, |
---|
630 | '#default_value' => isset($defaults['label']['format']) ? $defaults['label']['format'] : 'above', |
---|
631 | ); |
---|
632 | } |
---|
633 | |
---|
634 | // Formatters. |
---|
635 | $options = array(); |
---|
636 | foreach ($field_type['formatters'] as $formatter_name => $formatter_info) { |
---|
637 | $options[$formatter_name] = $formatter_info['label']; |
---|
638 | } |
---|
639 | $options['hidden'] = t('<Hidden>'); |
---|
640 | |
---|
641 | foreach ($contexts as $key => $value) { |
---|
642 | $form[$name][$key]['format'] = array( |
---|
643 | '#type' => 'select', |
---|
644 | '#options' => $options, |
---|
645 | '#default_value' => isset($defaults[$key]['format']) ? $defaults[$key]['format'] : 'default', |
---|
646 | ); |
---|
647 | // exclude from $content |
---|
648 | $form[$name][$key]['exclude'] = array( |
---|
649 | '#type' => 'checkbox', |
---|
650 | '#options' => array(0 => t('Include'), 1 => t('Exclude')), |
---|
651 | '#default_value' => isset($defaults[$key]['exclude']) ? $defaults[$key]['exclude'] : 0, |
---|
652 | ); |
---|
653 | } |
---|
654 | } |
---|
655 | |
---|
656 | // Groups. |
---|
657 | $label_options = array( |
---|
658 | 'above' => t('Above'), |
---|
659 | 'hidden' => t('<Hidden>'), |
---|
660 | ); |
---|
661 | $options = array( |
---|
662 | 'no_style' => t('no styling'), |
---|
663 | 'simple' => t('simple'), |
---|
664 | 'fieldset' => t('fieldset'), |
---|
665 | 'fieldset_collapsible' => t('fieldset - collapsible'), |
---|
666 | 'fieldset_collapsed' => t('fieldset - collapsed'), |
---|
667 | 'hidden' => t('<Hidden>'), |
---|
668 | ); |
---|
669 | foreach ($groups as $name => $group) { |
---|
670 | $defaults = $group['settings']['display']; |
---|
671 | $weight = $group['weight']; |
---|
672 | |
---|
673 | $form[$name] = array( |
---|
674 | 'human_name' => array('#value' => check_plain($group['label'])), |
---|
675 | 'weight' => array('#type' => 'value', '#value' => $weight), |
---|
676 | 'parent' => array('#type' => 'value', '#value' => ''), |
---|
677 | ); |
---|
678 | if ($contexts_selector == 'basic') { |
---|
679 | $form[$name]['label'] = array( |
---|
680 | '#type' => 'select', |
---|
681 | '#options' => $label_options, |
---|
682 | '#default_value' => isset($defaults['label']) ? $defaults['label'] : 'above', |
---|
683 | ); |
---|
684 | } |
---|
685 | foreach ($contexts as $key => $title) { |
---|
686 | $form[$name][$key]['format'] = array( |
---|
687 | '#type' => 'select', |
---|
688 | '#options' => $options, |
---|
689 | '#default_value' => isset($defaults[$key]['format']) ? $defaults[$key]['format'] : 'fieldset', |
---|
690 | ); |
---|
691 | // exclude in $content |
---|
692 | $form[$name][$key]['exclude'] = array( |
---|
693 | '#type' => 'checkbox', |
---|
694 | '#options' => array(0 => t('Include'), 1 => t('Exclude')), |
---|
695 | '#default_value' => isset($defaults[$key]['exclude']) ? $defaults[$key]['exclude'] : 0, |
---|
696 | ); |
---|
697 | } |
---|
698 | foreach ($group['fields'] as $field_name => $field) { |
---|
699 | $form[$field_name]['parent']['#value'] = $name; |
---|
700 | } |
---|
701 | $form[$name]['parent']['#value'] = $group['parent']; |
---|
702 | $form[$name]['group']['#value']['depth'] = $group['depth']; |
---|
703 | } |
---|
704 | |
---|
705 | $form['submit'] = array('#type' => 'submit', '#value' => t('Save')); |
---|
706 | return $form; |
---|
707 | } |
---|
708 | |
---|
709 | /** |
---|
710 | * Submit handler for the display overview form. |
---|
711 | */ |
---|
712 | function content_display_overview_form_submit($form, &$form_state) { |
---|
713 | module_load_include('inc', 'content', 'includes/content.crud'); |
---|
714 | $form_values = $form_state['values']; |
---|
715 | foreach ($form_values as $key => $values) { |
---|
716 | // Groups are handled in fieldgroup_display_overview_form_submit(). |
---|
717 | if (in_array($key, $form['#fields'])) { |
---|
718 | $field = content_fields($key, $form['#type_name']); |
---|
719 | // We have some numeric keys here, so we can't use array_merge. |
---|
720 | $field['display_settings'] = $values + $field['display_settings']; |
---|
721 | content_field_instance_update($field, FALSE); |
---|
722 | } |
---|
723 | } |
---|
724 | |
---|
725 | // Clear caches and rebuild menu. |
---|
726 | content_clear_type_cache(TRUE); |
---|
727 | menu_rebuild(); |
---|
728 | |
---|
729 | drupal_set_message(t('Your settings have been saved.')); |
---|
730 | } |
---|
731 | |
---|
732 | /** |
---|
733 | * Return an array of field_type options. |
---|
734 | */ |
---|
735 | function content_field_type_options() { |
---|
736 | static $options; |
---|
737 | |
---|
738 | if (!isset($options)) { |
---|
739 | $options = array(); |
---|
740 | $field_types = _content_field_types(); |
---|
741 | $field_type_options = array(); |
---|
742 | foreach ($field_types as $field_type_name => $field_type) { |
---|
743 | // skip field types which have no widget types. |
---|
744 | if (content_widget_type_options($field_type_name)) { |
---|
745 | $options[$field_type_name] = t($field_type['label']); |
---|
746 | } |
---|
747 | } |
---|
748 | asort($options); |
---|
749 | } |
---|
750 | return $options; |
---|
751 | } |
---|
752 | |
---|
753 | /** |
---|
754 | * Return an array of widget type options for a field type. |
---|
755 | * |
---|
756 | * If no field type is provided, returns a nested array of |
---|
757 | * all widget types, keyed by field type human name |
---|
758 | */ |
---|
759 | function content_widget_type_options($field_type = NULL, $by_label = FALSE) { |
---|
760 | static $options; |
---|
761 | |
---|
762 | if (!isset($options)) { |
---|
763 | $options = array(); |
---|
764 | foreach (_content_widget_types() as $widget_type_name => $widget_type) { |
---|
765 | foreach ($widget_type['field types'] as $widget_field_type) { |
---|
766 | $options[$widget_field_type][$widget_type_name] = t($widget_type['label']); |
---|
767 | } |
---|
768 | } |
---|
769 | } |
---|
770 | |
---|
771 | if ($field_type) { |
---|
772 | return !empty($options[$field_type]) ? $options[$field_type] : array(); |
---|
773 | } |
---|
774 | elseif ($by_label) { |
---|
775 | $field_types = _content_field_types(); |
---|
776 | $options_by_label = array(); |
---|
777 | foreach ($options as $field_type => $widgets) { |
---|
778 | $options_by_label[t($field_types[$field_type]['label'])] = $widgets; |
---|
779 | } |
---|
780 | return $options_by_label; |
---|
781 | } |
---|
782 | else { |
---|
783 | return $options; |
---|
784 | } |
---|
785 | } |
---|
786 | |
---|
787 | /** |
---|
788 | * Return an array of existing field to be added to a node type. |
---|
789 | */ |
---|
790 | function content_existing_field_options($type_name) { |
---|
791 | $type = content_types($type_name); |
---|
792 | $fields = content_fields(); |
---|
793 | $field_types = _content_field_types(); |
---|
794 | |
---|
795 | $options = array(); |
---|
796 | foreach ($fields as $field) { |
---|
797 | if (!isset($type['fields'][$field['field_name']]) && !$field['locked']) { |
---|
798 | $field_type = $field_types[$field['type']]; |
---|
799 | $text = t('@type: @field (@label)', array('@type' => t($field_type['label']), '@label' => t($field['widget']['label']), '@field' => $field['field_name'])); |
---|
800 | $options[$field['field_name']] = (drupal_strlen($text) > 80) ? truncate_utf8($text, 77) . '...' : $text; |
---|
801 | } |
---|
802 | } |
---|
803 | // Sort the list by type, then by field name, then by label. |
---|
804 | asort($options); |
---|
805 | |
---|
806 | return $options; |
---|
807 | } |
---|
808 | |
---|
809 | /** |
---|
810 | * A form element for selecting field, widget, and label. |
---|
811 | */ |
---|
812 | function content_field_basic_form(&$form_state, $form_values) { |
---|
813 | module_load_include('inc', 'content', 'includes/content.crud'); |
---|
814 | |
---|
815 | $type_name = $form_values['type_name']; |
---|
816 | $type = content_types($form_values['type_name']); |
---|
817 | $field_name = $form_values['field_name']; |
---|
818 | $field_type = $form_values['type']; |
---|
819 | $label = $form_values['label']; |
---|
820 | |
---|
821 | $form = array(); |
---|
822 | |
---|
823 | $form['basic'] = array( |
---|
824 | '#type' => 'fieldset', |
---|
825 | '#title' => t('Edit basic information'), |
---|
826 | ); |
---|
827 | $form['basic']['field_name'] = array( |
---|
828 | '#title' => t('Field name'), |
---|
829 | '#type' => 'textfield', |
---|
830 | '#value' => $field_name, |
---|
831 | '#description' => t("The machine-readable name of the field. This name cannot be changed."), |
---|
832 | '#disabled' => TRUE, |
---|
833 | ); |
---|
834 | $form['basic']['label'] = array( |
---|
835 | '#type' => 'textfield', |
---|
836 | '#title' => t('Label'), |
---|
837 | '#default_value' => $label, |
---|
838 | '#required' => TRUE, |
---|
839 | '#description' => t('A human-readable name to be used as the label for this field in the %type content type.', array('%type' => $type['name'])), |
---|
840 | ); |
---|
841 | $form['basic']['type'] = array( |
---|
842 | '#type' => 'select', |
---|
843 | '#title' => t('Field type'), |
---|
844 | '#options' => content_field_type_options(), |
---|
845 | '#default_value' => $field_type, |
---|
846 | '#description' => t('The type of data you would like to store in the database with this field. This option cannot be changed.'), |
---|
847 | '#disabled' => TRUE, |
---|
848 | ); |
---|
849 | $form['basic']['widget_type'] = array( |
---|
850 | '#type' => 'select', |
---|
851 | '#title' => t('Widget type'), |
---|
852 | '#required' => TRUE, |
---|
853 | '#options' => content_widget_type_options($field_type), |
---|
854 | '#default_value' => $form_values['widget_type'], |
---|
855 | '#description' => t('The type of form element you would like to present to the user when creating this field in the %type content type.', array('%type' => $type['name'])), |
---|
856 | ); |
---|
857 | |
---|
858 | $form['type_name'] = array( |
---|
859 | '#type' => 'value', |
---|
860 | '#value' => $type_name, |
---|
861 | ); |
---|
862 | |
---|
863 | $form['submit'] = array( |
---|
864 | '#type' => 'submit', |
---|
865 | '#value' => t('Continue'), |
---|
866 | ); |
---|
867 | |
---|
868 | $form['#validate'] = array(); |
---|
869 | $form['#submit'] = array('content_field_basic_form_submit'); |
---|
870 | |
---|
871 | return $form; |
---|
872 | } |
---|
873 | |
---|
874 | /** |
---|
875 | * Create a new field for a content type. |
---|
876 | */ |
---|
877 | function content_field_basic_form_submit($form, &$form_state) { |
---|
878 | $form_values = $form_state['values']; |
---|
879 | |
---|
880 | $label = $form_values['label']; |
---|
881 | |
---|
882 | // Set the right module information |
---|
883 | $field_types = _content_field_types(); |
---|
884 | $widget_types = _content_widget_types(); |
---|
885 | $form_values['module'] = $field_types[$form_values['type']]['module']; |
---|
886 | $form_values['widget_module'] = $widget_types[$form_values['widget_type']]['module']; |
---|
887 | |
---|
888 | // Make sure we retain previous values and only over-write changed values. |
---|
889 | module_load_include('inc', 'content', 'includes/content.crud'); |
---|
890 | $instances = content_field_instance_read(array('field_name' => $form_values['field_name'], 'type_name' => $form_values['type_name'])); |
---|
891 | $field = array_merge(content_field_instance_collapse($instances[0]), $form_values); |
---|
892 | if (content_field_instance_update($field)) { |
---|
893 | drupal_set_message(t('Updated basic settings for field %label.', array( |
---|
894 | '%label' => $label))); |
---|
895 | } |
---|
896 | else { |
---|
897 | drupal_set_message(t('There was a problem updating the basic settings for field %label.', array( |
---|
898 | '%label' => $label))); |
---|
899 | } |
---|
900 | |
---|
901 | $type = content_types($form_values['type_name']); |
---|
902 | $form_state['redirect'] = 'admin/content/node-type/'. $type['url_str'] .'/fields/'. $form_values['field_name']; |
---|
903 | $form_state['rebuild'] = FALSE; |
---|
904 | } |
---|
905 | |
---|
906 | /** |
---|
907 | * Menu callback; present a form for removing a field from a content type. |
---|
908 | */ |
---|
909 | function content_field_remove_form(&$form_state, $type_name, $field_name) { |
---|
910 | $type = content_types($type_name); |
---|
911 | $field = $type['fields'][$field_name]; |
---|
912 | |
---|
913 | $form = array(); |
---|
914 | $form['type_name'] = array( |
---|
915 | '#type' => 'value', |
---|
916 | '#value' => $type_name, |
---|
917 | ); |
---|
918 | $form['field_name'] = array( |
---|
919 | '#type' => 'value', |
---|
920 | '#value' => $field_name, |
---|
921 | ); |
---|
922 | |
---|
923 | $output = confirm_form($form, |
---|
924 | t('Are you sure you want to remove the field %field?', array('%field' => $field['widget']['label'])), |
---|
925 | 'admin/content/node-type/'. $type['url_str'] .'/fields', |
---|
926 | t('If you have any content left in this field, it will be lost. This action cannot be undone.'), |
---|
927 | t('Remove'), t('Cancel'), |
---|
928 | 'confirm' |
---|
929 | ); |
---|
930 | |
---|
931 | if ($field['locked']) { |
---|
932 | unset($output['actions']['submit']); |
---|
933 | $output['description']['#value'] = t('This field is <strong>locked</strong> and cannot be removed.'); |
---|
934 | } |
---|
935 | |
---|
936 | return $output; |
---|
937 | } |
---|
938 | |
---|
939 | /** |
---|
940 | * Remove a field from a content type. |
---|
941 | */ |
---|
942 | function content_field_remove_form_submit($form, &$form_state) { |
---|
943 | module_load_include('inc', 'content', 'includes/content.crud'); |
---|
944 | $form_values = $form_state['values']; |
---|
945 | |
---|
946 | $type = content_types($form_values['type_name']); |
---|
947 | $field = $type['fields'][$form_values['field_name']]; |
---|
948 | if ($field['locked']) { |
---|
949 | return; |
---|
950 | } |
---|
951 | |
---|
952 | if ($type && $field && $form_values['confirm']) { |
---|
953 | if (content_field_instance_delete($form_values['field_name'], $form_values['type_name'])) { |
---|
954 | drupal_set_message(t('Removed field %field from %type.', array( |
---|
955 | '%field' => $field['widget']['label'], |
---|
956 | '%type' => $type['name']))); |
---|
957 | } |
---|
958 | else { |
---|
959 | drupal_set_message(t('There was a problem deleting %field from %type.', array( |
---|
960 | '%field' => $field['widget']['label'], |
---|
961 | '%type' => $type['name']))); |
---|
962 | } |
---|
963 | $form_state['redirect'] = 'admin/content/node-type/'. $type['url_str'] .'/fields'; |
---|
964 | } |
---|
965 | } |
---|
966 | |
---|
967 | /** |
---|
968 | * Menu callback; presents the field editing page. |
---|
969 | */ |
---|
970 | function content_field_edit_form(&$form_state, $type_name, $field_name) { |
---|
971 | $output = ''; |
---|
972 | $type = content_types($type_name); |
---|
973 | $field = $type['fields'][$field_name]; |
---|
974 | |
---|
975 | if ($field['locked']) { |
---|
976 | $output = array(); |
---|
977 | $output['locked'] = array( |
---|
978 | '#value' => t('The field %field is locked and cannot be edited.', array('%field' => $field['widget']['label'])), |
---|
979 | ); |
---|
980 | return $output; |
---|
981 | } |
---|
982 | |
---|
983 | $field_types = _content_field_types(); |
---|
984 | $field_type = $field_types[$field['type']]; |
---|
985 | $widget_types = _content_widget_types(); |
---|
986 | $widget_type = $widget_types[$field['widget']['type']]; |
---|
987 | |
---|
988 | $title = isset($field['widget']['label']) ? $field['widget']['label'] : $field['field_name']; |
---|
989 | drupal_set_title(check_plain($title)); |
---|
990 | |
---|
991 | // See if we need to change the widget type or label. |
---|
992 | if (isset($form_state['change_basic'])) { |
---|
993 | module_load_include('inc', 'content', 'includes/content.crud'); |
---|
994 | $field_values = content_field_instance_collapse($field); |
---|
995 | return content_field_basic_form($form_state, $field_values); |
---|
996 | } |
---|
997 | |
---|
998 | $add_new_sequence = isset($_REQUEST['destinations']); |
---|
999 | |
---|
1000 | // Remove menu tabs when we are in an 'add new' sequence. |
---|
1001 | if ($add_new_sequence) { |
---|
1002 | menu_set_item(NULL, menu_get_item('node')); |
---|
1003 | } |
---|
1004 | |
---|
1005 | $form = array(); |
---|
1006 | $form['#field'] = $field; |
---|
1007 | $form['#type'] = $type; |
---|
1008 | |
---|
1009 | // Basic iformation : hide when we are in an 'add new' sequence. |
---|
1010 | $form['basic'] = array( |
---|
1011 | '#type' => 'fieldset', |
---|
1012 | '#title' => t('%type basic information', array('%type' => $type['name'])), |
---|
1013 | '#access' => !$add_new_sequence, |
---|
1014 | ); |
---|
1015 | $form['basic']['label'] = array( |
---|
1016 | '#type' => 'textfield', |
---|
1017 | '#title' => t('Label'), |
---|
1018 | '#value' => $field['widget']['label'], |
---|
1019 | '#disabled' => TRUE, |
---|
1020 | ); |
---|
1021 | $form['basic']['field_name'] = array( |
---|
1022 | '#type' => 'hidden', |
---|
1023 | '#title' => t('Field name'), |
---|
1024 | '#value' => $field['field_name'], |
---|
1025 | '#disabled' => TRUE, |
---|
1026 | ); |
---|
1027 | $form['basic']['type'] = array( |
---|
1028 | '#type' => 'hidden', |
---|
1029 | '#title' => t('Field type'), |
---|
1030 | '#value' => $field['type'], |
---|
1031 | '#disabled' => TRUE, |
---|
1032 | ); |
---|
1033 | $widget_options = content_widget_type_options($field['type']); |
---|
1034 | $form['basic']['widget_type'] = array( |
---|
1035 | '#type' => 'select', |
---|
1036 | '#title' => t('Widget type'), |
---|
1037 | '#options' => $widget_options, |
---|
1038 | '#default_value' => $field['widget']['type'] ? $field['widget']['type'] : key($widget_options), |
---|
1039 | '#disabled' => TRUE, |
---|
1040 | ); |
---|
1041 | $form['basic']['change'] = array( |
---|
1042 | '#type' => 'submit', |
---|
1043 | '#value' => t('Change basic information'), |
---|
1044 | '#submit' => array('content_field_edit_form_submit_update_basic'), |
---|
1045 | ); |
---|
1046 | |
---|
1047 | $form['widget'] = array( |
---|
1048 | '#type' => 'fieldset', |
---|
1049 | '#title' => t('%type settings', array('%type' => $type['name'])), |
---|
1050 | '#description' => t('These settings apply only to the %field field as it appears in the %type content type.', array( |
---|
1051 | '%field' => $field['widget']['label'], |
---|
1052 | '%type' => $type['name'])), |
---|
1053 | ); |
---|
1054 | $form['widget']['weight'] = array( |
---|
1055 | '#type' => 'hidden', |
---|
1056 | '#default_value' => $field['widget']['weight'], |
---|
1057 | ); |
---|
1058 | |
---|
1059 | $additions = (array) module_invoke($widget_type['module'], 'widget_settings', 'form', $field['widget']); |
---|
1060 | drupal_alter('widget_settings', $additions, 'form', $field['widget']); |
---|
1061 | $form['widget'] = array_merge($form['widget'], $additions); |
---|
1062 | |
---|
1063 | $form['widget']['description'] = array( |
---|
1064 | '#type' => 'textarea', |
---|
1065 | '#title' => t('Help text'), |
---|
1066 | '#default_value' => $field['widget']['description'], |
---|
1067 | '#rows' => 5, |
---|
1068 | '#description' => t('Instructions to present to the user below this field on the editing form.<br />Allowed HTML tags: @tags', array('@tags' => _content_filter_xss_display_allowed_tags())), |
---|
1069 | '#required' => FALSE, |
---|
1070 | ); |
---|
1071 | |
---|
1072 | // Add handling for default value if not provided by field. |
---|
1073 | if (content_callback('widget', 'default value', $field) == CONTENT_CALLBACK_DEFAULT) { |
---|
1074 | |
---|
1075 | // Store the original default value for use in programmed forms. |
---|
1076 | // Set '#default_value' instead of '#value' so programmed values |
---|
1077 | // can override whatever we set here. |
---|
1078 | $default_value = isset($field['widget']['default_value']) ? $field['widget']['default_value'] : array(); |
---|
1079 | $default_value_php = isset($field['widget']['default_value_php']) ? $field['widget']['default_value_php'] : ''; |
---|
1080 | $form['widget']['default_value'] = array( |
---|
1081 | '#type' => 'value', |
---|
1082 | '#default_value' => $default_value, |
---|
1083 | ); |
---|
1084 | $form['widget']['default_value_php'] = array( |
---|
1085 | '#type' => 'value', |
---|
1086 | '#default_value' => $default_value_php, |
---|
1087 | ); |
---|
1088 | |
---|
1089 | // We can't tell at the time we build the form if this is a programmed |
---|
1090 | // form or not, so we always end up adding the default value widget |
---|
1091 | // even if we won't use it. |
---|
1092 | $form['widget']['default_value_fieldset'] = array( |
---|
1093 | '#type' => 'fieldset', |
---|
1094 | '#title' => t('Default value'), |
---|
1095 | '#collapsible' => TRUE, |
---|
1096 | '#collapsed' => TRUE, |
---|
1097 | ); |
---|
1098 | |
---|
1099 | // Default value widget. |
---|
1100 | $widget_form = array('#node' => (object) array('type' => $type_name)); |
---|
1101 | $widget_form_state = array('values' => array($field['field_name'] => $default_value)); |
---|
1102 | // Make sure the default value is not a required field. |
---|
1103 | $widget_field = $field; |
---|
1104 | $widget_field['required'] = FALSE; |
---|
1105 | module_load_include('inc', 'content', 'includes/content.node_form'); |
---|
1106 | $form_element = content_field_form($widget_form, $widget_form_state, $widget_field, 0); |
---|
1107 | $form['widget']['default_value_fieldset']['default_value_widget'] = $form_element; |
---|
1108 | $form['widget']['default_value_fieldset']['default_value_widget']['#tree'] = TRUE; |
---|
1109 | // Set up form info that the default value widget will need to find in the form. |
---|
1110 | $form['#field_info'] = array($widget_field['field_name'] => $widget_field); |
---|
1111 | |
---|
1112 | // Advanced: PHP code. |
---|
1113 | $form['widget']['default_value_fieldset']['advanced_options'] = array( |
---|
1114 | '#type' => 'fieldset', |
---|
1115 | '#title' => t('PHP code'), |
---|
1116 | '#collapsible' => TRUE, |
---|
1117 | '#collapsed' => empty($field['widget']['default_value_php']), |
---|
1118 | ); |
---|
1119 | |
---|
1120 | if (user_access('Use PHP input for field settings (dangerous - grant with care)')) { |
---|
1121 | $db_info = content_database_info($field); |
---|
1122 | $columns = array_keys($db_info['columns']); |
---|
1123 | foreach ($columns as $key => $column) { |
---|
1124 | $columns[$key] = t("'@column' => value for @column", array('@column' => $column)); |
---|
1125 | } |
---|
1126 | $sample = t("return array(\n 0 => array(@columns),\n // You'll usually want to stop here. Provide more values\n // if you want your 'default value' to be multi-valued:\n 1 => array(@columns),\n 2 => ...\n);", array('@columns' => implode(', ', $columns))); |
---|
1127 | |
---|
1128 | $form['widget']['default_value_fieldset']['advanced_options']['default_value_php'] = array( |
---|
1129 | '#type' => 'textarea', |
---|
1130 | '#title' => t('Code'), |
---|
1131 | '#default_value' => isset($field['widget']['default_value_php']) ? $field['widget']['default_value_php'] : '', |
---|
1132 | '#rows' => 6, |
---|
1133 | '#tree' => TRUE, |
---|
1134 | '#description' => t('Advanced usage only: PHP code that returns a default value. Should not include <?php ?> delimiters. If this field is filled out, the value returned by this code will override any value specified above. Expected format: <pre>!sample</pre>To figure out the expected format, you can use the <em>devel load</em> tab provided by <a href="@link_devel">devel module</a> on a %type content page.', array( |
---|
1135 | '!sample' => $sample, |
---|
1136 | '@link_devel' => 'http://www.drupal.org/project/devel', |
---|
1137 | '%type' => $type_name)), |
---|
1138 | ); |
---|
1139 | } |
---|
1140 | else { |
---|
1141 | $form['widget']['default_value_fieldset']['advanced_options']['markup_default_value_php'] = array( |
---|
1142 | '#type' => 'item', |
---|
1143 | '#title' => t('Code'), |
---|
1144 | '#value' => !empty($field['widget']['default_value_php']) ? '<code>'. check_plain($field['widget']['default_value_php']) .'</code>' : t('<none>'), |
---|
1145 | '#description' => empty($field['widget']['default_value_php']) ? t("You're not allowed to input PHP code.") : t('This PHP code was set by an administrator and will override any value specified above.'), |
---|
1146 | ); |
---|
1147 | } |
---|
1148 | } |
---|
1149 | |
---|
1150 | $form['field'] = array( |
---|
1151 | '#type' => 'fieldset', |
---|
1152 | '#title' => t('Global settings'), |
---|
1153 | '#description' => t('These settings apply to the %field field in every content type in which it appears.', array('%field' => $field['widget']['label'])), |
---|
1154 | ); |
---|
1155 | $form['field']['required'] = array( |
---|
1156 | '#type' => 'checkbox', |
---|
1157 | '#title' => t('Required'), |
---|
1158 | '#default_value' => $field['required'], |
---|
1159 | ); |
---|
1160 | $description = t('Maximum number of values users can enter for this field.'); |
---|
1161 | if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE) { |
---|
1162 | $description .= '<br/>'. t("'Unlimited' will provide an 'Add more' button so the users can add as many values as they like."); |
---|
1163 | } |
---|
1164 | $description .= '<br/><strong>'. t('Warning! Changing this setting after data has been created could result in the loss of data!') .'</strong>'; |
---|
1165 | $form['field']['multiple'] = array( |
---|
1166 | '#type' => 'select', |
---|
1167 | '#title' => t('Number of values'), |
---|
1168 | '#options' => array(1 => t('Unlimited'), 0 => 1) + drupal_map_assoc(range(2, 10)), |
---|
1169 | '#default_value' => $field['multiple'], |
---|
1170 | '#description' => $description, |
---|
1171 | ); |
---|
1172 | |
---|
1173 | $form['field']['previous_field'] = array( |
---|
1174 | '#type' => 'hidden', |
---|
1175 | '#value' => serialize($field), |
---|
1176 | ); |
---|
1177 | |
---|
1178 | $additions = (array) module_invoke($field_type['module'], 'field_settings', 'form', $field); |
---|
1179 | drupal_alter('field_settings', $additions, 'form', $field); |
---|
1180 | $form['field'] = array_merge($form['field'], $additions); |
---|
1181 | |
---|
1182 | $form['submit'] = array( |
---|
1183 | '#type' => 'submit', |
---|
1184 | '#value' => t('Save field settings'), |
---|
1185 | ); |
---|
1186 | $form['type_name'] = array( |
---|
1187 | '#type' => 'value', |
---|
1188 | '#value' => $type_name, |
---|
1189 | ); |
---|
1190 | $form['field_name'] = array( |
---|
1191 | '#type' => 'value', |
---|
1192 | '#value' => $field_name, |
---|
1193 | ); |
---|
1194 | $form['type'] = array( |
---|
1195 | '#type' => 'value', |
---|
1196 | '#value' => $field['type'], |
---|
1197 | ); |
---|
1198 | $form['module'] = array( |
---|
1199 | '#type' => 'value', |
---|
1200 | '#value' => $field['module'], |
---|
1201 | ); |
---|
1202 | $form['widget']['label'] = array( |
---|
1203 | '#type' => 'value', |
---|
1204 | '#value' => $field['widget']['label'], |
---|
1205 | ); |
---|
1206 | $form['widget_module'] = array( |
---|
1207 | '#type' => 'value', |
---|
1208 | '#value' => $field['widget']['module'], |
---|
1209 | ); |
---|
1210 | $form['columns'] = array( |
---|
1211 | '#type' => 'value', |
---|
1212 | '#value' => $field['columns'], |
---|
1213 | ); |
---|
1214 | return $form; |
---|
1215 | } |
---|
1216 | |
---|
1217 | /** |
---|
1218 | * Validate a field's settings. |
---|
1219 | */ |
---|
1220 | function content_field_edit_form_validate($form, &$form_state) { |
---|
1221 | $form_values = $form_state['values']; |
---|
1222 | if (isset($form_state['change_basic']) || $form_values['op'] == t('Change basic information')) { |
---|
1223 | return; |
---|
1224 | } |
---|
1225 | |
---|
1226 | module_load_include('inc', 'content', 'includes/content.crud'); |
---|
1227 | $previous_field = unserialize($form_values['previous_field']); |
---|
1228 | $field = content_field_instance_expand($form_values); |
---|
1229 | $field['db_storage'] = content_storage_type($field); |
---|
1230 | |
---|
1231 | $field_types = _content_field_types(); |
---|
1232 | $field_type = $field_types[$field['type']]; |
---|
1233 | $widget_types = _content_widget_types(); |
---|
1234 | $widget_type = $widget_types[$field['widget']['type']]; |
---|
1235 | |
---|
1236 | if ($dropped_data = content_alter_db_analyze($previous_field, $field)) { |
---|
1237 | // @TODO |
---|
1238 | // This is a change that might result in loss of data. |
---|
1239 | // Add a confirmation form here. |
---|
1240 | // dsm($dropped_data); |
---|
1241 | } |
---|
1242 | |
---|
1243 | module_invoke($widget_type['module'], 'widget_settings', 'validate', array_merge($field, $form_values)); |
---|
1244 | module_invoke($field_type['module'], 'field_settings', 'validate', array_merge($field, $form_values)); |
---|
1245 | |
---|
1246 | // If content.module is handling the default value, |
---|
1247 | // validate the result using the field validation. |
---|
1248 | if (content_callback('widget', 'default value', $field) == CONTENT_CALLBACK_DEFAULT) { |
---|
1249 | |
---|
1250 | // If this is a programmed form, get rid of the default value widget, |
---|
1251 | // we have the default values already. |
---|
1252 | if ($form['#programmed']) { |
---|
1253 | form_set_value(array('#parents' => array('default_value_widget')), NULL, $form_state); |
---|
1254 | return; |
---|
1255 | } |
---|
1256 | |
---|
1257 | if (isset($form_values['default_value_php']) && |
---|
1258 | ($php = trim($form_values['default_value_php']))) { |
---|
1259 | $error = FALSE; |
---|
1260 | ob_start(); |
---|
1261 | $return = eval($php); |
---|
1262 | ob_end_clean(); |
---|
1263 | if (!is_array($return)) { |
---|
1264 | $error = TRUE; |
---|
1265 | } |
---|
1266 | else { |
---|
1267 | foreach ($return as $item) { |
---|
1268 | if (!is_array($item)) { |
---|
1269 | $error = TRUE; |
---|
1270 | break; |
---|
1271 | } |
---|
1272 | } |
---|
1273 | } |
---|
1274 | if ($error) { |
---|
1275 | $db_info = content_database_info($field); |
---|
1276 | $columns = array_keys($db_info['columns']); |
---|
1277 | foreach ($columns as $key => $column) { |
---|
1278 | $columns[$key] = t("'@column' => value for @column", array('@column' => $column)); |
---|
1279 | } |
---|
1280 | $sample = t("return array(\n 0 => array(@columns),\n // You'll usually want to stop here. Provide more values\n // if you want your 'default value' to be multi-valued:\n 1 => array(@columns),\n 2 => ...\n);", array('@columns' => implode(', ', $columns))); |
---|
1281 | |
---|
1282 | form_set_error('default_value_php', t('The default value PHP code returned an incorrect value.<br/>Expected format: <pre>!sample</pre> Returned value: @value', array( |
---|
1283 | '!sample' => $sample, |
---|
1284 | '@value' => print_r($return, TRUE)))); |
---|
1285 | return; |
---|
1286 | } |
---|
1287 | else { |
---|
1288 | $default_value = $return; |
---|
1289 | $is_code = TRUE; |
---|
1290 | form_set_value(array('#parents' => array('default_value_php')), $php, $form_state); |
---|
1291 | form_set_value(array('#parents' => array('default_value')), array(), $form_state); |
---|
1292 | } |
---|
1293 | } |
---|
1294 | elseif (!empty($form_values['default_value_widget'])) { |
---|
1295 | // Fields that handle their own multiple values may use an expected |
---|
1296 | // value as the top-level key, so just pop off the top element. |
---|
1297 | $key = array_shift(array_keys($form_values['default_value_widget'])); |
---|
1298 | $default_value = $form_values['default_value_widget'][$key]; |
---|
1299 | $is_code = FALSE; |
---|
1300 | form_set_value(array('#parents' => array('default_value_php')), '', $form_state); |
---|
1301 | form_set_value(array('#parents' => array('default_value')), $default_value, $form_state); |
---|
1302 | } |
---|
1303 | if (isset($default_value)) { |
---|
1304 | $node = array(); |
---|
1305 | $node[$form_values['field_name']] = $default_value; |
---|
1306 | $field['required'] = FALSE; |
---|
1307 | $field_function = $field_type['module'] .'_field'; |
---|
1308 | |
---|
1309 | $errors_before = form_get_errors(); |
---|
1310 | |
---|
1311 | // Widget now does its own validation, should be no need |
---|
1312 | // to add anything for widget validation here. |
---|
1313 | if (function_exists($field_function)) { |
---|
1314 | $field_function('validate', $node, $field, $default_value, $form, NULL); |
---|
1315 | } |
---|
1316 | // The field validation routine won't set an error on the right field, |
---|
1317 | // so set it here. |
---|
1318 | $errors_after = form_get_errors(); |
---|
1319 | if (count($errors_after) > count($errors_before)) { |
---|
1320 | if (trim($form_values['default_value_php'])) { |
---|
1321 | form_set_error('default_value_php', t("The PHP code for 'default value' returned @value, which is invalid.", array( |
---|
1322 | '@value' => print_r($default_value, TRUE)))); |
---|
1323 | } |
---|
1324 | else { |
---|
1325 | form_set_error('default_value', t('The default value is invalid.')); |
---|
1326 | } |
---|
1327 | } |
---|
1328 | } |
---|
1329 | } |
---|
1330 | } |
---|
1331 | |
---|
1332 | /** |
---|
1333 | * Button submit handler. |
---|
1334 | */ |
---|
1335 | function content_field_edit_form_submit_update_basic($form, &$form_state) { |
---|
1336 | $form_state['change_basic'] = TRUE; |
---|
1337 | $form_state['rebuild'] = TRUE; |
---|
1338 | } |
---|
1339 | |
---|
1340 | /** |
---|
1341 | * Save a field's settings after editing. |
---|
1342 | */ |
---|
1343 | function content_field_edit_form_submit($form, &$form_state) { |
---|
1344 | module_load_include('inc', 'content', 'includes/content.crud'); |
---|
1345 | $form_values = $form_state['values']; |
---|
1346 | content_field_instance_update($form_values); |
---|
1347 | |
---|
1348 | $destinations = !empty($_REQUEST['destinations']) ? $_REQUEST['destinations'] : array(); |
---|
1349 | // Remove any external URLs. |
---|
1350 | $destinations = array_diff($destinations, array_filter($destinations, 'menu_path_is_external')); |
---|
1351 | if ($destinations) { |
---|
1352 | drupal_set_message(t('Added field %label.', array('%label' => $form_values['label']))); |
---|
1353 | $form_state['redirect'] = content_get_destinations($destinations); |
---|
1354 | } |
---|
1355 | else { |
---|
1356 | drupal_set_message(t('Saved field %label.', array('%label' => $form_values['label']))); |
---|
1357 | $type = content_types($form_values['type_name']); |
---|
1358 | $form_state['redirect'] = 'admin/content/node-type/'. $type['url_str'] .'/fields'; |
---|
1359 | } |
---|
1360 | } |
---|
1361 | |
---|
1362 | /** |
---|
1363 | * Helper function to handle multipage redirects. |
---|
1364 | */ |
---|
1365 | function content_get_destinations($destinations) { |
---|
1366 | $query = array(); |
---|
1367 | $path = array_shift($destinations); |
---|
1368 | if ($destinations) { |
---|
1369 | $query['destinations'] = $destinations; |
---|
1370 | } |
---|
1371 | return array($path, $query); |
---|
1372 | } |
---|
1373 | |
---|
1374 | /** |
---|
1375 | * Content Schema Alter |
---|
1376 | * |
---|
1377 | * Alter the database schema. |
---|
1378 | * |
---|
1379 | * TODO figure out an API-safe way to use batching to update the nodes that |
---|
1380 | * will be affected by this change so the node_save() hooks will fire. |
---|
1381 | * |
---|
1382 | */ |
---|
1383 | function content_alter_schema($previous_field, $new_field) { |
---|
1384 | content_alter_db($previous_field, $new_field); |
---|
1385 | } |
---|
1386 | |
---|
1387 | /** |
---|
1388 | * Schema Alter Analyze |
---|
1389 | * |
---|
1390 | * Analyze if changes will remove columns or delta values, thus losing data. |
---|
1391 | * Do this so we can delete the data and fire the necessary hooks, before |
---|
1392 | * we actually alter the schema. |
---|
1393 | */ |
---|
1394 | function content_alter_db_analyze($previous_field, $new_field) { |
---|
1395 | $dropped = array(); |
---|
1396 | // There is no loss of data if there was no previous data. |
---|
1397 | if (empty($previous_field)) { |
---|
1398 | return $dropped; |
---|
1399 | } |
---|
1400 | |
---|
1401 | // Analyze possible data loss from changes in storage type. |
---|
1402 | if (!empty($previous_field) && !empty($new_field)) { |
---|
1403 | // Changing from multiple to not multiple data, will cause loss of all |
---|
1404 | // values greater than zero. |
---|
1405 | if ($previous_field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD && |
---|
1406 | $new_field['db_storage'] == CONTENT_DB_STORAGE_PER_CONTENT_TYPE) { |
---|
1407 | $dropped['delta'] = 0; |
---|
1408 | } |
---|
1409 | // Changing from one multiple value to another will cause loss of all |
---|
1410 | // values for deltas greater than or equal to the new multiple value. |
---|
1411 | elseif (isset($previous_field['multiple']) && isset($new_field['multiple'])) { |
---|
1412 | if ($previous_field['multiple'] > $new_field['multiple'] && |
---|
1413 | $new_field['multiple'] > 1) { |
---|
1414 | $dropped['delta'] = $new_field['multiple']; |
---|
1415 | } |
---|
1416 | } |
---|
1417 | } |
---|
1418 | |
---|
1419 | // Analyze possible data loss from changes in field columns. |
---|
1420 | $previous_schema = !empty($previous_field) ? content_table_schema($previous_field) : array('fields' => array()); |
---|
1421 | $new_schema = !empty($new_field) ? content_table_schema($new_field) : array('fields' => array()); |
---|
1422 | $dropped_columns = array_diff(array_keys($previous_schema['fields']), array_keys($new_schema['fields'])); |
---|
1423 | if ($dropped_columns) { |
---|
1424 | $dropped['columns'] = $dropped_columns; |
---|
1425 | } |
---|
1426 | // if (empty($new_schema['fields'])) { |
---|
1427 | // // No new columns, will lose all columns for a field. |
---|
1428 | // foreach ($previous_schema['fields'] as $column => $attributes) { |
---|
1429 | // $dropped['columns'][] = $column; |
---|
1430 | // } |
---|
1431 | // } |
---|
1432 | // else { |
---|
1433 | // // Check both old and new columns to see if we are deleting some columns for a field. |
---|
1434 | // foreach ($previous_schema['fields'] as $column => $attributes) { |
---|
1435 | // if (!isset($new_schema['fields'][$column])) { |
---|
1436 | // $dropped['columns'][] = $column; |
---|
1437 | // } |
---|
1438 | // } |
---|
1439 | // } |
---|
1440 | |
---|
1441 | return $dropped; |
---|
1442 | } |
---|
1443 | |
---|
1444 | /** |
---|
1445 | * Perform adds, alters, and drops as needed to synchronize the database with |
---|
1446 | * new field definitions. |
---|
1447 | */ |
---|
1448 | function content_alter_db($previous_field, $new_field) { |
---|
1449 | $ret = array(); |
---|
1450 | |
---|
1451 | // One or the other of these must be valid. |
---|
1452 | if (empty($previous_field) && empty($new_field)) { |
---|
1453 | return $ret; |
---|
1454 | } |
---|
1455 | |
---|
1456 | // Gather relevant information : schema, table name... |
---|
1457 | $previous_schema = !empty($previous_field) ? content_table_schema($previous_field) : array(); |
---|
1458 | $new_schema = !empty($new_field) ? content_table_schema($new_field) : array(); |
---|
1459 | if (!empty($previous_field)) { |
---|
1460 | $previous_db_info = content_database_info($previous_field); |
---|
1461 | $previous_table = $previous_db_info['table']; |
---|
1462 | } |
---|
1463 | if (!empty($new_field)) { |
---|
1464 | $new_db_info = content_database_info($new_field); |
---|
1465 | $new_table = $new_db_info['table']; |
---|
1466 | } |
---|
1467 | |
---|
1468 | // Deletion of a field instance: drop relevant columns and tables and return. |
---|
1469 | if (empty($new_field)) { |
---|
1470 | if ($previous_field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) { |
---|
1471 | db_drop_table($ret, $previous_table); |
---|
1472 | } |
---|
1473 | else { |
---|
1474 | foreach ($previous_schema['fields'] as $column => $attributes) { |
---|
1475 | if (!in_array($column, array('nid', 'vid', 'delta'))) { |
---|
1476 | db_drop_field($ret, $previous_table, $column); |
---|
1477 | } |
---|
1478 | } |
---|
1479 | } |
---|
1480 | content_alter_db_cleanup(); |
---|
1481 | return $ret; |
---|
1482 | } |
---|
1483 | |
---|
1484 | // Check that content types that have fields do have a per-type table. |
---|
1485 | if (!empty($new_field)) { |
---|
1486 | $base_tablename = _content_tablename($new_field['type_name'], CONTENT_DB_STORAGE_PER_CONTENT_TYPE); |
---|
1487 | if (!db_table_exists($base_tablename)) { |
---|
1488 | db_create_table($ret, $base_tablename, content_table_schema()); |
---|
1489 | } |
---|
1490 | } |
---|
1491 | |
---|
1492 | // Create new table and columns, if not already created. |
---|
1493 | if (!db_table_exists($new_table)) { |
---|
1494 | db_create_table($ret, $new_table, $new_schema); |
---|
1495 | } |
---|
1496 | else { |
---|
1497 | // Or add fields and/or indexes to an existing table. |
---|
1498 | foreach ($new_schema['fields'] as $column => $attributes) { |
---|
1499 | if (!in_array($column, array('nid', 'vid', 'delta'))) { |
---|
1500 | // Create the column if it does not exist. |
---|
1501 | if (!db_column_exists($new_table, $column)) { |
---|
1502 | db_add_field($ret, $new_table, $column, $attributes); |
---|
1503 | } |
---|
1504 | // Create the index if requested to, and it does not exist. |
---|
1505 | if (isset($new_schema['indexes'][$column]) && !content_db_index_exists($new_table, $column)) { |
---|
1506 | db_add_index($ret, $new_table, $column, $new_schema['indexes'][$column]); |
---|
1507 | } |
---|
1508 | } |
---|
1509 | } |
---|
1510 | } |
---|
1511 | |
---|
1512 | // If this is a new field, we're done. |
---|
1513 | if (empty($previous_field)) { |
---|
1514 | content_alter_db_cleanup(); |
---|
1515 | return $ret; |
---|
1516 | } |
---|
1517 | |
---|
1518 | // If the previous table doesn't exist, we're done. |
---|
1519 | // Could happen if someone tries to run a schema update from an |
---|
1520 | // content.install update function more than once. |
---|
1521 | if (!db_table_exists($previous_table)) { |
---|
1522 | content_alter_db_cleanup(); |
---|
1523 | return $ret; |
---|
1524 | } |
---|
1525 | |
---|
1526 | // If changing data from one schema to another, see if changes require that |
---|
1527 | // we drop multiple values or migrate data from one storage type to another. |
---|
1528 | $migrate_columns = array_intersect_assoc($new_schema['fields'], $previous_schema['fields']); |
---|
1529 | unset($migrate_columns['nid'], $migrate_columns['vid'], $migrate_columns['delta']); |
---|
1530 | |
---|
1531 | // If we're going from one multiple value a smaller one or to single, |
---|
1532 | // drop all delta values higher than the new maximum delta value. |
---|
1533 | // Not needed if the new multiple is unlimited or if the new table is the content table. |
---|
1534 | if ($new_table != $base_tablename && $new_field['multiple'] < $previous_field['multiple'] && $new_field['multiple'] != 1) { |
---|
1535 | db_query("DELETE FROM {". $new_table ."} WHERE delta >= ". max(1, $new_field['multiple'])); |
---|
1536 | } |
---|
1537 | |
---|
1538 | // If going from multiple to non-multiple, make sure the field tables have |
---|
1539 | // the right database structure to accept migrated data. |
---|
1540 | if ($new_field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) { |
---|
1541 | if ($previous_field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD && count($previous_schema['fields'])) { |
---|
1542 | // Already using per-field storage; change multiplicity if needed. |
---|
1543 | if ($previous_field['multiple'] > 0 && $new_field['multiple'] == 0) { |
---|
1544 | db_drop_field($ret, $new_table, 'delta'); |
---|
1545 | db_drop_primary_key($ret, $new_table); |
---|
1546 | db_add_primary_key($ret, $new_table, array('vid')); |
---|
1547 | } |
---|
1548 | else if ($previous_field['multiple'] == 0 && $new_field['multiple'] > 0) { |
---|
1549 | db_add_field($ret, $new_table, 'delta', array( |
---|
1550 | 'type' => 'int', |
---|
1551 | 'unsigned' => TRUE, |
---|
1552 | 'not null' => TRUE, |
---|
1553 | 'default' => 0)); |
---|
1554 | db_drop_primary_key($ret, $new_table); |
---|
1555 | db_add_primary_key($ret, $new_table, array('vid', 'delta')); |
---|
1556 | } |
---|
1557 | } |
---|
1558 | } |
---|
1559 | |
---|
1560 | // Migrate data from per-content-type storage. |
---|
1561 | if ($previous_field['db_storage'] == CONTENT_DB_STORAGE_PER_CONTENT_TYPE && |
---|
1562 | $new_field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD) { |
---|
1563 | $columns = array_keys($migrate_columns); |
---|
1564 | if ($new_field['multiple']) { |
---|
1565 | db_query('INSERT INTO {'. $new_table .'} (vid, nid, delta, '. implode(', ', $columns) .') '. |
---|
1566 | ' SELECT vid, nid, 0, '. implode(', ', $columns) .' FROM {'. $previous_table .'}'); |
---|
1567 | } |
---|
1568 | else { |
---|
1569 | db_query('INSERT INTO {'. $new_table .'} (vid, nid, '. implode(', ', $columns) .') '. |
---|
1570 | ' SELECT vid, nid, '. implode(', ', $columns) .' FROM {'. $previous_table .'}'); |
---|
1571 | } |
---|
1572 | foreach ($columns as $column_name) { |
---|
1573 | db_drop_field($ret, $previous_table, $column_name); |
---|
1574 | } |
---|
1575 | } |
---|
1576 | |
---|
1577 | // Migrate data from per-field storage, and drop per-field table. |
---|
1578 | if ($previous_field['db_storage'] == CONTENT_DB_STORAGE_PER_FIELD && |
---|
1579 | $new_field['db_storage'] == CONTENT_DB_STORAGE_PER_CONTENT_TYPE) { |
---|
1580 | // In order to be able to use drupal_write_record, we need to |
---|
1581 | // rebuild the schema now. |
---|
1582 | content_alter_db_cleanup(); |
---|
1583 | if ($previous_field['multiple']) { |
---|
1584 | $result = db_query("SELECT * FROM {". $previous_table ."} c JOIN {node} n ON c.nid = n.nid WHERE delta = 0 AND n.type = '%s'", $new_field['type_name']); |
---|
1585 | } |
---|
1586 | else { |
---|
1587 | $result = db_query("SELECT * FROM {". $previous_table ."} c JOIN {node} n ON c.nid = n.nid WHERE n.type = '%s'", $new_field['type_name']); |
---|
1588 | } |
---|
1589 | $record = array(); |
---|
1590 | while ($data = db_fetch_array($result)) { |
---|
1591 | $record['nid'] = $data['nid']; |
---|
1592 | $record['vid'] = $data['vid']; |
---|
1593 | if ($previous_field['multiple']) { |
---|
1594 | $record['delta'] = $data['delta']; |
---|
1595 | } |
---|
1596 | foreach ($migrate_columns as $column => $attributes) { |
---|
1597 | if (is_null($data[$column])) { |
---|
1598 | $record[$column] = NULL; |
---|
1599 | } |
---|
1600 | else { |
---|
1601 | $record[$column] = $data[$column]; |
---|
1602 | // Prevent double serializtion in drupal_write_record. |
---|
1603 | if (isset($attributes['serialize']) && $attributes['serialize']) { |
---|
1604 | $record[$column] = unserialize($record[$column]); |
---|
1605 | } |
---|
1606 | } |
---|
1607 | } |
---|
1608 | if (db_result(db_query('SELECT COUNT(*) FROM {'. $new_table . |
---|
1609 | '} WHERE vid = %d AND nid = %d', $data['vid'], $data['nid']))) { |
---|
1610 | $keys = $new_field['multiple'] ? array('vid', 'delta') : array('vid'); |
---|
1611 | drupal_write_record($new_table, $record, $keys); |
---|
1612 | } |
---|
1613 | else { |
---|
1614 | drupal_write_record($new_table, $record); |
---|
1615 | } |
---|
1616 | } |
---|
1617 | db_drop_table($ret, $previous_table); |
---|
1618 | } |
---|
1619 | |
---|
1620 | // Change modified columns that don't involve storage changes. |
---|
1621 | foreach ($new_schema['fields'] as $column => $attributes) { |
---|
1622 | if (isset($previous_schema['fields'][$column]) && |
---|
1623 | $previous_field['db_storage'] == $new_field['db_storage']) { |
---|
1624 | if ($attributes != $previous_schema['fields'][$column]) { |
---|
1625 | if (!in_array($column, array('nid', 'vid', 'delta'))) { |
---|
1626 | db_change_field($ret, $new_table, $column, $column, $attributes); |
---|
1627 | } |
---|
1628 | } |
---|
1629 | } |
---|
1630 | } |
---|
1631 | |
---|
1632 | // Remove obsolete columns. |
---|
1633 | foreach ($previous_schema['fields'] as $column => $attributes) { |
---|
1634 | if (!isset($new_schema['fields'][$column])) { |
---|
1635 | if (!in_array($column, array('nid', 'vid', 'delta'))) { |
---|
1636 | db_drop_field($ret, $previous_table, $column); |
---|
1637 | } |
---|
1638 | } |
---|
1639 | } |
---|
1640 | |
---|
1641 | // TODO: debugging stuff - should be removed |
---|
1642 | if (module_exists('devel')) { |
---|
1643 | //dsm($ret); |
---|
1644 | } |
---|
1645 | return $ret; |
---|
1646 | } |
---|
1647 | |
---|
1648 | /** |
---|
1649 | * Helper function for handling cleanup operations when schema changes are made. |
---|
1650 | */ |
---|
1651 | function content_alter_db_cleanup() { |
---|
1652 | // Rebuild the whole database schema. |
---|
1653 | // TODO: this could be optimized. We don't need to rebuild in *every case*... |
---|
1654 | // Or do we? This affects the schema and menu and may have unfortunate |
---|
1655 | // delayed effects if we don't clear everything out at this point. |
---|
1656 | content_clear_type_cache(TRUE); |
---|
1657 | } |
---|
1658 | |
---|
1659 | /** |
---|
1660 | * Helper function to order fields and groups when theming (preprocessing) |
---|
1661 | * overview forms. |
---|
1662 | * |
---|
1663 | * The $form is passed by reference because we assign depths as parenting |
---|
1664 | * relationships are sorted out. |
---|
1665 | */ |
---|
1666 | function _content_overview_order(&$form, $field_rows, $group_rows) { |
---|
1667 | // Put weight and parenting values into a $dummy render structure |
---|
1668 | // and let drupal_render figure out the corresponding row order. |
---|
1669 | $dummy = array(); |
---|
1670 | // Group rows: account for weight. |
---|
1671 | if (module_exists('fieldgroup')) { |
---|
1672 | foreach ($group_rows as $name) { |
---|
1673 | $dummy[$name] = array('#weight' => $form[$name]['weight']['#value'], '#value' => $name .' '); |
---|
1674 | } |
---|
1675 | } |
---|
1676 | // Field rows : account for weight and parenting. |
---|
1677 | foreach ($field_rows as $name) { |
---|
1678 | $dummy[$name] = array('#weight' => $form[$name]['weight']['#value'], '#value' => $name .' '); |
---|
1679 | if (module_exists('fieldgroup')) { |
---|
1680 | if ($parent = $form[$name]['parent']['#value']) { |
---|
1681 | $form[$name]['#depth'] = 1; |
---|
1682 | $dummy[$parent][$name] = $dummy[$name]; |
---|
1683 | unset($dummy[$name]); |
---|
1684 | } |
---|
1685 | } |
---|
1686 | } |
---|
1687 | // Nested fieldgroup |
---|
1688 | if (module_exists('fieldgroup')) { |
---|
1689 | // readjust the depth and parenting of fieldgroup |
---|
1690 | $nested = array(); |
---|
1691 | foreach ($group_rows as $name) { |
---|
1692 | if (empty($form[$name]['#depth'])) $form[$name]['#depth'] = 0; |
---|
1693 | |
---|
1694 | // Skip top level groups, only nested groups need adjustment. |
---|
1695 | if ($parent = $form[$name]['parent']['#value']) { |
---|
1696 | $form[$name]['#depth'] = $form[$parent]['#depth'] + 1; |
---|
1697 | if (array_key_exists($parent, $nested)){ |
---|
1698 | $nested[$parent][$name] = $dummy[$name]; |
---|
1699 | $nested[$name] = & $nested[$parent][$name]; |
---|
1700 | } |
---|
1701 | else { |
---|
1702 | $dummy[$parent][$name] = $dummy[$name]; |
---|
1703 | $nested[$name] = & $dummy[$parent][$name]; |
---|
1704 | } |
---|
1705 | unset($dummy[$name]); |
---|
1706 | } |
---|
1707 | } |
---|
1708 | // readjust the depth |
---|
1709 | foreach ($field_rows as $name) { |
---|
1710 | if ($parent = $form[$name]['parent']['#value']) { |
---|
1711 | $form[$name]['#depth'] = $form[$parent]['#depth'] + 1; |
---|
1712 | } |
---|
1713 | } |
---|
1714 | } |
---|
1715 | |
---|
1716 | return $dummy ? explode(' ', trim(drupal_render($dummy))) : array(); |
---|
1717 | } |
---|
1718 | |
---|
1719 | /** |
---|
1720 | * Batching process for changing the field schema, |
---|
1721 | * running each affected node through node_save() first, to |
---|
1722 | * fire all hooks. |
---|
1723 | * |
---|
1724 | * TODO This is just a placeholder for now because batching can't be safely |
---|
1725 | * used with API hooks. Need to come back and figure out how to incorporate |
---|
1726 | * this and get it working properly when the fields are altered via the API. |
---|
1727 | */ |
---|
1728 | function content_alter_fields($previous_field, $new_field) { |
---|
1729 | // See what values need to be updated in the field data. |
---|
1730 | $mask = content_alter_db_mask($previous_field, $new_field); |
---|
1731 | |
---|
1732 | // We use batch processing to prevent timeout when updating a large number |
---|
1733 | // of nodes. If there is no previous data to adjust, we can just go straight |
---|
1734 | // to altering the schema, otherwise use batch processing to update |
---|
1735 | // the database one node at a time, then update the schema. |
---|
1736 | if (empty($mask)) { |
---|
1737 | return content_alter_db($previous_field, $new_field); |
---|
1738 | } |
---|
1739 | $updates = array( |
---|
1740 | 'mask' => $mask['mask'], |
---|
1741 | 'alt_mask' => $mask['alt_mask'], |
---|
1742 | 'delta' => $mask['delta'], |
---|
1743 | ); |
---|
1744 | $batch = array( |
---|
1745 | 'operations' => array( |
---|
1746 | array('content_field_batch_update', array($previous_field['field_name'] => $updates)), |
---|
1747 | array('content_alter_db', array($previous_field, $new_field)) |
---|
1748 | ), |
---|
1749 | 'finished' => '_content_alter_fields_finished', |
---|
1750 | 'title' => t('Processing'), |
---|
1751 | 'error_message' => t('The update has encountered an error.'), |
---|
1752 | 'file' => './'. drupal_get_path('module', 'content') .'/includes/content.admin.inc', |
---|
1753 | ); |
---|
1754 | batch_set($batch); |
---|
1755 | if (!empty($url)) { |
---|
1756 | batch_process($url, $url); |
---|
1757 | } |
---|
1758 | } |
---|
1759 | |
---|
1760 | /** |
---|
1761 | * Content Replace Fields 'finished' callback. |
---|
1762 | */ |
---|
1763 | function _content_alter_fields_finished($success, $results, $operations) { |
---|
1764 | if ($success) { |
---|
1765 | drupal_set_message(t('The database has been altered and data has been migrated or deleted.')); |
---|
1766 | } |
---|
1767 | else { |
---|
1768 | drupal_set_message(t('An error occurred and database alteration did not complete.'), 'error'); |
---|
1769 | $message = format_plural(count($results), '1 item successfully processed:', '@count items successfully processed:'); |
---|
1770 | $message .= theme('item_list', $results); |
---|
1771 | drupal_set_message($message); |
---|
1772 | } |
---|
1773 | } |
---|
1774 | |
---|
1775 | /** |
---|
1776 | * Create a mask for the column data that should be deleted in each field. |
---|
1777 | * |
---|
1778 | * This is a bit tricky. We could theoretically have some columns |
---|
1779 | * that should be set to empty and others with valid info that should |
---|
1780 | * not be emptied out. But if delta values > X are to be wiped out, they |
---|
1781 | * need to wipe out even columns that still have values. And the NULL |
---|
1782 | * values in these columns after the alteration may be enough to make |
---|
1783 | * the item 'empty', as defined by hook_content_is_empty(), even if |
---|
1784 | * some columns still have values, so all these things need to be tested. |
---|
1785 | */ |
---|
1786 | function content_alter_db_mask($previous_field, $new_field) { |
---|
1787 | // Get an array of column values that will be dropped by this |
---|
1788 | // schema change and create a mask to feed to content_batch_update. |
---|
1789 | |
---|
1790 | $dropped = content_alter_db_analyze($previous_field, $new_field); |
---|
1791 | if (empty($dropped)) { |
---|
1792 | return array(); |
---|
1793 | } |
---|
1794 | $mask = array('mask' => array()); |
---|
1795 | foreach (array_keys($previous_field['columns']) as $column_name) { |
---|
1796 | // The basic mask will empty the dropped columns. |
---|
1797 | if (isset($dropped['columns']) && in_array($column_name, $dropped['columns'])) { |
---|
1798 | $mask['mask'][$column_name] = NULL; |
---|
1799 | } |
---|
1800 | // Over the delta we'll empty all columns. |
---|
1801 | if (isset($dropped['delta'])) { |
---|
1802 | $mask['alt_mask'][$column_name] = NULL; |
---|
1803 | } |
---|
1804 | } |
---|
1805 | if (isset($dropped['delta'])) { |
---|
1806 | $mask['delta'] = $dropped['delta']; |
---|
1807 | } |
---|
1808 | return $mask; |
---|
1809 | } |
---|
1810 | |
---|
1811 | /** |
---|
1812 | * Content Field Batch Update Operation |
---|
1813 | * |
---|
1814 | * Find all nodes that contain a field and update their values. |
---|
1815 | * |
---|
1816 | * @param $updates |
---|
1817 | * an array like: |
---|
1818 | * 'field_name' => array( |
---|
1819 | * 'mask' => array() |
---|
1820 | * // Keyed array of column names and replacement values for use |
---|
1821 | * // below delta, or for all values if no delta is supplied. |
---|
1822 | * 'alt_mask' => array() |
---|
1823 | * // Optional, keyed array of column names and replacement values for use |
---|
1824 | * // at or above delta, if a delta is supplied. |
---|
1825 | * 'delta' => # |
---|
1826 | * // Optional, the number to use as the delta value where you switch from |
---|
1827 | * // one mask to the other. |
---|
1828 | * ), |
---|
1829 | */ |
---|
1830 | function content_field_batch_update($updates, &$context) { |
---|
1831 | if (empty($field)) { |
---|
1832 | $context['finished'] = 1; |
---|
1833 | return; |
---|
1834 | } |
---|
1835 | $field_name = $updates['field_name']; |
---|
1836 | $field = content_fields($field_name); |
---|
1837 | |
---|
1838 | if (!isset($context['sandbox']['progress'])) { |
---|
1839 | $db_info = content_database_info($field); |
---|
1840 | |
---|
1841 | // Might run into non-existent tables when cleaning up a corrupted |
---|
1842 | // database, like some of the old content storage changes in the |
---|
1843 | // .install files. |
---|
1844 | if (!db_table_exists($db_info['table'])) { |
---|
1845 | return $context['finished'] = 1; |
---|
1846 | } |
---|
1847 | $nodes = array(); |
---|
1848 | $result = db_query("SELECT nid FROM {". $db_info['table'] ."}"); |
---|
1849 | while ($node = db_fetch_array($result)) { |
---|
1850 | $nodes[] = $node['nid']; |
---|
1851 | } |
---|
1852 | $context['sandbox']['progress'] = 0; |
---|
1853 | $context['sandbox']['max'] = count($nodes); |
---|
1854 | $context['sandbox']['nodes'] = $nodes; |
---|
1855 | } |
---|
1856 | |
---|
1857 | // Process nodes by groups of 5. |
---|
1858 | $count = min(5, count($context['sandbox']['nodes'])); |
---|
1859 | |
---|
1860 | for ($i = 1; $i <= $count; $i++) { |
---|
1861 | // For each nid, load the node, empty the column values |
---|
1862 | // or the whole field, and re-save it. |
---|
1863 | $nid = array_shift($context['sandbox']['nodes']); |
---|
1864 | $node = content_field_replace($nid, array($updates)); |
---|
1865 | |
---|
1866 | // Store result for post-processing in the finished callback. |
---|
1867 | $context['results'][] = l($node->title, 'node/'. $node->nid); |
---|
1868 | |
---|
1869 | // Update our progress information. |
---|
1870 | $context['sandbox']['progress']++; |
---|
1871 | $context['message'] = t('Processing %title', array('%title' => $node->title)); |
---|
1872 | } |
---|
1873 | |
---|
1874 | // Inform the batch engine that we are not finished, |
---|
1875 | // and provide an estimation of the completion level we reached. |
---|
1876 | if ($context['sandbox']['progress'] != $context['sandbox']['max']) { |
---|
1877 | $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max']; |
---|
1878 | } |
---|
1879 | } |
---|
1880 | |
---|
1881 | /** |
---|
1882 | * Content Field Replace |
---|
1883 | * |
---|
1884 | * Replace field values in a node from an array of update values. |
---|
1885 | * |
---|
1886 | * Supply an array of one or more fields and masks of field column values |
---|
1887 | * to be replaced into field values, one mask for basic values and an optional |
---|
1888 | * different mask for values in field items equal to or higher than a |
---|
1889 | * specified delta. |
---|
1890 | * |
---|
1891 | * The masks should contain only the column values to be substituted in. |
---|
1892 | * The supplied values will be merged into the existing values to replace |
---|
1893 | * only the values in the mask, leaving all other values unchanged. |
---|
1894 | * |
---|
1895 | * The ability to set different masks starting at a delta allows the |
---|
1896 | * possibility of setting values above a certain delta to NULL prior |
---|
1897 | * to altering the database schema. |
---|
1898 | * |
---|
1899 | * @param $nid |
---|
1900 | * @param $updates |
---|
1901 | * an array like: |
---|
1902 | * 'field_name' => array( |
---|
1903 | * 'mask' => array() |
---|
1904 | * // Keyed array of column names and replacement values for use |
---|
1905 | * // below delta, or for all values if no delta is supplied. |
---|
1906 | * 'alt_mask' => array() |
---|
1907 | * // Optional, keyed array of column names and replacement values for use |
---|
1908 | * // at or above delta, if a delta is supplied. |
---|
1909 | * 'delta' => # |
---|
1910 | * // Optional, the number to use as the delta value where you switch from |
---|
1911 | * // one mask to the other. |
---|
1912 | * ), |
---|
1913 | */ |
---|
1914 | function content_field_replace($nid, $updates) { |
---|
1915 | $node = node_load($nid, NULL, TRUE); |
---|
1916 | foreach ($updates as $field_name => $update) { |
---|
1917 | $items = isset($node->$field_name) ? $node->$field_name : array(); |
---|
1918 | foreach ($items as $delta => $value) { |
---|
1919 | $field_mask = (isset($update['delta']) && isset($update['alt_mask']) && $delta >= $update['delta']) ? $update['alt_mask'] : $mask['mask']; |
---|
1920 | // Merge the mask into the field values to do the replacements. |
---|
1921 | $items[$delta] = array_merge($items[$delta], $field_mask); |
---|
1922 | } |
---|
1923 | // Test if the new values will make items qualify as empty. |
---|
1924 | $items = content_set_empty($field, $items); |
---|
1925 | $node->$field_name = $items; |
---|
1926 | } |
---|
1927 | node_save($node); |
---|
1928 | return $node; |
---|
1929 | } |
---|
1930 | |
---|
1931 | /** |
---|
1932 | * Helper form element validator : integer. |
---|
1933 | */ |
---|
1934 | function _element_validate_integer($element, &$form_state) { |
---|
1935 | $value = $element['#value']; |
---|
1936 | if ($value !== '' && (!is_numeric($value) || intval($value) != $value)) { |
---|
1937 | form_error($element, t('%name must be an integer.', array('%name' => $element['#title']))); |
---|
1938 | } |
---|
1939 | } |
---|
1940 | |
---|
1941 | /** |
---|
1942 | * Helper form element validator : integer > 0. |
---|
1943 | */ |
---|
1944 | function _element_validate_integer_positive($element, &$form_state) { |
---|
1945 | $value = $element['#value']; |
---|
1946 | if ($value !== '' && (!is_numeric($value) || intval($value) != $value || $value <= 0)) { |
---|
1947 | form_error($element, t('%name must be a positive integer.', array('%name' => $element['#title']))); |
---|
1948 | } |
---|
1949 | } |
---|
1950 | |
---|
1951 | /** |
---|
1952 | * Helper form element validator : number. |
---|
1953 | */ |
---|
1954 | function _element_validate_number($element, &$form_state) { |
---|
1955 | $value = $element['#value']; |
---|
1956 | if ($value != '' && !is_numeric($value)) { |
---|
1957 | form_error($element, t('%name must be a number.', array('%name' => $element['#title']))); |
---|
1958 | } |
---|
1959 | } |
---|