/ */ class Comment_IndexController extends Zend_Controller_Action { public $contexts = array( 'post' => array('partial', 'json'), 'vote-up' => array('json'), 'vote-down' => array('json'), 'moderate' => array('json'), 'delete' => array('json') ); /** * Provide a comment form and save them when posted. */ public function postAction() { $view = $this->view; $request = $this->getRequest(); $path = $request->getParam('path'); $user = P4Cms_User::fetchActive(); // setup form. $form = new Comment_Form_Comment; $view->form = $form; $form->setDefault('path', $request->getParam('path')); $form->setAction( $this->getHelper('url')->url( array( 'module' => 'comment', 'controller' => 'index', 'action' => 'post' ) ) ); // can't post without a path. if (!$path) { throw new Comment_Exception( "Cannot post comments without specifying a comment path." ); } // verify posts are allowed to this path. $options = $this->_getOptionsForPath($path); if (!$options['allowComments'] || ($options['requireLoginPost'] && $user->isAnonymous()) ) { $message = $options['requireLoginPost'] ? "Anonymous comments " : "Comments "; $message .= "are not permitted on this content entry."; throw new P4Cms_AccessDeniedException($message); } // if not posted, all done. if (!$request->isPost()) { return; } // valid post, save the comment // otherwise, set errors on the view if ($form->isValid($request->getParams())) { $comment = new Comment_Model_Comment; $id = trim($form->getValue('path'), '\\/') . '/' . (string) new P4Cms_Uuid; // if explicit approval is required, mark as pending // otherwise, mark as approved automatically. $status = $options['requireApproval'] ? Comment_Model_Comment::STATUS_PENDING : Comment_Model_Comment::STATUS_APPROVED; $comment->setValues($form->getValues()) ->setValue('user', $user->getId()) ->setValue('postTime', time()) ->setValue('status', $status) ->setId($id) ->save(); } else { $view->errors = $form->getMessages(); } } /** * Vote up a given comment. */ public function voteUpAction() { $this->_vote(true); } /** * Vote down a given comment. */ public function voteDownAction() { $this->_vote(false); } /** * Moderate comments. * * @publishes p4cms.comment.grid.actions * Modify the passed menu (add/modify/delete items) to influence the actions shown * on entries in the Moderate Comments grid. * P4Cms_Navigation $actions A menu to hold grid actions. * * @publishes p4cms.comment.grid.data.item * Return the passed item after appling any modifications (add properties, change * values, etc.) to influence the row values sent to the Moderate Comments grid. * array $item The item to potentially modify. * mixed $model The original object/array that was used * to make the item. * Ui_View_Helper_DataGrid $helper The view helper that broadcast this * topic. * * @publishes p4cms.comment.grid.data * Adjust the passed data (add properties, modify values, etc.) to influence the * row values sent to the Moderate Comments grid. * Zend_Dojo_Data $data The data to be filtered. * Ui_View_Helper_DataGrid $helper The view helper that broadcast this * topic. * * @publishes p4cms.comment.grid.populate * Adjust the passed query (possibly based on values in the passed form) to * influence which comments will be shown on the Moderate Comments grid. * P4Cms_Record_Query $query The query used to filter comments. * P4Cms_Form_PubSubForm $form A form containing filter options. * * @publishes p4cms.comment.grid.render * Make adjustments to the datagrid helper's options pre-render (e.g. change * options to add columns) for the Moderate Comments grid. * Ui_View_Helper_DataGrid $helper The view helper that broadcast this * topic. */ public function moderateAction() { // enforce permissions $this->acl->check('comments', 'moderate'); // use the management layout (for traditional requests) if (!$this->contextSwitch->getCurrentContext()) { $this->getHelper('layout')->setLayout('manage-layout'); $this->getHelper('helpUrl')->setUrl('comments.manage.html'); } // setup grid options form $request = $this->getRequest(); $namespace = 'p4cms.comment.grid'; $form = new Ui_Form_GridOptions(array('namespace' => $namespace)); $form->populate($request->getParams()); // collect the actions from interested parties $actions = new P4Cms_Navigation; P4Cms_PubSub::publish($namespace . '.actions', $actions); // setup view $view = $this->view; $view->form = $form; $view->actions = $actions; $view->pageSize = $request->getParam('count', 100); $view->rowOffset = $request->getParam('start', 0); $view->pageOffset = round($view->rowOffset / $view->pageSize, 0) + 1; $view->headTitle()->set('Moderate Comments'); // set data-grid view helper namespace $helper = $view->dataGrid(); $helper->setNamespace($namespace); // early exit for standard requests if (!$this->contextSwitch->getCurrentContext()) { return; } // construct comment query. $query = new P4Cms_Record_Query; $query->setRecordClass('Comment_Model_Comment'); // prepare sorting options $sortKey = $request->getParam('sort', 'postTime'); if (substr($sortKey, 0, 1) == '-') { $query->setSortBy( substr($sortKey, 1), array(P4Cms_Record_Query::SORT_ASCENDING) ); } else { $query->setSortBy( $sortKey, array(P4Cms_Record_Query::SORT_DESCENDING) ); } // allow third-parties to influence query. try { P4Cms_PubSub::publish($namespace . '.populate', $query, $form); } catch (Exception $e) { P4Cms_Log::logException("Error building comments list.", $e); } // add query to the view. $view->query = $query; } /** * Delete a comment. */ public function deleteAction() { // enforce permissions $this->acl->check('comments', 'moderate'); $request = $this->getRequest(); // require post request method. if (!$request->isPost()) { throw new P4Cms_AccessDeniedException( "Cannot delete comment. Request method must be http post." ); } $comment = Comment_Model_Comment::fetch($request->getParam('id')); $comment->delete(); // setup view. $this->view->comment = $comment; // add notification and redirect for traditional requests. if (!$this->contextSwitch->getCurrentContext()) { P4Cms_Notifications::add('Comment deleted.', P4Cms_Notifications::SEVERITY_SUCCESS); $this->redirector->gotoSimple('moderate'); } } /** * Change the state of a comment. */ public function statusAction() { // enforce permissions $this->acl->check('comments', 'moderate'); // require post request method. $request = $this->getRequest(); if (!$request->isPost()) { throw new P4Cms_AccessDeniedException( "Cannot change comment status. Request method must be http post." ); } // update comment status. $state = $request->getParam('state'); $comment = Comment_Model_Comment::fetch($request->getParam('id')); $comment->setStatus($state) ->save("Changed comment status to '$state'."); // setup view. $this->view->comment = $comment; // add notification and redirect for traditional requests. if (!$this->contextSwitch->getCurrentContext()) { P4Cms_Notifications::add("Comment $state.", P4Cms_Notifications::SEVERITY_SUCCESS); $this->redirector->gotoSimple('moderate'); } } /** * Vote a comment up or down. Shared by vote-up/down actions. * * @param bool $up optional - vote up (defaults to true) * pass false to vote down. */ protected function _vote($up = true) { $user = P4Cms_User::fetchActive(); $request = $this->getRequest(); $comment = Comment_Model_Comment::fetch($request->getParam('id')); // check if voting is allowed. $options = $this->_getOptionsForPath(dirname($comment->getId())); if (!$options['allowVoting']) { throw new P4Cms_AccessDeniedException( "Voting is not permitted on this comment." ); } // if one vote per-user, no anonymous voting. if ($options['oneVotePerUser'] && $user->isAnonymous()) { throw new P4Cms_AccessDeniedException( "Anonymous voting is not permitted." ); } // only one vote per-user - if the user has already // voted on this comment, don't let them vote again if ($options['oneVotePerUser']) { $votedComments = Comment_Model_Comment::fetchVotedComments( P4Cms_User::fetchActive()->getId(), dirname($comment->getId()) ); $hasVoted = in_array($comment->getId(), $votedComments->invoke('getId')); if ($hasVoted) { throw new P4Cms_AccessDeniedException( "Only one vote is allowed per-user, per-comment." ); } } // up/down as appropriate. if ($up) { $comment->voteUp()->save('Vote up.'); } else { $comment->voteDown()->save('Vote down.'); } $this->view->comment = $comment; $this->view->options = $options; // redirect traditional requests. if (!$this->contextSwitch->getCurrentContext()) { $this->redirector->gotoUrl($request->getBaseUrl()); } } /** * Get comment options for the given path. Options for content paths * are pulled from the identified content entry (if one exists). * * @param string $path the comment path to get options for. * @return array normalized comment options array. */ protected function _getOptionsForPath($path) { $options = array(); // if this is a content path, we need to check the // content entry for comment settings. $matches = array(); if (preg_match('#content/(.+)#', $path, $matches)) { $id = $matches[1]; $entry = P4Cms_Content::fetch($id, array('includeDeleted' => true)); $options = $entry->getValue('comments'); } return Comment_Form_Content::getNormalizedOptions($options); } }