Added selectable CSS Icons, point calculations, and cleaned everything up.

This commit is contained in:
Zachary Doll 2013-10-28 15:03:05 -05:00
parent 6c909525c1
commit 5126183483
15 changed files with 353 additions and 52 deletions

View File

@ -37,7 +37,7 @@ class ActionsController extends DashboardController {
$this->Menu->HighlightRoute('/actions');
}
$this->AddJsFile('actions.js');
$this->AddCssFile('actions.css');
$this->AddCssFile('reactions.css');
}
public function Settings($Page = '') {
@ -56,10 +56,12 @@ class ActionsController extends DashboardController {
$this->Permission('Yaga.Reactions.Manage');
$this->AddSideMenu('actions/settings');
$this->Form->SetModel($this->ActionModel);
$Edit = FALSE;
if($ActionID) {
$this->Action = $this->ActionModel->GetAction($ActionID);
$this->Form->AddHidden('ActionID', $ActionID);
$Edit = TRUE;
}
if($this->Form->IsPostBack() == FALSE) {
@ -67,16 +69,28 @@ class ActionsController extends DashboardController {
}
else {
if($this->Form->Save()) {
$Action = $this->ActionModel->GetAction($this->Form->GetValue('ActionID'));
if($Edit) {
$Action = $this->ActionModel->GetAction($this->Form->GetFormValue('ActionID'));
}
else {
$Action = $this->ActionModel->GetNewestAction();
}
$NewActionRow = '<tr id="ActionID_' . $Action->ActionID . '" data-actionid="'. $Action->ActionID . '">';
$NewActionRow .= "<td>$Action->Name</td>";
$NewActionRow .= '<td><span class="ReactSprite ' . $Action->CssClass . '"> </span></td>';
$NewActionRow .= "<td>$Action->Description</td>";
$NewActionRow .= "<td>$Action->Tooltip</td>";
$NewActionRow .= "<td>$Action->AwardValue</td>";
$NewActionRow .= '<td>' . Anchor(T('Edit'), 'yaga/actions/edit/' . $Action->ActionID, array('class' => 'Popup SmallButton')) . Anchor(T('Delete'), 'yaga/actions/delete/' . $Action->ActionID, array('class' => 'Hijack SmallButton')) . '</td>';
$NewActionRow .= '</tr>';
$this->JsonTarget('#ActionID_' . $this->Action->ActionID, $NewActionRow, 'ReplaceWith');
$this->InformMessage('Action updated successfully!');
if($Edit) {
$this->JsonTarget('#ActionID_' . $this->Action->ActionID, $NewActionRow, 'ReplaceWith');
$this->InformMessage('Action updated successfully!');
}
else {
$this->JsonTarget('#Actions tbody', $NewActionRow, 'Append');
$this->InformMessage('Action added successfully!');
}
}
}

View File

@ -140,7 +140,7 @@ class ReactController extends YagaController {
$ActionsString = '';
foreach($Reactions as $Action) {
$ActionsString .= Anchor(
Wrap('&nbsp;', 'span', array('class' => 'ReactSprite React-' . $Action->ActionID)) .
Wrap('&nbsp;', 'span', array('class' => 'ReactSprite React-' . $Action->ActionID . ' ' . $Action->CssClass)) .
WrapIf(count($Action->UserIDs), 'span', array('class' => 'Count')) .
Wrap($Action->Name, 'span', array('class' => 'ReactLabel')), 'react/' . $Type . '/' . $ID . '/' . $Action->ActionID,
'Hijack ReactButton'

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,3 +1,7 @@
#icon-preview {
margin: auto 20px;
}
.Reactions {
position: relative;
font-size: 11px;
@ -30,7 +34,7 @@
}
.ReactButton {
display: inline-block;
margin: 0 2px;
margin: 0 5px;
line-height: 18px;
vertical-align: top;
position: relative;
@ -404,3 +408,60 @@ only screen and (max-device-width: 480px) {
-o-transition-property: left, right, top;
transition-property: left, right, top;
}
/* Imported from style.css for backward compatibility */
.ReactSprite {
display: inline-block;
width: 16px;
height: 16px;
margin-right: 4px;
line-height: 18px;
vertical-align: bottom;
background-image: url('images/action-sprites.png');
background-position: 16px 16px;
background-repeat: no-repeat;
}
input + .ReactSprite {
vertical-align: middle;
}
.ReactEverything { background-position: -197px -53px; }
.ReactAwesome { background-position: -269px -6px; }
.ReactFlag { background-position: -54px -52px; }
.ReactInsightful { background-position: -149px -6px; }
.ReactOffTopic { background-position: -173px -6px; }
.ReactAbuse { background-position: -6px -52px; }
.ReactSpam { background-position: -125px -6px; }
.ReactTroll { background-position: -101px -6px; }
.ReactPromote { background-position: -50px -29px; }
.ReactDisagree { background-position: -28px -5px; }
.ReactAgree { background-position: -4px -5px; }
.ReactDislike { background-position: -28px -28px; }
.ReactLike { background-position: -4px -31px; }
.ReactDown { background-position: -220px -29px; }
.ReactUp { background-position: -195px -30px; }
.ReactWTF { background-position: -76px -5px; }
.ReactLOL { background-position: -52px -5px; }
.ReactQuote { background-position: -316px -6px; }
.ReactInfraction { background-position: -100px -53px; }
.ReactAccept { background-position: -6px -102px; }
.ReactReject { background-position: -342px -6px; }
.ReactFacebook { background-position: -29px -124px; }
.ReactTwitter { background-position: -53px -124px; }
.ReactGooglePlus { background-position: -77px -124px; }
.ReactMessage { background-position: -101px -102px; }
.ReactWarn { background-position: -222px -6px; }
/* Switch to the white icons when hovering over a menuitem sprite */
.MenuItems a:hover .ReactSprite,
.MenuItems a:hover .Sprite {
background-image: url('images/action-sprites-white.png');
}
/* Switch to bordered icons for user reaction overlays */
.UserReactionWrap .ReactSprite {
background-image: url('images/action-sprites-bordered.png');
}
.ActionBlock {
margin: 15px 0 5px;
font-style: italic;
}

6
js/actions.js Normal file
View File

@ -0,0 +1,6 @@
/* Copyright 2013 Zachary Doll */
jQuery(document).ready(function($) {
$(document).on('change', 'select', function(){
$('#icon-preview').removeClass().addClass($('select').val() + ' ReactSprite');
});
});

View File

@ -49,6 +49,16 @@ class ActionModel extends Gdn_Model {
->FirstRow();
return $Action;
}
public function GetNewestAction() {
$Action = $this->SQL
->Select()
->From('Action')
->OrderBy('ActionID', 'desc')
->Get()
->FirstRow();
return $Action;
}
public function ActionExists($ActionID) {
return !empty($this->GetAction($ActionID));

View File

@ -64,6 +64,7 @@ class ReactionModel extends Gdn_Model {
$ReactionSet[$Index]->Name = $Action->Name;
$ReactionSet[$Index]->Description = $Action->Description;
$ReactionSet[$Index]->Tooltip = $Action->Tooltip;
$ReactionSet[$Index]->CssClass = $Action->CssClass;
$ReactionSet[$Index]->AwardValue = $Action->AwardValue;
$Reactions = $this->SQL

169
models/class.yagamodel.php Normal file
View File

@ -0,0 +1,169 @@
<?php if (!defined('APPLICATION')) exit();
/* Copyright 2013 Zachary Doll */
/**
* Cross table functions
*
* Events:
*
* @package Yaga
* @since 1.0
*/
class YagaModel extends Gdn_Model {
private static $_Reactions = array();
private static $_Actions = NULL;
/**
* Class constructor. Defines the related database table name.
*/
public function __construct() {
parent::__construct();
}
public function GetUserPoints($UserID) {
// TODO: Add in other sources other than reactions
$ActionPoints = $this->SQL
->Select('a.ActionID, a.Name, a.AwardValue, COUNT(*) Count')
->From('Action a')
->Join('Reaction r', 'a.ActionID = r.ActionID')
->Where('ParentAuthorID', $UserID)
->GroupBy('a.ActionID')
->Get()
->Result();
$Points = 0;
foreach($ActionPoints as $Action) {
$Points += $Action->AwardValue * $Action->Count;
}
return $Points;
}
/**
* Returns a list of all available actions
*/
public function GetActions() {
if(empty(self::$_Actions)) {
self::$_Actions = $this->SQL
->Select()
->From('Action')
->OrderBy('ActionID')
->Get()
->Result();
//decho('Filling the action cache.');
}
return self::$_Actions;
}
/**
* Returns data for a specific action
* @param int $ActionID
* @return dataset
*/
public function GetActionID($ActionID) {
return $this->SQL
->Select()
->From('Action')
->Where('ActionID', $ActionID)
->Get()
->FirstRow();
}
/**
* Returns the reactions associated a specified ID.
* @param int $ID
* @param enum $Type is the kind of ID. Valid values are comment and discussion
*/
public function GetReactions($ID, $Type) {
if(in_array($Type, array('discussion', 'comment', 'activity')) && $ID > 0) {
$ReactionSet = array();
if(empty(self::$_Reactions[$Type . $ID])) {
foreach($this->GetActions() as $Index => $Action) {
$ReactionSet[$Index]->ActionID = $Action->ActionID;
$ReactionSet[$Index]->Name = $Action->Name;
$ReactionSet[$Index]->Description = $Action->Description;
$ReactionSet[$Index]->Tooltip = $Action->Tooltip;
$ReactionSet[$Index]->CssClass = $Action->CssClass;
$ReactionSet[$Index]->AwardValue = $Action->AwardValue;
$Reactions = $this->SQL
->Select('InsertUserID as UserID, DateInserted')
->From('Reaction')
->Where('ActionID', $Action->ActionID)
->Where('ParentID', $ID)
->Where('ParentType', $Type)
->Get()
->Result();
//decho($Reaction);
foreach($Reactions as $Reaction) {
$ReactionSet[$Index]->UserIDs[] = $Reaction->UserID;
$ReactionSet[$Index]->Dates[] = $Reaction->DateInserted;
}
}
//decho('Filling in the reaction cache for ' . $Type . $ID);
self::$_Reactions[$Type . $ID] = $ReactionSet;
}
return self::$_Reactions[$Type . $ID];
}
else {
return NULL;
}
}
public function GetUserReaction($ID, $Type, $UserID) {
return $this->SQL
->Select()
->From('Reaction')
->Where('ParentID', $ID)
->Where('ParentType', $Type)
->Where('InsertUserID', $UserID)
->Get()
->FirstRow();
}
public function GetUserReactionCount($UserID, $ActionID) {
return $this->SQL
->Select()
->From('Reaction')
->Where('ActionID', $ActionID)
->Where('ParentAuthorID', $UserID)
->GetCount();
}
public function SetReaction($ID, $Type, $AuthorID, $UserID, $ActionID) {
// clear the cache
unset(self::$_Reactions[$Type . $ID]);
$CurrentReaction = $this->GetUserReaction($ID, $Type, $UserID);
if($CurrentReaction) {
if($ActionID == $CurrentReaction->ActionID) {
// remove the record
return $this->SQL->Delete('Reaction', array('ParentID' => $ID,
'ParentType' => $Type,
'InsertUserID' => $UserID,
'ActionID' => $ActionID));
}
else {
// update the record
return $this->SQL
->Update('Reaction')
->Set('ActionID', $ActionID)
->Set('DateInserted', date(DATE_ISO8601))
->Where('ParentID', $ID)
->Where('ParentType', $Type)
->Where('InsertUserID', $UserID)
->Put();
}
}
else {
// insert a record
return $this->SQL
->Insert('Reaction',
array('ActionID' => $ActionID,
'ParentID' => $ID,
'ParentType' => $Type,
'ParentAuthorID' => $AuthorID,
'InsertUserID' => $UserID,
'DateInserted' => date(DATE_ISO8601)));
}
}
}

View File

@ -14,7 +14,8 @@ class YagaHooks implements Gdn_IPlugin {
* @param type $Sender
*/
public function UserInfoModule_OnBasicInfo_Handler($Sender) {
$Points = 0;
$Model = new YagaModel();
$Points = $Model->GetUserPoints($Sender->User->UserID);
echo Wrap(T('Yaga.Points', 'Points'), 'dt') . ' ' . Wrap($Points, 'dd');
}
@ -32,9 +33,7 @@ class YagaHooks implements Gdn_IPlugin {
$Actions = $this->_ReactionModel->GetActions();
$String = '';
foreach($Actions as $Action) {
//decho($Action);
$Count = $this->_ReactionModel->GetUserReactionCount($User->UserID, $Action->ActionID);
//decho($Count);
$TempString = Wrap(Wrap(Gdn_Format::BigNumber($Count), 'span', array('title' => $Count)), 'span', array('class' => 'Yarbs_ReactionCount CountTotal'));
$TempString .= Wrap($Action->Name, 'span', array('class' => 'Yarbs_ReactionName CountLabel'));
@ -80,13 +79,12 @@ class YagaHooks implements Gdn_IPlugin {
}
$Reactions = $this->_ReactionModel->GetReactions($ID, $Type);
foreach($Reactions as $Reaction) {
if($Reaction->UserIDs) {
foreach($Reaction->UserIDs as $Index => $UserID) {
$User = Gdn::UserModel()->GetID($UserID);
$String = UserPhoto($User, array('Size' => 'Small'));
$String .= '<span class="ReactSprite ReactLol Reaction-' . $Reaction->ActionID . '"></span>';
$String .= '<span class="ReactSprite Reaction-' . $Reaction->ActionID . ' ' . $Reaction->CssClass . '"></span>';
$Wrapttributes = array(
'class' => 'UserReactionWrap',
'data-userid' => $User->UserID,
@ -174,7 +172,7 @@ class YagaHooks implements Gdn_IPlugin {
$ActionsString = '';
foreach($Reactions as $Action) {
$ActionsString .= Anchor(
Wrap('&nbsp;', 'span', array('class' => 'ReactSprite React-' . $Action->ActionID)) .
Wrap('&nbsp;', 'span', array('class' => 'ReactSprite React-' . $Action->ActionID . ' ' . $Action->CssClass)) .
WrapIf(count($Action->UserIDs), 'span', array('class' => 'Count')) .
Wrap($Action->Name, 'span', array('class' => 'ReactLabel')), 'react/' . $Type . '/' . $ID . '/' . $Action->ActionID, 'Hijack ReactButton'
);

View File

@ -29,6 +29,7 @@ $Construct->Table('Action')
->Column('Name', 'varchar(140)')
->Column('Description', 'varchar(255)')
->Column('Tooltip', 'varchar(255)')
->Column('CssClass', 'varchar(255)')
->Column('AwardValue', 'int', 1)
->Set($Explicit, $Drop);

View File

@ -1,22 +1,24 @@
<?php if (!defined('APPLICATION')) exit();
<?php if(!defined('APPLICATION')) exit();
/* Copyright 2013 Zachary Doll */
// Only do this once, ever.
if (!$Drop)
return;
if(!$Drop)
return;
$SQL = Gdn::Database()->SQL();
// Insert stub actions
$SQL->Insert('Action', array(
'Name' => 'Thumbs Up',
'Description' => 'I approve!',
'Tooltip' => 'This indicates casual approval',
'AwardValue' => 1
'Name' => 'Thumbs Up',
'Description' => 'I approve!',
'Tooltip' => 'This indicates casual approval',
'CssClass' => '.ReactLike',
'AwardValue' => 1
));
$SQL->Insert('Action', array(
'Name' => 'Thumbs Down',
'Description' => 'I disapprove!',
'Tooltip' => 'This indicates casual disapproval',
'AwardValue' => -1
'Name' => 'Thumbs Down',
'Description' => 'I disapprove!',
'Tooltip' => 'This indicates casual disapproval',
'CssClass' => '.ReactDislike',
'AwardValue' => -1
));

View File

@ -1,6 +1,6 @@
<?php if (!defined('APPLICATION')) exit();
<?php if(!defined('APPLICATION')) exit();
/* Copyright 2013 Zachary Doll */
if (is_object($this->Action)) {
if(is_object($this->Action)) {
echo Wrap(T('Edit Action'), 'h1');
}
else {
@ -11,29 +11,66 @@ echo $this->Form->Open(array('class' => 'Action'));
echo $this->Form->Errors();
?>
<ul>
<li>
<?php
echo $this->Form->Label('Username', 'Name');
echo $this->Form->TextBox('Name');
?>
</li>
<li>
<?php
echo $this->Form->Label('Description', 'Description');
echo $this->Form->TextBox('Description');
?>
</li>
<li>
<?php
echo $this->Form->Label('Tooltip', 'Tooltip');
echo $this->Form->TextBox('Tooltip');
?>
</li>
<li>
<?php
echo $this->Form->Label('Award Value', 'AwardValue');
echo $this->Form->TextBox('AwardValue');
?>
</li>
<li>
<?php
echo $this->Form->Label('Name', 'Name');
echo $this->Form->TextBox('Name');
?>
</li>
<li>
<?php
// TODO: Bundle and icon spritesheet for 2.0
echo $this->Form->Label('CSS Icon', 'CssClass');
echo $this->Form->Dropdown('CssClass', array(
'' => 'None',
'ReactEverything' => 'Gear',
'ReactAwesome' => 'Heart',
'ReactFlag' => 'Flag',
'ReactInsightful' => 'Eye',
'ReactOffTopic' => 'Slashed Eye',
'ReactAbuse' => 'Bomb',
'ReactSpam' => 'Nuclear Symbol',
'ReactTroll' => 'Masked Smiley',
'ReactPromote' => 'Pointer Up',
'ReactDisagree' => 'Frowning Smiley',
'ReactAgree' => 'Smiley',
'ReactDislike' => 'Thumb Down',
'ReactLike' => 'Thumb Up',
'ReactDown' => 'Arrow Down',
'ReactUp' => 'Arrow Up',
'ReactWTF' => 'Surprised Smiley',
'ReactLOL' => 'Winking Smiley',
'ReactQuote' => 'Double Quote',
'ReactInfraction' => 'Flame',
'ReactAccept' => 'Checkmark',
'ReactReject' => 'Do Not Enter',
'ReactFacebook' => 'Facebook',
'ReactTwitter' => 'Twitter',
'ReactGooglePlus' => 'Google Plus',
'ReactMessage' => 'Envelope',
'ReactWarn' => 'Angry Triangle'
));
echo Wrap('&nbsp;', 'span', array('class' => 'ReactSprite ' . $this->Form->GetValue('CssClass'), 'id' => 'icon-preview'));
?>
</li>
<li>
<?php
echo $this->Form->Label('Description', 'Description');
echo $this->Form->TextBox('Description');
?>
</li>
<li>
<?php
echo $this->Form->Label('Tooltip', 'Tooltip');
echo $this->Form->TextBox('Tooltip');
?>
</li>
<li>
<?php
echo $this->Form->Label('Award Value', 'AwardValue');
echo $this->Form->TextBox('AwardValue');
?>
</li>
</ul>
<?php echo $this->Form->Close('Save');
<?php
echo $this->Form->Close('Save');

View File

@ -10,6 +10,7 @@ echo Wrap(Anchor('Add Action', 'yaga/actions/add', array('class' => 'Popup Small
<thead>
<tr>
<th>Name</th>
<th>Icon</th>
<th>Description</th>
<th>Tooltip</th>
<th>Award Value</th>
@ -22,6 +23,7 @@ echo Wrap(Anchor('Add Action', 'yaga/actions/add', array('class' => 'Popup Small
foreach($this->Data('Actions') as $Action) {
echo '<tr id="ActionID_' . $Action->ActionID . '" data-actionid="'. $Action->ActionID . '"' . ($Alt ? ' class="Alt"' : '') . '>';
echo "<td>$Action->Name</td>";
echo '<td><span class="ReactSprite ' . $Action->CssClass . '"> </span></td>';
echo "<td>$Action->Description</td>";
echo "<td>$Action->Tooltip</td>";
echo "<td>$Action->AwardValue</td>";