<classname>wcf\system\user\activity\point\ReceivedLikesUserActivityPointObjectProcessor</classname>
</type>
<!-- /activity points -->
+
+ <type>
+ <name>com.woltlab.wcf.user.profileComment</name>
+ <definitionname>com.woltlab.wcf.comment.commentableContent</definitionname>
+ <classname>wcf\system\comment\manager\UserProfileCommentManager</classname>
+ </type>
+
+ <type>
+ <name>com.woltlab.wcf.comment</name>
+ <definitionname>com.woltlab.wcf.like.likeableObject</definitionname>
+ <classname>wcf\data\comment\LikeableCommentProvider</classname>
+ </type>
+
+ <type>
+ <name>com.woltlab.wcf.comment.response</name>
+ <definitionname>com.woltlab.wcf.like.likeableObject</definitionname>
+ <classname>wcf\data\comment\response\LikeableCommentResponseProvider</classname>
+ </type>
+
+ <type>
+ <name>com.woltlab.wcf.user.profileComment.recentActivityEvent</name>
+ <definitionname>com.woltlab.wcf.user.recentActivityEvent</definitionname>
+ <classname>wcf\system\user\activity\event\ProfileCommentUserActivityEvent</classname>
+ </type>
+
+ <type>
+ <name>com.woltlab.wcf.user.profileComment.response.recentActivityEvent</name>
+ <definitionname>com.woltlab.wcf.user.recentActivityEvent</definitionname>
+ <classname>wcf\system\user\activity\event\ProfileCommentResponseUserActivityEvent</classname>
+ </type>
+
+ <type>
+ <name>com.woltlab.wcf.user.profileComment.notification</name>
+ <definitionname>com.woltlab.wcf.notification.objectType</definitionname>
+ <classname>wcf\system\user\notification\object\type\UserProfileCommentUserNotificationObjectType</classname>
+ <category>com.woltlab.wcf.user</category>
+ </type>
+ <type>
+ <name>com.woltlab.wcf.user.profileComment.response.notification</name>
+ <definitionname>com.woltlab.wcf.notification.objectType</definitionname>
+ <classname>wcf\system\user\notification\object\type\UserProfileCommentResponseUserNotificationObjectType</classname>
+ <category>com.woltlab.wcf.user</category>
+ </type>
+
+ <!-- moderation -->
+ <type>
+ <name>com.woltlab.wcf.comment.comment</name>
+ <definitionname>com.woltlab.wcf.moderation.report</definitionname>
+ <classname>wcf\system\moderation\queue\report\CommentCommentModerationQueueReportHandler</classname>
+ </type>
+
+ <type>
+ <name>com.woltlab.wcf.comment.response</name>
+ <definitionname>com.woltlab.wcf.moderation.report</definitionname>
+ <classname>wcf\system\moderation\queue\report\CommentResponseModerationQueueReportHandler</classname>
+ </type>
+ <!-- /moderation -->
</import>
</data>
\ No newline at end of file
<name>com.woltlab.wcf.like.likeableObject</name>
<interfacename>wcf\data\like\ILikeObjectTypeProvider</interfacename>
</definition>
+
+ <definition>
+ <name>com.woltlab.wcf.comment.commentableContent</name>
+ <interfacename>wcf\system\comment\manager\ICommentManager</interfacename>
+ </definition>
</import>
</data>
<defaultvalue>1</defaultvalue>
</option>
+ <option name="module_user_profile_wall">
+ <categoryname>module.user</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>1</defaultvalue>
+ </option>
+
<!-- general.page -->
<option name="page_title">
<categoryname>general.page</categoryname>
--- /dev/null
+<script type="text/javascript" src="{@$__wcf->getPath()}js/WCF.Comment{if !ENABLE_DEBUG_MODE}.min{/if}.js"></script>
+<script type="text/javascript" src="{@$__wcf->getPath()}js/WCF.Moderation{if !ENABLE_DEBUG_MODE}.min{/if}.js"></script>
+<script type="text/javascript">
+ //<![CDATA[
+ $(function() {
+ WCF.Language.addObject({
+ 'wcf.comment.add': '{lang}wcf.comment.add{/lang}',
+ 'wcf.comment.button.response.add': '{lang}wcf.comment.button.response.add{/lang}',
+ 'wcf.comment.delete.confirmMessage': '{lang}wcf.comment.delete.confirmMessage{/lang}',
+ 'wcf.comment.description': '{lang}wcf.comment.description{/lang}',
+ 'wcf.comment.more': '{lang}wcf.comment.more{/lang}',
+ 'wcf.comment.response.add': '{lang}wcf.comment.response.add{/lang}',
+ 'wcf.comment.response.more': '{lang}wcf.comment.response.more{/lang}',
+ 'wcf.moderation.report.reportContent': '{lang}wcf.moderation.report.reportContent{/lang}',
+ 'wcf.moderation.report.success': '{lang}wcf.moderation.report.success{/lang}'
+ });
+
+ new {if $commentHandlerClass|isset}{@$commentHandlerClass}{else}WCF.Comment.Handler{/if}('{$commentContainerID}', '{@$__wcf->getUserProfileHandler()->getAvatar()->getImageTag(32)}');
+ {if MODULE_LIKE && $__wcf->getSession()->getPermission('user.like.canViewLike')}
+ new WCF.Comment.Like({if $__wcf->getUser()->userID && $__wcf->getSession()->getPermission('user.like.canLike')}1{else}0{/if}, {@LIKE_ENABLE_DISLIKE}, false, {@LIKE_ALLOW_FOR_OWN_CONTENT});
+ new WCF.Comment.Response.Like({if $__wcf->getUser()->userID && $__wcf->getSession()->getPermission('user.like.canLike')}1{else}0{/if}, {@LIKE_ENABLE_DISLIKE}, false, {@LIKE_ALLOW_FOR_OWN_CONTENT});
+ {/if}
+
+ new WCF.Moderation.Report.Content('com.woltlab.wcf.comment.comment', '.jsReportCommentComment');
+ new WCF.Moderation.Report.Content('com.woltlab.wcf.comment.response', '.jsReportCommentResponse');
+ });
+ //]]>
+</script>
+
+{event name='javascriptInclude'}
--- /dev/null
+{foreach from=$commentList item=comment}
+ <li class="comment jsComment" data-comment-id="{@$comment->commentID}" data-object-type="com.woltlab.wcf.comment" data-like-liked="{if $likeData[comment][$comment->commentID]|isset}{@$likeData[comment][$comment->commentID]->liked}{/if}" data-like-likes="{if $likeData[comment][$comment->commentID]|isset}{@$likeData[comment][$comment->commentID]->likes}{else}0{/if}" data-like-dislikes="{if $likeData[comment][$comment->commentID]|isset}{@$likeData[comment][$comment->commentID]->dislikes}{else}0{/if}" data-like-users='{if $likeData[comment][$comment->commentID]|isset}{ {implode from=$likeData[comment][$comment->commentID]->getUsers() item=likeUser}"{@$likeUser->userID}": { "username": "{$likeUser->username|encodeJSON}" }{/implode} }{else}{ }{/if}' data-can-edit="{if $comment->isEditable()}true{else}false{/if}" data-can-delete="{if $comment->isDeletable()}true{else}false{/if}" data-responses="{@$comment->responses}" data-last-response-time="{@$comment->getLastResponseTime()}" data-user-id="{@$comment->userID}">
+ <div class="box32">
+ <a href="{link controller='User' object=$comment->getUserProfile()}{/link}" title="{$comment->getUserProfile()->username}" class="framed">
+ {@$comment->getUserProfile()->getAvatar()->getImageTag(32)}
+ </a>
+
+ <div>
+ <div class="commentContent">
+ <div class="containerHeadline">
+ <h3><a href="{link controller='User' object=$comment->getUserProfile()}{/link}">{$comment->username}</a><small> - {@$comment->time|time}</small></h3>
+ </div>
+
+ <p class="userMessage">{@$comment->getFormattedMessage()}</p>
+
+ <nav class="jsMobileNavigation buttonGroupNavigation">
+ <ul class="commentOptions">
+ <li class="jsReportCommentComment jsOnly" data-object-id="{@$comment->commentID}"><a title="{lang}wcf.moderation.report.reportContent{/lang}" class="jsTooltip"><span class="icon icon16 icon-warning-sign"></span> <span class="invisible">{lang}wcf.moderation.report.reportContent{/lang}</span></a></li>
+
+ {event name='commentOptions'}
+ </ul>
+ </nav>
+ </div>
+
+ <ul data-responses="{@$comment->responses}" class="commentResponseList">
+ {if $comment|count}
+ {include file='commentResponseList' responseList=$comment}
+ {/if}
+ </ul>
+ </div>
+ </div>
+ </li>
+{/foreach}
--- /dev/null
+{foreach from=$responseList item=response}
+ <li class="commentResponse jsCommentResponse" data-response-id="{@$response->responseID}" data-object-type="com.woltlab.wcf.comment.response" data-like-liked="{if $likeData[response][$response->responseID]|isset}{@$likeData[response][$response->responseID]->liked}{/if}" data-like-likes="{if $likeData[response][$response->responseID]|isset}{@$likeData[response][$response->responseID]->likes}{else}0{/if}" data-like-dislikes="{if $likeData[response][$response->responseID]|isset}{@$likeData[response][$response->responseID]->dislikes}{else}0{/if}" data-like-users='{if $likeData[response][$response->responseID]|isset}{ {implode from=$likeData[response][$response->responseID]->getUsers() item=likeUser}"{@$likeUser->userID}": { "username": "{$likeUser->username|encodeJSON}" }{/implode} }{else}{ }{/if}' data-can-edit="{if $response->isEditable()}true{else}false{/if}" data-can-delete="{if $response->isDeletable()}true{else}false{/if}" data-user-id="{@$response->userID}">
+ <div class="box32">
+ <a href="{link controller='User' object=$response->getUserProfile()}{/link}" title="{$response->getUserProfile()->username}" class="framed">
+ {if $response->getUserProfile()->getAvatar()}
+ {@$response->getUserProfile()->getAvatar()->getImageTag(32)}
+ {/if}
+ </a>
+
+ <div class="commentContent commentResponseContent">
+ <div class="containerHeadline">
+ <h3><a href="{link controller='User' object=$response->getUserProfile()}{/link}">{$response->username}</a><small> - {@$response->time|time}</small></h3>
+ </div>
+
+ <p class="userMessage">{@$response->getFormattedMessage()}</p>
+
+ <nav class="jsMobileNavigation buttonGroupNavigation">
+ <ul class="commentOptions">
+ <li class="jsReportCommentResponse jsOnly" data-object-id="{@$response->responseID}"><a title="{lang}wcf.moderation.report.reportContent{/lang}" class="jsTooltip"><span class="icon icon16 icon-warning-sign"></span> <span class="invisible">{lang}wcf.moderation.report.reportContent{/lang}</span></a></li>
+
+ {event name='commentOptions'}
+ </ul>
+ </nav>
+ </div>
+ </div>
+ </li>
+{/foreach}
--- /dev/null
+<article class="message messageReduced">
+ <div>
+ <section class="messageContent">
+ <div>
+ <header class="messageHeader">
+ <div class="box32">
+ <a href="{link controller='User' object=$message->getUserProfile()->getDecoratedObject()}{/link}" class="framed">{@$message->getUserProfile()->getAvatar()->getImageTag(32)}</a>
+
+ <div class="messageHeadline">
+ <h1><a href="{@$message->getLink()}">{$message->getTitle()}</a></h1>
+ <p>
+ <span class="username"><a href="{link controller='User' object=$message->getUserProfile()->getDecoratedObject()}{/link}">{$message->getUsername()}</a></span>
+ {@$message->getTime()|time}
+ </p>
+ </div>
+ </div>
+ </header>
+
+ <div class="messageBody">
+ <div>
+ <div class="messageText">
+ {@$message->getFormattedMessage()}
+ </div>
+ </div>
+ </div>
+ </div>
+ </section>
+ </div>
+</article>
\ No newline at end of file
--- /dev/null
+{include file='__commentJavaScript' commentContainerID='userProfileCommentList'}
+
+{if $commentCanAdd}
+ <ul id="userProfileCommentList" class="commentList containerList" data-can-add="true" data-object-id="{@$userID}" data-object-type-id="{@$commentObjectTypeID}" data-comments="{@$commentList->countObjects()}" data-last-comment-time="{@$lastCommentTime}">
+ {include file='commentList'}
+ </ul>
+{else}
+ {hascontent}
+ <ul id="userProfileCommentList" class="commentList containerList" data-can-add="false" data-object-id="{@$userID}" data-object-type-id="{@$commentObjectTypeID}" data-comments="{@$commentList->countObjects()}" data-last-comment-time="{@$lastCommentTime}">
+ {content}
+ {include file='commentList'}
+ {/content}
+ </ul>
+ {hascontentelse}
+ <div class="containerPadding">
+ {lang}wcf.user.profile.content.wall.noEntries{/lang}
+ </div>
+ {/hascontent}
+{/if}
\ No newline at end of file
<category name="user.profile">
<parent>user</parent>
</category>
+ <category name="user.profileComment">
+ <parent>user.profile</parent>
+ </category>
<category name="user.signature">
<parent>user.profile</parent>
<category name="mod.general">
<parent>mod</parent>
</category>
+ <category name="mod.profileComment">
+ <parent>mod.general</parent>
+ </category>
<category name="admin"></category>
<category name="admin.general">
<optiontype>boolean</optiontype>
<defaultvalue>1</defaultvalue>
</option>
+
+ <!-- mod.general -->
+ <option name="mod.profileComment.canEditComment">
+ <categoryname>mod.profileComment</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>0</defaultvalue>
+ <admindefaultvalue>1</admindefaultvalue>
+ </option>
+ <option name="mod.profileComment.canDeleteComment">
+ <categoryname>mod.profileComment</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>0</defaultvalue>
+ <admindefaultvalue>1</admindefaultvalue>
+ </option>
+ <option name="mod.profileComment.canModerateComment">
+ <categoryname>mod.profileComment</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>0</defaultvalue>
+ <admindefaultvalue>1</admindefaultvalue>
+ </option>
+ <!-- /mod.general -->
+
+ <!-- user.profileComment -->
+ <option name="user.profileComment.canAddComment">
+ <categoryname>user.profileComment</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>1</defaultvalue>
+ </option>
+
+ <option name="user.profileComment.canEditComment">
+ <categoryname>user.profileComment</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>1</defaultvalue>
+ </option>
+
+ <option name="user.profileComment.canDeleteComment">
+ <categoryname>user.profileComment</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>0</defaultvalue>
+ <admindefaultvalue>1</admindefaultvalue>
+ </option>
+ <!-- /user.profileComment -->
</options>
</import>
</data>
<objecttype>com.woltlab.wcf.user.follow</objecttype>
<classname>wcf\system\user\notification\event\UserFollowFollowingUserNotificationEvent</classname>
</event>
+
+ <event>
+ <name>comment</name>
+ <objecttype>com.woltlab.wcf.user.profileComment.notification</objecttype>
+ <classname>wcf\system\user\notification\event\UserProfileCommentUserNotificationEvent</classname>
+ <preset>1</preset>
+ </event>
+ <event>
+ <name>commentResponse</name>
+ <objecttype>com.woltlab.wcf.user.profileComment.response.notification</objecttype>
+ <classname>wcf\system\user\notification\event\UserProfileCommentResponseUserNotificationEvent</classname>
+ <preset>1</preset>
+ </event>
+ <event>
+ <name>commentResponseOwner</name>
+ <objecttype>com.woltlab.wcf.user.profileComment.response.notification</objecttype>
+ <classname>wcf\system\user\notification\event\UserProfileCommentResponseOwnerUserNotificationEvent</classname>
+ <preset>1</preset>
+ </event>
</import>
</data>
<defaultvalue>1</defaultvalue>
</option>
<!-- /settings -->
+
+ <option name="canWriteProfileComments">
+ <categoryname>settings.privacy.messaging</categoryname>
+ <optiontype>select</optiontype>
+ <editable>3</editable>
+ <selectoptions><![CDATA[1:wcf.user.access.registered
+2:wcf.user.access.following
+3:wcf.user.access.nobody]]></selectoptions>
+ <defaultvalue>1</defaultvalue>
+ </option>
</options>
</import>
</data>
<classname>wcf\system\menu\user\profile\content\RecentActivityUserProfileMenuContent</classname>
<showorder>1</showorder>
</userprofilemenuitem>
+
+ <userprofilemenuitem name="wall">
+ <classname>wcf\system\menu\user\profile\content\CommentUserProfileMenuContent</classname>
+ <showorder>1</showorder>
+ <options>module_user_profile_wall</options>
+ </userprofilemenuitem>
</import>
</data>
--- /dev/null
+/**
+ * Namespace for comments
+ */
+WCF.Comment = {};
+
+/**
+ * Comment 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.Comment.Handler = Class.extend({
+ /**
+ * input element to add a comment
+ * @var jQuery
+ */
+ _commentAdd: null,
+
+ /**
+ * list of comment objects
+ * @var object
+ */
+ _comments: { },
+
+ /**
+ * comment container object
+ * @var jQuery
+ */
+ _container: null,
+
+ /**
+ * container id
+ * @var string
+ */
+ _containerID: '',
+
+ /**
+ * number of currently displayed comments
+ * @var integer
+ */
+ _displayedComments: 0,
+
+ /**
+ * button to load next comments
+ * @var jQuery
+ */
+ _loadNextComments: null,
+
+ /**
+ * buttons to load next responses per comment
+ * @var object
+ */
+ _loadNextResponses: { },
+
+ /**
+ * action proxy
+ * @var WCF.Action.Proxy
+ */
+ _proxy: null,
+
+ /**
+ * list of response objects
+ * @var object
+ */
+ _responses: { },
+
+ /**
+ * user's avatar
+ * @var string
+ */
+ _userAvatar: '',
+
+ /**
+ * Initializes the WCF.Comment.Handler class.
+ *
+ * @param string containerID
+ * @param string userAvatar
+ */
+ init: function(containerID, userAvatar) {
+ this._commentAdd = null;
+ this._comments = { };
+ this._containerID = containerID;
+ this._displayedComments = 0;
+ this._loadNextComments = null;
+ this._loadNextResponses = { };
+ this._responses = { };
+ this._userAvatar = userAvatar;
+
+ this._container = $('#' + $.wcfEscapeID(this._containerID));
+ if (!this._container.length) {
+ console.debug("[WCF.Comment.Handler] Unable to find container identified by '" + this._containerID + "'");
+ }
+
+ this._proxy = new WCF.Action.Proxy({
+ success: $.proxy(this._success, this)
+ });
+
+ WCF.DOMNodeInsertedHandler.enable();
+
+ this._initComments();
+ this._initResponses();
+
+ // add new comment
+ if (this._container.data('canAdd')) {
+ this._initAddComment();
+ }
+
+ WCF.DOMNodeInsertedHandler.disable();
+ WCF.DOMNodeInsertedHandler.addCallback('WCF.Comment.Handler', $.proxy(this._domNodeInserted, this));
+ },
+
+ /**
+ * Shows a button to load next comments.
+ */
+ _handleLoadNextComments: function() {
+ if (this._displayedComments < this._container.data('comments')) {
+ if (this._loadNextComments === null) {
+ this._loadNextComments = $('<li class="commentLoadNext"><button class="buttonPrimary small">' + WCF.Language.get('wcf.comment.more') + '</button></li>').appendTo(this._container);
+ this._loadNextComments.children('button').click($.proxy(this._loadComments, this));
+ }
+
+ this._loadNextComments.children('button').enable();
+ }
+ else if (this._loadNextComments !== null) {
+ this._loadNextComments.hide();
+ }
+ },
+
+ /**
+ * Shows a button to load next responses per comment.
+ *
+ * @param integer commentID
+ */
+ _handleLoadNextResponses: function(commentID) {
+ var $comment = this._comments[commentID];
+ $comment.data('displayedResponses', $comment.find('ul.commentResponseList > li').length);
+
+ if ($comment.data('displayedResponses') < $comment.data('responses')) {
+ if (this._loadNextResponses[commentID] === undefined) {
+ this._loadNextResponses[commentID] = $('<div class="responseLoadNext"><button class="small">' + WCF.Language.get('wcf.comment.response.more') + '</button></div>').insertAfter($comment.find('ul.commentResponseList'));
+ this._loadNextResponses[commentID].children('button').data('commentID', commentID).click($.proxy(this._loadResponses, this));
+ }
+
+ this._loadNextResponses[commentID].children('button').enable();
+ }
+ else if (this._loadNextResponses[commentID] !== undefined) {
+ this._loadNextResponses[commentID].hide();
+ }
+ },
+
+ /**
+ * Loads next comments.
+ */
+ _loadComments: function() {
+ this._loadNextComments.children('button').disable();
+
+ this._proxy.setOption('data', {
+ actionName: 'loadComments',
+ className: 'wcf\\data\\comment\\CommentAction',
+ parameters: {
+ data: {
+ objectID: this._container.data('objectID'),
+ objectTypeID: this._container.data('objectTypeID'),
+ lastCommentTime: this._container.data('lastCommentTime')
+ }
+ }
+ });
+ this._proxy.sendRequest();
+ },
+
+ /**
+ * Loads next responses for given comment.
+ *
+ * @param object event
+ */
+ _loadResponses: function(event) {
+ var $button = $(event.currentTarget).disable();
+ var $commentID = $button.data('commentID');
+
+ this._proxy.setOption('data', {
+ actionName: 'loadResponses',
+ className: 'wcf\\data\\comment\\response\\CommentResponseAction',
+ parameters: {
+ data: {
+ commentID: $commentID,
+ lastResponseTime: this._comments[$commentID].data('lastResponseTime')
+ }
+ }
+ });
+ this._proxy.sendRequest();
+ },
+
+ /**
+ * Handles DOMNodeInserted events.
+ */
+ _domNodeInserted: function() {
+ this._initComments();
+ this._initResponses();
+ },
+
+ /**
+ * Initializes available comments.
+ */
+ _initComments: function() {
+ var self = this;
+ var $loadedComments = false;
+ this._container.find('.jsComment').each(function(index, comment) {
+ var $comment = $(comment).removeClass('jsComment');
+ var $commentID = $comment.data('commentID');
+ self._comments[$commentID] = $comment;
+
+ self._initComment($commentID, $comment);
+ self._displayedComments++;
+
+ $loadedComments = true;
+ self._handleLoadNextResponses($commentID);
+ });
+
+ if ($loadedComments) {
+ this._handleLoadNextComments();
+ }
+ },
+
+ /**
+ * Initializes a specific comment.
+ *
+ * @param integer commentID
+ * @param jQuery comment
+ */
+ _initComment: function(commentID, comment) {
+ if (this._container.data('canAdd')) {
+ this._initAddResponse(commentID, comment);
+ }
+
+ if (comment.data('canEdit')) {
+ var $editButton = $('<li><a class="jsTooltip" title="' + WCF.Language.get('wcf.global.button.edit') + '"><span class="icon icon16 icon-pencil" /> <span class="invisible">' + WCF.Language.get('wcf.global.button.edit') + '</span></a></li>');
+ $editButton.data('commentID', commentID).appendTo(comment.find('ul.commentOptions:eq(0)')).click($.proxy(this._prepareEdit, this));
+ }
+
+ if (comment.data('canDelete')) {
+ var $deleteButton = $('<li><a class="jsTooltip" title="' + WCF.Language.get('wcf.global.button.delete') + '"><span class="icon icon16 icon-remove" /> <span class="invisible">' + WCF.Language.get('wcf.global.button.delete') + '</span></a></li>');
+ $deleteButton.data('commentID', commentID).appendTo(comment.find('ul.commentOptions:eq(0)')).click($.proxy(this._delete, this));
+ }
+ },
+
+ /**
+ * Initializes available responses.
+ */
+ _initResponses: function() {
+ var self = this;
+ this._container.find('.jsCommentResponse').each(function(index, response) {
+ var $response = $(response).removeClass('jsCommentResponse');
+ var $responseID = $response.data('responseID');
+ self._responses[$responseID] = $response;
+
+ self._initResponse($responseID, $response);
+ });
+ },
+
+ /**
+ * Initializes a specific response.
+ *
+ * @param integer responseID
+ * @param jQuery response
+ */
+ _initResponse: function(responseID, response) {
+ if (response.data('canEdit')) {
+ var $editButton = $('<li><a class="jsTooltip" title="' + WCF.Language.get('wcf.global.button.edit') + '"><span class="icon icon16 icon-pencil" /> <span class="invisible">' + WCF.Language.get('wcf.global.button.edit') + '</span></a></li>');
+
+ var self = this;
+ $editButton.data('responseID', responseID).appendTo(response.find('ul.commentOptions:eq(0)')).click(function(event) { self._prepareEdit(event, true); });
+ }
+
+ if (response.data('canDelete')) {
+ var $deleteButton = $('<li><a class="jsTooltip" title="' + WCF.Language.get('wcf.global.button.delete') + '"><span class="icon icon16 icon-remove" /> <span class="invisible">' + WCF.Language.get('wcf.global.button.delete') + '</span></a></li>');
+
+ var self = this;
+ $deleteButton.data('responseID', responseID).appendTo(response.find('ul.commentOptions:eq(0)')).click(function(event) { self._delete(event, true); });
+ }
+ },
+
+ /**
+ * Initializes the UI components to add a comment.
+ */
+ _initAddComment: function() {
+ // create UI
+ this._commentAdd = $('<li class="box32 jsCommentAdd"><span class="framed">' + this._userAvatar + '</span><div /></li>').prependTo(this._container);
+ var $inputContainer = this._commentAdd.children('div');
+ var $input = $('<input type="text" placeholder="' + WCF.Language.get('wcf.comment.add') + '" maxlength="65535" class="long" />').appendTo($inputContainer);
+ $('<small>' + WCF.Language.get('wcf.comment.description') + '</small>').appendTo($inputContainer);
+
+ $input.keyup($.proxy(this._keyUp, this));
+ },
+
+ /**
+ * Initializes the UI elements to add a response.
+ *
+ * @param integer commentID
+ * @param jQuery comment
+ */
+ _initAddResponse: function(commentID, comment) {
+ var $placeholder = $('<div class="commentResponseAdd jsCommentResponseAddPlaceholder"><a class="button small">' + WCF.Language.get('wcf.comment.button.response.add') + '</a></div>').insertBefore(comment.find('ul.commentResponseList'));
+ $placeholder.data('commentID', commentID).click($.proxy(this._showAddResponse, this));
+
+ var $listItem = $('<div class="box32 commentResponseAdd jsCommentResponseAdd"><span class="framed">' + this._userAvatar + '</span><div /></div>').hide().insertAfter($placeholder);
+ var $inputContainer = $listItem.children('div');
+ var $input = $('<input type="text" placeholder="' + WCF.Language.get('wcf.comment.response.add') + '" maxlength="65535" class="long" />').data('commentID', commentID).appendTo($inputContainer);
+ $('<small>' + WCF.Language.get('wcf.comment.description') + '</small>').appendTo($inputContainer);
+
+ var self = this;
+ $input.keyup(function(event) { self._keyUp(event, true); }).blur($.proxy(this._hideAddResponse, this));
+
+ comment.data('responsePlaceholder', $placeholder).data('responseInput', $listItem);
+ },
+
+ /**
+ * Prepares editing of a comment or response.
+ *
+ * @param object event
+ * @param boolean isResponse
+ */
+ _prepareEdit: function(event, isResponse) {
+ var $button = $(event.currentTarget);
+ var $data = {
+ objectID: this._container.data('objectID'),
+ objectTypeID: this._container.data('objectTypeID')
+ };
+
+ if (isResponse === true) {
+ $data.responseID = $button.data('responseID');
+ }
+ else {
+ $data.commentID = $button.data('commentID');
+ }
+
+ this._proxy.setOption('data', {
+ actionName: 'prepareEdit',
+ className: 'wcf\\data\\comment\\CommentAction',
+ parameters: {
+ data: $data
+ }
+ });
+ this._proxy.sendRequest();
+ },
+
+ /**
+ * Displays the UI elements to create a response.
+ *
+ * @param object event
+ */
+ _showAddResponse: function(event) {
+ var $commentID = $(event.currentTarget).data('commentID');
+ this._comments[$commentID].data('responsePlaceholder').hide();
+
+ var $responseInput = this._comments[$commentID].data('responseInput').show();
+ $responseInput.find('input').focus();
+ },
+
+ /**
+ * Hides the UI elements to create a response.
+ *
+ * @param object event
+ */
+ _hideAddResponse: function(event) {
+ var $input = $(event.currentTarget);
+ if ($.trim($input.val()) !== '') {
+ return;
+ }
+
+ // delay execution by 50ms
+ var self = this;
+ new WCF.PeriodicalExecuter(function(pe) {
+ pe.stop();
+
+ self._comments[$input.data('commentID')].data('responsePlaceholder').show();
+
+ var $responseInput = self._comments[$input.data('commentID')].data('responseInput');
+ $responseInput.hide().find('input').val('');
+ }, 50);
+ },
+
+ /**
+ * Handles the keyup event for comments and responses.
+ *
+ * @param object event
+ * @param boolean isResponse
+ */
+ _keyUp: function(event, isResponse) {
+ // ignore every key except for [Enter] and [Esc]
+ if (event.which !== 13 && event.which !== 27) {
+ return;
+ }
+
+ var $input = $(event.currentTarget);
+
+ // cancel input
+ if (event.which === 27) {
+ $input.val('').trigger('blur', event);
+ return;
+ }
+
+ var $value = $.trim($input.val());
+
+ // ignore empty comments
+ if ($value == '') {
+ return;
+ }
+
+ var $actionName = 'addComment';
+ var $data = {
+ message: $value,
+ objectID: this._container.data('objectID'),
+ objectTypeID: this._container.data('objectTypeID')
+ };
+ if (isResponse === true) {
+ $actionName = 'addResponse';
+ $data.commentID = $input.data('commentID');
+ }
+
+ this._proxy.setOption('data', {
+ actionName: $actionName,
+ className: 'wcf\\data\\comment\\CommentAction',
+ parameters: {
+ data: $data
+ }
+ });
+ this._proxy.sendRequest();
+
+ // reset input
+ $input.val('').blur();
+ },
+
+ /**
+ * Shows a confirmation message prior to comment or response deletion.
+ *
+ * @param object event
+ * @param boolean isResponse
+ */
+ _delete: function(event, isResponse) {
+ WCF.System.Confirmation.show(WCF.Language.get('wcf.comment.delete.confirmMessage'), $.proxy(function(action) {
+ if (action === 'confirm') {
+ var $data = {
+ objectID: this._container.data('objectID'),
+ objectTypeID: this._container.data('objectTypeID')
+ };
+ if (isResponse !== true) {
+ $data.commentID = $(event.currentTarget).data('commentID');
+ }
+ else {
+ $data.responseID = $(event.currentTarget).data('responseID');
+ }
+
+ this._proxy.setOption('data', {
+ actionName: 'remove',
+ className: 'wcf\\data\\comment\\CommentAction',
+ parameters: {
+ data: $data
+ }
+ });
+ this._proxy.sendRequest();
+ }
+ }, this));
+ },
+
+ /**
+ * Handles successful AJAX requests.
+ *
+ * @param object data
+ * @param string textStatus
+ * @param jQuery jqXHR
+ */
+ _success: function(data, textStatus, jqXHR) {
+ switch (data.actionName) {
+ case 'addComment':
+ $(data.returnValues.template).insertAfter(this._commentAdd).wcfFadeIn();
+ break;
+
+ case 'addResponse':
+ $(data.returnValues.template).prependTo(this._comments[data.returnValues.commentID].find('ul.commentResponseList')).wcfFadeIn();
+ break;
+
+ case 'edit':
+ this._update(data);
+ break;
+
+ case 'loadComments':
+ this._insertComments(data);
+ break;
+
+ case 'loadResponses':
+ this._insertResponses(data);
+ break;
+
+ case 'prepareEdit':
+ this._edit(data);
+ break;
+
+ case 'remove':
+ this._remove(data);
+ break;
+ }
+
+ WCF.DOMNodeInsertedHandler.forceExecution();
+ },
+
+ /**
+ * Inserts previously loaded comments.
+ *
+ * @param object data
+ */
+ _insertComments: function(data) {
+ // insert comments
+ $(data.returnValues.template).insertBefore(this._loadNextComments);
+
+ // update time of last comment
+ this._container.data('lastCommentTime', data.returnValues.lastCommentTime);
+ },
+
+ /**
+ * Inserts previously loaded responses.
+ *
+ * @param object data
+ */
+ _insertResponses: function(data) {
+ var $comment = this._comments[data.returnValues.commentID];
+
+ // insert responses
+ $(data.returnValues.template).appendTo($comment.find('ul.commentResponseList'));
+
+ // update time of last response
+ $comment.data('lastResponseTime', data.returnValues.lastResponseTime);
+
+ // update button state to load next responses
+ this._handleLoadNextResponses(data.returnValues.commentID);
+ },
+
+ /**
+ * Removes a comment or response from list.
+ *
+ * @param object data
+ */
+ _remove: function(data) {
+ if (data.returnValues.commentID) {
+ this._comments[data.returnValues.commentID].remove();
+ delete this._comments[data.returnValues.commentID];
+ }
+ else {
+ this._responses[data.returnValues.responseID].remove();
+ delete this._responses[data.returnValues.responseID];
+ }
+ },
+
+ /**
+ * Prepares editing of a comment or response.
+ *
+ * @param object data
+ */
+ _edit: function(data) {
+ if (data.returnValues.commentID) {
+ var $content = this._comments[data.returnValues.commentID].find('.commentContent:eq(0) .userMessage:eq(0)');
+ }
+ else {
+ var $content = this._responses[data.returnValues.responseID].find('.commentContent:eq(0) .userMessage:eq(0)');
+ }
+
+ // replace content with input field
+ $content.html($.proxy(function(index, oldHTML) {
+ var $input = $('<input type="text" class="long" maxlength="65535" /><small>' + WCF.Language.get('wcf.comment.description') + '</small>').val(data.returnValues.message);
+ $input.data('__html', oldHTML).keyup($.proxy(this._saveEdit, this));
+
+ if (data.returnValues.commentID) {
+ $input.data('commentID', data.returnValues.commentID);
+ }
+ else {
+ $input.data('responseID', data.returnValues.responseID);
+ }
+
+ return $input;
+ }, this));
+ $content.children('input').focus();
+
+ // hide elements
+ $content.parent().find('.containerHeadline:eq(0)').hide();
+ $content.parent().find('.buttonGroupNavigation:eq(0)').hide();
+ },
+
+ /**
+ * Updates a comment or response.
+ *
+ * @param object data
+ */
+ _update: function(data) {
+ if (data.returnValues.commentID) {
+ var $input = this._comments[data.returnValues.commentID].find('.commentContent:eq(0) .userMessage:eq(0) > input');
+ }
+ else {
+ var $input = this._responses[data.returnValues.responseID].find('.commentContent:eq(0) .userMessage:eq(0) > input');
+ }
+
+ $input.data('__html', data.returnValues.message);
+
+ this._cancelEdit($input);
+ },
+
+ /**
+ * Saves editing of a comment or response.
+ *
+ * @param object event
+ */
+ _saveEdit: function(event) {
+ var $input = $(event.currentTarget);
+
+ // abort with [Esc]
+ if (event.which === 27) {
+ this._cancelEdit($input);
+ return;
+ }
+ else if (event.which !== 13) {
+ // ignore everything except for [Enter]
+ return;
+ }
+
+ var $message = $.trim($input.val());
+
+ // ignore empty message
+ if ($message === '') {
+ return;
+ }
+
+ var $data = {
+ message: $message,
+ objectID: this._container.data('objectID'),
+ objectTypeID: this._container.data('objectTypeID')
+ };
+ if ($input.data('commentID')) {
+ $data.commentID = $input.data('commentID');
+ }
+ else {
+ $data.responseID = $input.data('responseID');
+ }
+
+ this._proxy.setOption('data', {
+ actionName: 'edit',
+ className: 'wcf\\data\\comment\\CommentAction',
+ parameters: {
+ data: $data
+ }
+ });
+ this._proxy.sendRequest()
+ },
+
+ /**
+ * Cancels editing of a comment or response.
+ *
+ * @param jQuery input
+ */
+ _cancelEdit: function(input) {
+ // restore elements
+ input.parent().prev('.containerHeadline:eq(0)').show();
+ input.parent().next('.buttonGroupNavigation:eq(0)').show();
+
+ // restore HTML
+ input.parent().html(input.data('__html'));
+ }
+});
+
+/**
+ * Like support for comments
+ *
+ * @see WCF.Like
+ */
+WCF.Comment.Like = WCF.Like.extend({
+ /**
+ * @see WCF.Like._getContainers()
+ */
+ _getContainers: function() {
+ return $('.commentList > li.comment');
+ },
+
+ /**
+ * @see WCF.Like._getObjectID()
+ */
+ _getObjectID: function(containerID) {
+ return this._containers[containerID].data('commentID');
+ },
+
+ /**
+ * @see WCF.Like._buildWidget()
+ */
+ _buildWidget: function(containerID, likeButton, dislikeButton, badge, summary) {
+ this._containers[containerID].find('.containerHeadline:eq(0) > h3').append(badge);
+
+ if (this._canLike) {
+ likeButton.appendTo(this._containers[containerID].find('.commentOptions:eq(0)'));
+ dislikeButton.appendTo(this._containers[containerID].find('.commentOptions:eq(0)'));
+ }
+ },
+
+ /**
+ * @see WCF.Like._getWidgetContainer()
+ */
+ _getWidgetContainer: function(containerID) {},
+
+ /**
+ * @see WCF.Like._addWidget()
+ */
+ _addWidget: function(containerID, widget) {}
+});
+
+/**
+ * Namespace for comment responses
+ */
+WCF.Comment.Response = { };
+
+/**
+ * Like support for comments responses.
+ *
+ * @see WCF.Like
+ */
+WCF.Comment.Response.Like = WCF.Like.extend({
+ /**
+ * @see WCF.Like._addWidget()
+ */
+ _addWidget: function(containerID, widget) { },
+
+ /**
+ * @see WCF.Like._buildWidget()
+ */
+ _buildWidget: function(containerID, likeButton, dislikeButton, badge, summary) {
+ this._containers[containerID].find('.containerHeadline:eq(0) > h3').append(badge);
+
+ if (this._canLike) {
+ likeButton.appendTo(this._containers[containerID].find('.commentOptions:eq(0)'));
+ dislikeButton.appendTo(this._containers[containerID].find('.commentOptions:eq(0)'));
+ }
+ },
+
+ /**
+ * @see WCF.Like._getContainers()
+ */
+ _getContainers: function() {
+ return $('.commentResponseList > li.commentResponse');
+ },
+
+ /**
+ * @see WCF.Like._getObjectID()
+ */
+ _getObjectID: function(containerID) {
+ return this._containers[containerID].data('responseID');
+ },
+
+ /**
+ * @see WCF.Like._getWidgetContainer()
+ */
+ _getWidgetContainer: function(containerID) { }
+});
\ No newline at end of file
--- /dev/null
+WCF.Comment={};WCF.Comment.Handler=Class.extend({_commentAdd:null,_comments:{},_container:null,_containerID:"",_displayedComments:0,_loadNextComments:null,_loadNextResponses:{},_proxy:null,_responses:{},_userAvatar:"",init:function(a,b){this._commentAdd=null;this._comments={};this._containerID=a;this._displayedComments=0;this._loadNextComments=null;this._loadNextResponses={};this._responses={};this._userAvatar=b;this._container=$("#"+$.wcfEscapeID(this._containerID));if(!this._container.length){console.debug("[WCF.Comment.Handler] Unable to find container identified by '"+this._containerID+"'")}this._proxy=new WCF.Action.Proxy({success:$.proxy(this._success,this)});WCF.DOMNodeInsertedHandler.enable();this._initComments();this._initResponses();if(this._container.data("canAdd")){this._initAddComment()}WCF.DOMNodeInsertedHandler.disable();WCF.DOMNodeInsertedHandler.addCallback("WCF.Comment.Handler",$.proxy(this._domNodeInserted,this))},_handleLoadNextComments:function(){if(this._displayedComments<this._container.data("comments")){if(this._loadNextComments===null){this._loadNextComments=$('<li class="commentLoadNext"><button class="buttonPrimary small">'+WCF.Language.get("wcf.comment.more")+"</button></li>").appendTo(this._container);this._loadNextComments.children("button").click($.proxy(this._loadComments,this))}this._loadNextComments.children("button").enable()}else{if(this._loadNextComments!==null){this._loadNextComments.hide()}}},_handleLoadNextResponses:function(a){var b=this._comments[a];b.data("displayedResponses",b.find("ul.commentResponseList > li").length);if(b.data("displayedResponses")<b.data("responses")){if(this._loadNextResponses[a]===undefined){this._loadNextResponses[a]=$('<div class="responseLoadNext"><button class="small">'+WCF.Language.get("wcf.comment.response.more")+"</button></div>").insertAfter(b.find("ul.commentResponseList"));this._loadNextResponses[a].children("button").data("commentID",a).click($.proxy(this._loadResponses,this))}this._loadNextResponses[a].children("button").enable()}else{if(this._loadNextResponses[a]!==undefined){this._loadNextResponses[a].hide()}}},_loadComments:function(){this._loadNextComments.children("button").disable();this._proxy.setOption("data",{actionName:"loadComments",className:"wcf\\data\\comment\\CommentAction",parameters:{data:{objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID"),lastCommentTime:this._container.data("lastCommentTime")}}});this._proxy.sendRequest()},_loadResponses:function(b){var c=$(b.currentTarget).disable();var a=c.data("commentID");this._proxy.setOption("data",{actionName:"loadResponses",className:"wcf\\data\\comment\\response\\CommentResponseAction",parameters:{data:{commentID:a,lastResponseTime:this._comments[a].data("lastResponseTime")}}});this._proxy.sendRequest()},_domNodeInserted:function(){this._initComments();this._initResponses()},_initComments:function(){var a=this;var b=false;this._container.find(".jsComment").each(function(d,f){var e=$(f).removeClass("jsComment");var c=e.data("commentID");a._comments[c]=e;a._initComment(c,e);a._displayedComments++;b=true;a._handleLoadNextResponses(c)});if(b){this._handleLoadNextComments()}},_initComment:function(a,d){if(this._container.data("canAdd")){this._initAddResponse(a,d)}if(d.data("canEdit")){var b=$('<li><a class="jsTooltip" title="'+WCF.Language.get("wcf.global.button.edit")+'"><span class="icon icon16 icon-pencil" /> <span class="invisible">'+WCF.Language.get("wcf.global.button.edit")+"</span></a></li>");b.data("commentID",a).appendTo(d.find("ul.commentOptions:eq(0)")).click($.proxy(this._prepareEdit,this))}if(d.data("canDelete")){var c=$('<li><a class="jsTooltip" title="'+WCF.Language.get("wcf.global.button.delete")+'"><span class="icon icon16 icon-remove" /> <span class="invisible">'+WCF.Language.get("wcf.global.button.delete")+"</span></a></li>");c.data("commentID",a).appendTo(d.find("ul.commentOptions:eq(0)")).click($.proxy(this._delete,this))}},_initResponses:function(){var a=this;this._container.find(".jsCommentResponse").each(function(d,c){var b=$(c).removeClass("jsCommentResponse");var e=b.data("responseID");a._responses[e]=b;a._initResponse(e,b)})},_initResponse:function(a,c){if(c.data("canEdit")){var d=$('<li><a class="jsTooltip" title="'+WCF.Language.get("wcf.global.button.edit")+'"><span class="icon icon16 icon-pencil" /> <span class="invisible">'+WCF.Language.get("wcf.global.button.edit")+"</span></a></li>");var b=this;d.data("responseID",a).appendTo(c.find("ul.commentOptions:eq(0)")).click(function(f){b._prepareEdit(f,true)})}if(c.data("canDelete")){var e=$('<li><a class="jsTooltip" title="'+WCF.Language.get("wcf.global.button.delete")+'"><span class="icon icon16 icon-remove" /> <span class="invisible">'+WCF.Language.get("wcf.global.button.delete")+"</span></a></li>");var b=this;e.data("responseID",a).appendTo(c.find("ul.commentOptions:eq(0)")).click(function(f){b._delete(f,true)})}},_initAddComment:function(){this._commentAdd=$('<li class="box32 jsCommentAdd"><span class="framed">'+this._userAvatar+"</span><div /></li>").prependTo(this._container);var a=this._commentAdd.children("div");var b=$('<input type="text" placeholder="'+WCF.Language.get("wcf.comment.add")+'" maxlength="65535" class="long" />').appendTo(a);$("<small>"+WCF.Language.get("wcf.comment.description")+"</small>").appendTo(a);b.keyup($.proxy(this._keyUp,this))},_initAddResponse:function(d,g){var c=$('<div class="commentResponseAdd jsCommentResponseAddPlaceholder"><a class="button small">'+WCF.Language.get("wcf.comment.button.response.add")+"</a></div>").insertBefore(g.find("ul.commentResponseList"));c.data("commentID",d).click($.proxy(this._showAddResponse,this));var e=$('<div class="box32 commentResponseAdd jsCommentResponseAdd"><span class="framed">'+this._userAvatar+"</span><div /></div>").hide().insertAfter(c);var a=e.children("div");var f=$('<input type="text" placeholder="'+WCF.Language.get("wcf.comment.response.add")+'" maxlength="65535" class="long" />').data("commentID",d).appendTo(a);$("<small>"+WCF.Language.get("wcf.comment.description")+"</small>").appendTo(a);var b=this;f.keyup(function(h){b._keyUp(h,true)}).blur($.proxy(this._hideAddResponse,this));g.data("responsePlaceholder",c).data("responseInput",e)},_prepareEdit:function(c,a){var d=$(c.currentTarget);var b={objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID")};if(a===true){b.responseID=d.data("responseID")}else{b.commentID=d.data("commentID")}this._proxy.setOption("data",{actionName:"prepareEdit",className:"wcf\\data\\comment\\CommentAction",parameters:{data:b}});this._proxy.sendRequest()},_showAddResponse:function(b){var a=$(b.currentTarget).data("commentID");this._comments[a].data("responsePlaceholder").hide();var c=this._comments[a].data("responseInput").show();c.find("input").focus()},_hideAddResponse:function(b){var c=$(b.currentTarget);if($.trim(c.val())!==""){return}var a=this;new WCF.PeriodicalExecuter(function(d){d.stop();a._comments[c.data("commentID")].data("responsePlaceholder").show();var e=a._comments[c.data("commentID")].data("responseInput");e.hide().find("input").val("")},50)},_keyUp:function(e,b){if(e.which!==13&&e.which!==27){return}var f=$(e.currentTarget);if(e.which===27){f.val("").trigger("blur",e);return}var d=$.trim(f.val());if(d==""){return}var a="addComment";var c={message:d,objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID")};if(b===true){a="addResponse";c.commentID=f.data("commentID")}this._proxy.setOption("data",{actionName:a,className:"wcf\\data\\comment\\CommentAction",parameters:{data:c}});this._proxy.sendRequest();f.val("").blur()},_delete:function(b,a){WCF.System.Confirmation.show(WCF.Language.get("wcf.comment.delete.confirmMessage"),$.proxy(function(d){if(d==="confirm"){var c={objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID")};if(a!==true){c.commentID=$(b.currentTarget).data("commentID")}else{c.responseID=$(b.currentTarget).data("responseID")}this._proxy.setOption("data",{actionName:"remove",className:"wcf\\data\\comment\\CommentAction",parameters:{data:c}});this._proxy.sendRequest()}},this))},_success:function(b,c,a){switch(b.actionName){case"addComment":$(b.returnValues.template).insertAfter(this._commentAdd).wcfFadeIn();break;case"addResponse":$(b.returnValues.template).prependTo(this._comments[b.returnValues.commentID].find("ul.commentResponseList")).wcfFadeIn();break;case"edit":this._update(b);break;case"loadComments":this._insertComments(b);break;case"loadResponses":this._insertResponses(b);break;case"prepareEdit":this._edit(b);break;case"remove":this._remove(b);break}WCF.DOMNodeInsertedHandler.forceExecution()},_insertComments:function(a){$(a.returnValues.template).insertBefore(this._loadNextComments);this._container.data("lastCommentTime",a.returnValues.lastCommentTime)},_insertResponses:function(b){var a=this._comments[b.returnValues.commentID];$(b.returnValues.template).appendTo(a.find("ul.commentResponseList"));a.data("lastResponseTime",b.returnValues.lastResponseTime);this._handleLoadNextResponses(b.returnValues.commentID)},_remove:function(a){if(a.returnValues.commentID){this._comments[a.returnValues.commentID].remove();delete this._comments[a.returnValues.commentID]}else{this._responses[a.returnValues.responseID].remove();delete this._responses[a.returnValues.responseID]}},_edit:function(b){if(b.returnValues.commentID){var a=this._comments[b.returnValues.commentID].find(".commentContent:eq(0) .userMessage:eq(0)")}else{var a=this._responses[b.returnValues.responseID].find(".commentContent:eq(0) .userMessage:eq(0)")}a.html($.proxy(function(d,c){var e=$('<input type="text" class="long" maxlength="65535" /><small>'+WCF.Language.get("wcf.comment.description")+"</small>").val(b.returnValues.message);e.data("__html",c).keyup($.proxy(this._saveEdit,this));if(b.returnValues.commentID){e.data("commentID",b.returnValues.commentID)}else{e.data("responseID",b.returnValues.responseID)}return e},this));a.children("input").focus();a.parent().find(".containerHeadline:eq(0)").hide();a.parent().find(".buttonGroupNavigation:eq(0)").hide()},_update:function(a){if(a.returnValues.commentID){var b=this._comments[a.returnValues.commentID].find(".commentContent:eq(0) .userMessage:eq(0) > input")}else{var b=this._responses[a.returnValues.responseID].find(".commentContent:eq(0) .userMessage:eq(0) > input")}b.data("__html",a.returnValues.message);this._cancelEdit(b)},_saveEdit:function(c){var d=$(c.currentTarget);if(c.which===27){this._cancelEdit(d);return}else{if(c.which!==13){return}}var b=$.trim(d.val());if(b===""){return}var a={message:b,objectID:this._container.data("objectID"),objectTypeID:this._container.data("objectTypeID")};if(d.data("commentID")){a.commentID=d.data("commentID")}else{a.responseID=d.data("responseID")}this._proxy.setOption("data",{actionName:"edit",className:"wcf\\data\\comment\\CommentAction",parameters:{data:a}});this._proxy.sendRequest()},_cancelEdit:function(a){a.parent().prev(".containerHeadline:eq(0)").show();a.parent().next(".buttonGroupNavigation:eq(0)").show();a.parent().html(a.data("__html"))}});WCF.Comment.Like=WCF.Like.extend({_getContainers:function(){return $(".commentList > li.comment")},_getObjectID:function(a){return this._containers[a].data("commentID")},_buildWidget:function(b,a,d,c,e){this._containers[b].find(".containerHeadline:eq(0) > h3").append(c);if(this._canLike){a.appendTo(this._containers[b].find(".commentOptions:eq(0)"));d.appendTo(this._containers[b].find(".commentOptions:eq(0)"))}},_getWidgetContainer:function(a){},_addWidget:function(a,b){}});WCF.Comment.Response={};WCF.Comment.Response.Like=WCF.Like.extend({_addWidget:function(a,b){},_buildWidget:function(b,a,d,c,e){this._containers[b].find(".containerHeadline:eq(0) > h3").append(c);if(this._canLike){a.appendTo(this._containers[b].find(".commentOptions:eq(0)"));d.appendTo(this._containers[b].find(".commentOptions:eq(0)"))}},_getContainers:function(){return $(".commentResponseList > li.commentResponse")},_getObjectID:function(a){return this._containers[a].data("responseID")},_getWidgetContainer:function(a){}});
\ No newline at end of file
<?php
namespace wcf\acp\form;
+use wcf\data\object\type\ObjectTypeCache;
use wcf\data\user\UserAction;
use wcf\form\AbstractForm;
use wcf\system\clipboard\ClipboardHandler;
parent::save();
+ // comment
+ $conditions = new PreparedStatementConditionBuilder();
+ $conditions->add("userID IN (?)", array($this->mergedUserIDs));
+ $sql = "UPDATE wcf".WCF_N."_comment
+ SET userID = ?
+ ".$conditions;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array_merge(array($this->destinationUserID), $conditions->getParameters()));
+
+ // comment_response
+ $conditions = new PreparedStatementConditionBuilder();
+ $conditions->add("userID IN (?)", array($this->mergedUserIDs));
+ $sql = "UPDATE wcf".WCF_N."_comment_response
+ SET userID = ?
+ ".$conditions;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array_merge(array($this->destinationUserID), $conditions->getParameters()));
+
+ // profile comments
+ $objectType = ObjectTypeCache::getInstance()->getObjectTypeByName('com.woltlab.wcf.comment.commentableContent', 'com.woltlab.wcf.user.profileComment');
+ $conditions = new PreparedStatementConditionBuilder();
+ $conditions->add("objectTypeID = ?", array($objectType->objectTypeID));
+ $conditions->add("objectID IN (?)", array($this->mergedUserIDs));
+ $sql = "UPDATE wcf".WCF_N."_comment
+ SET objectID = ?
+ ".$conditions;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array_merge(array($this->destinationUserID), $conditions->getParameters()));
+
// like (userID)
$conditions = new PreparedStatementConditionBuilder();
$conditions->add("userID IN (?)", array($this->mergedUserIDs));
--- /dev/null
+<?php
+namespace wcf\data\comment;
+use wcf\data\DatabaseObject;
+use wcf\data\IMessage;
+use wcf\system\bbcode\SimpleMessageParser;
+use wcf\system\comment\CommentHandler;
+use wcf\util\StringUtil;
+
+/**
+ * Represents a comment.
+ *
+ * @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.comment
+ * @subpackage data.comment
+ * @category Community Framework
+ */
+class Comment extends DatabaseObject implements IMessage {
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'comment';
+
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableIndexName
+ */
+ protected static $databaseTableIndexName = 'commentID';
+
+ /**
+ * Returns a list of last response ids.
+ *
+ * @return array<integer>
+ */
+ public function getLastResponseIDs() {
+ if ($this->lastResponseIDs === null || $this->lastResponseIDs == '') {
+ return array();
+ }
+
+ $lastResponseIDs = @unserialize($this->lastResponseIDs);
+ if ($lastResponseIDs === false) {
+ return array();
+ }
+
+ return $lastResponseIDs;
+ }
+
+ /**
+ * @see wcf\data\IMessage::getFormattedMessage()
+ */
+ public function getFormattedMessage() {
+ return SimpleMessageParser::getInstance()->parse($this->message);
+ }
+
+ /**
+ * @see wcf\data\IMessage::getExcerpt()
+ */
+ public function getExcerpt($maxLength = 255) {
+ return StringUtil::truncateHTML($this->getFormattedMessage(), $maxLength);
+ }
+
+ /**
+ * @see wcf\data\IMessage::getMessage()
+ */
+ public function getMessage() {
+ return $this->message;
+ }
+
+ /**
+ * @see wcf\data\IUserContent::getTime()
+ */
+ public function getTime() {
+ return $this->time;
+ }
+
+ /**
+ * @see wcf\data\IUserContent::getUserID()
+ */
+ public function getUserID() {
+ return $this->userID;
+ }
+
+ /**
+ * @see wcf\data\IUserContent::getUsername()
+ */
+ public function getUsername() {
+ return $this->username;
+ }
+
+ /**
+ * @see wcf\data\ILinkableObject::getLink()
+ */
+ public function getLink() {
+ return CommentHandler::getInstance()->getObjectType($this->objectTypeID)->getProcessor()->getLink($this->objectTypeID, $this->objectID);
+ }
+
+ /**
+ * @see wcf\data\ITitledObject::getTitle()
+ */
+ public function getTitle() {
+ return CommentHandler::getInstance()->getObjectType($this->objectTypeID)->getProcessor()->getTitle($this->objectTypeID, $this->objectID);
+ }
+
+ /**
+ * @see wcf\data\IMessage::isVisible()
+ */
+ public function isVisible() {
+ return true;
+ }
+
+ /**
+ * @see wcf\data\IMessage::__toString()
+ */
+ public function __toString() {
+ return $this->getFormattedMessage();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment;
+use wcf\data\comment\response\CommentResponse;
+use wcf\data\comment\response\CommentResponseAction;
+use wcf\data\comment\response\CommentResponseEditor;
+use wcf\data\comment\response\CommentResponseList;
+use wcf\data\comment\response\StructuredCommentResponse;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\user\UserProfile;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\system\comment\CommentHandler;
+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\notification\object\CommentResponseUserNotificationObject;
+use wcf\system\user\notification\object\CommentUserNotificationObject;
+use wcf\system\user\notification\UserNotificationHandler;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Executes comment-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.comment
+ * @subpackage data.comment
+ * @category Community Framework
+ */
+class CommentAction extends AbstractDatabaseObjectAction {
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$allowGuestAccess
+ */
+ protected $allowGuestAccess = array('loadComments');
+
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$className
+ */
+ protected $className = 'wcf\data\comment\CommentEditor';
+
+ /**
+ * comment object
+ * @var wcf\data\comment\Comment
+ */
+ protected $comment = null;
+
+ /**
+ * comment processor
+ * @var wcf\system\comment\manager\ICommentManager
+ */
+ protected $commentProcessor = null;
+
+ /**
+ * response object
+ * @var wcf\data\comment\response\CommentResponse
+ */
+ protected $response = null;
+
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::delete()
+ */
+ public function delete() {
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ // update counters
+ $processors = array();
+ $groupCommentIDs = $commentIDs = array();
+ foreach ($this->objects as $comment) {
+ if (!isset($processors[$comment->objectTypeID])) {
+ $objectType = ObjectTypeCache::getInstance()->getObjectType($comment->objectTypeID);
+ $processors[$comment->objectTypeID] = $objectType->getProcessor();
+
+ $groupCommentIDs[$comment->objectTypeID] = array();
+ }
+
+ $processors[$comment->objectTypeID]->updateCounter($comment->objectID, -1 * ($comment->responses + 1));
+ $groupCommentIDs[$comment->objectTypeID][] = $comment->commentID;
+ $commentIDs[] = $comment->commentID;
+ }
+
+ if (!empty($groupCommentIDs)) {
+ $likeObjectIDs = array();
+ foreach ($groupCommentIDs as $objectTypeID => $objectIDs) {
+ // remove activity events
+ $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID);
+ if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType.'.recentActivityEvent')) {
+ UserActivityEventHandler::getInstance()->removeEvents($objectType->objectType.'.recentActivityEvent', $objectIDs);
+ }
+
+ $likeObjectIDs = array_merge($likeObjectIDs, $objectIDs);
+
+ // delete notifications
+ $objectType = ObjectTypeCache::getInstance()->getObjectType($comment->objectTypeID);
+ if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.notification')) {
+ UserNotificationHandler::getInstance()->deleteNotifications('comment', $objectType->objectType.'.notification', array(), $objectIDs);
+ }
+ }
+
+ // remove likes
+ LikeHandler::getInstance()->removeLikes('com.woltlab.wcf.comment', $likeObjectIDs);
+ }
+
+ // delete responses
+ if (!empty($commentIDs)) {
+ $commentResponseList = new CommentResponseList();
+ $commentResponseList->getConditionBuilder()->add('comment_response.commentID IN (?)', array($commentIDs));
+ $commentResponseList->readObjectIDs();
+ if (count($commentResponseList->getObjectIDs())) {
+ $action = new CommentResponseAction($commentResponseList->getObjectIDs(), 'delete');
+ $action->executeAction();
+ }
+ }
+
+ return parent::delete();
+ }
+
+ /**
+ * Validates parameters to load comments.
+ */
+ public function validateLoadComments() {
+ $this->readInteger('lastCommentTime', false, 'data');
+ $this->readInteger('objectID', false, 'data');
+
+ $objectType = $this->validateObjectType();
+ $this->commentProcessor = $objectType->getProcessor();
+ if (!$this->commentProcessor->isAccessible($this->parameters['data']['objectID'])) {
+ throw new PermissionDeniedException();
+ }
+ }
+
+ /**
+ * Returns parsed comments.
+ *
+ * @return array
+ */
+ public function loadComments() {
+ $commentList = CommentHandler::getInstance()->getCommentList($this->commentProcessor, $this->parameters['data']['objectTypeID'], $this->parameters['data']['objectID'], false);
+ $commentList->getConditionBuilder()->add("comment.time < ?", array($this->parameters['data']['lastCommentTime']));
+ $commentList->readObjects();
+
+ WCF::getTPL()->assign(array(
+ 'commentList' => $commentList,
+ 'likeData' => (MODULE_LIKE ? $commentList->getLikeData() : array())
+ ));
+
+ return array(
+ 'lastCommentTime' => $commentList->getMinCommentTime(),
+ 'template' => WCF::getTPL()->fetch('commentList')
+ );
+ }
+
+ /**
+ * Validates parameters to add a comment.
+ */
+ public function validateAddComment() {
+ $this->readInteger('objectID', false, 'data');
+ $this->validateMessage();
+ $objectType = $this->validateObjectType();
+
+ // validate object id and permissions
+ $this->commentProcessor = $objectType->getProcessor();
+ if (!$this->commentProcessor->canAdd($this->parameters['data']['objectID'])) {
+ throw new PermissionDeniedException();
+ }
+ }
+
+ /**
+ * Adds a comment.
+ *
+ * @return array
+ */
+ public function addComment() {
+ // create comment
+ $comment = CommentEditor::create(array(
+ 'objectTypeID' => $this->parameters['data']['objectTypeID'],
+ 'objectID' => $this->parameters['data']['objectID'],
+ 'time' => TIME_NOW,
+ 'userID' => WCF::getUser()->userID,
+ 'username' => WCF::getUser()->username,
+ 'message' => $this->parameters['data']['message'],
+ 'responses' => 0,
+ 'lastResponseIDs' => serialize(array())
+ ));
+
+ // update counter
+ $this->commentProcessor->updateCounter($this->parameters['data']['objectID'], 1);
+
+ // fire activity event
+ $objectType = ObjectTypeCache::getInstance()->getObjectType($this->parameters['data']['objectTypeID']);
+ if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType.'.recentActivityEvent')) {
+ UserActivityEventHandler::getInstance()->fireEvent($objectType->objectType.'.recentActivityEvent', $comment->commentID);
+ }
+
+ // fire notification event
+ if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.notification')) {
+ $notificationObjectType = UserNotificationHandler::getInstance()->getObjectTypeProcessor($objectType->objectType.'.notification');
+ $userID = $notificationObjectType->getOwnerID($comment->commentID);
+ if ($userID != WCF::getUser()->userID) {
+ $notificationObject = new CommentUserNotificationObject($comment);
+
+ UserNotificationHandler::getInstance()->fireEvent('comment', $objectType->objectType.'.notification', $notificationObject, array($userID));
+ }
+ }
+
+ return array(
+ 'template' => $this->renderComment($comment)
+ );
+ }
+
+ /**
+ * Validates parameters to add a response.
+ */
+ public function validateAddResponse() {
+ $this->readInteger('objectID', false, 'data');
+
+ // validate comment id
+ $this->validateCommentID();
+
+ // validate object type id
+ $objectType = $this->validateObjectType();
+
+ // validate object id and permissions
+ $this->commentProcessor = $objectType->getProcessor();
+ if (!$this->commentProcessor->canAdd($this->parameters['data']['objectID'])) {
+ throw new PermissionDeniedException();
+ }
+ }
+
+ /**
+ * Adds a response.
+ *
+ * @return array
+ */
+ public function addResponse() {
+ // create response
+ $response = CommentResponseEditor::create(array(
+ 'commentID' => $this->comment->commentID,
+ 'time' => TIME_NOW,
+ 'userID' => WCF::getUser()->userID,
+ 'username' => WCF::getUser()->username,
+ 'message' => $this->parameters['data']['message']
+ ));
+
+ // update response data
+ $lastResponseIDs = $this->comment->getLastResponseIDs();
+ if (count($lastResponseIDs) == 3) array_shift($lastResponseIDs);
+ $lastResponseIDs[] = $response->responseID;
+ $responses = $this->comment->responses + 1;
+
+ // update comment
+ $commentEditor = new CommentEditor($this->comment);
+ $commentEditor->update(array(
+ 'lastResponseIDs' => serialize($lastResponseIDs),
+ 'responses' => $responses
+ ));
+
+ // update counter
+ $this->commentProcessor->updateCounter($this->parameters['data']['objectID'], 1);
+
+ // fire activity event
+ $objectType = ObjectTypeCache::getInstance()->getObjectType($this->comment->objectTypeID);
+ if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType.'.response.recentActivityEvent')) {
+ UserActivityEventHandler::getInstance()->fireEvent($objectType->objectType.'.response.recentActivityEvent', $response->responseID);
+ }
+
+ // fire notification event
+ if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.response.notification')) {
+ $notificationObject = new CommentResponseUserNotificationObject($response);
+ if ($this->comment->userID != WCF::getUser()->userID) {
+ UserNotificationHandler::getInstance()->fireEvent('commentResponse', $objectType->objectType.'.response.notification', $notificationObject, array($this->comment->userID));
+ }
+
+ // notify the container owner
+ if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.notification')) {
+ $notificationObjectType = UserNotificationHandler::getInstance()->getObjectTypeProcessor($objectType->objectType.'.notification');
+ $userID = $notificationObjectType->getOwnerID($this->comment->commentID);
+
+ if ($userID != $this->comment->userID && $userID != WCF::getUser()->userID) {
+ UserNotificationHandler::getInstance()->fireEvent('commentResponseOwner', $objectType->objectType.'.response.notification', $notificationObject, array($userID));
+ }
+ }
+ }
+
+ return array(
+ 'commentID' => $this->comment->commentID,
+ 'template' => $this->renderResponse($response),
+ 'responses' => $responses
+ );
+ }
+
+ /**
+ * Validates parameters to edit a comment or a response.
+ */
+ public function validatePrepareEdit() {
+ // validate comment id or response id
+ try {
+ $this->validateCommentID();
+ }
+ catch (UserInputException $e) {
+ try {
+ $this->validateResponseID();
+ }
+ catch (UserInputException $e) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+
+ // validate object type id
+ $objectType = $this->validateObjectType();
+
+ // validate object id and permissions
+ $this->commentProcessor = $objectType->getProcessor();
+ if ($this->comment !== null) {
+ if (!$this->commentProcessor->canEditComment($this->comment)) {
+ throw new PermissionDeniedException();
+ }
+ }
+ else {
+ if (!$this->commentProcessor->canEditResponse($this->response)) {
+ throw new PermissionDeniedException();
+ }
+ }
+ }
+
+ /**
+ * Prepares editing of a comment or a response.
+ *
+ * @return array
+ */
+ public function prepareEdit() {
+ $message = '';
+ if ($this->comment !== null) {
+ $message = $this->comment->message;
+ }
+ else {
+ $message = $this->response->message;
+ }
+
+ $returnValues = array(
+ 'action' => 'prepare',
+ 'message' => $message
+ );
+ if ($this->comment !== null) {
+ $returnValues['commentID'] = $this->comment->commentID;
+ }
+ else {
+ $returnValues['responseID'] = $this->response->responseID;
+ }
+
+ return $returnValues;
+ }
+
+ /**
+ * @see wcf\data\comment\CommentAction::validatePrepareEdit()
+ */
+ public function validateEdit() {
+ $this->validatePrepareEdit();
+
+ $this->validateMessage();
+ }
+
+ /**
+ * Edits a comment or response.
+ *
+ * @return array
+ */
+ public function edit() {
+ $returnValues = array(
+ 'action' => 'saved',
+ );
+
+ if ($this->response === null) {
+ $editor = new CommentEditor($this->comment);
+ $editor->update(array(
+ 'message' => $this->parameters['data']['message']
+ ));
+ $comment = new Comment($this->comment->commentID);
+ $returnValues['commentID'] = $this->comment->commentID;
+ $returnValues['message'] = $comment->getFormattedMessage();
+ }
+ else {
+ $editor = new CommentResponseEditor($this->response);
+ $editor->update(array(
+ 'message' => $this->parameters['data']['message']
+ ));
+ $response = new CommentResponse($this->response->responseID);
+ $returnValues['responseID'] = $this->response->responseID;
+ $returnValues['message'] = $response->getFormattedMessage();
+ }
+
+ return $returnValues;
+ }
+
+ /**
+ * Validates parameters to remove a comment or response.
+ */
+ public function validateRemove() {
+ // validate comment id or response id
+ try {
+ $this->validateCommentID();
+ }
+ catch (UserInputException $e) {
+ try {
+ $this->validateResponseID();
+ }
+ catch (UserInputException $e) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+
+ // validate object type id
+ $objectType = $this->validateObjectType();
+
+ // validate object id and permissions
+ $this->commentProcessor = $objectType->getProcessor();
+ if ($this->comment !== null) {
+ if (!$this->commentProcessor->canDeleteComment($this->comment)) {
+ throw new PermissionDeniedException();
+ }
+ }
+ else {
+ if (!$this->commentProcessor->canDeleteResponse($this->response)) {
+ throw new PermissionDeniedException();
+ }
+ }
+ }
+
+ /**
+ * Removes a comment or response.
+ *
+ * @return array
+ */
+ public function remove() {
+ if ($this->comment !== null) {
+ $objectAction = new CommentAction(array($this->comment), 'delete');
+ $objectAction->executeAction();
+
+ return array(
+ 'commentID' => $this->comment->commentID
+ );
+ }
+ else {
+ $objectAction = new CommentResponseAction(array($this->response), 'delete');
+ $objectAction->executeAction();
+
+ return array(
+ 'responseID' => $this->response->responseID
+ );
+ }
+ }
+
+ /**
+ * Renders a comment.
+ *
+ * @param wcf\data\comment\Comment $comment
+ * @return string
+ */
+ protected function renderComment(Comment $comment) {
+ $comment = new StructuredComment($comment);
+ $comment->setIsDeletable($this->commentProcessor->canDeleteComment($comment->getDecoratedObject()));
+ $comment->setIsEditable($this->commentProcessor->canEditComment($comment->getDecoratedObject()));
+
+ // set user profile
+ $userProfile = UserProfile::getUserProfile($comment->userID);
+ $comment->setUserProfile($userProfile);
+
+ WCF::getTPL()->assign(array(
+ 'commentList' => array($comment)
+ ));
+ return WCF::getTPL()->fetch('commentList');
+ }
+
+ /**
+ * Renders a response.
+ *
+ * @param wcf\data\comment\response\CommentResponse $response
+ * @return string
+ */
+ protected function renderResponse(CommentResponse $response) {
+ $response = new StructuredCommentResponse($response);
+ $response->setIsDeletable($this->commentProcessor->canDeleteResponse($response->getDecoratedObject()));
+ $response->setIsEditable($this->commentProcessor->canEditResponse($response->getDecoratedObject()));
+
+ // set user profile
+ $userProfile = UserProfile::getUserProfile($response->userID);
+ $response->setUserProfile($userProfile);
+
+ // render response
+ WCF::getTPL()->assign(array(
+ 'responseList' => array($response)
+ ));
+ return WCF::getTPL()->fetch('commentResponseList');
+ }
+
+ /**
+ * Validates message parameter.
+ */
+ protected function validateMessage() {
+ $this->readString('message', false, 'data');
+
+ if (empty($this->parameters['data']['message'])) {
+ throw new UserInputException('message');
+ }
+ }
+
+ /**
+ * Validates object type id parameter.
+ *
+ * @return wcf\data\object\type\ObjectType
+ */
+ protected function validateObjectType() {
+ $this->readInteger('objectTypeID', false, 'data');
+
+ $objectType = ObjectTypeCache::getInstance()->getObjectType($this->parameters['data']['objectTypeID']);
+ if ($objectType === null) {
+ throw new UserInputException('objectTypeID');
+ }
+
+ return $objectType;
+ }
+
+ /**
+ * Validates comment id parameter.
+ */
+ protected function validateCommentID() {
+ $this->readInteger('commentID', false, 'data');
+
+ $this->comment = new Comment($this->parameters['data']['commentID']);
+ if ($this->comment === null || !$this->comment->commentID) {
+ throw new UserInputException('commentID');
+ }
+ }
+
+ /**
+ * Validates response id parameter.
+ */
+ protected function validateResponseID() {
+ if (isset($this->parameters['data']['responseID'])) {
+ $this->response = new CommentResponse($this->parameters['data']['responseID']);
+ }
+ if ($this->response === null || !$this->response->responseID) {
+ throw new UserInputException('responseID');
+ }
+ }
+
+ /**
+ * Returns the comment object.
+ *
+ * @return wcf\data\comment\Comment
+ */
+ public function getComment() {
+ return $this->comment;
+ }
+
+ /**
+ * Returns the comment response object.
+ *
+ * @return wcf\data\comment\response\CommentResponse
+ */
+ public function getResponse() {
+ return $this->response;
+ }
+
+ /**
+ * Returns the comment manager.
+ *
+ * @return wcf\system\comment\manager\ICommentManager
+ */
+ public function getCommentManager() {
+ return $this->commentProcessor;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment;
+use wcf\data\DatabaseObjectEditor;
+use wcf\system\WCF;
+
+/**
+ * Provides functions to edit comments.
+ *
+ * @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.comment
+ * @subpackage data.comment
+ * @category Community Framework
+ */
+class CommentEditor extends DatabaseObjectEditor {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\comment\Comment';
+
+ /**
+ * Updates last response ids.
+ */
+ public function updateLastResponseIDs() {
+ $sql = "SELECT responseID
+ FROM wcf".WCF_N."_comment_response
+ WHERE commentID = ?
+ ORDER BY time DESC";
+ $statement = WCF::getDB()->prepareStatement($sql, 3);
+ $statement->execute(array($this->commentID));
+ $responseIDs = array();
+ while ($row = $statement->fetchArray()) {
+ $responseIDs[] = $row['responseID'];
+ }
+
+ $this->update(array(
+ 'lastResponseIDs' => serialize($responseIDs)
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of comments.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.comment
+ * @subpackage data.comment
+ * @category Community Framework
+ */
+class CommentList extends DatabaseObjectList {
+ /**
+ * @see wcf\data\DatabaseObjectList::$className
+ */
+ public $className = 'wcf\data\comment\Comment';
+}
--- /dev/null
+<?php
+namespace wcf\data\comment;
+use wcf\data\like\object\AbstractLikeObject;
+use wcf\data\object\type\ObjectTypeCache;
+
+/**
+ * Likeable object implementation for comments.
+ *
+ * @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.comment
+ * @subpackage data.comment
+ * @category Community Framework
+ */
+class LikeableComment extends AbstractLikeObject {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\comment\Comment';
+
+ /**
+ * @see wcf\data\like\object\ILikeObject::getTitle()
+ */
+ public function getTitle() {
+ return $this->message;
+ }
+
+ /**
+ * @see wcf\data\like\object\ILikeObject::getURL()
+ */
+ public function getURL() {
+ return $this->getLink();
+ }
+
+ /**
+ * @see wcf\data\like\object\ILikeObject::getUserID()
+ */
+ public function getUserID() {
+ return $this->userID;
+ }
+
+ /**
+ * @see wcf\data\like\object\ILikeObject::getObjectType()
+ */
+ public function getObjectType() {
+ if ($this->objectType === null) {
+ $this->objectType = ObjectTypeCache::getInstance()->getObjectType($this->getDecoratedObject()->objectTypeID);
+ }
+
+ return $this->objectType;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment;
+use wcf\data\like\object\ILikeObject;
+use wcf\data\like\ILikeObjectTypeProvider;
+use wcf\data\object\type\AbstractObjectTypeProvider;
+use wcf\system\comment\CommentHandler;
+
+/**
+ * Object type provider for comments
+ *
+ * @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.comment
+ * @subpackage data.comment
+ * @category Community Framework
+ */
+class LikeableCommentProvider extends AbstractObjectTypeProvider implements ILikeObjectTypeProvider {
+ /**
+ * @see wcf\data\object\type\AbstractObjectTypeProvider::$className
+ */
+ public $className = 'wcf\data\comment\Comment';
+
+ /**
+ * @see wcf\data\object\type\AbstractObjectTypeProvider::$decoratorClassName
+ */
+ public $decoratorClassName = 'wcf\data\comment\LikeableComment';
+
+ /**
+ * @see wcf\data\object\type\AbstractObjectTypeProvider::$listClassName
+ */
+ public $listClassName = 'wcf\data\comment\CommentList';
+
+ /**
+ * @see wcf\data\like\ILikeObjectTypeProvider::checkPermissions()
+ */
+ public function checkPermissions(ILikeObject $comment) {
+ $objectType = CommentHandler::getInstance()->getObjectType($comment->objectTypeID);
+ return CommentHandler::getInstance()->getCommentManager($objectType->objectType)->isAccessible($comment->objectID);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment;
+use wcf\data\comment\response\StructuredCommentResponse;
+use wcf\data\user\UserProfile;
+use wcf\data\DatabaseObjectDecorator;
+
+/**
+ * Provides methods to handle responses for this comment.
+ *
+ * @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.comment
+ * @subpackage data.comment
+ * @category Community Framework
+ */
+class StructuredComment extends DatabaseObjectDecorator implements \Countable, \Iterator {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ public static $baseClass = 'wcf\data\comment\Comment';
+
+ /**
+ * list of ordered responses
+ * @var array<wcf\data\comment\response\StructuredCommentResponse>
+ */
+ protected $responses = array();
+
+ /**
+ * deletable by current user
+ * @var boolean
+ */
+ public $deletable = false;
+
+ /**
+ * editable by current user
+ * @var boolean
+ */
+ public $editable = false;
+
+ /**
+ * iterator index
+ * @var integer
+ */
+ private $position = 0;
+
+ /**
+ * user profile object
+ * @var wcf\data\user\UserProfile
+ */
+ public $userProfile = null;
+
+ /**
+ * Adds an response
+ *
+ * @param wcf\data\comment\response\StructuredCommentResponse $response
+ */
+ public function addResponse(StructuredCommentResponse $response) {
+ $this->responses[] = $response;
+ }
+
+ /**
+ * Returns the last responses for this comment.
+ *
+ * @return array<wcf\data\comment\response\StructuredCommentReponse>
+ */
+ public function getResponses() {
+ return $this->responses;
+ }
+
+ /**
+ * Returns timestamp of oldest response loaded.
+ *
+ * @return integer
+ */
+ public function getLastResponseTime() {
+ $lastResponseTime = 0;
+ foreach ($this->responses as $response) {
+ if (!$lastResponseTime) {
+ $lastResponseTime = $response->time;
+ }
+
+ $lastResponseTime = min($lastResponseTime, $response->time);
+ }
+
+ return $lastResponseTime;
+ }
+
+ /**
+ * Sets the user's profile.
+ *
+ * @param wcf\data\user\UserProfile $userProfile
+ */
+ public function setUserProfile(UserProfile $userProfile) {
+ $this->userProfile = $userProfile;
+ }
+
+ /**
+ * Returns the user's profile.
+ *
+ * @return wcf\data\user\UserProfile
+ */
+ public function getUserProfile() {
+ return $this->userProfile;
+ }
+
+ /**
+ * Sets deletable state.
+ *
+ * @param boolean $deletable
+ */
+ public function setIsDeletable($deletable) {
+ $this->deletable = $deletable;
+ }
+
+ /**
+ * Sets editable state.
+ *
+ * @param boolean $editable
+ */
+ public function setIsEditable($editable) {
+ $this->editable = $editable;
+ }
+
+ /**
+ * Returns true if the comment is deletable by current user.
+ *
+ * @return boolean
+ */
+ public function isDeletable() {
+ return $this->deletable;
+ }
+
+ /**
+ * Returns true if the comment is editable by current user.
+ *
+ * @return boolean
+ */
+ public function isEditable() {
+ return $this->editable;
+ }
+
+ /**
+ * @see \Countable::count()
+ */
+ public function count() {
+ return count($this->responses);
+ }
+
+ /**
+ * @see \Iterator::current()
+ */
+ public function current() {
+ return $this->responses[$this->position];
+ }
+
+ /**
+ * @see \Iterator::key()
+ */
+ public function key() {
+ return $this->postition;
+ }
+
+ /**
+ * @see \Iterator::next()
+ */
+ public function next() {
+ $this->position++;
+ }
+
+ /**
+ * @see \Iterator::rewind()
+ */
+ public function rewind() {
+ $this->position = 0;
+ }
+
+ /**
+ * @see \Iterator::valid()
+ */
+ public function valid() {
+ return isset($this->responses[$this->position]);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment;
+use wcf\data\comment\response\CommentResponseList;
+use wcf\data\comment\response\StructuredCommentResponse;
+use wcf\data\user\UserProfile;
+use wcf\system\comment\manager\ICommentManager;
+use wcf\system\like\LikeHandler;
+
+/**
+ * Provides a structured comment list fetching last responses for every comment.
+ *
+ * @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.comment
+ * @subpackage data.comment
+ * @category Community Framework
+ */
+class StructuredCommentList extends CommentList {
+ /**
+ * comment manager object
+ * @var wcf\system\comment\manager\ICommentManager
+ */
+ public $commentManager = null;
+
+ /**
+ * minimum comment time
+ * @var integer
+ */
+ public $minCommentTime = 0;
+
+ /**
+ * object type id
+ * @var integer
+ */
+ public $objectTypeID = 0;
+
+ /**
+ * object id
+ * @var integer
+ */
+ public $objectID = 0;
+
+ /**
+ * ids of the responses of the comments in the list
+ * @var array<integer>
+ */
+ public $responseIDs = array();
+
+ /**
+ * @see wcf\data\DatabaseObjectList::$sqlLimit
+ */
+ public $sqlLimit = 10;
+
+ /**
+ * @see wcf\data\DatabaseObjectList::$sqlOrderBy
+ */
+ public $sqlOrderBy = 'comment.time DESC';
+
+ /**
+ * Creates a new structured comment list.
+ *
+ * @param wcf\system\comment\manager\ICommentManager $commentManager
+ * @param integer $objectTypeID
+ * @param integer $objectID
+ */
+ public function __construct(ICommentManager $commentManager, $objectTypeID, $objectID) {
+ parent::__construct();
+
+ $this->commentManager = $commentManager;
+ $this->objectTypeID = $objectTypeID;
+ $this->objectID = $objectID;
+
+ $this->getConditionBuilder()->add("comment.objectTypeID = ?", array($objectTypeID));
+ $this->getConditionBuilder()->add("comment.objectID = ?", array($objectID));
+ $this->sqlLimit = $this->commentManager->getCommentsPerPage();
+ }
+
+ /**
+ * @see wcf\data\DatabaseObjectList::readObjects()
+ */
+ public function readObjects() {
+ parent::readObjects();
+
+ // fetch last response ids
+ $responseIDs = array();
+ $userIDs = array();
+ foreach ($this->objects as &$comment) {
+ if (!$this->minCommentTime || $comment->time < $this->minCommentTime) $this->minCommentTime = $comment->time;
+ $lastResponseIDs = $comment->getLastResponseIDs();
+ if (!empty($lastResponseIDs)) {
+ foreach ($lastResponseIDs as $responseID) {
+ $this->responseIDs[] = $responseID;
+ $responseIDs[$responseID] = $comment->commentID;
+ }
+ }
+ $userIDs[] = $comment->userID;
+
+ $comment = new StructuredComment($comment);
+ $comment->setIsDeletable($this->commentManager->canDeleteComment($comment->getDecoratedObject()));
+ $comment->setIsEditable($this->commentManager->canEditComment($comment->getDecoratedObject()));
+ }
+ unset($comment);
+
+ // fetch last responses
+ if (!empty($responseIDs)) {
+ // invert sort order (maintains order within StructuredComment's response array)
+ $sqlOrder = (strpos($this->sqlOrderBy, 'ASC') === false) ? 'DESC' : 'ASC';
+
+ $responseList = new CommentResponseList();
+ $responseList->getConditionBuilder()->add("comment_response.responseID IN (?)", array(array_keys($responseIDs)));
+ $responseList->sqlOrderBy = "comment_response.time ".$sqlOrder;
+ $responseList->readObjects();
+
+ foreach ($responseList as $response) {
+ $response = new StructuredCommentResponse($response);
+ $response->setIsDeletable($this->commentManager->canDeleteResponse($response->getDecoratedObject()));
+ $response->setIsEditable($this->commentManager->canEditResponse($response->getDecoratedObject()));
+
+ $commentID = $responseIDs[$response->responseID];
+ $this->objects[$commentID]->addResponse($response);
+
+ $userIDs[] = $response->userID;
+ }
+ }
+
+ // fetch user data and avatars
+ if (!empty($userIDs)) {
+ $userIDs = array_unique($userIDs);
+
+ $users = UserProfile::getUserProfiles($userIDs);
+ foreach ($this->objects as $comment) {
+ if (isset($users[$comment->userID])) {
+ $comment->setUserProfile($users[$comment->userID]);
+ }
+
+ foreach ($comment as $response) {
+ if (isset($users[$response->userID])) {
+ $response->setUserProfile($users[$response->userID]);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Fetches the like data.
+ *
+ * @return array
+ */
+ public function getLikeData() {
+ if (empty($this->objectIDs)) return array();
+
+ $likeData = array();
+ $commentObjectType = LikeHandler::getInstance()->getObjectType('com.woltlab.wcf.comment');
+ LikeHandler::getInstance()->loadLikeObjects($commentObjectType, $this->getObjectIDs());
+ $likeData['comment'] = LikeHandler::getInstance()->getLikeObjects($commentObjectType);
+
+ if (!empty($this->responseIDs)) {
+ $responseObjectType = LikeHandler::getInstance()->getObjectType('com.woltlab.wcf.comment.response');
+ LikeHandler::getInstance()->loadLikeObjects($responseObjectType, $this->responseIDs);
+ $likeData['response'] = LikeHandler::getInstance()->getLikeObjects($responseObjectType);
+ }
+
+ return $likeData;
+ }
+
+ /**
+ * Returns minimum comment time.
+ *
+ * @return integer
+ */
+ public function getMinCommentTime() {
+ return $this->minCommentTime;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment;
+use wcf\data\user\User;
+use wcf\data\user\UserProfile;
+use wcf\data\DatabaseObjectDecorator;
+
+/**
+ * Represents a viewable comment.
+ *
+ * @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.comment
+ * @subpackage data.comment
+ * @category Community Framework
+ */
+class ViewableComment extends DatabaseObjectDecorator {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\comment\Comment';
+
+ /**
+ * user profile object
+ * @var wcf\data\user\UserProfile
+ */
+ protected $userProfile = null;
+
+ /**
+ * Returns the user profile object.
+ *
+ * @return wcf\data\user\UserProfile
+ */
+ public function getUserProfile() {
+ if ($this->userProfile === null) {
+ $this->userProfile = new UserProfile(new User(null, $this->getDecoratedObject()->data));
+ }
+
+ return $this->userProfile;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment\response;
+use wcf\data\comment\Comment;
+use wcf\data\DatabaseObject;
+use wcf\data\IMessage;
+use wcf\system\bbcode\SimpleMessageParser;
+use wcf\system\comment\CommentHandler;
+use wcf\util\StringUtil;
+
+/**
+ * Represents a comment response.
+ *
+ * @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.comment
+ * @subpackage data.comment.response
+ * @category Community Framework
+ */
+class CommentResponse extends DatabaseObject implements IMessage {
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'comment_response';
+
+ /**
+ * @see wcf\data\DatabaseObject::$databaseTableIndexName
+ */
+ protected static $databaseTableIndexName = 'responseID';
+
+ /**
+ * comment object
+ * @var wcf\data\comment\Comment
+ */
+ protected $comment = null;
+
+ /**
+ * @see wcf\data\IMessage::getFormattedMessage()
+ */
+ public function getFormattedMessage() {
+ return SimpleMessageParser::getInstance()->parse($this->message);
+ }
+
+ /**
+ * Returns comment object related to this response.
+ *
+ * @return wcf\data\comment\Comment
+ */
+ public function getComment() {
+ if ($this->comment === null) {
+ $this->comment = new Comment($this->commentID);
+ }
+
+ return $this->comment;
+ }
+
+ /**
+ * Sets related comment object.
+ *
+ * @param wcf\data\comment\Comment
+ */
+ public function setComment(Comment $comment) {
+ if ($this->commentID == $comment->commentID) {
+ $this->comment = $comment;
+ }
+ }
+
+ /**
+ * @see wcf\data\IMessage::getExcerpt()
+ */
+ public function getExcerpt($maxLength = 255) {
+ return StringUtil::truncateHTML($this->getFormattedMessage(), $maxLength);
+ }
+
+ /**
+ * @see wcf\data\IMessage::getMessage()
+ */
+ public function getMessage() {
+ return $this->message;
+ }
+
+ /**
+ * @see wcf\data\IUserContent::getTime()
+ */
+ public function getTime() {
+ return $this->time;
+ }
+
+ /**
+ * @see wcf\data\IUserContent::getUserID()
+ */
+ public function getUserID() {
+ return $this->userID;
+ }
+
+ /**
+ * @see wcf\data\IUserContent::getUsername()
+ */
+ public function getUsername() {
+ return $this->username;
+ }
+
+ /**
+ * @see wcf\data\ILinkableObject::getLink()
+ */
+ public function getLink() {
+ return CommentHandler::getInstance()->getObjectType($this->getComment()->objectTypeID)->getProcessor()->getLink($this->getComment()->objectTypeID, $this->getComment()->objectID);
+ }
+
+ /**
+ * @see wcf\data\ITitledObject::getTitle()
+ */
+ public function getTitle() {
+ return CommentHandler::getInstance()->getObjectType($this->getComment()->objectTypeID)->getProcessor()->getTitle($this->getComment()->objectTypeID, $this->getComment()->objectID, true);
+ }
+
+ /**
+ * @see wcf\data\IMessage::isVisible()
+ */
+ public function isVisible() {
+ return true;
+ }
+
+ /**
+ * @see wcf\data\IMessage::__toString()
+ */
+ public function __toString() {
+ return $this->getFormattedMessage();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment\response;
+use wcf\data\comment\Comment;
+use wcf\data\comment\CommentEditor;
+use wcf\data\comment\CommentList;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\system\exception\PermissionDeniedException;
+use wcf\system\exception\UserInputException;
+use wcf\system\user\activity\event\UserActivityEventHandler;
+use wcf\system\user\notification\UserNotificationHandler;
+use wcf\system\WCF;
+
+/**
+ * Executes comment response-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.comment
+ * @subpackage data.comment.response
+ * @category Community Framework
+ */
+class CommentResponseAction extends AbstractDatabaseObjectAction {
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$allowGuestAccess
+ */
+ protected $allowGuestAccess = array('loadResponses');
+
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::$className
+ */
+ protected $className = 'wcf\data\comment\response\CommentResponseEditor';
+
+ /**
+ * comment object
+ * @var wcf\data\comment\Comment
+ */
+ public $comment = null;
+
+ /**
+ * comment manager object
+ * @var wcf\system\comment\manager\ICommentManager
+ */
+ public $commentManager = null;
+
+ /**
+ * @see wcf\data\AbstractDatabaseObjectAction::delete()
+ */
+ public function delete() {
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ if (empty($this->objects)) {
+ return 0;
+ }
+
+ // read object type ids for comments
+ $commentIDs = array();
+ foreach ($this->objects as $response) {
+ $commentIDs[] = $response->commentID;
+ }
+
+ $commentList = new CommentList();
+ $commentList->getConditionBuilder()->add("comment.commentID IN (?)", array($commentIDs));
+ $commentList->readObjects();
+ $comments = $commentList->getObjects();
+
+ // update counters
+ $processors = $responseIDs = $updateComments = array();
+ foreach ($this->objects as $response) {
+ $objectTypeID = $comments[$response->commentID]->objectTypeID;
+
+ if (!isset($processors[$objectTypeID])) {
+ $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID);
+ $processors[$objectTypeID] = $objectType->getProcessor();
+ $responseIDs[$objectTypeID] = array();
+ }
+
+ $processors[$objectTypeID]->updateCounter($comments[$response->commentID]->objectID, -1);
+ $responseIDs[$objectTypeID][] = $response->responseID;
+
+ if (!isset($updateComments[$response->commentID])) {
+ $updateComments[$response->commentID] = 0;
+ }
+
+ $updateComments[$response->commentID]++;
+ }
+
+ // remove responses
+ $count = parent::delete();
+
+ // update comment responses and cached response ids
+ foreach ($comments as $comment) {
+ $commentEditor = new CommentEditor($comment);
+ $commentEditor->updateLastResponseIDs();
+ $commentEditor->updateCounters(array(
+ 'responses' => -1 * $updateComments[$comment->commentID]
+ ));
+ }
+
+ foreach ($responseIDs as $objectTypeID => $objectIDs) {
+ // remove activity events
+ $objectType = ObjectTypeCache::getInstance()->getObjectType($objectTypeID);
+ if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectType->objectType.'.response.recentActivityEvent')) {
+ UserActivityEventHandler::getInstance()->removeEvents($objectType->objectType.'.response.recentActivityEvent', $objectIDs);
+ }
+
+ // delete notifications
+ if (UserNotificationHandler::getInstance()->getObjectTypeID($objectType->objectType.'.response.notification')) {
+ UserNotificationHandler::getInstance()->deleteNotifications('commentResponse', $objectType->objectType.'.response.notification', array(), $objectIDs);
+ UserNotificationHandler::getInstance()->deleteNotifications('commentResponseOwner', $objectType->objectType.'.response.notification', array(), $objectIDs);
+ }
+ }
+
+ return $count;
+ }
+
+ /**
+ * Validates parameters to load responses for a given comment id.
+ */
+ public function validateLoadResponses() {
+ $this->readInteger('commentID', false, 'data');
+ $this->readInteger('lastResponseTime', false, 'data');
+
+ $this->comment = new Comment($this->parameters['data']['commentID']);
+ if (!$this->comment->commentID) {
+ throw new UserInputException('commentID');
+ }
+
+ $this->commentManager = ObjectTypeCache::getInstance()->getObjectType($this->comment->objectTypeID)->getProcessor();
+ if (!$this->commentManager->isAccessible($this->comment->objectID)) {
+ throw new PermissionDeniedException();
+ }
+ }
+
+ /**
+ * Returns parsed responses for given comment id.
+ *
+ * @return array
+ */
+ public function loadResponses() {
+ // get response list
+ $responseList = new StructuredCommentResponseList($this->commentManager, $this->comment);
+ $responseList->getConditionBuilder()->add("comment_response.time < ?", array($this->parameters['data']['lastResponseTime']));
+ $responseList->sqlLimit = 50;
+ $responseList->readObjects();
+
+ $lastResponseTime = 0;
+ foreach ($responseList as $response) {
+ if (!$lastResponseTime) {
+ $lastResponseTime = $response->time;
+ }
+
+ $lastResponseTime = min($lastResponseTime, $response->time);
+ }
+
+ WCF::getTPL()->assign(array(
+ 'likeData' => (MODULE_LIKE ? $responseList->getLikeData() : array()),
+ 'responseList' => $responseList
+ ));
+
+ return array(
+ 'commentID' => $this->comment->commentID,
+ 'lastResponseTime' => $lastResponseTime,
+ 'template' => WCF::getTPL()->fetch('commentResponseList')
+ );
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment\response;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit comment responses.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.comment
+ * @subpackage data.comment.response
+ * @category Community Framework
+ */
+class CommentResponseEditor extends DatabaseObjectEditor {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\comment\response\CommentResponse';
+}
--- /dev/null
+<?php
+namespace wcf\data\comment\response;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of comment responses.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.comment
+ * @subpackage data.comment.response
+ * @category Community Framework
+ */
+class CommentResponseList extends DatabaseObjectList {
+ /**
+ * @see wcf\data\DatabaseObjectList::$className
+ */
+ public $className = 'wcf\data\comment\response\CommentResponse';
+}
--- /dev/null
+<?php
+namespace wcf\data\comment\response;
+use wcf\data\like\object\AbstractLikeObject;
+use wcf\data\object\type\ObjectTypeCache;
+
+/**
+ * Likeable object implementation for comment reponses.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.comment
+ * @subpackage data.comment.response
+ * @category Community Framework
+ */
+class LikeableCommentResponse extends AbstractLikeObject {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\comment\response\CommentResponse';
+
+ /**
+ * @see wcf\data\like\object\ILikeObject::getObjectType()
+ */
+ public function getObjectType() {
+ if ($this->objectType === null) {
+ $this->objectType = ObjectTypeCache::getInstance()->getObjectType($this->getDecoratedObject()->objectTypeID);
+ }
+
+ return $this->objectType;
+ }
+
+ /**
+ * @see wcf\data\ITitledObject::getTitle()
+ */
+ public function getTitle() {
+ return $this->message;
+ }
+
+ /**
+ * @see wcf\data\like\object\ILikeObject::getURL()
+ */
+ public function getURL() {
+ return $this->getLink();
+ }
+
+ /**
+ * @see wcf\data\like\object\ILikeObject::getUserID()
+ */
+ public function getUserID() {
+ return $this->userID;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment\response;
+use wcf\data\comment\Comment;
+use wcf\data\like\object\ILikeObject;
+use wcf\data\like\ILikeObjectTypeProvider;
+use wcf\data\object\type\AbstractObjectTypeProvider;
+use wcf\system\comment\CommentHandler;
+
+/**
+ * Object type provider for likeable comment responses.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2013 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.comment
+ * @subpackage data.comment.response
+ * @category Community Framework
+ */
+class LikeableCommentResponseProvider extends AbstractObjectTypeProvider implements ILikeObjectTypeProvider {
+ /**
+ * @see wcf\data\object\type\AbstractObjectTypeProvider::$className
+ */
+ public $className = 'wcf\data\comment\response\CommentResponse';
+
+ /**
+ * @see wcf\data\object\type\AbstractObjectTypeProvider::$decoratorClassName
+ */
+ public $decoratorClassName = 'wcf\data\comment\response\LikeableCommentResponse';
+
+ /**
+ * @see wcf\data\object\type\AbstractObjectTypeProvider::$listClassName
+ */
+ public $listClassName = 'wcf\data\comment\response\CommentResponseList';
+
+ /**
+ * @see wcf\data\like\ILikeObjectTypeProvider::checkPermissions()
+ */
+ public function checkPermissions(ILikeObject $response) {
+ $comment = new Comment($response->commentID);
+ if (!$comment->commentID) {
+ return false;
+ }
+
+ $objectType = CommentHandler::getInstance()->getObjectType($comment->objectTypeID);
+ return CommentHandler::getInstance()->getCommentManager($objectType->objectType)->isAccessible($comment->objectID);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment\response;
+use wcf\data\user\UserProfile;
+use wcf\data\DatabaseObjectDecorator;
+
+/**
+ * Provides methods to handle response data.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf.comment
+ * @subpackage data.comment.response
+ * @category Community Framework
+ */
+class StructuredCommentResponse extends DatabaseObjectDecorator {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ public static $baseClass = 'wcf\data\comment\response\CommentResponse';
+
+ /**
+ * deletable by current user
+ * @var boolean
+ */
+ public $deletable = false;
+
+ /**
+ * editable for current user
+ * @var boolean
+ */
+ public $editable = false;
+
+ /**
+ * user profile object
+ * @var wcf\data\user\UserProfile
+ */
+ public $userProfile = null;
+
+ /**
+ * Sets the user's profile.
+ *
+ * @param wcf\data\user\UserProfile $userProfile
+ */
+ public function setUserProfile(UserProfile $userProfile) {
+ $this->userProfile = $userProfile;
+ }
+
+ /**
+ * Returns the user's profile.
+ *
+ * @return wcf\data\user\UserProfile
+ */
+ public function getUserProfile() {
+ return $this->userProfile;
+ }
+
+ /**
+ * Returns a structured response.
+ *
+ * @param integer $responseID
+ * @return wcf\data\comment\response\StructuredCommentResponse
+ */
+ public static function getResponse($responseID) {
+ $response = new CommentResponse($responseID);
+ if (!$response->responseID) {
+ return null;
+ }
+
+ // prepare structured response
+ $response = new StructuredCommentResponse($response);
+
+ // add user profile
+ $userProfile = UserProfile::getUserProfile($response->userID);
+ $response->setUserProfile($userProfile);
+
+ return $response;
+ }
+
+ /**
+ * Sets deletable state.
+ *
+ * @param boolean $deletable
+ */
+ public function setIsDeletable($deletable) {
+ $this->deletable = $deletable;
+ }
+
+ /**
+ * Sets editable state.
+ *
+ * @param boolean $editable
+ */
+ public function setIsEditable($editable) {
+ $this->editable = $editable;
+ }
+
+ /**
+ * Returns true if the response is deletable by current user.
+ *
+ * @return boolean
+ */
+ public function isDeletable() {
+ return $this->deletable;
+ }
+
+ /**
+ * Returns true if the response is editable by current user.
+ *
+ * @return boolean
+ */
+ public function isEditable() {
+ return $this->editable;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment\response;
+use wcf\data\comment\Comment;
+use wcf\data\user\UserProfile;
+use wcf\system\comment\manager\ICommentManager;
+use wcf\system\like\LikeHandler;
+
+/**
+ * Provides a structured comment response list.
+ *
+ * @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.comment
+ * @subpackage data.comment.response
+ * @category Community Framework
+ */
+class StructuredCommentResponseList extends CommentResponseList {
+ /**
+ * comment object
+ * @var wcf\data\comment\Comment;
+ */
+ public $comment = null;
+
+ /**
+ * comment manager
+ * @var wcf\system\comment\manager\ICommentManager
+ */
+ public $commentManager = null;
+
+ /**
+ * minimum response time
+ * @var integer
+ */
+ public $minResponseTime = 0;
+
+ /**
+ * @see wcf\data\DatabaseObjectList::$sqlLimit
+ */
+ public $sqlLimit = 50;
+
+ /**
+ * @see wcf\data\DatabaseObjectList::$sqlOrderBy
+ */
+ public $sqlOrderBy = 'comment_response.time DESC';
+
+ /**
+ * Creates a new structured comment response list.
+ *
+ * @param wcf\system\comment\manager\ICommentManager $commentManager
+ * @param wcf\data\comment\Comment $comment
+ */
+ public function __construct(ICommentManager $commentManager, Comment $comment) {
+ parent::__construct();
+
+ $this->comment = $comment;
+ $this->commentManager = $commentManager;
+
+ $this->getConditionBuilder()->add("comment_response.commentID = ?", array($this->comment->commentID));
+ $this->sqlLimit = $this->commentManager->getCommentsPerPage();
+ }
+
+ /**
+ * @see wcf\data\DatabaseObjectList::readObjects()
+ */
+ public function readObjects() {
+ parent::readObjects();
+
+ // get user ids
+ $userIDs = array();
+ foreach ($this->objects as &$response) {
+ if (!$this->minResponseTime || $response->time < $this->minResponseTime) $this->minResponseTime = $response->time;
+ $userIDs[] = $response->userID;
+
+ $response = new StructuredCommentResponse($response);
+ $response->setIsDeletable($this->commentManager->canDeleteResponse($response->getDecoratedObject()));
+ $response->setIsEditable($this->commentManager->canEditResponse($response->getDecoratedObject()));
+ }
+ unset($response);
+
+ // fetch user data and avatars
+ if (!empty($userIDs)) {
+ $userIDs = array_unique($userIDs);
+
+ $users = UserProfile::getUserProfiles($userIDs);
+ foreach ($this->objects as $response) {
+ if (isset($users[$response->userID])) {
+ $response->setUserProfile($users[$response->userID]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Fetches the like data.
+ *
+ * @return array
+ */
+ public function getLikeData() {
+ if (empty($this->objectIDs)) return array();
+
+ $objectType = LikeHandler::getInstance()->getObjectType('com.woltlab.wcf.comment.response');
+ LikeHandler::getInstance()->loadLikeObjects($objectType, $this->objectIDs);
+ $likeData = array('response' => LikeHandler::getInstance()->getLikeObjects($objectType));
+
+ return $likeData;
+ }
+
+ /**
+ * Returns mimimum response time.
+ *
+ * @return integer
+ */
+ public function getMinResponseTime() {
+ return $this->minResponseTime;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\comment\response;
+use wcf\data\user\User;
+use wcf\data\user\UserProfile;
+use wcf\data\DatabaseObjectDecorator;
+
+/**
+ * Represents a viewable comment response.
+ *
+ * @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.comment
+ * @subpackage data.comment.response
+ * @category Community Framework
+ */
+class ViewableCommentResponse extends DatabaseObjectDecorator {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\comment\response\CommentResponse';
+
+ /**
+ * user profile object
+ * @var wcf\data\user\UserProfile
+ */
+ protected $userProfile = null;
+
+ /**
+ * Returns the user profile object.
+ *
+ * @return wcf\data\user\UserProfile
+ */
+ public function getUserProfile() {
+ if ($this->userProfile === null) {
+ $this->userProfile = new UserProfile(new User(null, $this->getDecoratedObject()->data));
+ }
+
+ return $this->userProfile;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\comment;
+use wcf\data\comment\response\CommentResponseList;
+use wcf\data\comment\CommentEditor;
+use wcf\data\comment\CommentList;
+use wcf\data\comment\StructuredCommentList;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\comment\manager\ICommentManager;
+use wcf\system\exception\SystemException;
+use wcf\system\like\LikeHandler;
+use wcf\system\user\activity\event\UserActivityEventHandler;
+use wcf\system\user\notification\UserNotificationHandler;
+use wcf\system\SingletonFactory;
+
+/**
+ * Provides methods for comment object handling.
+ *
+ * @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.comment
+ * @subpackage system.comment
+ * @category Community Framework
+ */
+class CommentHandler extends SingletonFactory {
+ /**
+ * cached object types
+ * @var array<array>
+ */
+ protected $cache = null;
+
+ /**
+ * @see wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ $this->cache = array(
+ 'objectTypes' => array(),
+ 'objectTypeIDs' => array()
+ );
+
+ $cache = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.comment.commentableContent');
+ foreach ($cache as $objectType) {
+ $this->cache['objectTypes'][$objectType->objectTypeID] = $objectType;
+ $this->cache['objectTypeIDs'][$objectType->objectType] = $objectType->objectTypeID;
+ }
+ }
+
+ /**
+ * Returns the object type id for a given object type.
+ *
+ * @param string $objectType
+ * @return integer
+ */
+ public function getObjectTypeID($objectType) {
+ if (isset($this->cache['objectTypeIDs'][$objectType])) {
+ return $this->cache['objectTypeIDs'][$objectType];
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the object type for a given object type id.
+ *
+ * @param integer $objectTypeID
+ * @return wcf\data\object\type\ObjectType
+ */
+ public function getObjectType($objectTypeID) {
+ if (isset($this->cache['objectTypes'][$objectTypeID])) {
+ return $this->cache['objectTypes'][$objectTypeID];
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns comment manager object for given object type.
+ *
+ * @param string $objectType
+ * @return wcf\system\comment\manager\ICommentManager
+ */
+ public function getCommentManager($objectType) {
+ $objectTypeID = $this->getObjectTypeID($objectType);
+ if ($objectTypeID === null) {
+ throw new SystemException("Unable to find object type for '".$objectType."'");
+ }
+
+ return $this->getObjectType($objectTypeID)->getProcessor();
+
+ }
+
+ /**
+ * Returns a comment list for a given object type and object id.
+ *
+ * @param wcf\data\comment\manager\ICommentManager $commentManager
+ * @param integer $objectTypeID
+ * @param integer $objectID
+ * @param boolean $readObjects
+ * @return wcf\data\comment\StructuredCommentList
+ */
+ public function getCommentList(ICommentManager $commentManager, $objectTypeID, $objectID, $readObjects = true) {
+ $commentList = new StructuredCommentList($commentManager, $objectTypeID, $objectID);
+ if ($readObjects) {
+ $commentList->readObjects();
+ }
+
+ return $commentList;
+ }
+
+ /**
+ * Removes all comments for given objects.
+ *
+ * @param string $objectType
+ * @param array<integer> $objectIDs
+ */
+ public function deleteObjects($objectType, array $objectIDs) {
+ $objectTypeID = $this->getObjectTypeID($objectType);
+ $objectTypeObj = $this->getObjectType($objectTypeID);
+
+ // get comment ids
+ $commentList = new CommentList();
+ $commentList->getConditionBuilder()->add('comment.objectTypeID = ?', array($objectTypeID));
+ $commentList->getConditionBuilder()->add('comment.objectID IN (?)', array($objectIDs));
+ $commentList->readObjectIDs();
+ $commentIDs = $commentList->getObjectIDs();
+
+ // no comments -> skip
+ if (empty($commentIDs)) return;
+
+ // get response ids
+ $responseList = new CommentResponseList();
+ $responseList->getConditionBuilder()->add('comment_response.commentID IN (?)', array($commentIDs));
+ $responseList->readObjectIDs();
+ $responseIDs = $responseList->getObjectIDs();
+
+ // delete likes
+ LikeHandler::getInstance()->removeLikes('com.woltlab.wcf.comment', $commentIDs);
+
+ // delete activity events
+ if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectTypeObj->objectType.'.recentActivityEvent')) {
+ UserActivityEventHandler::getInstance()->removeEvents($objectTypeObj->objectType.'.recentActivityEvent', $commentIDs);
+ }
+ // delete notifications
+ if (UserNotificationHandler::getInstance()->getObjectTypeID($objectTypeObj->objectType.'.notification')) {
+ UserNotificationHandler::getInstance()->deleteNotifications('comment', $objectTypeObj->objectType.'.notification', array(), $commentIDs);
+ }
+
+ if (!empty($responseIDs)) {
+ // delete activity events (for responses)
+ if (UserActivityEventHandler::getInstance()->getObjectTypeID($objectTypeObj->objectType.'.response.recentActivityEvent')) {
+ UserActivityEventHandler::getInstance()->removeEvents($objectTypeObj->objectType.'.response.recentActivityEvent', $responseIDs);
+ }
+ // delete notifications (for responses)
+ if (UserNotificationHandler::getInstance()->getObjectTypeID($objectTypeObj->objectType.'.response.notification')) {
+ UserNotificationHandler::getInstance()->deleteNotifications('commentResponse', $objectTypeObj->objectType.'.response.notification', array(), $responseIDs);
+ UserNotificationHandler::getInstance()->deleteNotifications('commentResponseOwner', $objectTypeObj->objectType.'.response.notification', array(), $responseIDs);
+ }
+ }
+
+ // delete comments / responses
+ CommentEditor::deleteAll($commentIDs);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\comment\manager;
+use wcf\data\comment\response\CommentResponse;
+use wcf\data\comment\Comment;
+use wcf\system\SingletonFactory;
+use wcf\system\WCF;
+
+/**
+ * Default implementation for comment managers.
+ *
+ * @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.comment
+ * @subpackage system.comment.manager
+ * @category Community Framework
+ */
+abstract class AbstractCommentManager extends SingletonFactory implements ICommentManager {
+ /**
+ * display comments per page
+ * @var integer
+ */
+ public $commentsPerPage = 10;
+
+ /**
+ * permission name for comment/response creation
+ * @var string
+ */
+ protected $permissionAdd = '';
+
+ /**
+ * permission name for comment/response moderation
+ * @var string
+ */
+ protected $permissionCanModerate = '';
+
+ /**
+ * permission name for deletion of own comments/responses
+ * @var string
+ */
+ protected $permissionDelete = '';
+
+ /**
+ * permission name for editing of own comments/responses
+ * @var string
+ */
+ protected $permissionEdit = '';
+
+ /**
+ * permission name for deletion of comments/responses (moderator)
+ * @var string
+ */
+ protected $permissionModDelete = '';
+
+ /**
+ * permission name for editing of comments/responses (moderator)
+ * @var string
+ */
+ protected $permissionModEdit = '';
+
+ /**
+ * @see wcf\system\comment\manager\ICommentManager::canAdd()
+ */
+ public function canAdd($objectID) {
+ if (!$this->isAccessible($objectID, true)) {
+ return false;
+ }
+
+ return (WCF::getSession()->getPermission($this->permissionAdd) ? true : false);
+ }
+
+ /**
+ * @see wcf\system\comment\manager\ICommentManager::canEditComment()
+ */
+ public function canEditComment(Comment $comment) {
+ return $this->canEdit(($comment->userID == WCF::getUser()->userID));
+ }
+
+ /**
+ * @see wcf\system\comment\manager\ICommentManager::canEditResponse()
+ */
+ public function canEditResponse(CommentResponse $response) {
+ return $this->canEdit(($response->userID == WCF::getUser()->userID));
+ }
+
+ /**
+ * @see wcf\system\comment\manager\ICommentManager::canDeleteComment()
+ */
+ public function canDeleteComment(Comment $comment) {
+ return $this->canDelete(($comment->userID == WCF::getUser()->userID));
+ }
+
+ /**
+ * @see wcf\system\comment\manager\ICommentManager::canDeleteResponse()
+ */
+ public function canDeleteResponse(CommentResponse $response) {
+ return $this->canDelete(($response->userID == WCF::getUser()->userID));
+ }
+
+ /**
+ * @see wcf\system\comment\manager\ICommentManager::canModerate()
+ */
+ public function canModerate($objectTypeID, $objectID) {
+ return (WCF::getSession()->getPermission($this->permissionCanModerate) ? true : false);
+ }
+
+ /**
+ * Returns true if the current user may edit a comment/response.
+ *
+ * @param boolean $isOwner
+ * @return boolean
+ */
+ protected function canEdit($isOwner) {
+ // disallow guests
+ if (!WCF::getUser()->userID) {
+ return false;
+ }
+
+ // check moderator permission
+ if (WCF::getSession()->getPermission($this->permissionModEdit)) {
+ return true;
+ }
+
+ // check user permission and ownership
+ if ($isOwner && WCF::getSession()->getPermission($this->permissionEdit)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the current user may delete a comment/response.
+ *
+ * @param boolean $isOwner
+ * @return boolean
+ */
+ protected function canDelete($isOwner) {
+ // disallow guests
+ if (!WCF::getUser()->userID) {
+ return false;
+ }
+
+ // check moderator permission
+ if (WCF::getSession()->getPermission($this->permissionModDelete)) {
+ return true;
+ }
+
+ // check user permission and ownership
+ if ($isOwner && WCF::getSession()->getPermission($this->permissionDelete)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @see wcf\system\comment\manager\ICommentManager::getCommentsPerPage()
+ */
+ public function getCommentsPerPage() {
+ return $this->commentsPerPage;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\comment\manager;
+use wcf\data\comment\response\CommentResponse;
+use wcf\data\comment\Comment;
+
+/**
+ * Default interface for comment managers.
+ *
+ * @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.comment
+ * @subpackage system.comment.manager
+ * @category Community Framework
+ */
+interface ICommentManager {
+ /**
+ * Returns true if the current user may add comments or responses.
+ *
+ * @param integer $objectID
+ * @return boolean
+ */
+ public function canAdd($objectID);
+
+ /**
+ * Returns true if the current user may edit given comment.
+ *
+ * @param wcf\data\comment\Comment $comment
+ * @return boolean
+ */
+ public function canEditComment(Comment $comment);
+
+ /**
+ * Returns true if the current user may edit given response.
+ *
+ * @param wcf\data\comment\response\CommentResponse $response
+ * @return boolean
+ */
+ public function canEditResponse(CommentResponse $response);
+
+ /**
+ * Returns true if the current user may delete given comment.
+ *
+ * @param wcf\data\comment\Comment $comment
+ * @return boolean
+ */
+ public function canDeleteComment(Comment $comment);
+
+ /**
+ * Returns true if the current user may delete given response.
+ *
+ * @param wcf\data\comment\response\CommentResponse $response
+ */
+ public function canDeleteResponse(CommentResponse $response);
+
+ /**
+ * Returns true if the current user may moderated content identified by
+ * object type id and object id.
+ *
+ * @param integer $objectTypeID
+ * @param integer $objectID
+ * @return boolean
+ */
+ public function canModerate($objectTypeID, $objectID);
+
+ /**
+ * Returns the amount of comments per page.
+ *
+ * @return integer
+ */
+ public function getCommentsPerPage();
+
+ /**
+ * Returns a link to given object type id and object id.
+ *
+ * @param integer $objectTypeID
+ * @param integer $objectID
+ * @return string
+ */
+ public function getLink($objectTypeID, $objectID);
+
+ /**
+ * Returns the title for a comment or response.
+ *
+ * @param integer $objectTypeID
+ * @param integer $objectID
+ * @param boolean $isResponse
+ * @return string
+ */
+ public function getTitle($objectTypeID, $objectID, $isResponse = false);
+
+ /**
+ * Returns true if comments and responses for given object id are accessible
+ * by current user.
+ *
+ * @param integer $objectID
+ * @param boolean $validateWritePermission
+ * @return boolean
+ */
+ public function isAccessible($objectID, $validateWritePermission = false);
+
+ /**
+ * Updates total count of comments (includes responses).
+ *
+ * @param integer $objectID
+ * @param integer $value
+ */
+ public function updateCounter($objectID, $value);
+}
--- /dev/null
+<?php
+namespace wcf\system\comment\manager;
+use wcf\data\user\UserProfile;
+use wcf\system\request\LinkHandler;
+use wcf\system\WCF;
+
+/**
+ * User profile comment manager implementation.
+ *
+ * @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.comment
+ * @subpackage system.comment.manager
+ * @category Community Framework
+ */
+class UserProfileCommentManager extends AbstractCommentManager {
+ /**
+ * @see wcf\system\comment\manager\AbstractCommentManager::$permissionAdd
+ */
+ protected $permissionAdd = 'user.profileComment.canAddComment';
+
+ /**
+ * @see wcf\system\comment\manager\AbstractCommentManager::$permissionCanModerate
+ */
+ protected $permissionCanModerate = 'mod.profileComment.canModerateComment';
+
+ /**
+ * @see wcf\system\comment\manager\AbstractCommentManager::$permissionDelete
+ */
+ protected $permissionDelete = 'user.profileComment.canDeleteComment';
+
+ /**
+ * @see wcf\system\comment\manager\AbstractCommentManager::$permissionEdit
+ */
+ protected $permissionEdit = 'user.profileComment.canEditComment';
+
+ /**
+ * @see wcf\system\comment\manager\AbstractCommentManager::$permissionModDelete
+ */
+ protected $permissionModDelete = 'mod.profileComment.canDeleteComment';
+
+ /**
+ * @see wcf\system\comment\manager\AbstractCommentManager::$permissionModEdit
+ */
+ protected $permissionModEdit = 'mod.profileComment.canEditComment';
+
+ /**
+ * @see wcf\system\comment\manager\ICommentManager::isAccessible()
+ */
+ public function isAccessible($objectID, $validateWritePermission = false) {
+ // check object id
+ $userProfile = UserProfile::getUserProfile($objectID);
+ if ($userProfile === null) {
+ return false;
+ }
+
+ // check visibility
+ if ($userProfile->isProtected()) {
+ return false;
+ }
+
+ // check target user settings
+ if ($validateWritePermission) {
+ if (!$userProfile->isAccessible('canWriteProfileComments') && $userProfile->userID != WCF::getUser()->userID) {
+ return false;
+ }
+
+ if ($userProfile->isIgnoredUser(WCF::getUser()->userID)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @see wcf\system\comment\manager\ICommentManager::getLink()
+ */
+ public function getLink($objectTypeID, $objectID) {
+ return LinkHandler::getInstance()->getLink('User', array('id' => $objectID));
+ }
+
+ /**
+ * @see wcf\system\comment\manager\ICommentManager::getTitle()
+ */
+ public function getTitle($objectTypeID, $objectID, $isResponse = false) {
+ if ($isResponse) return WCF::getLanguage()->get('wcf.user.profile.content.wall.commentResponse');
+
+ return WCF::getLanguage()->getDynamicVariable('wcf.user.profile.content.wall.comment');
+ }
+
+ /**
+ * @see wcf\system\comment\manager\ICommentManager::updateCounter()
+ */
+ public function updateCounter($objectID, $value) { }
+}
--- /dev/null
+<?php
+namespace wcf\system\menu\user\profile\content;
+use wcf\system\comment\CommentHandler;
+use wcf\system\SingletonFactory;
+use wcf\system\WCF;
+
+/**
+ * Handles user profile comment content.
+ *
+ * @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.comment
+ * @subpackage system.menu.user.profile.content
+ * @category Community Framework
+ */
+class CommentUserProfileMenuContent extends SingletonFactory implements IUserProfileMenuContent {
+ /**
+ * comment manager object
+ * @var wcf\system\comment\manager\ICommentManager
+ */
+ public $commentManager = null;
+
+ /**
+ * object type id
+ * @var integer
+ */
+ public $objectTypeID = 0;
+
+ /**
+ * @see wcf\system\menu\user\profile\content\IUserProfileMenuContent::getContent()
+ */
+ public function getContent($userID) {
+ if ($this->commentManager === null) {
+ $this->objectTypeID = CommentHandler::getInstance()->getObjectTypeID('com.woltlab.wcf.user.profileComment');
+ $objectType = CommentHandler::getInstance()->getObjectType($this->objectTypeID);
+ $this->commentManager = $objectType->getProcessor();
+ }
+
+ $commentList = CommentHandler::getInstance()->getCommentList($this->commentManager, $this->objectTypeID, $userID);
+
+ // assign variables
+ WCF::getTPL()->assign(array(
+ 'commentCanAdd' => $this->commentManager->canAdd($userID),
+ 'commentList' => $commentList,
+ 'commentObjectTypeID' => $this->objectTypeID,
+ 'userID' => $userID,
+ 'lastCommentTime' => $commentList->getMinCommentTime(),
+ 'likeData' => (MODULE_LIKE ? $commentList->getLikeData() : array())
+ ));
+
+ return WCF::getTPL()->fetch('userProfileCommentList');
+ }
+
+ /**
+ * @see wcf\system\menu\user\profile\content\IUserProfileMenuContent::isVisible()
+ */
+ public function isVisible($userID) {
+ return true;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\moderation\queue\report;
+use wcf\data\comment\Comment;
+use wcf\data\comment\CommentAction;
+use wcf\data\comment\CommentList;
+use wcf\data\comment\ViewableComment;
+use wcf\data\moderation\queue\ModerationQueue;
+use wcf\data\moderation\queue\ViewableModerationQueue;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\moderation\queue\AbstractModerationQueueHandler;
+use wcf\system\moderation\queue\ModerationQueueManager;
+use wcf\system\WCF;
+
+/**
+ * An implementation of IModerationQueueReportHandler for comments.
+ *
+ * @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.comment
+ * @subpackage system.moderation.queue
+ * @category Community Framework
+ */
+class CommentCommentModerationQueueReportHandler extends AbstractModerationQueueHandler implements IModerationQueueReportHandler {
+ /**
+ * @see wcf\system\moderation\queue\AbstractModerationQueueHandler::$definitionName
+ */
+ protected $definitionName = 'com.woltlab.wcf.moderation.report';
+
+ /**
+ * @see wcf\system\moderation\queue\AbstractModerationQueueHandler::$objectType
+ */
+ protected $objectType = 'com.woltlab.wcf.comment.comment';
+
+ /**
+ * list of comments
+ * @var array<wcf\data\comment\Comment>
+ */
+ protected static $comments = array();
+
+ /**
+ * list of comment managers
+ * @var array<wcf\system\comment\manager\ICommentManager>
+ */
+ protected static $commentManagers = array();
+
+ /**
+ * @see wcf\system\moderation\queue\IModerationQueueHandler::assignQueues()
+ */
+ public function assignQueues(array $queues) {
+ $assignments = array();
+
+ // read comments
+ $commentIDs = array();
+ foreach ($queues as $queue) {
+ $commentIDs[] = $queue->objectID;
+ }
+
+ $conditions = new PreparedStatementConditionBuilder();
+ $conditions->add("commentID IN (?)", array($commentIDs));
+
+ $sql = "SELECT commentID, objectTypeID, objectID
+ FROM wcf".WCF_N."_comment
+ ".$conditions;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute($conditions->getParameters());
+ $comments = array();
+ while ($row = $statement->fetchArray()) {
+ $comments[$row['commentID']] = new Comment(null, $row);
+ }
+
+ foreach ($queues as $queue) {
+ $assignUser = false;
+
+ $comment = $comments[$queue->objectID];
+ if ($this->getCommentManager($comment)->canModerate($comment->objectTypeID, $comment->objectID)) {
+ $assignUser = true;
+ }
+
+ $assignments[$queue->queueID] = $assignUser;
+ }
+
+ ModerationQueueManager::getInstance()->setAssignment($assignments);
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\report\IModerationQueueReportHandler::canReport()
+ */
+ public function canReport($objectID) {
+ if (!$this->isValid($objectID)) {
+ return false;
+ }
+
+ $comment = $this->getComment($objectID);
+ if (!$this->getCommentManager($comment)->isAccessible($comment->objectID)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\IModerationQueueHandler::getContainerID()
+ */
+ public function getContainerID($objectID) {
+ return 0;
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\report\IModerationQueueReportHandler::getReportedContent()
+ */
+ public function getReportedContent(ViewableModerationQueue $queue) {
+ WCF::getTPL()->assign(array(
+ 'message' => new ViewableComment($queue->getAffectedObject())
+ ));
+
+ return WCF::getTPL()->fetch('moderationComment');
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\report\IModerationQueueReportHandler::getReportedObject()
+ */
+ public function getReportedObject($objectID) {
+ if ($this->isValid($objectID)) {
+ return $this->getComment($objectID);
+ }
+
+ return null;
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\IModerationQueueHandler::isValid()
+ */
+ public function isValid($objectID) {
+ if ($this->getComment($objectID) === null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a comment object by comment id or null if comment id is invalid.
+ *
+ * @param integer $objectID
+ * @return wcf\data\comment\Comment
+ */
+ protected function getComment($objectID) {
+ if (!array_key_exists($objectID, self::$comments)) {
+ self::$comments[$objectID] = new Comment($objectID);
+ if (!self::$comments[$objectID]->commentID) {
+ self::$comments[$objectID] = null;
+ }
+ }
+
+ return self::$comments[$objectID];
+ }
+
+ /**
+ * Returns a comment manager for given comment.
+ *
+ * @param wcf\data\comment\Comment $comment
+ * @return wcf\system\comment\manager\ICommentManager
+ */
+ protected function getCommentManager(Comment $comment) {
+ if (!isset(self::$commentManagers[$comment->objectTypeID])) {
+ self::$commentManagers[$comment->objectTypeID] = ObjectTypeCache::getInstance()->getObjectType($comment->objectTypeID)->getProcessor();
+ }
+
+ return self::$commentManagers[$comment->objectTypeID];
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\IModerationQueueHandler::populate()
+ */
+ public function populate(array $queues) {
+ $objectIDs = array();
+ foreach ($queues as $object) {
+ $objectIDs[] = $object->objectID;
+ }
+
+ // fetch comments
+ $commentList = new CommentList();
+ $commentList->getConditionBuilder()->add("comment.commentID IN (?)", array($objectIDs));
+ $commentList->readObjects();
+ $comments = $commentList->getObjects();
+
+ foreach ($queues as $object) {
+ if (isset($comments[$object->objectID])) {
+ $object->setAffectedObject($comments[$object->objectID]);
+ }
+ else {
+ $object->setIsOrphaned();
+ }
+ }
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\IModerationQueueHandler::removeContent()
+ */
+ public function removeContent(ModerationQueue $queue, $message) {
+ if ($this->isValid($queue->objectID)) {
+ $commentAction = new CommentAction(array($this->getComment($queue->objectID)), 'delete');
+ $commentAction->executeAction();
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\moderation\queue\report;
+use wcf\data\comment\response\CommentResponse;
+use wcf\data\comment\response\CommentResponseAction;
+use wcf\data\comment\response\CommentResponseList;
+use wcf\data\comment\response\ViewableCommentResponse;
+use wcf\data\comment\Comment;
+use wcf\data\comment\CommentList;
+use wcf\data\moderation\queue\ModerationQueue;
+use wcf\data\moderation\queue\ViewableModerationQueue;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\moderation\queue\ModerationQueueManager;
+use wcf\system\WCF;
+
+/**
+ * An implementation of IModerationQueueReportHandler for comment responses.
+ *
+ * @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.comment
+ * @subpackage system.moderation.queue
+ * @category Community Framework
+ */
+class CommentResponseModerationQueueReportHandler extends CommentCommentModerationQueueReportHandler {
+ /**
+ * @see wcf\system\moderation\queue\AbstractModerationQueueHandler::$objectType
+ */
+ protected $objectType = 'com.woltlab.wcf.comment.response';
+
+ /**
+ * list of comment responses
+ * @var array<wcf\data\comment\response\CommentResponse>
+ */
+ protected static $responses = array();
+
+ /**
+ * @see wcf\system\moderation\queue\IModerationQueueHandler::assignQueues()
+ */
+ public function assignQueues(array $queues) {
+ $assignments = array();
+
+ // read comments and responses
+ $responseIDs = array();
+ foreach ($queues as $queue) {
+ $responseIDs[] = $queue->objectID;
+ }
+
+ $conditions = new PreparedStatementConditionBuilder();
+ $conditions->add("comment_response.responseID IN (?)", array($responseIDs));
+
+ $sql = "SELECT comment_response.responseID, comment.commentID, comment.objectTypeID, comment.objectID
+ FROM wcf".WCF_N."_comment_response comment_response
+ LEFT JOIN wcf".WCF_N."_comment comment
+ ON (comment.commentID = comment_response.commentID)
+ ".$conditions;
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute($conditions->getParameters());
+ $comments = $responses = array();
+ while ($row = $statement->fetchArray()) {
+ $comments[$row['commentID']] = new Comment(null, $row);
+ $responses[$row['responseID']] = new CommentResponse(null, $row);
+ }
+
+ foreach ($queues as $queue) {
+ $assignUser = false;
+
+ $comment = $comments[$responses[$queue->objectID]->commentID];
+ if ($this->getCommentManager($comment)->canModerate($comment->objectTypeID, $comment->objectID)) {
+ $assignUser = true;
+ }
+
+ $assignments[$queue->queueID] = $assignUser;
+ }
+
+ ModerationQueueManager::getInstance()->setAssignment($assignments);
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\report\IModerationQueueReportHandler::canReport()
+ */
+ public function canReport($objectID) {
+ if (!$this->isValid($objectID)) {
+ return false;
+ }
+
+ $response = $this->getResponse($objectID);
+ $comment = $this->getComment($response->commentID);
+ if (!$this->getCommentManager($comment)->isAccessible($comment->objectID)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\IModerationQueueHandler::getContainerID()
+ */
+ public function getContainerID($objectID) {
+ return 0;
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\report\IModerationQueueReportHandler::getReportedContent()
+ */
+ public function getReportedContent(ViewableModerationQueue $queue) {
+ WCF::getTPL()->assign(array(
+ 'message' => new ViewableCommentResponse($queue->getAffectedObject())
+ ));
+
+ return WCF::getTPL()->fetch('moderationComment');
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\report\IModerationQueueReportHandler::getReportedObject()
+ */
+ public function getReportedObject($objectID) {
+ if ($this->isValid($objectID)) {
+ return $this->getResponse($objectID);
+ }
+
+ return null;
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\IModerationQueueHandler::isValid()
+ */
+ public function isValid($objectID) {
+ if ($this->getResponse($objectID) === null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns a comment response object by response id or null if response id is invalid.
+ *
+ * @param integer $objectID
+ * @return wcf\data\comment\response\CommentResponse
+ */
+ protected function getResponse($objectID) {
+ if (!array_key_exists($objectID, self::$responses)) {
+ self::$responses[$objectID] = new CommentResponse($objectID);
+ if (!self::$responses[$objectID]->responseID) {
+ self::$responses[$objectID] = null;
+ }
+ }
+
+ return self::$responses[$objectID];
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\IModerationQueueHandler::populate()
+ */
+ public function populate(array $queues) {
+ $objectIDs = array();
+ foreach ($queues as $object) {
+ $objectIDs[] = $object->objectID;
+ }
+
+ // fetch responses
+ $responseList = new CommentResponseList();
+ $responseList->getConditionBuilder()->add("comment_response.responseID IN (?)", array($objectIDs));
+ $responseList->readObjects();
+ $responses = $responseList->getObjects();
+
+ // fetch comments
+ $commentIDs = array();
+ foreach ($responses as $response) {
+ $commentIDs[] = $response->commentID;
+ }
+
+ if (!empty($commentIDs)) {
+ $commentList = new CommentList();
+ $commentList->getConditionBuilder()->add("comment.commentID IN (?)", array($commentIDs));
+ $commentList->readObjects();
+ $comments = $commentList->getObjects();
+ }
+
+ foreach ($queues as $object) {
+ if (isset($responses[$object->objectID])) {
+ $response = $responses[$object->objectID];
+ $response->setComment($comments[$response->commentID]);
+
+ $object->setAffectedObject($response);
+ }
+ else {
+ $object->setIsOrphaned();
+ }
+ }
+ }
+
+ /**
+ * @see wcf\system\moderation\queue\IModerationQueueHandler::removeContent()
+ */
+ public function removeContent(ModerationQueue $queue, $message) {
+ if ($this->isValid($queue->objectID)) {
+ $responseAction = new CommentResponseAction(array($this->getResponse($queue->objectID)), 'delete');
+ $responseAction->executeAction();
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\activity\event;
+use wcf\data\comment\response\CommentResponseList;
+use wcf\data\comment\CommentList;
+use wcf\data\user\UserList;
+use wcf\system\user\activity\event\IUserActivityEvent;
+use wcf\system\SingletonFactory;
+use wcf\system\WCF;
+
+/**
+ * User activity event implementation for profile comment responses.
+ *
+ * @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.comment
+ * @subpackage system.user.activity.event
+ * @category Community Framework
+ */
+class ProfileCommentResponseUserActivityEvent extends SingletonFactory implements IUserActivityEvent {
+ /**
+ * @see wcf\system\user\activity\event\IUserActivityEvent::prepare()
+ */
+ public function prepare(array $events) {
+ $responseIDs = array();
+ foreach ($events as $event) {
+ $responseIDs[] = $event->objectID;
+ }
+
+ // fetch responses
+ $responseList = new CommentResponseList();
+ $responseList->getConditionBuilder()->add("comment_response.responseID IN (?)", array($responseIDs));
+ $responseList->readObjects();
+ $responses = $responseList->getObjects();
+
+ // fetch comments
+ $commentIDs = $comments = array();
+ foreach ($responses as $response) {
+ $commentIDs[] = $response->commentID;
+ }
+ if (!empty($commentIDs)) {
+ $commentList = new CommentList();
+ $commentList->getConditionBuilder()->add("comment.commentID IN (?)", array($commentIDs));
+ $commentList->readObjects();
+ $comments = $commentList->getObjects();
+ }
+
+ // fetch users
+ $userIDs = $users = array();
+ foreach ($comments as $comment) {
+ $userIDs[] = $comment->objectID;
+ $userIDs[] = $comment->userID;
+ }
+ if (!empty($userIDs)) {
+ $userList = new UserList();
+ $userList->getConditionBuilder()->add("user_table.userID IN (?)", array($userIDs));
+ $userList->readObjects();
+ $users = $userList->getObjects();
+ }
+
+ // set message
+ foreach ($events as $event) {
+ if (isset($responses[$event->objectID])) {
+ $response = $responses[$event->objectID];
+ $comment = $comments[$response->commentID];
+ if (isset($users[$comment->objectID]) && isset($users[$comment->userID])) {
+ $event->setIsAccessible();
+
+ // title
+ $text = WCF::getLanguage()->getDynamicVariable('wcf.user.profile.recentActivity.profileCommentResponse', array(
+ 'commentAuthor' => $users[$comment->userID],
+ 'user' => $users[$comment->objectID]
+ ));
+ $event->setTitle($text);
+
+ // description
+ $event->setDescription($response->getExcerpt());
+ continue;
+ }
+ }
+
+ $event->setIsOrphaned();
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\activity\event;
+use wcf\data\comment\CommentList;
+use wcf\data\user\UserList;
+use wcf\system\user\activity\event\IUserActivityEvent;
+use wcf\system\SingletonFactory;
+use wcf\system\WCF;
+
+/**
+ * User activity event implementation for profile comments.
+ *
+ * @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.comment
+ * @subpackage system.user.activity.event
+ * @category Community Framework
+ */
+class ProfileCommentUserActivityEvent extends SingletonFactory implements IUserActivityEvent {
+ /**
+ * @see wcf\system\user\activity\event\IUserActivityEvent::prepare()
+ */
+ public function prepare(array $events) {
+ $comentIDs = array();
+ foreach ($events as $event) {
+ $comentIDs[] = $event->objectID;
+ }
+
+ // fetch comments
+ $commentList = new CommentList();
+ $commentList->getConditionBuilder()->add("comment.commentID IN (?)", array($comentIDs));
+ $commentList->readObjects();
+ $comments = $commentList->getObjects();
+
+ // fetch users
+ $userIDs = $users = array();
+ foreach ($comments as $comment) {
+ $userIDs[] = $comment->objectID;
+ }
+ if (!empty($userIDs)) {
+ $userList = new UserList();
+ $userList->getConditionBuilder()->add("user_table.userID IN (?)", array($userIDs));
+ $userList->readObjects();
+ $users = $userList->getObjects();
+ }
+
+ // set message
+ foreach ($events as $event) {
+ if (isset($comments[$event->objectID])) {
+ // short output
+ $comment = $comments[$event->objectID];
+ if (isset($users[$comment->objectID])) {
+ $event->setIsAccessible();
+
+ $user = $users[$comment->objectID];
+ $text = WCF::getLanguage()->getDynamicVariable('wcf.user.profile.recentActivity.profileComment', array('user' => $user));
+ $event->setTitle($text);
+
+ // output
+ $event->setDescription($comment->getExcerpt());
+ continue;
+ }
+ }
+
+ $event->setIsOrphaned();
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\event;
+use wcf\data\comment\Comment;
+use wcf\data\user\User;
+use wcf\system\request\LinkHandler;
+use wcf\system\user\notification\event\AbstractUserNotificationEvent;
+use wcf\system\WCF;
+
+/**
+ * User notification event for profile's owner for commment responses.
+ *
+ * @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.comment
+ * @subpackage system.user.notification.event
+ * @category Community Framework
+ */
+class UserProfileCommentResponseOwnerUserNotificationEvent extends AbstractUserNotificationEvent {
+ /**
+ * @see wcf\system\user\notification\event\IUserNotificationEvent::getTitle()
+ */
+ public function getTitle() {
+ return $this->getLanguage()->get('wcf.user.notification.commentResponseOwner.title');
+ }
+
+ /**
+ * @see wcf\system\user\notification\event\IUserNotificationEvent::getMessage()
+ */
+ public function getMessage() {
+ // @todo: use cache or a single query to retrieve required data
+ $comment = new Comment($this->userNotificationObject->commentID);
+ $commentAuthor = new User($comment->userID);
+
+ return $this->getLanguage()->getDynamicVariable('wcf.user.notification.commentResponseOwner.message', array(
+ 'author' => $this->author,
+ 'commentAuthor' => $commentAuthor
+ ));
+ }
+
+ /**
+ * @see wcf\system\user\notification\event\IUserNotificationEvent::getEmailMessage()
+ */
+ public function getEmailMessage() {
+ $comment = new Comment($this->userNotificationObject->commentID);
+ $commentAuthor = new User($comment->userID);
+ $owner = new User($comment->objectID);
+
+ return $this->getLanguage()->getDynamicVariable('wcf.user.notification.commentResponseOwner.mail', array(
+ 'author' => $this->author,
+ 'commentAuthor' => $commentAuthor,
+ 'owner' => $owner
+ ));
+ }
+
+ /**
+ * @see wcf\system\user\notification\event\IUserNotificationEvent::getLink()
+ */
+ public function getLink() {
+ return LinkHandler::getInstance()->getLink('User', array('object' => WCF::getUser()), '#wall');
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\event;
+use wcf\data\comment\Comment;
+use wcf\data\user\User;
+use wcf\system\request\LinkHandler;
+use wcf\system\user\notification\event\AbstractUserNotificationEvent;
+
+/**
+ * User notification event for profile commment responses.
+ *
+ * @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.comment
+ * @subpackage system.user.notification.event
+ * @category Community Framework
+ */
+class UserProfileCommentResponseUserNotificationEvent extends AbstractUserNotificationEvent {
+ /**
+ * @see wcf\system\user\notification\event\IUserNotificationEvent::getTitle()
+ */
+ public function getTitle() {
+ return $this->getLanguage()->get('wcf.user.notification.commentResponse.title');
+ }
+
+ /**
+ * @see wcf\system\user\notification\event\IUserNotificationEvent::getMessage()
+ */
+ public function getMessage() {
+ // @todo: use cache or a single query to retrieve required data
+ $comment = new Comment($this->userNotificationObject->commentID);
+ $user = new User($comment->objectID);
+
+ return $this->getLanguage()->getDynamicVariable('wcf.user.notification.commentResponse.message', array(
+ 'author' => $this->author,
+ 'owner' => $user
+ ));
+ }
+
+ /**
+ * @see wcf\system\user\notification\event\IUserNotificationEvent::getEmailMessage()
+ */
+ public function getEmailMessage() {
+ $comment = new Comment($this->userNotificationObject->commentID);
+ $user = new User($comment->objectID);
+
+ return $this->getLanguage()->getDynamicVariable('wcf.user.notification.commentResponse.mail', array(
+ 'author' => $this->author,
+ 'owner' => $user
+ ));
+ }
+
+ /**
+ * @see wcf\system\user\notification\event\IUserNotificationEvent::getLink()
+ */
+ public function getLink() {
+ // @todo: use cache or a single query to retrieve required data
+ $comment = new Comment($this->userNotificationObject->commentID);
+ $user = new User($comment->objectID);
+
+ return LinkHandler::getInstance()->getLink('User', array('object' => $user), '#wall');
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\event;
+use wcf\data\user\User;
+use wcf\system\request\LinkHandler;
+use wcf\system\user\notification\event\AbstractUserNotificationEvent;
+use wcf\system\WCF;
+
+/**
+ * User notification event for profile commments.
+ *
+ * @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.comment
+ * @subpackage system.user.notification.event
+ * @category Community Framework
+ */
+class UserProfileCommentUserNotificationEvent extends AbstractUserNotificationEvent {
+ /**
+ * @see wcf\system\user\notification\event\IUserNotificationEvent::getTitle()
+ */
+ public function getTitle() {
+ return $this->getLanguage()->get('wcf.user.notification.comment.title');
+ }
+
+ /**
+ * @see wcf\system\user\notification\event\IUserNotificationEvent::getMessage()
+ */
+ public function getMessage() {
+ return $this->getLanguage()->getDynamicVariable('wcf.user.notification.comment.message', array(
+ 'author' => $this->author
+ ));
+ }
+
+ /**
+ * @see wcf\system\user\notification\event\IUserNotificationEvent::getEmailMessage()
+ */
+ public function getEmailMessage() {
+ $user = new User($this->userNotificationObject->objectID);
+
+ return $this->getLanguage()->getDynamicVariable('wcf.user.notification.comment.mail', array(
+ 'author' => $this->author,
+ 'owner' => $user
+ ));
+ }
+
+ /**
+ * @see wcf\system\user\notification\event\IUserNotificationEvent::getLink()
+ */
+ public function getLink() {
+ return LinkHandler::getInstance()->getLink('User', array('object' => WCF::getUser()), '#wall');
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\object;
+use wcf\data\DatabaseObjectDecorator;
+
+/**
+ * Notification object for comment responses.
+ *
+ * @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.comment
+ * @subpackage system.user.notification.object
+ * @category Community Framework
+ */
+class CommentResponseUserNotificationObject extends DatabaseObjectDecorator implements IUserNotificationObject {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\comment\response\CommentResponse';
+
+ /**
+ * @see wcf\system\user\notification\object\IUserNotificationObject::getTitle()
+ */
+ public function getTitle() {
+ return '';
+ }
+
+ /**
+ * @see wcf\system\user\notification\object\IUserNotificationObject::getURL()
+ */
+ public function getURL() {
+ return '';
+ }
+
+ /**
+ * @see wcf\system\user\notification\object\IUserNotificationObject::getAuthorID()
+ */
+ public function getAuthorID() {
+ return $this->userID;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\object;
+use wcf\data\DatabaseObjectDecorator;
+
+/**
+ * Notification object for comments.
+ *
+ * @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.comment
+ * @subpackage system.user.notification.object
+ * @category Community Framework
+ */
+class CommentUserNotificationObject extends DatabaseObjectDecorator implements IUserNotificationObject {
+ /**
+ * @see wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\comment\Comment';
+
+ /**
+ * @see wcf\system\user\notification\object\IUserNotificationObject::getTitle()
+ */
+ public function getTitle() {
+ return '';
+ }
+
+ /**
+ * @see wcf\system\user\notification\object\IUserNotificationObject::getURL()
+ */
+ public function getURL() {
+ return '';
+ }
+
+ /**
+ * @see wcf\system\user\notification\object\IUserNotificationObject::getAuthorID()
+ */
+ public function getAuthorID() {
+ return $this->userID;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\object\type;
+
+/**
+ * Default interface for comment user notification object types.
+ *
+ * @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.comment
+ * @subpackage system.user.notification.object.type
+ * @category Community Framework
+ */
+interface ICommentUserNotificationObjectType {
+ /**
+ * Returns owner id of comment context.
+ *
+ * @param integer $objectID
+ * @return integer
+ */
+ public function getOwnerID($objectID);
+}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\object\type;
+
+/**
+ * Represents a comment response notification object type.
+ *
+ * @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.user
+ * @subpackage system.user.notification.object.type
+ * @category Community Framework
+ */
+class UserProfileCommentResponseUserNotificationObjectType extends AbstractUserNotificationObjectType {
+ /**
+ * @see wcf\system\user\notification\object\type\AbstractUserNotificationObjectType::$decoratorClassName
+ */
+ protected static $decoratorClassName = 'wcf\system\user\notification\object\CommentResponseUserNotificationObject';
+
+ /**
+ * @see wcf\system\user\notification\object\type\AbstractUserNotificationObjectType::$objectClassName
+ */
+ protected static $objectClassName = 'wcf\data\comment\response\CommentResponse';
+
+ /**
+ * @see wcf\system\user\notification\object\type\AbstractUserNotificationObjectType::$objectListClassName
+ */
+ protected static $objectListClassName = 'wcf\data\comment\response\CommentResponseList';
+}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\object\type;
+use wcf\system\WCF;
+
+/**
+ * Represents a comment notification object type.
+ *
+ * @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.user
+ * @subpackage system.user.notification.object.type
+ * @category Community Framework
+ */
+class UserProfileCommentUserNotificationObjectType extends AbstractUserNotificationObjectType implements ICommentUserNotificationObjectType {
+ /**
+ * @see wcf\system\user\notification\object\type\AbstractUserNotificationObjectType::$decoratorClassName
+ */
+ protected static $decoratorClassName = 'wcf\system\user\notification\object\CommentUserNotificationObject';
+
+ /**
+ * @see wcf\system\user\notification\object\type\AbstractUserNotificationObjectType::$objectClassName
+ */
+ protected static $objectClassName = 'wcf\data\comment\Comment';
+
+ /**
+ * @see wcf\system\user\notification\object\type\AbstractUserNotificationObjectType::$objectListClassName
+ */
+ protected static $objectListClassName = 'wcf\data\comment\CommentList';
+
+ /**
+ * @see wcf\system\user\notification\object\type\ICommentUserNotificationObjectType::getOwnerID()
+ */
+ public function getOwnerID($objectID) {
+ $sql = "SELECT objectID
+ FROM wcf".WCF_N."_comment
+ WHERE commentID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($objectID));
+ $row = $statement->fetchArray();
+
+ return ($row ? $row['objectID'] : 0);
+ }
+}
--- /dev/null
+/* ############## Profile Comments ############## */
+.comment,
+.commentResponse {
+ position: relative;
+}
+
+.commentList .buttonGroupNavigation {
+ position: absolute;
+ top: @wcfGapTiny;
+ right: @wcfGapMedium;
+
+ > ul {
+ > li {
+ float: left;
+ opacity: 0;
+
+ .transition(opacity, .1s);
+
+ > a {
+ padding: @wcfGapTiny;
+ }
+ }
+ }
+}
+
+.commentResponseList .buttonGroupNavigation {
+ top: @wcfGapSmall;
+ right: @wcfGapSmall;
+}
+
+.commentContent:hover > .buttonGroupNavigation > ul > li {
+ opacity: 1;
+}
+
+.commentList input[type='text'] {
+ + small {
+ color: @wcfDimmedColor;
+ opacity: 0;
+
+ .transition(opacity, .1s);
+ }
+
+ &:focus + small {
+ opacity: 1;
+ }
+}
+
+.commentResponse {
+ border-top: 1px solid @wcfContainerBorderColor;
+ padding: @wcfGapSmall;
+}
+
+.commentResponseAdd {
+ border-top: 1px solid @wcfContainerBorderColor;
+ margin-top: @wcfGapMedium;
+ padding: 7px 7px 0;
+}
+
+.commentResponseList .commentResponse:first-child {
+ margin-top: @wcfGapMedium;
+}
+
+.commentResponseAdd + .commentResponseList .commentResponse:first-child {
+ margin-top: 7px;
+}
+
+.commentList > li:nth-child(2n) .commentResponseList .commentResponse:nth-child(2n+1) {
+ background-color: @wcfContainerBackgroundColor;
+
+ .transition(background-color, .1s);
+}
+
+.commentList > li:nth-child(2n+1) .commentResponseList .commentResponse:nth-child(2n+1) {
+ background-color: @wcfContainerAccentBackgroundColor;
+
+ .transition(background-color, .1s);
+}
+
+.commentResponseList > li:hover {
+ background-color: @wcfContainerHoverBackgroundColor !important;
+}
+
+.commentList > li:not(.commentAdd):hover {
+ background-color: @wcfContainerBackgroundColor;
+
+ &:nth-child(2n) {
+ background-color: @wcfContainerAccentBackgroundColor;
+ }
+}
+
+/* buttons to load comments/responses */
+.commentList > .commentLoadNext,
+.comment .responseLoadNext {
+ text-align: center;
+
+ > button {
+ padding-left: 30px;
+ padding-right: 30px;
+ }
+}
+
+.comment .responseLoadNext {
+ padding-top: @wcfGapMedium;
+}
+
+.commentList .userMessage {
+ margin-top: 0;
+}
+
+/* like display */
+.commentList .likesBadge {
+ display: inline-block;
+ margin: -2px 0 -2px @wcfGapTiny;
+}
<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>
+ <item name="wcf.acp.group.option.category.user.profileComment"><![CDATA[Benutzerprofil-Pinnwand]]></item>
+ <item name="wcf.acp.group.option.user.profileComment.canAddComment"><![CDATA[Kann Pinnwand-Kommentare erstellen]]></item>
+ <item name="wcf.acp.group.option.user.profileComment.canEditComment"><![CDATA[Kann eigene Pinnwand-Kommentare bearbeiten]]></item>
+ <item name="wcf.acp.group.option.user.profileComment.canDeleteComment"><![CDATA[Kann eigene Pinnwand-Kommentare löschen]]></item>
+ <item name="wcf.acp.group.option.mod.profileComment.canEditComment"><![CDATA[Kann Pinnwand-Kommentare bearbeiten]]></item>
+ <item name="wcf.acp.group.option.mod.profileComment.canDeleteComment"><![CDATA[Kann Pinnwand-Kommentare löschen]]></item>
+ <item name="wcf.acp.group.option.mod.profileComment.canModerateComment"><![CDATA[Kann Pinnwand-Kommentare moderieren]]></item>
+ <item name="wcf.acp.group.option.category.mod.profileComment"><![CDATA[Benutzerprofil-Pinnwand]]></item>
</category>
<category name="wcf.acp.index">
<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>
+ <item name="wcf.acp.option.module_user_profile_wall"><![CDATA[Benutzerprofil-Pinnwand]]></item>
</category>
<category name="wcf.acp.package">
<item name="wcf.bbcode.spoiler.text"><![CDATA[(Versteckter Text)]]></item>
</category>
-
<category name="wcf.category">
<item name="wcf.category.add"><![CDATA[Kategorie hinzufügen]]></item>
<item name="wcf.category.button.list"><![CDATA[Kategorien auflisten]]></item>
<item name="wcf.clipboard.item.com.woltlab.wcf.user.enable"><![CDATA[Aktivieren]]></item>
</category>
+ <category name="wcf.comment">
+ <item name="wcf.comment.add"><![CDATA[Kommentar schreiben …]]></item>
+ <item name="wcf.comment.delete.confirmMessage"><![CDATA[Wollen Sie diesen Kommentar wirklich löschen?]]></item>
+ <item name="wcf.comment.description"><![CDATA[Drücken Sie die Eingabetaste, um abzusenden oder Escape, um abzubrechen.]]></item>
+ <item name="wcf.comment.more"><![CDATA[Weitere Kommentare]]></item>
+ <item name="wcf.comment.response.add"><![CDATA[Antworten …]]></item>
+ <item name="wcf.comment.response.more"><![CDATA[Weitere Antworten]]></item>
+ <item name="wcf.comment.button.response.add"><![CDATA[Antworten]]></item>
+ </category>
+
<category name="wcf.dashboard">
<item name="wcf.dashboard.box.availableBoxes"><![CDATA[Deaktivierte Boxen]]></item>
<item name="wcf.dashboard.box.enabledBoxes"><![CDATA[Aktive Boxen]]></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>
+ <item name="wcf.like.objectType.com.woltlab.wcf.comment"><![CDATA[Kommentar]]></item>
+ <item name="wcf.like.objectType.com.woltlab.wcf.comment.response"><![CDATA[Kommentar-Antwort]]></item>
</category>
<category name="wcf.message">
<item name="wcf.user.recentActivity.noMoreEntries"><![CDATA[Keine weiteren Aktivitäten]]></item>
<item name="wcf.user.recentActivity.noEntries"><![CDATA[Es sind keine Aktivitäten vorhanden.]]></item>
<item name="wcf.user.recentActivity.com.woltlab.wcf.user.recentActivityEvent.follow"><![CDATA[Folgen]]></item>
+ <item name="wcf.user.recentActivity.com.woltlab.wcf.user.profileComment.recentActivityEvent"><![CDATA[Pinnwand-Kommentar]]></item>
+ <item name="wcf.user.recentActivity.com.woltlab.wcf.user.profileComment.response.recentActivityEvent"><![CDATA[Pinnwand-Antwort]]></item>
</category>
<category name="wcf.user.3rdparty">
<item name="wcf.user.notification.showAll"><![CDATA[Alle Benachrichtigungen anzeigen]]></item>
<item name="wcf.user.notification.com.woltlab.wcf.user"><![CDATA[Benutzer-Profile]]></item>
<item name="wcf.user.notification.com.woltlab.wcf.user.follow.following"><![CDATA[Jemand folgt Ihnen]]></item>
+ <item name="wcf.user.notification.comment.title"><![CDATA[Neuer Kommentar (Pinnwand)]]></item>
+ <item name="wcf.user.notification.comment.message"><![CDATA[Hat einen Kommentar an Ihrer Pinnwand verfasst.]]></item>
+ <item name="wcf.user.notification.comment.mail"><![CDATA[{@$author->username} hat einen Kommentar an Ihrer Pinnwand verfasst:
+{link controller='User' object=$owner encode=false forceFrontend=true}{/link}#wall]]></item>
+ <item name="wcf.user.notification.commentResponse.title"><![CDATA[Neue Antwort (Pinnwand)]]></item>
+ <item name="wcf.user.notification.commentResponse.message"><![CDATA[Hat eine Antwort zu Ihrem Kommentar an der Pinnwand von {$owner->username} verfasst.]]></item>
+ <item name="wcf.user.notification.commentResponse.mail"><![CDATA[{@$author->username} hat eine Antwort zu Ihrem Kommentar an der Pinnwand von "{@$owner->username}" verfasst:
+{link controller='User' object=$owner encode=false forceFrontend=true}{/link}#wall]]></item>
+ <item name="wcf.user.notification.commentResponseOwner.title"><![CDATA[Neue Antwort (Pinnwand)]]></item>
+ <item name="wcf.user.notification.commentResponseOwner.message"><![CDATA[Hat eine Antwort zum Kommentar von {$commentAuthor->username} an Ihrer Pinnwand verfasst.]]></item>
+ <item name="wcf.user.notification.commentResponseOwner.mail"><![CDATA[{@$author->username} hat eine Antwort zum Kommentar von "{@$commentAuthor->username}" an Ihrer Pinnwand verfasst:
+{link controller='User' object=$owner encode=false forceFrontend=true}{/link}#wall]]></item>
+ <item name="wcf.user.notification.com.woltlab.wcf.user.profileComment.notification.comment"><![CDATA[Neuer Kommentar an Ihrer Pinnwand]]></item>
+ <item name="wcf.user.notification.com.woltlab.wcf.user.profileComment.response.notification.commentResponse"><![CDATA[Neue Antwort auf einen Kommentar an Ihrer Pinnwand]]></item>
+ <item name="wcf.user.notification.com.woltlab.wcf.user.profileComment.response.notification.commentResponseOwner"><![CDATA[Neue Antwort auf einen Kommentar von Ihnen]]></item>
</category>
<category name="wcf.user.profile">
<item name="wcf.user.profile.oldUsername"><![CDATA[Hieß früher „{$user->getOldUsername()}“]]></item>
<item name="wcf.user.profile.recentActivity.follow"><![CDATA[Folgt nun <a href="{link controller='User' object=$user}{/link}">{$user->username}</a>.]]></item>
<item name="wcf.user.profile.visitors"><![CDATA[Profil-Besucher]]></item>
+ <item name="wcf.user.profile.content.wall.comment"><![CDATA[Kommentar an der Pinnwand]]></item>
+ <item name="wcf.user.profile.content.wall.commentResponse"><![CDATA[Antwort auf einen Pinnwand-Kommentar]]></item>
+ <item name="wcf.user.profile.content.wall.noEntries"><![CDATA[Es wurden noch keine Einträge an der Pinnwand verfasst.]]></item>
+ <item name="wcf.user.profile.menu.wall"><![CDATA[Pinnwand]]></item>
+ <item name="wcf.user.profile.recentActivity.profileComment"><![CDATA[Hat einen Kommentar an die <a href="{link controller='User' object=$user}{/link}#wall">Pinnwand von {$user->username}</a> geschrieben.]]></item>
+ <item name="wcf.user.profile.recentActivity.profileCommentResponse"><![CDATA[Hat auf einen Kommentar von <a href="{link controller='User' object=$commentAuthor}{/link}">{$commentAuthor->username}</a> an der <a href="{link controller='User' object=$user}{/link}#wall">Pinnwand von {$user->username}</a> geantwortet.]]></item>
</category>
<category name="wcf.user.objectWatch">
<item name="wcf.user.option.twitter"><![CDATA[Twitter]]></item>
<item name="wcf.user.option.googlePlus"><![CDATA[Google+]]></item>
<item name="wcf.user.option.googlePlus.description"><![CDATA[Geben Sie Ihre 21-stellige Google+-ID an.]]></item>
+ <item name="wcf.user.option.canWriteProfileComments"><![CDATA[Kann Pinnwand-Kommentare schreiben]]></item>
</category>
<category name="wcf.user.mail">
<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>
+ <item name="wcf.acp.group.option.category.user.profileComment"><![CDATA[User Profile Wall]]></item>
+ <item name="wcf.acp.group.option.user.profileComment.canAddComment"><![CDATA[Can create comments]]></item>
+ <item name="wcf.acp.group.option.user.profileComment.canEditComment"><![CDATA[Cam edit own comments]]></item>
+ <item name="wcf.acp.group.option.user.profileComment.canDeleteComment"><![CDATA[Cam delete own comments]]></item>
+ <item name="wcf.acp.group.option.mod.profileComment.canEditComment"><![CDATA[Cam edit comments]]></item>
+ <item name="wcf.acp.group.option.mod.profileComment.canDeleteComment"><![CDATA[Can delete comments]]></item>
+ <item name="wcf.acp.group.option.mod.profileComment.canModerateComment"><![CDATA[Can moderate comments]]></item>
+ <item name="wcf.acp.group.option.category.mod.profileComment"><![CDATA[User Profile Wall]]></item>
</category>
<category name="wcf.acp.index">
<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>
+ <item name="wcf.acp.option.module_user_profile_wall"><![CDATA[User Profile Wall]]></item>
</category>
<category name="wcf.acp.package">
<item name="wcf.clipboard.item.com.woltlab.wcf.user.enable"><![CDATA[Approve]]></item>
</category>
+ <category name="wcf.comment">
+ <item name="wcf.comment.add"><![CDATA[Write a comment …]]></item>
+ <item name="wcf.comment.delete.confirmMessage"><![CDATA[Do you really want to delete this comment?]]></item>
+ <item name="wcf.comment.description"><![CDATA[Press Enter to send or Escape to cancel.]]></item>
+ <item name="wcf.comment.more"><![CDATA[More Comments]]></item>
+ <item name="wcf.comment.response.add"><![CDATA[Reply …]]></item>
+ <item name="wcf.comment.response.more"><![CDATA[More Replies]]></item>
+ <item name="wcf.comment.button.response.add"><![CDATA[Reply]]></item>
+ </category>
+
<category name="wcf.dashboard">
<item name="wcf.dashboard.box.availableBoxes"><![CDATA[Disabled Boxes]]></item>
<item name="wcf.dashboard.box.enabledBoxes"><![CDATA[Active Boxes]]></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>
+ <item name="wcf.like.objectType.com.woltlab.wcf.comment"><![CDATA[Comment]]></item>
+ <item name="wcf.like.objectType.com.woltlab.wcf.comment.response"><![CDATA[Comment Reply]]></item>
</category>
<category name="wcf.message">
<item name="wcf.user.recentActivity.noMoreEntries"><![CDATA[No more activities]]></item>
<item name="wcf.user.recentActivity.noEntries"><![CDATA[There are no activities.]]></item>
<item name="wcf.user.recentActivity.com.woltlab.wcf.user.recentActivityEvent.follow"><![CDATA[Follow]]></item>
+ <item name="wcf.user.recentActivity.com.woltlab.wcf.user.profileComment.recentActivityEvent"><![CDATA[Wall Comment]]></item>
+ <item name="wcf.user.recentActivity.com.woltlab.wcf.user.profileComment.response.recentActivityEvent"><![CDATA[Wall Reply]]></item>
</category>
<category name="wcf.user.3rdparty">
<item name="wcf.user.notification.showAll"><![CDATA[Show All Notifications]]></item>
<item name="wcf.user.notification.com.woltlab.wcf.user"><![CDATA[User Profiles]]></item>
<item name="wcf.user.notification.com.woltlab.wcf.user.follow.following"><![CDATA[New follower]]></item>
+ <item name="wcf.user.notification.comment.title"><![CDATA[New Comment (Wall)]]></item>
+ <item name="wcf.user.notification.comment.message"><![CDATA[Wrote a comment on your wall.]]></item>
+ <item name="wcf.user.notification.comment.mail"><![CDATA[{@$author->username} wrote a comment on your wall:
+{link controller='User' object=$owner encode=false forceFrontend=true}#wall{/link}]]></item>
+ <item name="wcf.user.notification.commentResponse.title"><![CDATA[New Reply (Wall)]]></item>
+ <item name="wcf.user.notification.commentResponse.message"><![CDATA[Wrote a reply to your comment on {$owner->username}’s wall.]]></item>
+ <item name="wcf.user.notification.commentResponse.mail"><![CDATA[{@$author->username} wrote a reply to your comment on {@$owner->username}’s wall:
+{link controller='User' object=$owner encode=false forceFrontend=true}{/link}#wall]]></item>
+ <item name="wcf.user.notification.commentResponseOwner.title"><![CDATA[New Reply (Wall)]]></item>
+ <item name="wcf.user.notification.commentResponseOwner.message"><![CDATA[Wrote a reply to {$commentAuthor->username}’s comment on your wall.]]></item>
+ <item name="wcf.user.notification.commentResponseOwner.mail"><![CDATA[{@$author->username} wrote a reply to {@$commentAuthor->username}’s comment on your wall:
+{link controller='User' object=$owner encode=false forceFrontend=true}{/link}#wall]]></item>
+ <item name="wcf.user.notification.com.woltlab.wcf.user.profileComment.notification.comment"><![CDATA[New comment on your wall]]></item>
+ <item name="wcf.user.notification.com.woltlab.wcf.user.profileComment.response.notification.commentResponse"><![CDATA[New reply to a comment on your wall]]></item>
+ <item name="wcf.user.notification.com.woltlab.wcf.user.profileComment.response.notification.commentResponseOwner"><![CDATA[New reply to one of your comments]]></item>
</category>
<category name="wcf.user.profile">
<item name="wcf.user.profile.oldUsername"><![CDATA[Used to be called “{$user->getOldUsername()}”]]></item>
<item name="wcf.user.profile.recentActivity.follow"><![CDATA[Now follows <a href="{link controller='User' object=$user}{/link}">{$user->username}</a>.]]></item>
<item name="wcf.user.profile.visitors"><![CDATA[Profile Visitors]]></item>
+ <item name="wcf.user.profile.content.wall.comment"><![CDATA[Wall Comment]]></item>
+ <item name="wcf.user.profile.content.wall.commentResponse"><![CDATA[Reply to wall comment]]></item>
+ <item name="wcf.user.profile.content.wall.noEntries"><![CDATA[There are no comments yet.]]></item>
+ <item name="wcf.user.profile.menu.wall"><![CDATA[Wall]]></item>
+ <item name="wcf.user.profile.recentActivity.profileComment"><![CDATA[Wrote a comment on <a href="{link controller='User' object=$user}{/link}#wall">{$user->username}’s wall</a>.]]></item>
+ <item name="wcf.user.profile.recentActivity.profileCommentResponse"><![CDATA[Replied to a comment by <a href="{link controller='User' object=$commentAuthor}{/link}">{$commentAuthor->username}</a> on <a href="{link controller='User' object=$user}{/link}#wall">{$user->username}’s wall</a>.]]></item>
</category>
<category name="wcf.user.objectWatch">
<item name="wcf.user.option.twitter"><![CDATA[Twitter]]></item>
<item name="wcf.user.option.googlePlus"><![CDATA[Google+]]></item>
<item name="wcf.user.option.googlePlus.description"><![CDATA[Enter your Google Plus user ID with a length of 21 digits.]]></item>
+ <item name="wcf.user.option.canWriteProfileComments"><![CDATA[Can Write Comments on My Wall]]></item>
</category>
<category name="wcf.user.mail">
actionID INT(10) NOT NULL DEFAULT 0
);
+DROP TABLE IF EXISTS wcf1_comment;
+CREATE TABLE wcf1_comment (
+ commentID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ objectTypeID INT(10) NOT NULL,
+ objectID INT(10) NOT NULL,
+ time INT(10) NOT NULL DEFAULT '0',
+ userID INT(10),
+ username VARCHAR(255) NOT NULL,
+ message TEXT NOT NULL,
+ responses MEDIUMINT(7) NOT NULL DEFAULT '0',
+ lastResponseIDs VARCHAR(255) NOT NULL DEFAULT '',
+
+ KEY (objectTypeID, objectID, time)
+);
+
+DROP TABLE IF EXISTS wcf1_comment_response;
+CREATE TABLE wcf1_comment_response (
+ responseID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ commentID INT(10) NOT NULL,
+ time INT(10) NOT NULL DEFAULT '0',
+ userID INT(10),
+ username VARCHAR(255) NOT NULL,
+ message TEXT NOT NULL,
+
+ KEY (commentID, time)
+);
+
DROP TABLE IF EXISTS wcf1_core_object;
CREATE TABLE wcf1_core_object (
objectID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
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;
+ALTER TABLE wcf1_comment ADD FOREIGN KEY (objectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
+ALTER TABLE wcf1_comment ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE SET NULL;
+
+ALTER TABLE wcf1_comment_response ADD FOREIGN KEY (commentID) REFERENCES wcf1_comment (commentID) ON DELETE CASCADE;
+ALTER TABLE wcf1_comment_response ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE SET NULL;
+
/* default inserts */
-- default user groups