? live_d5_node_preview.patch ? node Index: live.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/live/live.info,v retrieving revision 1.1.2.6 diff -u -p -r1.1.2.6 live.info --- live.info 4 Feb 2008 20:01:05 -0000 1.1.2.6 +++ live.info 24 Oct 2008 11:19:24 -0000 @@ -1,4 +1,9 @@ ; $Id: live.info,v 1.1.2.6 2008/02/04 20:01:05 Gurpartap Exp $ name = Live description = Preview nodes and comments instantly, while authoring. Returns content securely from an Ajax call, viz filtered based upon the selected Input Format. -package = Javascript \ No newline at end of file +package = Javascript +; Information added by drupal.org packaging script on 2008-03-20 +version = "5.x-1.x-dev" +project = "live" +datestamp = "1206014768" + Index: live.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/live/live.module,v retrieving revision 1.1.2.8 diff -u -p -r1.1.2.8 live.module --- live.module 20 Mar 2008 05:41:55 -0000 1.1.2.8 +++ live.module 24 Oct 2008 11:19:24 -0000 @@ -2,10 +2,15 @@ // $Id: live.module,v 1.1.2.8 2008/03/20 05:41:55 heine Exp $ /** + * @file + * Allows live previews of comments and nodes. + */ + +/** * Implementation of hook_perm(). */ function live_perm() { - return array('use live comment preview'); + return array('use live comment preview', 'use live node preview'); } /** @@ -23,20 +28,21 @@ function live_menu($may_cache) { ); $items[] = array( + 'path' => 'live/node/preview', + 'callback' => 'live_node_preview', + 'access' => user_access('use live node preview'), + 'type' => MENU_CALLBACK, + ); + + $items[] = array( 'path' => 'admin/settings/live', 'title' => t('Live'), 'description' => t('Basic configuration for preview placements and effects.'), 'callback' => 'drupal_get_form', - 'callback arguments' => array('live_comment_settings'), + 'callback arguments' => array('live_preview_settings'), 'access' => user_access('administer site configuration'), ); - $items[] = array( - 'path' => 'admin/settings/live/comment', - 'title' => t('Comment settings'), - 'callback' => 'drupal_get_form', - 'callback arguments' => array('live_comment_settings'), - ); } drupal_add_css(drupal_get_path('module', 'live') .'/common.css'); @@ -46,7 +52,7 @@ function live_menu($may_cache) { /** * Live Comments settings page. */ -function live_comment_settings() { +function live_preview_settings() { $form['live_comment_preview'] = array( '#type' => 'fieldset', '#title' => t('Live Comment Preview settings'), @@ -79,6 +85,38 @@ function live_comment_settings() { '#description' => t('After how long (in milliseconds) should comment preview update on focus, keyup or blur event.'), ); + $form['live_node_preview'] = array( + '#type' => 'fieldset', + '#title' => t('Live Node Preview settings'), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + ); + + $form['live_node_preview']['live_node_preview_element'] = array( + '#type' => 'textfield', + '#title' => t('CSS selector to attach node preview box to'), + '#default_value' => variable_get('live_node_preview_element', 'form#node-form'), + '#description' => t('A valid CSS selector to attach the node preview box to. Example: body, form#node-form, div.my-class'), + '#required' => TRUE, + ); + + $form['live_node_preview']['live_node_preview_element_method'] = array( + '#type' => 'radios', + '#title' => t('Attach method'), + '#options' => drupal_map_assoc(array('prepend', 'append', 'replace')), + '#default_value' => variable_get('live_node_preview_element_method', 'append'), + '#description' => t('Choose how the node preview box should be attached to the above selector.'), + '#required' => TRUE, + ); + + $form['live_node_preview']['live_node_preview_types_enabled'] = array( + '#type' => 'checkboxes', + '#title' => t('Node types'), + '#description' => t('Select the node types you want to enable live previews for.'), + '#default_value' => variable_get('live_node_preview_types_enabled', array()), + '#options' => node_get_types('names'), + ); + return system_settings_form($form); } @@ -86,9 +124,11 @@ function live_comment_settings() { * Implementation of hook_form_alter(). */ function live_form_alter($form_id, &$form) { - $access = user_access('use live comment preview'); + $access_preview_comment = user_access('use live comment preview'); + $access_preview_node = user_access('use live node preview'); - if ($form_id == 'comment_form' && $form['#after_build'][0] != 'comment_form_add_preview' && $access) { + // Comment preview + if ($form_id == 'comment_form' && $form['#after_build'][0] != 'comment_form_add_preview' && $access_preview_comment) { global $base_url; @@ -110,6 +150,32 @@ function live_form_alter($form_id, &$for drupal_add_js($path .'/comment/live-comment.js'); } + + // Node preview + if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id + && in_array($form['type']['#value'], variable_get('live_node_preview_types_enabled', array()), TRUE) && user_access('use live node preview')) { + + global $base_url; + + $path = drupal_get_path('module', 'live'); + $token = $form['#token'] ? $form['#token'] : ($form['type']['#value'] .'_node_form'); + + drupal_add_js(array( + 'live' => array( + 'node' => array( + 'node_type' => $form['type']['#value'], + 'base_url' => $base_url, + 'token_value' => $token, + 'module_path' => $path, + 'element' => variable_get('live_node_preview_element', 'form#node-form'), + 'placement' => variable_get('live_node_preview_element_method', 'append'), + ), + ) + ), 'setting'); + + drupal_add_js($path .'/node/live-node.js'); + + } } /** @@ -122,7 +188,7 @@ function live_comment_preview() { $body = isset($_POST['body']) ? $_POST['body'] : ''; $access = user_access('use live comment preview'); $token = $_POST['token'] != "" ? $_POST['token'] : ""; - $skip_anonymous = $user->uid == 0 ? true : false; + $skip_anonymous = $user->uid == 0 ? TRUE : FALSE; $token_value = isset($_POST['token_value']) ? $_POST['token_value'] : ''; $valid_token = drupal_valid_token($token, $token_value, $skip_anonymous); @@ -143,7 +209,7 @@ function live_comment_preview() { $comment->comment = check_markup($body, $format); $comment->subject = $_POST['subject'] ? $_POST['subject'] : trim(truncate_utf8(decode_entities(strip_tags($comment->comment)), 29, TRUE)); - $comment->uid = isset($user->uid) ? $user->uid : null; + $comment->uid = isset($user->uid) ? $user->uid : NULL; $comment->timestamp = time(); $output = '

'. t('Comment Preview') .'

'; @@ -152,3 +218,120 @@ function live_comment_preview() { print $output; } + +/** + * Construct and Output node preview securely. + */ +function live_node_preview() { + global $user; + + $node->title = isset($_POST['title']) ? $_POST['title'] : ''; + $node->format = isset($_POST['format']) ? $_POST['format'] : 1; + $node->body = isset($_POST['body']) ? check_markup($_POST['body'], $node->format) : ''; + $node->type = isset($_POST['node_type']) ? $_POST['node_type'] : ''; + $node->created = isset($_POST['date']) ? strtotime($_POST['date']) : time(); + $node->changed = time(); + $node->name = isset($_POST['username']) ? $_POST['username'] : t('Anonymous'); + + $access = user_access('use live node preview'); + $token = $_POST['token'] != "" ? $_POST['token'] : ""; + $skip_anonymous = $user->uid == 0 ? TRUE : FALSE; + $token_value = isset($_POST['token_value']) ? $_POST['token_value'] : ''; + $valid_token = drupal_valid_token($token, $token_value, $skip_anonymous); + + if (!filter_access($node->format) || $node->title == "" || !$access || !$valid_token) { + // SECURITY CHECK! + // Deny access: + // 1) If the current user is not allowed to use specified input format; or + // 2) If the node title is empty + // 3) If user does not have the permission. + drupal_access_denied(); + return; + } + + // Avoid debug information(devel.module) from being added to the preview. + $GLOBALS['devel_shutdown'] = FALSE; + + // Validate node + // @FIXME this simply doesn't work yet + $errors = live_node_validate($node); + if ($errors != TRUE) { + $print = print_r($errors, true); + print $print; + } + + // Load the user's name when needed. + if (isset($node->name)) { + // The use of isset() is mandatory in the context of user IDs, because + // user ID 0 denotes the anonymous user. + if ($user = user_load(array('name' => $node->name))) { + $node->uid = $user->uid; + $node->picture = $user->picture; + } + else { + $node->uid = 0; // anonymous user + } + } + + // Display a preview of the node. + // Previewing alters $node so it needs to be cloned. + $cloned_node = drupal_clone($node); + $cloned_node->build_mode = NODE_BUILD_PREVIEW; + $cloned_node->in_preview = 1; + + $output = theme('node_preview', $cloned_node); + print $output; +} + +/** + * Validate the node + */ +function live_node_validate($node) { + // Convert the node to an object, if necessary. + $node = (object)$node; + $type = node_get_types('type', $node); + $errors = array(); + + if ($node->title == '') { + $errors[] = t('Title field is required.'); + } + + // Make sure the body has the minimum number of words. + // TODO : use a better word counting algorithm that will work in other languages + if (!empty($type->min_word_count) && isset($node->body) && count(explode(' ', $node->body)) < $type->min_word_count) { + //form_set_error('body', t('The body of your @type is too short. You need at least %words words.', array('%words' => $type->min_word_count, '@type' => $type->name))); + $errors[] = t('The body of your @type is too short. You need at least %words words.', array('%words' => $type->min_word_count, '@type' => $type->name)); + } + + if (isset($node->nid) && (node_last_changed($node->nid) > $node->changed)) { + //form_set_error('changed', t('This content has been modified by another user, changes cannot be saved.')); + $errors[] = t('This content has been modified by another user, changes cannot be saved.'); + } + + if (user_access('administer nodes')) { + // Validate the "authored by" field. + if (!empty($node->name) && !($account = user_load(array('name' => $node->name)))) { + // The use of empty() is mandatory in the context of usernames + // as the empty string denotes the anonymous user. In case we + // are dealing with an anonymous user we set the user ID to 0. + //form_set_error('name', t('The username %name does not exist.', array('%name' => $node->name))); + $errors[] = t('The username %name does not exist.', array('%name' => $node->name)); + } + + // Validate the "authored on" field. As of PHP 5.1.0, strtotime returns FALSE instead of -1 upon failure. + if (!empty($node->date) && strtotime($node->date) <= 0) { + //form_set_error('date', t('You have to specify a valid date.')); + $errors[] = t('You have to specify a valid date.'); + } + } + + // Do node-type-specific validation checks. + //node_invoke($node, 'validate', $form); + //node_invoke_nodeapi($node, 'validate', $form); + + if (count($errors)) { + return $errors; + } + + return true; +} --- /dev/null 2008-07-02 12:47:48.000000000 +0200 +++ node/live-node.js 2008-10-24 13:18:40.000000000 +0200 @@ -0,0 +1,79 @@ +// $Id$ + +Drupal.liveNodePreview = function() { + var element = Drupal.settings.live.node.element; + var node_type = Drupal.settings.live.node.node_type; + var base_url = Drupal.settings.live.node.basePath + '/'; + var module_path = Drupal.settings.live.node.module_path + '/'; + var token_value = Drupal.settings.live.node.token_value; + $('#node-form input#edit-preview').one('click', function() { + var box = '
'; + switch (Drupal.settings.live.node.placement) { + case 'prepend': + $(box).prependTo(element); + break; + case 'append': + $(box).appendTo(element); + break; + case 'replace': + $(element).html(box); + break; + default: + $(box).appendTo('form#node-form'); + break; + } + }).bind('click', updateNodePreview); + + function updateNodePreview() { + var preview_button = $(this); + var title = $('input#edit-title').val() || ''; + var body = $('textarea#edit-body').val() || ''; + var username = $('input#edit-name').val() || ''; + var date = $('input#edit-date').val() || ''; + var format = $('input[@name=format][@type=radio][@checked]').val() || 1; + var token = $('input[@name=form_token]#edit-'+ node_type +'-node-form-form-token').val() || ""; + + var node_div = $('div#live-node-preview'); + var node_div_background = $('div#live-node-preview-background'); + var progress_panel = $('
Loading...
'); + + jQuery.ajax({ + type: "POST", + url: base_url + 'index.php?q=live/node/preview', + data: { + node_type: node_type, + title: title, + body: body, + username: username, + date: date, + format: format, + token: token, + token_value: token_value + }, + timeout: 5000, + beforeSend: function() { + progress_panel.appendTo('body').hide().fadeIn(200); + }, + success: function(data){ + node_div.html(data); + $('div.live-progress-panel').remove(); + preview_button.val('Update Preview'); + }, + error: function() { + progress_panel.html('Error requesting data!'); + preview_button.val('Preview'); + node_div.parent().slideUp(); + setTimeout(function() { + progress_panel.fadeTo(1500, 0, function() { + $(this).remove(); + }); + }, 2500); + } + }); + return false; + } +} + +if (Drupal.jsEnabled) { + $(document).ready(Drupal.liveNodePreview); +}