Skip to content

Issues-475: Voting plugin #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Voting/addon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"key": "Voting",
"name": "Voting",
"description": "This plugin allows users to vote for comments and discussions.",
"version": "1.0.0",
"documentationUrl": "http://discussions.topcoder.com",
"type": "addon",
"priority": "100",
"icon": "topcoder-logo.jpeg",
"mobileFriendly": true,
"hasLocale": false,
"authors": [
{
"name": "Topcoder",
"email": "support@topcoder.com",
"homepage": "https://topcoder.com"
}
],
"require": {
"vanilla": ">=3.0"
}
}
201 changes: 201 additions & 0 deletions Voting/class.voting.plugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
<?php if (!defined('APPLICATION')) exit();

class VotingPlugin extends Gdn_Plugin {

/**
* Add JS & CSS to the page.
*/
public function AddJsCss($Sender) {
$Sender->AddCSSFile('voting.css', 'plugins/Voting');
$Sender->AddJSFile('voting.js', 'plugins/Voting');
}

public function addVotingBox($sender, $args) {
$session = Gdn::Session();
$object = $args['Object'];
$VoteType = $args['Type'] == 'Discussion' ? 'votediscussion' : 'votecomment';
$id = $args['Type'] == 'Discussion' ? val('DiscussionID', $object) : val('CommentID', $object);
$score = val('Score', $object);
$cssClass = '';
$voteUpUrl = '/discussion/'.$VoteType.'/'.$id.'/voteup/'.$session->TransientKey().'/';
$voteDownUrl = '/discussion/'.$VoteType.'/'.$id.'/votedown/'.$session->TransientKey().'/';
if (!$session->IsValid()) {
$voteUpUrl = Gdn::Authenticator()->SignInUrl($sender->SelfUrl);
$voteDownUrl = $voteUpUrl;
$cssClass = ' SignInPopup';
}

if($args['Type'] == 'Discussion') {
$discussionModel = new DiscussionModel();
$currentUserVote = $discussionModel->GetUserScore($id, $session->UserID);
} else {
$commentModel = new CommentModel();
$currentUserVote = $commentModel->GetUserScore($id, $session->UserID);
}
$cssClassVoteUp = $cssClassVoteDown = '';
if($currentUserVote > 0) {
$cssClassVoteUp = ' Voted';
} else if($currentUserVote < 0){
$cssClassVoteDown = ' Voted';
}

$formattedScore = $this->formattedScore($score);
echo '<span class="Voter">';
echo Anchor(Wrap('Vote Up', 'span', array('class' => 'ArrowSprite SpriteUp'.$cssClassVoteUp , 'rel' => 'nofollow')), $voteUpUrl, 'VoteUp'.$cssClass);
echo Wrap($formattedScore, 'span', array('class' => 'CountVoices'));
echo Anchor(Wrap('Vote Down', 'span', array('class' => 'ArrowSprite SpriteDown'.$cssClassVoteDown, 'rel' => 'nofollow')), $voteDownUrl, 'VoteDown'.$cssClass);
echo '</span>&nbsp;|&nbsp;';

}

private function formattedScore($score) {
if(StringIsNullOrEmpty($score)) {
$formattedScore = '0';
} else {
$formattedScore = $score <= 0 ? Gdn_Format::BigNumber($score):'+' . Gdn_Format::BigNumber($score);
}

return $formattedScore;
}


public function discussionController_BeforeInlineDiscussionOptions_handler($sender, $args) {
$this->addVotingBox($sender, $args);
}

public function discussionController_BeforeInlineCommentOptions_handler($sender, $args) {
$this->addVotingBox($sender, $args);
}

public function postController_BeforeInlineCommentOptions_handler($sender, $args) {
$this->addVotingBox($sender, $args);
}


/**
* Add the files to discussions page
*/
public function discussionController_render_Before($sender) {
$this->AddJsCss($sender);
}


/**
* Increment/decrement comment scores
*/
public function discussionController_VoteComment_create($sender) {
$CommentID = GetValue(0, $sender->RequestArgs, 0);
$VoteType = GetValue(1, $sender->RequestArgs);
$TransientKey = GetValue(2, $sender->RequestArgs);
$Session = Gdn::Session();
$FinalVote = 0;
$Total = 0;
if ($Session->IsValid() && $Session->ValidateTransientKey($TransientKey) && $CommentID > 0) {
$CommentModel = new CommentModel();
$OldUserVote = $CommentModel->GetUserScore($CommentID, $Session->UserID);
switch ($VoteType) {
case 'voteup':
$NewUserVote = 1;
break;
case 'votedown':
$NewUserVote = -1;
break;
default:
$NewUserVote = 0;
}
$FinalVote = intval($OldUserVote) + intval($NewUserVote);
if ($FinalVote == 2 || $FinalVote == -2) {
// user cancelled a voice
$FinalVote = 0;
} else {
$FinalVote = $NewUserVote;
}

$Total = $CommentModel->SetUserScore($CommentID, $Session->UserID, $FinalVote);
}
$sender->DeliveryType(DELIVERY_TYPE_BOOL);
$sender->SetJson('TotalScore', $this->formattedScore($Total));
$sender->SetJson('FinalVote', $FinalVote);
$sender->SetJson('VoteUpCssClass', $FinalVote > 0? 'Voted':'');
$sender->SetJson('VoteDownCssClass', $FinalVote < 0? 'Voted':'');
$sender->Render();
}

/**
* Increment/decrement discussion scores
*/
public function discussionController_VoteDiscussion_create($sender) {
$DiscussionID = GetValue(0, $sender->RequestArgs, 0);
$TransientKey = GetValue(1, $sender->RequestArgs);
$VoteType = FALSE;
if ($TransientKey == 'voteup' || $TransientKey == 'votedown') {
$VoteType = $TransientKey;
$TransientKey = GetValue(2, $sender->RequestArgs);
}
$Session = Gdn::Session();
$NewUserVote = 0;
$Total = 0;
if ($Session->IsValid() && $Session->ValidateTransientKey($TransientKey) && $DiscussionID > 0) {
$DiscussionModel = new DiscussionModel();
$OldUserVote = $DiscussionModel->GetUserScore($DiscussionID, $Session->UserID);

switch ($VoteType) {
case 'voteup':
$NewUserVote = 1;
break;
case 'votedown':
$NewUserVote = -1;
break;
default:
$NewUserVote = 0;
}

$FinalVote = intval($OldUserVote) + intval($NewUserVote);
if ($FinalVote == 2 || $FinalVote == -2) {
// user cancelled a voice
$FinalVote = 0;
} else {
$FinalVote = $NewUserVote;
}
$Total = $DiscussionModel->SetUserScore($DiscussionID, $Session->UserID, $FinalVote);
}
$sender->DeliveryType(DELIVERY_TYPE_BOOL);
$sender->SetJson('TotalScore', $this->formattedScore($Total));
$sender->SetJson('FinalVote', $FinalVote);
$sender->SetJson('VoteUpCssClass', $FinalVote > 0? 'Voted':'');
$sender->SetJson('VoteDownCssClass', $FinalVote < 0? 'Voted':'');
$sender->Render();
}

/**
* Grab the score field whenever the discussions are queried.
*/
public function DiscussionModel_AfterDiscussionSummaryQuery_Handler($Sender) {
$Sender->SQL->Select('d.Score');
}

/**
* Add voting css to post controller.
*/
public function PostController_Render_Before($Sender) {
$this->AddJsCss($Sender);
}

public function Setup() {
}

public function OnDisable() {
}

public function dashboardNavModule_init_handler($sender) {
/** @var DashboardNavModule $nav */
$nav = $sender;
$sort = -1;
$nav->addGroupToSection('Moderation', t('Voting'), 'voting', '', ['after'=>'site'])
->addLinkToSectionIf('Garden.Settings.Manage', 'Moderation', t('Discussions'), '/voting/discussions',
'voting.discussions', '', $sort)
->addLinkToSectionIf('Garden.Settings.Manage', 'Moderation', t('Comments'), '/voting/comments',
'voting.comments', '', $sort);

}
}
167 changes: 167 additions & 0 deletions Voting/controllers/class.votingcontroller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<?php
/**
* View Voting Discussions/Comments
*
* @copyright 2009-2019 Vanilla Forums Inc.
* @license GPL-2.0-only
* @package Dashboard
* @since 2.0
*/

use Vanilla\Contracts\ConfigurationInterface;

/**
* Handles /voting endpoint.
*/
class VotingController extends DashboardController {

/** @var ConfigurationInterface */
private $config;

/** @var array Models to automatically instantiate. */
public $Uses = ['Database', 'Form'];

/** @var Gdn_Form */
public $Form;

public $discussionModel;

/**
* Configure the controller.
*/
public function __construct(ConfigurationInterface $config = null) {
$this->config = $config instanceof ConfigurationInterface ? $config : Gdn::getContainer()->get(ConfigurationInterface::class);
$this->discussionModel = Gdn::getContainer()->get(DiscussionModel::class);
parent::__construct();
}

/**
* Highlight menu path. Automatically run on every use.
*/
public function initialize() {
parent::initialize();
Gdn_Theme::section('Dashboard');
if ($this->Menu) {
$this->Menu->highlightRoute('/dashboard/settings');
}
$this->fireEvent('Init');
}

/**
* Discussion list.
* @param string $page Page number.
* @param string $sort
* @throws Exception
*/
public function discussions($page = '', $sort = 'top') {
$this->permission('Garden.Settings.Manage');

// Page setup
$this->addJsFile('jquery.gardenmorepager.js');
$this->title(t('Voting Discussions'));
$this->setHighlightRoute('voting/discussions');
Gdn_Theme::section('Moderation');

// Input Validation.
list($offset, $limit) = offsetLimit($page, PagerModule::$DefaultPageSize);

$DiscussionModel = new DiscussionModel();
$DiscussionModel->setSort($sort);

$where = ['Announce' => 'all', 'd.Score is not null' => ''];
// Get Discussion Count
$CountDiscussions = $DiscussionModel->getCount($where);

$this->setData('RecordCount', $CountDiscussions);
if ($offset >= $CountDiscussions) {
$offset = $CountDiscussions - $limit;
}

// Get Discussions and Announcements
$discussionData = $DiscussionModel->getWhereRecent($where, $limit, $offset);
$this->setData('Discussions', $discussionData);

// Deliver json data if necessary
if ($this->_DeliveryType != DELIVERY_TYPE_ALL && $this->_DeliveryMethod == DELIVERY_METHOD_XHTML) {
$this->setJson('LessRow', $this->Pager->toString('less'));
$this->setJson('MoreRow', $this->Pager->toString('more'));
$this->View = 'discussions';
}

$this->render();
}

/**
* Comment list.
* @param string $page Page number.
* @param string $sort
* @throws Exception
*/
public function comments($page = '', $sort = 'top') {
$this->permission('Garden.Settings.Manage');

// Page setup
$this->addJsFile('jquery.gardenmorepager.js');
$this->title(t('Voting Comments'));
$this->setHighlightRoute('voting/comments');
Gdn_Theme::section('Moderation');

// Input Validation.
list($offset, $limit) = offsetLimit($page, PagerModule::$DefaultPageSize);

$CommentModel = new CommentModel();

switch (strtolower($sort)) {
case 'top':
$CommentModel->OrderBy(array('c.Score desc', 'c.CommentID desc'));
break;
default:
$CommentModel->OrderBy(array('c.Score desc', 'c.CommentID desc'));
break;
}

$where = ['Score is not null' => ''];
// Get Comment Count
$CountComments = $CommentModel->getCount($where);

$this->setData('RecordCount', $CountComments);
if ($offset >= $CountComments) {
$offset = $CountComments - $limit;
}

$data = $CommentModel->getWhere($where,'', '' , $limit, $offset);
$this->setData('Comments', $data);

// Deliver json data if necessary
if ($this->_DeliveryType != DELIVERY_TYPE_ALL && $this->_DeliveryMethod == DELIVERY_METHOD_XHTML) {
$this->setJson('LessRow', $this->Pager->toString('less'));
$this->setJson('MoreRow', $this->Pager->toString('more'));
$this->View = 'discussions';
}

$this->render();
}


/**
* Build URL to order users by value passed.
*/
protected function _OrderDiscussionsUrl($field) {
$get = Gdn::request()->get();
$get['sort'] = $field;
$get['Page'] = 'p1';
return '/voting/discussions?'.http_build_query($get);
}

/**
* Build URL to order users by value passed.
*/
protected function _OrderCommentsUrl($field) {
$get = Gdn::request()->get();
$get['sort'] = $field;
$get['Page'] = 'p1';
return '/voting/comments?'.http_build_query($get);
}


}
Loading