Coming from #644222: Some forms are incompatible with form caching, #245682-127: Enable use of Mollom for any form, and http://drupal.org/project/form_controller, I absolutely need a consistent form structure for comments in D7.

comment_form() currently contains a bloat of code to output 3 entirely different forms:

1) Comment administration form for editing existing comments

2) Comment submission form for authenticated users

3) Comment submission form for anonymous users

This leads to a complexity that is totally unnecessary, very hard to grok, and unpredictable for contributed modules that want to alter the form.

This patch does not change any functionality. The sole purpose is to achieve a consistent form structure for all three cases, which is not hard - it just wasn't done yet.

To clarify in advance: comment_form() also contains some very old form code ($_POST['op'] and #token), which are not used at all and probably date back to Drupal 4.7.

comment_form() also uses some strange code for embedding a comment preview. This won't be touched here, but needs to be fixed in #644222: Some forms are incompatible with form caching instead.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

sun’s picture

Sorry, silly typo. Also fixed tests accordingly.

I forgot to mention that this patch also brings consistency into node forms and comment forms. With this patch, both forms use $form['author'] as container for content author fields and (always) $form['author']['name'] as the username field (which is the most important part of this patch).

Status: Needs review » Needs work

The last submitted patch failed testing.

Dries’s picture

Status: Needs work » Needs review

Yay, for this clean-up and bringing consistency/sanity to comment module.

+++ modules/comment/comment.module	27 Nov 2009 12:58:18 -0000
@@ -1648,11 +1648,13 @@ function comment_get_display_page($cid, 
+  $anonymous_contact = variable_get('comment_anonymous_' . $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT);
+  $is_admin = (!empty($comment->cid) && user_access('administer comments'));

We might want to be consistent here. $is_anonymous and $is_admin? Or $anonymous and $administer? I know they are not 100% identical so it is not a must.

sun’s picture

@Dries: $is_admin and $anonymous_contact denote entirely different things. The latter holds the configuration value of the setting whether anonymous users may post their contact information. (on that note, the constant names are a bit wonky)

This patch fixes the failing tests.

Dries’s picture

Status: Needs review » Fixed

OK, this looks good to me then. Committed to CVS HEAD.

sun’s picture

Status: Fixed » Needs review
FileSize
2.12 KB
+++ modules/comment/comment.module	27 Nov 2009 20:28:56 -0000
@@ -1891,12 +1839,8 @@ function comment_form($form, &$form_stat
-  $form['#token'] = 'comment' . $comment->nid . (isset($comment->pid) ? $comment->pid : '');

I have no idea what this is or was for.

I need someone to fill in and replace the @todos in this patch.

sun’s picture

Title: comment_form() structure and elements are inconsistent » comment_form() structure is inconsistent / $form['#token'] WTF
Component: comment.module » forms system
Priority: Normal » Critical
Issue tags: +Security

Although dww just mentioned in IRC that he thinks that no form should define a custom #token, I absolutely don't want to be guilty for introducing a security issue. Hence, changing priority and component accordingly.

sun’s picture

Also note that http://api.drupal.org/api/function/contact_site_form/7 equally defines a custom #token. I asked chx and Heine to chime in here.

sun’s picture

Until we have background information on Form API's proper usage of #token, let's proceed with this:

Users with "administer comments" should always see the Save button when editing an existing comment.

chx’s picture

Status: Needs review » Reviewed & tested by the community

custom tokens... no wonder they appear in contact and comment -- if you do not specify a token it'll be session bound, wont work for anon cached pages. So let's leave them. The patch is good however.

sun’s picture

yay! Information! :)

So now I could write the docs for when to use a custom #token and when to not use one. However, when is it legit to do #token = FALSE?

Additionally, since we now support block caching (e.g. per role) for auth users, and blocks can contain forms, I guess the same rule that applies for anon cached pages applies to such forms in blocks?

chx’s picture

#token FALSE is useful if cross site request forgery is irrelevant to the form. Say, search. There is no harm done from searching. I would need to think on block caching and tokens... mind to open an issue and whip up a test?

Dries’s picture

Let's get some extra documentation in the patch then?

sun’s picture

chx rtbc'ed the patch in #9, which just always displays the "Save" button on comment forms for users having the "administer comments" permission. There's no need to require them to (p)review their edits.

I will do a follow-up patch to revert the removal of #token in comment_form anyway though.

sun’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
5.26 KB

I started to incorporate the gained knowledge into the code and write docs, but...

WTF? I stepped through the logic for #token all over again, and in the end, I also checked the actual output of current forms in HEAD.

There is no 'token' element in any form for anonymous users.

I thought I would have understood it, but now I'm even more confused than before... :-/

Attached patch contains the same logic for #token as before, but using a more grokable logic and most importantly more comments. Also contains the comment preview button patch from #9.

Of course, the custom tokens assigned in comment.module and contact.module make no sense with that.

Status: Needs review » Needs work

The last submitted patch failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
5.33 KB
+++ includes/form.inc	29 Nov 2009 02:59:46 -0000
@@ -666,14 +666,21 @@ function drupal_prepare_form($form_id, &
+  // @todo isset($user->uid) ?!
   elseif (isset($user->uid) && $user->uid && !$form_state['programmed']) {

Aha! Now I understand it :P

This review is powered by Dreditor.

Status: Needs review » Needs work

The last submitted patch failed testing.

chx’s picture

Riiiiiiiiight. It does not add any token to any anon page. isset($user->uid) && $user->uid that's some seriously crooked code -- it needs to be !empty in the first place and on second place... maybe the installer needs the check to not be $user->uid > 0 ? I dunno , but !empty will do. Why do we need a #token on the comment form? For anonymous? Beats me! Other security people? Why did we add this? Edit: can it be that when we added in some ancient issue the general tokening was not in place and now it can take over? Edit2: I did trace comment[token] to #28420: comment preview "Required" is easily bypassed. I *bet* it does not need to be there now.

sun’s picture

Status: Needs work » Needs review
FileSize
6.04 KB

Alright, instead of assuming it should work, we want to test and ensure that it works. :)

I expect this patch to fail, because I cannot get page caching for anon users to work in HEAD.

If it turns out that it will fail, then this needs to be fixed elsewhere.

More related to the patch: Since we're now removing the last two instances of #token in core, I don't think that there is any use-case anywhere for setting a custom token.

sun’s picture

um. That passed. Not sure why I cannot get page caching to work on my local dev site.

This one hardens the caching assertion. But tinkering some more about this, I don't think that the situation from 2005 can happen today, because Form API doesn't allow to post the form with 'op' = 'Post comment' (or whatever) in any case.

sun’s picture

So, actually, I don't think that the test makes sense in any way.

Should we drop it?

chx’s picture

I would rather see that test generalized into a form test -- submit a nonexisting button and watch submit handlers not firing...

sun’s picture

I'm trying to wrap my head around this suggestion, but I don't see how I could write such a test, because:

  protected function drupalPost($path, $edit, $submit, array $options = array(), array $headers = array()) {
...
        // We post only if we managed to handle every field in edit and the
        // submit button matches.
        if (!$edit && $submit_matches) {

Removed the previously added comment test for now.

sun’s picture

Fixed a stale comment.

Damien Tournoud’s picture

We had a way to do a direct post in a very old patch that got staled for lack of reviews: #346095-20: Test #ajax

sun’s picture

Priority: Critical » Normal

Further debugging revealed something else, which is going to be tackled on the security list. I think that this patch is fine to commit though.

chx’s picture

Status: Needs review » Reviewed & tested by the community

I think so too. Raw posting is a diff issue.

Dries’s picture

Status: Reviewed & tested by the community » Fixed

Looks good to me too. The newly added documentation is helpful. Committed. Thanks!

Heine’s picture

can it be that when we added in some ancient issue the general tokening was not in place and now it can take over?

Right, the token was only in comments / contact to combat spam and later coopted to use as CSRF protection. This was also when we noticed that if you removed the token field from $_POST, #default_value would be used. As such, anti-spam tokens were never working :)

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

sun’s picture

For D8, we can finally remove the inconsistent form element for the comment author name:
#1866978: Leverage new #input facility of #type 'item' for author name in comment form