diff --git a/file/js/be.bastelstu.Chat.litcoffee b/file/js/be.bastelstu.Chat.litcoffee index 1c1849e..986bd9f 100644 --- a/file/js/be.bastelstu.Chat.litcoffee +++ b/file/js/be.bastelstu.Chat.litcoffee @@ -85,7 +85,7 @@ exposed by a function if necessary. Initialize **Tims Chat**. Bind needed DOM events and initialize data structures. initialized = false - init = (roomID, config, titleTemplate, messageTemplate, userTemplate, userMenuTemplate) -> + init = (roomID, config, titleTemplate, messageTemplate, userTemplate, userMenuTemplate, userInviteDialogTemplate) -> return false if initialized initialized = true @@ -96,13 +96,24 @@ Initialize **Tims Chat**. Bind needed DOM events and initialize data structures. v.messageTemplate = messageTemplate v.userTemplate = userTemplate v.userMenuTemplate = userMenuTemplate + v.userInviteDialogTemplate = userInviteDialogTemplate + v.userInviteDialogUserListEntryTemplate = new WCF.Template """ +
+
+
+ +
+
+ """ console.log 'Initializing' When **Tims Chat** becomes focused mark the chat as active and remove the number of new messages from the title. $(window).focus -> - document.title = v.titleTemplate.fetch(roomList.active) if roomList.active?.title? and roomList.active.title.trim() isnt '' + document.title = v.titleTemplate.fetch(getRoomList().active) if roomList.active?.title? and roomList.active.title.trim() isnt '' newMessageCount = 0 isActive = true @@ -178,7 +189,7 @@ Open the smiley wcfDialog overlaySmileyList.css 'max-height': $(window).height() - overlaySmileyList.parent().siblings('.dialogTitlebar').outerHeight() 'overflow': 'auto' - + Handle private channel menu $('#timsChatMessageTabMenu > .tabMenu').on 'click', '.timsChatMessageTabMenuAnchor', -> @@ -239,7 +250,7 @@ The the word the caret is in will be passed to `autocomplete` and replaced if a toComplete = autocomplete.value.substring lastSpace + 1 nextSpace = toComplete.indexOf ' ' if nextSpace is -1 - afterComplete = ''; + afterComplete = '' else afterComplete = toComplete.substring nextSpace + 1 toComplete = toComplete.substring 0, nextSpace @@ -384,6 +395,68 @@ Toggle checkboxes. else $('.timsChatMessageContainer').removeClass 'markEnabled' +Show invite dialog. + + $('#timsChatInvite').click (event) -> + do event.preventDefault + + new WCF.Action.Proxy + autoSend: true + data: + actionName: 'prepareInvite' + className: 'chat\\data\\user\\UserAction' + success: (data) -> + $('
').appendTo 'body' unless $.wcfIsset 'timsChatInviteDialog' + + timsChatInviteDialog = $ '#timsChatInviteDialog' + + # Remove old event listeners + do timsChatInviteDialog.find('#userInviteDialogUsernameInput').off().remove + + timsChatInviteDialog.html v.userInviteDialogTemplate.fetch + users: data.returnValues.users + + new WCF.Search.User '#userInviteDialogUsernameInput', (user) -> + if $.wcfIsset "userInviteDialogUserID-#{user.objectID}" + $("#userInviteDialogUserID-#{user.objectID}").prop 'checked', true + else + $('#userInviteDialogUserList').append v.userInviteDialogUserListEntryTemplate.fetch + user: user + + $('#userInviteDialogUsernameInput').val "" + , false, [ WCF.User.username ], false + + $('#userInviteDialogFormSubmit').on 'click', (event) -> + checked = $ '#userInviteDialogUserList input[type=checkbox]:checked, #userInviteDialogFollowingList input[type=checkbox]:checked' + inviteUserList = [ ] + + checked.each (k, v) -> inviteUserList.push do $(v).val + + if inviteUserList.length + new WCF.Action.Proxy + autoSend: true + data: + actionName: 'invite' + className: 'chat\\data\\user\\UserAction' + parameters: + recipients: inviteUserList + success: (data) -> + do new WCF.System.Notification(WCF.Language.get 'wcf.global.success').show + + failure: (data) -> + return true unless (data?.returnValues?.errorType?) or (data?.message?) + + $("""
#{(data?.returnValues?.errorType) ? data.message}
""").wcfDialog title: WCF.Language.get 'wcf.global.error.title' + + return false + + $('#timsChatInviteDialog').wcfDialog 'close' + + timsChatInviteDialog.wcfDialog + title: WCF.Language.get 'chat.global.invite' + onShow: -> do $('#userInviteDialogUsernameInput').focus + + Hide topic container. $('#timsChatTopicCloser').on 'click', -> @@ -1056,7 +1129,7 @@ Joins a room. $('.timsChatMessage').addClass 'unloaded' - document.title = v.titleTemplate.fetch roomList.active + document.title = v.titleTemplate.fetch getRoomList().active handleMessages roomList.active.messages do getMessages do refreshRoomList @@ -1191,6 +1264,8 @@ Remove the given callback from the given event. true + getRoomList = -> JSON.parse JSON.stringify roomList + The following code handles attachment uploads Enable attachment code if `WCF.Attachment.Upload` is defined @@ -1416,8 +1491,8 @@ Return a copy of the object containing the IDs of the marked messages getMarkedMessages: -> JSON.parse JSON.stringify markedMessages getUserList: -> JSON.parse JSON.stringify userList - getRoomList: -> JSON.parse JSON.stringify roomList - + getRoomList: getRoomList + refreshRoomList: refreshRoomList insertText: insertText freeTheFish: freeTheFish diff --git a/file/lib/data/user/UserAction.class.php b/file/lib/data/user/UserAction.class.php index 3201aa5..938dba0 100644 --- a/file/lib/data/user/UserAction.class.php +++ b/file/lib/data/user/UserAction.class.php @@ -42,4 +42,86 @@ public function updateOption() { )); $userAction->executeAction(); } + + /** + * Validates invite preparation. + */ + public function validatePrepareInvite() { + WCF::getSession()->checkPermissions(array('user.chat.canInvite')); + + if (!WCF::getUser()->chatRoomID) throw new \wcf\system\exception\PermissionDeniedException(); + + $room = \chat\data\room\RoomCache::getInstance()->getRoom(WCF::getUser()->chatRoomID); + + if ($room === null) throw new \wcf\system\exception\IllegalLinkException(); + if (!$room->canEnter()) throw new \wcf\system\exception\PermissionDeniedException(); + } + + /** + * Prepares invites. + */ + public function prepareInvite() { + $followingList = new \wcf\data\user\follow\UserFollowingList(); + $followingList->getConditionBuilder()->add('user_follow.userID = ?', array(WCF::getUser()->userID)); + $followingList->readObjects(); + $users = $followingList->getObjects(); + + $json = array(); + foreach ($users as $user) { + $json[] = array( + 'userID' => $user->userID, + 'username' => $user->username + ); + } + + return array( + 'users' => $json + ); + } + + /** + * Validates invites. + */ + public function validateInvite() { + WCF::getSession()->checkPermissions(array('user.chat.canInvite')); + + if (!WCF::getUser()->chatRoomID) throw new \wcf\system\exception\PermissionDeniedException(); + + $this->room = \chat\data\room\RoomCache::getInstance()->getRoom(WCF::getUser()->chatRoomID); + + if ($this->room === null) throw new \wcf\system\exception\IllegalLinkException(); + if (!$this->room->canEnter()) throw new \wcf\system\exception\PermissionDeniedException(); + + if (!isset($this->parameters['recipients'])) throw new \wcf\system\exception\UserInputException("recipients"); + + $this->parameters['recipients'] = \wcf\util\ArrayUtil::toIntegerArray($this->parameters['recipients']); + + $ignoredByList = new \wcf\data\user\ignore\UserIgnoreList(); + $ignoredByList->getConditionBuilder()->add('user_ignore.ignoreUserID = ?', array(WCF::getUser()->userID)); + $ignoredByList->getConditionBuilder()->add('user_ignore.userID IN (?)', array($this->parameters['recipients'])); + + if (!empty($ignoredByList->sqlSelects)) $ignoredByList->sqlSelects .= ','; + $ignoredByList->sqlSelects .= "user_ignore.ignoreID"; + $ignoredByList->sqlSelects .= ", user_table.username"; + $ignoredByList->sqlJoins .= " LEFT JOIN wcf".WCF_N."_user user_table ON (user_table.userID = user_ignore.userID)"; + + $ignoredByList->readObjects(); + $ignoredByUsers = $ignoredByList->getObjects(); + + if (!empty($ignoredByUsers)) { + $usernames = array(); + foreach ($ignoredByUsers as $user) { + $usernames[] = $user->username; + } + + throw new \wcf\system\exception\UserInputException("recipients", WCF::getLanguage()->getDynamicVariable('chat.error.invite.ignored', array('users' => $ignoredByUsers, 'usernames' => $usernames))); + } + } + + /** + * Invites users. + */ + public function invite() { + \wcf\system\user\notification\UserNotificationHandler::getInstance()->fireEvent('invited', 'be.bastelstu.chat.room', new \chat\system\user\notification\object\RoomUserNotificationObject($this->room), $this->parameters['recipients'], array('userID' => WCF::getUser()->userID)); + } } diff --git a/file/lib/system/user/notification/event/InvitedUserNotificationEvent.class.php b/file/lib/system/user/notification/event/InvitedUserNotificationEvent.class.php new file mode 100644 index 0000000..c76d2ff --- /dev/null +++ b/file/lib/system/user/notification/event/InvitedUserNotificationEvent.class.php @@ -0,0 +1,75 @@ + + * @package be.bastelstu.chat + * @subpackage system.user.notification.event + */ +class InvitedUserNotificationEvent extends \wcf\system\user\notification\event\AbstractUserNotificationEvent { + /** + * @see \wcf\system\user\notification\event\AbstractUserNotificationEvent::$stackable + */ + protected $stackable = false; + + /** + * @see \wcf\system\user\notification\event\IUserNotificationEvent::getAuthorID() + */ + public function getAuthorID() { + return $this->getAuthor()->userID; + } + + /** + * @see \wcf\system\user\notification\event\IUserNotificationEvent::getAuthor() + */ + public function getAuthor() { + // TODO: caching + return new \wcf\data\user\UserProfile(new \wcf\data\user\User($this->additionalData['userID'])); + } + + /** + * @see \wcf\system\user\notification\event\IUserNotificationEvent::getTitle() + */ + public function getTitle() { + return $this->getLanguage()->get('chat.notification.invited.title'); + } + + /** + * @see \wcf\system\user\notification\event\IUserNotificationEvent::getMessage() + */ + public function getMessage() { + return $this->getLanguage()->getDynamicVariable('chat.notification.invited.message', array( + 'userNotificationObject' => $this->userNotificationObject, + 'author' => $this->author + )); + } + + /** + * @see \wcf\system\user\notification\event\IUserNotificationEvent::getLink() + */ + public function getLink() { + return \wcf\system\request\LinkHandler::getInstance()->getLink('Chat', array( + 'application' => 'chat', + 'object' => $this->userNotificationObject->getDecoratedObject() + )); + } + + /** + * @see \wcf\system\user\notification\event\IUserNotificationEvent::supportsEmailNotification() + */ + public function supportsEmailNotification() { + return false; + } + + /** + * @see \wcf\system\user\notification\event\IUserNotificationEvent::checkAccess() + */ + public function checkAccess() { + // TODO + return true; + } +} diff --git a/file/lib/system/user/notification/object/RoomUserNotificationObject.class.php b/file/lib/system/user/notification/object/RoomUserNotificationObject.class.php new file mode 100644 index 0000000..18d95ec --- /dev/null +++ b/file/lib/system/user/notification/object/RoomUserNotificationObject.class.php @@ -0,0 +1,50 @@ + + * @package be.bastelstu.chat + * @subpackage system.user.notification.object + */ +class RoomUserNotificationObject extends \wcf\data\DatabaseObjectDecorator implements \wcf\system\user\notification\object\IUserNotificationObject { + /** + * @see \wcf\data\DatabaseObjectDecorator::$baseClass + */ + protected static $baseClass = 'chat\data\room\Room'; + + /** + * @see \wcf\system\user\notification\object\IUserNotificationObject::getObjectID() + */ + public function getObjectID() { + return $this->roomID; + } + + /** + * @see \wcf\system\user\notification\object\IUserNotificationObject::getTitle() + */ + public function getTitle() { + return $this->getDecoratedObject()->getTitle(); + } + + /** + * @see \wcf\system\user\notification\object\IUserNotificationObject::getURL() + */ + public function getURL() { + return \wcf\system\request\LinkHandler::getInstance()->getLink('Chat', array( + 'application' => 'chat', + 'object' => $this->userNotificationObject->getRoom(), + )); + } + + /** + * @see \wcf\system\user\notification\object\IUserNotificationObject::getAuthorID() + */ + public function getAuthorID() { + // this value is ignored + return PHP_INT_MAX; + } +} diff --git a/file/lib/system/user/notification/object/type/RoomUserNotificationObjectType.class.php b/file/lib/system/user/notification/object/type/RoomUserNotificationObjectType.class.php new file mode 100644 index 0000000..5a0eed1 --- /dev/null +++ b/file/lib/system/user/notification/object/type/RoomUserNotificationObjectType.class.php @@ -0,0 +1,28 @@ + + * @package be.bastelstu.chat + * @subpackage system.user.notification.object.type + */ +class RoomUserNotificationObjectType extends \wcf\system\user\notification\object\type\AbstractUserNotificationObjectType { + /** + * @see \wcf\system\user\notification\object\type\AbstractUserNotificationObjectType::$decoratorClassName + */ + protected static $decoratorClassName = 'chat\system\user\notification\object\RoomUserNotificationObject'; + + /** + * @see \wcf\system\user\notification\object\type\AbstractUserNotificationObjectType::$objectClassName + */ + protected static $objectClassName = 'chat\data\room\Room'; + + /** + * @see \wcf\system\user\notification\object\type\AbstractUserNotificationObjectType::$objectListClassName + */ + protected static $objectListClassName = 'chat\data\room\RoomList'; +} diff --git a/language/de.xml b/language/de.xml index e2597a8..1547589 100644 --- a/language/de.xml +++ b/language/de.xml @@ -39,6 +39,11 @@ + + + username}“ möchte mit Ihnen im Chatraum „{$userNotificationObject->getTitle()}“ chatten!]]> + + @@ -55,6 +60,7 @@ + @@ -148,6 +154,7 @@ Probieren Sie, den Chat neu zu laden