To quote http://drupal.org/node/141043:

Remember anonymous comment posters. When someone leaves a comment as an anonymous user, his name, e-mail address and URL are not remembered. If we'd store that information in the user's session, he or she wouldn't have to re-enter that information every single time. A subtle but important improvement.

This is essentially moving the comment info module (with improvements I am sure) into core.

Dries brought up that this may be possible to do entirely in sessions for both caching and non-caching situations. This is a problem that pushed the comment info module to use jQuery for caching situations.

When caching in enabled (tested with non-aggressive) a page is generated with one persons session information placed in the fields. This page is cached with that persons information filled in and other users then see that persons information rather than their own in the cached page. The issue is at http://drupal.org/node/91849. Our solution was to use jQuery and cookies for cached situations putting the remembering in to the browser.

The session only code can be seen at:
http://cvs.drupal.org/viewcvs/drupal/contributions/modules/comment_info/...

Here is the relevant code:

/** 
 * Insert our checkbox, and populate fields.
 * set validation hook.
 */
function comment_info_form_alter($form_id, &$form) { 

  global $user;
  if ($user->uid || ($form_id != 'comment_form')) {
    return;
  }

  foreach (array('name','homepage','mail') as $field) {
    if (is_array($form[$field]) && $_SESSION['comment_info']['optin']) {
      $form[$field]['#value'] = $_SESSION['comment_info'][$field];
    }
  }
    
  $form['#validate']['comment_info_validate'] = array();

  $form['comment_info']['optin'] = array(
    '#type' => 'checkbox',
    '#title' => 'Save my Comment Information for next time.', 
    '#default_value' => $_SESSION['comment_info']['optin'] 
  );
}

/** 
 * save our data.
 */
function comment_info_validate($form_id, $form_values) { 

  if ($form_values['optin']) {
    foreach (array('name','homepage','mail','optin') as $field) {
      $_SESSION['comment_info'][$field] = $form_values[$field];
    }
  } else {
    foreach (array('name','homepage','mail','optin') as $field) {
      unset($_SESSION['comment_info'][$field]);
    }
  }
}

Is there a way to make this work with caching and without javascript?

The jQuery version only uses jQuery when caching is enabled. That code is at:
http://cvs.drupal.org/viewcvs/drupal/contributions/modules/comment_info/...

When I get to my development machine I'll start whipping up a patch. But first, I would like to know if there is another way to deal with cached pages. Any thoughts?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mfer’s picture

Status: Active » Needs review
FileSize
11.88 KB

Here is my first go at this patch.

The css is in system.css

When caching is disabled the session stores the anonymous users info.

When caching is enabled jQuery and cookies do the remembering. I split up the javascript. The cookie code is the jQuery cookie plugin and in is /misc/jquery.cookie.js. This can be used by other modules and is the .cookie code described at visualjquery.com.

The rest of the javascript is in /modules/comment/comment.js.

Dries’s picture

I think we can simplify this a bit. Why would we want to have a setting for this? If you look at systems like WordPress, they just remember their users. No configuration, no choice. I think it makes for sane default behavior. Therefore, I'd prefer to start out with no configuration settings. :-)

Is there a good reason to use both sessions and javascript? If we have to use Javascript, we might as well always use Javascript? Just thinking up loud.

Thanks for taking a lead in this mfer.

Dries’s picture

Another solution might be to use jQuery to call a JSON-callback in Drupal. We could store the information in the session variable, and retrieve it with a simple HTTP request. Might be less jQuery code. Food for thought.

mfer’s picture

Simplification = good

@todo: Remove checkbox to remember. Just do it.
@todo: Remove the on/off setting.

I thought about requesting the information with jQuery from drupal but figured it wasn't worth the performance hit.

Time to do some digging into how wordpress does this.

mfer’s picture

Here is another shot that is much more simplified. The jquery.cookie.js in /misc is compressed with packer. All of the configuration settings are gone and it just works when the fields are there. The comment.js file has been greatly simplified.

mfer’s picture

Lets try that attachment again

mfer’s picture

added "// $Id: $" to the comment.js file

Dries’s picture

What does that compressed function do?

mfer’s picture

The compressed function is jQuery.cookie. It's the jQuery cookie plugin from jquery.com. you can read about it's usage at http://www.visualjquery.com in the plugins section. The function reads:

jQuery.cookie = function(name, value, options) {
  if (typeof value != 'undefined') { // name and value given, set cookie
    options = options || {};
    var expires = '';
    if (options.expires && (typeof options.expires == 'number' || options.expires.toGMTString)) {
      var date;
      if (typeof options.expires == 'number') {
        date = new Date();
        date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
      } else {
        date = options.expires;
      }
      expires = '; expires=' + date.toGMTString(); // use expires attribute, max-age is not supported by IE
    }
    var path = options.path ? '; path=' + options.path : '';
    var domain = options.domain ? '; domain=' + options.domain : '';
    var secure = options.secure ? '; secure' : '';
    document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
  } else { // only name given, get cookie
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
      var cookies = document.cookie.split(';');
      for (var i = 0; i < cookies.length; i++) {
        var cookie = jQuery.trim(cookies[i]);
        // Does this cookie string begin with the name we want?
        if (cookie.substring(0, name.length + 1) == (name + '=')) {
          cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
          break;
        }
      }
    }
    return cookieValue;
  }
};

I figured it is better to use the jQuery plugin than to roll my own. And, to separate it out in case anyone else wants to use it.

mfer’s picture

I compared the jQuery.cookie code to some roll your own cookie reading and writing javascript in the contrib modules. Going with custom cookie javascript can save about 300 bytes of javascript.

Dries’s picture

Wouldn't it be easier to set the cookie from PHP?

mfer’s picture

This patch sets the cookie in the comment_form_submit and reads it with javascript. The jquery.cookie plugin is not used here and a custom function is used to read the cookie. It's less javascript.

The only question I have is how to word the watchdog event when a cookie isn't sent from php.

This or the solution in #7 both work. They just solve it different ways.

ChrisKennedy’s picture

Status: Needs review » Needs work

You have string concatenation & addition spacing errors on the setcookie line, the inline comment should go on its own line, string concatenation spacing error on the drupal_add_js & foreach lines, and returnvalue should be returnValue (although I'd just change it to be "result").

Also, it might be best to store the data in a single cookie rather than three cookies, considering that a domain has a limit of 20 cookies total per browser.

mfer’s picture

fixed the concatenation and spacing issues. Changed setcookie to setrawcookie with rawurlencode so the escape characters would be the same between php and javascript. Updated javascript to returnValue.

Moved setting the cookie from _submit to _validate to work with preview. When it saves it in _submit, you have saved values, you change a value and hit preview, the value would revert.

Still looking in to how to do it with one cookie.

mfer’s picture

Status: Needs work » Needs review
FileSize
2.96 KB

This patch moves everything into one cookie and is post FAPI3.

Dries’s picture

1. I'm trying to understand why you set one cookie, rather than 3 separate cookies?

2. The error message "Comment: failed to save cookie on node %node" is not helpful. What is the point of telling this to the administrator? What can he learn from this? What are the instructions to solve this problem? I'm not sure this is useful.

3.

ChrisKennedy’s picture

I suggested storing the data in a single cookie in #13 because only 20 cookies are allowed per domain.

mfer’s picture

I'm not sure what I was thinking with that warning message. I removed it in this patch.

I moved everything to one cookie due to the suggestion by ChrisKennedy in #13. It seemed to make sense. There is a 20 cookie limit per domain per rfc2109 which is what most browsers including IE and Firefox go by.

If we go with 3 cookies that would put drupal core at 4 cookie (with the phpsession). This way there are just 2 for drupal core. One for the session and one for the anonymous comment information. This leaves 18 open cookies rather than 16 for other modules or apps to use.

Dries’s picture

I don't think the cookie limit will be a problem. Most people will want to store data in the session anyway -- at least, that's what the past 6 years have shown us. Let's go with 3 cookies instead! :)

mfer’s picture

3 cookies it is :-D

Back to 3 cookies. This is similar to #14.

I, also, found that if the cookie doesn't exist we shouldn't be overwriting the value of the form field with '' which is what the cookie variable in the javascript is initialized with. So, if the cookie is empty no changes are made to that part of the form. This may come in handy if someone tries to theme the form to add default values.

Dries’s picture

The last patch looks good to me, and I'm inclined to commit it. I'd add an entry to the CHANGELOG.txt but I can do that for you.

The one thing I'm not 100% about is the setrawcookie() and the url-encoding. It looks like you are doing the right thing, but there might be a simpler way to achieve the say. Unfortunately, I'm not that well-versed with jQuery.

Either way, if someone can confirm that this patch works, it is RTBC. Feel free to mark it as such after some testing.

Dries’s picture

Oh, btw, thanks mfer! Good job. :)

mfer’s picture

@dries - The reason for the setrawcookie and rawurlencode has to do with the difference between php and javascript handling spaces, which drupal allows in the username. Some details in the php handbook at http://us2.php.net/manual/en/function.setcookie.php#62848.

Basically, setcookie uses urlencode to encode the cookie. This function is not RFC 1738 compliant and encodes spaces as a + which javascript does does not turn back into a space. According to the php manual on urlencode, this functionality is used for historical reasons. rawurlencode is RFC 1738 compliant and uses the same encoding as javascript.

Dries’s picture

Status: Needs review » Fixed

Didn't some more testing and research, and committed this patch. Thanks, mfer! :)

Senpai’s picture

Just an FYI, but sending three cookies to a security paranoid web surfer using IE6 is going to result in three buttons to click "Yes, accept this cookie" for. That's going to happen immediately after an anon comment event too. Doesn't look good for a site to be tossing three cookies at a user after they've submitted an anon form.

One single cookie which stores all the info for this simple, menial anonymous remembrance task would be better. It's also easier to keep track of when you're sorting through your browser's cookies looking for incriminating evidence that needs deleted.

mfer’s picture

@Senpai: I'm not sure that the 3 cookies will really be much of an issue compared to one. The trade off is about 10 more lines of javascript for that handful of users while wordpress and others are already using 3 cookies for this which has set a precedence.

Dries wanted the 3 and I'm really OK with that. If you want to submit an issue I'll subscribe to it and roll a patch if the powers at be deem it worthy.

erdemkose’s picture

Title: Remember anonymous comment posters. » Remember anonymous comment posters. (UTF-8 bug)
Status: Fixed » Needs review
FileSize
589 bytes

If you enter some non-ASCII characters in name field, after preview they will be replaced by two ASCII characters (Erdem Köse > Erdem Köse). Because escape() javascript function doesn't work with UTF-8 values. It should be replaced with decodeURIComponent() function.

mfer’s picture

oops. Looks like unescape() is deprecated. decodeURIComponent() seems to be the utf8 compliant solution here.

I haven't tested it. I'll put that on my to do list for the weekend but everything I've read says this patch has it right.

Nice catch.

webchick’s picture

This change broke comment module on Dreamhost:

http://ca.php.net/setrawcookie is only available on PHP 5.

webchick’s picture

Category: task » bug
Priority: Normal » Critical
webchick’s picture

To reproduce, try and post a comment. ;)

mfer’s picture

erdemkose and webchick, nice catches. That'll teach me to read the manual and to not only develop in php5. :-)

This patch fixes the uft bug of eremkose and worked for me on php4.

Stefan Nagtegaal’s picture

Status: Needs review » Reviewed & tested by the community

Works as expected, RTBC.

webchick’s picture

Fixing a very minor code standards issue. Please don't credit me in the patch.

Confirmed this works again under PHP 4. Thanks. :)

This patch introduced some notices, but we can clean those up after code freeze.

ChrisKennedy’s picture

Might as well fix the other spacing error.

Dries’s picture

Status: Reviewed & tested by the community » Fixed

Committed to CVS HEAD. Thanks.

erdemkose’s picture

Status: Fixed » Active

Dries, I think something went wrong. comment.js is not fixed by the patch you applied.

erdemkose’s picture

Status: Active » Fixed

comment.js is fixed in commit #67988.

robertDouglass’s picture

The cookie gets set for everybody, not just anonymous users. This is a bug. This patch removes some of the warnings and fixes the cookie bug: http://drupal.org/node/147417

Anonymous’s picture

Status: Fixed » Closed (fixed)