content_types.inc 14.7 KB
Newer Older
drumm's avatar
drumm committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
// $Id$

/**
 * @file
 * Content type editing UI.
 */

/**
 * Displays the content type admin overview page.
 */
function node_overview_types() {
  $types = node_get_types();
  $names = node_get_types('names');
15
  $header = array(t('Name'), array('data' => t('Operations'), 'colspan' => '2'));
drumm's avatar
drumm committed
16
17
18
19
  $rows = array();

  foreach ($names as $key => $name) {
    $type = $types[$key];
20
    if (node_hook($type, 'form')) {
drumm's avatar
drumm committed
21
      $type_url_str = str_replace('_', '-', $type->type);
22
      $row = array(theme('node_admin_overview', $name, $type));
drumm's avatar
drumm committed
23
      // Set the edit column.
Dries's avatar
Dries committed
24
      $row[] = array('data' => l(t('edit'), 'admin/build/node-type/' . $type_url_str));
drumm's avatar
drumm committed
25
26
27

      // Set the delete column.
      if ($type->custom) {
Dries's avatar
Dries committed
28
        $row[] = array('data' => l(t('delete'), 'admin/build/node-type/' . $type_url_str . '/delete'));
drumm's avatar
drumm committed
29
30
      }
      else {
31
        $row[] = array('data' => '');
drumm's avatar
drumm committed
32
33
34
35
36
37
38
39
40
41
42
43
      }
      $rows[] = $row;
    }
  }

  if (empty($rows)) {
    $rows[] = array(array('data' => t('No content types available.'), 'colspan' => '5', 'class' => 'message'));
  }

  return theme('table', $header, $rows);
}

44
45
46
47
48
49
50
function theme_node_admin_overview($name, $type) {
  $output = check_plain($name);
  $output .= ' <small> (Machine name: ' . check_plain($type->type) . ')</small>';
  $output .= '<div class="description">' . filter_xss_admin($type->description) . '</div>';
  return $output;
}

drumm's avatar
drumm committed
51
52
53
/**
 * Generates the node type editing form.
 */
54
function node_type_form(&$form_state, $type = NULL) {
55
  drupal_add_js(drupal_get_path('module', 'node') .'/content_types.js');
drumm's avatar
drumm committed
56
  if (!isset($type->type)) {
57
    // This is a new type. Node module managed types are custom and unlocked.
58
    $type = node_type_set_defaults(array('custom' => 1, 'locked' => 0));
drumm's avatar
drumm committed
59
60
  }

61
62
  // Make the type object available to implementations of hook_form_alter.
  $form['#node_type'] = $type;
63

drumm's avatar
drumm committed
64
65
66
67
68
69
70
71
  $form['identity'] = array(
    '#type' => 'fieldset',
    '#title' => t('Identification'),
  );
  $form['identity']['name'] = array(
    '#title' => t('Name'),
    '#type' => 'textfield',
    '#default_value' => $type->name,
72
    '#description' => t('The human-readable name of this content type. This text will be displayed as part of the list on the <em>create content</em> page. It is recommended that this name begin with a capital letter and contain only letters, numbers, and <strong>spaces</strong>. This name must be unique.'),
drumm's avatar
drumm committed
73
    '#required' => TRUE,
74
    '#field_suffix' => ' <small id="node-type-name-suffix">&nbsp;</small>',
drumm's avatar
drumm committed
75
76
77
78
  );

  if (!$type->locked) {
    $form['identity']['type'] = array(
79
      '#title' => t('Machine name'),
drumm's avatar
drumm committed
80
81
82
83
      '#type' => 'textfield',
      '#default_value' => $type->type,
      '#maxlength' => 32,
      '#required' => TRUE,
84
      '#description' => t('The machine-readable name of this content type. This text will be used for constructing the URL of the <em>create content</em> page for this content type. This name must contain only lowercase letters, numbers, and underscores. Underscores will be converted into hyphens when constructing the URL of the <em>create content</em> page. This name must be unique.'),
drumm's avatar
drumm committed
85
86
87
88
89
90
91
92
    );
  }
  else {
    $form['identity']['type'] = array(
      '#type' => 'value',
      '#value' => $type->type,
    );
    $form['identity']['type_display'] = array(
93
      '#title' => t('Machine name'),
drumm's avatar
drumm committed
94
      '#type' => 'item',
95
      '#markup' => theme('placeholder', $type->type),
drumm's avatar
drumm committed
96
97
98
99
      '#description' => t('The machine-readable name of this content type. This field cannot be modified for system-defined content types.'),
    );
  }

100
101
102
103
104
105
106
  $form['identity']['description'] = array(
    '#title' => t('Description'),
    '#type' => 'textarea',
    '#default_value' => $type->description,
    '#description' => t('A brief description of this content type. This text will be displayed as part of the list on the <em>create content</em> page.'),
    );

drumm's avatar
drumm committed
107
108
  $form['submission'] = array(
    '#type' => 'fieldset',
109
    '#title' => t('Submission form settings'),
drumm's avatar
drumm committed
110
111
    '#collapsible' => TRUE,
  );
112
113
114
115
116
117
118
119
120
121
122
123
  $form['submission']['title_label'] = array(
    '#title' => t('Title field label'),
    '#type' => 'textfield',
    '#default_value' => $type->title_label,
    '#required' => TRUE,
  );
  if (!$type->has_title) {
    // Avoid overwriting a content type that intentionally does not have a
    // title field.
    $form['submission']['title_label']['#attributes'] = array('disabled' => 'disabled');
    $form['submission']['title_label']['#description'] = t('This content type does not have a title field.');
    $form['submission']['title_label']['#required'] = FALSE;
drumm's avatar
drumm committed
124
  }
125
126
127
  $form['submission']['body_label'] = array(
    '#title' => t('Body field label'),
    '#type' => 'textfield',
128
    '#default_value' => isset($type->body_label) ? $type->body_label : '',
129
130
    '#description' => t('To omit the body field for this content type, remove any text and leave this field blank.'),
  );
drumm's avatar
drumm committed
131
132
133
134
  $form['submission']['min_word_count'] = array(
    '#type' => 'select',
    '#title' => t('Minimum number of words'),
    '#default_value' => $type->min_word_count,
135
    '#options' => drupal_map_assoc(array(0, 1, 10, 25, 50, 75, 100, 125, 150, 175, 200)),
drumm's avatar
drumm committed
136
137
    '#description' => t('The minimum number of words for the body field to be considered valid for this content type. This can be useful to rule out submissions that do not meet the site\'s standards, such as short test posts.')
  );
138
139
140
  $form['submission']['help']  = array(
    '#type' => 'textarea',
    '#title' => t('Explanation or submission guidelines'),
141
    '#default_value' => $type->help,
142
143
    '#description' => t('This text will be displayed at the top of the submission form for this content type. It is useful for helping or instructing your users.')
  );
drumm's avatar
drumm committed
144
145
  $form['workflow'] = array(
    '#type' => 'fieldset',
146
    '#title' => t('Workflow settings'),
drumm's avatar
drumm committed
147
    '#collapsible' => TRUE,
148
    '#collapsed' => TRUE,
drumm's avatar
drumm committed
149
150
151
  );
  $form['workflow']['node_options'] = array('#type' => 'checkboxes',
    '#title' => t('Default options'),
152
    '#default_value' => variable_get('node_options_' . $type->type, array('status', 'promote')),
drumm's avatar
drumm committed
153
154
155
156
157
158
159
160
    '#options' => array(
      'status' => t('Published'),
      'promote' => t('Promoted to front page'),
      'sticky' => t('Sticky at top of lists'),
      'revision' => t('Create new revision'),
    ),
    '#description' => t('Users with the <em>administer nodes</em> permission will be able to override these options.'),
  );
161
162
163
164
165
166
167
168
169
170
  $form['display'] = array(
    '#type' => 'fieldset',
    '#title' => t('Display settings'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['display']['node_submitted'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display post information'),
    '#default_value' => variable_get('node_submitted_'. $type->type, TRUE),
Dries's avatar
Dries committed
171
172
    '#description' => t('Enable the <em>submitted by Username on date</em> text.'),
  );
drumm's avatar
drumm committed
173
174
175
176
177
178
  $form['old_type'] = array(
    '#type' => 'value',
    '#value' => $type->type,
  );
  $form['orig_type'] = array(
    '#type' => 'value',
179
    '#value' => isset($type->orig_type) ? $type->orig_type : '',
drumm's avatar
drumm committed
180
  );
181
  $form['base'] = array(
drumm's avatar
drumm committed
182
    '#type' => 'value',
183
    '#value' => $type->base,
drumm's avatar
drumm committed
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
  );
  $form['custom'] = array(
    '#type' => 'value',
    '#value' => $type->custom,
  );
  $form['modified'] = array(
    '#type' => 'value',
    '#value' => $type->modified,
  );
  $form['locked'] = array(
    '#type' => 'value',
    '#value' => $type->locked,
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save content type'),
201
    '#weight' => 40,
drumm's avatar
drumm committed
202
203
204
205
206
207
208
  );

  if ($type->custom) {
    if (!empty($type->type)) {
      $form['delete'] = array(
        '#type' => 'submit',
        '#value' => t('Delete content type'),
209
        '#weight' => 45,
drumm's avatar
drumm committed
210
211
212
213
214
215
216
      );
    }
  }
  else {
    $form['reset'] = array(
      '#type' => 'submit',
      '#value' => t('Reset to defaults'),
217
      '#weight' => 50,
drumm's avatar
drumm committed
218
219
220
    );
  }

221
  return $form;
drumm's avatar
drumm committed
222
223
224
225
226
}

/**
 * Implementation of hook_form_validate().
 */
227
function node_type_form_validate($form, &$form_state) {
drumm's avatar
drumm committed
228
  $type = new stdClass();
229
230
  $type->type = trim($form_state['values']['type']);
  $type->name = trim($form_state['values']['name']);
drumm's avatar
drumm committed
231
232

  // Work out what the type was before the user submitted this form
233
  $old_type = trim($form_state['values']['old_type']);
drumm's avatar
drumm committed
234
235
236

  $types = node_get_types('names');

237
  if (!$form_state['values']['locked']) {
drumm's avatar
drumm committed
238
    if (isset($types[$type->type]) && $type->type != $old_type) {
239
      form_set_error('type', t('The machine-readable name %type is already taken.', array('%type' => $type->type)));
drumm's avatar
drumm committed
240
    }
241
    if (!preg_match('!^[a-z0-9_]+$!', $type->type)) {
242
      form_set_error('type', t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
drumm's avatar
drumm committed
243
    }
244
245
246
247
    // 'theme' conflicts with theme_node_form().
    // '0' is invalid, since elsewhere we check it using empty().
    if (in_array($type->type, array('0', 'theme'))) {
      form_set_error('type', t("Invalid machine-readable name. Please enter a name other than %invalid.", array('%invalid' => $type->type)));
248
    }
drumm's avatar
drumm committed
249
250
251
252
253
  }

  $names = array_flip($types);

  if (isset($names[$type->name]) && $names[$type->name] != $old_type) {
254
    form_set_error('name', t('The human-readable name %name is already taken.', array('%name' => $type->name)));
drumm's avatar
drumm committed
255
256
257
258
259
260
  }
}

/**
 * Implementation of hook_form_submit().
 */
261
262
function node_type_form_submit($form, &$form_state) {
  $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
drumm's avatar
drumm committed
263

264
  $type = node_type_set_defaults();
drumm's avatar
drumm committed
265

266
267
268
269
  $type->type = trim($form_state['values']['type']);
  $type->name = trim($form_state['values']['name']);
  $type->orig_type = trim($form_state['values']['orig_type']);
  $type->old_type = isset($form_state['values']['old_type']) ? $form_state['values']['old_type'] : $type->type;
drumm's avatar
drumm committed
270

271
272
273
274
275
  $type->description = $form_state['values']['description'];
  $type->help = $form_state['values']['help'];
  $type->min_word_count = $form_state['values']['min_word_count'];
  $type->title_label = $form_state['values']['title_label'];
  $type->body_label = $form_state['values']['body_label'];
drumm's avatar
drumm committed
276

277
278
279
280
281
  // title_label is required in core; has_title will always be true, unless a
  // module alters the title field.
  $type->has_title = ($type->title_label != '');
  $type->has_body = ($type->body_label != '');

282
  $type->base = !empty($form_state['values']['base']) ? $form_state['values']['base'] : 'node_content';
283
  $type->custom = $form_state['values']['custom'];
drumm's avatar
drumm committed
284
  $type->modified = TRUE;
285
  $type->locked = $form_state['values']['locked'];
drumm's avatar
drumm committed
286
287
288
289
290

  if ($op == t('Reset to defaults')) {
    node_type_reset($type);
  }
  elseif ($op == t('Delete content type')) {
Dries's avatar
Dries committed
291
    $form_state['redirect'] = 'admin/build/node-type/' . str_replace('_', '-', $type->old_type) . '/delete';
292
    return;
drumm's avatar
drumm committed
293
294
295
296
  }

  $status = node_type_save($type);

297
  $variables = $form_state['values'];
298

drumm's avatar
drumm committed
299
300
  // Remove everything that's been saved already - whatever's left is assumed
  // to be a persistent variable.
301
  foreach ($variables as $key => $value) {
drumm's avatar
drumm committed
302
    if (isset($type->$key)) {
303
      unset($variables[$key]);
drumm's avatar
drumm committed
304
305
306
    }
  }

307
  unset($variables['form_token'], $variables['op'], $variables['submit'], $variables['delete'], $variables['reset'], $variables['form_id']);
drumm's avatar
drumm committed
308
309

  // Save or reset persistent variable values.
310
  foreach ($variables as $key => $value) {
311
312
    $variable_new = $key . '_' . $type->type;
    $variable_old = $key . '_' . $type->old_type;
313

drumm's avatar
drumm committed
314
    if ($op == t('Reset to defaults')) {
315
      variable_del($variable_old);
drumm's avatar
drumm committed
316
317
318
319
320
    }
    else {
      if (is_array($value)) {
        $value = array_keys(array_filter($value));
      }
321
      variable_set($variable_new, $value);
drumm's avatar
drumm committed
322

323
324
      if ($variable_new != $variable_old) {
        variable_del($variable_old);
drumm's avatar
drumm committed
325
326
327
328
329
      }
    }
  }

  node_types_rebuild();
330
  $t_args = array('%name' => $type->name);
drumm's avatar
drumm committed
331
332
333
334
335
336
337
338
339
340
341

  if ($op == t('Reset to defaults')) {
    drupal_set_message(t('The content type %name has been reset to its default values.', $t_args));
    return;
  }

  if ($status == SAVED_UPDATED) {
    drupal_set_message(t('The content type %name has been updated.', $t_args));
  }
  elseif ($status == SAVED_NEW) {
    drupal_set_message(t('The content type %name has been added.', $t_args));
342
    watchdog('node', 'Added content type %name.', $t_args, WATCHDOG_NOTICE, l(t('view'), 'admin/build/types'));
drumm's avatar
drumm committed
343
344
  }

345
  $form_state['redirect'] = 'admin/build/types';
346
  return;
drumm's avatar
drumm committed
347
348
}

349
350
351
352
/**
 * Implementation of hook_node_type().
 */
function node_node_type($op, $info) {
353
  if ($op != 'delete' && !empty($info->old_type) && $info->old_type != $info->type) {
354
355
356
    $update_count = node_type_update_nodes($info->old_type, $info->type);

    if ($update_count) {
357
      drupal_set_message(format_plural($update_count, 'Changed the content type of 1 post from %old-type to %type.', 'Changed the content type of @count posts from %old-type to %type.', array('%old-type' => $info->old_type, '%type' => $info->type)));
358
359
360
361
    }
  }
}

drumm's avatar
drumm committed
362
363
364
365
366
367
368
369
370
371
/**
 * Resets all of the relevant fields of a module-defined node type to their
 * default values.
 *
 * @param &$type
 *   The node type to reset. The node type is passed back by reference with its
 *   resetted values. If there is no module-defined info for this node type,
 *   then nothing happens.
 */
function node_type_reset(&$type) {
372
  $info_array = module_invoke_all('node_info');
drumm's avatar
drumm committed
373
  if (isset($info_array[$type->orig_type])) {
374
    $info_array[$type->orig_type]['type'] = $type->orig_type;
375
    $info = node_type_set_defaults($info_array[$type->orig_type]);
drumm's avatar
drumm committed
376
377
378
379
380
381
382
383
384
385

    foreach ($info as $field => $value) {
      $type->$field = $value;
    }
  }
}

/**
 * Menu callback; delete a single content type.
 */
386
function node_type_delete_confirm(&$form_state, $type) {
drumm's avatar
drumm committed
387
388
389
  $form['type'] = array('#type' => 'value', '#value' => $type->type);
  $form['name'] = array('#type' => 'value', '#value' => $type->name);

390
  $message = t('Are you sure you want to delete the content type %type?', array('%type' => $type->name));
drumm's avatar
drumm committed
391
392
  $caption = '';

393
  $num_nodes = db_query("SELECT COUNT(*) FROM {node} WHERE type = :type", array(':type' => $type->type))->fetchField();
drumm's avatar
drumm committed
394
  if ($num_nodes) {
395
    $caption .= '<p>' . format_plural($num_nodes, '<strong>Warning:</strong> there is currently 1 %type post on your site. It may not be able to be displayed or edited correctly, once you have removed this content type.', '<strong>Warning:</strong> there are currently @count %type posts on your site. They may not be able to be displayed or edited correctly, once you have removed this content type.', array('%type' => $type->name)) . '</p>';
drumm's avatar
drumm committed
396
397
  }

398
  $caption .= '<p>' . t('This action cannot be undone.') . '</p>';
drumm's avatar
drumm committed
399

400
  return confirm_form($form, $message, 'admin/build/types', $caption, t('Delete'));
drumm's avatar
drumm committed
401
402
403
}

/**
404
 * Process content type delete confirm submissions.
drumm's avatar
drumm committed
405
 */
406
407
function node_type_delete_confirm_submit($form, &$form_state) {
  node_type_delete($form_state['values']['type']);
drumm's avatar
drumm committed
408

409
  $t_args = array('%name' => $form_state['values']['name']);
drumm's avatar
drumm committed
410
  drupal_set_message(t('The content type %name has been deleted.', $t_args));
411
  watchdog('menu', 'Deleted content type %name.', $t_args, WATCHDOG_NOTICE);
drumm's avatar
drumm committed
412
413
414

  node_types_rebuild();

415
  $form_state['redirect'] = 'admin/build/types';
416
  return;
drumm's avatar
drumm committed
417
}