|
1 <?php |
|
2 |
|
3 /** |
|
4 * @file |
|
5 * Page callbacks for adding, editing, deleting, and revisions management for content. |
|
6 */ |
|
7 |
|
8 /** |
|
9 * Menu callback; presents the node editing form. |
|
10 */ |
|
11 function node_page_edit($node) { |
|
12 $type_name = node_type_get_name($node); |
|
13 drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => $type_name, '@title' => $node->title)), PASS_THROUGH); |
|
14 return drupal_get_form($node->type . '_node_form', $node); |
|
15 } |
|
16 |
|
17 /** |
|
18 * Page callback: Displays add content links for available content types. |
|
19 * |
|
20 * Redirects to node/add/[type] if only one content type is available. |
|
21 * |
|
22 * @see node_menu() |
|
23 */ |
|
24 function node_add_page() { |
|
25 $item = menu_get_item(); |
|
26 $content = system_admin_menu_block($item); |
|
27 // Bypass the node/add listing if only one content type is available. |
|
28 if (count($content) == 1) { |
|
29 $item = array_shift($content); |
|
30 drupal_goto($item['href']); |
|
31 } |
|
32 return theme('node_add_list', array('content' => $content)); |
|
33 } |
|
34 |
|
35 /** |
|
36 * Returns HTML for a list of available node types for node creation. |
|
37 * |
|
38 * @param $variables |
|
39 * An associative array containing: |
|
40 * - content: An array of content types. |
|
41 * |
|
42 * @ingroup themeable |
|
43 */ |
|
44 function theme_node_add_list($variables) { |
|
45 $content = $variables['content']; |
|
46 $output = ''; |
|
47 |
|
48 if ($content) { |
|
49 $output = '<dl class="node-type-list">'; |
|
50 foreach ($content as $item) { |
|
51 $output .= '<dt>' . l($item['title'], $item['href'], $item['localized_options']) . '</dt>'; |
|
52 $output .= '<dd>' . filter_xss_admin($item['description']) . '</dd>'; |
|
53 } |
|
54 $output .= '</dl>'; |
|
55 } |
|
56 else { |
|
57 $output = '<p>' . t('You have not created any content types yet. Go to the <a href="@create-content">content type creation page</a> to add a new content type.', array('@create-content' => url('admin/structure/types/add'))) . '</p>'; |
|
58 } |
|
59 return $output; |
|
60 } |
|
61 |
|
62 |
|
63 /** |
|
64 * Returns a node submission form. |
|
65 * |
|
66 * @param $type |
|
67 * The node type for the submitted node. |
|
68 * |
|
69 * @return |
|
70 * The themed form. |
|
71 */ |
|
72 function node_add($type) { |
|
73 global $user; |
|
74 |
|
75 $types = node_type_get_types(); |
|
76 $node = (object) array('uid' => $user->uid, 'name' => (isset($user->name) ? $user->name : ''), 'type' => $type, 'language' => LANGUAGE_NONE); |
|
77 drupal_set_title(t('Create @name', array('@name' => $types[$type]->name)), PASS_THROUGH); |
|
78 $output = drupal_get_form($type . '_node_form', $node); |
|
79 |
|
80 return $output; |
|
81 } |
|
82 |
|
83 /** |
|
84 * Form validation handler for node_form(). |
|
85 * |
|
86 * @see node_form() |
|
87 * @see node_form_submit() |
|
88 */ |
|
89 function node_form_validate($form, &$form_state) { |
|
90 // $form_state['node'] contains the actual entity being edited, but we must |
|
91 // not update it with form values that have not yet been validated, so we |
|
92 // create a pseudo-entity to use during validation. |
|
93 $node = (object) $form_state['values']; |
|
94 node_validate($node, $form, $form_state); |
|
95 entity_form_field_validate('node', $form, $form_state); |
|
96 } |
|
97 |
|
98 /** |
|
99 * Form constructor for the node add/edit form. |
|
100 * |
|
101 * @see node_form_validate() |
|
102 * @see node_form_submit() |
|
103 * @see node_form_build_preview() |
|
104 * @see node_form_delete_submit() |
|
105 * @ingroup forms |
|
106 */ |
|
107 function node_form($form, &$form_state, $node) { |
|
108 global $user; |
|
109 |
|
110 // During initial form build, add the node entity to the form state for use |
|
111 // during form building and processing. During a rebuild, use what is in the |
|
112 // form state. |
|
113 if (!isset($form_state['node'])) { |
|
114 if (!isset($node->title)) { |
|
115 $node->title = NULL; |
|
116 } |
|
117 node_object_prepare($node); |
|
118 $form_state['node'] = $node; |
|
119 } |
|
120 else { |
|
121 $node = $form_state['node']; |
|
122 } |
|
123 |
|
124 // Some special stuff when previewing a node. |
|
125 if (isset($form_state['node_preview'])) { |
|
126 $form['#prefix'] = $form_state['node_preview']; |
|
127 $node->in_preview = TRUE; |
|
128 } |
|
129 else { |
|
130 unset($node->in_preview); |
|
131 } |
|
132 |
|
133 // Identify this as a node edit form. |
|
134 // @todo D8: Remove. Modules can implement hook_form_BASE_FORM_ID_alter() now. |
|
135 $form['#node_edit_form'] = TRUE; |
|
136 |
|
137 $form['#attributes']['class'][] = 'node-form'; |
|
138 if (!empty($node->type)) { |
|
139 $form['#attributes']['class'][] = 'node-' . $node->type . '-form'; |
|
140 } |
|
141 |
|
142 // Basic node information. |
|
143 // These elements are just values so they are not even sent to the client. |
|
144 foreach (array('nid', 'vid', 'uid', 'created', 'type', 'language') as $key) { |
|
145 $form[$key] = array( |
|
146 '#type' => 'value', |
|
147 '#value' => isset($node->$key) ? $node->$key : NULL, |
|
148 ); |
|
149 } |
|
150 |
|
151 // Changed must be sent to the client, for later overwrite error checking. |
|
152 $form['changed'] = array( |
|
153 '#type' => 'hidden', |
|
154 '#default_value' => isset($node->changed) ? $node->changed : NULL, |
|
155 ); |
|
156 // Invoke hook_form() to get the node-specific bits. Can't use node_invoke(), |
|
157 // because hook_form() needs to be able to receive $form_state by reference. |
|
158 // @todo hook_form() implementations are unable to add #validate or #submit |
|
159 // handlers to the form buttons below. Remove hook_form() entirely. |
|
160 $function = node_type_get_base($node) . '_form'; |
|
161 if (function_exists($function) && ($extra = $function($node, $form_state))) { |
|
162 $form = array_merge_recursive($form, $extra); |
|
163 } |
|
164 // If the node type has a title, and the node type form defined no special |
|
165 // weight for it, we default to a weight of -5 for consistency. |
|
166 if (isset($form['title']) && !isset($form['title']['#weight'])) { |
|
167 $form['title']['#weight'] = -5; |
|
168 } |
|
169 // @todo D8: Remove. Modules should access the node using $form_state['node']. |
|
170 $form['#node'] = $node; |
|
171 |
|
172 $form['additional_settings'] = array( |
|
173 '#type' => 'vertical_tabs', |
|
174 '#weight' => 99, |
|
175 ); |
|
176 |
|
177 // Add a log field if the "Create new revision" option is checked, or if the |
|
178 // current user has the ability to check that option. |
|
179 $form['revision_information'] = array( |
|
180 '#type' => 'fieldset', |
|
181 '#title' => t('Revision information'), |
|
182 '#collapsible' => TRUE, |
|
183 // Collapsed by default when "Create new revision" is unchecked |
|
184 '#collapsed' => !$node->revision, |
|
185 '#group' => 'additional_settings', |
|
186 '#attributes' => array( |
|
187 'class' => array('node-form-revision-information'), |
|
188 ), |
|
189 '#attached' => array( |
|
190 'js' => array(drupal_get_path('module', 'node') . '/node.js'), |
|
191 ), |
|
192 '#weight' => 20, |
|
193 '#access' => $node->revision || user_access('administer nodes'), |
|
194 ); |
|
195 $form['revision_information']['revision'] = array( |
|
196 '#type' => 'checkbox', |
|
197 '#title' => t('Create new revision'), |
|
198 '#default_value' => $node->revision, |
|
199 '#access' => user_access('administer nodes'), |
|
200 ); |
|
201 // Check the revision log checkbox when the log textarea is filled in. |
|
202 // This must not happen if "Create new revision" is enabled by default, since |
|
203 // the state would auto-disable the checkbox otherwise. |
|
204 if (!$node->revision) { |
|
205 $form['revision_information']['revision']['#states'] = array( |
|
206 'checked' => array( |
|
207 'textarea[name="log"]' => array('empty' => FALSE), |
|
208 ), |
|
209 ); |
|
210 } |
|
211 $form['revision_information']['log'] = array( |
|
212 '#type' => 'textarea', |
|
213 '#title' => t('Revision log message'), |
|
214 '#rows' => 4, |
|
215 '#default_value' => !empty($node->log) ? $node->log : '', |
|
216 '#description' => t('Provide an explanation of the changes you are making. This will help other authors understand your motivations.'), |
|
217 ); |
|
218 |
|
219 // Node author information for administrators |
|
220 $form['author'] = array( |
|
221 '#type' => 'fieldset', |
|
222 '#access' => user_access('administer nodes'), |
|
223 '#title' => t('Authoring information'), |
|
224 '#collapsible' => TRUE, |
|
225 '#collapsed' => TRUE, |
|
226 '#group' => 'additional_settings', |
|
227 '#attributes' => array( |
|
228 'class' => array('node-form-author'), |
|
229 ), |
|
230 '#attached' => array( |
|
231 'js' => array( |
|
232 drupal_get_path('module', 'node') . '/node.js', |
|
233 array( |
|
234 'type' => 'setting', |
|
235 'data' => array('anonymous' => variable_get('anonymous', t('Anonymous'))), |
|
236 ), |
|
237 ), |
|
238 ), |
|
239 '#weight' => 90, |
|
240 ); |
|
241 $form['author']['name'] = array( |
|
242 '#type' => 'textfield', |
|
243 '#title' => t('Authored by'), |
|
244 '#maxlength' => 60, |
|
245 '#autocomplete_path' => 'user/autocomplete', |
|
246 '#default_value' => !empty($node->name) ? $node->name : '', |
|
247 '#weight' => -1, |
|
248 '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))), |
|
249 ); |
|
250 $form['author']['date'] = array( |
|
251 '#type' => 'textfield', |
|
252 '#title' => t('Authored on'), |
|
253 '#maxlength' => 25, |
|
254 '#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array('%time' => !empty($node->date) ? date_format(date_create($node->date), 'Y-m-d H:i:s O') : format_date($node->created, 'custom', 'Y-m-d H:i:s O'), '%timezone' => !empty($node->date) ? date_format(date_create($node->date), 'O') : format_date($node->created, 'custom', 'O'))), |
|
255 '#default_value' => !empty($node->date) ? $node->date : '', |
|
256 ); |
|
257 |
|
258 // Node options for administrators |
|
259 $form['options'] = array( |
|
260 '#type' => 'fieldset', |
|
261 '#access' => user_access('administer nodes'), |
|
262 '#title' => t('Publishing options'), |
|
263 '#collapsible' => TRUE, |
|
264 '#collapsed' => TRUE, |
|
265 '#group' => 'additional_settings', |
|
266 '#attributes' => array( |
|
267 'class' => array('node-form-options'), |
|
268 ), |
|
269 '#attached' => array( |
|
270 'js' => array(drupal_get_path('module', 'node') . '/node.js'), |
|
271 ), |
|
272 '#weight' => 95, |
|
273 ); |
|
274 $form['options']['status'] = array( |
|
275 '#type' => 'checkbox', |
|
276 '#title' => t('Published'), |
|
277 '#default_value' => $node->status, |
|
278 ); |
|
279 $form['options']['promote'] = array( |
|
280 '#type' => 'checkbox', |
|
281 '#title' => t('Promoted to front page'), |
|
282 '#default_value' => $node->promote, |
|
283 ); |
|
284 $form['options']['sticky'] = array( |
|
285 '#type' => 'checkbox', |
|
286 '#title' => t('Sticky at top of lists'), |
|
287 '#default_value' => $node->sticky, |
|
288 ); |
|
289 |
|
290 // Add the buttons. |
|
291 $form['actions'] = array('#type' => 'actions'); |
|
292 $form['actions']['submit'] = array( |
|
293 '#type' => 'submit', |
|
294 '#access' => variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_REQUIRED || (!form_get_errors() && isset($form_state['node_preview'])), |
|
295 '#value' => t('Save'), |
|
296 '#weight' => 5, |
|
297 '#submit' => array('node_form_submit'), |
|
298 ); |
|
299 $form['actions']['preview'] = array( |
|
300 '#access' => variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_DISABLED, |
|
301 '#type' => 'submit', |
|
302 '#value' => t('Preview'), |
|
303 '#weight' => 10, |
|
304 '#submit' => array('node_form_build_preview'), |
|
305 ); |
|
306 if (!empty($node->nid) && node_access('delete', $node)) { |
|
307 $form['actions']['delete'] = array( |
|
308 '#type' => 'submit', |
|
309 '#value' => t('Delete'), |
|
310 '#weight' => 15, |
|
311 '#submit' => array('node_form_delete_submit'), |
|
312 ); |
|
313 } |
|
314 // This form uses a button-level #submit handler for the form's main submit |
|
315 // action. node_form_submit() manually invokes all form-level #submit handlers |
|
316 // of the form. Without explicitly setting #submit, Form API would auto-detect |
|
317 // node_form_submit() as submit handler, but that is the button-level #submit |
|
318 // handler for the 'Save' action. To maintain backwards compatibility, a |
|
319 // #submit handler is auto-suggested for custom node type modules. |
|
320 $form['#validate'][] = 'node_form_validate'; |
|
321 if (!isset($form['#submit']) && function_exists($node->type . '_node_form_submit')) { |
|
322 $form['#submit'][] = $node->type . '_node_form_submit'; |
|
323 } |
|
324 $form += array('#submit' => array()); |
|
325 |
|
326 field_attach_form('node', $node, $form, $form_state, entity_language('node', $node)); |
|
327 return $form; |
|
328 } |
|
329 |
|
330 /** |
|
331 * Form submission handler for node_form(). |
|
332 * |
|
333 * Handles the 'Delete' button on the node form. |
|
334 * |
|
335 * @see node_form() |
|
336 * @see node_form_validate() |
|
337 */ |
|
338 function node_form_delete_submit($form, &$form_state) { |
|
339 $destination = array(); |
|
340 if (isset($_GET['destination'])) { |
|
341 $destination = drupal_get_destination(); |
|
342 unset($_GET['destination']); |
|
343 } |
|
344 $node = $form['#node']; |
|
345 $form_state['redirect'] = array('node/' . $node->nid . '/delete', array('query' => $destination)); |
|
346 } |
|
347 |
|
348 /** |
|
349 * Form submission handler for node_form(). |
|
350 * |
|
351 * Handles the 'Preview' button on the node form. |
|
352 * |
|
353 * @see node_form() |
|
354 * @see node_form_validate() |
|
355 */ |
|
356 function node_form_build_preview($form, &$form_state) { |
|
357 $node = node_form_submit_build_node($form, $form_state); |
|
358 $form_state['node_preview'] = node_preview($node); |
|
359 $form_state['rebuild'] = TRUE; |
|
360 } |
|
361 |
|
362 /** |
|
363 * Generates a node preview. |
|
364 * |
|
365 * @param $node |
|
366 * The node to preview. |
|
367 * |
|
368 * @return |
|
369 * An HTML-formatted string of a node preview. |
|
370 * |
|
371 * @see node_form_build_preview() |
|
372 */ |
|
373 function node_preview($node) { |
|
374 // Clone the node before previewing it to prevent the node itself from being |
|
375 // modified. |
|
376 $cloned_node = clone $node; |
|
377 if (node_access('create', $cloned_node) || node_access('update', $cloned_node)) { |
|
378 _field_invoke_multiple('load', 'node', array($cloned_node->nid => $cloned_node)); |
|
379 // Load the user's name when needed. |
|
380 if (isset($cloned_node->name)) { |
|
381 // The use of isset() is mandatory in the context of user IDs, because |
|
382 // user ID 0 denotes the anonymous user. |
|
383 if ($user = user_load_by_name($cloned_node->name)) { |
|
384 $cloned_node->uid = $user->uid; |
|
385 $cloned_node->picture = $user->picture; |
|
386 } |
|
387 else { |
|
388 $cloned_node->uid = 0; // anonymous user |
|
389 } |
|
390 } |
|
391 elseif ($cloned_node->uid) { |
|
392 $user = user_load($cloned_node->uid); |
|
393 $cloned_node->name = $user->name; |
|
394 $cloned_node->picture = $user->picture; |
|
395 } |
|
396 |
|
397 $cloned_node->changed = REQUEST_TIME; |
|
398 $nodes = array($cloned_node->nid => $cloned_node); |
|
399 |
|
400 // Display a preview of the node. |
|
401 if (!form_get_errors()) { |
|
402 $cloned_node->in_preview = TRUE; |
|
403 $output = theme('node_preview', array('node' => $cloned_node)); |
|
404 unset($cloned_node->in_preview); |
|
405 } |
|
406 drupal_set_title(t('Preview'), PASS_THROUGH); |
|
407 |
|
408 return $output; |
|
409 } |
|
410 } |
|
411 |
|
412 /** |
|
413 * Returns HTML for a node preview for display during node creation and editing. |
|
414 * |
|
415 * @param $variables |
|
416 * An associative array containing: |
|
417 * - node: The node object which is being previewed. |
|
418 * |
|
419 * @see node_preview() |
|
420 * @ingroup themeable |
|
421 */ |
|
422 function theme_node_preview($variables) { |
|
423 $node = $variables['node']; |
|
424 |
|
425 $output = '<div class="preview">'; |
|
426 |
|
427 $preview_trimmed_version = FALSE; |
|
428 |
|
429 $elements = node_view(clone $node, 'teaser'); |
|
430 $trimmed = drupal_render($elements); |
|
431 $elements = node_view($node, 'full'); |
|
432 $full = drupal_render($elements); |
|
433 |
|
434 // Do we need to preview trimmed version of post as well as full version? |
|
435 if ($trimmed != $full) { |
|
436 drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication.<span class="no-js"> You can insert the delimiter "<!--break-->" (without the quotes) to fine-tune where your post gets split.</span>')); |
|
437 $output .= '<h3>' . t('Preview trimmed version') . '</h3>'; |
|
438 $output .= $trimmed; |
|
439 $output .= '<h3>' . t('Preview full version') . '</h3>'; |
|
440 $output .= $full; |
|
441 } |
|
442 else { |
|
443 $output .= $full; |
|
444 } |
|
445 $output .= "</div>\n"; |
|
446 |
|
447 return $output; |
|
448 } |
|
449 |
|
450 /** |
|
451 * Form submission handler for node_form(). |
|
452 * |
|
453 * @see node_form() |
|
454 * @see node_form_validate() |
|
455 */ |
|
456 function node_form_submit($form, &$form_state) { |
|
457 $node = node_form_submit_build_node($form, $form_state); |
|
458 $insert = empty($node->nid); |
|
459 node_save($node); |
|
460 $node_link = l(t('view'), 'node/' . $node->nid); |
|
461 $watchdog_args = array('@type' => $node->type, '%title' => $node->title); |
|
462 $t_args = array('@type' => node_type_get_name($node), '%title' => $node->title); |
|
463 |
|
464 if ($insert) { |
|
465 watchdog('content', '@type: added %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link); |
|
466 drupal_set_message(t('@type %title has been created.', $t_args)); |
|
467 } |
|
468 else { |
|
469 watchdog('content', '@type: updated %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link); |
|
470 drupal_set_message(t('@type %title has been updated.', $t_args)); |
|
471 } |
|
472 if ($node->nid) { |
|
473 $form_state['values']['nid'] = $node->nid; |
|
474 $form_state['nid'] = $node->nid; |
|
475 $form_state['redirect'] = node_access('view', $node) ? 'node/' . $node->nid : '<front>'; |
|
476 } |
|
477 else { |
|
478 // In the unlikely case something went wrong on save, the node will be |
|
479 // rebuilt and node form redisplayed the same way as in preview. |
|
480 drupal_set_message(t('The post could not be saved.'), 'error'); |
|
481 $form_state['rebuild'] = TRUE; |
|
482 } |
|
483 // Clear the page and block caches. |
|
484 cache_clear_all(); |
|
485 } |
|
486 |
|
487 /** |
|
488 * Updates the form state's node entity by processing this submission's values. |
|
489 * |
|
490 * This is the default builder function for the node form. It is called |
|
491 * during the "Save" and "Preview" submit handlers to retrieve the entity to |
|
492 * save or preview. This function can also be called by a "Next" button of a |
|
493 * wizard to update the form state's entity with the current step's values |
|
494 * before proceeding to the next step. |
|
495 * |
|
496 * @see node_form() |
|
497 */ |
|
498 function node_form_submit_build_node($form, &$form_state) { |
|
499 // @todo Legacy support for modules that extend the node form with form-level |
|
500 // submit handlers that adjust $form_state['values'] prior to those values |
|
501 // being used to update the entity. Module authors are encouraged to instead |
|
502 // adjust the node directly within a hook_node_submit() implementation. For |
|
503 // Drupal 8, evaluate whether the pattern of triggering form-level submit |
|
504 // handlers during button-level submit processing is worth supporting |
|
505 // properly, and if so, add a Form API function for doing so. |
|
506 unset($form_state['submit_handlers']); |
|
507 form_execute_handlers('submit', $form, $form_state); |
|
508 |
|
509 $node = $form_state['node']; |
|
510 entity_form_submit_build_entity('node', $node, $form, $form_state); |
|
511 |
|
512 node_submit($node); |
|
513 foreach (module_implements('node_submit') as $module) { |
|
514 $function = $module . '_node_submit'; |
|
515 $function($node, $form, $form_state); |
|
516 } |
|
517 return $node; |
|
518 } |
|
519 |
|
520 /** |
|
521 * Form constructor for the node deletion confirmation form. |
|
522 * |
|
523 * @see node_delete_confirm_submit() |
|
524 */ |
|
525 function node_delete_confirm($form, &$form_state, $node) { |
|
526 $form['#node'] = $node; |
|
527 // Always provide entity id in the same form key as in the entity edit form. |
|
528 $form['nid'] = array('#type' => 'value', '#value' => $node->nid); |
|
529 return confirm_form($form, |
|
530 t('Are you sure you want to delete %title?', array('%title' => $node->title)), |
|
531 'node/' . $node->nid, |
|
532 t('This action cannot be undone.'), |
|
533 t('Delete'), |
|
534 t('Cancel') |
|
535 ); |
|
536 } |
|
537 |
|
538 /** |
|
539 * Executes node deletion. |
|
540 * |
|
541 * @see node_delete_confirm() |
|
542 */ |
|
543 function node_delete_confirm_submit($form, &$form_state) { |
|
544 if ($form_state['values']['confirm']) { |
|
545 $node = node_load($form_state['values']['nid']); |
|
546 node_delete($form_state['values']['nid']); |
|
547 cache_clear_all(); |
|
548 watchdog('content', '@type: deleted %title.', array('@type' => $node->type, '%title' => $node->title)); |
|
549 drupal_set_message(t('@type %title has been deleted.', array('@type' => node_type_get_name($node), '%title' => $node->title))); |
|
550 } |
|
551 |
|
552 $form_state['redirect'] = '<front>'; |
|
553 } |
|
554 |
|
555 /** |
|
556 * Generates an overview table of older revisions of a node. |
|
557 * |
|
558 * @param $node |
|
559 * A node object. |
|
560 * |
|
561 * @return array |
|
562 * An array as expected by drupal_render(). |
|
563 * |
|
564 * @see node_menu() |
|
565 */ |
|
566 function node_revision_overview($node) { |
|
567 drupal_set_title(t('Revisions for %title', array('%title' => $node->title)), PASS_THROUGH); |
|
568 |
|
569 $header = array(t('Revision'), array('data' => t('Operations'), 'colspan' => 2)); |
|
570 |
|
571 $revisions = node_revision_list($node); |
|
572 |
|
573 $rows = array(); |
|
574 $revert_permission = FALSE; |
|
575 if ((user_access('revert revisions') || user_access('administer nodes')) && node_access('update', $node)) { |
|
576 $revert_permission = TRUE; |
|
577 } |
|
578 $delete_permission = FALSE; |
|
579 if ((user_access('delete revisions') || user_access('administer nodes')) && node_access('delete', $node)) { |
|
580 $delete_permission = TRUE; |
|
581 } |
|
582 foreach ($revisions as $revision) { |
|
583 $row = array(); |
|
584 $operations = array(); |
|
585 |
|
586 if ($revision->current_vid > 0) { |
|
587 $row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "node/$node->nid"), '!username' => theme('username', array('account' => $revision)))) |
|
588 . (($revision->log != '') ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : ''), |
|
589 'class' => array('revision-current')); |
|
590 $operations[] = array('data' => drupal_placeholder(t('current revision')), 'class' => array('revision-current'), 'colspan' => 2); |
|
591 } |
|
592 else { |
|
593 $row[] = t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "node/$node->nid/revisions/$revision->vid/view"), '!username' => theme('username', array('account' => $revision)))) |
|
594 . (($revision->log != '') ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : ''); |
|
595 if ($revert_permission) { |
|
596 $operations[] = l(t('revert'), "node/$node->nid/revisions/$revision->vid/revert"); |
|
597 } |
|
598 if ($delete_permission) { |
|
599 $operations[] = l(t('delete'), "node/$node->nid/revisions/$revision->vid/delete"); |
|
600 } |
|
601 } |
|
602 $rows[] = array_merge($row, $operations); |
|
603 } |
|
604 |
|
605 $build['node_revisions_table'] = array( |
|
606 '#theme' => 'table', |
|
607 '#rows' => $rows, |
|
608 '#header' => $header, |
|
609 ); |
|
610 |
|
611 return $build; |
|
612 } |
|
613 |
|
614 /** |
|
615 * Asks for confirmation of the reversion to prevent against CSRF attacks. |
|
616 * |
|
617 * @param int $node_revision |
|
618 * The node revision ID. |
|
619 * |
|
620 * @return array |
|
621 * An array as expected by drupal_render(). |
|
622 * |
|
623 * @see node_menu() |
|
624 * @see node_revision_revert_confirm_submit() |
|
625 * @ingroup forms |
|
626 */ |
|
627 function node_revision_revert_confirm($form, $form_state, $node_revision) { |
|
628 $form['#node_revision'] = $node_revision; |
|
629 return confirm_form($form, t('Are you sure you want to revert to the revision from %revision-date?', array('%revision-date' => format_date($node_revision->revision_timestamp))), 'node/' . $node_revision->nid . '/revisions', '', t('Revert'), t('Cancel')); |
|
630 } |
|
631 |
|
632 /** |
|
633 * Form submission handler for node_revision_revert_confirm(). |
|
634 */ |
|
635 function node_revision_revert_confirm_submit($form, &$form_state) { |
|
636 $node_revision = $form['#node_revision']; |
|
637 $node_revision->revision = 1; |
|
638 $node_revision->log = t('Copy of the revision from %date.', array('%date' => format_date($node_revision->revision_timestamp))); |
|
639 |
|
640 node_save($node_revision); |
|
641 |
|
642 watchdog('content', '@type: reverted %title revision %revision.', array('@type' => $node_revision->type, '%title' => $node_revision->title, '%revision' => $node_revision->vid)); |
|
643 drupal_set_message(t('@type %title has been reverted back to the revision from %revision-date.', array('@type' => node_type_get_name($node_revision), '%title' => $node_revision->title, '%revision-date' => format_date($node_revision->revision_timestamp)))); |
|
644 $form_state['redirect'] = 'node/' . $node_revision->nid . '/revisions'; |
|
645 } |
|
646 |
|
647 /** |
|
648 * Form constructor for the revision deletion confirmation form. |
|
649 * |
|
650 * This form prevents against CSRF attacks. |
|
651 * |
|
652 * @param $node_revision |
|
653 * The node revision ID. |
|
654 * |
|
655 * @return |
|
656 * An array as expected by drupal_render(). |
|
657 * |
|
658 * @see node_menu() |
|
659 * @see node_revision_delete_confirm_submit() |
|
660 * @ingroup forms |
|
661 */ |
|
662 function node_revision_delete_confirm($form, $form_state, $node_revision) { |
|
663 $form['#node_revision'] = $node_revision; |
|
664 return confirm_form($form, t('Are you sure you want to delete the revision from %revision-date?', array('%revision-date' => format_date($node_revision->revision_timestamp))), 'node/' . $node_revision->nid . '/revisions', t('This action cannot be undone.'), t('Delete'), t('Cancel')); |
|
665 } |
|
666 |
|
667 /** |
|
668 * Form submission handler for node_revision_delete_confirm(). |
|
669 */ |
|
670 function node_revision_delete_confirm_submit($form, &$form_state) { |
|
671 $node_revision = $form['#node_revision']; |
|
672 node_revision_delete($node_revision->vid); |
|
673 |
|
674 watchdog('content', '@type: deleted %title revision %revision.', array('@type' => $node_revision->type, '%title' => $node_revision->title, '%revision' => $node_revision->vid)); |
|
675 drupal_set_message(t('Revision from %revision-date of @type %title has been deleted.', array('%revision-date' => format_date($node_revision->revision_timestamp), '@type' => node_type_get_name($node_revision), '%title' => $node_revision->title))); |
|
676 $form_state['redirect'] = 'node/' . $node_revision->nid; |
|
677 if (db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid', array(':nid' => $node_revision->nid))->fetchField() > 1) { |
|
678 $form_state['redirect'] .= '/revisions'; |
|
679 } |
|
680 } |