Simpletest Coverage - modules/node/content_types.inc

1 <?php
2 // $Id: content_types.inc,v 1.87 2009/08/14 00:12:57 webchick Exp $
3
4 /**
5 * @file
6 * Content type editing UI.
7 */
8
9 /**
10 * Displays the content type admin overview page.
11 */
12 function node_overview_types() {
13 $types = node_type_get_types();
14 $names = node_type_get_names();
15 $header = array(t('Name'), array('data' => t('Operations'), 'colspan' => '2'));
16 $rows = array();
17
18 foreach ($names as $key => $name) {
19 $type = $types[$key];
20 if (node_hook($type->type, 'form')) {
21 $type_url_str = str_replace('_', '-', $type->type);
22 $row = array(theme('node_admin_overview', $name, $type));
23 // Set the edit column.
24 $row[] = array('data' => l(t('edit'), 'admin/structure/node-type/' . $type_url_str));
25
26 // Set the delete column.
27 if ($type->custom) {
28 $row[] = array('data' => l(t('delete'), 'admin/structure/node-type/' . $type_url_str . '/delete'));
29 }
30 else {
31 $row[] = array('data' => '');
32 }
33 $rows[] = $row;
34 }
35 }
36
37 if (empty($rows)) {
38 $rows[] = array(array('data' => t('No content types available. <a href="@link">Add content type</a>.', array('@link' => url('admin/structure/types/add'))), 'colspan' => '5', 'class' => 'message'));
39 }
40
41 $build['node_table'] = array(
42 '#theme' => 'table',
43 '#header' => $header,
44 '#rows' => $rows
45 );
46
47 return $build;
48 }
49
50 function theme_node_admin_overview($name, $type) {
51 $output = check_plain($name);
52 $output .= ' <small> (Machine name: ' . check_plain($type->type) . ')</small>';
53 $output .= '<div class="description">' . filter_xss_admin($type->description) . '</div>';
54 return $output;
55 }
56
57 /**
58 * Generates the node type editing form.
59 */
60 function node_type_form(&$form_state, $type = NULL) {
61 drupal_add_js(drupal_get_path('module', 'node') . '/content_types.js');
62 if (!isset($type->type)) {
63 // This is a new type. Node module managed types are custom and unlocked.
64 $type = node_type_set_defaults(array('custom' => 1, 'locked' => 0));
65 }
66
67 // Make the type object available to implementations of hook_form_alter.
68 $form['#node_type'] = $type;
69
70 $form['identity'] = array(
71 '#type' => 'fieldset',
72 '#title' => t('Identification'),
73 );
74 $form['identity']['name'] = array(
75 '#title' => t('Name'),
76 '#type' => 'textfield',
77 '#default_value' => $type->name,
78 '#description' => t('The human-readable name of this content type. This text will be displayed as part of the list on the <em>add new 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.'),
79 '#required' => TRUE,
80 '#size' => 30,
81 '#field_suffix' => ' <small id="node-type-name-suffix">&nbsp;</small>',
82 );
83
84 if (!$type->locked) {
85 $form['identity']['type'] = array(
86 '#title' => t('Machine name'),
87 '#type' => 'textfield',
88 '#default_value' => $type->type,
89 '#maxlength' => 32,
90 '#required' => TRUE,
91 '#description' => t('The machine-readable name of this content type. This text will be used for constructing the URL of the <em>add new 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>add new content</em> page. This name must be unique.'),
92 );
93 }
94 else {
95 $form['identity']['type'] = array(
96 '#type' => 'value',
97 '#value' => $type->type,
98 );
99 $form['identity']['type_display'] = array(
100 '#title' => t('Machine name'),
101 '#type' => 'item',
102 '#markup' => theme('placeholder', $type->type),
103 '#description' => t('The machine-readable name of this content type. This field cannot be modified for system-defined content types.'),
104 );
105 }
106
107 $form['identity']['description'] = array(
108 '#title' => t('Description'),
109 '#type' => 'textarea',
110 '#default_value' => $type->description,
111 );
112
113 $form['submission'] = array(
114 '#type' => 'fieldset',
115 '#title' => t('Submission form settings'),
116 '#collapsible' => TRUE,
117 );
118 $form['submission']['title_label'] = array(
119 '#title' => t('Title field label'),
120 '#type' => 'textfield',
121 '#default_value' => $type->title_label,
122 '#required' => TRUE,
123 );
124 if (!$type->has_title) {
125 // Avoid overwriting a content type that intentionally does not have a
126 // title field.
127 $form['submission']['title_label']['#attributes'] = array('disabled' => 'disabled');
128 $form['submission']['title_label']['#description'] = t('This content type does not have a title field.');
129 $form['submission']['title_label']['#required'] = FALSE;
130 }
131 $form['submission']['body_label'] = array(
132 '#title' => t('Body field label'),
133 '#type' => 'textfield',
134 '#default_value' => isset($type->body_label) ? $type->body_label : '',
135 '#description' => t('To remove the body field, remove text and leave blank.'),
136 );
137 $form['submission']['node_preview'] = array(
138 '#type' => 'radios',
139 '#title' => t('Preview before submitting'),
140 '#default_value' => variable_get('node_preview_' . $type->type, DRUPAL_OPTIONAL),
141 '#options' => array(
142 DRUPAL_DISABLED => t('Disabled'),
143 DRUPAL_OPTIONAL => t('Optional'),
144 DRUPAL_REQUIRED => t('Required'),
145 ),
146 );
147 $form['submission']['help'] = array(
148 '#type' => 'textarea',
149 '#title' => t('Explanation or submission guidelines'),
150 '#default_value' => $type->help,
151 '#description' => t('This text will be displayed at the top of the submission form for this content type.')
152 );
153 $form['workflow'] = array(
154 '#type' => 'fieldset',
155 '#title' => t('Publishing options'),
156 '#collapsible' => TRUE,
157 '#collapsed' => TRUE,
158 );
159 $form['workflow']['node_options'] = array('#type' => 'checkboxes',
160 '#title' => t('Default options'),
161 '#default_value' => variable_get('node_options_' . $type->type, array('status', 'promote')),
162 '#options' => array(
163 'status' => t('Published'),
164 'promote' => t('Promoted to front page'),
165 'sticky' => t('Sticky at top of lists'),
166 'revision' => t('Create new revision'),
167 ),
168 '#description' => t('Users with the <em>administer nodes</em> permission will be able to override these options.'),
169 );
170 $form['display'] = array(
171 '#type' => 'fieldset',
172 '#title' => t('Display settings'),
173 '#collapsible' => TRUE,
174 '#collapsed' => TRUE,
175 );
176 $form['display']['node_submitted'] = array(
177 '#type' => 'checkbox',
178 '#title' => t('Display post information'),
179 '#default_value' => variable_get('node_submitted_' . $type->type, TRUE),
180 '#description' => t('Enable the <em>submitted by Username on date</em> text.'),
181 );
182 $form['display']['teaser_length'] = array(
183 '#type' => 'select',
184 '#title' => t('Length of trimmed posts'),
185 '#default_value' => variable_get('teaser_length_' . $type->type, 600),
186 '#options' => drupal_map_assoc(array(0, 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000), '_node_characters'),
187 '#description' => t("The maximum number of characters used in the trimmed version of content.")
188 );
189 $form['old_type'] = array(
190 '#type' => 'value',
191 '#value' => $type->type,
192 );
193 $form['orig_type'] = array(
194 '#type' => 'value',
195 '#value' => isset($type->orig_type) ? $type->orig_type : '',
196 );
197 $form['base'] = array(
198 '#type' => 'value',
199 '#value' => $type->base,
200 );
201 $form['custom'] = array(
202 '#type' => 'value',
203 '#value' => $type->custom,
204 );
205 $form['modified'] = array(
206 '#type' => 'value',
207 '#value' => $type->modified,
208 );
209 $form['locked'] = array(
210 '#type' => 'value',
211 '#value' => $type->locked,
212 );
213
214 $form['submit'] = array(
215 '#type' => 'submit',
216 '#value' => t('Save content type'),
217 '#weight' => 40,
218 );
219
220 if ($type->custom) {
221 if (!empty($type->type)) {
222 $form['delete'] = array(
223 '#type' => 'submit',
224 '#value' => t('Delete content type'),
225 '#weight' => 45,
226 );
227 }
228 }
229
230 return $form;
231 }
232
233 /**
234 * Helper function for teaser length choices.
235 */
236 function _node_characters($length) {
237 return ($length == 0) ? t('Unlimited') : format_plural($length, '1 character', '@count characters');
238 }
239
240 /**
241 * Validates the content type submission form generated by node_type_form().
242 */
243 function node_type_form_validate($form, &$form_state) {
244 $type = new stdClass();
245 $type->type = trim($form_state['values']['type']);
246 $type->name = trim($form_state['values']['name']);
247
248 // Work out what the type was before the user submitted this form
249 $old_type = trim($form_state['values']['old_type']);
250
251 $types = node_type_get_names();
252
253 if (!$form_state['values']['locked']) {
254 if (isset($types[$type->type]) && $type->type != $old_type) {
255 form_set_error('type', t('The machine-readable name %type is already taken.', array('%type' => $type->type)));
256 }
257 if (!preg_match('!^[a-z0-9_]+$!', $type->type)) {
258 form_set_error('type', t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
259 }
260 // 'theme' conflicts with theme_node_form().
261 // '0' is invalid, since elsewhere we check it using empty().
262 if (in_array($type->type, array('0', 'theme'))) {
263 form_set_error('type', t("Invalid machine-readable name. Please enter a name other than %invalid.", array('%invalid' => $type->type)));
264 }
265 }
266
267 $names = array_flip($types);
268
269 if (isset($names[$type->name]) && $names[$type->name] != $old_type) {
270 form_set_error('name', t('The human-readable name %name is already taken.', array('%name' => $type->name)));
271 }
272 }
273
274 /**
275 * Implement hook_form_submit().
276 */
277 function node_type_form_submit($form, &$form_state) {
278 $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
279
280 $type = node_type_set_defaults();
281
282 $type->type = trim($form_state['values']['type']);
283 $type->name = trim($form_state['values']['name']);
284 $type->orig_type = trim($form_state['values']['orig_type']);
285 $type->old_type = isset($form_state['values']['old_type']) ? $form_state['values']['old_type'] : $type->type;
286
287 $type->description = $form_state['values']['description'];
288 $type->help = $form_state['values']['help'];
289 $type->title_label = $form_state['values']['title_label'];
290 $type->body_label = $form_state['values']['body_label'];
291
292 // title_label is required in core; has_title will always be true, unless a
293 // module alters the title field.
294 $type->has_title = ($type->title_label != '');
295 $type->has_body = ($type->body_label != '');
296
297 $type->base = !empty($form_state['values']['base']) ? $form_state['values']['base'] : 'node_content';
298 $type->custom = $form_state['values']['custom'];
299 $type->modified = TRUE;
300 $type->locked = $form_state['values']['locked'];
301
302 if ($op == t('Delete content type')) {
303 $form_state['redirect'] = 'admin/structure/node-type/' . str_replace('_', '-', $type->old_type) . '/delete';
304 return;
305 }
306
307 $status = node_type_save($type);
308
309 $variables = $form_state['values'];
310
311 // Remove everything that's been saved already - whatever's left is assumed
312 // to be a persistent variable.
313 foreach ($variables as $key => $value) {
314 if (isset($type->$key)) {
315 unset($variables[$key]);
316 }
317 }
318
319 unset($variables['form_token'], $variables['op'], $variables['submit'], $variables['delete'], $variables['reset'], $variables['form_id']);
320
321 // Save or reset persistent variable values.
322 foreach ($variables as $key => $value) {
323 $variable_new = $key . '_' . $type->type;
324 $variable_old = $key . '_' . $type->old_type;
325
326 if (is_array($value)) {
327 $value = array_keys(array_filter($value));
328 }
329 variable_set($variable_new, $value);
330
331 if ($variable_new != $variable_old) {
332 variable_del($variable_old);
333 }
334 }
335
336 node_types_rebuild();
337 $t_args = array('%name' => $type->name);
338
339 if ($status == SAVED_UPDATED) {
340 drupal_set_message(t('The content type %name has been updated.', $t_args));
341 }
342 elseif ($status == SAVED_NEW) {
343 drupal_set_message(t('The content type %name has been added.', $t_args));
344 watchdog('node', 'Added content type %name.', $t_args, WATCHDOG_NOTICE, l(t('view'), 'admin/structure/types'));
345 }
346
347 $form_state['redirect'] = 'admin/structure/types';
348 return;
349 }
350
351 /**
352 * Implement hook_node_type_insert().
353 */
354 function node_node_type_insert($info) {
355 if (!empty($info->old_type) && $info->old_type != $info->type) {
356 $update_count = node_type_update_nodes($info->old_type, $info->type);
357
358 if ($update_count) {
359 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)));
360 }
361 }
362 }
363
364 /**
365 * Implement hook_node_type_update().
366 */
367 function node_node_type_update($info) {
368 if (!empty($info->old_type) && $info->old_type != $info->type) {
369 $update_count = node_type_update_nodes($info->old_type, $info->type);
370
371 if ($update_count) {
372 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)));
373 }
374 }
375 }
376
377 /**
378 * Resets all of the relevant fields of a module-defined node type to their
379 * default values.
380 *
381 * @param $type
382 * The node type to reset. The node type is passed back by reference with its
383 * resetted values. If there is no module-defined info for this node type,
384 * then nothing happens.
385 */
386 function node_type_reset($type) {
387 $info_array = module_invoke_all('node_info');
388 if (isset($info_array[$type->orig_type])) {
389 $info_array[$type->orig_type]['type'] = $type->orig_type;
390 $info = node_type_set_defaults($info_array[$type->orig_type]);
391
392 foreach ($info as $field => $value) {
393 $type->$field = $value;
394 }
395 }
396 }
397
398 /**
399 * Menu callback; delete a single content type.
400 */
401 function node_type_delete_confirm(&$form_state, $type) {
402 $form['type'] = array('#type' => 'value', '#value' => $type->type);
403 $form['name'] = array('#type' => 'value', '#value' => $type->name);
404
405 $message = t('Are you sure you want to delete the content type %type?', array('%type' => $type->name));
406 $caption = '';
407
408 $num_nodes = db_query("SELECT COUNT(*) FROM {node} WHERE type = :type", array(':type' => $type->type))->fetchField();
409 if ($num_nodes) {
410 $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>';
411 }
412
413 $caption .= '<p>' . t('This action cannot be undone.') . '</p>';
414
415 return confirm_form($form, $message, 'admin/structure/types', $caption, t('Delete'));
416 }
417
418 /**
419 * Process content type delete confirm submissions.
420 */
421 function node_type_delete_confirm_submit($form, &$form_state) {
422 node_type_delete($form_state['values']['type']);
423
424 variable_del('teaser_length_' . $form_state['values']['type']);
425 variable_del('node_preview_' . $form_state['values']['type']);
426 $t_args = array('%name' => $form_state['values']['name']);
427 drupal_set_message(t('The content type %name has been deleted.', $t_args));
428 watchdog('menu', 'Deleted content type %name.', $t_args, WATCHDOG_NOTICE);
429
430 node_types_rebuild();
431
432 $form_state['redirect'] = 'admin/structure/types';
433 return;
434 }
435

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.