SSbits - Home page
Site by Carbon Crayon
Submit a Post >

Tutorials - Big bits of code to help you do more

Adding an Email Field to the Page Comment Form

SilverStripes comment system is great when you don't need anything more than the standard feilds, but as soon as you need some extra functionality, things get a little complicated. In this tutorial we will be adding an Email field to the comment form.

This isn't as simple as it should be.  I am also sure that things could be done in a better way, please comment if you have improvments.

Override PageComments

You'll need to override the ContentController->PageComments() function.  This is essentially a copy and paste from sapphire/core/control/ContentController.php and change the reference to PageCommentInterface to PageCommentExtendedInterface, which you will create in the next step.

class Page extends SiteTree {
class Page_Controller extends ContentController {
    * Returns a page comment system
   function PageComments() {
      if($this->data() && $this->data()->ProvideComments) {
         return new PageCommentExtendedInterface($this, 'PageComments', $this->data());
      } else {
         if(isset($_REQUEST['executeForm']) && $_REQUEST['executeForm'] == 'PageComments.PostCommentForm') {
            echo "Comments have been disabled for this page";

Extending PageCommentInterface

PageCommentInterface is the Request Handler for page comments.  You will need to override the function PostCommentForm().  Unfortunately because of the way this function is written calling the parent on it and then adding the new fields is not possible (as far as I know), so you must copy the existing function from the parent class and then add your new field in the fields and then to load the new field from the session you'll also have to add your new field to the loadDataFrom array.  You are not done with this file yet, now you must also set the session for your new field.  To accomplish this you'll have to extend the internal class PageCommentInterface_Form and the function postComment().  In this case you can load up your session and then just call the parent function.  Don't forget to change the reference to PageCommentInterface_Form to your new class.

class PageCommentExtendedInterface extends PageCommentInterface {
   function PostCommentForm() {
    		$fields = new FieldSet(
			new HiddenField("ParentID", "ParentID", $this->page->ID)
		$member = Member::currentUser();
		if((self::$comments_require_login || self::$comments_require_permission) && $member && $member->FirstName) {
			// note this was a ReadonlyField - which displayed the name in a span as well as the hidden field but
			// it was not saving correctly. Have changed it to a hidden field. It passes the data correctly but I 
			// believe the id of the form field is wrong.
			$fields->push(new ReadonlyField("NameView", _t('PageCommentInterface.YOURNAME', 'Your name'), $member->getName()));
			$fields->push(new HiddenField("Name", "", $member->getName()));
		} else {
			$fields->push(new TextField("Name", _t('PageCommentInterface.YOURNAME', 'Your name')));
		// optional commenter EMAIL
        $fields->push(new TextField("CommenterEmail", _t('PageCommentInterface.COMMENTEREMAIL', "Email:")));
		// optional commenter URL
		$fields->push(new TextField("CommenterURL", _t('PageCommentInterface.COMMENTERURL', "Your website URL")));
			$fields->push(new TextField("Math", sprintf(_t('PageCommentInterface.SPAMQUESTION', "Spam protection question: %s"), MathSpamProtection::getMathQuestion())));
		$fields->push(new TextareaField("Comment", _t('PageCommentInterface.YOURCOMMENT', "Comments")));
		$form = new NewPageCommentInterface_Form($this, "PostCommentForm", $fields, new FieldSet(
			new FormAction("postcomment", _t('PageCommentInterface.POST', 'Post'))
		// Set it so the user gets redirected back down to the form upon form fail
		// Optional Spam Protection.
		if(class_exists('SpamProtectorManager')) {
			SpamProtectorManager::update_form($form, null, array('Name', 'CommenterURL', 'Comment'));
		// Shall We use AJAX?
		if(self::$use_ajax_commenting) {
			Requirements::javascript(THIRDPARTY_DIR . '/behaviour.js');
			Requirements::javascript(THIRDPARTY_DIR . '/prototype.js');
			Requirements::javascript(THIRDPARTY_DIR . '/scriptaculous/effects.js');
			Requirements::javascript(CMS_DIR . '/javascript/PageCommentInterface.js');
		// Load the data from Session
			"Name" => Cookie::get("PageCommentInterface_Name"),
			"Comment" => Cookie::get("PageCommentInterface_Comment"),
			"CommenterEmail" => Cookie::get("PageCommentInterface_CommenterEmail"),
			"CommenterURL" => Cookie::get("PageCommentInterface_CommenterURL")	
		return $form;
 * @package cms
 * @subpackage comments
class NewPageCommentInterface_Form extends PageCommentInterface_Form {
	function postcomment($data) {
		Cookie::set("PageCommentInterface_CommenterEmail", $data['CommenterEmail']);
		return parent::postcomment($data);

Setuping up your site configuration

You will be creating a decorator, you must include some code in your _config.php to assign it.  You will also be creating an extension to an Admin section you will want to add a rule to redirect the comments action to your new extension, also remove the old admin section.
DataObject::add_extension('PageComment','PageCommentDecorator'); Director::addRules(50, array( 'admin/comments//$Action/$ID' => 'CommentAdminExtension', )); CMSMenu::remove_menu_item('CommentAdmin');
(NOTE:  I tried to use LeftAndMainDecorator initially instead of extending CommentAdmin, but I ran into several issues)

Decorating PageComment

This could be as simple as just adding a new db static and pushing a new TextField onto CMSFields, however I took it a step further and set it up to send and email after someone posts a comment.  The email needs better formatting and it could use the admin email and the links to delete and mark as spam could be generated more dynamically.  This is not really part of the tutorial but I thought I would share.

class PageCommentDecorator extends DataObjectDecorator  {
   function extraStatics() {
      return array(
         'db' => array(
            "CommenterEmail" => "Varchar(255)",
	public function updateCMSFields(FieldSet &$fields) {
        $fields->push(new TextField('CommenterEmail', 'Commenter Email'));
	function updateFieldLabels(&$labels) {
      $labels['CommenterEmail'] = "Commenter Email";
   function onAfterWrite() {
        $post = DataObject::get_one('SiteTree', "`SiteTree`.ID=".$this->owner->getField('ParentID'));
        $e = new Email(
            $this->owner->getField('CommenterEmail'),//From Address
            '',//To Address - Currently hard coded should get admin address and/or posters address
            "Posted a comment on ".$post->title,//Subject
            "".$this->owner->getField('Name') . " said " . '"' . $this->owner->getField('Comment') . '"<br/>'.
            '<a href="' . $this->owner->getField('ID') . '?methodName=spam">Mark as SPAM</a><br/>'.
            '<a href="' . $this->owner->getField('ID') . '/delete">DELETE</a>'

Extending CommentAdmin

And Finally, if you want to see the new data fields in the backend you'll have to extend the CommentAdmin and override EditForm(). Again, I had to copy and paste the function from the source due to the way it was written.  You must add the table field and the PopupFields for your new data field.

/*Copied from CommentAdmin.php version 2.3.1
    - Added Email field
class CommentAdminExtension extends CommentAdmin  {	
	public function EditForm() {
		$section = $this->Section();
		if($section == 'approved') {
			$filter = 'IsSpam=0 AND NeedsModeration=0';
			$title = "<h2>". _t('CommentAdmin.APPROVEDCOMMENTS', 'Approved Comments')."</h2>";
		} else if($section == 'unmoderated') {
			$filter = 'NeedsModeration=1';
			$title = "<h2>"._t('CommentAdmin.COMMENTSAWAITINGMODERATION', 'Comments Awaiting Moderation')."</h2>";
		} else {
			$filter = 'IsSpam=1';
			$title = "<h2>"._t('CommentAdmin.SPAM', 'Spam')."</h2>";
		$filter .= ' AND ParentID>0';
		$tableFields = array(
			"Name" => _t('CommentAdmin.AUTHOR', 'Author'),
			"Comment" => _t('CommentAdmin.COMMENT', 'Comment'),
			"CommenterEmail" => _t('CommentAdmin.COMMENTEREMAIL', 'Email'),
			"PageTitle" => _t('CommentAdmin.PAGE', 'Page'),
			"CommenterURL" => _t('CommentAdmin.COMMENTERURL', 'URL'),
			"Created" => _t('CommentAdmin.DATEPOSTED', 'Date Posted')
		$popupFields = new FieldSet(
			new TextField('Name', _t('CommentAdmin.NAME', 'Name')),
			new TextField('CommenterEmail', _t('CommentAdmin.COMMENTEREMAIL', 'Email')),
			new TextField('CommenterURL', _t('CommentAdmin.COMMENTERURL', 'URL')),
			new TextareaField('Comment', _t('CommentAdmin.COMMENT', 'Comment'))
		$idField = new HiddenField('ID', '', $section);
		$table = new CommentTableField($this, "Comments", "PageComment", $section, $tableFields, $popupFields, array($filter));
		$fields = new FieldSet(
			new TabSet(	'Root',
				new Tab(_t('CommentAdmin.COMMENTS', 'Comments'),
					new LiteralField("Title", $title),
		$actions = new FieldSet();
		if($section == 'unmoderated') {
			$actions->push(new FormAction('acceptmarked', _t('CommentAdmin.ACCEPT', 'Accept')));
		if($section == 'approved' || $section == 'unmoderated') {
			$actions->push(new FormAction('spammarked', _t('CommentAdmin.SPAMMARKED', 'Mark as spam')));
		if($section == 'spam') {
			$actions->push(new FormAction('hammarked', _t('CommentAdmin.MARKASNOTSPAM', 'Mark as not spam')));
		$actions->push(new FormAction('deletemarked', _t('CommentAdmin.DELETE', 'Delete')));
		if($section == 'spam') {
			$actions->push(new FormAction('deleteall', _t('CommentAdmin.DELETEALL', 'Delete All')));
		$form = new Form($this, "EditForm", $fields, $actions);
		return $form;

That is it.  I'd apreciate any feedback you have as this is my first tutorial.  I'm considering packaging this as a module, it would also be my first module.  I feel the code needs to be a bit better for a module so if you have any improvements don't hesitate contacting me.

Special thanks to Mike Mannix for running through this and proving it works and is somewhat easy to follow, as well as fixing my horrible grammar.  Mike is working on his first Silverstripe site, he completed the level one tutorials and then moved on to this.  It is a work in progress so keep an eye on it.

Dan Rye avatar

Dan Rye

Dan is a developer running a small Photography, Design, and Development Studio - Rye Designs with his wife Vicki. Based in the Cincinnati, OH area, serving the tri-state area and beyond. Dan is also a Software Engineer for various other companies, developing various other applications.

  • Bert
    10/11/2010 9:23pm (7 years ago)

    Has the last part of this tutorial been cut off? Because it seems incomplete.

  • Dan Rye
    11/11/2010 2:16am (7 years ago)

    Bert, Thanks for commenting. There is an issue with the formatting, I believe all the content is there however. I have submitted an update for approval.

  • Barry Keenan
    13/12/2010 1:51am (7 years ago)

    Nice one! It's so not obvious how to do this. I tried useCustomClass() and
    add_extension() in _config.php before I came across this post. Cheers :)

  • Aram Balakjian
    21/12/2010 1:29pm (7 years ago)

    Hi Guys, this was indeed missing a load of code. I have recopied it from the original, let me know if there are any more issues.

  • Bert
    08/01/2011 8:35pm (6 years ago)

    Great stuff. Thanks for the update Dan & Aral!

  • Bert
    08/01/2011 8:36pm (6 years ago)

    Aram that is :-)

  • borriej
    02/03/2011 12:12pm (6 years ago)

    Ok nice, it works, but how do i add required fields? I can post empty comments!

  • borriej
    02/03/2011 1:00pm (6 years ago)


  • borriej
    02/03/2011 1:23pm (6 years ago)

    Ok found it!

    $form = new NewPageCommentInterface_Form($this, "PostCommentForm", $fields, new FieldSet(
    new FormAction("postcomment", _t('PageCommentInterface.POST', 'Post'))), new RequiredFields('Name', 'CommenterEmail', 'Comment'));

  • artsus4
    21/09/2011 11:20pm (6 years ago)

    Thanks for the article. It inspired me to write a post with a solution for those who just need to delete "Your web site URL" field:

  • Colin Burns
    05/10/2011 10:52pm (6 years ago)

    Hi Guys,

    I have implemented this additional Email field on a couple of sites now and I get the same problem on both so I thought I would ask if any of you guys have solved it before I try to tackle it.

    In the Comment Admin section the first page of comments displays fine and you're able approve/spam/delete comments etc, but unfortunately the pagination no longer works when this CommentInterface is implement.

    Error is:

    The action 'Comments' does not exist in class CommentAdminExtension

    Just thought I would check here before I try to solve the problem myself. If No one has I will try to figure it out and then post the solution.


  • John Black
    28/10/2011 7:06pm (6 years ago)

    I hope this kind of customization is being improved in upcoming releases. It seems like there is a disconnect between page comments and other Member behaviors/identities. For example, if I am not logged in and I post a comment with an email address that matches a Member, is any association made with that member, even though I didn't supply a password?

    Has anyone had any luck integrating 3rd-party auth (openID, facebook connect, Janrain/RPXNow, etc.) with comments?

  • heibe
    03/04/2012 4:14am (5 years ago)

    I admire what you have done here. I love the part where you say you are doing this to give back but I would assume by all the comments that is working for you as well. Do you have any more info on this?

  • Room9
    16/04/2012 12:43am (5 years ago)

    This is much easier now they've added an extension. The following remove the URL field and adds an email one:


    Object::add_extension('PageCommentInterface', 'MyPageCommentInterface');
    Director::addRules(50, array( 'admin/comments//$Action/$ID' => 'MyCommentAdmin', ));


    class MyPageCommentInterface extends DataObjectDecorator {
    function updatePageCommentForm($form) {
    // optional commenter email
    $form->fields->insertBefore(new TextField("CommenterEmail", _t('PageCommentInterface.COMMENTEREMAIL', "Your email")),'Comment');



    class MyPageComment extends DataObjectDecorator {
    function extraStatics() {
    return array(
    'db' => array(
    "CommenterEmail" => "Varchar(255)",
    public function updateCMSFields(FieldSet &$fields) {
    $fields->push(new TextField('CommenterEmail', 'Commenter Email'));

    function updateFieldLabels(&$labels) {
    $labels['CommenterEmail'] = "Commenter Email";




    class MyCommentAdmin extends CommentAdmin {

    function EditForm() {
    $form = parent::EditForm();
    $commentfields = $form->fields->fieldByName('Comments')->fieldList;
    $commentfields['CommenterEmail'] = _t('CommentAdmin.COMMENTEREMAIL', 'Email');
    $form->fields->fieldByName('Comments')->fieldList = $commentfields;
    $form->fields->fieldByName('Comments')->detailFormFields->insertBefore(new TextField('CommenterEmail', _t('CommentAdmin.COMMENTEREMAIL', 'Email')),'Comment');
    return $form;


  • Terry Apodaca
    27/06/2012 11:57pm (5 years ago)

    I just tried this and it works great. only thing I can't figure out is getting the Cookie to set correctly. Now only the email field is prefilled and the others are not. i followed the tutorial line by line.

  • Xurk
    18/12/2012 1:16pm (5 years ago)

    @Colin Burns: I was just struggling with the exact same error and after mucking about for an hour trying to figure it out, I discovered that it was due to the fact that I had overlooked the *double* forward-slashes in the following line of code, between "comments" and "$Action" (turns out it is essential for executing any actions on the comment objects!):

    Director::addRules(50, array('admin/comments//$Action/$ID' => 'CommentAdminExtension'));[

  • KirAgrealkesk
    14/02/2013 2:52am (4 years ago)

    отлично излагаете <a href=""> </a>

  • Padaunpagma
    15/02/2013 8:53pm (4 years ago)

    круто..взяла почти все)) <a href=""> </a>

  • bestybearywap
    21/02/2013 8:30am (4 years ago)

    Я от них безума! <a href=""> </a> <a href=""> </a>

  • bestybearywap
    21/02/2013 10:19am (4 years ago)

    Красивый пост, многосмысленный… <a href=""> </a> <a href=""> </a>

  • Igor-IteltyScect
    05/03/2013 1:13pm (4 years ago)

    Доброго времени суток! Не вижу условия использования информации.<a href="">

  • RolfReerbisse
    07/03/2013 7:14pm (4 years ago)

    Как часто человеку приходиться выбирать между синицей в руках и журавлем, парящим над головой. Но на самом деле он выбирает между страхами. Он боится оставить все так, как есть, если его это не устраивает. И боится, что не добьется того, на что надеется, но потеряет синицу. <a href=""> </a>

Post a comment ...

You cannot post comments until you have logged in. Login Here.


Site of the Month

Find SSbits on

Top Contributers

Rank Avatar Name
1 article image Aram Balakjian
2 article image wfacewomens
3 article image Daniel Hensby
4 article image Marcus Dalgren
5 article image ryduhartchas
6 article image Hamish Campbell
7 article image Ty Barho
8 article image Martijn van Nieuwenhoven
9 article image Darren-Lee
10 article image Roman Schmid

View full leaderboard