diff --git a/code/controllers/CommentingController.php b/code/controllers/CommentingController.php index 700e778..f333579 100644 --- a/code/controllers/CommentingController.php +++ b/code/controllers/CommentingController.php @@ -5,7 +5,7 @@ */ class CommentingController extends Controller { - + private static $allowed_actions = array( 'delete', 'spam', @@ -43,7 +43,7 @@ class CommentingController extends Controller { /** * The record this commenting form is for - * + * * @var DataObject */ private $ownerRecord = null; @@ -127,7 +127,7 @@ class CommentingController extends Controller { if($record = $this->getOwnerRecord()) { return $record->getCommentsOption($key); } - + // Otherwise a singleton of that record if($class = $this->getBaseClass()) { return singleton($class)->getCommentsOption($key); @@ -136,7 +136,7 @@ class CommentingController extends Controller { // Otherwise just use the default options return singleton('CommentsExtension')->getCommentsOption($key); } - + /** * Workaround for generating the link to this controller * @@ -145,7 +145,7 @@ class CommentingController extends Controller { public function Link($action = '', $id = '', $other = '') { return Controller::join_links(Director::baseURL(), __CLASS__ , $action, $id, $other); } - + /** * Outputs the RSS feed of comments * @@ -156,7 +156,7 @@ class CommentingController extends Controller { } /** - * Return an RSSFeed of comments for a given set of comments or all + * Return an RSSFeed of comments for a given set of comments or all * comments on the website. * * To maintain backwards compatibility with 2.4 this supports mapping @@ -205,10 +205,10 @@ class CommentingController extends Controller { $comments->setPageLength($this->getOption('comments_per_page')); return new RSSFeed( - $comments, - $link, - $title, - $link, + $comments, + $link, + $title, + $link, 'Title', 'EscapedComment', 'AuthorName' ); } @@ -241,12 +241,9 @@ class CommentingController extends Controller { return Security::permissionFailure($this, 'You do not have permission to edit this comment'); } if(!$comment->getSecurityToken()->checkRequest($this->request)) return $this->httpError(400); - - $comment->markSpam(); - return $this->request->isAjax() - ? $comment->renderWith('CommentsInterface_singlecomment') - : $this->redirectBack(); + $comment->markSpam(); + return $this->renderChangedCommentState($comment); } /** @@ -261,10 +258,7 @@ class CommentingController extends Controller { if(!$comment->getSecurityToken()->checkRequest($this->request)) return $this->httpError(400); $comment->markApproved(); - - return $this->request->isAjax() - ? $comment->renderWith('CommentsInterface_singlecomment') - : $this->redirectBack(); + return $this->renderChangedCommentState($comment); } /** @@ -279,12 +273,37 @@ class CommentingController extends Controller { if(!$comment->getSecurityToken()->checkRequest($this->request)) return $this->httpError(400); $comment->markApproved(); - - return $this->request->isAjax() - ? $comment->renderWith('CommentsInterface_singlecomment') - : $this->redirectBack(); + return $this->renderChangedCommentState($comment); } - + + /** + * Redirect back to referer if available, ensuring that only site URLs + * are allowed to avoid phishing. If it's an AJAX request render the + * comment in it's new state + */ + private function renderChangedCommentState($comment) { + $referer = $this->request->getHeader('Referer'); + + // Render comment using AJAX + if ($this->request->isAjax()) { + return $comment->renderWith('CommentsInterface_singlecomment'); + } else { + // Redirect to either the comment or start of the page + if (empty($referer)) { + return $this->redirectBack(); + } else { + // Redirect to the comment, but check for phishing + $url = $referer . '#comment-' . $comment->ID; + // absolute redirection URLs not located on this site may cause phishing + if(Director::is_site_url($url)) { + return $this->redirect($url); + } else { + return false; + } + } + } + } + /** * Returns the comment referenced in the URL (by ID). Permission checking * should be done in the callee. @@ -324,11 +343,11 @@ class CommentingController extends Controller { // Customise action $form->setFormAction($this->Link('reply', $comment->ID)); - + $this->extend('updateReplyForm', $form); return $form; } - + /** * Request handler for reply form. @@ -403,7 +422,7 @@ class CommentingController extends Controller { 'Comment' ); } - + $dataFields->addExtraClass('data-fields'); // save actions @@ -438,7 +457,7 @@ class CommentingController extends Controller { $fields->push(new HiddenField("Name", "", $member->getName())); $fields->push(new HiddenField("Email", "", $member->Email)); } - + // we do not want to read a new URL when the form has already been submitted // which in here, it hasn't been. $form->loadDataFrom(array( @@ -447,14 +466,14 @@ class CommentingController extends Controller { 'BaseClass' => $this->getBaseClass() )); } - + // Set it so the user gets redirected back down to the form upon form fail $form->setRedirectToFormOnValidationError(true); // load any data from the cookies if($data = Cookie::get('CommentsForm_UserData')) { - $data = Convert::json2array($data); - + $data = Convert::json2array($data); + $form->loadDataFrom(array( "Name" => isset($data['Name']) ? $data['Name'] : '', "URL" => isset($data['URL']) ? $data['URL'] : '', @@ -470,17 +489,17 @@ class CommentingController extends Controller { if(!empty($member)) { $form->loadDataFrom($member); } - + // hook to allow further extensions to alter the comments form $this->extend('alterCommentForm', $form); return $form; } - + /** * Process which creates a {@link Comment} once a user submits a comment from this form. * - * @param array $data + * @param array $data * @param Form $form */ public function doPostComment($data, $form) { @@ -492,14 +511,14 @@ class CommentingController extends Controller { $this->setOwnerRecord($class::get()->byID($data['ParentID'])); } if(!$this->getOwnerRecord()) return $this->httpError(404); - + // cache users data Cookie::set("CommentsForm_UserData", Convert::raw2json($data)); Cookie::set("CommentsForm_Comment", $data['Comment']); - + // extend hook to allow extensions. Also see onAfterPostComment - $this->extend('onBeforePostComment', $form); - + $this->extend('onBeforePostComment', $form); + // If commenting can only be done by logged in users, make sure the user is logged in if(!$this->getOwnerRecord()->canPostComment()) { return Security::permissionFailure( @@ -514,7 +533,7 @@ class CommentingController extends Controller { if($member = Member::currentUser()) { $form->Fields()->push(new HiddenField("AuthorID", "Author ID", $member->ID)); - } + } // What kind of moderation is required? switch($this->getOwnerRecord()->ModerationRequired) { @@ -552,7 +571,7 @@ class CommentingController extends Controller { if ($requireModeration && !$comment->IsSpam) { Session::set('CommentsModerated', 1); } - + // clear the users comment since it passed validation Cookie::set('CommentsForm_Comment', false);