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