Here I am, once again, still trying to upgrade the DiscussThis! module from D6 to D7, and making heavier weather than I had expected. I've got to the point where the DiscussThis module creates what is essentially a copy of the D6 comment module's comment entry form, however when I press "Submit", DiscussThis calls comment_form_validate(), at which point I get a catastrophic error as follows:
EntityMalformedException: Missing bundle property on entity of type comment. in entity_extract_ids() (line 7665 of /Users/joelguesclin/Sites/acquia-drupal/includes/common.inc).
Now I'm completely lost, since I have no idea of how Entities work. I presume that there is something missing from my comment form, but what?
Any pointers would be welcome.

Comments

jaypan’s picture

Since you haven't shown us any code, unless a psychic comes along, it's not likely we can help.

Contact me to contract me for D7 -> D10/11 migrations.

joel_guesclin’s picture

The problem is, I didn't write the module so I'm a bit lost in exactly how it works. Roughly, what is supposed to happen is this:
1) The user who wants to start a discussion on an article clicks on the "DiscussThis" link, which causes a "comment" form to be displayed in a separate page. When the user presses the "Submit" button, the module creates a new Forum topic with links to the original node displayed in the body of the topic, and adds the comment as the first comment under the topic.
Unfortunately I don't know any psychics, so here is the code which as far as I can see actually builds the form that gets filled in:

function discussthis_create_form($form_state, $nid) {
    // the user usually enters this function because the topic does not
    // exist yet, this form asks for the comment with our own discussthis
    // form (i.e. avoid creating a new forum node until the person posts
    // his/her comment out)
    //
    // IMPORTANT: we do NOT check whether the topic already exists because
    // it might when the person clicks on Preview. Please, see the submit
    // function for more information in that regard. Just don't add a test
    // for the topic ID in this function...

    // current user has the right to do that?!
    if (!user_access('initiate discuss this topics')) {
        drupal_access_denied();
        return;
    }

    $title = variable_get('discussthis_new_post_title', '');
    if ($title) {
        drupal_set_title($title);
    }

    global $user;
    $op = empty($form_state['post']['op']) ? '' : $form_state['post']['op'];
    $is_ready = $op == t('Preview') || $op == t('Save');
    if ($is_ready) {
        $comment = $user;
        $comment->comment = check_markup($form_state['post']['comment'], $form_state['post']['format'], $langcode = '' /* TODO Set this variable. */, FALSE);
        $comment->timestamp = REQUEST_TIME;
        $comment->new = FALSE; // this is not displayed in a very good way, in general
        $comment->preview = TRUE;
        $comment->subject = $form_state['post']['subject'];
        $node = array(
                'type' => 'forum',
        );
        $node = (object) $node;
        $form['preview_comment'] = array(
                '#title' => t('Preview'),
                '#value' => theme('comment', $comment, $node),
        );
    }

    $comment_anonymous_forum = variable_get('comment_anonymous_forum', COMMENT_ANONYMOUS_MAYNOT_CONTACT);
    if (!$user->uid && $comment_anonymous_forum != COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
        drupal_add_js(drupal_get_path('module', 'comment') . '/comment.js');
    }

// Note that in D7 the #value key doesn't work you need to use #markup

    if ($user->uid) {
        $form['author']['_author'] = array(
                '#type' => 'item',
                '#title' => t('Your name ho hum '),
                '#markup' => theme('username', array('account' => $user)),
        );
        $form['author']['author'] = array(
                '#type' => 'value',
                '#value' => $user->name,
        );
    }
    elseif ($comment_anonymous_forum == COMMENT_ANONYMOUS_MAY_CONTACT) {
        $form['name'] = array(
                '#type' => 'textfield',
                '#title' => t('Your name'),
                '#maxlength' => 60,
                '#size' => 30,
                '#default_value' => variable_get('anonymous', t('Anonymous')),
        );

        $form['mail'] = array(
                '#type' => 'textfield',
                '#title' => t('E-mail'),
                '#maxlength' => 64,
                '#size' => 30,
                '#description' => t('The content of this field is kept private and will not be shown publicly.'),
        );

        $form['homepage'] = array(
                '#type' => 'textfield',
                '#title' => t('Homepage'),
                '#maxlength' => 255,
                '#size' => 30,
        );
    }
    elseif ($comment_anonymous_forum == COMMENT_ANONYMOUS_MUST_CONTACT) {
        $form['name'] = array(
                '#type' => 'textfield',
                '#title' => t('Your name'),
                '#maxlength' => 60,
                '#size' => 30,
                '#default_value' => variable_get('anonymous', t('Anonymous')),
                '#required' => TRUE,
        );

        $form['mail'] = array(
                '#type' => 'textfield',
                '#title' => t('E-mail'),
                '#maxlength' => 64,
                '#size' => 30,
                '#description' => t('The content of this field is kept private and will not be shown publicly.'),
                '#required' => TRUE,
        );

        $form['homepage'] = array(
                '#type' => 'textfield',
                '#title' => t('Homepage'),
                '#maxlength' => 255,
                '#size' => 30,
        );
    }

    if (variable_get('comment_subject_field_forum', 1) == 1) {
        $form['subject'] = array(
                '#type' => 'textfield',
                '#title' => t('Subject'),
                '#maxlength' => 64,
        );
    }

    $form['comment_filter']['comment'] = array(
            '#type' => 'textarea',
            '#title' => t('Comment'),
            '#rows' => 15,
            '#required' => TRUE,
    );

//    $form['comment_filter']['format'] = filter_form(FILTER_FORMAT_DEFAULT);

    $form['uid'] = array(
            '#type' => 'value',
            '#value' => $user->uid,
    );
    $form['discussthis_nid'] = array(
            '#type' => 'value',
            '#value' => $nid,
    );

    // Only show save button if preview is optional or if we are in preview mode.
    // We show the save button in preview mode even if there are form errors so that
    // optional form elements (e.g., captcha) can be updated in preview mode.
    if ($is_ready || variable_get('comment_preview_forum', COMMENT_PREVIEW_REQUIRED) == COMMENT_PREVIEW_OPTIONAL) {
        $form['submit'] = array(
                '#type' => 'submit',
                '#value' => t('Save'),
                '#weight' => 19,
        );
        $form['#submit'][] = 'discussthis_create_form_submit';
//    }

    $form['preview'] = array(
            '#type' => 'button',
            '#value' => t('Preview'),
            '#weight' => 20,
    );

    return $form;
}

I presume that pressing submit causes this function to be called, though I am not sure how:

function discussthis_create_form_validate($form, &$form_state) {
    comment_form_validate($form, $form_state);
}

As far as I can work out, it is during comment_form_validate() that the error is generated

jaypan’s picture

There are a couple of issues in your code. First, the relevant code is here:

       $comment = $user;
        $comment->comment = check_markup($form_state['post']['comment'], $form_state['post']['format'], $langcode = '' /* TODO Set this variable. */, FALSE);
        $comment->timestamp = REQUEST_TIME;
        $comment->new = FALSE; // this is not displayed in a very good way, in general
        $comment->preview = TRUE;
        $comment->subject = $form_state['post']['subject'];

Your comment is being initialized with this line of code:

$comment = $user;

I have no idea what the original developer was doing, but this is most definitely the wrong way to be doing it. They are initializing the comment object as a copy of the $user object. The problem with this is that a comment is not a user, it's a comment - completely different entities. This could even potentially open some security holes, because if you have fields with the same name on both entities, you could expose private information when saving the comment this way.

To initialize an entity, you should use the following:

$entity = entity_get_controller($entity_type)->create($entity_type, $bundle_type);

So you will use something like:
To initialize an entity, you should use the following:

$comment = entity_get_controller('comment')->create('comment', 'comment');

However, I have not checked to see if the entity type and bundle type of comments are actually 'comment' as I've used in the example above, so if that fails, you should use entity_get_info() to get all info types, and dig through the returned value to find the entity name for comments, as well as the bundle name.

Contact me to contract me for D7 -> D10/11 migrations.

joel_guesclin’s picture

The code you suggested didn't work I'm afraid because "create" is not a method of the class. I tried this:

$comment = entity_get_controller('comment')->__construct('comment', 'comment');
$form_state['comment'] = $comment;

It worked, inasmuch as it didn't give me an error, but it didn't solve the problem.

jaypan’s picture

You're right, it appears that no create() method exists for the comment class. You can read more about programmatically creating comments here: http://drupal.org/node/1030676

Contact me to contract me for D7 -> D10/11 migrations.