Customizing 'Comments' (code / tutorial)

Hi All,

As some of you have probably already seen from other posts around the elgg community I've been tinkering with having my own customized comments system for use within a plugin. So far I've had some pretty pleasing results (thanks to the support of the elgg community & despite my not especially solid programming).

So its time to share what I have so far - The end result is basically a comments form that allows for a comment (text, ->candid) a rating (radio button, ->rate) and a feature (checkbox, ->features). Of course this can be extended to whatever you want to get feedback on or whatever areas you want users to provide input. I have.

Two significant problems/issues so far are:

1. It turns out this will overwrite the default comments system for all other instances in other plugins (i.e the default BLOG, or PAGES etc). This might be fine for you - My intention was for it to be a comments system for the specific plugin (i.e so the comments form can relate to the specific plugin its housed in so to speak.)

2. The delete function for the comments works - but returns a fatal error before doing as desired.

Elgg community feedback & suggestions are greatly appreciated. This should be a resource for all to take advantage of & I hope in posting this I'm helping & not opening a big ol can of worms....

CODE TO HELP CUSTOMIZE COMMENTS!

1.0 myplugin/start.php

1.1 myplugin/actions/comments/add.php

1.2 myplugin/actions/comments/delete.php

1.3 myplugin/views/default/comments/forms/edit.php

2.0 Get the comments and display them

2.1 Add a delete button to each comment so the owner of the object can delete rubbish

2.2 Show the comments input form for users to add comments

 

1.0 -------> myplugin/start.php

 

<?php  

//all the other usual start.php stuff

 

function myplugin_annotate_comments($hook, $entity_type, $returnvalue, $params)

{

$entity = $params['entity'];

$full = $params['full'];

if (

($entity instanceof ElggEntity) && // Is the right type 

($entity->getSubtype() == 'myplugin') &&  // Is the right subtype

($entity->comments_on!='Off') && // Comments are enabled

($full) // This is the full view

)

{

// Display comments

return elgg_view_comments($entity);

}

}

//plus register the actions for the comments

register_action("myplugin/comment/add",false,$CONFIG->pluginspath . "myplugin/actions/comments/add.php");

register_action("myplugin/comment/delete",false,$CONFIG->pluginspath . "myplugin/actions/comments/delete.php");

?>

 

1.1 -------> myplugin/actions/comments/add.php

<?php

gatekeeper();

action_gatekeeper();

// Get input

$entity_guid = (int) get_input('entity_guid');

$comment_candid = get_input('candid_comment');

$comment_rate = get_input('rate_comment');

$comment_features = serialize(get_input('features_comment'));

 

// Make sure details aren't empty

if (empty($comment_candid) || empty($comment_rate)) {

register_error(elgg_echo("myplugin:commentblank"));

forward("mod/myplugin/read.php?mypluginpost={$entity_guid}");

//forward("pg/myplugin/{$entity_guid}");

// Start Saving the comment

} else {

// get an entity with the GUID

if ($entity = get_entity($entity_guid)) {

              

       try {

// create a new type of ElggObject for comments   

$myplugincomment = new ElggObject();   

$myplugincomment->subtype = "comment";   

$myplugincomment->owner_guid = $_SESSION['user']->getGUID();   

$myplugincomment->container_guid = $_SESSION['user']->getGUID();

$myplugincomment->access_id = 1; // logged in users   

$myplugincomment->parent_guid = $entity_guid; 

 

$myplugincomment->candid = $comment_candid;

$myplugincomment->rate = $comment_rate;

$myplugincomment->features = $comment_features;

$myplugincomment->save();  

if ($entity->owner_guid != $_SESSION['user']->getGUID())

notify_user($entity->owner_guid, $_SESSION['user']->getGUID(), elgg_echo('generic_comment:email:subject'), 

sprintf(

elgg_echo('generic_comment:email:body'),

$entity->title,

$_SESSION['user']->name,

$comment_text,

$entity->getURL(),

$_SESSION['user']->name,

$_SESSION['user']->getURL()

)

); 

system_message(elgg_echo("generic_comment:posted"));  

         add_to_river('river/object/myplugin/annotate','annotate',$_SESSION['user']->guid, $entity_guid);

       } catch (Exception $e) {

        register_error(elgg_echo("generic_comment:failure"));

}

       

} else {

register_error(elgg_echo("generic_comment:notfound"));

}

forward($entity->getURL());

}

?>

 

1.2 ------> myplugin/actions/comments/delete.php

 

<?php

gatekeeper();

$returnurl = full_url();

$guid = get_input('comment_guid');

$comment = get_entity($guid);

 

// Delete the myplugin post

$rowsaffected = $comment->delete();

if ($rowsaffected > 0) {

// Success message

system_message(elgg_echo("generic_comment:deleted"));

} else {

register_error(elgg_echo("generic_comment:notdeleted"));

}

 

forward($returnurl); 

?>

 

1.3 ------> myplugin/views/default/comments/forms/edit.php

<?php

 

if (isset($vars['entity']) && isloggedin()) {

 

$form_body = "<div class=\"contentWrapper\"><p class='longtext_editarea'><label>".elgg_echo('myplugin:comments:title')."<br /><small>".elgg_echo('myplugin:comments:help') . "</small></label>"; 

 

// candid_comment

$form_body .= "<br/><br/><label><strong>".elgg_echo('Write a comment: ') ."</strong><br />". elgg_view('input/text',array('internalname' => 'candid_comment')) . "</label></p>";

// rate_comment

$form_body .= "<label><strong>".elgg_echo('Rate this: ')."&nbsp;&nbsp; </strong><br />" . elgg_view('input/radio',array('internalname' => 'rate_comment', 'value'=>"2" , 'options' => array(elgg_echo("Poor") => "1", elgg_echo("Ordinary") => "2", elgg_echo("Good") => "3", elgg_echo("Impressive") => "4", elgg_echo("Amazing") => "5"))) . "</label>";

// features_comment

$form_body .= "<label><strong>".elgg_echo('Features: ')."&nbsp;&nbsp; </strong><br />" . elgg_view('input/checkboxes',array('internalname' => 'features_comment' , 'options' => array(elgg_echo("Original") => "Original" , elgg_echo("Smelly") => "Smelly" ))) ."</label>";

 

$form_body .= "<p>" . elgg_view('input/hidden', array('internalname' => 'entity_guid', 'value' => $vars['entity']->getGUID()));

$form_body .= elgg_view('input/submit', array('value' => elgg_echo("save"))) . "</p></div>";

echo elgg_view('input/form', array('body' => $form_body, 'action' => "{$vars['url']}action/myplugin/comment/add"));

   }

    

?>

 

2.0 -------> Get the comments and display them

<?php

// from within whatever page/view you have the user comments displayed on in your plugin

if($comments_on && $vars['entity'] instanceof ElggObject){

$composts = elgg_get_entities_from_metadata(array('metadata_names'=>'parent_guid','metadata_values'=>$vars['entity']->guid, 'type'=>'object', 'subtype'=>'comment'));

foreach($composts as $compost) {

$compostguid = $compost->guid;

$compostcreated = sprintf(elgg_echo("myplugin:strapline"), date("G:H j/m-Y",$vars['entity']->time_created)) ;

$owner = $compost->getOwnerEntity() ;

//set some basic pre-text for presenting the commentary responses.

$bytxt = elgg_echo('by');

$commenttxt = elgg_echo('Comment:');

$ratetxt = elgg_echo('Rating:');

$featurestxt = elgg_echo('Features:');

// $icon = "<p><a href=\"{$compost->getURL()}\"><img src=\"".$CONFIG->wwwroot."mod/myplugin/icon.php?mypluginguid=".$compostguid."&size=small\" heigth=\"40\" width=\"40\" /></a></p>";

$icon = elgg_view("profile/icon",array('entity' => $owner, 'size' => 'small')); 

 

$info = "<p class=\"owner_timestamp\"><small>{$compostcreated} {$bytxt} <a href=\"{$owner->getURL()}\">{$owner->name}</a></small></p>";

$info .= "<p>{$commenttxt} {$compost->candid}</p>";

$info .= "<p>{$ratetxt} {$compost->rate}</p>";

//this will return just a number I think - refer to the comments form.

$info .= "<p>{$featuretxt} {$compost->features}</p>";

//being the features are a checkbox you probably need to unserialize or work to get something more than just an 'array' 

 

2.1 -------> Add a delete button to each comment so the owner of the object can delete rubbish

if ( $compost->canEdit() ) {

$info .= elgg_view("output/confirmlink",

array( 'href' => $vars['url'] . "action/myplugin/comment/delete?comment_guid=" . $compost->getGUID(),

'text' => elgg_echo('delete'),

'confirm' => elgg_echo('deleteconfirm'),

));

 

2.2 -------> Show the comments input form for users to add comments (presented after existing comments are displayed)

// after you've ended the foreach routine at the start of 2.0 and use a new or different css div

 

echo elgg_view("comments/forms/edit",array('entity' => $vars['entity']));

 

  • 2.0... forgot to put an elgg_view at the end to display a listing of both $icon and $info

    just use something like:

    echo elgg_view_listing($icon, $info);

    and end the foreach routine

    ***NOW JUST TO GET THAT DELETE BUTTON WORKING! BUMP***

  • I realized, of course, and obviously! why this above would override comments site-wide.

    using views/default/comments/forms/edit.php in the plugin overrides the views system - so best to put the comment form somewhere else or call it something different. :)

    ...hrmm *any ideas on the delete button problem?*

  • Hey...

    I'm now completely stumped - more so than I was - regarding the Delete button/function.

    It still returns a 


    Fatal error: Call to a member function delete() on a non-object in /home/ban33409/public_html/test/mod/bars/actions/comments/delete.php on line so & so

    (so & so being where the call to ->delete() is obviously)

    I've also tried delete_entity providing the $guid and both TRUE or FALSE for the recursive value (whatever that is, TRUE reads like what i want)

    any help cool awesome knowledgeable elggsters?

    bueller?

    bueller?

    bueller?

    please let me know if you have any thoughts at all....greatly appreciated.

  • bumpty bump.. I not see this before ;-) (best way to get my attention is to post on Hackers Elggalaxy - so I get Email notifications).

    Do you have a PlugIn package of your your code ? debugging is really easier when one can install a PlugIn and run it and check out what works and whatever issues arise can be then debugged and fixed much quickly. It is harder to try to locate pieces of code into the brain and then think over the code executing mentally...

    I'll give some time to testing/ debugging once I can install as PlugIn..

    Cheers ;-)

     

  • Hey James!

    I only briefly glanced over the code, but it looks like interesting stuff!  You may be interested in keeping your eye on (or even contributing to!!) http://trac.elgg.org/ticket/2146.  

  • Thanks guys, interesting to read, Evan. (& also Mike - vazco's comment on another page)...

    I think the focus may need to be how I'm calling the action file and what GUID (+ other) info I can send to it to ensure it's picking up the elgg object and delete it as an entity. Very odd that at this time it reports a fatal error, but still deletes the comment object.

    Any Ideas?

    @Dhrup - this plugin project as a whole will need some debugging and I'll happily pay (whoever/you) for that work when the time is right - but at the moment I'd just like to get passed this last major hurdle (he says with fingers crossed); in reality its last part of 'functionality' required.

    help/feedback greatly appreciated... :D

     

  • Hi Guys,

    furthermore to the above - Brett suggested I use var_dump to look at what entity i have hold of; and it returns the following:

    object(ElggObject)#99 (7) { ["attributes:protected"]=> array(15) { ["guid"]=> string(3) "123" ["type"]=> string(6) "object" ["subtype"]=> string(2) "17" ["owner_guid"]=> string(1) "2" ["container_guid"]=> string(1) "2" ["site_guid"]=> string(1) "1" ["access_id"]=> string(1) "1" ["time_created"]=> string(10) "1300905531" ["time_updated"]=> string(10) "1300905531" ["enabled"]=> string(3) "yes" ["tables_split"]=> int(2) ["tables_loaded"]=> int(2) ["title"]=> string(0) "" ["description"]=> string(20) "

    This is the comment right here!

    " ["last_action"]=> string(1) "0" } ["url_override:protected"]=> NULL ["icon_override:protected"]=> NULL ["temp_metadata:protected"]=> array(0) { } ["temp_annotations:protected"]=> array(0) { } ["volatile:protected"]=> array(0) { } ["valid:private"]=> bool(false) }

    Which to me looks like the right 'object' -But I can't for the life of me delete it. ANY HINTS? SUGGESTIONS?

    The only progress I have made is in being able to delete ALL comments at once. this is via something similar to:

    //delete.php

    $guid = get_input('compost_guid');

    $the_comment = get_entity($guid);    //returns heaps of data

    $posts_to_delete = elgg_get_entities_from_metadata(array('metadata_names'=>'parent_guid','metadata_values'=>$vars['entity']->guid, 'type'=>'object', 'guid'=>$guid , 'subtype'=>'myplugincomment'));

    // changed the subtype (from above code) to properly differentiate from the default comment system - just in case?

     foreach ($posts_to_delete as $post_to_delete) {
                   $humpday = $post_to_delete->getGUID();
                   $rowsaffected = delete_entity($humpday, TRUE);
                   if ($rowsaffected > 0) {
                    // Success message
                   system_message(elgg_echo("generic_comment:deleted"));
                  } else {
                   register_error(elgg_echo("generic_comment:notdeleted"));
                  }

     

     

  • I'm no elgg pro, but have you registered the entity in the plugin's start file. While not mandatory (I managed to have a working plugin without doing that) it is good elgg coding etiquette.

    coding is something like

    register_entity('comment', 'object'); 

    The reason I say this is because comments as standard are annotations not objects, but it seems that you have changed them into objects. Thus you might need to register them somewhere for the system to know better.

  • Hi Trajan,

    I have both;

            register_entity_type('object','myplugin');   //the main, primary object you can create with the plugin
                    register_entity_type('object','myplugincomment'); //the comment objects you can create in relation

    But I suspect this is mainly for the internal elgg site search engine (i'm guessing).

    Still no luck - I'll persist for the rest of today - In the meantime your Robust Blog is BRILLIANT! :D