Imported drupal-5.3
[drupal.git] / modules / node / content_types.inc
blobfc9c633f70bf9081d498e32a1be611fa030c781f
1 <?php
2 // $Id: content_types.inc,v 1.24.2.2 2007/07/26 19:16:46 drumm Exp $
4 /**
5  * @file
6  * Content type editing UI.
7  */
9 /**
10  * Displays the content type admin overview page.
11  */
12 function node_overview_types() {
13   $types = node_get_types();
14   $names = node_get_types('names');
15   $header = array(t('Name'), t('Type'), t('Description'), array('data' => t('Operations'), 'colspan' => '2'));
16   $rows = array();
18   foreach ($names as $key => $name) {
19     $type = $types[$key];
20     if (function_exists($type->module .'_form')) {
21       $type_url_str = str_replace('_', '-', $type->type);
22       // Populate the operations field.
23       $operations = array();
25       // Set the edit column.
26       $operations[] = array('data' => l(t('edit'), 'admin/content/types/'. $type_url_str));
28       // Set the delete column.
29       if ($type->custom) {
30         $operations[] = array('data' => l(t('delete'), 'admin/content/types/'. $type_url_str .'/delete'));
31       }
32       else {
33         $operations[] = array('data' => '');
34       }
36       $row = array(array('data' => l($name, 'admin/content/types/'. $type_url_str), 'class' => $class), array('data' => check_plain($type->type), 'class' => $class), array('data' => check_plain($type->description), 'class' => $class));
37       foreach ($operations as $operation) {
38         $operation['class'] = $class;
39         $row[] = $operation;
40       }
41       $rows[] = $row;
42     }
43   }
45   if (empty($rows)) {
46     $rows[] = array(array('data' => t('No content types available.'), 'colspan' => '5', 'class' => 'message'));
47   }
49   return theme('table', $header, $rows);
52 /**
53  * Generates the node type editing form.
54  */
55 function node_type_form($type = NULL) {
56   if (!isset($type->type)) {
57     $type = new stdClass();
58     $type->type = $type->name = $type->module = $type->description = $type->help = '';
59     $type->min_word_count = 0;
60     $type->has_title = TRUE;
61     $type->has_body = TRUE;
62     $type->title_label = t('Title');
63     $type->body_label = t('Body');
64     $type->custom = TRUE;
65     $type->modified = FALSE;
66     $type->locked = FALSE;
67   }
69   $form['#node_type'] = $type; // Make the type object available to implementations of hook_form_alter.
71   $form['identity'] = array(
72     '#type' => 'fieldset',
73     '#title' => t('Identification'),
74   );
75   $form['identity']['name'] = array(
76     '#title' => t('Name'),
77     '#type' => 'textfield',
78     '#default_value' => $type->name,
79     '#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 begins with a capital letter and consists only of letters, numbers, and <strong>spaces</strong>. This name must be unique to this content type.'),
80     '#required' => TRUE,
81   );
83   if (!$type->locked) {
84     $form['identity']['type'] = array(
85       '#title' => t('Type'),
86       '#type' => 'textfield',
87       '#default_value' => $type->type,
88       '#maxlength' => 32,
89       '#required' => TRUE,
90       '#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 may consist of only of lowercase letters, numbers, and underscores. Dashes are not allowed. Underscores will be converted into dashes when constructing the URL of the <em>create content</em> page. This name must be unique to this content type.'),
91     );
92   }
93   else {
94     $form['identity']['type'] = array(
95       '#type' => 'value',
96       '#value' => $type->type,
97     );
98     $form['identity']['type_display'] = array(
99       '#title' => t('Type'),
100       '#type' => 'item',
101       '#value' => theme('placeholder', $type->type),
102       '#description' => t('The machine-readable name of this content type. This field cannot be modified for system-defined content types.'),
103     );
104   }
106   $form['identity']['description'] = array(
107     '#title' => t('Description'),
108     '#type' => 'textarea',
109     '#default_value' => $type->description,
110     '#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.'),
111     );
113   $form['submission'] = array(
114     '#type' => 'fieldset',
115     '#title' =>t('Submission form'),
116     '#collapsible' => TRUE,
117     '#collapsed' => FALSE,
118   );
119   $form['submission']['title_label'] = array(
120     '#title' => t('Title field label'),
121     '#type' => 'textfield',
122     '#default_value' => $type->title_label,
123     '#required' => TRUE,
124   );
125   if (!$type->has_title) {
126     // Avoid overwriting a content type that intentionally does not have a
127     // title field.
128     $form['submission']['title_label']['#attributes'] = array('disabled' => 'disabled');
129     $form['submission']['title_label']['#description'] = t('This content type does not have a title field.');
130     $form['submission']['title_label']['#required'] = FALSE;
131   }
132   $form['submission']['body_label'] = array(
133     '#title' => t('Body field label'),
134     '#type' => 'textfield',
135     '#default_value' => $type->body_label,
136     '#description' => t('To omit the body field for this content type, remove any text and leave this field blank.'),
137   );
138   $form['submission']['min_word_count'] = array(
139     '#type' => 'select',
140     '#title' => t('Minimum number of words'),
141     '#default_value' => $type->min_word_count,
142     '#options' => drupal_map_assoc(array(0, 10, 25, 50, 75, 100, 125, 150, 175, 200)),
143     '#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.')
144   );
145   $form['submission']['help']  = array(
146     '#type' => 'textarea',
147     '#title' => t('Explanation or submission guidelines'),
148     '#default_value' =>  $type->help,
149     '#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.')
150   );
151   $form['workflow'] = array(
152     '#type' => 'fieldset',
153     '#title' =>t('Workflow'),
154     '#collapsible' => TRUE,
155   );
156   $form['workflow']['node_options'] = array('#type' => 'checkboxes',
157     '#title' => t('Default options'),
158     '#default_value' => variable_get('node_options_'. $type->type, array('status', 'promote')),
159     '#options' => array(
160       'status' => t('Published'),
161       'promote' => t('Promoted to front page'),
162       'sticky' => t('Sticky at top of lists'),
163       'revision' => t('Create new revision'),
164     ),
165     '#description' => t('Users with the <em>administer nodes</em> permission will be able to override these options.'),
166   );
168   $form['old_type'] = array(
169     '#type' => 'value',
170     '#value' => $type->type,
171   );
172   $form['orig_type'] = array(
173     '#type' => 'value',
174     '#value' => $type->orig_type,
175   );
176   $form['module'] = array(
177     '#type' => 'value',
178     '#value' => $type->module,
179   );
180   $form['custom'] = array(
181     '#type' => 'value',
182     '#value' => $type->custom,
183   );
184   $form['modified'] = array(
185     '#type' => 'value',
186     '#value' => $type->modified,
187   );
188   $form['locked'] = array(
189     '#type' => 'value',
190     '#value' => $type->locked,
191   );
193   $form['submit'] = array(
194     '#type' => 'submit',
195     '#value' => t('Save content type'),
196     '#weight' => 40,
197   );
199   if ($type->custom) {
200     if (!empty($type->type)) {
201       $form['delete'] = array(
202         '#type' => 'submit',
203         '#value' => t('Delete content type'),
204         '#weight' => 45,
205       );
206     }
207   }
208   else {
209     $form['reset'] = array(
210       '#type' => 'submit',
211       '#value' => t('Reset to defaults'),
212       '#weight' => 50,
213     );
214   }
216   return $form;
220  * Implementation of hook_form_validate().
221  */
222 function node_type_form_validate($form_id, $form_values) {
223   $type = new stdClass();
224   $type->type = trim($form_values['type']);
225   $type->name = trim($form_values['name']);
227   // Work out what the type was before the user submitted this form
228   $old_type = trim($form_values['old_type']);
229   if (empty($old_type)) {
230     $old_type = $type->type;
231   }
233   $types = node_get_types('names');
235   if (!$form_values['locked']) {
236     if (isset($types[$type->type]) && $type->type != $old_type) {
237       form_set_error('type', t('The machine-readable name %type is already taken.', array('%type' => $type->type)));
238     }
239     if (!preg_match('!^[a-z0-9_]+$!', $type->type)) {
240       form_set_error('type', t('The machine-readable name can only consist of lowercase letters, underscores, and numbers.'));
241     }
242     // The type cannot be just the character '0', since elsewhere we check it using empty().
243     if ($type->type === '0') {
244       form_set_error('type', t("Invalid type. Please enter a type name other than '0' (the character zero)."));
245     }
246   }
248   $names = array_flip($types);
250   if (isset($names[$type->name]) && $names[$type->name] != $old_type) {
251     form_set_error('name', t('The human-readable name %name is already taken.', array('%name' => $names[$type->name])));
252   }
256  * Implementation of hook_form_submit().
257  */
258 function node_type_form_submit($form_id, $form_values) {
259   $op = isset($form_values['op']) ? $form_values['op'] : '';
261   $type = new stdClass();
263   $type->type = trim($form_values['type']);
264   $type->name = trim($form_values['name']);
265   $type->orig_type = trim($form_values['orig_type']);
266   $type->old_type = isset($form_values['old_type']) ? $form_values['old_type'] : $type->type;
268   $type->description = $form_values['description'];
269   $type->help = $form_values['help'];
270   $type->min_word_count = $form_values['min_word_count'];
271   $type->title_label = $form_values['title_label'];
272   $type->body_label = $form_values['body_label'];
274   // title_label is required in core; has_title will always be true, unless a
275   // module alters the title field.
276   $type->has_title = ($type->title_label != '');
277   $type->has_body = ($type->body_label != '');
279   $type->module = !empty($form_values['module']) ? $form_values['module'] : 'node';
280   $type->custom = $form_values['custom'];
281   $type->modified = TRUE;
282   $type->locked = $form_values['locked'];
284   if ($op == t('Reset to defaults')) {
285     node_type_reset($type);
286   }
287   elseif ($op == t('Delete content type')) {
288     return 'admin/content/types/'. str_replace('_', '-', $type->old_type) .'/delete';
289   }
291   $status = node_type_save($type);
293   $variables = $form_values;
295   // Remove everything that's been saved already - whatever's left is assumed
296   // to be a persistent variable.
297   foreach ($variables as $key => $value) {
298     if (isset($type->$key)) {
299       unset($variables[$key]);
300     }
301   }
303   unset($variables['form_token'], $variables['op'], $variables['submit'], $variables['delete'], $variables['reset'], $variables['form_id']);
305   // Save or reset persistent variable values.
306   foreach ($variables as $key => $value) {
307     $key .= '_'. $type->type;
308     if ($op == t('Reset to defaults')) {
309       variable_del($key);
310     }
311     else {
312       if (is_array($value)) {
313         $value = array_keys(array_filter($value));
314       }
315       variable_set($key, $value);
317       if ($type->old_type != $type->type) {
318         $key = str_replace($type->type, $type->old_type, $key);
319         variable_del($key);
320       }
321     }
322   }
324   node_types_rebuild();
325   // menu_rebuild clears the cache, too
326   menu_rebuild();
327   $t_args = array('%name' => $type->name);
329   if ($op == t('Reset to defaults')) {
330     drupal_set_message(t('The content type %name has been reset to its default values.', $t_args));
331     return;
332   }
334   if ($status == SAVED_UPDATED) {
335     drupal_set_message(t('The content type %name has been updated.', $t_args));
336   }
337   elseif ($status == SAVED_NEW) {
338     drupal_set_message(t('The content type %name has been added.', $t_args));
339     watchdog('node', t('Added content type %name.', $t_args), WATCHDOG_NOTICE, l(t('view'), 'admin/content/types'));
340   }
342   return 'admin/content/types';
346  * Implementation of hook_node_type().
347  */
348 function node_node_type($op, $info) {
349   if ($op != 'delete' && !empty($info->old_type) && $info->old_type != $info->type) {
350     $update_count = node_type_update_nodes($info->old_type, $info->type);
352     if ($update_count) {
353       $substr_pre = 'Changed the content type of ';
354       $substr_post = strtr(' from %old-type to %type.', array('%old-type' => theme('placeholder', $info->old_type), '%type' => theme('placeholder', $info->type)));
355       drupal_set_message(format_plural($update_count, $substr_pre .'@count post'. $substr_post, $substr_pre .'@count posts'. $substr_post));
356     }
357   }
361  * Resets all of the relevant fields of a module-defined node type to their
362  * default values.
364  * @param &$type
365  *   The node type to reset. The node type is passed back by reference with its
366  *   resetted values. If there is no module-defined info for this node type,
367  *   then nothing happens.
368  */
369 function node_type_reset(&$type) {
370   $info_array = module_invoke_all('node_info');
371   if (isset($info_array[$type->orig_type])) {
372     $info = _node_type_set_defaults($info_array[$type->orig_type]);
373     $info['type'] = $type->orig_type;
375     foreach ($info as $field => $value) {
376       $type->$field = $value;
377     }
378   }
382  * Menu callback; delete a single content type.
383  */
384 function node_type_delete_confirm($type) {
385   $form['type'] = array('#type' => 'value', '#value' => $type->type);
386   $form['name'] = array('#type' => 'value', '#value' => $type->name);
388   $message = t('Are you sure you want to delete the content type %type?', array('%type' => $type->name));
389   $caption = '';
391   $num_nodes = db_num_rows(db_query("SELECT * FROM {node} WHERE type = '%s'", $type->type));
392   if ($num_nodes) {
393     $caption .= '<p>'. strtr(format_plural($num_nodes, '<strong>Warning:</strong> there is currently @count %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' => theme('placeholder', $type->name))) .'</p>';
394   }
396   $caption .= '<p>'. t('This action cannot be undone.') .'</p>';
398   return confirm_form($form, $message, 'admin/content/types', $caption, t('Delete'));
402  * Process content type delete confirm submissions.
403  */
404 function node_type_delete_confirm_submit($form_id, $form_values) {
405   node_type_delete($form_values['type']);
407   $t_args = array('%name' => $form_values['name']);
408   drupal_set_message(t('The content type %name has been deleted.', $t_args));
409   watchdog('menu', t('Deleted content type %name.', $t_args), WATCHDOG_NOTICE);
411   node_types_rebuild();
412   menu_rebuild();
414   return 'admin/content/types';