? 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. <em>Example: body, form#node-form, div.my-class</em>'),
+    '#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 = '<h2>'. t('Comment Preview') .'</h2><div class="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 = '<div id="live-preview-container"><div id="live-node-preview"></div><div id="live-node-preview-background"></div></div>';
+    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 = $('<div class="live-progress-panel">Loading...</div>');
+
+    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);
+}
