1
0
mirror of https://github.com/wbbaddons/Tims-Chat.git synced 2024-10-31 14:10:08 +00:00

Merge branch 'master' into protocol

Conflicts:
	acpMenu.xml
This commit is contained in:
Tim Düsterhus 2013-07-24 21:55:37 +02:00
commit db8fa89e7b
42 changed files with 1452 additions and 617 deletions

View File

@ -1,5 +1,6 @@
language: php
php:
- 5.5
- 5.4
- 5.3
before_install:

View File

@ -23,6 +23,13 @@
<showorder>2</showorder>
</acpmenuitem>
<acpmenuitem name="chat.acp.menu.link.suspension.list">
<controller><![CDATA[chat\acp\page\ChatSuspensionListPage]]></controller>
<parent>chat.acp.menu.link.chat</parent>
<permissions>admin.chat.canManageSuspensions</permissions>
<showorder>3</showorder>
</acpmenuitem>
<acpmenuitem name="chat.acp.menu.link.log">
<controller><![CDATA[chat\acp\page\MessageLogListPage]]></controller>
<parent>chat.acp.menu.link.chat</parent>

View File

@ -0,0 +1,167 @@
{include file='header' pageTitle='chat.acp.suspension.list'}
<script>
//<![CDATA[
$(function() {
new WCF.Search.User('#username', null, false, [ ], false);
new WCF.Search.User('#issuerUsername', null, false, [ ], false);
var proxy = new WCF.Action.Proxy({
success: function (data, textStatus, jqXHR) {
$('.jsSuspensionRow').each(function(index, row) {
var row = $(row);
if (WCF.inArray(row.data('objectID'), data.objectIDs)) {
row.find('.jsRevokeButton').addClass('disabled').removeClass('pointer').off('click');
(new WCF.System.Notification('{"chat.acp.suspension.revoke.success"|language|encodeJS}')).show();
}
});
}
});
$('.jsRevokeButton:not(.disabled)').click(function () {
var objectID = $(this).parents('.jsSuspensionRow').data('objectID');
WCF.System.Confirmation.show($(this).data('confirmMessage'), $.proxy(function (action) {
if (action === 'confirm') {
proxy.setOption('data', {
actionName: 'revoke',
className: '\\chat\\data\\suspension\\SuspensionAction',
objectIDs: [ objectID ]
});
proxy.sendRequest();
}
}, this));
});
});
//]]>
</script>
<header class="boxHeadline">
<h1>{lang}chat.acp.suspension.list{/lang}</h1>
</header>
<form method="post" action="{link controller='ChatSuspensionList' application='chat'}{/link}">
<div class="container containerPadding marginTop">
<fieldset>
<legend>{lang}wcf.global.filter{/lang}</legend>
<dl>
<dd>
<label><input type="checkbox" id="displayRevoked" name="displayRevoked" value="1"{if $displayRevoked} checked="checked"{/if} /> {lang}chat.acp.suspension.displayRevoked{/lang}</label>
</dd>
</dl>
<dl>
<dt><label for="username">{lang}wcf.user.username{/lang}</label></dt>
<dd>
<input type="text" id="username" name="username" class="medium" value="{$username}" />
</dd>
</dl>
<dl>
<dt><label for="issuerUsername">{lang}chat.acp.suspension.issuer{/lang}</label></dt>
<dd>
<input type="text" id="issuerUsername" name="issuerUsername" class="medium" value="{$issuerUsername}" />
</dd>
</dl>
<dl>
<dt><label for="roomID">{lang}chat.general.room{/lang}</label></dt>
<dd>
<select id="roomID" name="roomID">
<option value="-1"{if $roomID == -1} selected="selected"{/if}></option>
<option value="0"{if $roomID == 0} selected="selected"{/if}>{lang}chat.room.global{/lang}</option>
<option value="" disabled="disabled">-------------</option>
{foreach from=$availableRooms key=id item=room}
<option value="{$id}" {if $roomID == $id}selected="selected"{/if}>{$room}</option>
{/foreach}
</select>
</dd>
</dl>
<dl>
<dt><label for="suspensionType">{lang}chat.acp.suspension.type{/lang}</label></dt>
<dd>
<select id="suspensionType" name="suspensionType">
<option value=""{if $suspensionType == null} selected="selected"{/if}></option>
<option value="{'\chat\data\suspension\Suspension::TYPE_MUTE'|constant}"{if $suspensionType == '\chat\data\suspension\Suspension::TYPE_MUTE'|constant} selected="selected"{/if}>{lang}chat.suspension.{'\chat\data\suspension\Suspension::TYPE_MUTE'|constant}{/lang}</option>
<option value="{'\chat\data\suspension\Suspension::TYPE_BAN'|constant}"{if $suspensionType == '\chat\data\suspension\Suspension::TYPE_BAN'|constant} selected="selected"{/if}>{lang}chat.suspension.{'\chat\data\suspension\Suspension::TYPE_BAN'|constant}{/lang}</option>
</select>
</dd>
</dl>
</fieldset>
</div>
<div class="formSubmit">
<input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
</div>
</form>
{if $objects|count}
{capture assign=additionalParameters}{*
*}{if $userID}&userID={$userID}{/if}{*
*}{if $issuerUserID}&issuerUserID={$issuerUserID}{/if}{*
*}{if $roomID}&roomID={$roomID}{/if}{*
*}{if $suspensionType}&suspensionType={$suspensionType}{/if}{*
*}{if $displayRevoked}&displayRevoked={$displayRevoked}{/if}{*
*}{/capture}
<div class="contentNavigation">
{pages print=true assign=pagesLinks application="chat" controller="ChatSuspensionList" link="pageNo=%d$additionalParameters"}
</div>
<div class="tabularBox tabularBoxTitle marginTop">
<header>
<h2>{lang}chat.acp.suspension.list{/lang} <span class="badge badgeInverse">{#$items}</span></h2>
</header>
<table class="table">
<thead>
<tr>
<th class="columnID{if $sortField == 'suspensionID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=suspensionID&sortOrder={if $sortField == 'suspensionID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
<th class="columnUsername{if $sortField == 'username'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=username&sortOrder={if $sortField == 'username' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}wcf.user.username{/lang}</a></th>
<th class="columnRoomID{if $sortField == 'roomID'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=roomID&sortOrder={if $sortField == 'roomID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.general.room{/lang}</a></th>
<th class="columnSuspensionType{if $sortField == 'type'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=type&sortOrder={if $sortField == 'type' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.acp.suspension.type{/lang}</a></th>
<th class="columnTime{if $sortField == 'time'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=time&sortOrder={if $sortField == 'time' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.general.time{/lang}</a></th>
<th class="columnExpires{if $sortField == 'expires'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=expires&sortOrder={if $sortField == 'expires' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.general.expires{/lang}</a></th>
<th class="columnIssuer{if $sortField == 'issuer'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=issuer&sortOrder={if $sortField == 'issuer' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.acp.suspension.issuer{/lang}</a></th>
<th class="columnMessage{if $sortField == 'reason'} active {@$sortOrder}{/if}"><a href="{link application='chat' controller='ChatSuspensionList'}pageNo={@$pageNo}&sortField=reason&sortOrder={if $sortField == 'reason' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$additionalParameters}{/link}">{lang}chat.acp.suspension.reason{/lang}</a></th>
{event name='columnHeads'}
</tr>
</thead>
<tbody>
{foreach from=$objects item=$suspension}
<tr class="jsSuspensionRow" data-object-id="{$suspension->suspensionID}">
<td class="columnIcon">
<span class="icon icon16 icon-undo{if $suspension->expires <= TIME_NOW} disabled{else} pointer{/if} jsRevokeButton" title="{lang}chat.acp.suspension.revoked{/lang}" data-confirm-message="{lang}chat.acp.suspension.revoke.sure{/lang}"></span>
{event name='rowButtons'}
</td>
<td id="columnID">{#$suspension->suspensionID}</td>
<td id="columnUsername"><a href="{link application='chat' controller='ChatSuspensionList'}userID={$suspension->userID}{/link}">{$suspension->username}</a></td>
<td id="columnRoomID"><a href="{link application='chat' controller='ChatSuspensionList'}roomID={if $suspension->roomID}{$suspension->roomID}{else}0{/if}{/link}">{if $suspension->roomID}{$suspension->roomTitle|language}{else}{lang}chat.room.global{/lang}{/if}</a></td>
<td id="columnSuspensionType"><a href="{link application='chat' controller='ChatSuspensionList'}suspensionType={$suspension->type}{/link}">{lang}chat.suspension.{@$suspension->type}{/lang}</a></td>
<td id="columnTime">{$suspension->time|plainTime}</td>
<td id="columnExpires">
<p>{$suspension->expires|plainTime}{if $suspension->expires > TIME_NOW} ({$suspension->expires|dateDiff}){/if}</p>
{if $suspension->revoker && $suspension->expires <= TIME_NOW}<p><small>{lang}chat.acp.suspension.revokedBy{/lang}</small></p>{/if}
</td>
<td id="columnIssuer"><a href="{link application='chat' controller='ChatSuspensionList'}issuerUserID={$suspension->issuer}{/link}">{$suspension->issuerUsername}</a></td>
<td id="columnMessage"{if $suspension->reason != $suspension->reason|truncate:30} class="jsTooltip" title="{$suspension->reason}"{/if}>{$suspension->reason|truncate:30}</a></td>
{event name='columns'}
</tr>
{/foreach}
</tbody>
</table>
</div>
<div class="contentNavigation">
{@$pagesLinks}
</div>
{else}
<p class="info">{lang}wcf.global.noItems{/lang}</p>
{/if}
{include file='footer'}

View File

@ -1,7 +1,7 @@
{include file='header' pageTitle='chat.acp.room.'|concat:$action}
<script type="text/javascript" src="{@$__wcf->getPath('wcf')}js/WCF.ACL.js"></script>
<script type="text/javascript">
<script src="{@$__wcf->getPath('wcf')}js/WCF.ACL.js"></script>
<script>
//<![CDATA[
$(function() {
new WCF.ACL.List($('#groupPermissions'), {@$objectTypeID}, ''{if $roomID|isset}, {@$roomID}{/if});

View File

@ -1,6 +1,6 @@
{include file='header' pageTitle='chat.acp.room.list'}
<script type="text/javascript">
<script>
//<![CDATA[
$(function() {
new WCF.Action.Delete('\\chat\\data\\room\\RoomAction', $('.chatRoomRow'));

View File

@ -33,7 +33,16 @@ exposed by a function if necessary.
newMessageCount = 0
scrollUpNotifications = off
chatSession = Date.now()
userList =
current: {}
allTime: {}
currentRoom = {}
errorVisible = false
inputErrorHidingTimer = null
lastMessage = null
openChannel = 0
remainingFailures = 3
@ -106,21 +115,29 @@ Make the user leave the chat when **Tims Chat** is about to be unloaded.
Insert the appropriate smiley code into the input when a smiley is clicked.
$('#smilies').on 'click', 'img', ->
insertText ' ' + $(@).attr('alt') + ' '
insertText " #{$(@).attr('alt')} "
Handle private channel menu
$('#privateChannelsMenu').on 'click', '.privateChannel', ->
openPrivateChannel $(@).data 'privateChannelID'
Handle submitting the form. The message will be validated by some basic checks, passed to the `submit` eventlisteners
and afterwards sent to the server by an AJAX request.
$('#timsChatForm').submit (event) ->
event.preventDefault()
do event.preventDefault
text = $('#timsChatInput').val().trim()
text = do $('#timsChatInput').val().trim
$('#timsChatInput').val('').focus().keyup()
return false if text.length is 0
unless openChannel is 0
text = "/whisper #{userList.allTime[openChannel].username}, #{text}"
# Free the fish!
freeTheFish() if text.toLowerCase() is '/free the fish'
do freeTheFish if text.toLowerCase() is '/free the fish'
text = do (text) ->
obj =
@ -139,16 +156,13 @@ and afterwards sent to the server by an AJAX request.
enableSmilies: $('#timsChatSmilies').data 'status'
showLoadingOverlay: false
success: ->
$('#timsChatInputContainer').removeClass('formError').find('.innerError').hide()
getMessages()
do hideInputError
do getMessages
failure: (data) ->
return true unless (data?.returnValues?.errorType?) or (data?.message?)
$('#timsChatInputContainer').addClass('formError').find('.innerError').show().html (data?.returnValues?.errorType) ? data.message
setTimeout ->
$('#timsChatInputContainer').removeClass('formError').find('.innerError').hide()
, 5e3
showInputError (data?.returnValues?.errorType) ? data.message
false
@ -157,11 +171,11 @@ The the word the caret is in will be passed to `autocomplete` and replaced if a
$('#timsChatInput').keydown (event) ->
if event.keyCode is $.ui.keyCode.TAB
input = $(event.currentTarget)
event.preventDefault()
do event.preventDefault
input = $ @
autocomplete.value ?= input.val()
autocomplete.caret ?= input.getCaret()
autocomplete.value ?= do input.val
autocomplete.caret ?= do input.getCaret
beforeCaret = autocomplete.value.substring 0, autocomplete.caret
lastSpace = beforeCaret.lastIndexOf ' '
@ -177,7 +191,7 @@ The the word the caret is in will be passed to `autocomplete` and replaced if a
return if toComplete.length is 0
console.log "Autocompleting '#{toComplete}'"
if beforeComplete is '' and toComplete.substring(0, 1) is '/'
if beforeComplete is '' and (toComplete.substring 0, 1) is '/'
regex = new RegExp "^#{WCF.String.escapeRegExp toComplete.substring 1}", "i"
# TODO: Proper command list
commands = (command for command in v.config.installedCommands when regex.test command)
@ -185,7 +199,10 @@ The the word the caret is in will be passed to `autocomplete` and replaced if a
toComplete = '/' + commands[autocomplete.offset++ % commands.length] + ' ' if commands.length isnt 0
else
regex = new RegExp "^#{WCF.String.escapeRegExp toComplete}", "i"
users = (username for user in $('.timsChatUser') when regex.test(username = $(user).data('username')))
users = [ ]
for userID, user of userList.current
users.push user.username if regex.test user.username
toComplete = users[autocomplete.offset++ % users.length] + ', ' if users.length isnt 0
@ -195,7 +212,7 @@ The the word the caret is in will be passed to `autocomplete` and replaced if a
Reset autocompleter to default status, when a key is pressed that is not TAB.
else
$('#timsChatInput').click()
do $('#timsChatInput').click
Reset autocompleter to default status, when the input is `click`ed, as the position of the caret may have changed.
@ -208,14 +225,14 @@ Reset autocompleter to default status, when the input is `click`ed, as the posit
Refresh the room list when the associated button is `click`ed.
$('#timsChatRoomList button').click ->
refreshRoomList()
do refreshRoomList
Clear the chat by removing every single message once the clear button is `clicked`.
$('#timsChatClear').click (event) ->
event.preventDefault()
$('.timsChatMessage').remove()
$('#timsChatMessageContainer').scrollTop $('#timsChatMessageContainer').prop('scrollHeight')
do event.preventDefault
do $('.timsChatMessageContainer.active .timsChatMessage').remove
$('.timsChatMessageContainer.active').scrollTop $('.timsChatMessageContainer.active').prop 'scrollHeight'
Handle toggling of the toggleable buttons.
@ -230,7 +247,7 @@ Handle toggling of the toggleable buttons.
element.addClass 'active'
element.attr 'title', element.data 'disableMessage'
$('#timsChatInput').focus()
do $('#timsChatInput').focus
Mark smilies as disabled when they are disabled.
@ -251,7 +268,7 @@ Toggle fullscreen mode.
else
$('html').removeClass 'fullscreen'
Toggle checkboxes
Toggle checkboxes.
$('#timsChatMark').click (event) ->
if $(@).data 'status'
@ -259,6 +276,14 @@ Toggle checkboxes
else
$('.timsChatMessageContainer').removeClass 'markEnabled'
Hide topic container.
$('.jsTopicCloser').on 'click', ->
if $('.timsChatMessageContainer.active').data('userID') is 0
$('#timsChatTopic').addClass 'hidden'
else
closePrivateChannel $('.timsChatMessageContainer.active').data('userID')
Visibly mark the message once the associated checkbox is checked.
$(document).on 'click', '.timsChatMessage :checkbox', (event) ->
@ -271,9 +296,9 @@ Scroll down when autoscroll is being activated.
$('#timsChatAutoscroll').click (event) ->
if $('#timsChatAutoscroll').data 'status'
$('#timsChatMessageContainer').scrollTop $('#timsChatMessageContainer').prop('scrollHeight')
$('.timsChatMessageContainer.active').scrollTop $('.timsChatMessageContainer.active').prop 'scrollHeight'
$('#timsChatMessageContainer').on 'scroll', (event) ->
$('.timsChatMessageContainer.active').on 'scroll', (event) ->
element = $ @
scrollTop = element.scrollTop()
scrollHeight = element.prop 'scrollHeight'
@ -282,13 +307,13 @@ Scroll down when autoscroll is being activated.
if scrollTop < scrollHeight - height - 25
if $('#timsChatAutoscroll').data('status') is 1
scrollUpNotifications = on
$('#timsChatAutoscroll').click()
do $('#timsChatAutoscroll').click
if scrollTop > scrollHeight - height - 10
if $('#timsChatAutoscroll').data('status') is 0
scrollUpNotifications = off
$(@).removeClass 'notification'
$('#timsChatAutoscroll').click()
do $('#timsChatAutoscroll').click
Enable duplicate tab detection.
@ -321,11 +346,11 @@ load messages if the appropriate event arrives.
do ->
be.bastelstu.wcf.nodePush.onConnect ->
console.log 'Disabling periodic loading'
pe.getMessages.stop()
do pe.getMessages.stop
be.bastelstu.wcf.nodePush.onDisconnect ->
console.log 'Enabling periodic loading'
getMessages()
do getMessages
pe.getMessages = new WCF.PeriodicalExecuter getMessages, v.config.reloadTime * 1e3
be.bastelstu.wcf.nodePush.onMessage 'be.bastelstu.chat.newMessage', getMessages
@ -334,18 +359,37 @@ load messages if the appropriate event arrives.
Finished! Enable the input now and join the chat.
join roomID
$('#timsChatInput').enable().jCounter().focus();
do $('#timsChatInput').enable().jCounter().focus
console.log "Finished initializing"
true
Shows an error message below the input.
showInputError = (message) ->
$('#timsChatInputContainer').addClass('formError').find('.innerError').show().html message
clearTimeout inputErrorHidingTimer if inputErrorHidingTimer?
inputErrorHidingTimer = setTimeout ->
do hideInputError
, 5e3
Hides the error message below the input.
hideInputError = ->
clearTimeout inputErrorHidingTimer if inputErrorHidingTimer?
inputErrorHidingTimer = null
do $('#timsChatInputContainer').removeClass('formError').find('.innerError').hide
Free the fish.
freeTheFish = ->
return if $.wcfIsset 'fish'
console.warn 'Freeing the fish'
fish = $ """<div id="fish">#{WCF.String.escapeHTML('><((((\u00B0>')}</div>"""
fish.css
position: 'absolute'
top: '150px'
@ -389,7 +433,7 @@ Fetch new messages from the server and pass them to `handleMessages`. The userli
error: ->
console.error "Message loading failed, #{--remainingFailures} remaining"
if remainingFailures <= 0
freeTheFish()
do freeTheFish
console.error 'To many failures, aborting'
showError WCF.Language.get 'chat.error.onMessageLoad'
@ -406,12 +450,24 @@ Prevent loading messages in parallel.
Insert the given messages into the chat stream.
handleMessages = (messages) ->
$('#timsChatMessageContainer').trigger 'scroll'
$('.timsChatMessageContainer.active').trigger 'scroll'
for message in messages
message.isInPrivateChannel = (String(message.type) is v.config.messageTypes.WHISPER) and ($.wcfIsset("timsChatMessageContainer#{message.receiver}") or $.wcfIsset("timsChatMessageContainer#{message.sender}"))
events.newMessage.fire message
output = v.messageTemplate.fetch message
createNewMessage = yes
if $('.timsChatMessage:last-child .text').is('ul') and lastMessage isnt null and lastMessage.type in [ 0, 7 ]
if lastMessage.type is message.type and lastMessage.sender is message.sender and lastMessage.receiver is message.receiver and lastMessage.isInPrivateChannel is message.isInPrivateChannel
createNewMessage = no
if createNewMessage
message.isFollowUp = no
output = v.messageTemplate.fetch
message: message
messageTypes: v.config.messageTypes
li = $ '<li></li>'
li.addClass 'timsChatMessage'
li.addClass "timsChatMessage#{message.type}"
@ -419,16 +475,40 @@ Insert the given messages into the chat stream.
li.addClass 'ownMessage' if message.sender is WCF.User.userID
li.append output
li.appendTo $ '#timsChatMessageContainer > ul'
if message.isInPrivateChannel and message.sender is WCF.User.userID
li.appendTo $ "#timsChatMessageContainer#{message.receiver} > ul"
else if message.isInPrivateChannel
li.appendTo $ "#timsChatMessageContainer#{message.sender} > ul"
else
li.appendTo $ '#timsChatMessageContainer0 > ul'
else
message.isFollowUp = yes
output = v.messageTemplate.fetch
message: message
messageTypes: v.config.messageTypes
$('#timsChatMessageContainer').scrollTop $('#timsChatMessageContainer').prop('scrollHeight') if $('#timsChatAutoscroll').data('status') is 1
if message.isInPrivateChannel and message.sender is WCF.User.userID
messageContainerID = message.receiver
else if message.isInPrivateChannel
messageContainerID = message.sender
else
messageContainerID = 0
$("#timsChatMessageContainer#{messageContainerID} .timsChatMessage:last-child .text").append $(output).find('.text li:last-child')
lastMessage = message
$('.timsChatMessageContainer.active').scrollTop $('.timsChatMessageContainer.active').prop('scrollHeight') if $('#timsChatAutoscroll').data('status') is 1
Rebuild the userlist based on the given `users`.
handleUsers = (users) ->
foundUsers = { }
userList.current = { }
for user in users
do (user) ->
userList.current[user.userID] = userList.allTime[user.userID] = user
id = "timsChatUser#{user.userID}"
Move the user to the new position if he was found in the old list.
@ -471,15 +551,18 @@ Build HTML of the user and insert it into the list, if the users was not found i
li.append v.userTemplate.fetch user
menu = $ '<ul></ul>'
menu.addClass 'dropdownMenu'
menu.append $ "<li><a>#{WCF.Language.get('chat.general.query')}</a></li>"
unless user.userID is WCF.User.userID
menu.append $("<li><a>#{WCF.Language.get('chat.general.query')}</a></li>").click -> openPrivateChannel user.userID
menu.append $ "<li><a>#{WCF.Language.get('chat.general.kick')}</a></li>"
menu.append $ "<li><a>#{WCF.Language.get('chat.general.ban')}</a></li>"
menu.append $ """<li><a href="#{user.link}">#{WCF.Language.get('chat.general.profile')}</a></li>"""
events.userMenu.fire user, menu
if menu.find('li').length
li.append menu
menu.addClass 'dropdownMenu'
li.appendTo $ '#timsChatUserList > ul'
foundUsers[id] = true
@ -489,7 +572,7 @@ Remove all users that left the chat.
$('.timsChatUser').each ->
unless foundUsers[$(@).attr('id')]?
console.log "Removing User: '#{$(@).data('username')}'"
$(@).remove();
do $(@).remove
$('#toggleUsers .badge').text $('.timsChatUser').length
@ -505,18 +588,29 @@ the existing text. If `options.submit` is true the message will be sent to the s
text = $('#timsChatInput').val() + text if options.append
$('#timsChatInput').val text
$('#timsChatInput').keyup()
do $('#timsChatInput').keyup
if (options.submit)
$('#timsChatForm').submit()
if options.submit
do $('#timsChatForm').submit
else
$('#timsChatInput').focus()
do $('#timsChatInput').focus
Send out notifications for the given `message`. The number of unread messages will be prepended to `document.title` and if available desktop notifications will be sent.
notify = (message) ->
if scrollUpNotifications
$('#timsChatMessageContainer').addClass 'notification'
$('.timsChatMessageContainer.active').addClass 'notification'
if message.isInPrivateChannel
if message.sender is WCF.User.userID
privateChannelID = message.receiver
else
privateChannelID = message.sender
if $('.timsChatMessageContainer.active').data('userID') isnt privateChannelID
$("#privateChannel#{privateChannelID}").addClass 'notify'
else if $('.timsChatMessageContainer.active').data('userID') isnt 0
$("#privateChannel0").addClass 'notify'
return if isActive or $('#timsChatNotify').data('status') is 0
@ -532,9 +626,9 @@ Send out notifications for the given `message`. The number of unread messages wi
notification = new window.Notification title,
body: content
onclick: ->
notification.close()
do notification.close
setTimeout ->
notification.close()
do notification.close
, 5e3
Fetch the roomlist from the server and update it in the GUI.
@ -550,7 +644,7 @@ Fetch the roomlist from the server and update it in the GUI.
showLoadingOverlay: false
suppressErrors: true
success: (data) ->
$('.timsChatRoom').remove()
do $('.timsChatRoom').remove
$('#toggleRooms .badge').text data.returnValues.length
for room in data.returnValues
@ -561,8 +655,8 @@ Fetch the roomlist from the server and update it in the GUI.
if window.history?.replaceState?
$('.timsChatRoom').click (event) ->
event.preventDefault()
target = $(@)
do event.preventDefault
target = $ @
window.history.replaceState {}, '', target.attr 'href'
@ -580,8 +674,8 @@ Shows an unrecoverable error with the given text.
loading = true
pe.refreshRoomList.stop()
pe.getMessages.stop()
do pe.refreshRoomList.stop
do pe.getMessages.stop
errorDialog = $("""
<div id="timsChatLoadingErrorDialog">
@ -592,7 +686,7 @@ Shows an unrecoverable error with the given text.
formSubmit = $("""<div class="formSubmit"></div>""").appendTo errorDialog
reloadButton = $("""<button class="buttonPrimary">#{WCF.Language.get 'chat.error.reload'}</button>""").appendTo formSubmit
reloadButton.on 'click', ->
window.location.reload()
do window.location.reload
$('#timsChatLoadingErrorDialog').wcfDialog
closable: false
@ -612,35 +706,116 @@ Joins a room.
success: (data) ->
loading = false
$('#timsChatTopic').text data.returnValues.topic
if data.returnValues.topic.trim() is ''
$('#timsChatTopic').removeClass 'hidden'
currentRoom = data.returnValues
currentRoom.roomID = roomID
$('#timsChatTopic > .topic').text currentRoom.topic
if currentRoom.topic.trim() is ''
$('#timsChatTopic').addClass 'empty'
else
$('#timsChatTopic').removeClass 'empty'
$('.timsChatMessage').addClass 'unloaded'
document.title = v.titleTemplate.fetch data.returnValues
handleMessages data.returnValues.messages
getMessages()
refreshRoomList()
document.title = v.titleTemplate.fetch currentRoom
handleMessages currentRoom.messages
do getMessages
do refreshRoomList
failure: ->
showError WCF.Language.get 'chat.error.join'
Open private channel
openPrivateChannel = (userID) ->
userID = parseInt userID
console.log "Opening private channel #{userID}"
unless $.wcfIsset "timsChatMessageContainer#{userID}"
return unless userList.allTime[userID]?
div = $ '<div>'
div.attr 'id', "timsChatMessageContainer#{userID}"
div.data 'userID', userID
div.addClass 'timsChatMessageContainer'
div.addClass 'marginTop'
div.addClass 'container'
div.wrapInner '<ul>'
$('#timsChatMessageContainer0').after div
$('.privateChannel').removeClass 'active'
if userID isnt 0
$('#timsChatTopic').removeClass 'hidden empty'
$('#timsChatTopic > .topic').text WCF.Language.get 'chat.general.privateChannelTopic', {username: userList.allTime[userID].username}
$('#timsChatTopic > .jsTopicCloser').attr 'title', WCF.Language.get 'chat.general.closePrivateChannel'
unless $.wcfIsset "privateChannel#{userID}"
li = $ '<li>'
li.attr 'id', "privateChannel#{userID}"
li.data 'privateChannelID', userID
li.addClass 'privateChannel'
span = $ '<span class="userAvatar framed" />'
avatar = $ userList.allTime[userID].avatar[16]
avatar.addClass 'jsTooltip'
avatar.attr 'title', userList.allTime[userID].username
avatar.wrap span
li.append avatar.parent().addClass 'small'
avatar = $ userList.allTime[userID].avatar[32]
avatar.addClass 'jsTooltip'
avatar.attr 'title', userList.allTime[userID].username
avatar.wrap span
li.append avatar.parent().addClass 'large'
$('#privateChannelsMenu ul').append li
$('#privateChannelsMenu').addClass 'shown'
else
$('#timsChatTopic > .topic').text currentRoom.topic
$('#timsChatTopic > .jsTopicCloser').attr 'title', WCF.Language.get 'chat.general.closeTopic'
if currentRoom.topic.trim() is ''
$('#timsChatTopic').addClass 'empty'
else
$('#timsChatTopic').removeClass 'empty'
do WCF.DOMNodeInsertedHandler.execute
$('.timsChatMessageContainer').removeClass 'active'
$("#timsChatMessageContainer#{userID}").addClass 'active'
$("#privateChannel#{userID}").addClass('active').removeClass 'notify'
openChannel = userID
Close private channel
closePrivateChannel = (userID) ->
unless userID is 0
do $("#privateChannel#{userID}").remove
do $("#timsChatMessageContainer#{userID}").remove
if $('#privateChannelsMenu li').length <= 1
$('#privateChannelsMenu').removeClass 'shown'
openPrivateChannel 0
Bind the given callback to the given event.
addListener = (event, callback) ->
return false unless events[event]?
events[event].add callback
true
Remove the given callback from the given event.
removeListener = (event, callback) ->
return false unless events[event]?
events[event].remove callback
true
And finally export the public methods and variables.
Chat =
@ -650,6 +825,8 @@ And finally export the public methods and variables.
insertText: insertText
freeTheFish: freeTheFish
join: join
closePrivateChannel: closePrivateChannel # TODO: REMOVE AFTER DEBUGGING
openPrivateChannel: openPrivateChannel # TODO: REMOVE AFTER DEBUGGING
listener:
add: addListener
remove: removeListener

View File

@ -116,7 +116,7 @@ public function save() {
}
\wcf\system\acl\ACLHandler::getInstance()->save($roomID, $this->objectTypeID);
\chat\system\permission\permissionHandler::clearCache();
\chat\system\permission\PermissionHandler::clearCache();
$this->saved();

View File

@ -0,0 +1,172 @@
<?php
namespace chat\acp\page;
use \wcf\system\WCF;
/**
* Lists chat suspensions.
*
* @author Maximilian Mader
* @copyright 2010-2013 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage acp.page
*/
class ChatSuspensionListPage extends \wcf\page\SortablePage {
/**
* @see \wcf\page\AbstractPage::$activeMenuItem
*/
public $activeMenuItem = 'chat.acp.menu.link.suspension.list';
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array('admin.chat.canManageSuspensions');
/**
* @see \wcf\page\SortablePage::$defaultSortField
*/
public $defaultSortField = 'expires';
/**
* @see \wcf\page\SortablePage::$validSortFields
*/
public $validSortFields = array('suspensionID', 'userID', 'username', 'roomID', 'type', 'expires', 'issuer', 'time', 'reason');
/**
* @see \wcf\page\MultipleLinkPage::$objectListClassName
*/
public $objectListClassName = 'chat\data\suspension\SuspensionList';
/**
* type filter
*
* @var integer
*/
public $filterSuspensionType = null;
/**
* user filter
*
* @var integer
*/
public $filterUserID = null;
/*
* username
*
* @var String
*/
public $filterUsername = null;
/**
* issuer filter
*
* @var integer
*/
public $filterIssuerUserID = null;
/*
* issuer username
*
* @var String
*/
public $filterIssuerUsername = null;
/**
* room filter
*
* @var integer
*/
public $filterRoomID = null;
/**
* display revoked suspensions
*
* @var integer
*/
public $displayRevoked = 0;
/**
* @see \wcf\page\IPage::readParameters()
*/
public function readParameters() {
parent::readParameters();
// get usernames
if (isset($_REQUEST['username']) && !empty($_REQUEST['username'])) $this->filterUsername = \wcf\util\StringUtil::trim($_REQUEST['username']);
if (isset($_REQUEST['issuerUsername']) && !empty($_REQUEST['issuerUsername'])) $this->filterIssuerUsername = \wcf\util\StringUtil::trim($_REQUEST['issuerUsername']);
// get user IDs by username
if ($this->filterUsername != null) $this->filterUserID = \wcf\data\user\UserProfile::getUserProfileByUsername($this->filterUsername)->userID;
if ($this->filterIssuerUsername != null) $this->filterIssuerUserID = \wcf\data\user\UserProfile::getUserProfileByUsername($this->filterIssuerUsername)->userID;
// get user IDs by request if no username was sent
if ($this->filterUserID === null && isset($_REQUEST['userID']) && !empty($_REQUEST['userID'])) $this->filterUserID = intval($_REQUEST['userID']);
if ($this->filterIssuerUserID === null && isset($_REQUEST['issuerUserID']) && !empty($_REQUEST['issuerUserID'])) $this->filterIssuerUserID = intval($_REQUEST['issuerUserID']);
// get usernames by ID if no usernames were sent
if ($this->filterUsername === null) $this->filterUsername = \wcf\data\user\UserProfile::getUserProfile($this->filterUserID);
if ($this->filterIssuerUsername === null) $this->filterIssuerUsername = \wcf\data\user\UserProfile::getUserProfile($this->filterIssuerUserID);
// get room IDs by request
if (isset($_REQUEST['roomID']) && $_REQUEST['roomID'] != -1) $this->filterRoomID = intval($_REQUEST['roomID']);
if (isset($_REQUEST['suspensionType']) && !empty($_REQUEST['suspensionType'])) $this->filterSuspensionType = intval($_REQUEST['suspensionType']);
// display revoked
if (isset($_REQUEST['displayRevoked'])) $this->displayRevoked = intval($_REQUEST['displayRevoked']);
}
/**
* @see wcf\page\IPage::assignVariables()
*/
public function assignVariables() {
parent::assignVariables();
WCF::getTPL()->assign(array(
'availableRooms' => \chat\data\room\RoomCache::getInstance()->getRooms(),
'roomID' => ($this->filterRoomID !== null) ? $this->filterRoomID : -1,
'username' => $this->filterUsername,
'issuerUsername' => $this->filterIssuerUsername,
'suspensionType' => $this->filterSuspensionType,
'userID' => $this->filterUserID,
'issuerUserID' => $this->filterIssuerUserID,
'displayRevoked' => $this->displayRevoked
));
}
/**
* @see \wcf\page\MultipleLinkPage::readObjects()
*/
protected function initObjectList() {
parent::initObjectList();
$this->objectList->sqlSelects .= "user_table.username, user_table2.username AS issuerUsername, user_table3.username AS revokerUsername, room_table.title AS roomTitle";
$this->objectList->sqlJoins .= "
LEFT JOIN wcf".WCF_N."_user user_table
ON suspension.userID = user_table.userID
LEFT JOIN wcf".WCF_N."_user user_table2
ON suspension.issuer = user_table2.userID
LEFT JOIN wcf".WCF_N."_user user_table3
ON suspension.issuer = user_table3.userID";
$conditionJoins = " LEFT JOIN chat".WCF_N."_room room_table
ON suspension.roomID = room_table.roomID";
$this->objectList->sqlConditionJoins .= $conditionJoins;
$this->objectList->sqlJoins .= $conditionJoins;
if (!$this->displayRevoked) {
$this->objectList->getConditionBuilder()->add('expires > ?', array(TIME_NOW));
}
$this->objectList->getConditionBuilder()->add('(room_table.permanent = ? OR suspension.roomID IS NULL)', array(1));
if ($this->filterSuspensionType !== null) $this->objectList->getConditionBuilder()->add('suspension.type = ?', array($this->filterSuspensionType));
if ($this->filterUserID !== null) $this->objectList->getConditionBuilder()->add('suspension.userID = ?', array($this->filterUserID));
if ($this->filterIssuerUserID !== null) $this->objectList->getConditionBuilder()->add('suspension.issuer = ?', array($this->filterIssuerUserID));
if ($this->filterRoomID !== null) {
if ($this->filterRoomID === 0) {
$this->objectList->getConditionBuilder()->add('suspension.roomID IS NULL', array());
}
else {
$this->objectList->getConditionBuilder()->add('suspension.roomID = ?', array($this->filterRoomID));
}
}
}
}

View File

@ -24,6 +24,8 @@ class MessageList extends \wcf\data\DatabaseObjectList {
* @return array<\chat\data\message\Message>
*/
public static function getNewestMessages(\chat\data\room\Room $room, $number = CHAT_LASTMESSAGES) {
if ($number === 0) return array();
$messageList = new static();
$messageList->sqlOrderBy = "message.messageID DESC";
$messageList->sqlLimit = $number;

View File

@ -39,26 +39,33 @@ public function __toString() {
public function canEnter(\wcf\data\user\User $user = null) {
if ($user === null) $user = WCF::getUser();
if (!$user->userID) return false;
$user = new \wcf\data\user\UserProfile($user);
$ph = new \chat\system\permission\PermissionHandler($user);
$suspensions = Suspension::getSuspensionsForUser($user);
if ($user->getPermission('admin.chat.canManageSuspensions')) return true;
if ($user->getPermission('mod.chat.canGban')) return true;
$canEnter = $ph->getPermission($this, 'user.canEnter');
$ph = new \chat\system\permission\PermissionHandler($user->getDecoratedObject());
if ($ph->getPermission($this, 'mod.canAlwaysEnter')) return true;
if ($ph->getPermission($this, 'mod.canBan')) return true;
if (!$ph->getPermission($this, 'user.canEnter')) return false;
$suspensions = Suspension::getSuspensionsForUser($user->getDecoratedObject());
// room suspension
if ($canEnter && isset($suspensions[$this->roomID][Suspension::TYPE_BAN])) {
if (isset($suspensions[$this->roomID][Suspension::TYPE_BAN])) {
if ($suspensions[$this->roomID][Suspension::TYPE_BAN]->isValid()) {
$canEnter = false;
return false;
}
}
// global suspension
if ($canEnter && isset($suspensions[null][Suspension::TYPE_BAN])) {
if (isset($suspensions[null][Suspension::TYPE_BAN])) {
if ($suspensions[null][Suspension::TYPE_BAN]->isValid()) {
$canEnter = false;
return false;
}
}
return $canEnter || $ph->getPermission($this, 'mod.canAlwaysEnter');
return true;
}
/**
@ -70,26 +77,33 @@ public function canEnter(\wcf\data\user\User $user = null) {
public function canWrite(\wcf\data\user\User $user = null) {
if ($user === null) $user = WCF::getUser();
if (!$user->userID) return false;
$user = new \wcf\data\user\UserProfile($user);
$ph = new \chat\system\permission\PermissionHandler($user);
$suspensions = Suspension::getSuspensionsForUser($user);
if ($user->getPermission('admin.chat.canManageSuspensions')) return true;
if ($user->getPermission('mod.chat.canGmute')) return true;
$canWrite = $ph->getPermission($this, 'user.canWrite');
$ph = new \chat\system\permission\PermissionHandler($user->getDecoratedObject());
if ($ph->getPermission($this, 'mod.canAlwaysWrite')) return true;
if ($ph->getPermission($this, 'mod.canMute')) return true;
if (!$ph->getPermission($this, 'user.canWrite')) return false;
$suspensions = Suspension::getSuspensionsForUser($user->getDecoratedObject());
// room suspension
if ($canWrite && isset($suspensions[$this->roomID][Suspension::TYPE_MUTE])) {
if (isset($suspensions[$this->roomID][Suspension::TYPE_MUTE])) {
if ($suspensions[$this->roomID][Suspension::TYPE_MUTE]->isValid()) {
$canWrite = false;
return false;
}
}
// global suspension
if ($canWrite && isset($suspensions[null][Suspension::TYPE_MUTE])) {
if (isset($suspensions[null][Suspension::TYPE_MUTE])) {
if ($suspensions[null][Suspension::TYPE_MUTE]->isValid()) {
$canWrite = false;
return false;
}
}
return $canWrite || $ph->getPermission($this, 'mod.canAlwaysWrite');
return true;
}
/**

View File

@ -221,21 +221,22 @@ public function join() {
$messageAction->executeAction();
}
$newestMessages = message\ViewableMessageList::getNewestMessages($room, CHAT_LASTMESSAGES);
$newestMessages = message\ViewableMessageList::getNewestMessages($room, CHAT_LASTMESSAGES + CHAT_DISPLAY_JOIN_LEAVE);
try {
$lastSeen = end($newestMessages)->messageID;
}
catch (\wcf\system\exception\SystemException $e) {
$lastSeen = 0;
}
// update last seen message
$sql = "SELECT
MAX(messageID)
FROM
chat".WCF_N."_message";
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute();
$editor = new \wcf\data\user\UserEditor($this->parameters['user']);
$editor->update(array(
'chatRoomID' => $room->roomID,
'chatAway' => null,
'chatLastActivity' => TIME_NOW,
'chatLastSeen' => $lastSeen
'chatLastSeen' => $stmt->fetchColumn() ?: 0
));
// add activity points

View File

@ -22,8 +22,8 @@ class Suspension extends \chat\data\CHATDatabaseObject {
*/
protected static $databaseTableIndexName = 'suspensionID';
const TYPE_MUTE = 1;
const TYPE_BAN = 2;
const TYPE_MUTE = 'mute';
const TYPE_BAN = 'ban';
/**
* Returns whether the suspension still is valid.
@ -34,6 +34,36 @@ public function isValid() {
return $this->expires > TIME_NOW;
}
/**
* Returns whether the given user may view this suspension.
*
* @param \wcf\data\user\User $user
* @return boolean
*/
public function isVisible($user = null) {
if ($user === null) $user = WCF::getUser();
$user = new \wcf\data\user\UserProfile($user);
$ph = new \chat\system\permission\PermissionHandler($user->getDecoratedObject());
if ($user->getPermission('admin.chat.canManageSuspensions')) return true;
if ($user->getPermission('mod.chat.canG'.$this->type)) return true;
if (!$this->roomID) return false;
if ($ph->getPermission($this->getRoom(), 'mod.can'.ucfirst($this->type))) return true;
return false;
}
/**
* Returns the room of this suspension.
*
* @return \chat\data\room\Room
*/
public function getRoom() {
if (!$this->roomID) return new \chat\data\room\Room(null, array('roomID' => null));
return \chat\data\room\RoomCache::getInstance()->getRoom($this->roomID);
}
/**
* Returns all the suspensions for the specified user (current user if no user was specified).
*
@ -53,15 +83,17 @@ public static function getSuspensionsForUser(\wcf\data\user\User $user = null) {
if ($suspensions === false) throw new \wcf\system\exception\SystemException();
}
catch (\wcf\system\exception\SystemException $e) {
$condition = new \wcf\system\database\util\PreparedStatementConditionBuilder();
$condition->add('userID = ?', array($user->userID));
$condition->add('expires > ?', array(TIME_NOW));
$sql = "SELECT
*
FROM
chat".WCF_N."_suspension
WHERE
userID = ?
AND expires > ?";
".$condition;
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute(array($user->userID, TIME_NOW));
$stmt->execute($condition->getParameters());
$suspensions = array();
while ($suspension = $stmt->fetchObject('\chat\data\suspension\Suspension')) {
@ -76,7 +108,7 @@ public static function getSuspensionsForUser(\wcf\data\user\User $user = null) {
/**
* Returns the appropriate suspension for user, room and type.
* Returns false if no suspension was found.
* Returns false if no active suspension was found.
*
* @param \wcf\data\user\User $user
* @param \chat\data\room\Room $room
@ -84,23 +116,21 @@ public static function getSuspensionsForUser(\wcf\data\user\User $user = null) {
* @return \chat\data\suspension\Suspension
*/
public static function getSuspensionByUserRoomAndType(\wcf\data\user\User $user, \chat\data\room\Room $room, $type) {
$condition = new \wcf\system\database\util\PreparedStatementConditionBuilder();
$condition->add('userID = ?', array($user->userID));
$condition->add('type = ?', array($type));
$condition->add('expires > ?', array(TIME_NOW));
if ($room->roomID) $condition->add('roomID = ?', array($room->roomID));
else $condition->add('roomID IS NULL');
$sql = "SELECT
*
FROM
chat".WCF_N."_suspension
WHERE
userID = ?
AND type = ?";
$parameter = array($user->userID, $type);
if ($room->roomID) {
$sql .= " AND roomID = ?";
$parameter[] = $room->roomID;
}
else $sql .= " AND roomID IS NULL";
".$condition;
$statement = WCF::getDB()->prepareStatement($sql);
$statement->execute($parameter);
$statement->execute($condition->getParameters());
$row = $statement->fetchArray();
if (!$row) return false;

View File

@ -1,5 +1,6 @@
<?php
namespace chat\data\suspension;
use \wcf\system\WCF;
/**
* Executes chat-suspension-related actions.
@ -17,23 +18,28 @@ class SuspensionAction extends \wcf\data\AbstractDatabaseObjectAction {
protected $className = '\chat\data\suspension\SuspensionEditor';
/**
* Deletes expired suspensions.
*
* @return integer Number of deleted suspensions
* Validates permissions and parameters
*/
public function prune() {
$sql = "SELECT
".call_user_func(array($this->className, 'getDatabaseTableIndexName'))."
FROM
".call_user_func(array($this->className, 'getDatabaseTableName'))."
WHERE
expires < ?";
$stmt = \wcf\system\WCF::getDB()->prepareStatement($sql);
$stmt->execute(array(TIME_NOW));
$objectIDs = array();
public function validateRevoke() {
WCF::getSession()->checkPermissions((array) 'admin.chat.canManageSuspensions');
while ($objectID = $stmt->fetchColumn()) $objectIDs[] = $objectID;
$this->parameters['revoker'] = WCF::getUser()->userID;
}
return call_user_func(array($this->className, 'deleteAll'), $objectIDs);
/**
* Revokes suspensions.
*/
public function revoke() {
if (!isset($this->parameters['revoker'])) {
$this->parameters['revoker'] = null;
}
$objectAction = new self($this->objectIDs, 'update', array(
'data' => array(
'expires' => TIME_NOW,
'revoker' => $this->parameters['revoker']
)
));
$objectAction->executeAction();
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace chat\data\suspension;
/**
* Represents a list of chat suspensions.
*
* @author Maximilian Mader
* @copyright 2010-2013 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage data.suspension
*/
class SuspensionList extends \wcf\data\DatabaseObjectList {
/**
* @see wcf\data\DatabaseObjectList::$className
*/
public $className = 'chat\data\suspension\Suspension';
}

View File

@ -84,11 +84,14 @@ class ChatPage extends \wcf\page\AbstractPage {
public function assignVariables() {
parent::assignVariables();
$reflection = new \ReflectionClass('\chat\data\message\Message');
WCF::getTPL()->assign(array(
'room' => $this->room,
'roomID' => $this->roomID,
'rooms' => $this->rooms,
'commands' => $this->commands,
'messageTypes' => $reflection->getConstants(),
'defaultSmilies' => $this->defaultSmilies,
'smileyCategories' => $this->smileyCategories,
'sidebarCollapsed' => \wcf\system\user\collapsible\content\UserCollapsibleContentHandler::getInstance()->isCollapsed('com.woltlab.wcf.collapsibleSidebar', 'be.bastelstu.chat.ChatPage'),

View File

@ -106,10 +106,7 @@ public function show() {
parent::show();
@header('Content-type: application/json');
// enable gzip compression
if (HTTP_ENABLE_GZIP && HTTP_GZIP_LEVEL > 0 && HTTP_GZIP_LEVEL < 10 && !defined('HTTP_DISABLE_GZIP')) {
\wcf\util\HeaderUtil::compressOutput();
}
\wcf\util\HeaderUtil::sendNoCacheHeaders();
$json = array('users' => array(), 'messages' => array());

View File

@ -0,0 +1,126 @@
<?php
namespace chat\system\command;
use \chat\data\suspension;
use \chat\util\ChatUtil;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Default implementation for suspension commands
*
* @author Tim Düsterhus
* @copyright 2010-2013 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command
*/
abstract class AbstractSuspensionCommand extends AbstractRestrictedCommand {
public $user = null;
public $expires = 0;
public $suspensionAction = null;
public $link = '';
public $room = null;
public $reason = '';
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
try {
$parameters = explode(',', $commandHandler->getParameters(), 3);
list($username, $modifier) = $parameters;
if (isset($parameters[2])) {
$this->reason = \wcf\util\StringUtil::trim($parameters[2]);
}
$modifier = ChatUtil::timeModifier(\wcf\util\StringUtil::trim($modifier));
$expires = strtotime($modifier, TIME_NOW);
$this->expires = min(max(-0x80000000, $expires), 0x7FFFFFFF);
}
catch (\wcf\system\exception\SystemException $e) {
throw new \InvalidArgumentException();
}
$this->user = User::getUserByUsername($username);
if (!$this->user->userID) throw new \chat\system\command\UserNotFoundException($username);
$profile = \wcf\system\request\LinkHandler::getInstance()->getLink('User', array(
'object' => $this->user
));
$this->link = "[url='".$profile."']".$this->user->username.'[/url]';
$this->executeAction();
$this->didInit();
}
public function executeAction() {
if (static::IS_GLOBAL) $room = new \chat\data\room\Room(null, array('roomID' => null));
else $room = $this->room;
if ($suspension = suspension\Suspension::getSuspensionByUserRoomAndType($this->user, $room, static::SUSPENSION_TYPE)) {
if ($suspension->expires >= $this->expires) {
throw new \wcf\system\exception\UserInputException('text', WCF::getLanguage()->get('wcf.chat.suspension.exists'));
}
$action = new suspension\SuspensionAction(array($suspension), 'revoke', array(
'revoker' => WCF::getUser()->userID
));
$action->executeAction();
}
$this->suspensionAction = new suspension\SuspensionAction(array(), 'create', array(
'data' => array(
'userID' => $this->user->userID,
'roomID' => $room->roomID ?: null,
'type' => static::SUSPENSION_TYPE,
'expires' => $this->expires,
'time' => TIME_NOW,
'issuer' => WCF::getUser()->userID,
'reason' => $this->reason
)
));
$this->suspensionAction->executeAction();
}
/**
* @see \chat\system\command\IRestrictedChatCommand::checkPermission()
*/
public function checkPermission() {
parent::checkPermission();
$this->room = $this->commandHandler->getRoom();
if (WCF::getSession()->getPermission('admin.chat.canManageSuspensions')) return;
$ph = new \chat\system\permission\PermissionHandler();
if (static::IS_GLOBAL) {
WCF::getSession()->checkPermissions((array) 'mod.chat.canG'.static::SUSPENSION_TYPE);
}
else {
if (!WCF::getSession()->getPermission('mod.chat.canG'.static::SUSPENSION_TYPE)) {
if (!$ph->getPermission($this->room, 'mod.can'.ucfirst(static::SUSPENSION_TYPE))) {
throw new \wcf\system\exception\PermissionDeniedException();
}
}
}
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_MODERATE;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return serialize(array(
'link' => $this->link,
'expires' => $this->expires,
'type' => (static::IS_GLOBAL ? 'g' : '').static::SUSPENSION_TYPE,
'reason' => $this->reason
));
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace chat\system\command;
use \chat\data\suspension;
use \chat\util\ChatUtil;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Default implementation for suspension commands
*
* @author Tim Düsterhus
* @copyright 2010-2013 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command
*/
abstract class AbstractUnsuspensionCommand extends AbstractRestrictedCommand {
public $user = null;
public $suspensionAction = null;
public $link = '';
public $room = null;
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
$username = rtrim($commandHandler->getParameters(), ',');
$this->user = User::getUserByUsername($username);
if (!$this->user->userID) throw new \chat\system\command\UserNotFoundException($username);
$profile = \wcf\system\request\LinkHandler::getInstance()->getLink('User', array(
'object' => $this->user
));
$this->link = "[url='".$profile."']".$this->user->username.'[/url]';
$this->executeAction();
$this->didInit();
}
public function executeAction() {
if (static::IS_GLOBAL) $room = new \chat\data\room\Room(null, array('roomID' => null));
else $room = $this->room;
if ($suspension = suspension\Suspension::getSuspensionByUserRoomAndType($this->user, $room, static::SUSPENSION_TYPE)) {
$action = new suspension\SuspensionAction(array($suspension), 'revoke', array(
'revoker' => WCF::getUser()->userID
));
$action->executeAction();
}
else {
throw new \wcf\system\exception\UserInputException('text', WCF::getLanguage()->get('wcf.chat.suspension.notExists'));
}
}
/**
* @see \chat\system\command\IRestrictedChatCommand::checkPermission()
*/
public function checkPermission() {
parent::checkPermission();
$this->room = $this->commandHandler->getRoom();
if (WCF::getSession()->getPermission('admin.chat.canManageSuspensions')) return;
$ph = new \chat\system\permission\PermissionHandler();
if (static::IS_GLOBAL) {
WCF::getSession()->checkPermission((array) 'mod.chat.canG'.static::SUSPENSION_TYPE);
}
else {
if (!WCF::getSession()->getPermission('mod.chat.canG'.static::SUSPENSION_TYPE)) {
if (!$ph->getPermission($this->room, 'mod.can'.ucfirst(static::SUSPENSION_TYPE))) {
throw new \wcf\system\exception\PermissionDeniedException();
}
}
}
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_MODERATE;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return serialize(array(
'link' => $this->link,
'type' => 'un'.(static::IS_GLOBAL ? 'g' : '').static::SUSPENSION_TYPE,
));
}
}

View File

@ -36,11 +36,4 @@ public function getType() {
public function getMessage() {
return \wcf\system\bbcode\PreParser::getInstance()->parse($this->commandHandler->getParameters(), explode(',', WCF::getSession()->getPermission('user.chat.allowedBBCodes')));
}
/**
* @see \chat\system\command\ICommand::getReceiver()
*/
public function getReceiver() {
return WCF::getUser()->userID;
}
}

View File

@ -1,9 +1,5 @@
<?php
namespace chat\system\command\commands;
use \chat\data\suspension;
use \chat\util\ChatUtil;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Bans a user.
@ -14,25 +10,7 @@
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class BanCommand extends MuteCommand {
public function executeAction() {
if ($suspension = suspension\Suspension::getSuspensionByUserRoomAndType($this->user, $this->room, suspension\Suspension::TYPE_BAN)) {
if ($suspension->expires > $this->expires) {
throw new \wcf\system\exception\UserInputException('text', WCF::getLanguage()->get('wcf.chat.suspension.exists'));
}
$action = new suspension\SuspensionAction(array($suspension), 'delete');
$action->executeAction();
}
$this->suspensionAction = new suspension\SuspensionAction(array(), 'create', array(
'data' => array(
'userID' => $this->user->userID,
'roomID' => WCF::getUser()->chatRoomID,
'type' => suspension\Suspension::TYPE_BAN,
'expires' => $this->expires
)
));
$this->suspensionAction->executeAction();
}
class BanCommand extends \chat\system\command\AbstractSuspensionCommand {
const IS_GLOBAL = false;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_BAN;
}

View File

@ -1,9 +1,5 @@
<?php
namespace chat\system\command\commands;
use \chat\data\suspension;
use \chat\util\ChatUtil;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Globally bans a user.
@ -14,27 +10,7 @@
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class GbanCommand extends MuteCommand {
public function executeAction() {
$room = new \chat\data\room\Room(null, array('roomID' => null));
if ($suspension = suspension\Suspension::getSuspensionByUserRoomAndType($this->user, $room, suspension\Suspension::TYPE_BAN)) {
if ($suspension->expires > $this->expires) {
throw new \wcf\system\exception\UserInputException('text', WCF::getLanguage()->get('wcf.chat.suspension.exists'));
}
$action = new suspension\SuspensionAction(array($suspension), 'delete');
$action->executeAction();
}
$this->suspensionAction = new suspension\SuspensionAction(array(), 'create', array(
'data' => array(
'userID' => $this->user->userID,
'roomID' => null,
'type' => suspension\Suspension::TYPE_BAN,
'expires' => $this->expires
)
));
$this->suspensionAction->executeAction();
}
class GbanCommand extends \chat\system\command\AbstractSuspensionCommand {
const IS_GLOBAL = true;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_BAN;
}

View File

@ -1,12 +1,8 @@
<?php
namespace chat\system\command\commands;
use \chat\data\suspension;
use \chat\util\ChatUtil;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Globally bans a user.
* Globally mutes a user.
*
* @author Tim Düsterhus
* @copyright 2010-2013 Tim Düsterhus
@ -14,27 +10,7 @@
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class GmuteCommand extends MuteCommand {
public function executeAction() {
$room = new \chat\data\room\Room(null, array('roomID' => null));
if ($suspension = suspension\Suspension::getSuspensionByUserRoomAndType($this->user, $room, suspension\Suspension::TYPE_MUTE)) {
if ($suspension->expires > $this->expires) {
throw new \wcf\system\exception\UserInputException('text', WCF::getLanguage()->get('wcf.chat.suspension.exists'));
}
$action = new suspension\SuspensionAction(array($suspension), 'delete');
$action->executeAction();
}
$this->suspensionAction = new suspension\SuspensionAction(array(), 'create', array(
'data' => array(
'userID' => $this->user->userID,
'roomID' => null,
'type' => suspension\Suspension::TYPE_MUTE,
'expires' => $this->expires
)
));
$this->suspensionAction->executeAction();
}
class GmuteCommand extends \chat\system\command\AbstractSuspensionCommand {
const IS_GLOBAL = true;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_MUTE;
}

View File

@ -1,32 +0,0 @@
<?php
namespace chat\system\command\commands;
use \chat\data\suspension;
use \chat\util\ChatUtil;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Unbans a user globally.
*
* @author Tim Düsterhus
* @copyright 2010-2013 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class GunbanCommand extends UnmuteCommand {
/**
* @see \chat\system\command\commands\UnmuteCommand::executeAction()
*/
public function executeAction() {
$room = new \chat\data\room\Room(null, array('roomID' => null));
if ($suspension = suspension\Suspension::getSuspensionByUserRoomAndType($this->user, $room, suspension\Suspension::TYPE_BAN)) {
$action = new suspension\SuspensionAction(array($suspension), 'delete');
$action->executeAction();
}
else {
throw new \wcf\system\exception\UserInputException('text', WCF::getLanguage()->get('wcf.chat.suspension.notExists'));
}
}
}

View File

@ -1,32 +0,0 @@
<?php
namespace chat\system\command\commands;
use \chat\data\suspension;
use \chat\util\ChatUtil;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Unmutes a user globally.
*
* @author Tim Düsterhus
* @copyright 2010-2013 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class GunmuteCommand extends UnmuteCommand {
/**
* @see \chat\system\command\commands\UnmuteCommand::executeAction()
*/
public function executeAction() {
$room = new \chat\data\room\Room(null, array('roomID' => null));
if ($suspension = suspension\Suspension::getSuspensionByUserRoomAndType($this->user, $room, suspension\Suspension::TYPE_MUTE)) {
$action = new suspension\SuspensionAction(array($suspension), 'delete');
$action->executeAction();
}
else {
throw new \wcf\system\exception\UserInputException('text', WCF::getLanguage()->get('wcf.chat.suspension.notExists'));
}
}
}

View File

@ -45,11 +45,11 @@ public function __construct(\chat\system\command\CommandHandler $commandHandler)
}
// Suspensions
// TODO: Permissions
$suspensions = \chat\data\suspension\Suspension::getSuspensionsForUser($this->user);
foreach ($suspensions as $roomSuspensions) {
foreach ($roomSuspensions as $typeSuspension) {
if (!$typeSuspension->isValid()) continue;
if (!$typeSuspension->isVisible()) continue;
$dateTime = DateUtil::getDateTimeByTimestamp($typeSuspension->expires);
$name = WCF::getLanguage()->getDynamicVariable('chat.general.information.suspension', array(

View File

@ -1,9 +1,5 @@
<?php
namespace chat\system\command\commands;
use \chat\data\suspension;
use \chat\util\ChatUtil;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Mutes a user.
@ -14,86 +10,7 @@
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class MuteCommand extends \chat\system\command\AbstractRestrictedCommand {
public $user = null;
public $expires = 0;
public $suspensionAction = null;
public $link = '';
public $room = null;
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
try {
list($username, $modifier) = explode(',', $commandHandler->getParameters(), 2);
$modifier = ChatUtil::timeModifier(\wcf\util\StringUtil::trim($modifier));
$expires = strtotime($modifier, TIME_NOW);
$this->expires = min(max(-0x80000000, $expires), 0x7FFFFFFF);
}
catch (\wcf\system\exception\SystemException $e) {
throw new \InvalidArgumentException();
}
$this->user = User::getUserByUsername($username);
if (!$this->user->userID) throw new \chat\system\command\UserNotFoundException($username);
$profile = \wcf\system\request\LinkHandler::getInstance()->getLink('User', array(
'object' => $this->user
));
$this->link = "[url='".$profile."']".$this->user->username.'[/url]';
$this->executeAction();
$this->didInit();
}
public function executeAction() {
if ($suspension = suspension\Suspension::getSuspensionByUserRoomAndType($this->user, $this->room, suspension\Suspension::TYPE_MUTE)) {
if ($suspension->expires > $this->expires) {
throw new \wcf\system\exception\UserInputException('text', WCF::getLanguage()->get('wcf.chat.suspension.exists'));
}
$action = new suspension\SuspensionAction(array($suspension), 'delete');
$action->executeAction();
}
$this->suspensionAction = new suspension\SuspensionAction(array(), 'create', array(
'data' => array(
'userID' => $this->user->userID,
'roomID' => WCF::getUser()->chatRoomID,
'type' => suspension\Suspension::TYPE_MUTE,
'expires' => $this->expires
)
));
$this->suspensionAction->executeAction();
}
/**
* @see \chat\system\command\IRestrictedChatCommand::checkPermission()
*/
public function checkPermission() {
parent::checkPermission();
$this->room = $this->commandHandler->getRoom();
$ph = new \chat\system\permission\PermissionHandler();
if (!$ph->getPermission($this->room, 'mod.can'.str_replace(array('chat\system\command\commands\\', 'Command'), '', get_class($this)))) throw new \wcf\system\exception\PermissionDeniedException();
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_MODERATE;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return serialize(array(
'link' => $this->link,
'expires' => $this->expires,
'type' => str_replace(array('chat\system\command\commands\\', 'command'), '', strtolower(get_class($this)))
));
}
class MuteCommand extends \chat\system\command\AbstractSuspensionCommand {
const IS_GLOBAL = false;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_MUTE;
}

View File

@ -1,9 +1,5 @@
<?php
namespace chat\system\command\commands;
use \chat\data\suspension;
use \chat\util\ChatUtil;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Bans a user.
@ -14,17 +10,7 @@
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class UnbanCommand extends UnmuteCommand {
/**
* @see \chat\system\command\commands\UnmuteCommand::executeAction()
*/
public function executeAction() {
if ($suspension = suspension\Suspension::getSuspensionByUserRoomAndType($this->user, $this->room, suspension\Suspension::TYPE_BAN)) {
$action = new suspension\SuspensionAction(array($suspension), 'delete');
$action->executeAction();
}
else {
throw new \wcf\system\exception\UserInputException('text', WCF::getLanguage()->get('wcf.chat.suspension.notExists'));
}
}
class UnbanCommand extends \chat\system\command\AbstractUnsuspensionCommand {
const IS_GLOBAL = false;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_BAN;
}

View File

@ -0,0 +1,16 @@
<?php
namespace chat\system\command\commands;
/**
* Unbans a user globally.
*
* @author Tim Düsterhus
* @copyright 2010-2013 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class UngbanCommand extends \chat\system\command\AbstractUnsuspensionCommand {
const IS_GLOBAL = true;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_BAN;
}

View File

@ -0,0 +1,16 @@
<?php
namespace chat\system\command\commands;
/**
* Unmutes a user globally.
*
* @author Tim Düsterhus
* @copyright 2010-2013 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class UngmuteCommand extends \chat\system\command\AbstractUnsuspensionCommand {
const IS_GLOBAL = true;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_MUTE;
}

View File

@ -1,8 +1,5 @@
<?php
namespace chat\system\command\commands;
use \chat\data\suspension;
use \wcf\data\user\User;
use \wcf\system\WCF;
/**
* Unmutes a user.
@ -13,67 +10,7 @@
* @package be.bastelstu.chat
* @subpackage system.chat.command.commands
*/
class UnmuteCommand extends \chat\system\command\AbstractRestrictedCommand {
public $user = null;
public $suspensionAction = null;
public $link = '';
public $room = null;
public function __construct(\chat\system\command\CommandHandler $commandHandler) {
parent::__construct($commandHandler);
$username = rtrim($commandHandler->getParameters(), ',');
$this->user = User::getUserByUsername($username);
if (!$this->user->userID) throw new \chat\system\command\UserNotFoundException($username);
$profile = \wcf\system\request\LinkHandler::getInstance()->getLink('User', array(
'object' => $this->user
));
$this->link = "[url='".$profile."']".$this->user->username.'[/url]';
$this->executeAction();
$this->didInit();
}
/**
* Removes the suspension.
*/
public function executeAction() {
if ($suspension = suspension\Suspension::getSuspensionByUserRoomAndType($this->user, $this->room, suspension\Suspension::TYPE_MUTE)) {
$action = new suspension\SuspensionAction(array($suspension), 'delete');
$action->executeAction();
}
else {
throw new \wcf\system\exception\UserInputException('text', WCF::getLanguage()->get('wcf.chat.suspension.notExists'));
}
}
/**
* @see \chat\system\command\IRestrictedChatCommand::checkPermission()
*/
public function checkPermission() {
parent::checkPermission();
$this->room = $this->commandHandler->getRoom();
$ph = new \chat\system\permission\PermissionHandler();
if (!$ph->getPermission($this->room, 'mod.can'.ucfirst(str_replace(array('chat\system\command\commands\\Un', 'Command'), '', get_class($this))))) throw new \wcf\system\exception\PermissionDeniedException();
}
/**
* @see \chat\system\command\ICommand::getType()
*/
public function getType() {
return \chat\data\message\Message::TYPE_MODERATE;
}
/**
* @see \chat\system\command\ICommand::getMessage()
*/
public function getMessage() {
return serialize(array(
'link' => $this->link,
'type' => str_replace(array('chat\system\command\commands\\', 'command'), '', strtolower(get_class($this)))
));
}
class UnmuteCommand extends \chat\system\command\AbstractUnsuspensionCommand {
const IS_GLOBAL = false;
const SUSPENSION_TYPE = \chat\data\suspension\Suspension::TYPE_MUTE;
}

View File

@ -20,8 +20,6 @@ public function execute(\wcf\data\cronjob\Cronjob $cronjob) {
$messageAction->executeAction();
$roomAction = new data\room\RoomAction(array(), 'prune');
$roomAction->executeAction();
$suspensionAction = new data\suspension\SuspensionAction(array(), 'prune');
$suspensionAction->executeAction();
// kill dead users
$roomAction = new data\room\RoomAction(array(), 'removeDeadUsers');

View File

@ -16,6 +16,18 @@
}
}
.__bubbleArrow {
border-color: transparent @wcfContainerBorderColor;
left: -6px;
top: 5px;
border-width: 6px 6px 6px 0;
border-style: solid;
content: "";
display: block;
position: absolute;
width: 0;
}
#tplChat {
#main > div {
overflow: hidden;
@ -28,17 +40,120 @@
.transition(padding-top, .2s);
.transition(padding-bottom, .2s);
&.empty {
&.empty, &.hidden {
height: 0px;
overflow: hidden;
border: 0px;
padding: 0px;
margin: 0px;
~ #timsChatMessageContainer {
margin-top: 0;
}
}
.jsTopicCloser {
cursor: pointer;
float: right;
}
}
#privateChannelsMenu {
.transition(opacity, .2s);
.marginTop;
z-index: -1;
position: absolute;
opacity: 0;
&.shown {
opacity: 1;
z-index: 130;
~ .timsChatMessageContainer {
margin-left: 35px;
border-top-left-radius: 0;
}
}
> ul {
text-align: right;
> li:first-child {
> .userAvatar.framed {
img, > canvas, > .icon {
border-radius: @wcfContainerBorderRadius 0 0 0;
}
}
}
> li:last-child {
> .userAvatar.framed {
img, > canvas, > .icon {
border-radius: 0 0 0 @wcfContainerBorderRadius;
}
}
}
> li {
margin-bottom: -1px;
background-color: @wcfContainerBackgroundColor;
> .userAvatar, .userAvatar > .icon {
cursor: pointer;
}
> .userAvatar {
&.large {
display: none;
}
&.small {
display: block;
}
&.framed {
> .icon {
background-color: @wcfContentBackgroundColor;
border: 1px solid @wcfContainerBorderColor;
padding: 1px;
}
}
}
&.active {
> .userAvatar {
&.large {
display: block;
}
&.small {
display: none;
}
&.framed {
> img, > canvas, > .icon {
border-right-color: @wcfContentBackgroundColor;
border-radius: @wcfContainerBorderRadius 0 0 @wcfContainerBorderRadius;
}
}
}
}
&.notify {
> .userAvatar {
> * {
// TODO
opacity: .4;
}
}
}
}
}
}
.timsChatMessageContainer {
height: 200px;
height: 320px;
overflow-y: scroll;
overflow-x: hidden;
display: none;
@ -55,9 +170,9 @@
color: @wcfSelectedColor;
}
> {
> .innerMessageContainer {
.markContainer {
display: table-cell;
display: block;
}
}
}
@ -77,47 +192,138 @@
opacity: .5;
}
&::before {
.icon;
.icon16;
padding: @wcfGapTiny;
}
&:nth-child(even) {
> .innerMessageContainer.bubble .innerMessage, .innerMessageContainer.right.bubble .innerMessage {
background-color: @wcfContainerAccentBackgroundColor;
&:after {
border-color: transparent @wcfContainerAccentBackgroundColor;
}
}
}
> {
time, .usernameContainer, .text, .markContainer {
display: table-cell;
vertical-align: top;
padding: @wcfGapTiny 0;
.messageIcon {
float: left;
padding: 8px 0 0 4px;
margin-right: -16px;
}
.markContainer {
display: none;
padding: 0;
> .innerMessageContainer {
padding: 5px 20px 5px 5px;
position: relative;
.userAvatar {
float: left;
margin-left: 16px;
}
.innerMessage {
margin-left: 46px;
padding: 2px 5px 5px;
time {
&::before {
content: "[";
float: right;
}
> .text {
img {
max-width: 480px;
max-height: 320px;
}
&::after {
content: "]";
}
}
.usernameContainer {
text-align: right;
min-width: 100px;
padding-right: @wcfGapSmall;
white-space: nowrap;
&.bubble {
.userAvatar {
margin-left: 0;
.icon {
padding: 2px;
}
}
.innerMessage {
border-width: 1px;
border-style: solid;
border-color: @wcfContainerBorderColor;
border-radius: @wcfContainerBorderRadius;
background-color: @wcfContainerBackgroundColor;
position: relative;
.username {
font-weight: bold;
}
.text {
width: 100%;
> ul.text {
li {
.clearfix;
border-style: solid;
border-width: 0 0 1px 0;
border-color: @wcfContainerBorderColor;
padding: 3px 0 4px;
&:last-child {
border-style: none;
padding: 3px 0 0 0;
}
}
}
&:before {
.__bubbleArrow;
}
&:after {
.__bubbleArrow;
border-color: transparent @wcfContainerBackgroundColor;
left: -5px;
top: 6px;
border-width: 5px 5px 5px 0;
}
}
&.right {
.userAvatar {
float: right;
}
.innerMessage {
margin-right: 46px;
margin-left: 0px;
&:before {
.__bubbleArrow;
left: auto;
right: -6px;
border-width: 6px 0 6px 6px;
}
&:after {
.__bubbleArrow;
border-color: transparent @wcfContainerBackgroundColor;
left: auto;
right: -5px;
top: 6px;
border-width: 5px 0 5px 5px;
}
time {
float: left;
margin-right: @wcfGapTiny;
}
.username {
float: right;
}
}
}
}
> .markContainer {
display: none;
position: absolute;
right: 0px;
top: 6px;
}
}
}

View File

@ -46,10 +46,14 @@ CREATE TABLE chat1_suspension (
suspensionID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
userID INT(10) NOT NULL,
roomID INT(10) DEFAULT NULL,
type TINYINT(3) NOT NULL,
type VARCHAR(15) NOT NULL,
expires INT(10) NOT NULL,
time INT(10) NOT NULL,
issuer INT(10) DEFAULT NULL,
reason VARCHAR(255) NOT NULL DEFAULT '',
revoker INT(10) DEFAULT NULL,
UNIQUE KEY suspension (userID, roomID, type),
KEY suspension (userID, roomID, type),
KEY (roomID),
KEY (type),
KEY (expires)
@ -70,10 +74,12 @@ ALTER TABLE chat1_room ADD FOREIGN KEY (owner) REFERENCES wcf1_user (userID) ON
ALTER TABLE chat1_suspension ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
ALTER TABLE chat1_suspension ADD FOREIGN KEY (roomID) REFERENCES chat1_room (roomID) ON DELETE CASCADE;
ALTER TABLE chat1_suspension ADD FOREIGN KEY (issuer) REFERENCES wcf1_user (userID) ON DELETE SET NULL;
ALTER TABLE chat1_suspension ADD FOREIGN KEY (revoker) REFERENCES wcf1_user (userID) ON DELETE SET NULL;
ALTER TABLE wcf1_user ADD FOREIGN KEY (chatRoomID) REFERENCES chat1_room (roomID) ON DELETE SET NULL;
INSERT INTO chat1_room (title, topic, showOrder) VALUES ('chat.room.title1', 'chat.room.topic1', 1);
INSERT INTO chat1_room (title, topic, showOrder) VALUES ('Testroom 2', 'Topic of Testroom 2', 2);
INSERT INTO chat1_room (title, topic, showOrder) VALUES ('Testroom with a very long', 'The topic of this room is rather loing as well!', 3);
INSERT INTO chat1_room (title, topic, showOrder) VALUES ('Testroom with a very long name', 'The topic of this room is rather loing as well!', 3);
INSERT INTO chat1_room (title, topic, showOrder) VALUES ('Room w/o topic', '', 4);

View File

@ -11,10 +11,32 @@
<item name="chat.acp.room.delete.sure"><![CDATA[Wollen Sie den Raum „{$chatRoom}“ wirklich löschen?]]></item>
</category>
<category name="chat.acp.suspension">
<item name="chat.acp.suspension.list"><![CDATA[Sanktionen]]></item>
<item name="chat.acp.suspension.type"><![CDATA[Art der Sanktion]]></item>
<item name="chat.acp.suspension.issuer"><![CDATA[Aussteller]]></item>
<item name="chat.acp.suspension.displayRevoked"><![CDATA[Abgelaufene Sanktionen anzeigen]]></item>
<item name="chat.acp.suspension.reason"><![CDATA[Grund]]></item>
<item name="chat.acp.suspension.revoked"><![CDATA[Zurückziehen]]></item>
<item name="chat.acp.suspension.revokedBy"><![CDATA[Zurückgezogen von {$suspension->revokerUsername}]]></item>
</category>
<category name="chat.acp.menu">
<item name="chat.acp.menu.link"><![CDATA[Chat]]></item>
<item name="chat.acp.menu.link.room.list"><![CDATA[Chaträume auflisten]]></item>
<item name="chat.acp.menu.link.room.add"><![CDATA[Chatraum hinzufügen]]></item>
<item name="chat.acp.menu.link.suspension.list"><![CDATA[Sanktionen auflisten]]></item>
</category>
<category name="wcf.acl.option">
<item name="wcf.acl.option.category.be.bastelstu.chat.room.user"><![CDATA[Benutzerrechte]]></item>
<item name="wcf.acl.option.category.be.bastelstu.chat.room.mod"><![CDATA[Moderative Rechte]]></item>
<item name="wcf.acl.option.be.bastelstu.chat.room.user.canEnter"><![CDATA[Kann Raum betreten]]></item>
<item name="wcf.acl.option.be.bastelstu.chat.room.user.canWrite"><![CDATA[Kann schreiben]]></item>
<item name="wcf.acl.option.be.bastelstu.chat.room.mod.canAlwaysEnter"><![CDATA[Kann Raum immer betreten]]></item>
<item name="wcf.acl.option.be.bastelstu.chat.room.mod.canAlwaysWrite"><![CDATA[Kann immer schreiben]]></item>
<item name="wcf.acl.option.be.bastelstu.chat.room.mod.canMute"><![CDATA[Kann knebeln]]></item>
<item name="wcf.acl.option.be.bastelstu.chat.room.mod.canBan"><![CDATA[Kann bannen]]></item>
</category>
<category name="wcf.acp.group">
@ -24,12 +46,24 @@
<item name="wcf.acp.group.option.user.chat.canTempRoom"><![CDATA[Kann temporäre Räume erstellen]]></item>
<item name="wcf.acp.group.option.category.mod.chat"><![CDATA[Chat]]></item>
<item name="wcf.acp.group.option.mod.chat.canAlwaysEnter"><![CDATA[Kann jeden Raum betreten]]></item>
<item name="wcf.acp.group.option.mod.chat.canAlwaysEnter"><![CDATA[Kann Räume immer betreten]]></item>
<item name="wcf.acp.group.option.mod.chat.canAlwaysEnter.description"><![CDATA[Setzt Banns außer Kraft.]]></item>
<item name="wcf.acp.group.option.mod.chat.canAlwaysWrite"><![CDATA[Kann in jedem Raum schreiben]]></item>
<item name="wcf.acp.group.option.mod.chat.canAlwaysWrite"><![CDATA[Kann immer schreiben]]></item>
<item name="wcf.acp.group.option.mod.chat.canAlwaysWrite.description"><![CDATA[Setzt Knebel außer Kraft.]]></item>
<item name="wcf.acp.group.option.mod.chat.canRestore"><![CDATA[Kann User zurücksetzen]]></item>
<item name="wcf.acp.group.option.mod.chat.canRestore.description"><![CDATA[Setzt Nutzerdaten (Farbe) zurück.]]></item>
<item name="wcf.acp.group.option.mod.chat.canBan"><![CDATA[Kann bannen]]></item>
<item name="wcf.acp.group.option.mod.chat.canBan.description"><![CDATA[Impliziert „Kann Chat immer betreten“]]></item>
<item name="wcf.acp.group.option.mod.chat.canMute"><![CDATA[Kann knebeln]]></item>
<item name="wcf.acp.group.option.mod.chat.canMute.description"><![CDATA[Impliziert „Kann immer schreiben“]]></item>
<item name="wcf.acp.group.option.mod.chat.canGban"><![CDATA[Kann global bannen]]></item>
<item name="wcf.acp.group.option.mod.chat.canGban.description"><![CDATA[Impliziert „Kann bannen“]]></item>
<item name="wcf.acp.group.option.mod.chat.canGmute"><![CDATA[Kann global knebeln]]></item>
<item name="wcf.acp.group.option.mod.chat.canGmute.description"><![CDATA[Impliziert „Kann knebeln“]]></item>
<item name="wcf.acp.group.option.category.admin.chat"><![CDATA[Chat]]></item>
<item name="wcf.acp.group.option.admin.chat.canManageSuspensions"><![CDATA[Kann Sanktionen verwalten]]></item>
<item name="wcf.acp.group.option.admin.chat.canManageSuspensions.description"><![CDATA[Impliziert „Kann bannen“, „Kann knebeln“, „Kann global bannen“, „Kann global knebeln“ und gegenbenenfalls weitere Rechte.]]></item>
</category>
<category name="wcf.acp.option">
@ -59,7 +93,7 @@
</category>
<category name="chat.error">
<item name="chat.error.notFound"><![CDATA[Der Befehl wurde nicht gefunden.]]></item>
<item name="chat.error.notFound"><![CDATA[Der Befehl „{$exception->getCommand()}“ wurde nicht gefunden.]]></item>
<item name="chat.error.userNotFound"><![CDATA[Der Benutzer „{$exception->getUsername()}“ wurde nicht gefunden.]]></item>
<item name="chat.error.permissionDenied"><![CDATA[Sie dürfen diesen Befehl nicht verwenden.]]></item>
<item name="chat.error.duplicateTab"><![CDATA[Der Chat wurde in einem weiteren Tab geöffnet.]]></item>
@ -68,6 +102,9 @@
</category>
<category name="chat.general">
<item name="chat.general.expires"><![CDATA[Ablaufzeitpunkt]]></item>
<item name="chat.general.time"><![CDATA[Datum]]></item>
<item name="chat.general.title"><![CDATA[{lang}chat.header.menu.chat{/lang}]]></item>
<item name="chat.general.protocol"><![CDATA[Protokoll]]></item>
@ -100,6 +137,10 @@
<item name="chat.general.information"><![CDATA[Information]]></item>
<item name="chat.general.information.chatUpdate"><![CDATA[Der Chat wurde aktualisiert. Bitte laden Sie die Seite neu, da es sonst zu Fehlern kommen kann.]]></item>
<item name="chat.general.information.suspension"><![CDATA[{lang}chat.suspension.{$suspension->type}{/lang} ({if $room}{$room}{else}{lang}chat.room.global{/lang}{/if})]]></item>
<item name="chat.general.privateChannelTopic"><![CDATA[{literal}Sie befinden sich in einem privaten Kanal mit „{$username}“, um diesen zu schließen, klicken Sie einfach auf das Kreuz rechts.{/literal}]]></item>
<item name="chat.general.closePrivateChannel"><![CDATA[Privaten Kanal schließen]]></item>
<item name="chat.general.closeTopic"><![CDATA[Thema ausblenden]]></item>
</category>
<category name="chat.header">
@ -117,21 +158,21 @@
<item name="chat.message.4"><![CDATA[ist jetzt wieder da.]]></item>
<!-- 5 = TYPE_MODERATE -->
<item name="chat.message.5.restore"><![CDATA[hat {@$link} zurückgesetzt.]]></item>
<item name="chat.message.5.mute"><![CDATA[hat {@$link} bis {@$expires|plainTime} geknebelt.]]></item>
<item name="chat.message.5.ban"><![CDATA[hat {@$link} bis {@$expires|plainTime} gebannt.]]></item>
<item name="chat.message.5.gmute"><![CDATA[hat {@$link} bis {@$expires|plainTime} global geknebelt.]]></item>
<item name="chat.message.5.gban"><![CDATA[hat {@$link} bis {@$expires|plainTime} global gebannt.]]></item>
<item name="chat.message.5.mute"><![CDATA[hat {@$link} bis {@$expires|plainTime} geknebelt{if !$reason|empty}: {$reason}{else}.{/if}]]></item>
<item name="chat.message.5.ban"><![CDATA[hat {@$link} bis {@$expires|plainTime} gebannt{if !$reason|empty}: {$reason}{else}.{/if}]]></item>
<item name="chat.message.5.gmute"><![CDATA[hat {@$link} bis {@$expires|plainTime} global geknebelt{if !$reason|empty}: {$reason}{else}.{/if}]]></item>
<item name="chat.message.5.gban"><![CDATA[hat {@$link} bis {@$expires|plainTime} global gebannt{if !$reason|empty}: {$reason}{else}.{/if}]]></item>
<item name="chat.message.5.unmute"><![CDATA[hat {@$link} entknebelt.]]></item>
<item name="chat.message.5.unban"><![CDATA[hat {@$link} entbannt.]]></item>
<item name="chat.message.5.gunmute"><![CDATA[hat {@$link} global entknebelt.]]></item>
<item name="chat.message.5.gunban"><![CDATA[hat {@$link} global entbannt.]]></item>
<item name="chat.message.5.ungmute"><![CDATA[hat {@$link} global entknebelt.]]></item>
<item name="chat.message.5.ungban"><![CDATA[hat {@$link} global entbannt.]]></item>
<item name="chat.message.color.success"><![CDATA[Die Farbe wurde erfolgreich geändert.]]></item>
</category>
<category name="chat.suspension">
<item name="chat.suspension.1"><![CDATA[Knebel]]></item>
<item name="chat.suspension.2"><![CDATA[Bann]]></item>
<item name="chat.suspension.mute"><![CDATA[Knebel]]></item>
<item name="chat.suspension.ban"><![CDATA[Bann]]></item>
</category>
<!-- I18N Values -->

View File

@ -49,7 +49,9 @@
<optiontype>textarea</optiontype>
<defaultvalue>afk:away
col:color
msg:whisper</defaultvalue>
msg:whisper
gunban:ungban
gunmute:ungmute</defaultvalue>
</option>
<!-- general chat options end -->

View File

@ -5,7 +5,7 @@
<packagedescription><![CDATA[Chat for WoltLab Community Framework™.]]></packagedescription>
<packagedescription language="de"><![CDATA[Chat für WoltLab Community Framework™.]]></packagedescription>
<isapplication>1</isapplication>
<version>3.0.0 Alpha 58</version><!-- Codename: Codenames are overrated -->
<version>3.0.0 Alpha 80</version><!-- Codename: Codenames are overrated -->
<date>2011-11-26</date>
<license><![CDATA[Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>]]></license>
</packageinformation>

View File

@ -5,7 +5,7 @@
{include file='headInclude' sandbox=false}
{include file='javascriptInclude' application='chat'}
<script type="text/javascript">
<script>
//<![CDATA[
(function ($, window) {
$(function(){
@ -15,27 +15,33 @@
'chat.general.ban': '{lang}chat.general.ban{/lang}',
'chat.general.profile': '{lang}chat.general.profile{/lang}',
'chat.general.notify.title': '{lang}chat.general.notify.title{/lang}',
'chat.general.privateChannelTopic': '{lang}chat.general.privateChannelTopic{/lang}',
'chat.general.closePrivateChannel': '{lang}chat.general.closePrivateChannel{/lang}',
'chat.general.closeTopic': '{lang}chat.general.closeTopic{/lang}',
'chat.error.onMessageLoad': '{lang}chat.error.onMessageLoad{/lang}',
'chat.error.duplicateTab': '{lang}chat.error.duplicateTab{/lang}',
'chat.error.join': '{lang}chat.error.join{/lang}',
'chat.error.reload': '{lang}chat.error.reload{/lang}'
});
{event name='beforeInit'}
// Boot the chat
{if MODULE_SMILEY}WCF.TabMenu.init();{/if}
new WCF.Message.Smilies();
{capture assign='messageTemplate'}{include application='chat' file='message'}{/capture}
{capture assign='userTemplate'}{include application='chat' file='userListUser'}{/capture}
be.bastelstu.Chat.init(
{$roomID},
{
var config = {
reloadTime: {@CHAT_RELOADTIME},
messageURL: '{link application="chat" controller="NewMessages"}{/link}',
installedCommands: [ {implode from=$commands item='command'}'{$command|encodeJS}'{/implode} ]
},
installedCommands: [ {implode from=$commands item='command'}'{$command|encodeJS}'{/implode} ],
messageTypes: { {implode from=$messageTypes key='name' item='messageType'}'{$name|substr:5|encodeJS}': '{$messageType|encodeJS}'{/implode} }
};
{event name='beforeInit'}
be.bastelstu.Chat.init(
{$roomID},
config,
new WCF.Template('{literal}{if $newMessageCount}({#$newMessageCount}) {/if}{$title} - {/literal}{"chat.general.title"|language|encodeJS} - {PAGE_TITLE|language|encodeJS}'),
new WCF.Template('{@$messageTemplate|encodeJS}'),
new WCF.Template('{@$userTemplate|encodeJS}')
@ -54,36 +60,31 @@
})(jQuery, this);
//]]>
</script>
<style type="text/css">
/*<![CDATA[*/
.timsChatMessage::before {
content: "";
}
{assign var='type' value='\chat\data\message\Message::TYPE_'}
.timsChatMessage{$type|concat:'JOIN'|constant}::before {
content: "\f090";
}
.timsChatMessage{$type|concat:'LEAVE'|constant}::before {
content: "\f08b";
}
.timsChatMessage{$type|concat:'INFORMATION'|constant}::before {
content: "\f05a";
}
/*]]>*/
</style>
</head>
<body id="tpl{$templateName|ucfirst}">
{capture assign='sidebar'}{include application='chat' file='sidebar'}{/capture}
{include file='header' sandbox=false sidebarOrientation='right'}
<div id="timsChatTopic" class="container{if $room->topic|language === ''} empty{/if}">{$room->topic|language}</div>
<div id="timsChatTopic" class="container{if $room->topic|language === ''} empty{/if}">
<span class="icon icon16 icon-remove jsTopicCloser jsTooltip" title="{lang}chat.general.closeTopic{/lang}"></span>
<span class="topic">{$room->topic|language}</span>
</div>
<div id="timsChatMessageContainer" class="timsChatMessageContainer marginTop container active">
<div id="privateChannelsMenu">
<ul>
<li id="privateChannel0" class="privateChannel active" data-private-channel-id="0">
<span class="userAvatar framed small">
<span class="icon icon16 icon-comment-alt jsTooltip" title="{lang}chat.general.room{/lang}"></span>
</span>
<span class="userAvatar framed large">
<span class="icon icon32 icon-comment-alt jsTooltip" title="{lang}chat.general.room{/lang}"></span>
</span>
</li>
</ul>
</div>
<div id="timsChatMessageContainer0" class="timsChatMessageContainer marginTop container active" data-user-id="0">
<p class="error noJsOnly" style="display: none;">{lang}chat.general.noJs{/lang}</p>
<ul>
</ul>
@ -93,7 +94,7 @@
<fieldset>
<dl class="wide" id="timsChatInputContainer">
<dd>
<input id="timsChatInput" accesskey="w" type="text" class="inputText long" name="text" autocomplete="off" maxlength="{@CHAT_MAX_LENGTH}" disabled="disabled" required="required" placeholder="{lang}chat.general.submit.default{/lang}" />
<input id="timsChatInput" accesskey="w" type="text" class="inputText long" name="text" autocomplete="off" maxlength="{@CHAT_MAX_LENGTH}" disabled="disabled" placeholder="{lang}chat.general.submit.default{/lang}" />
<small class="innerError" style="display: none;">Lorem ipsum dolor sit amet.</small>
</dd>
</dl>
@ -105,7 +106,7 @@
{include file='messageFormSmilies' wysiwygSelector=''}
{/if}
<nav id="timsChatOptions" class="marginTop buttonGroupNavigation">
<nav id="timsChatOptions" class="marginTop jsMobileNavigation buttonGroupNavigation">
<span class="invisible">{lang}chat.general.controls{/lang}</span>
<ul class="smallButtons buttonGroup">
<li><a id="timsChatAutoscroll" accesskey="d" class="button active timsChatToggle jsTooltip" title="{lang}chat.general.scroll{/lang}" data-status="1"><span class="icon icon16 icon-arrow-down"></span><span class="invisible">{lang}chat.general.scroll{/lang}</span></a></li>{*

View File

@ -35,7 +35,7 @@
{@$roomList}
</ul>
</div>
<script type="text/javascript">
<script>
//<![CDATA[
(function($, window, undefined) {
proxy = new WCF.Action.Proxy({

View File

@ -1,3 +1,3 @@
<script type="text/javascript" src="{$__wcf->getPath('chat')}js/be.bastelstu.Chat{if !ENABLE_DEBUG_MODE}.min{/if}.js?version={PACKAGE_VERSION|rawurlencode}"></script>
<!--script type="text/javascript" src="{$__wcf->getPath('chat')}js/be.bastelstu.Chat.Log{if !ENABLE_DEBUG_MODE}.min{/if}.js?version={PACKAGE_VERSION|rawurlencode}"></script-->
<script src="{$__wcf->getPath('chat')}js/be.bastelstu.Chat{if !ENABLE_DEBUG_MODE}.min{/if}.js?version={PACKAGE_VERSION|rawurlencode}"></script>
<!--script src="{$__wcf->getPath('chat')}js/be.bastelstu.Chat.Log{if !ENABLE_DEBUG_MODE}.min{/if}.js?version={PACKAGE_VERSION|rawurlencode}"></script-->
{event name='javascript'}

View File

@ -4,7 +4,7 @@
</ul>
</div>
</div>
<script type="text/javascript">
<script>
//<![CDATA[
var log = new be.bastelstu.Chat.Log(chat);
log.handleMessages([

View File

@ -1,24 +1,54 @@
{literal}
<time>{@$formattedTime}</time>
<span class="usernameContainer">
<span class="username">{*
*}{if $type != 7}{*
*}{@$formattedUsername}{*
*}{else}
{if $receiver == WCF.User.userID}
{@$formattedUsername}
<div class="messageIcon">
{if $message.type == $messageTypes.LEAVE || $message.type == $messageTypes.JOIN}
<span class="icon icon16 icon-{if $message.type == $messageTypes.LEAVE}signout{else}signin{/if}"></span>
{/if}
</div>
<div class="innerMessageContainer{if $message.type == $messageTypes.NORMAL || $message.type == $messageTypes.WHISPER || $message.type == $messageTypes.INFORMATION} bubble{/if}{if $message.type == $messageTypes.WHISPER && $message.sender != $__wcf.User.userID} right{/if}">
<div class="userAvatar framed">
{if $message.type != $messageTypes.INFORMATION}
{if $message.type == $messageTypes.NORMAL || $message.type == $messageTypes.WHISPER}
{@$message.avatar[32]}
{else}
{@$message.avatar[16]}
{/if}
{else}
<span class="icon icon32 icon-info-sign"></span>
{/if}
</div>
<div class="innerMessage">
<span class="username">
{if ($message.type == $messageTypes.WHISPER && $message.sender == WCF.User.userID) || $message.type != $messageTypes.WHISPER}
{@$message.formattedUsername}
{else}
{$message.additionalData.receiverUsername}
{/if}
{if $message.type == $messageTypes.WHISPER}
<span class="icon icon16 icon-double-angle-{if $message.sender == WCF.User.userID}right{else}left{/if} jsTooltip" title="{/literal}{lang}chat.ui.whispers{/lang}{literal}" onclick="be.bastelstu.Chat.insertText('/whisper {if $message.receiver == WCF.User.userID}{$message.username.replace("\\", "\\\\").replace("'", "\\'")}{else}{$message.additionalData.receiverUsername.replace("\\", "\\\\").replace("'", "\\'")}{/if}, ', { append: false });"></span>
{if ($message.type == $messageTypes.WHISPER && $message.sender == WCF.User.userID) || $message.type != $messageTypes.WHISPER}
{$message.additionalData.receiverUsername}
{else}
{@$message.formattedUsername}
{/if}
<span class="icon icon16 icon-double-angle-right jsTooltip" title="{/literal}{lang}chat.ui.whispers{/lang}{literal}" onclick="be.bastelstu.Chat.insertText('/whisper {if $receiver == WCF.User.userID}{$username.replace("\\", "\\\\").replace("'", "\\'")}{else}{$additionalData.receiverUsername.replace("\\", "\\\\").replace("'", "\\'")}{/if}, ', { append: false });"></span>
{if $receiver != WCF.User.userID}
{$additionalData.receiverUsername}{/if}{*
*}{/if}{*
*}</span>{*
*}{if $receiver != WCF.User.userID}{*
*}<span class="separator">{$separator}</span>
{/if}
</span>
<span class="text">{@$formattedMessage}</span>
<time>{@$message.formattedTime}</time>
{if $message.type == $messageTypes.NORMAL || $message.type == $messageTypes.WHISPER}
<ul class="text">
<li>
{if $message.isFollowUp} <time>{@$message.formattedTime}</time>{/if}
{@$message.formattedMessage}
</li>
</ul>
{else}
<span class="text">{@$message.formattedMessage}</span>
{/if}
</div>
<span class="markContainer">
<input type="checkbox" value="{@$messageID}" />
<input type="checkbox" value="{@$message.messageID}" />
</span>
</div>
{/literal}

View File

@ -104,6 +104,12 @@
<defaultvalue>0</defaultvalue>
<admindefaultvalue>1</admindefaultvalue>
</option>
<option name="admin.chat.canManageSuspensions">
<categoryname>admin.chat</categoryname>
<optiontype>boolean</optiontype>
<defaultvalue>0</defaultvalue>
<admindefaultvalue>1</admindefaultvalue>
</option>
</options>
</import>
</data>