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