<classname><![CDATA[wcf\system\dashboard\box\MostActiveMembersDashboardBox]]></classname>
<boxtype>sidebar</boxtype>
</dashboardbox>
+
+ <dashboardbox name="com.woltlab.wcf.like.mostLikedMembers">
+ <classname><![CDATA[wcf\system\dashboard\box\MostLikedMembersDashboardBox]]></classname>
+ <boxtype>sidebar</boxtype>
+ </dashboardbox>
</import>
</data>
<classname>wcf\system\moderation\queue\ModerationQueueReportManager</classname>
</type>
<!-- /moderation type -->
+
+ <!-- activity points -->
+ <type>
+ <name>com.woltlab.wcf.like.activityPointEvent.receivedLikes</name>
+ <definitionname>com.woltlab.wcf.user.activityPointEvent</definitionname>
+ <points>1</points>
+ <classname>wcf\system\user\activity\point\ReceivedLikesUserActivityPointObjectProcessor</classname>
+ </type>
+ <!-- /activity points -->
</import>
</data>
\ No newline at end of file
<name>com.woltlab.wcf.moderation.report</name>
<interfacename>wcf\system\moderation\queue\report\IModerationQueueReportHandler</interfacename>
</definition>
+
+ <definition>
+ <name>com.woltlab.wcf.like.likeableObject</name>
+ <interfacename>wcf\data\like\ILikeObjectTypeProvider</interfacename>
+ </definition>
</import>
</data>
<category name="message.general.share">
<parent>message.general</parent>
</category>
+ <category name="message.general.likes">
+ <parent>message.general</parent>
+ </category>
<category name="message.attachment">
<parent>message</parent>
<defaultvalue>1</defaultvalue>
</option>
+ <option name="module_like">
+ <categoryname>module.community</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>1</defaultvalue>
+ </option>
+
<!-- general.page -->
<option name="page_title">
<categoryname>general.page</categoryname>
<defaultvalue><![CDATA[username]]></defaultvalue>
<selectoptions><![CDATA[username:wcf.user.username
registrationDate:wcf.user.registrationDate
-activityPoints:wcf.user.activityPoint]]></selectoptions>
+activityPoints:wcf.user.activityPoint
+likedReceived:wcf.like.likesReceived]]></selectoptions>
</option>
<option name="members_list_default_sort_order">
<categoryname>user.list.members</categoryname>
<defaultvalue>5</defaultvalue>
</option>
<!-- /dashboard -->
+
+ <option name="like_allow_for_own_content">
+ <categoryname>message.general.likes</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>1</defaultvalue>
+ <options>module_like</options>
+ </option>
+
+ <option name="like_enable_dislike">
+ <categoryname>message.general.likes</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>1</defaultvalue>
+ <options>module_like</options>
+ </option>
+
+ <option name="like_show_summary">
+ <categoryname>message.general.likes</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>1</defaultvalue>
+ <options>module_like</options>
+ </option>
</options>
</import>
</data>
--- /dev/null
+<ul class="sidebarBoxList">
+ {foreach from=$mostLikedMembers item=likedMember}
+ <li class="box24">
+ <a href="{link controller='User' object=$likedMember}{/link}" class="framed">{@$likedMember->getAvatar()->getImageTag(24)}</a>
+
+ <div class="sidebarBoxHeadline">
+ <h3><a href="{link controller='User' object=$likedMember}{/link}" class="userLink" data-user-id="{@$likedMember->userID}">{$likedMember->username}</a></h3>
+ <small>{lang}wcf.dashboard.box.mostLikedMembers.likes{/lang}</small>
+ </div>
+ </li>
+ {/foreach}
+</ul>
</script>
<script type="text/javascript" src="{@$__wcf->getPath()}js/WCF.Message{if !ENABLE_DEBUG_MODE}.min{/if}.js"></script>
<script type="text/javascript" src="{@$__wcf->getPath()}js/WCF.User{if !ENABLE_DEBUG_MODE}.min{/if}.js"></script>
+<script type="text/javascript" src="{@$__wcf->getPath('wcf')}js/WCF.Like{if !ENABLE_DEBUG_MODE}.min{/if}.js"></script>
{event name='javascriptInclude'}
<!-- Stylesheets -->
'wcf.global.thousandsSeparator': '{capture assign=thousandsSeparator}{lang}wcf.global.thousandsSeparator{/lang}{/capture}{@$thousandsSeparator|encodeJS}',
'wcf.page.sitemap': '{lang}wcf.page.sitemap{/lang}',
'wcf.style.changeStyle': '{lang}wcf.style.changeStyle{/lang}'
+ {if MODULE_LIKE}
+ ,'wcf.like.button.like': '{lang}wcf.like.button.like{/lang}',
+ 'wcf.like.button.dislike': '{lang}wcf.like.button.dislike{/lang}',
+ 'wcf.like.tooltip': '{lang}wcf.like.jsTooltip{/lang}',
+ 'wcf.like.summary': '{lang}wcf.like.summary{/lang}',
+ 'wcf.like.details': '{lang}wcf.like.details{/lang}'
+ {/if}
+
{event name='javascriptLanguageImport'}
});
<option value="username"{if $sortField == 'username'} selected="selected"{/if}>{lang}wcf.user.username{/lang}</option>
<option value="registrationDate"{if $sortField == 'registrationDate'} selected="selected"{/if}>{lang}wcf.user.registrationDate{/lang}</option>
<option value="activityPoints"{if $sortField == 'activityPoints'} selected="selected"{/if}>{lang}wcf.user.activityPoint{/lang}</option>
+ <option value="likesReceived"{if $sortField == 'likesReceived'} selected="selected"{/if}>{lang}wcf.like.likesReceived{/lang}</option>
{event name='sortField'}
</select>
<select name="sortOrder">
<dl class="plain inlineDataList userStats">
{event name='statistics'}
+ {if MODULE_LIKE}
+ <dt>{lang}wcf.like.likesReceived{/lang}</dt>
+ <dd>{#$user->likesReceived}</dd>
+ {/if}
+
<dt>{lang}wcf.user.activityPoint{/lang}</dt>
<dd>{#$user->activityPoints}</dd>
</dl>
\ No newline at end of file
<dl class="plain statsDataList">
{event name='statistics'}
+ {if MODULE_LIKE}
+ <dt>{lang}wcf.like.likesReceived{/lang}</dt>
+ <dd>{#$user->likesReceived}</dd>
+ {/if}
+
<dt>{if $user->activityPoints}<a class="activityPointsDisplay jsTooltip" title="{lang}wcf.user.activityPoint.showDetails{/lang}" data-user-id="{@$user->userID}">{lang}wcf.user.activityPoint{/lang}</a>{else}{lang}wcf.user.activityPoint{/lang}{/if}</dt>
<dd>{#$user->activityPoints}</dd>
<category name="user.message">
<parent>user</parent>
</category>
+ <category name="user.like">
+ <parent>user.message</parent>
+ </category>
<category name="user.message.attachment">
<parent>user.message</parent>
</category>
<defaultvalue>0</defaultvalue>
<admindefaultvalue>1</admindefaultvalue>
</option>
+
+ <option name="user.like.canViewLike">
+ <categoryname>user.like</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>1</defaultvalue>
+ </option>
+ <option name="user.like.canLike">
+ <categoryname>user.like</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>1</defaultvalue>
+ </option>
</options>
</import>
</data>
--- /dev/null
+/**
+ * Like support for WCF
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ */
+WCF.Like = Class.extend({
+ /**
+ * true, if users can like their own content
+ * @var boolean
+ */
+ _allowForOwnContent: false,
+
+ /**
+ * user can like
+ * @var boolean
+ */
+ _canLike: false,
+
+ /**
+ * list of containers
+ * @var object
+ */
+ _containers: { },
+
+ /**
+ * container meta data
+ * @var object
+ */
+ _containerData: { },
+
+ /**
+ * enables the dislike option
+ */
+ _enableDislikes: true,
+
+ /**
+ * prevents like/dislike until the server responded
+ * @var boolean
+ */
+ _isBusy: false,
+
+ /**
+ * cached template for like details
+ * @var object
+ */
+ _likeDetails: { },
+
+ /**
+ * dialog overlay for like details
+ * @var jQuery
+ */
+ _likeDetailsDialog: null,
+
+ /**
+ * proxy object
+ * @var WCF.Action.Proxy
+ */
+ _proxy: null,
+
+ /**
+ * shows the detailed summary of users who liked the object
+ */
+ _showSummary: true,
+
+ /**
+ * Initializes like support.
+ *
+ * @param boolean canLike
+ * @param boolean enableDislikes
+ * @param boolean showSummary
+ * @param boolean allowForOwnContent
+ */
+ init: function(canLike, enableDislikes, showSummary, allowForOwnContent) {
+ this._canLike = canLike;
+ this._enableDislikes = enableDislikes;
+ this._isBusy = false;
+ this._likeDetails = { };
+ this._likeDetailsDialog = null;
+ this._showSummary = showSummary;
+ this._allowForOwnContent = allowForOwnContent;
+
+ var $containers = this._getContainers();
+ this._initContainers($containers);
+
+ this._proxy = new WCF.Action.Proxy({
+ success: $.proxy(this._success, this)
+ });
+
+ // bind dom node inserted listener
+ var $date = new Date();
+ var $identifier = $date.toString().hashCode + $date.getUTCMilliseconds();
+ WCF.DOMNodeInsertedHandler.addCallback('WCF.Like' + $identifier, $.proxy(this._domNodeInserted, this));
+ },
+
+ /**
+ * Initialize containers once new nodes are inserted.
+ */
+ _domNodeInserted: function() {
+ var $containers = this._getContainers();
+ this._initContainers($containers);
+
+ },
+
+ /**
+ * Initializes like containers.
+ *
+ * @param object containers
+ */
+ _initContainers: function(containers) {
+ var $createdWidgets = false;
+ containers.each($.proxy(function(index, container) {
+ // set container
+ var $container = $(container);
+ var $containerID = $container.wcfIdentify();
+
+ if (!this._containers[$containerID]) {
+ this._containers[$containerID] = $container;
+
+ // set container data
+ this._containerData[$containerID] = {
+ 'likeButton': null,
+ 'badge': null,
+ 'dislikeButton': null,
+ 'likes': $container.data('like-likes'),
+ 'dislikes': $container.data('like-dislikes'),
+ 'objectType': $container.data('objectType'),
+ 'objectID': this._getObjectID($containerID),
+ 'users': eval($container.data('like-users')),
+ 'liked': $container.data('like-liked')
+ };
+
+ // create UI
+ this._createWidget($containerID);
+
+ $createdWidgets = true;
+ }
+ }, this));
+
+ if ($createdWidgets) {
+ new WCF.PeriodicalExecuter(function(pe) {
+ pe.stop();
+
+ WCF.DOMNodeInsertedHandler.forceExecution();
+ }, 250);
+ }
+ },
+
+ /**
+ * Returns a list of available object containers.
+ *
+ * @return jQuery
+ */
+ _getContainers: function() { },
+
+ /**
+ * Returns widget container for target object container.
+ *
+ * @param string containerID
+ * @return jQuery
+ */
+ _getWidgetContainer: function(containerID) { },
+
+ /**
+ * Returns object id for targer object container.
+ *
+ * @param string containerID
+ * @return integer
+ */
+ _getObjectID: function(containerID) { },
+
+ /**
+ * Adds the like widget.
+ *
+ * @param integer containerID
+ * @param jQuery widget
+ */
+ _addWidget: function(containerID, widget) {
+ var $widgetContainer = this._getWidgetContainer(containerID);
+
+ widget.appendTo($widgetContainer);
+ },
+
+ /**
+ * Builds the like widget.
+ *
+ * @param integer containerID
+ * @param jQuery likeButton
+ * @param jQuery dislikeButton
+ * @param jQuery badge
+ * @param jQuery summary
+ */
+ _buildWidget: function(containerID, likeButton, dislikeButton, badge, summary) {
+ var $widget = $('<aside class="likesWidget"><ul></ul></aside>');
+ if (this._canLike) {
+ likeButton.appendTo($widget.find('ul'));
+ dislikeButton.appendTo($widget.find('ul'));
+ }
+ badge.appendTo($widget);
+
+ this._addWidget(containerID, $widget);
+ },
+
+ /**
+ * Creates the like widget.
+ *
+ * @param integer containerID
+ */
+ _createWidget: function(containerID) {
+ var $likeButton = $('<li class="likeButton"><a title="'+WCF.Language.get('wcf.like.button.like')+'" class="jsTooltip"><span class="icon icon16 icon-thumbs-up" /> <span class="invisible">'+WCF.Language.get('wcf.like.button.like')+'</span></a></li>');
+ var $dislikeButton = $('<li class="dislikeButton"><a title="'+WCF.Language.get('wcf.like.button.dislike')+'" class="jsTooltip"><span class="icon icon16 icon-thumbs-down" /> <span class="invisible">'+WCF.Language.get('wcf.like.button.dislike')+'</span></a></li>');
+ if (!this._enableDislikes) $dislikeButton.hide();
+
+ if (!this._allowForOwnContent && (WCF.User.userID == this._containers[containerID].data('userID'))) {
+ $likeButton.hide();
+ $dislikeButton.hide();
+ }
+
+ var $badge = $('<a class="badge jsTooltip likesBadge" />').data('containerID', containerID).click($.proxy(this._showLikeDetails, this));
+
+ var $summary = null;
+ if (this._showSummary) {
+ $summary = $('<p class="likesSummary"><span class="pointer" /></p>');
+ $summary.children('span').data('containerID', containerID).click($.proxy(this._showLikeDetails, this));
+ }
+ this._buildWidget(containerID, $likeButton, $dislikeButton, $badge, $summary);
+
+ this._containerData[containerID].likeButton = $likeButton;
+ this._containerData[containerID].dislikeButton = $dislikeButton;
+ this._containerData[containerID].badge = $badge;
+ this._containerData[containerID].summary = $summary;
+
+ $likeButton.data('containerID', containerID).data('type', 'like').click($.proxy(this._click, this));
+ $dislikeButton.data('containerID', containerID).data('type', 'dislike').click($.proxy(this._click, this));
+ this._setActiveState($likeButton, $dislikeButton, this._containerData[containerID].liked);
+ this._updateBadge(containerID);
+ if (this._showSummary) this._updateSummary(containerID);
+ },
+
+ /**
+ * Displays like details for an object.
+ *
+ * @param object event
+ * @param string containerID
+ */
+ _showLikeDetails: function(event, containerID) {
+ var $containerID = (event === null) ? containerID : $(event.currentTarget).data('containerID');
+
+ if (this._likeDetails[$containerID] === undefined) {
+ this._proxy.setOption('data', {
+ actionName: 'getLikeDetails',
+ className: 'wcf\\data\\like\\LikeAction',
+ parameters: {
+ data: {
+ containerID: $containerID,
+ objectID: this._containerData[$containerID].objectID,
+ objectType: this._containerData[$containerID].objectType
+ }
+ }
+ });
+ this._proxy.sendRequest();
+ }
+ else {
+ if (this._likeDetailsDialog === null) {
+ this._likeDetailsDialog = $('<div>' + this._likeDetails[$containerID] + '</div>').hide().appendTo(document.body);
+ this._likeDetailsDialog.wcfDialog({
+ title: WCF.Language.get('wcf.like.details')
+ });
+ }
+ else {
+ this._likeDetailsDialog.html(this._likeDetails[$containerID]).wcfDialog('open');
+ }
+ }
+ },
+
+ /**
+ * Handles likes and dislikes.
+ *
+ * @param object event
+ */
+ _click: function(event) {
+ var $button = $(event.currentTarget);
+ if ($button === null) {
+ console.debug("[WCF.Like] Unable to find target button, aborting.");
+ return;
+ }
+
+ this._sendRequest($button.data('containerID'), $button.data('type'));
+ },
+
+ /**
+ * Sends request through proxy.
+ *
+ * @param integer containerID
+ * @param string type
+ */
+ _sendRequest: function(containerID, type) {
+ // ignore retards spamming clicks on the buttons
+ if (this._isBusy) {
+ return;
+ }
+
+ this._proxy.setOption('data', {
+ actionName: type,
+ className: 'wcf\\data\\like\\LikeAction',
+ parameters: {
+ data: {
+ containerID: containerID,
+ objectID: this._containerData[containerID].objectID,
+ objectType: this._containerData[containerID].objectType
+ }
+ }
+ });
+
+ this._proxy.sendRequest();
+ },
+
+ /**
+ * Updates likeable object.
+ *
+ * @param object data
+ * @param string textStatus
+ * @param object jqXHR
+ */
+ _success: function(data, textStatus, jqXHR) {
+ var $containerID = data.returnValues.containerID;
+
+ if (!this._containers[$containerID]) {
+ return;
+ }
+
+ switch (data.actionName) {
+ case 'dislike':
+ case 'like':
+ // update container data
+ this._containerData[$containerID].likes = parseInt(data.returnValues.likes);
+ this._containerData[$containerID].dislikes = parseInt(data.returnValues.dislikes);
+ this._containerData[$containerID].users = data.returnValues.users;
+
+ // update label
+ this._updateBadge($containerID);
+ // update summary
+ if (this._showSummary) this._updateSummary($containerID);
+
+ // mark button as active
+ var $likeButton = this._containerData[$containerID].likeButton;
+ var $dislikeButton = this._containerData[$containerID].dislikeButton;
+ var $likeStatus = 0;
+ if (data.returnValues.isLiked) $likeStatus = 1;
+ else if (data.returnValues.isDisliked) $likeStatus = -1;
+ this._setActiveState($likeButton, $dislikeButton, $likeStatus);
+
+ // invalidate cache for like details
+ if (this._likeDetails[$containerID] !== undefined) {
+ delete this._likeDetails[$containerID];
+ }
+
+ this._isBusy = false;
+ break;
+
+ case 'getLikeDetails':
+ this._likeDetails[$containerID] = data.returnValues.template;
+ this._showLikeDetails(null, $containerID);
+ break;
+ }
+ },
+
+ _updateBadge: function(containerID) {
+ if (!this._containerData[containerID].likes && !this._containerData[containerID].dislikes) {
+ this._containerData[containerID].badge.hide();
+ }
+ else {
+ this._containerData[containerID].badge.show();
+
+ // update like counter
+ var $cumulativeLikes = this._containerData[containerID].likes - this._containerData[containerID].dislikes;
+ var $badge = this._containerData[containerID].badge;
+ $badge.removeClass('green red');
+ if ($cumulativeLikes > 0) {
+ $badge.text('+' + WCF.String.formatNumeric($cumulativeLikes));
+ $badge.addClass('green');
+ }
+ else if ($cumulativeLikes < 0) {
+ $badge.text(WCF.String.formatNumeric($cumulativeLikes));
+ $badge.addClass('red');
+ }
+ else {
+ $badge.text('\u00B10');
+ }
+
+ // update tooltip
+ var $likes = this._containerData[containerID].likes;
+ var $dislikes = this._containerData[containerID].dislikes;
+ $badge.data('tooltip', eval(WCF.Language.get('wcf.like.tooltip')));
+ }
+ },
+
+ _updateSummary: function(containerID) {
+ if (!this._containerData[containerID].likes) {
+ this._containerData[containerID].summary.hide();
+ }
+ else {
+ this._containerData[containerID].summary.show();
+
+ var $users = this._containerData[containerID].users;
+ var $userArray = [];
+ for (var $userID in $users) $userArray.push($users[$userID].username);
+ var $others = this._containerData[containerID].likes - $userArray.length;
+
+ this._containerData[containerID].summary.children('span').html(eval(WCF.Language.get('wcf.like.summary')));
+ }
+ },
+
+ /**
+ * Sets button active state.
+ *
+ * @param jquery likeButton
+ * @param jquery dislikeButton
+ * @param integer likeStatus
+ */
+ _setActiveState: function(likeButton, dislikeButton, likeStatus) {
+ likeButton.removeClass('active');
+ dislikeButton.removeClass('active');
+
+ if (likeStatus == 1) {
+ likeButton.addClass('active');
+ }
+ else if (likeStatus == -1) {
+ dislikeButton.addClass('active');
+ }
+ }
+});
--- /dev/null
+WCF.Like=Class.extend({_allowForOwnContent:false,_canLike:false,_containers:{},_containerData:{},_enableDislikes:true,_isBusy:false,_likeDetails:{},_likeDetailsDialog:null,_proxy:null,_showSummary:true,init:function(a,c,f,e){this._canLike=a;this._enableDislikes=c;this._isBusy=false;this._likeDetails={};this._likeDetailsDialog=null;this._showSummary=f;this._allowForOwnContent=e;var d=this._getContainers();this._initContainers(d);this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)});var g=new Date();var b=g.toString().hashCode+g.getUTCMilliseconds();WCF.DOMNodeInsertedHandler.addCallback("WCF.Like"+b,$.proxy(this._domNodeInserted,this))},_domNodeInserted:function(){var a=this._getContainers();this._initContainers(a)},_initContainers:function(containers){var $createdWidgets=false;containers.each($.proxy(function(index,container){var $container=$(container);var $containerID=$container.wcfIdentify();if(!this._containers[$containerID]){this._containers[$containerID]=$container;this._containerData[$containerID]={likeButton:null,badge:null,dislikeButton:null,likes:$container.data("like-likes"),dislikes:$container.data("like-dislikes"),objectType:$container.data("objectType"),objectID:this._getObjectID($containerID),users:eval($container.data("like-users")),liked:$container.data("like-liked")};this._createWidget($containerID);$createdWidgets=true}},this));if($createdWidgets){new WCF.PeriodicalExecuter(function(pe){pe.stop();WCF.DOMNodeInsertedHandler.forceExecution()},250)}},_getContainers:function(){},_getWidgetContainer:function(a){},_getObjectID:function(a){},_addWidget:function(a,b){var c=this._getWidgetContainer(a);b.appendTo(c)},_buildWidget:function(b,a,d,c,e){var f=$('<aside class="likesWidget"><ul></ul></aside>');if(this._canLike){a.appendTo(f.find("ul"));d.appendTo(f.find("ul"))}c.appendTo(f);this._addWidget(b,f)},_createWidget:function(b){var e=$('<li class="likeButton"><a title="'+WCF.Language.get("wcf.like.button.like")+'" class="jsTooltip"><span class="icon icon16 icon-thumbs-up" /> <span class="invisible">'+WCF.Language.get("wcf.like.button.like")+"</span></a></li>");var c=$('<li class="dislikeButton"><a title="'+WCF.Language.get("wcf.like.button.dislike")+'" class="jsTooltip"><span class="icon icon16 icon-thumbs-down" /> <span class="invisible">'+WCF.Language.get("wcf.like.button.dislike")+"</span></a></li>");if(!this._enableDislikes){c.hide()}if(!this._allowForOwnContent&&(WCF.User.userID==this._containers[b].data("userID"))){e.hide();c.hide()}var d=$('<a class="badge jsTooltip likesBadge" />').data("containerID",b).click($.proxy(this._showLikeDetails,this));var a=null;if(this._showSummary){a=$('<p class="likesSummary"><span class="pointer" /></p>');a.children("span").data("containerID",b).click($.proxy(this._showLikeDetails,this))}this._buildWidget(b,e,c,d,a);this._containerData[b].likeButton=e;this._containerData[b].dislikeButton=c;this._containerData[b].badge=d;this._containerData[b].summary=a;e.data("containerID",b).data("type","like").click($.proxy(this._click,this));c.data("containerID",b).data("type","dislike").click($.proxy(this._click,this));this._setActiveState(e,c,this._containerData[b].liked);this._updateBadge(b);if(this._showSummary){this._updateSummary(b)}},_showLikeDetails:function(c,a){var b=(c===null)?a:$(c.currentTarget).data("containerID");if(this._likeDetails[b]===undefined){this._proxy.setOption("data",{actionName:"getLikeDetails",className:"wcf\\data\\like\\LikeAction",parameters:{data:{containerID:b,objectID:this._containerData[b].objectID,objectType:this._containerData[b].objectType}}});this._proxy.sendRequest()}else{if(this._likeDetailsDialog===null){this._likeDetailsDialog=$("<div>"+this._likeDetails[b]+"</div>").hide().appendTo(document.body);this._likeDetailsDialog.wcfDialog({title:WCF.Language.get("wcf.like.details")})}else{this._likeDetailsDialog.html(this._likeDetails[b]).wcfDialog("open")}}},_click:function(a){var b=$(a.currentTarget);if(b===null){console.debug("[WCF.Like] Unable to find target button, aborting.");return}this._sendRequest(b.data("containerID"),b.data("type"))},_sendRequest:function(a,b){if(this._isBusy){return}this._proxy.setOption("data",{actionName:b,className:"wcf\\data\\like\\LikeAction",parameters:{data:{containerID:a,objectID:this._containerData[a].objectID,objectType:this._containerData[a].objectType}}});this._proxy.sendRequest()},_success:function(d,g,b){var a=d.returnValues.containerID;if(!this._containers[a]){return}switch(d.actionName){case"dislike":case"like":this._containerData[a].likes=parseInt(d.returnValues.likes);this._containerData[a].dislikes=parseInt(d.returnValues.dislikes);this._containerData[a].users=d.returnValues.users;this._updateBadge(a);if(this._showSummary){this._updateSummary(a)}var f=this._containerData[a].likeButton;var e=this._containerData[a].dislikeButton;var c=0;if(d.returnValues.isLiked){c=1}else{if(d.returnValues.isDisliked){c=-1}}this._setActiveState(f,e,c);if(this._likeDetails[a]!==undefined){delete this._likeDetails[a]}this._isBusy=false;break;case"getLikeDetails":this._likeDetails[a]=d.returnValues.template;this._showLikeDetails(null,a);break}},_updateBadge:function(containerID){if(!this._containerData[containerID].likes&&!this._containerData[containerID].dislikes){this._containerData[containerID].badge.hide()}else{this._containerData[containerID].badge.show();var $cumulativeLikes=this._containerData[containerID].likes-this._containerData[containerID].dislikes;var $badge=this._containerData[containerID].badge;$badge.removeClass("green red");if($cumulativeLikes>0){$badge.text("+"+WCF.String.formatNumeric($cumulativeLikes));$badge.addClass("green")}else{if($cumulativeLikes<0){$badge.text(WCF.String.formatNumeric($cumulativeLikes));$badge.addClass("red")}else{$badge.text("\u00B10")}}var $likes=this._containerData[containerID].likes;var $dislikes=this._containerData[containerID].dislikes;$badge.data("tooltip",eval(WCF.Language.get("wcf.like.tooltip")))}},_updateSummary:function(containerID){if(!this._containerData[containerID].likes){this._containerData[containerID].summary.hide()}else{this._containerData[containerID].summary.show();var $users=this._containerData[containerID].users;var $userArray=[];for(var $userID in $users){$userArray.push($users[$userID].username)}var $others=this._containerData[containerID].likes-$userArray.length;this._containerData[containerID].summary.children("span").html(eval(WCF.Language.get("wcf.like.summary")))}},_setActiveState:function(a,b,c){a.removeClass("active");b.removeClass("active");if(c==1){a.addClass("active")}else{if(c==-1){b.addClass("active")}}}});
\ No newline at end of file
parent::save();
+ // like (userID)
+ $conditions = new PreparedStatementConditionBuilder();
+ $conditions->add("userID IN (?)", array($this->mergedUserIDs));
+ $sql = "UPDATE IGNORE wcf".WCF_N."_like
+ SET userID = ?
+ ".$conditions;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array_merge(array($this->destinationUserID), $conditions->getParameters()));
+ // like (objectUserID)
+ $conditions = new PreparedStatementConditionBuilder();
+ $conditions->add("objectUserID IN (?)", array($this->mergedUserIDs));
+ $sql = "UPDATE wcf".WCF_N."_like
+ SET objectUserID = ?
+ ".$conditions;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array_merge(array($this->destinationUserID), $conditions->getParameters()));
+
+ // like_object
+ $conditions = new PreparedStatementConditionBuilder();
+ $conditions->add("objectUserID IN (?)", array($this->mergedUserIDs));
+ $sql = "UPDATE wcf".WCF_N."_like_object
+ SET objectUserID = ?
+ ".$conditions;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array_merge(array($this->destinationUserID), $conditions->getParameters()));
+
// user_follow (userID)
$conditions = new PreparedStatementConditionBuilder();
$conditions->add("userID IN (?)", array($this->mergedUserIDs));
--- /dev/null
+<?php
+namespace wcf\data\like;
+use wcf\data\like\object\ILikeObject;
+use wcf\data\object\type\IObjectTypeProvider;
+
+/**
+ * Default interface for like object type providers.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.like
+ * @subpackage data.like
+ * @category Community Framework
+ */
+interface ILikeObjectTypeProvider extends IObjectTypeProvider {
+ /**
+ * Returns true if the active user can access the given likeable object.
+ *
+ * @param wcf\data\like\object\ILikeObject $object
+ * @return boolean
+ */
+ public function checkPermissions(ILikeObject $object);
+}
--- /dev/null
+<?php
+namespace wcf\data\like;
+use wcf\data\DatabaseObject;
+use wcf\system\WCF;
+
+/**
+ * Represents a like of an object.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.like
+ * @subpackage data.like
+ * @category Community Framework
+ */
+class Like extends DatabaseObject {
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'like';
+
+ /**
+ * @see wcf\data\DatabaseObject::$databaseIndexName
+ */
+ protected static $databaseTableIndexName = 'likeID';
+
+ /**
+ * like value
+ * @var integer
+ */
+ const LIKE = 1;
+
+ /**
+ * dislike value
+ * @var integer
+ */
+ const DISLIKE = -1;
+
+ /**
+ * Gets a like by type, object id and user id.
+ *
+ * @param integer $objectTypeID
+ * @param integer $objectID
+ * @param integer $userID
+ * @return Like
+ */
+ public static function getLike($objectTypeID, $objectID, $userID) {
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_like
+ WHERE objectTypeID = ?
+ AND objectID = ?
+ AND userID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array(
+ $objectTypeID,
+ $objectID,
+ $userID
+ ));
+
+ $row = $statement->fetchArray();
+
+ if (!$row) {
+ $row = array();
+ }
+
+ return new Like(null, $row);
+ }
+
+ /**
+ * @see wcf\data\IStorableObject::getDatabaseTableAlias()
+ */
+ public static function getDatabaseTableAlias() {
+ return 'like_table';
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\like;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\system\exception\PermissionDeniedException;
+use wcf\system\exception\UserInputException;
+use wcf\system\like\LikeHandler;
+use wcf\system\user\activity\event\UserActivityEventHandler;
+use wcf\system\user\GroupedUserList;
+use wcf\system\WCF;
+
+/**
+ * Executes like-related actions.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.like
+ * @category Community Framework
+ */
+class LikeAction extends AbstractDatabaseObjectAction {
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$allowGuestAccess
+ */
+ protected $allowGuestAccess = array('getLikeDetails');
+
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$className
+ */
+ protected $className = 'wcf\data\like\LikeEditor';
+
+ /**
+ * likeable object
+ * @var wcf\data\like\object\ILikeObject
+ */
+ public $likeableObject = null;
+
+ /**
+ * object type object
+ * @var wcf\data\object\type\ObjectType
+ */
+ public $objectType = null;
+
+ /**
+ * like object type provider object
+ * @var wcf\data\like\ILikeObjectTypeProvider
+ */
+ public $objectTypeProvider = null;
+
+ /**
+ * Validates parameters to fetch like details.
+ */
+ public function validateGetLikeDetails() {
+ $this->validateObjectParameters();
+ }
+
+ /**
+ * Returns like details.
+ *
+ * @return array<string>
+ */
+ public function getLikeDetails() {
+ $sql = "SELECT userID, likeValue
+ FROM wcf".WCF_N."_like
+ WHERE objectID = ?
+ AND objectTypeID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array(
+ $this->parameters['data']['objectID'],
+ $this->objectType->objectTypeID
+ ));
+ $data = array(
+ Like::LIKE => array(),
+ Like::DISLIKE => array()
+ );
+ while ($row = $statement->fetchArray()) {
+ $data[$row['likeValue']][] = $row['userID'];
+ }
+
+ $values = array();
+ if (!empty($data[Like::LIKE])) {
+ $values[Like::LIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.like'));
+ $values[Like::LIKE]->addUserIDs($data[Like::LIKE]);
+ }
+ if (!empty($data[Like::DISLIKE])) {
+ $values[Like::DISLIKE] = new GroupedUserList(WCF::getLanguage()->get('wcf.like.details.dislike'));
+ $values[Like::DISLIKE]->addUserIDs($data[Like::DISLIKE]);
+ }
+
+ // load user profiles
+ GroupedUserList::loadUsers();
+
+ WCF::getTPL()->assign(array(
+ 'groupedUsers' => $values
+ ));
+
+ return array(
+ 'containerID' => $this->parameters['data']['containerID'],
+ 'template' => WCF::getTPL()->fetch('groupedUserList')
+ );
+ }
+
+ /**
+ * Validates parameters for like-related actions.
+ */
+ public function validateLike() {
+ $this->validateObjectParameters();
+
+ // check permissions
+ if (!WCF::getUser()->userID || !WCF::getSession()->getPermission('user.like.canLike')) {
+ throw new PermissionDeniedException();
+ }
+
+ // check if liking own content but forbidden by configuration
+ $this->likeableObject = $this->objectTypeProvider->getObjectByID($this->parameters['data']['objectID']);
+ $this->likeableObject->setObjectType($this->objectType);
+ if (!LIKE_ALLOW_FOR_OWN_CONTENT && ($this->likeableObject->getUserID() == WCF::getUser()->userID)) {
+ throw new PermissionDeniedException();
+ }
+ }
+
+ /**
+ * @see wcf\data\like\LikeAction::updateLike()
+ */
+ public function like() {
+ return $this->updateLike(Like::LIKE);
+ }
+
+ /**
+ * @see wcf\data\like\LikeAction::validateLike()
+ */
+ public function validateDislike() {
+ $this->validateLike();
+ }
+
+ /**
+ * @see wcf\data\like\LikeAction::updateLike()
+ */
+ public function dislike() {
+ return $this->updateLike(Like::DISLIKE);
+ }
+
+ /**
+ * Sets like/dislike for an object, executing this method again with the same parameters
+ * will revert the status (removing like/dislike).
+ *
+ * @return array
+ */
+ protected function updateLike($likeValue) {
+ $likeData = LikeHandler::getInstance()->like($this->likeableObject, WCF::getUser(), $likeValue);
+
+ // handle activity event
+ if (UserActivityEventHandler::getInstance()->getObjectTypeID($this->objectType->objectType.'.recentActivityEvent')) {
+ if ($likeData['data']['liked'] == 1) {
+ UserActivityEventHandler::getInstance()->fireEvent($this->objectType->objectType.'.recentActivityEvent', $this->parameters['data']['objectID']);
+ }
+ else {
+ UserActivityEventHandler::getInstance()->removeEvents($this->objectType->objectType.'.recentActivityEvent', array($this->parameters['data']['objectID']));
+ }
+ }
+
+ // get stats
+ return array(
+ 'likes' => ($likeData['data']['likes'] === null) ? 0 : $likeData['data']['likes'],
+ 'dislikes' => ($likeData['data']['dislikes'] === null) ? 0 : $likeData['data']['dislikes'],
+ 'cumulativeLikes' => ($likeData['data']['cumulativeLikes'] === null) ? 0 : $likeData['data']['cumulativeLikes'],
+ 'isLiked' => ($likeData['data']['liked'] == 1) ? 1 : 0,
+ 'isDisliked' => ($likeData['data']['liked'] == -1) ? 1 : 0,
+ 'containerID' => $this->parameters['data']['containerID'],
+ 'newValue' => $likeData['newValue'],
+ 'oldValue' => $likeData['oldValue'],
+ 'users' => $likeData['users']
+ );
+ }
+
+ /**
+ * Validates permissions for given object.
+ */
+ protected function validateObjectParameters() {
+ if (!MODULE_LIKE) {
+ throw new PermissionDeniedException();
+ }
+
+ $this->readString('containerID', false, 'data');
+ $this->readInteger('objectID', false, 'data');
+ $this->readString('objectType', false, 'data');
+
+ $this->objectType = LikeHandler::getInstance()->getObjectType($this->parameters['data']['objectType']);
+ if ($this->objectType === null) {
+ throw new UserInputException('objectType');
+ }
+
+ $this->objectTypeProvider = $this->objectType->getProcessor();
+ $this->likeableObject = $this->objectTypeProvider->getObjectByID($this->parameters['data']['objectID']);
+ $this->likeableObject->setObjectType($this->objectType);
+ if (!$this->objectTypeProvider->checkPermissions($this->likeableObject)) {
+ throw new PermissionDeniedException();
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\like;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Extends the like object with functions to create, update and delete likes.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.like
+ * @subpackage data.like
+ * @category Community Framework
+ */
+class LikeEditor extends DatabaseObjectEditor {
+ /**
+ * @see wcf\data\DatabaseObjectEditor::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\like\Like';
+}
--- /dev/null
+<?php
+namespace wcf\data\like;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of likes.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.like
+ * @category Community Framework
+ */
+class LikeList extends DatabaseObjectList {
+ /**
+ * @see wcf\data\DatabaseObjectList::$className
+ */
+ public $className = 'wcf\data\like\Like';
+}
--- /dev/null
+<?php
+namespace wcf\data\like\object;
+use wcf\data\object\type\ObjectType;
+use wcf\data\DatabaseObjectDecorator;
+
+/**
+ * Provides a default implementation for like objects.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.like
+ * @subpackage data.like.object
+ * @category Community Framework
+ */
+abstract class AbstractLikeObject extends DatabaseObjectDecorator implements ILikeObject {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\like\object\LikeObject';
+
+ /**
+ * object type
+ * @var wcf\data\object\type\ObjectType
+ */
+ protected $objectType = null;
+
+ /**
+ * @see wcf\data\like\object\ILikeObject::updateLikeCounter()
+ */
+ public function updateLikeCounter($cumulativeLikes) { }
+
+ /**
+ * @see wcf\data\like\object\ILikeObject::getObjectType()
+ */
+ public function getObjectType() {
+ return $this->objectType;
+ }
+
+ /**
+ * @see wcf\data\like\object\ILikeObject::setObjectType()
+ */
+ public function setObjectType(ObjectType $objectType) {
+ $this->objectType = $objectType;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\like\object;
+use wcf\data\object\type\ObjectType;
+use wcf\data\IDatabaseObjectProcessor;
+use wcf\data\ITitledObject;
+
+/**
+ * Any likeable object should implement this interface.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.like
+ * @subpackage data.like.object
+ * @category Community Framework
+ */
+interface ILikeObject extends IDatabaseObjectProcessor, ITitledObject {
+ /**
+ * Returns the url to this likeable.
+ *
+ * @return string
+ */
+ public function getURL();
+
+ /**
+ * Returns the user id of the owner of this object.
+ *
+ * @return integer
+ */
+ public function getUserID();
+
+ /**
+ * Returns the id of this object.
+ *
+ * @return integer
+ */
+ public function getObjectID();
+
+ /**
+ * Gets the object type.
+ *
+ * @return wcf\data\like\object\type\LikeObjectType
+ */
+ public function getObjectType();
+
+ /**
+ * Updates the cumulative likes for this object.
+ *
+ * @param integer $cumulativeLikes
+ */
+ public function updateLikeCounter($cumulativeLikes);
+
+ /**
+ * Sets the object type.
+ *
+ * @param wcf\data\object\type\ObjectType
+ */
+ public function setObjectType(ObjectType $objectType);
+}
--- /dev/null
+<?php
+namespace wcf\data\like\object;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\user\User;
+use wcf\data\DatabaseObject;
+use wcf\system\WCF;
+
+/**
+ * Represents a liked object.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.like
+ * @subpackage data.like.object
+ * @category Community Framework
+ */
+class LikeObject extends DatabaseObject {
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'like_object';
+
+ /**
+ * @see wcf\data\DatabaseObject::$databaseIndexName
+ */
+ protected static $databaseTableIndexName = 'likeObjectID';
+
+ /**
+ * liked object
+ * @var wcf\data\like\object\ILikeObject
+ */
+ protected $likedObject = null;
+
+ /**
+ * list of users who liked this object
+ * @var array<wcf\data\user\User>
+ */
+ protected $users = array();
+
+ /**
+ * @see wcf\data\DatabaseObject::handleData();
+ */
+ protected function handleData($data) {
+ parent::handleData($data);
+
+ // get user objects from cache
+ if (!empty($data['cachedUsers'])) {
+ $cachedUsers = @unserialize($data['cachedUsers']);
+
+ if (is_array($cachedUsers)) {
+ foreach ($cachedUsers as $cachedUserData) {
+ $user = new User(null, $cachedUserData);
+ $this->users[$user->userID] = $user;
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the first 3 users who liked this object.
+ *
+ * @return array<wcf\data\user\User>
+ */
+ public function getUsers() {
+ return $this->users;
+ }
+
+ /**
+ * Returns the liked object.
+ *
+ * @return wcf\data\like\object\ILikeObject
+ */
+ public function getLikedObject() {
+ if ($this->likedObject === null) {
+ $this->likedObject = ObjectTypeCache::getInstance()->getObjectType($this->objectTypeID)->getProcessor()->getObjectByID($this->objectID);
+ }
+
+ return $this->likedObject;
+ }
+
+ /**
+ * Sets the liked object.
+ *
+ * @param wcf\data\like\object\ILikeObject $likeObject
+ */
+ public function setLikedObject(ILikeObject $likedObject) {
+ $this->likedObject = $likedObject;
+ }
+
+ /**
+ * Gets a like object by type and object id.
+ *
+ * @param integer $objectTypeID
+ * @param integer $objectID
+ * @return wcf\data\like\object\LikeObject
+ */
+ public static function getLikeObject($objectTypeID, $objectID) {
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_like_object
+ WHERE objectTypeID = ?
+ AND objectID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array(
+ $objectTypeID,
+ $objectID
+ ));
+ $row = $statement->fetchArray();
+
+ if (!$row) {
+ $row = array();
+ }
+
+ return new LikeObject(null, $row);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\like\object;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Extends the LikeObject object with functions to create, update and delete liked objects.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.like
+ * @subpackage data.like.object
+ * @category Community Framework
+ */
+class LikeObjectEditor extends DatabaseObjectEditor {
+ /**
+ * @see wcf\data\DatabaseObjectEditor::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\like\object\LikeObject';
+}
--- /dev/null
+<?php
+namespace wcf\data\like\object;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of like objects.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.like.object
+ * @category Community Framework
+ */
+class LikeObjectList extends DatabaseObjectList {
+ /**
+ * @see wcf\data\DatabaseObjectList::$className
+ */
+ public $className = 'wcf\data\like\object\LikeObject';
+}
/**
* @see wcf\page\SortablePage::$validSortFields
*/
- public $validSortFields = array('username', 'registrationDate', 'activityPoints');
+ public $validSortFields = array('username', 'registrationDate', 'activityPoints', 'likesReceived');
/**
* @see wcf\page\MultipleLinkPage::$objectListClassName
--- /dev/null
+<?php
+namespace wcf\system\cache\builder;
+use wcf\data\user\UserList;
+use wcf\system\WCF;
+
+/**
+ * Caches a list of the most liked members.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.like
+ * @subpackage system.cache.builder
+ * @category Community Framework
+ */
+class MostLikedMembersCacheBuilder extends AbstractCacheBuilder {
+ /**
+ * @see wcf\system\cache\builder\AbstractCacheBuilder::$maxLifetime
+ */
+ protected $maxLifetime = 600;
+
+ /**
+ * @see wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
+ */
+ protected function rebuild(array $parameters) {
+ $userProfileList = new UserList();
+ $userProfileList->getConditionBuilder()->add('user_table.likesReceived > 0');
+ $userProfileList->sqlOrderBy = 'user_table.likesReceived DESC';
+ $userProfileList->sqlLimit = 5;
+ $userProfileList->readObjectIDs();
+
+ return $userProfileList->getObjectIDs();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\dashboard\box;
+use wcf\data\user\UserProfileList;
+use wcf\system\cache\builder\MostLikedMembersCacheBuilder;
+use wcf\system\WCF;
+
+/**
+ * Shows a list of the most liked members.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.like
+ * @subpackage system.dashboard.box
+ * @category Community Framework
+ */
+class MostLikedMembersDashboardBox extends AbstractSidebarDashboardBox {
+ /**
+ * @see wcf\system\dashboard\box\AbstractContentDashboardBox::render()
+ */
+ protected function render() {
+ // get ids
+ $mostLikedMemberIDs = MostLikedMembersCacheBuilder::getInstance()->getData();
+ if (empty($mostLikedMemberIDs)) return '';
+
+ // get profile data
+ $userProfileList = new UserProfileList();
+ $userProfileList->sqlOrderBy = 'user_table.likesReceived DESC';
+ $userProfileList->setObjectIDs($mostLikedMemberIDs);
+ $userProfileList->readObjects();
+
+ WCF::getTPL()->assign(array(
+ 'mostLikedMembers' => $userProfileList
+ ));
+ return WCF::getTPL()->fetch('dashboardBoxMostLikedMembers');
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\like;
+use wcf\data\like\object\ILikeObject;
+use wcf\data\like\object\LikeObject;
+use wcf\data\like\object\LikeObjectEditor;
+use wcf\data\like\object\LikeObjectList;
+use wcf\data\like\Like;
+use wcf\data\like\LikeEditor;
+use wcf\data\like\LikeList;
+use wcf\data\object\type\ObjectType;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\user\User;
+use wcf\data\user\UserEditor;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\user\activity\event\UserActivityEventHandler;
+use wcf\system\user\activity\point\UserActivityPointHandler;
+use wcf\system\SingletonFactory;
+use wcf\system\WCF;
+
+/**
+ * Handles the likes of liked objects.
+ *
+ * Usage (retrieve all likes for a list of objects):
+ * // get type object
+ * $objectType = LikeHandler::getInstance()->getObjectType('com.woltlab.wcf.foo.bar');
+ * // load like data
+ * LikeHandler::getInstance()->loadLikeObjects($objectType, $objectIDs);
+ * // get like data
+ * $likeObjects = LikeHandler::getInstance()->getLikeObjects($objectType);
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.like
+ * @subpackage system.like
+ * @category Community Framework
+ */
+class LikeHandler extends SingletonFactory {
+ /**
+ * loaded like objects
+ * @var array<array>
+ */
+ protected $likeObjectCache = array();
+
+ /**
+ * cached object types
+ * @var array<array>
+ */
+ protected $cache = null;
+
+ /**
+ * Creates a new LikeHandler instance.
+ */
+ protected function init() {
+ // load cache
+ $this->cache = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.like.likeableObject');
+ }
+
+ /**
+ * Returns an object type from cache.
+ *
+ * @return wcf\data\object\type\ObjectType
+ */
+ public function getObjectType($objectName) {
+ if (isset($this->cache[$objectName])) {
+ return $this->cache[$objectName];
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets a like object.
+ *
+ * @param wcf\data\object\type\ObjectType $objectType
+ * @param integer $objectID
+ * @return wcf\data\like\object\LikeObject
+ */
+ public function getLikeObject(ObjectType $objectType, $objectID) {
+ if (isset($this->likeObjectCache[$objectType->objectTypeID][$objectID])) {
+ return $this->likeObjectCache[$objectType->objectTypeID][$objectID];
+ }
+
+ return null;
+ }
+
+ /**
+ * Gets the like objects of a specific object type.
+ *
+ * @param wcf\data\object\type\ObjectType $objectType
+ * @return array<wcf\data\like\object\LikeObject>
+ */
+ public function getLikeObjects(ObjectType $objectType) {
+ if (isset($this->likeObjectCache[$objectType->objectTypeID])) {
+ return $this->likeObjectCache[$objectType->objectTypeID];
+ }
+
+ return array();
+ }
+
+ /**
+ * Loads the like data for a set of objects and returns the number of loaded
+ * like objects
+ *
+ * @param wcf\data\object\type\ObjectType $objectType
+ * @param array $objectIDs
+ * @return integer
+ */
+ public function loadLikeObjects(ObjectType $objectType, array $objectIDs) {
+ $i = 0;
+
+ $conditions = new PreparedStatementConditionBuilder();
+ $conditions->add("like_object.objectTypeID = ?", array($objectType->objectTypeID));
+ $conditions->add("like_object.objectID IN (?)", array($objectIDs));
+ $parameters = $conditions->getParameters();
+
+ if (WCF::getUser()->userID) {
+ $sql = "SELECT like_object.*,
+ CASE WHEN like_table.userID IS NOT NULL THEN like_table.likeValue ELSE 0 END AS liked
+ FROM wcf".WCF_N."_like_object like_object
+ LEFT JOIN wcf".WCF_N."_like like_table
+ ON (like_table.objectTypeID = like_object.objectTypeID
+ AND like_table.objectID = like_object.objectID
+ AND like_table.userID = ?)
+ ".$conditions;
+
+ array_unshift($parameters, WCF::getUser()->userID);
+ }
+ else {
+ $sql = "SELECT like_object.*, 0 AS liked
+ FROM wcf".WCF_N."_like_object like_object
+ ".$conditions;
+ }
+
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute($parameters);
+ while ($row = $statement->fetchArray()) {
+ $this->likeObjectCache[$objectType->objectTypeID][$row['objectID']] = new LikeObject(null, $row);
+ $i++;
+ }
+
+ return $i;
+ }
+
+ /**
+ * Saves the like of an object.
+ *
+ * @param wcf\data\like\object\ILikeObject $likeable
+ * @param wcf\data\user\User $user
+ * @param integer $likeValue
+ * @param integer $time
+ * @return array
+ */
+ public function like(ILikeObject $likeable, User $user, $likeValue, $time = TIME_NOW) {
+ if ($user->userID == 35) {
+ file_put_contents(WCF_DIR.'__like.dt', microtime(true) . "\tt={$likeable->getObjectType()->objectType}\to={$likeable->getObjectID()}\tlike={$likeValue}\n", FILE_APPEND);
+ }
+
+ // verify if object is already liked by user
+ $like = Like::getLike($likeable->getObjectType()->objectTypeID, $likeable->getObjectID(), $user->userID);
+
+ // get like object
+ $likeObject = LikeObject::getLikeObject($likeable->getObjectType()->objectTypeID, $likeable->getObjectID());
+
+ // if vote is identically just revert the vote
+ if ($like->likeID && ($like->likeValue == $likeValue)) {
+ return $this->revertLike($like, $likeable, $likeObject, $user);
+ }
+
+ // like data
+ $cumulativeLikes = 0;
+ $newValue = $oldValue = null;
+ $users = array();
+
+ // update existing object
+ if ($likeObject->likeObjectID) {
+ $likes = $likeObject->likes;
+ $dislikes = $likeObject->dislikes;
+ $cumulativeLikes = $likeObject->cumulativeLikes;
+
+ // previous (dis-)like already exists
+ if ($like->likeID) {
+ $oldValue = $like->likeValue;
+
+ // revert like and replace it with a dislike
+ if ($like->likeValue == Like::LIKE) {
+ $likes--;
+ $dislikes++;
+ $cumulativeLikes -= 2;
+ $newValue = Like::DISLIKE;
+ }
+ else {
+ // revert dislike and replace it with a like
+ $likes++;
+ $dislikes--;
+ $cumulativeLikes += 2;
+ $newValue = Like::LIKE;
+ }
+ }
+ else {
+ if ($likeValue == Like::LIKE) {
+ $likes++;
+ $cumulativeLikes++;
+ $newValue = Like::LIKE;
+ }
+ else {
+ $dislikes++;
+ $cumulativeLikes--;
+ $newValue = Like::DISLIKE;
+ }
+ }
+
+ // build update date
+ $updateData = array(
+ 'likes' => $likes,
+ 'dislikes' => $dislikes,
+ 'cumulativeLikes' => $cumulativeLikes
+ );
+
+ if ($likeValue == 1) {
+ $users = unserialize($likeObject->cachedUsers);
+ if (count($users) < 3) {
+ $users[$user->userID] = array('userID' => $user->userID, 'username' => $user->username);
+ $updateData['cachedUsers'] = serialize($users);
+ }
+ }
+ else if ($likeValue == -1) {
+ $users = unserialize($likeObject->cachedUsers);
+ if (isset($users[$user->userID])) {
+ unset($users[$user->userID]);
+ $updateData['cachedUsers'] = serialize($users);
+ }
+ }
+
+ // update data
+ $likeObjectEditor = new LikeObjectEditor($likeObject);
+ $likeObjectEditor->update($updateData);
+ }
+ else {
+ $cumulativeLikes = $likeValue;
+ $newValue = $likeValue;
+ $users = array();
+ if ($likeValue == 1) $users = array($user->userID => array('userID' => $user->userID, 'username' => $user->username));
+
+ // create cache
+ $likeObject = LikeObjectEditor::create(array(
+ 'objectTypeID' => $likeable->getObjectType()->objectTypeID,
+ 'objectID' => $likeable->getObjectID(),
+ 'objectUserID' => ($likeable->getUserID() ?: null),
+ 'likes' => ($likeValue == Like::LIKE) ? 1 : 0,
+ 'dislikes' => ($likeValue == Like::DISLIKE) ? 1 : 0,
+ 'cumulativeLikes' => $cumulativeLikes,
+ 'cachedUsers' => serialize($users)
+ ));
+ }
+
+ // update owner's like counter
+ if ($likeable->getUserID()) {
+ if ($like->likeID) {
+ $userEditor = new UserEditor(new User($likeable->getUserID()));
+ $userEditor->updateCounters(array(
+ 'likesReceived' => ($like->likeValue == Like::LIKE ? -1 : 1)
+ ));
+ }
+ else if ($likeValue == Like::LIKE) {
+ $userEditor = new UserEditor(new User($likeable->getUserID()));
+ $userEditor->updateCounters(array(
+ 'likesReceived' => 1
+ ));
+ }
+ }
+
+ if (!$like->likeID) {
+ // save like
+ $like = LikeEditor::create(array(
+ 'objectID' => $likeable->getObjectID(),
+ 'objectTypeID' => $likeable->getObjectType()->objectTypeID,
+ 'objectUserID' => ($likeable->getUserID() ?: null),
+ 'userID' => $user->userID,
+ 'time' => $time,
+ 'likeValue' => $likeValue
+ ));
+
+ if ($likeValue == Like::LIKE && $likeable->getUserID()) UserActivityPointHandler::getInstance()->fireEvent('com.woltlab.wcf.like.activityPointEvent.receivedLikes', $like->likeID, $likeable->getUserID());
+ }
+ else {
+ $likeEditor = new LikeEditor($like);
+ $likeEditor->update(array(
+ 'time' => $time,
+ 'likeValue' => $likeValue
+ ));
+
+ if ($likeValue == Like::DISLIKE) UserActivityPointHandler::getInstance()->removeEvents('com.woltlab.wcf.like.activityPointEvent.receivedLikes', array($like->likeID));
+ }
+
+ // update object's like counter
+ $likeable->updateLikeCounter($cumulativeLikes);
+
+ return array(
+ 'data' => $this->loadLikeStatus($likeObject, $user),
+ 'like' => $like,
+ 'newValue' => $newValue,
+ 'oldValue' => $oldValue,
+ 'users' => $users
+ );
+ }
+
+ /**
+ * Reverts the like of an object.
+ *
+ * @param wcf\data\like\Like $like
+ * @param wcf\data\like\object\ILikeObject $likeable
+ * @param wcf\data\like\object\LikeObject $likeObject
+ * @param wcf\data\user\User $user
+ * @return array
+ */
+ public function revertLike(Like $like, ILikeObject $likeable, LikeObject $likeObject, User $user) {
+ // delete like
+ $editor = new LikeEditor($like);
+ $editor->delete();
+
+ // update like object cache
+ $likes = $likeObject->likes;
+ $dislikes = $likeObject->dislikes;
+ $cumulativeLikes = $likeObject->cumulativeLikes;
+
+ if ($like->likeValue == Like::LIKE) {
+ $likes--;
+ $cumulativeLikes--;
+ }
+ else {
+ $dislikes--;
+ $cumulativeLikes++;
+ }
+
+ // build update data
+ $updateData = array(
+ 'likes' => $likes,
+ 'dislikes' => $dislikes,
+ 'cumulativeLikes' => $cumulativeLikes
+ );
+
+ $users = $likeObject->getUsers();
+ $usersArray = array();
+ foreach ($users as $user2) {
+ $usersArray[$user2->userID] = array('userID' => $user2->userID, 'username' => $user2->username);
+ }
+
+ if (isset($usersArray[$user->userID])) {
+ unset($usersArray[$user->userID]);
+ $updateData['cachedUsers'] = serialize($usersArray);
+ }
+
+ $likeObjectEditor = new LikeObjectEditor($likeObject);
+ if (!$updateData['likes'] && !$updateData['dislikes']) {
+ // remove object instead
+ $likeObjectEditor->delete();
+ }
+ else {
+ // update data
+ $likeObjectEditor->update($updateData);
+ }
+
+ // update owner's like counter and activity points
+ if ($likeable->getUserID()) {
+ if ($like->likeValue == Like::LIKE) {
+ $userEditor = new UserEditor(new User($likeable->getUserID()));
+ $userEditor->updateCounters(array(
+ 'likesReceived' => -1
+ ));
+ }
+
+ UserActivityPointHandler::getInstance()->removeEvents('com.woltlab.wcf.like.activityPointEvent.receivedLikes', array($like->likeID));
+ }
+
+ // update object's like counter
+ $likeable->updateLikeCounter($cumulativeLikes);
+
+ return array(
+ 'data' => $this->loadLikeStatus($likeObject, $user),
+ 'newValue' => null,
+ 'oldValue' => $like->likeValue,
+ 'users' => $usersArray
+ );
+ }
+
+ /**
+ * Removes all likes for given objects.
+ *
+ * @param string $objectType
+ * @param array<integer> $objectIDs
+ */
+ public function removeLikes($objectType, array $objectIDs) {
+ $objectTypeObj = $this->getObjectType($objectType);
+
+ // get like objects
+ $likeObjectList = new LikeObjectList();
+ $likeObjectList->getConditionBuilder()->add('like_object.objectTypeID = ?', array($objectTypeObj->objectTypeID));
+ $likeObjectList->getConditionBuilder()->add('like_object.objectID IN (?)', array($objectIDs));
+ $likeObjectList->readObjects();
+ $likeObjects = $likeObjectList->getObjects();
+ $likeObjectIDs = $likeObjectList->getObjectIDs();
+
+ // reduce count of received users
+ $users = array();
+ foreach ($likeObjects as $likeObject) {
+ if ($likeObject->likes) {
+ if (!isset($users[$likeObject->objectUserID])) $users[$likeObject->objectUserID] = 0;
+ $users[$likeObject->objectUserID] += $likeObject->likes;
+ }
+ }
+ foreach ($users as $userID => $receivedLikes) {
+ $userEditor = new UserEditor(new User(null, array('userID' => $userID)));
+ $userEditor->updateCounters(array(
+ 'likesReceived' => $receivedLikes * -1
+ ));
+ }
+
+ // get like ids
+ $likeList = new LikeList();
+ $likeList->getConditionBuilder()->add('like_table.objectTypeID = ?', array($objectTypeObj->objectTypeID));
+ $likeList->getConditionBuilder()->add('like_table.objectID IN (?)', array($objectIDs));
+ $likeList->readObjectIDs();
+ $likeIDs = $likeList->getObjectIDs();
+
+ if (!empty($likeIDs)) {
+ // revoke activity points
+ UserActivityPointHandler::getInstance()->removeEvents('com.woltlab.wcf.like.activityPointEvent.receivedLikes', $likeIDs);
+
+ // delete likes
+ LikeEditor::deleteAll($likeIDs);
+ }
+
+ // delete like objects
+ if (!empty($likeObjectIDs)) {
+ LikeObjectEditor::deleteAll($likeObjectIDs);
+ }
+
+ // delete activity events
+ if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectTypeObj->objectType.'.recentActivityEvent')) {
+ UserActivityEventHandler::getInstance()->removeEvents($objectTypeObj->objectType.'.recentActivityEvent', $objectIDs);
+ }
+ }
+
+ /**
+ * Returns current like object status.
+ *
+ * @param wcf\data\like\object\LikeObject $likeObject
+ * @param wcf\data\user\User $user
+ * @return array
+ */
+ protected function loadLikeStatus(LikeObject $likeObject, User $user) {
+ $sql = "SELECT like_object.likes, like_object.dislikes, like_object.cumulativeLikes,
+ CASE WHEN like_table.likeValue IS NOT NULL THEN like_table.likeValue ELSE 0 END AS liked
+ FROM wcf".WCF_N."_like_object like_object
+ LEFT JOIN wcf".WCF_N."_like like_table
+ ON (like_table.objectTypeID = ".$likeObject->objectTypeID."
+ AND like_table.objectID = like_object.objectID
+ AND like_table.userID = ?)
+ WHERE like_object.likeObjectID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array(
+ $user->userID,
+ $likeObject->likeObjectID
+ ));
+ $row = $statement->fetchArray();
+
+ return $row;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\activity\point;
+use wcf\data\like\Like;
+use wcf\data\object\type\ObjectType;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\user\activity\point\IUserActivityPointObjectProcessor;
+use wcf\system\WCF;
+
+/**
+ * Updates events for received likes.
+ *
+ * @author Tim Düsterhus
+ * @copyright 2001-2012 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.like
+ * @subpackage system.user.activity.point
+ * @category Community Framework
+ */
+class ReceivedLikesUserActivityPointObjectProcessor implements IUserActivityPointObjectProcessor {
+ public $limit = 500;
+ public $objectType = null;
+
+ /**
+ * Creates a new instance of ReceivedLikesUserActivityPointObjectProcessor.
+ *
+ * @param wcf\data\object\type\ObjectType $objectType
+ */
+ public function __construct(ObjectType $objectType) {
+ $this->objectType = $objectType;
+ }
+
+ /**
+ * @see wcf\system\user\activity\point\IUserActivityPointObject::countRequests();
+ */
+ public function countRequests() {
+ $sql = "SELECT COUNT(*) AS count
+ FROM wcf".WCF_N."_like
+ WHERE likeValue = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array(Like::LIKE));
+ $row = $statement->fetchArray();
+
+ return ceil($row['count'] / $this->limit) + 1;
+ }
+
+ /**
+ * @see wcf\system\user\activity\point\IUserActivityPointObject::updateActivityPointEvents();
+ */
+ public function updateActivityPointEvents($request) {
+ if ($request == 0) {
+ // first request
+ $sql = "DELETE FROM wcf".WCF_N."_user_activity_point_event
+ WHERE objectTypeID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($this->objectType->objectTypeID));
+ }
+ else {
+ $sql = "SELECT likeID
+ FROM wcf".WCF_N."_like
+ WHERE likeValue = ?
+ ORDER BY likeID ASC";
+ $statement = WCF::getDB()->prepareStatement($sql, $this->limit, ($this->limit * ($request - 1)));
+ $statement->execute(array(Like::LIKE));
+ $likeIDs = array();
+ while ($row = $statement->fetchArray()) {
+ $likeIDs[] = $row['likeID'];
+ }
+
+ if (empty($likeIDs)) return;
+
+ $conditionBuilder = new PreparedStatementConditionBuilder();
+ $conditionBuilder->add("objectTypeID = ?", array($this->objectType->objectTypeID));
+ $conditionBuilder->add("objectID IN (?)", array($likeIDs));
+
+ // avoid problems with duplicate keys, as likes may be created in the meantime
+ $sql = "DELETE FROM wcf".WCF_N."_user_activity_point_event
+ ".$conditionBuilder;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute($conditionBuilder->getParameters());
+
+ $conditionBuilder = new PreparedStatementConditionBuilder();
+ $conditionBuilder->add("likeID IN (?)", array($likeIDs));
+ // use INSERT … SELECT as this makes bulk updating easier
+ $sql = "INSERT INTO wcf".WCF_N."_user_activity_point_event
+ (userID, objectTypeID, objectID, additionalData)
+ SELECT objectUserID AS userID,
+ ?,
+ likeID AS objectID,
+ ?
+ FROM wcf".WCF_N."_like
+ ".$conditionBuilder;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array_merge(array($this->objectType->objectTypeID, serialize(array())), $conditionBuilder->getParameters()));
+ }
+ }
+}
--- /dev/null
+.likeButton.active {
+ .icon {
+ color: rgba(0, 153, 0, 1);
+ }
+}
+
+.dislikeButton.active {
+ .icon {
+ color: rgba(204, 0, 0, 1);
+ }
+}
\ No newline at end of file
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="UTF-8"?>6
<language xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/language.xsd" languagecode="de" languagename="Deutsch" countrycode="de">
<category name="wcf.acl">
<item name="wcf.acl.option.deny"><![CDATA[Verweigern]]></item>
<item name="wcf.acp.group.option.user.profile.renamePeriod.description"><![CDATA[Zeitraum nach dem Mitglieder dieser Benutzergruppe ihren Benutzernamen ändern können. [Zeit in Tagen]]]></item>
<item name="wcf.acp.group.option.user.profile.cannotBeIgnored"><![CDATA[Kann nicht ignoriert werden]]></item>
<item name="wcf.acp.group.option.mod.general.canUseModeration"><![CDATA[Kann Moderation benutzen]]></item>
+ <item name="wcf.acp.group.option.category.user.like"><![CDATA[Like-System]]></item>
+ <item name="wcf.acp.group.option.user.like.canViewLike"><![CDATA[Kann vergebene Likes sehen]]></item>
+ <item name="wcf.acp.group.option.user.like.canLike"><![CDATA[Kann Inhalte liken]]></item>
</category>
<category name="wcf.acp.index">
<item name="wcf.acp.option.user_cleanup_profile_visitor_lifetime.description"><![CDATA[Zeitraum nach dem Profil-Besucher automatisch verworfen werden. [Zeitraum in Tagen]]]></item>
<item name="wcf.acp.option.recent_activity_items"><![CDATA[Anzahl Einträge]]></item>
<item name="wcf.acp.option.recent_activity_sidebar_items"><![CDATA[Anzahl Einträge]]></item>
+ <item name="wcf.acp.option.category.message.general.likes"><![CDATA[Like-System]]></item>
+ <item name="wcf.acp.option.module_like"><![CDATA[Like-System]]></item>
+ <item name="wcf.acp.option.like_allow_for_own_content"><![CDATA[Benutzer können eigene Inhalte liken]]></item>
+ <item name="wcf.acp.option.like_enable_dislike"><![CDATA[Benutzer können Inhalte disliken]]></item>
+ <item name="wcf.acp.option.like_show_summary"><![CDATA[Zusammenfassung anzeigen]]></item>
</category>
<category name="wcf.acp.package">
<item name="wcf.dashboard.objectType.com.woltlab.wcf.user.MembersListPage"><![CDATA[Mitgliederliste]]></item>
<item name="wcf.dashboard.box.mostActiveMembers.points"><![CDATA[{#$activeMember->activityPoints} Punkt{if $activeMember->activityPoints != 1}e{/if}]]></item>
<item name="wcf.dashboard.box.com.woltlab.wcf.user.recentActivity"><![CDATA[Letzte Aktivität]]></item>
+ <item name="wcf.dashboard.box.com.woltlab.wcf.like.mostLikedMembers"><![CDATA[Mitglieder mit den meisten Likes]]></item>
+ <item name="wcf.dashboard.box.mostLikedMembers.likes"><![CDATA[{#$likedMember->likesReceived} Like{if $likedMember->likesReceived != 1}s{/if}]]></item>
</category>
<category name="wcf.date">
<item name="wcf.imageViewer.previous"><![CDATA[Vorheriges Bild]]></item>
</category>
+ <category name="wcf.like">
+ <item name="wcf.like.cumulativeLikes"><![CDATA[Likes]]></item>
+ <item name="wcf.like.tooltip"><![CDATA[{if $likes}{#$likes} Like{if $likes != 1}s{/if}{if $dislikes}, {/if}{/if}{if $dislikes}{#$dislikes} Dislike{if $dislikes != 1}s{/if}{/if}]]></item>
+ <item name="wcf.like.jsTooltip"><![CDATA[($likes > 0 ? ($likes + " Like" + ($likes != 1 ? "s" : "") + ($dislikes > 0 ? ", " : "")) : "") + ($dislikes > 0 ? ($dislikes + " Dislike" + ($dislikes != 1 ? "s" : "")) : "")]]></item>
+ <item name="wcf.like.button.like"><![CDATA[Gefällt mir]]></item>
+ <item name="wcf.like.button.dislike"><![CDATA[Gefällt mir nicht]]></item>
+ <item name="wcf.like.likesReceived"><![CDATA[Erhaltene Likes]]></item>
+ <item name="wcf.like.summary"><![CDATA[$userArray.join(", ") + ($others > 0 ? (" und " + $others + (" weitere" + ($others > 1 ? "n" : "m"))) : "") + " gefällt das."]]></item>
+ <item name="wcf.like.details"><![CDATA[Details]]></item>
+ <item name="wcf.like.details.like"><![CDATA[Likes]]></item>
+ <item name="wcf.like.details.dislike"><![CDATA[Dislikes]]></item>
+ </category>
+
<category name="wcf.message">
<item name="wcf.message.bbcode.code.copy"><![CDATA[Inhalt kopieren]]></item>
<item name="wcf.message.quote.insertAllQuotes"><![CDATA[Alle Zitate einfügen]]></item>
<item name="wcf.user.login.3rdParty"><![CDATA[Anmeldung über Drittanbieter]]></item>
<item name="wcf.user.search.results"><![CDATA[Suchergebnisse]]></item>
<item name="wcf.user.lastActivityTime"><![CDATA[Letzte Aktivität]]></item>
+ <item name="wcf.user.activityPoint.objectType.com.woltlab.wcf.like.activityPointEvent.receivedLikes"><![CDATA[Erhaltene Likes]]></item>
</category>
<category name="wcf.user.menu">
<item name="wcf.acp.group.option.user.profile.renamePeriod.description"><![CDATA[Minimum period until members may rename themselves. [time in days]]]></item>
<item name="wcf.acp.group.option.user.profile.cannotBeIgnored"><![CDATA[Can not be ignored]]></item>
<item name="wcf.acp.group.option.mod.general.canUseModeration"><![CDATA[Can access moderation]]></item>
+ <item name="wcf.acp.group.option.category.user.like"><![CDATA[Like System]]></item>
+ <item name="wcf.acp.group.option.user.like.canViewLike"><![CDATA[Can view likes]]></item>
+ <item name="wcf.acp.group.option.user.like.canLike"><![CDATA[Can like content]]></item>
</category>
<category name="wcf.acp.index">
<item name="wcf.acp.option.user_cleanup_profile_visitor_lifetime.description"><![CDATA[Profile visitors will be discarded if they are older than the given period. [time in days]]]></item>
<item name="wcf.acp.option.recent_activity_items"><![CDATA[Number of Entries]]></item>
<item name="wcf.acp.option.recent_activity_sidebar_items"><![CDATA[Number of Entries]]></item>
+ <item name="wcf.acp.option.category.message.general.likes"><![CDATA[Like System]]></item>
+ <item name="wcf.acp.option.module_like"><![CDATA[Like System]]></item>
+ <item name="wcf.acp.option.like_allow_for_own_content"><![CDATA[Users can like their own content]]></item>
+ <item name="wcf.acp.option.like_enable_dislike"><![CDATA[Users can dislike content]]></item>
+ <item name="wcf.acp.option.like_show_summary"><![CDATA[Show like summary]]></item>
</category>
<category name="wcf.acp.package">
<item name="wcf.dashboard.objectType.com.woltlab.wcf.user.MembersListPage"><![CDATA[Members List]]></item>
<item name="wcf.dashboard.box.mostActiveMembers.points"><![CDATA[{#$activeMember->activityPoints} Point{if $activeMember->activityPoints != 1}s{/if}]]></item>
<item name="wcf.dashboard.box.com.woltlab.wcf.user.recentActivity"><![CDATA[Recent Activity]]></item>
+ <item name="wcf.dashboard.box.com.woltlab.wcf.like.mostLikedMembers"><![CDATA[Most Liked Members]]></item>
+ <item name="wcf.dashboard.box.mostLikedMembers.likes"><![CDATA[{#$likedMember->likesReceived} Like{if $likedMember->likesReceived != 1}s{/if}]]></item>
</category>
<category name="wcf.date">
<item name="wcf.imageViewer.next"><![CDATA[Next Image]]></item>
<item name="wcf.imageViewer.previous"><![CDATA[Previous Image]]></item>
</category>
-
+
+ <category name="wcf.like">
+ <item name="wcf.like.cumulativeLikes"><![CDATA[Likes]]></item>
+ <item name="wcf.like.tooltip"><![CDATA[{if $likes}{#$likes} Like{if $likes != 1}s{/if}{if $dislikes}, {/if}{/if}{if $dislikes}{#$dislikes} Dislike{if $dislikes != 1}s{/if}{/if}]]></item>
+ <item name="wcf.like.jsTooltip"><![CDATA[($likes > 0 ? ($likes + " Like" + ($likes != 1 ? "s" : "") + ($dislikes > 0 ? ", " : "")) : "") + ($dislikes > 0 ? ($dislikes + " Dislike" + ($dislikes != 1 ? "s" : "")) : "")]]></item>
+ <item name="wcf.like.button.like"><![CDATA[Like]]></item>
+ <item name="wcf.like.button.dislike"><![CDATA[Dislike]]></item>
+ <item name="wcf.like.likesReceived"><![CDATA[Likes Received]]></item>
+ <item name="wcf.like.summary"><![CDATA[$userArray.join(", ") + ($others > 0 ? (" and " + $others + (" other" + ($others > 1 ? "s" : ""))) : "") + " like" + ($userArray.length == 1 ? "s" : "") + " this."]]></item>
+ <item name="wcf.like.details"><![CDATA[Details]]></item>
+ <item name="wcf.like.details.like"><![CDATA[Likes]]></item>
+ <item name="wcf.like.details.dislike"><![CDATA[Dislikes]]></item>
+ </category>
+
<category name="wcf.message">
<item name="wcf.message.bbcode.code.copy"><![CDATA[Copy Contents]]></item>
<item name="wcf.message.quote.insertAllQuotes"><![CDATA[Insert All Quotes]]></item>
<item name="wcf.user.login.3rdParty"><![CDATA[Third-Party Login]]></item>
<item name="wcf.user.search.results"><![CDATA[Search Results]]></item>
<item name="wcf.user.lastActivityTime"><![CDATA[Last Activity]]></item>
+ <item name="wcf.user.activityPoint.objectType.com.woltlab.wcf.like.activityPointEvent.receivedLikes"><![CDATA[Likes Received]]></item>
</category>
<category name="wcf.user.menu">
isDisabled TINYINT(1) NOT NULL DEFAULT 0
);
+DROP TABLE IF EXISTS wcf1_like;
+CREATE TABLE wcf1_like (
+ likeID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ objectID INT(10) NOT NULL DEFAULT 0,
+ objectTypeID INT(10) NOT NULL,
+ objectUserID INT(10),
+ userID INT(10) NOT NULL,
+ time INT(10) NOT NULL DEFAULT 0,
+ likeValue TINYINT(1) NOT NULL DEFAULT 1,
+ UNIQUE KEY (objectTypeID, objectID, userID)
+);
+
+DROP TABLE IF EXISTS wcf1_like_object;
+CREATE TABLE wcf1_like_object (
+ likeObjectID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ objectTypeID INT(10) NOT NULL,
+ objectID INT(10) NOT NULL DEFAULT 0,
+ objectUserID INT(10),
+ likes MEDIUMINT(7) NOT NULL DEFAULT 0,
+ dislikes MEDIUMINT(7) NOT NULL DEFAULT 0,
+ cumulativeLikes MEDIUMINT(7) NOT NULL DEFAULT 0,
+ cachedUsers TEXT,
+ UNIQUE KEY (objectTypeID, objectID)
+);
+
DROP TABLE IF EXISTS wcf1_moderation_queue;
CREATE TABLE wcf1_moderation_queue (
queueID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
activityPoints INT(10) NOT NULL DEFAULT 0,
notificationMailToken VARCHAR(20) NOT NULL DEFAULT '',
authData VARCHAR(255) NOT NULL DEFAULT '',
+ likesReceived MEDIUMINT(7) NOT NULL DEFAULT 0,
KEY username (username),
KEY registrationDate (registrationDate),
KEY styleID (styleID),
KEY activationCode (activationCode),
KEY registrationData (registrationIpAddress, registrationDate),
- KEY activityPoints (activityPoints)
+ KEY activityPoints (activityPoints),
+ KEY likesReceived (likesReceived)
);
DROP TABLE IF EXISTS wcf1_user_activity_event;
ALTER TABLE wcf1_moderation_queue_to_user ADD FOREIGN KEY (queueID) REFERENCES wcf1_moderation_queue (queueID) ON DELETE CASCADE;
ALTER TABLE wcf1_moderation_queue_to_user ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
+ALTER TABLE wcf1_like ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
+ALTER TABLE wcf1_like ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
+ALTER TABLE wcf1_like ADD FOREIGN KEY (objectUserID) REFERENCES wcf1_user (userID) ON DELETE SET NULL;
+
+ALTER TABLE wcf1_like_object ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
+ALTER TABLE wcf1_like_object ADD FOREIGN KEY (objectUserID) REFERENCES wcf1_user (userID) ON DELETE SET NULL;
+
/* default inserts */
-- default user groups