diff --git a/Voting/class.voting.plugin.php b/Voting/class.voting.plugin.php index a4bbce7..236452f 100644 --- a/Voting/class.voting.plugin.php +++ b/Voting/class.voting.plugin.php @@ -2,6 +2,12 @@ class VotingPlugin extends Gdn_Plugin { + /** + * Database updates. + */ + public function structure() { + include __DIR__.'/structure.php'; + } /** * Add JS & CSS to the page. */ @@ -15,10 +21,9 @@ public function addVotingBox($sender, $args) { $object = $args['Object']; $VoteType = $args['Type'] == 'Discussion' ? 'votediscussion' : 'votecomment'; $id = $args['Type'] == 'Discussion' ? val('DiscussionID', $object) : val('CommentID', $object); - $score = val('Score', $object); + $pScore = val('PScore', $object); + $nScore = val('NScore', $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; @@ -32,32 +37,11 @@ public function addVotingBox($sender, $args) { $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 ''; - 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 ' | '; + echo generateVoterBox($id,$args['Type'], $pScore, $nScore, $currentUserVote ).''; } - 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); @@ -79,7 +63,145 @@ public function discussionController_render_Before($sender) { $this->AddJsCss($sender); } + /** + * Sets the discussion score for specified user. + * + * @param int $discussionID Unique ID of discussion to update. + * @param int $userID Unique ID of user setting score. + * @param int $score New score for discussion. + * @return int Total score. + */ + public function discussionModel_setUserScores_create($sender) { + $discussionID = val(0, $sender->EventArguments); + $userID = val(1, $sender->EventArguments); + $score = val(2, $sender->EventArguments); + $prevScore = val(3, $sender->EventArguments); + + + // Insert or update the UserDiscussion row + $sender->SQL->replace( + 'UserDiscussion', + ['Score' => $score], + ['DiscussionID' => $discussionID, 'UserID' => $userID] + ); + + // Get the current total score + $totalScore = $sender->SQL->select('Score', 'sum', 'TotalScore') + ->select('NScore', 'sum', 'TotalNScore') + ->select('PScore', 'sum', 'TotalPScore') + ->from('Discussion') + ->where('DiscussionID', $discussionID) + ->get() + ->firstRow(); + + $pScore = 0; + $nScore = 0; + if($totalScore) { + $pScore = $totalScore->TotalPScore? $totalScore->TotalPScore : 0; + $nScore = $totalScore->TotalNScore? $totalScore->TotalNScore: 0; + } + if ($prevScore == null) { + $pScore = $score > 0? $pScore+1 : $pScore; + $nScore = $score < 0? $nScore+1 : $nScore; + $tScore = $pScore+$nScore; + } else { + if ($score == 0) { // cancelled a vote + $pScore = $prevScore > 0 ? $pScore - 1 : $pScore; + $nScore = $prevScore < 0 ? $nScore - 1 : $nScore; + $tScore = $pScore + $nScore; + } else { //change a vote + $pScore = $pScore + $score ; + $nScore = $nScore + (-1)*$score; + $tScore = $pScore + $nScore; + } + } + + // Update the Discussion's cached version + $sender->SQL->update('Discussion') + ->set('Score', $tScore) + ->set('PScore', $pScore ) + ->set('NScore', $nScore) + ->where('DiscussionID', $discussionID) + ->put(); + + $updatedTotalScores = $sender->SQL->select('Score', 'sum', 'TotalScore') + ->select('NScore', 'sum', 'TotalNScore') + ->select('PScore', 'sum', 'TotalPScore') + ->from('Discussion') + ->where('DiscussionID', $discussionID) + ->get() + ->firstRow(); + return $updatedTotalScores; + } + /** + * Upadte Comment Score value for the specified user and update Total Comment Scores + * + * @param int $commentID Unique ID of comment we're getting the score for. + * @param int $userID Unique ID of user who scored the comment. + */ + public function commentModel_setUserScores_create($sender) { + + $commentID = val(0, $sender->EventArguments); + $userID = val(1, $sender->EventArguments); + $score = val(2, $sender->EventArguments); + $prevScore = val(3, $sender->EventArguments); + + // Insert or update the UserComment row + $sender->SQL->replace( + 'UserComment', + ['Score' => $score], + ['CommentID' => $commentID, 'UserID' => $userID] + ); + + $totalScore = $sender->SQL->select('Score', 'sum', 'TotalScore') + ->select('NScore', 'sum', 'TotalNScore') + ->select('PScore', 'sum', 'TotalPScore') + ->from('Comment') + ->where('CommentID', $commentID) + ->get() + ->firstRow(); + + $pScore = 0; + $nScore = 0; + + if($totalScore) { + $pScore = $totalScore->TotalPScore? $totalScore->TotalPScore : 0; + $nScore = $totalScore->TotalNScore? $totalScore->TotalNScore: 0; + } + if ($prevScore == null) { + $pScore = $score > 0? $pScore+1 : $pScore; + $nScore = $score < 0? $nScore+1 : $nScore; + $tScore = $pScore+$nScore; + } else { + if ($score == 0) { // cancelled a vote + $pScore = $prevScore > 0 ? $pScore - 1 : $pScore; + $nScore = $prevScore < 0 ? $nScore - 1 : $nScore; + $tScore = $pScore + $nScore; + } else { //change a vote + $pScore = $pScore + $score ; + $nScore = $nScore + (-1)*$score; + $tScore = $pScore + $nScore; + } + } + + // Update the comment's cached version + $sender->SQL->update('Comment') + ->set('Score', $tScore) + ->set('PScore', $pScore ) + ->set('NScore', $nScore) + ->where('CommentID', $commentID) + ->put(); + + $updatedTotalScores = $sender->SQL->select('Score', 'sum', 'TotalScore') + ->select('NScore', 'sum', 'TotalNScore') + ->select('PScore', 'sum', 'TotalPScore') + ->from('Comment') + ->where('CommentID', $commentID) + ->get() + ->firstRow(); + return $updatedTotalScores; + } /** * Increment/decrement comment scores */ @@ -110,15 +232,16 @@ public function discussionController_VoteComment_create($sender) { } 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(); + + $Total = $CommentModel->SetUserScores($CommentID, $Session->UserID, $FinalVote, $OldUserVote); + $sender->DeliveryType(DELIVERY_TYPE_VIEW); + $voterBoxID = '#Voter_Comment_'.$CommentID; + $pScore = val('TotalPScore', $Total); + $nScore = val('TotalNScore', $Total); + $html = generateVoterBox($CommentID,'Comment', $pScore, $nScore, $FinalVote); + $sender->jsonTarget($voterBoxID, $html, 'ReplaceWith'); + $sender->render('Blank', 'Utility', 'Dashboard'); } /** @@ -126,15 +249,9 @@ public function discussionController_VoteComment_create($sender) { */ 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); - } + $VoteType = GetValue(1, $sender->RequestArgs); + $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); @@ -157,14 +274,15 @@ public function discussionController_VoteDiscussion_create($sender) { } else { $FinalVote = $NewUserVote; } - $Total = $DiscussionModel->SetUserScore($DiscussionID, $Session->UserID, $FinalVote); + $Total = $DiscussionModel->SetUserScores($DiscussionID, $Session->UserID, $FinalVote,$OldUserVote); + $sender->DeliveryType(DELIVERY_TYPE_VIEW); + $voterBoxID = '#Voter_Discussion_'.$DiscussionID; + $pScore = val('TotalPScore', $Total); + $nScore = val('TotalNScore', $Total); + $html = generateVoterBox($DiscussionID,'Discussion', $pScore, $nScore, $FinalVote); + $sender->jsonTarget($voterBoxID, $html, 'ReplaceWith'); + $sender->render('Blank', 'Utility', 'Dashboard'); } - $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(); } /** @@ -182,6 +300,7 @@ public function PostController_Render_Before($Sender) { } public function Setup() { + $this->structure(); } public function OnDisable() { @@ -198,4 +317,58 @@ public function dashboardNavModule_init_handler($sender) { 'voting.comments', '', $sort); } +} + +if (!function_exists('formattedNScore')) { + function formattedNScore($score) + { + if (StringIsNullOrEmpty($score)) { + $formattedScore = '-0'; + } else { + $formattedScore = '-' . Gdn_Format::BigNumber($score); + } + + return $formattedScore; + } +} + +if (!function_exists('formattedPScore')) { + function formattedPScore($score) + { + if (StringIsNullOrEmpty($score)) { + $formattedScore = '+0'; + } else { + $formattedScore = '+' . Gdn_Format::BigNumber($score); + } + + return $formattedScore; + } +} + +if (!function_exists('generateVoterBox')) { + function generateVoterBox($id, $VoteType, $pScore, $nScore, $currentUserVote) + { + $cssClassVoteUp = 'SpriteVoteUp'; + $cssClassVoteDown = 'SpriteVoteDown'; + if($currentUserVote > 0) { + $cssClassVoteUp = 'SpriteVoteUpActive'; + } else if($currentUserVote < 0){ + $cssClassVoteDown = 'SpriteVoteDownActive'; + } + + $voterBoxID = 'Voter_' . $VoteType . '_' . $id; + $voteUpUrl = '/discussion/vote' . strtolower($VoteType) . '/' . $id . '/voteup/' . Gdn::session()->TransientKey() . '/'; + $voteDownUrl = '/discussion/vote' . strtolower($VoteType) . '/' . $id . '/votedown/' . Gdn::session()->TransientKey() . '/'; + $result = ''; + $result .= Anchor(Wrap('', 'span', array('class' => 'icon ' . $cssClassVoteUp, 'rel' => 'nofollow')), $voteUpUrl, 'VoteUp'); + $counts = formattedPScore($pScore); + if(!StringIsNullOrEmpty($nScore) && $nScore != 0) { + $counts .= '/' . formattedNScore($nScore); + } + $result .= Wrap($counts, 'span', array('class' => 'CountVoices')); + $result .= Anchor(Wrap('', 'span', array('class' => 'icon ' . $cssClassVoteDown, 'rel' => 'nofollow')), $voteDownUrl, 'VoteDown'); + $result .= ''; + + return $result; + } } \ No newline at end of file diff --git a/Voting/controllers/class.votingcontroller.php b/Voting/controllers/class.votingcontroller.php index 00145e6..963137b 100644 --- a/Voting/controllers/class.votingcontroller.php +++ b/Voting/controllers/class.votingcontroller.php @@ -53,7 +53,7 @@ public function initialize() { * @param string $sort * @throws Exception */ - public function discussions($page = '', $sort = 'top') { + public function discussions($page = '', $sort = 'totalvotes') { $this->permission('Garden.Settings.Manage'); // Page setup @@ -66,19 +66,35 @@ public function discussions($page = '', $sort = 'top') { list($offset, $limit) = offsetLimit($page, PagerModule::$DefaultPageSize); $DiscussionModel = new DiscussionModel(); - $DiscussionModel->setSort($sort); + switch (strtolower($sort)) { + case 'totalvotes': + // $orderBy = ['d.Score'=> 'desc', 'd.PScore'=>' desc', 'd.NScore'=> 'desc']; + $orderBy = 'd.Score'; + break; + case 'votesup': + $orderBy = 'd.PScore'; + break; + case 'votesdown': + $orderBy = 'd.NScore'; + break; + case 'comments': + $orderBy = 'd.CountComments'; + break; + default: + $orderBy = 'd.Score'; + } + // TODO + // , 'd.Score is not null' => '' $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); + //$discussionData = $DiscussionModel->getWhereRecent($where, $limit, $offset); + $discussionData = $DiscussionModel->getWhere($where, $orderBy, 'desc', $limit, $offset); $this->setData('Discussions', $discussionData); // Deliver json data if necessary @@ -97,7 +113,7 @@ public function discussions($page = '', $sort = 'top') { * @param string $sort * @throws Exception */ - public function comments($page = '', $sort = 'top') { + public function comments($page = '', $sort = 'totalvotes') { $this->permission('Garden.Settings.Manage'); // Page setup @@ -112,22 +128,24 @@ public function comments($page = '', $sort = 'top') { $CommentModel = new CommentModel(); switch (strtolower($sort)) { - case 'top': - $CommentModel->OrderBy(array('c.Score desc', 'c.CommentID desc')); + case 'totalvotes': + $CommentModel->OrderBy(array('c.Score desc', 'c.PScore desc', 'c.NScore desc')); + break; + case 'votesup': + $CommentModel->OrderBy(array('c.PScore desc', 'c.Score desc', 'c.NScore desc')); + break; + case 'votesdown': + $CommentModel->OrderBy(array('c.NScore desc', 'c.Score desc', 'c.PScore desc')); break; default: - $CommentModel->OrderBy(array('c.Score desc', 'c.CommentID desc')); + $CommentModel->OrderBy(array('c.Score desc', 'c.PScore desc', 'c.NScore 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); diff --git a/Voting/design/images/thumb_down_active.svg b/Voting/design/images/thumb_down_active.svg new file mode 100644 index 0000000..46d9b0c --- /dev/null +++ b/Voting/design/images/thumb_down_active.svg @@ -0,0 +1,23 @@ + + + 70D1152A-93E6-426E-A7E5-4DB226B343FA + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Voting/design/images/thumb_down_inactive.png b/Voting/design/images/thumb_down_inactive.png new file mode 100644 index 0000000..c137fa6 Binary files /dev/null and b/Voting/design/images/thumb_down_inactive.png differ diff --git a/Voting/design/images/thump_up_active.svg b/Voting/design/images/thump_up_active.svg new file mode 100644 index 0000000..eedee0a --- /dev/null +++ b/Voting/design/images/thump_up_active.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Voting/design/images/thump_up_inactive.svg b/Voting/design/images/thump_up_inactive.svg new file mode 100644 index 0000000..e2827b2 --- /dev/null +++ b/Voting/design/images/thump_up_inactive.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Voting/design/voting.css b/Voting/design/voting.css index 624a3f2..79d0b48 100644 --- a/Voting/design/voting.css +++ b/Voting/design/voting.css @@ -7,4 +7,68 @@ } .Voted { color: #0AB88A; +} + +.SpriteVoteUp { + width: 17px; + height: 18px; + vertical-align: middle; + line-height: inherit; +} + +span.SpriteVoteUp::before { + content: ""; + width: 17px; + height: 18px; + background: url(./images/thump_up_inactive.svg); + background-size: 17px 18px; + background-position: center; + font-size: 0 !important; + background-repeat: no-repeat; + } + +span.SpriteVoteUpActive::before { + content: ""; + width: 17px; + height: 18px; + background: url(./images/thump_up_active.svg); + background-size: 17px 18px; + background-position: center; + font-size: 0 !important; + background-repeat: no-repeat; +} + + +span.SpriteVoteDown::before { + content: ""; + width: 17px; + height: 18px; + background: url(./images/thumb_down_inactive.png); + background-size: 17px 18px; + background-position: center; + font-size: 0 !important; + background-repeat: no-repeat; + top: 3px; + position: relative; +} +span.SpriteVoteDownActive::before { + content: ""; + width: 17px; + height: 18px; + background: url(./images/thumb_down_active.svg); + background-size: 17px 18px; + background-position: center; + font-size: 0 !important; + background-repeat: no-repeat; + top: 3px; + position: relative; +} +span.VoiceDivider { + padding-left: 1px; + padding-right: 1px; + +} +span.line{ + border-left: 1px solid #D9D9D9; + margin: 0px 15px } \ No newline at end of file diff --git a/Voting/js/voting.js b/Voting/js/voting.js index 35393e4..7ccf2c9 100644 --- a/Voting/js/voting.js +++ b/Voting/js/voting.js @@ -6,37 +6,19 @@ jQuery(document).ready(function($) { var btn = this; var parent = $(this).parents('.Voter'); var votes = $(parent).find('span.CountVoices'); - var voteUp = $(parent).find('.SpriteUp'); - var voteDown = $(parent).find('.SpriteDown'); $.ajax({ type: "POST", url: btn.href, - data: 'DeliveryType=BOOL&DeliveryMethod=JSON', + data: 'DeliveryType=VIEW&DeliveryMethod=JSON', dataType: 'json', - error: function(XMLHttpRequest, textStatus, errorThrown) { + error: function(xhr, textStatus, errorThrown) { gdn.informError(xhr); }, success: function(json) { - // Change the Vote count - $(votes).text(json.TotalScore); - $(voteUp).removeClass('Voted'); - $(voteDown).removeClass('Voted'); - $(voteUp).addClass(json.VoteUpCssClass); - $(voteDown).addClass(json.VoteDownCssClass); - gdn.inform(json); + gdn.processTargets(json.Targets); } }); return false; }); }); - -// Updates The Total Of Comments After A Comment Has Been Added -$(document).on('CommentAdded', function() { - $('.VotingSort strong').html($('.MessageList li.ItemComment').length+' Comment'+($('.MessageList li.ItemComment').length > 1 ? 's' : '')); -}); - -// Updates The Total Of Comments After A Comment Has Been Deleted -$(document).on('CommentDeleted', function() { - $('.VotingSort strong').html($('.MessageList li.ItemComment').length+' Comment'+($('.MessageList li.ItemComment').length > 1 ? 's' : '')); -}); \ No newline at end of file diff --git a/Voting/structure.php b/Voting/structure.php new file mode 100644 index 0000000..782dc68 --- /dev/null +++ b/Voting/structure.php @@ -0,0 +1,19 @@ +table('Discussion')->columnExists('PScore')) { + Gdn::structure()->table('Discussion') + ->column('PScore', 'float', null) + ->column('NScore', 'float', null) + ->set(false, false); +} + +// Add the column PScore/NScore in Comment : +if(!Gdn::structure()->table('Comment')->columnExists('PScore')) { + Gdn::structure()->table('Comment') + ->column('PScore', 'float', null) + ->column('NScore', 'float', null) + ->set(false, false); +} \ No newline at end of file diff --git a/Voting/views/voting/_comments.php b/Voting/views/voting/_comments.php index b66a073..dedfbf8 100644 --- a/Voting/views/voting/_comments.php +++ b/Voting/views/voting/_comments.php @@ -32,6 +32,8 @@ ?> Score ?> + PScore ?> + NScore ?> t('View Post'), 'target'=>'_blank', 'aria-label' => t('View Post'), 'class' => 'btn btn-icon btn-icon-sm']; diff --git a/Voting/views/voting/_discussions.php b/Voting/views/voting/_discussions.php index 2c63362..8536b0c 100644 --- a/Voting/views/voting/_discussions.php +++ b/Voting/views/voting/_discussions.php @@ -42,7 +42,8 @@ ?> Score ?> - CountViews ?> + PScore ?> + NScore ?> CountComments ?> - + _OrderCommentsUrl('totalvotes')); ?> + _OrderCommentsUrl('votesup')); ?> + _OrderCommentsUrl('votesdown')); ?> diff --git a/Voting/views/voting/discussions.php b/Voting/views/voting/discussions.php index 9769db4..e6450de 100644 --- a/Voting/views/voting/discussions.php +++ b/Voting/views/voting/discussions.php @@ -19,8 +19,9 @@ - _OrderDiscussionsUrl('top')); ?> - _OrderDiscussionsUrl('views')); ?> + _OrderDiscussionsUrl('totalvotes')); ?> + _OrderDiscussionsUrl('votesup')); ?> + _OrderDiscussionsUrl('votesdown')); ?> _OrderDiscussionsUrl('comments')); ?>