1
0
mirror of https://github.com/wbbaddons/Tims-Chat.git synced 2025-01-22 02:00:40 +00:00

Merge branch 'master' into commands

Conflicts:
	file/lib/form/ChatForm.class.php
	template/chatMessage.tpl
This commit is contained in:
Tim Düsterhus 2012-03-04 19:57:23 +01:00
commit 81bdbccbe2
42 changed files with 1292 additions and 277 deletions

View File

@ -1,8 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/aclOption.xsd"> <data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/aclOption.xsd">
<import> <import>
<option name="canEnter"> <categories>
<objecttype>timwolla.wcf.chat.room</objecttype> <category name="user">
</option> <objecttype>timwolla.wcf.chat.room</objecttype>
</category>
<category name="mod">
<objecttype>timwolla.wcf.chat.room</objecttype>
</category>
</categories>
<options>
<option name="user.canEnter">
<objecttype>timwolla.wcf.chat.room</objecttype>
<categoryname>user</categoryname>
</option>
<option name="user.canWrite">
<objecttype>timwolla.wcf.chat.room</objecttype>
<categoryname>user</categoryname>
</option>
<option name="mod.canAlwaysEnter">
<objecttype>timwolla.wcf.chat.room</objecttype>
<categoryname>mod</categoryname>
</option>
<option name="mod.canAlwaysWrite">
<objecttype>timwolla.wcf.chat.room</objecttype>
<categoryname>mod</categoryname>
</option>
</options>
</import> </import>
</data> </data>

22
acpMenu.xml Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/acpMenu.xsd">
<import>
<acpmenuitem name="wcf.acp.menu.link.chat">
<parent>wcf.acp.menu.link.content</parent>
</acpmenuitem>
<acpmenuitem name="wcf.acp.menu.link.chat.room.list">
<link>index.php/ChatRoomList/</link>
<parent>wcf.acp.menu.link.chat</parent>
<permissions>admin.content.chat.canEditRoom,admin.content.chat.canDeleteRoom</permissions>
<showorder>1</showorder>
</acpmenuitem>
<acpmenuitem name="wcf.acp.menu.link.chat.room.add">
<link>index.php/ChatRoomAdd/</link>
<parent>wcf.acp.menu.link.chat</parent>
<permissions>admin.content.chat.canAddRoom</permissions>
<showorder>2</showorder>
</acpmenuitem>
</import>
</data>

101
acptemplate/chatRoomAdd.tpl Normal file
View File

@ -0,0 +1,101 @@
{include file='header'}
<!-- ToDo: DEBUG ONLY -->
<link rel="stylesheet" type="text/css" href="{@$__wcf->getPath('wcf')}style/acl.css" />
<!-- /DEBUG ONLY -->
<script type="text/javascript" src="{@$__wcf->getPath('wcf')}js/WCF.ACL.js"></script>
<script type="text/javascript">
//<![CDATA[
$(function() {
WCF.Icon.addObject({
'wcf.icon.delete': '{@$__wcf->getPath('wcf')}icon/delete1.svg',
'wcf.icon.user': '{@$__wcf->getPath('wcf')}icon/user1.svg',
'wcf.icon.users': '{@$__wcf->getPath('wcf')}icon/users1.svg'
});
new WCF.ACL.List($('#groupPermissions'), {@$objectTypeID}, ''{if $roomID|isset}, {@$roomID}{/if});
});
//]]>
</script>
<header class="wcf-mainHeading wcf-container">
<img src="{@$__wcf->getPath('wcf')}icon/{$action}1.svg" alt="" class="wcf-containerIcon" />
<hgroup class="wcf-containerContent">
<h1>{lang}wcf.acp.chat.room.{$action}{/lang}</h1>
</hgroup>
</header>
{if $errorField}
<p class="wcf-error">{lang}wcf.global.form.error{/lang}</p>
{/if}
{if $success|isset}
<p class="wcf-success">{lang}wcf.global.form.{$action}.success{/lang}</p>
{/if}
<div class="wcf-contentHeader">
<nav>
<ul class="wcf-largeButtons">
<li><a href="{link controller='ChatRoomList'}{/link}" title="{lang}wcf.acp.menu.link.chat.room.list{/lang}" class="wcf-button"><img src="{@$__wcf->getPath('wcf')}icon/chat1.svg" alt="" /> <span>{lang}wcf.acp.menu.link.chat.room.list{/lang}</span></a></li>
</ul>
</nav>
</div>
<form method="post" action="{if $action == 'add'}{link controller='ChatRoomAdd'}{/link}{else}{link controller='ChatRoomEdit'}{/link}{/if}">
<div class="wcf-border wcf-content">
<fieldset>
<legend>{lang}wcf.acp.chat.room.data{/lang}</legend>
<dl{if $errorField == 'title'} class="wcf-formError"{/if}>
<dt><label for="title">{lang}wcf.acp.chat.room.title{/lang}</label></dt>
<dd>
<input type="text" id="title" name="title" value="{$title}" autofocus="autofocus" class="long" />
{if $errorField == 'title'}
<small class="wcf-innerError">
{if $errorType == 'empty'}
{lang}wcf.global.form.error.empty{/lang}
{else}
{lang}wcf.acp.chat.room.title.error.{@$errorType}{/lang}
{/if}
</small>
{/if}
</dd>
</dl>
{include file='multipleLanguageInputJavascript' elementIdentifier='title'}
<dl{if $errorField == 'topic'} class="wcf-formError"{/if}>
<dt><label for="topic">{lang}wcf.acp.chat.room.topic{/lang}</label></dt>
<dd>
<input type="text" id="topic" name="topic" value="{$topic}" class="long" />
{if $errorField == 'topic'}
<small class="wcf-innerError">
{if $errorType == 'empty'}
{lang}wcf.global.form.error.empty{/lang}
{else}
{lang}wcf.acp.chat.room.topic.error.{@$errorType}{/lang}
{/if}
</small>
{/if}
</dd>
</dl>
{include file='multipleLanguageInputJavascript' elementIdentifier='topic'}
<dl id="groupPermissions">
<dt>{lang}wcf.acp.acl.permissions{/lang}</dt>
<dd></dd>
</dl>
</fieldset>
</div>
<div class="wcf-formSubmit">
<input type="reset" value="{lang}wcf.global.button.reset{/lang}" accesskey="r" />
<input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
{@SID_INPUT_TAG}
{if $roomID|isset}<input type="hidden" name="id" value="{@$roomID}" />{/if}
</div>
</form>
{include file='footer'}

View File

@ -0,0 +1,58 @@
{include file='header'}
<script type="text/javascript">
//<![CDATA[
$(function() {
new WCF.Action.Delete('\\wcf\\data\\chat\\room\\ChatRoomAction', $('.chatRoomRow'));
new WCF.Sortable.List('chatRoomList', '\\wcf\\data\\chat\\room\\ChatRoomAction');
});
//]]>
</script>
<header class="wcf-mainHeading wcf-container">
<img src="{@$__wcf->getPath('wcf')}icon/chat1.svg" alt="" class="wcf-containerIcon" />
<hgroup class="wcf-containerContent">
<h1>{lang}wcf.acp.chat.room.list{/lang}</h1>
</hgroup>
</header>
<div class="wcf-contentHeader">
{pages print=true assign=pagesLinks controller="ChatRoomList" link="pageNo=%d"}
{if $__wcf->session->getPermission('admin.content.chat.canAddRoom')}
<nav>
<ul class="wcf-largeButtons">
<li><a href="{link controller='ChatRoomAdd'}{/link}" title="{lang}wcf.acp.chat.room.add{/lang}" class="wcf-button"><img src="{@$__wcf->getPath('wcf')}icon/add1.svg" alt="" /> <span>{lang}wcf.acp.chat.room.add{/lang}</span></a></li>
</ul>
</nav>
{/if}
</div>
<section id="chatRoomList" class="wcf-border wcf-sortableListContainer">
{hascontent}
<ol class="wcf-sortableList" data-object-id="0">
{content}
{foreach from=$objects item=chatRoom}
<li class="wcf-sortableNode chatRoomRow" data-object-id="{@$chatRoom->roomID}">
<span class="wcf-sortableNodeLabel">
<a href="{link controller='ChatRoomEdit' id=$chatRoom->roomID}{/link}">{$chatRoom->title|language}</a>
<span class="wcf-sortableButtonContainer">
{if $__wcf->session->getPermission('admin.content.chat.canEditRoom')}
<a href="{link controller='ChatRoomEdit' id=$chatRoom->roomID}{/link}"><img src="{@$__wcf->getPath('wcf')}icon/edit1.svg" alt="" title="{lang}wcf.global.button.edit{/lang}" class="balloonTooltip" /></a>
{/if}
{if $__wcf->session->getPermission('admin.content.chat.canDeleteRoom')}
<img src="{@$__wcf->getPath('wcf')}icon/delete1.svg" alt="" title="{lang}wcf.global.button.delete{/lang}" class="jsDeleteButton jsTooltip" data-object-id="{@$chatRoom->roomID}" data-confirm-message="{lang}wcf.acp.bbcode.delete.sure{/lang}" />
{/if}
</span>
</span>
</li>
{/foreach}
{/content}
</ol>
{hascontentelse}
<p class="wcf-warning">{lang}wcf.acp.chat.room.noneAvailable{/lang}</p>
{/hascontent}
</section>
{include file='footer'}

View File

@ -15,6 +15,7 @@ Cleaning up
EOT; EOT;
if (file_exists('file.tar')) unlink('file.tar'); if (file_exists('file.tar')) unlink('file.tar');
if (file_exists('template.tar')) unlink('template.tar'); if (file_exists('template.tar')) unlink('template.tar');
if (file_exists('acptemplate.tar')) unlink('acptemplate.tar');
foreach (glob('file/js/*.js') as $jsFile) unlink($jsFile); foreach (glob('file/js/*.js') as $jsFile) unlink($jsFile);
foreach (glob('file/style/*.css') as $cssFile) unlink($cssFile); foreach (glob('file/style/*.css') as $cssFile) unlink($cssFile);
if (file_exists('timwolla.wcf.chat.tar')) unlink('timwolla.wcf.chat.tar'); if (file_exists('timwolla.wcf.chat.tar')) unlink('timwolla.wcf.chat.tar');
@ -60,15 +61,25 @@ EOT;
if ($code != 0) exit($code); if ($code != 0) exit($code);
echo <<<EOT echo <<<EOT
Building acptemplate.tar
---------------------
EOT;
chdir('../acptemplate');
passthru('tar cvf ../acptemplate.tar *', $code);
if ($code != 0) exit($code);
echo <<<EOT
Building timwolla.wcf.chat.tar Building timwolla.wcf.chat.tar
------------------------------ ------------------------------
EOT; EOT;
chdir('..'); chdir('..');
passthru('tar cvf timwolla.wcf.chat.tar * --exclude=file --exclude=template --exclude=build.php', $code); passthru('tar cvf timwolla.wcf.chat.tar * --exclude=file --exclude=template --exclude=acptemplate --exclude=build.php', $code);
if ($code != 0) exit($code); if ($code != 0) exit($code);
if (file_exists('file.tar')) unlink('file.tar'); if (file_exists('file.tar')) unlink('file.tar');
if (file_exists('template.tar')) unlink('template.tar'); if (file_exists('template.tar')) unlink('template.tar');
if (file_exists('acptemplate.tar')) unlink('acptemplate.tar');
foreach (glob('file/js/*.js') as $jsFile) unlink($jsFile); foreach (glob('file/js/*.js') as $jsFile) unlink($jsFile);
foreach (glob('file/style/*.css') as $cssFile) unlink($cssFile); foreach (glob('file/style/*.css') as $cssFile) unlink($cssFile);

View File

@ -0,0 +1,34 @@
<?php
namespace timwolla\wcf\chat;
/**
* Handles updates.
*
* @author Tim Düsterhus
* @copyright 2010-2012 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package timwolla.wcf.chat
*/
final class Update {
private $rooms = null;
public function __construct() {
$this->rooms = \wcf\data\chat\room\ChatRoom::getCache();
}
public function execute() {
foreach ($this->rooms as $room) {
$messageAction = new \wcf\data\chat\message\ChatMessageAction(array(), 'create', array(
'data' => array(
'roomID' => $room->roomID,
'time' => TIME_NOW,
'type' => \wcf\data\chat\message\ChatMessage::TYPE_INFORMATION,
// TODO: Language item
'message' => 'Tims Chat was updated. Please refresh the page.'
)
));
$messageAction->executeAction();
}
}
}
$update = new Update();
$update->execute();

58
file/icon/chat.svg Normal file
View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!--
@author Maximilian Mader
@copyright 2011 Tim Düsterhus
@license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
-->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" xml:space="preserve">
<title>Chat</title>
<desc>Chat Icon (outlined)</desc>
<defs>
<style type="text/css">
<![CDATA[
.Lower .bubble {
fill:none;
stroke:#ffffff;
stroke-opacity:1;
}
.Lower .leaderLine {
fill:#ffffff;
fill-opacity:1;
stroke:none;
}
.Upper .bubble {
fill:none;
stroke:#336699;
stroke-opacity:1;
}
.Upper .leaderLine {
fill:#336699;
fill-opacity:1;
stroke:none;
}
]]>
</style>
</defs>
<g id="IconChat">
<g class="Lower" style="display:inline">
<path class="bubble"
d="M 15,8 A 6,5 0 1 1 3,8 6,5 0 1 1 15,8 z"
transform="matrix(1.0345788,0,0,0.95089627,-1.510083,0.10801843)" />
<path class="leaderLine"
d="m 3.9001344,11.947742 c 0.075049,1.120603 -0.7948094,1.693805 -1.4915533,2.143417 1.0104403,
0.104041 3.4597063,-0.953975 3.9167086,-1.276108 0.00808,-0.0057 -2.4251553,-0.867309 -2.4251553,-0.867309 z" />
</g>
<g class="Upper" style="display:inline">
<path class="bubble"
d="M 15,8 A 6,5 0 1 1 3,8 6,5 0 1 1 15,8 z"
transform="matrix(1.0345788,0,0,0.95089627,-1.5100816,-0.50773368)" />
<path class="leaderLine"
d="m 3.9001358,11.331991 c 0.075049,1.120603 -0.7948094,1.693805 -1.4915533,2.143417 1.0104403,
0.104041 3.4597063,-0.953975 3.9167086,-1.276108 0.00808,-0.0057 -2.4251553,-0.867309 -2.4251553,-0.867309 z" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -9,12 +9,18 @@
TimWolla ?= {} TimWolla ?= {}
TimWolla.WCF ?= {} TimWolla.WCF ?= {}
consoleMock = console
consoleMock ?= consoleMock ?=
log: () ->, log: () ->,
warn: () -> warn: () ->,
error: () ->
(($, window, console) -> (($, window, console) ->
TimWolla.WCF.Chat = TimWolla.WCF.Chat =
# Tims Chat stops loading when this reaches zero
# TODO: We need an explosion animation
shields: 3
# Templates # Templates
titleTemplate: null titleTemplate: null
messageTemplate: null messageTemplate: null
@ -34,17 +40,21 @@ consoleMock ?=
events: events:
newMessage: $.Callbacks() newMessage: $.Callbacks()
userMenu: $.Callbacks() userMenu: $.Callbacks()
pe:
getMessages: null
refreshRoomList: null
fish: null
init: () -> init: () ->
console.log '[TimWolla.WCF.Chat] Initializing' console.log '[TimWolla.WCF.Chat] Initializing'
@bindEvents() @bindEvents()
@events.newMessage.add $.proxy @notify, @ @events.newMessage.add $.proxy @notify, @
new WCF.PeriodicalExecuter $.proxy(@refreshRoomList, @), 60e3 @pe.refreshRoomList = new WCF.PeriodicalExecuter $.proxy(@refreshRoomList, @), 60e3
new WCF.PeriodicalExecuter $.proxy(@getMessages, @), @config.reloadTime * 1e3 @pe.getMessages = new WCF.PeriodicalExecuter $.proxy(@getMessages, @), @config.reloadTime * 1e3
@refreshRoomList() @refreshRoomList()
@getMessages() @getMessages()
console.log '[TimWolla.WCF.Chat] Finished initializing' console.log '[TimWolla.WCF.Chat] Finished initializing - Shields at 104 percent'
### ###
# Autocompletes a username # Autocompletes a username
### ###
@ -64,6 +74,7 @@ consoleMock ?=
# Binds all the events needed for Tims Chat. # Binds all the events needed for Tims Chat.
### ###
bindEvents: () -> bindEvents: () ->
# Mark window as focused
$(window).focus $.proxy () -> $(window).focus $.proxy () ->
document.title = @titleTemplate.fetch document.title = @titleTemplate.fetch
title: $('#timsChatRoomList .activeMenuItem a').text() title: $('#timsChatRoomList .activeMenuItem a').text()
@ -71,12 +82,19 @@ consoleMock ?=
@isActive = true @isActive = true
, @ , @
# Mark window as blurred
$(window).blur $.proxy () -> $(window).blur $.proxy () ->
@isActive = false @isActive = false
, @ , @
# Unload the chat
window.onbeforeunload = $.proxy () ->
@unload()
return undefined
, @
# Insert a smiley # Insert a smiley
$('.smiley').click $.proxy (event) -> $('.jsSmiley').click $.proxy (event) ->
@insertText ' ' + $(event.target).attr('alt') + ' ' @insertText ' ' + $(event.target).attr('alt') + ' '
, @ , @
@ -118,11 +136,11 @@ consoleMock ?=
# Clears the stream # Clears the stream
$('#timsChatClear').click (event) -> $('#timsChatClear').click (event) ->
event.preventDefault() event.preventDefault()
$('.timsChatMessage').remove() $('.timsChatMessage').remove()
@oldScrollTop = $('.timsChatMessageContainer').scrollTop() @oldScrollTop = null
$('.timsChatMessageContainer').scrollTop $('.timsChatMessageContainer ul').height() $('.timsChatMessageContainer').scrollTop $('.timsChatMessageContainer ul').height()
$('#timsChatInput').focus() $('#timsChatInput').focus()
# Toggle Buttons # Toggle Buttons
$('.timsChatToggle').click (event) -> $('.timsChatToggle').click (event) ->
@ -139,7 +157,7 @@ consoleMock ?=
# Immediatly scroll down when activating autoscroll # Immediatly scroll down when activating autoscroll
$('#timsChatAutoscroll').click (event) -> $('#timsChatAutoscroll').click (event) ->
$(this).removeClass('hot') $(this).parent().removeClass('default')
if $(this).data 'status' if $(this).data 'status'
$('.timsChatMessageContainer').scrollTop $('.timsChatMessageContainer ul').height() $('.timsChatMessageContainer').scrollTop $('.timsChatMessageContainer ul').height()
@oldScrollTop = $('.timsChatMessageContainer').scrollTop() @oldScrollTop = $('.timsChatMessageContainer').scrollTop()
@ -172,15 +190,15 @@ consoleMock ?=
# Set new topic # Set new topic
if data.topic is '' if data.topic is ''
return if $('#topic').text().trim() is '' return if $('#timsChatTopic').text().trim() is ''
$('#topic').wcfBlindOut 'vertical', () -> $('#timsChatTopic').wcfBlindOut 'vertical', () ->
$(@).text '' $(@).text ''
else else
$('#topic').text data.topic $('#timsChatTopic').text data.topic
$('#topic').wcfBlindIn() if $('#topic').text().trim() isnt '' and $('#topic').is(':hidden') $('#timsChatTopic').wcfBlindIn() if $('#timsChatTopic').text().trim() isnt '' and $('#timsChatTopic').is(':hidden')
$('.timsChatMessage').animate('opacity', .8); $('.timsChatMessage').addClass('unloaded', 800);
@handleMessages data.messages @handleMessages data.messages
document.title = @titleTemplate.fetch data document.title = @titleTemplate.fetch data
, @) , @)
@ -198,7 +216,7 @@ consoleMock ?=
# Frees the fish # Frees the fish
### ###
freeTheFish: () -> freeTheFish: () ->
return if $.wcfIsset('fish') return if $.wcfIsset 'fish'
console.warn '[TimWolla.WCF.Chat] Freeing the fish' console.warn '[TimWolla.WCF.Chat] Freeing the fish'
fish = $ '<div id="fish">' + WCF.String.escapeHTML('><((((\u00B0>') + '</div>' fish = $ '<div id="fish">' + WCF.String.escapeHTML('><((((\u00B0>') + '</div>'
fish.css fish.css
@ -210,16 +228,16 @@ consoleMock ?=
zIndex: 9999 zIndex: 9999
fish.appendTo $ 'body' fish.appendTo $ 'body'
new WCF.PeriodicalExecuter(() -> @pe.fish = new WCF.PeriodicalExecuter(() ->
left = Math.random() * 100 - 50 left = Math.random() * 100 - 50
top = Math.random() * 100 - 50 top = Math.random() * 100 - 50
fish = $('#fish') fish = $ '#fish'
left *= -1 unless fish.width() < (fish.position().left + left) < ($(document).width() - fish.width()) left *= -1 unless fish.width() < (fish.position().left + left) < ($(document).width() - fish.width())
top *= -1 unless fish.height() < (fish.position().top + top) < ($(document).height() - fish.height()) top *= -1 unless fish.height() < (fish.position().top + top) < ($(document).height() - fish.height())
fish.text('><((((\u00B0>') if left > 0 fish.text '><((((\u00B0>' if left > 0
fish.text('<\u00B0))))><') if left < 0 fish.text '<\u00B0))))><' if left < 0
fish.animate fish.animate
top: '+=' + top top: '+=' + top
@ -237,6 +255,15 @@ consoleMock ?=
@handleMessages(data.messages) @handleMessages(data.messages)
@handleUsers(data.users) @handleUsers(data.users)
, @) , @)
error: $.proxy((jqXHR, textStatus, errorThrown) ->
console.error '[TimWolla.WCF.Chat] Battle Station hit - shields at ' + (--@shields / 3 * 104) + ' percent'
if @shields is 0
@pe.refreshRoomList.stop()
@pe.getMessages.stop()
@freeTheFish()
console.error '[TimWolla.WCF.Chat] We got destroyed, but could free our friend the fish before he was killed as well. Have a nice life in freedom!'
alert 'herp i cannot load messages'
, @)
### ###
# Inserts the new messages. # Inserts the new messages.
# #
@ -248,14 +275,16 @@ consoleMock ?=
if $('.timsChatMessageContainer').scrollTop() < @oldScrollTop if $('.timsChatMessageContainer').scrollTop() < @oldScrollTop
if $('#timsChatAutoscroll').data('status') is 1 if $('#timsChatAutoscroll').data('status') is 1
$('#timsChatAutoscroll').click() $('#timsChatAutoscroll').click()
$('#timsChatAutoscroll').addClass('hot').fadeOut('slow').fadeIn('slow') $('#timsChatAutoscroll').parent().addClass('default').fadeOut('slow').fadeIn('slow')
# Insert the messages # Insert the messages
for message in messages for message in messages
continue if $.wcfIsset 'timsChatMessage'+message.messageID # Prevent problems with race condition
@events.newMessage.fire message @events.newMessage.fire message
output = @messageTemplate.fetch message output = @messageTemplate.fetch message
li = $ '<li></li>' li = $ '<li></li>'
li.attr 'id', 'timsChatMessage'+message.messageID
li.addClass 'timsChatMessage timsChatMessage'+message.type li.addClass 'timsChatMessage timsChatMessage'+message.type
li.addClass 'ownMessage' if message.sender is WCF.User.userID li.addClass 'ownMessage' if message.sender is WCF.User.userID
li.append output li.append output
@ -314,7 +343,7 @@ consoleMock ?=
$(@).remove(); $(@).remove();
$('#toggleUsers .badge').text(users.length); $('#toggleUsers .wcf-badge').text(users.length);
### ###
# Inserts text into our input. # Inserts text into our input.
# #
@ -371,7 +400,7 @@ consoleMock ?=
success: $.proxy((data, textStatus, jqXHR) -> success: $.proxy((data, textStatus, jqXHR) ->
$('#timsChatRoomList li').remove() $('#timsChatRoomList li').remove()
$('#toggleRooms a').removeClass 'ajaxLoad' $('#toggleRooms a').removeClass 'ajaxLoad'
$('#toggleRooms .badge').text(data.length); $('#toggleRooms .wcf-badge').text(data.length);
for room in data for room in data
li = $ '<li></li>' li = $ '<li></li>'
@ -447,4 +476,11 @@ consoleMock ?=
else else
li.addClass 'activeMenuItem' li.addClass 'activeMenuItem'
li.find('.timsChatUserMenu').wcfBlindIn 'vertical' li.find('.timsChatUserMenu').wcfBlindIn 'vertical'
###
# Unloads the chat.
###
unload: () ->
$.ajax @config.unloadURL,
type: 'POST'
async: false
)(jQuery, @, consoleMock) )(jQuery, @, consoleMock)

View File

@ -0,0 +1,149 @@
<?php
namespace wcf\acp\form;
use \wcf\system\exception\UserInputException;
use \wcf\system\language\I18nHandler;
use \wcf\system\package\PackageDependencyHandler;
use \wcf\system\WCF;
/**
* Shows the chatroom add form.
*
* @author Tim Düsterhus
* @copyright 2010-2012 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package timwolla.wcf.chat
* @subpackage acp.form
*/
class ChatRoomAddForm extends ACPForm {
/**
* @see \wcf\acp\form\ACPForm::$activeMenuItem
*/
public $activeMenuItem = 'wcf.acp.menu.link.chat.room.add';
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array('admin.content.chat.canAddRoom');
/**
* Title of the room
*
* @var string
*/
public $title = '';
/**
* Topic of the room
*
* @var string
*/
public $topic = '';
/**
* @see \wcf\page\AbstractPage::__construct()
*/
public function __construct() {
$this->objectTypeID = \wcf\system\acl\ACLHandler::getInstance()->getObjectTypeID('timwolla.wcf.chat.room');
parent::__construct();
}
/**
* @see \wcf\page\IPage::readParameters()
*/
public function readParameters() {
parent::readParameters();
I18nHandler::getInstance()->register('title');
I18nHandler::getInstance()->register('topic');
}
/**
* @see \wcf\form\IForm::readFormParameters()
*/
public function readFormParameters() {
parent::readFormParameters();
I18nHandler::getInstance()->readValues();
if (I18nHandler::getInstance()->isPlainValue('title')) $this->title = I18nHandler::getInstance()->getValue('title');
if (I18nHandler::getInstance()->isPlainValue('topic')) $this->topic = I18nHandler::getInstance()->getValue('topic');
}
/**
* @see \wcf\form\IForm::validate()
*/
public function validate() {
parent::validate();
// validate title
if (!I18nHandler::getInstance()->validateValue('title')) {
throw new UserInputException('title');
}
}
/**
* @see \wcf\form\IForm::save()
*/
public function save() {
parent::save();
// save room
$this->objectAction = new \wcf\data\chat\room\ChatRoomAction(array(), 'create', array('data' => array(
'title' => $this->title,
'topic' => $this->topic
)));
$this->objectAction->executeAction();
$returnValues = $this->objectAction->getReturnValues();
$chatRoomEditor = new \wcf\data\chat\room\ChatRoomEditor($returnValues['returnValues']);
$roomID = $returnValues['returnValues']->roomID;
if (!I18nHandler::getInstance()->isPlainValue('title')) {
I18nHandler::getInstance()->save('title', 'wcf.chat.room.title'.$roomID, 'wcf.chat.room', PackageDependencyHandler::getPackageID('timwolla.wcf.chat'));
// update title
$chatRoomEditor->update(array(
'title' => 'wcf.chat.room.title'.$roomID
));
}
if (!I18nHandler::getInstance()->isPlainValue('topic')) {
I18nHandler::getInstance()->save('topic', 'wcf.chat.room.topic'.$roomID, 'wcf.chat.room', PackageDependencyHandler::getPackageID('timwolla.wcf.chat'));
// update topic
$chatRoomEditor->update(array(
'topic' => 'wcf.chat.room.topic'.$roomID
));
}
\wcf\system\acl\ACLHandler::getInstance()->save($roomID, $this->objectTypeID);
\wcf\system\chat\permission\ChatPermissionHandler::clearCache();
$this->saved();
// reset values
$this->topic = $this->title = '';
I18nHandler::getInstance()->disableAssignValueVariables();
// show success
WCF::getTPL()->assign(array(
'success' => true
));
}
/**
* @see \wcf\page\IPage::assignVariables()
*/
public function assignVariables() {
parent::assignVariables();
I18nHandler::getInstance()->assignVariables();
WCF::getTPL()->assign(array(
'action' => 'add',
'title' => $this->title,
'topic' => $this->topic,
'objectTypeID' => $this->objectTypeID
));
}
}

View File

@ -0,0 +1,129 @@
<?php
namespace wcf\acp\form;
use wcf\system\language\I18nHandler;
use wcf\system\package\PackageDependencyHandler;
use wcf\system\WCF;
/**
* Shows the chatroom edit form.
*
* @author Tim Düsterhus
* @copyright 2010-2012 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package timwolla.wcf.chat
* @subpackage acp.form
*/
class ChatRoomEditForm extends ChatRoomAddForm {
/**
* @see \wcf\page\AbstractPage::$templateName
*/
public $templateName = 'chatRoomAdd';
/**
* @see \wcf\acp\form\ACPForm::$activeMenuItem
*/
public $activeMenuItem = 'wcf.acp.menu.link.chat.room.list';
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array('admin.content.chat.canEditRoom');
/**
* room id
*
* @var integer
*/
public $roomID = 0;
/**
* room object
*
* @var \wcf\data\chat\room\ChatRoom
*/
public $roomObj = null;
/**
* @see \wcf\page\IPage::readParameters()
*/
public function readParameters() {
parent::readParameters();
if (isset($_REQUEST['id'])) $this->roomID = intval($_REQUEST['id']);
$this->roomObj = new \wcf\data\chat\room\ChatRoom($this->roomID);
if (!$this->roomObj->roomID) {
throw new \wcf\system\exception\IllegalLinkException();
}
}
/**
* @see \wcf\form\IForm::save()
*/
public function save() {
ACPForm::save();
$this->title = 'wcf.chat.room.title'.$this->roomObj->roomID;
if (I18nHandler::getInstance()->isPlainValue('title')) {
I18nHandler::getInstance()->remove($this->title, PackageDependencyHandler::getPackageID('timwolla.wcf.chat'));
$this->title = I18nHandler::getInstance()->getValue('title');
}
else {
I18nHandler::getInstance()->save('title', $this->title, 'wcf.chat.room', PackageDependencyHandler::getPackageID('timwolla.wcf.chat'));
}
$this->topic = 'wcf.chat.room.topic'.$this->roomObj->roomID;
if (I18nHandler::getInstance()->isPlainValue('topic')) {
I18nHandler::getInstance()->remove($this->topic, PackageDependencyHandler::getPackageID('timwolla.wcf.chat'));
$this->topic = I18nHandler::getInstance()->getValue('topic');
}
else {
I18nHandler::getInstance()->save('topic', $this->topic, 'wcf.chat.room', PackageDependencyHandler::getPackageID('timwolla.wcf.chat'));
}
\wcf\system\acl\ACLHandler::getInstance()->save($this->roomID, $this->objectTypeID);
\wcf\system\chat\permission\ChatPermissionHandler::clearCache();
// update room
$this->objectAction = new \wcf\data\chat\room\ChatRoomAction(array($this->roomID), 'update', array('data' => array(
'title' => $this->title,
'topic' => $this->topic
)));
$this->objectAction->executeAction();
$this->saved();
// show success
WCF::getTPL()->assign(array(
'success' => true
));
}
/**
* @see \wcf\page\IPage::readData()
*/
public function readData() {
parent::readData();
if (!count($_POST)) {
I18nHandler::getInstance()->setOptions('title', PackageDependencyHandler::getPackageID('timwolla.wcf.chat'), $this->roomObj->title, 'wcf.chat.room.title\d+');
I18nHandler::getInstance()->setOptions('topic', PackageDependencyHandler::getPackageID('timwolla.wcf.chat'), $this->roomObj->topic, 'wcf.chat.room.topic\d+');
$this->title = $this->roomObj->title;
$this->topic = $this->roomObj->topic;
}
}
/**
* @see \wcf\page\IPage::assignVariables()
*/
public function assignVariables() {
parent::assignVariables();
I18nHandler::getInstance()->assignVariables((bool) count($_POST));
WCF::getTPL()->assign(array(
'roomID' => $this->roomID,
'action' => 'edit'
));
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace wcf\acp\page;
/**
* Lists available chatrooms.
*
* @author Tim Düsterhus
* @copyright 2010-2012 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package timwolla.wcf.chat
* @subpackage acp.page
*/
class ChatRoomListPage extends \wcf\page\MultipleLinkPage {
/**
* @see \wcf\page\AbstractPage::$neededPermissions
*/
public $neededPermissions = array(
'admin.content.chat.canEditRoom',
'admin.content.chat.canDeleteRoom'
);
/**
* @see \wcf\page\MultipleLinkPage::$objectListClassName
*/
public $objectListClassName = '\wcf\data\chat\room\ChatRoomList';
/**
* @see \wcf\page\MultipleLinkPage::$sortField
*/
public $sortField = 'position';
/**
* @see \wcf\page\MultipleLinkPage::$sortOrder
*/
public $sortOrder = 'ASC';
/**
* @see \wcf\page\IPage::show()
*/
public function show() {
// set active menu item.
\wcf\system\menu\acp\ACPMenu::getInstance()->setActiveMenuItem('wcf.acp.menu.link.chat.room.list');
parent::show();
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace wcf\action;
use \wcf\data\chat;
use \wcf\system\WCF;
/**
* Makes the user leave Tims Chat.
*
* @author Tim Düsterhus
* @copyright 2010-2012 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package timwolla.wcf.chat
* @subpackage action
*/
class ChatLeaveAction extends AbstractAction {
/**
* @see \wcf\action\AbstractAction::$neededModules
*/
public $neededModules = array('CHAT_ACTIVE');
//public $neededPermissions = array('user.chat.canEnter');
public $room = null;
public $userData = array();
/**
* @see \wcf\action\IAction::execute()
*/
public function execute() {
parent::execute();
// validate
if (!WCF::getUser()->userID) {
throw new IllegalLinkException();
}
$this->userData['roomID'] = \wcf\util\ChatUtil::readUserData('roomID');
$this->room = chat\room\ChatRoom::getCache()->search($this->userData['roomID']);
if (!$this->room) throw new \wcf\system\exception\IllegalLinkException();
if (!$this->room->canEnter()) throw new \wcf\system\exception\PermissionDeniedException();
if (CHAT_DISPLAY_JOIN_LEAVE) {
$this->userData['color'] = \wcf\util\ChatUtil::readUserData('color');
$messageAction = new chat\message\ChatMessageAction(array(), 'create', array(
'data' => array(
'roomID' => $this->room->roomID,
'sender' => WCF::getUser()->userID,
'username' => WCF::getUser()->username,
'time' => TIME_NOW,
'type' => chat\message\ChatMessage::TYPE_LEAVE,
'message' => '',
'color1' => $this->userData['color'][1],
'color2' => $this->userData['color'][2]
)
));
$messageAction->executeAction();
}
\wcf\util\ChatUtil::writeUserData(array('roomID' => null));
$this->executed();
header("HTTP/1.0 204 No Content");
exit;
}
}

View File

@ -13,12 +13,12 @@ use \wcf\system\WCF;
*/ */
class ChatMessage extends \wcf\data\DatabaseObject { class ChatMessage extends \wcf\data\DatabaseObject {
/** /**
* @see wcf\data\DatabaseObject::$databaseTableName * @see \wcf\data\DatabaseObject::$databaseTableName
*/ */
protected static $databaseTableName = 'chat_message'; protected static $databaseTableName = 'chat_message';
/** /**
* @see wcf\data\DatabaseObject::$databaseTableIndexName * @see \wcf\data\DatabaseObject::$databaseTableIndexName
*/ */
protected static $databaseTableIndexName = 'messageID'; protected static $databaseTableIndexName = 'messageID';
@ -110,7 +110,8 @@ class ChatMessage extends \wcf\data\DatabaseObject {
'time' => $this->time, 'time' => $this->time,
'receiver' => $this->receiver, 'receiver' => $this->receiver,
'type' => $this->type, 'type' => $this->type,
'roomID' => $this->roomID 'roomID' => $this->roomID,
'messageID' => $this->messageID
); );
if ($raw) return $array; if ($raw) return $array;

View File

@ -12,7 +12,7 @@ namespace wcf\data\chat\message;
*/ */
class ChatMessageAction extends \wcf\data\AbstractDatabaseObjectAction { class ChatMessageAction extends \wcf\data\AbstractDatabaseObjectAction {
/** /**
* @see wcf\data\AbstractDatabaseObjectAction::$className * @see \wcf\data\AbstractDatabaseObjectAction::$className
*/ */
protected $className = '\wcf\data\chat\message\ChatMessageEditor'; protected $className = '\wcf\data\chat\message\ChatMessageEditor';
} }

View File

@ -12,7 +12,7 @@ namespace wcf\data\chat\message;
*/ */
class ChatMessageEditor extends \wcf\data\DatabaseObjectEditor { class ChatMessageEditor extends \wcf\data\DatabaseObjectEditor {
/** /**
* @see wcf\data\DatabaseObjectDecorator::$baseClass * @see \wcf\data\DatabaseObjectDecorator::$baseClass
*/ */
protected static $baseClass = '\wcf\data\chat\message\ChatMessage'; protected static $baseClass = '\wcf\data\chat\message\ChatMessage';

View File

@ -12,7 +12,7 @@ namespace wcf\data\chat\message;
*/ */
class ChatMessageList extends \wcf\data\DatabaseObjectList { class ChatMessageList extends \wcf\data\DatabaseObjectList {
/** /**
* @see wcf\data\DatabaseObjectList::$className * @see \wcf\data\DatabaseObjectList::$className
*/ */
public $className = 'wcf\data\chat\message\ChatMessage'; public $className = 'wcf\data\chat\message\ChatMessage';

View File

@ -1,6 +1,7 @@
<?php <?php
namespace wcf\data\chat\room; namespace wcf\data\chat\room;
use \wcf\system\cache\CacheHandler; use \wcf\system\cache\CacheHandler;
use \wcf\system\WCF;
/** /**
* Represents a chat room. * Represents a chat room.
@ -13,12 +14,12 @@ use \wcf\system\cache\CacheHandler;
*/ */
class ChatRoom extends \wcf\data\DatabaseObject implements \wcf\system\request\IRouteController { class ChatRoom extends \wcf\data\DatabaseObject implements \wcf\system\request\IRouteController {
/** /**
* @see wcf\data\DatabaseObject::$databaseTableName * @see \wcf\data\DatabaseObject::$databaseTableName
*/ */
protected static $databaseTableName = 'chat_room'; protected static $databaseTableName = 'chat_room';
/** /**
* @see wcf\data\DatabaseObject::$databaseTableIndexName * @see \wcf\data\DatabaseObject::$databaseTableIndexName
*/ */
protected static $databaseTableIndexName = 'roomID'; protected static $databaseTableIndexName = 'roomID';
@ -29,6 +30,36 @@ class ChatRoom extends \wcf\data\DatabaseObject implements \wcf\system\request\I
*/ */
protected static $cache = null; protected static $cache = null;
/**
* @see \wcf\data\chat\room\ChatRoom::getTitle();
*/
public function __toString() {
return $this->getTitle();
}
/**
* Returns the number of users currently active in this room.
*
* @return integer
*/
public function countUsers() {
$packageID = \wcf\system\package\PackageDependencyHandler::getPackageID('timwolla.wcf.chat');
$sql = "SELECT
count(*) as count
FROM
wcf".WCF_N."_user_storage
WHERE
field = 'roomID'
AND packageID = ".intval($packageID)."
AND fieldValue = ".intval($this->roomID);
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute();
$row = $stmt->fetchArray();
return $row['count'];
}
/** /**
* Loads the room cache. * Loads the room cache.
*/ */
@ -37,7 +68,7 @@ class ChatRoom extends \wcf\data\DatabaseObject implements \wcf\system\request\I
CacheHandler::getInstance()->addResource( CacheHandler::getInstance()->addResource(
'chatrooms', 'chatrooms',
WCF_DIR.'cache/cache.chatrooms.php', WCF_DIR.'cache/cache.chatrooms.php',
'wcf\system\cache\builder\ChatRoomCacheBuilder' '\wcf\system\cache\builder\ChatRoomCacheBuilder'
); );
self::$cache = CacheHandler::getInstance()->get('chatrooms'); self::$cache = CacheHandler::getInstance()->get('chatrooms');
} }
@ -46,10 +77,12 @@ class ChatRoom extends \wcf\data\DatabaseObject implements \wcf\system\request\I
} }
/** /**
* @see \wcf\data\chat\room\ChatRoom::getTitle(); * Returns the ID of this chat-room.
*
* @see \wcf\system\request\IRouteController
*/ */
public function __toString() { public function getID() {
return $this->getTitle(); return $this->roomID;
} }
/** /**
@ -62,12 +95,39 @@ class ChatRoom extends \wcf\data\DatabaseObject implements \wcf\system\request\I
} }
/** /**
* Returns the ID of this chat-room. * Returns the users that are currently active in this room.
* *
* @see \wcf\system\request\RRouteHandler * @return array<\wcf\data\user\User>
*/ */
public function getID() { public function getUsers() {
return $this->roomID; $packageID = \wcf\system\package\PackageDependencyHandler::getPackageID('timwolla.wcf.chat');
$sql = "SELECT
userID
FROM
wcf".WCF_N."_user_storage
WHERE
field = 'roomID'
AND packageID = ".intval($packageID)."
AND fieldValue = ".intval($this->roomID);
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute();
$userIDs = array();
while ($row = $stmt->fetchArray()) $userIDs[] = $row['userID'];
if (!count($userIDs)) return;
$sql = "SELECT
*
FROM
wcf".WCF_N."_user
WHERE
userID IN (".rtrim(str_repeat('?,', count($userIDs)), ',').")
ORDER BY
username ASC";
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute($userIDs);
return $stmt->fetchObjects('\wcf\data\user\User');
} }
/** /**
@ -76,6 +136,8 @@ class ChatRoom extends \wcf\data\DatabaseObject implements \wcf\system\request\I
* @return boolean * @return boolean
*/ */
public function canEnter() { public function canEnter() {
return \wcf\system\chat\permissions\ChatPermissionHandler::getInstance()->getPermission($this, 'canEnter'); $ph = \wcf\system\chat\permission\ChatPermissionHandler::getInstance();
return $ph->getPermission($this, 'user.canEnter') || $ph->getPermission($this, 'mod.canAlwaysEnter');
} }
} }

View File

@ -0,0 +1,95 @@
<?php
namespace wcf\data\chat\room;
use \wcf\system\WCF;
/**
* Executes chatroom-related actions.
*
* @author Tim Düsterhus
* @copyright 2010-2012 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package timwolla.wcf.chat
* @subpackage data.chat.room
*/
class ChatRoomAction extends \wcf\data\AbstractDatabaseObjectAction {
/**
* @see \wcf\data\AbstractDatabaseObjectAction::$className
*/
protected $className = '\wcf\data\chat\room\ChatRoomEditor';
/**
* @see \wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
*/
protected $permissionsDelete = array('admin.content.chat.canDeleteRoom');
/**
* @see \wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
*/
protected $permissionsUpdate = array('admin.content.chat.canEditRoom');
/**
* Fixes create to append new boards.
*/
public function create() {
$room = parent::create();
WCF::getDB()->beginTransaction();
$sql = "SELECT max(position) as max
FROM wcf".WCF_N."_chat_room
FOR UPDATE";
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute();
$row = $stmt->fetchArray();
$sql = "UPDATE wcf".WCF_N."_chat_room
SET position = ".($row['max'] + 1)."
WHERE roomID = ?";
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute(array($room->roomID));
WCF::getDB()->commitTransaction();
return $room;
}
/**
* Validates parameters to update sorting.
*/
public function validateUpdatePosition() {
// validate permissions
if (is_array($this->permissionsUpdate) && count($this->permissionsUpdate)) {
try {
WCF::getSession()->checkPermissions($this->permissionsUpdate);
}
catch (\wcf\system\exception\PermissionDeniedException $e) {
throw new ValidateActionException('Insufficient permissions');
}
}
else {
throw new ValidateActionException('Insufficient permissions');
}
if (!isset($this->parameters['data']['structure'])) {
throw new ValidateActionException('Missing parameter structure');
}
}
/**
* Updates sorting.
*/
public function updatePosition() {
$roomList = new \wcf\data\chat\room\ChatRoomList();
$roomList->sqlOrderBy = "chat_room.position";
$roomList->sqlLimit = 0;
$roomList->readObjects();
$i = 0;
WCF::getDB()->beginTransaction();
foreach ($this->parameters['data']['structure'][0] as $roomID) {
$room = $roomList->search($roomID);
if ($room === null) continue;
$editor = new ChatRoomEditor($room);
$editor->update(array('position' => $i++));
}
WCF::getDB()->commitTransaction();
}
}

View File

@ -1,5 +1,6 @@
<?php <?php
namespace wcf\data\chat\room; namespace wcf\data\chat\room;
use \wcf\system\WCF;
/** /**
* Provides functions to edit chat rooms. * Provides functions to edit chat rooms.
@ -12,7 +13,7 @@ namespace wcf\data\chat\room;
*/ */
class ChatRoomEditor extends \wcf\data\DatabaseObjectEditor implements \wcf\data\IEditableCachedObject { class ChatRoomEditor extends \wcf\data\DatabaseObjectEditor implements \wcf\data\IEditableCachedObject {
/** /**
* @see wcf\data\DatabaseObjectDecorator::$baseClass * @see \wcf\data\DatabaseObjectDecorator::$baseClass
*/ */
protected static $baseClass = '\wcf\data\chat\room\ChatRoom'; protected static $baseClass = '\wcf\data\chat\room\ChatRoom';
@ -20,7 +21,23 @@ class ChatRoomEditor extends \wcf\data\DatabaseObjectEditor implements \wcf\data
* Clears the room cache. * Clears the room cache.
*/ */
public static function resetCache() { public static function resetCache() {
self::getCache(); \wcf\system\cache\CacheHandler::getInstance()->clear(WCF_DIR.'cache', 'cache.chatrooms.php');
CacheHandler::getInstance()->clearResource('chatrooms'); }
/**
* @see \wcf\data\DatabaseObjectEditor::deleteAll()
*/
public static function deleteAll(array $objectIDs = array()) {
parent::deleteAll($objectIDs);
$packageID = \wcf\system\package\PackageDependencyHandler::getPackageID('timwolla.wcf.chat');
WCF::getDB()->beginTransaction();
foreach ($objectIDs as $objectID) {
\wcf\system\language\I18nHandler::getInstance()->remove('wcf.chat.room.title'.$objectID, $packageID);
\wcf\system\language\I18nHandler::getInstance()->remove('wcf.chat.room.topic'.$objectID, $packageID);
}
WCF::getDB()->commitTransaction();
return count($objectIDs);
} }
} }

View File

@ -12,7 +12,7 @@ namespace wcf\data\chat\room;
*/ */
class ChatRoomList extends \wcf\data\DatabaseObjectList { class ChatRoomList extends \wcf\data\DatabaseObjectList {
/** /**
* @see wcf\data\DatabaseObjectList::$className * @see \wcf\data\DatabaseObjectList::$className
*/ */
public $className = 'wcf\data\chat\room\ChatRoom'; public $className = 'wcf\data\chat\room\ChatRoom';
} }

View File

@ -1,7 +1,6 @@
<?php <?php
namespace wcf\form; namespace wcf\form;
use \wcf\data\chat; use \wcf\data\chat;
use \wcf\system\exception\PermissionDeniedException;
use \wcf\system\exception\UserInputException; use \wcf\system\exception\UserInputException;
use \wcf\system\WCF; use \wcf\system\WCF;
use \wcf\util\StringUtil; use \wcf\util\StringUtil;
@ -9,7 +8,7 @@ use \wcf\util\StringUtil;
/** /**
* Inserts a message * Inserts a message
* *
* @author Tim Düsterhus * @author Tim Düsterhus
* @copyright 2010-2012 Tim Düsterhus * @copyright 2010-2012 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode> * @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package timwolla.wcf.chat * @package timwolla.wcf.chat
@ -20,10 +19,14 @@ class ChatForm extends AbstractForm {
public $message = ''; public $message = '';
public $room = null; public $room = null;
public $userData = array(); public $userData = array();
/**
* @see \wcf\page\AbstractForm::$useTemplate
*/
public $useTemplate = false; public $useTemplate = false;
/** /**
* @see \wcf\page\AbstractPage::readData() * @see \wcf\page\IPage::readData()
*/ */
public function readData() { public function readData() {
$this->userData['color'] = \wcf\util\ChatUtil::readUserData('color'); $this->userData['color'] = \wcf\util\ChatUtil::readUserData('color');
@ -37,17 +40,17 @@ class ChatForm extends AbstractForm {
} }
/** /**
* @see \wcf\form\AbstractForm::readFormParameters() * @see \wcf\form\IForm::readFormParameters()
*/ */
public function readFormParameters() { public function readFormParameters() {
parent::readFormParameters(); parent::readFormParameters();
if (isset($_REQUEST['text'])) $this->message = StringUtil::trim($_REQUEST['text']); if (isset($_REQUEST['text'])) $this->message = \wcf\util\MessageUtil::stripCrap(StringUtil::trim($_REQUEST['text']));
if (isset($_REQUEST['smilies'])) $this->enableSmilies = intval($_REQUEST['smilies']); if (isset($_REQUEST['smilies'])) $this->enableSmilies = intval($_REQUEST['smilies']);
} }
/** /**
* @see \wcf\form\AbstractForm::validate() * @see \wcf\form\IForm::validate()
*/ */
public function validate() { public function validate() {
parent::validate(); parent::validate();
@ -55,10 +58,14 @@ class ChatForm extends AbstractForm {
if ($this->message === '') { if ($this->message === '') {
throw new UserInputException('text'); throw new UserInputException('text');
} }
if (strlen($this->message) > CHAT_MAX_LENGTH) {
throw new UserInputException('text', 'tooLong');
}
} }
/** /**
* @see \wcf\form\AbstractForm::save() * @see \wcf\form\IForm::save()
*/ */
public function save() { public function save() {
parent::save(); parent::save();
@ -107,4 +114,12 @@ class ChatForm extends AbstractForm {
$this->saved(); $this->saved();
} }
/**
* @see \wcf\page\IPage::show()
*/
public function show() {
header("HTTP/1.0 204 No Content");
parent::show();
}
} }

View File

@ -22,14 +22,14 @@ class ChatMessagePage extends AbstractPage {
public $useTemplate = false; public $useTemplate = false;
/** /**
* @see \wcf\page\Page::readData() * @see \wcf\page\Page::readData()
*/ */
public function readData() { public function readData() {
parent::readData(); parent::readData();
$this->readRoom(); $this->readRoom();
$this->readMessages(); $this->readMessages();
$this->readUsers(); $this->users = $this->room->getUsers();
} }
public function readMessages() { public function readMessages() {
@ -54,34 +54,6 @@ class ChatMessagePage extends AbstractPage {
if (!$this->room->canEnter()) throw new \wcf\system\exception\PermissionDeniedException(); if (!$this->room->canEnter()) throw new \wcf\system\exception\PermissionDeniedException();
} }
public function readUsers() {
$packageID = \wcf\system\package\PackageDependencyHandler::getPackageID('timwolla.wcf.chat');
$sql = "SELECT
userID
FROM
wcf".WCF_N."_user_storage
WHERE
field = 'roomID'
AND packageID = ".intval($packageID)."
AND fieldValue = ".intval($this->roomID);
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute();
while ($row = $stmt->fetchArray()) $userIDs[] = $row['userID'];
$sql = "SELECT
*
FROM
wcf".WCF_N."_user
WHERE
userID IN (".rtrim(str_repeat('?,', count($userIDs)), ',').")
ORDER BY
username ASC";
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute($userIDs);
$this->users = $stmt->fetchObjects('\wcf\data\user\User');
}
/** /**
* @see \wcf\page\IPage::show() * @see \wcf\page\IPage::show()
*/ */
@ -94,6 +66,11 @@ class ChatMessagePage extends AbstractPage {
parent::show(); parent::show();
@header('Content-type: application/json'); @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();
}
$json = array('users' => array(), 'messages' => array()); $json = array('users' => array(), 'messages' => array());
foreach ($this->messages as $message) { foreach ($this->messages as $message) {

View File

@ -105,25 +105,24 @@ class ChatPage extends AbstractPage {
public function readParameters() { public function readParameters() {
parent::readParameters(); parent::readParameters();
if ($this->action == 'Message') { switch ($this->action) {
new ChatMessagePage(); case 'Message':
exit; new ChatMessagePage();
} exit;
else if ($this->action == 'Log') { case 'Log':
//TODO: Initialise LogPage exit;
exit; case 'RefreshRoomList':
} new ChatRefreshRoomListPage();
else if ($this->action == 'RefreshRoomList') { exit;
new ChatRefreshRoomListPage(); case 'Send':
exit; new \wcf\form\ChatForm();
} exit;
else if ($this->action == 'Send') { case 'Leave':
new \wcf\form\ChatForm(); new \wcf\action\ChatLeaveAction();
exit; exit;
} case 'Copyright':
else if ($this->action == 'Copyright') { new ChatCopyrightPage();
new ChatCopyrightPage(); exit;
exit;
} }
if (isset($_REQUEST['id'])) $this->roomID = (int) $_REQUEST['id']; if (isset($_REQUEST['id'])) $this->roomID = (int) $_REQUEST['id'];

View File

@ -0,0 +1,44 @@
<?php
namespace wcf\system\cache\builder;
use wcf\system\WCF;
/**
* Caches the chat permissions for a combination of user groups.
*
* @author Tim Düsterhus, Marcel Werk
* @copyright 2010-2012 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @package timwolla.wcf.chat
* @subpackage system.cache.builder
*/
class ChatPermissionCacheBuilder implements ICacheBuilder {
/**
* @see wcf\system\cache\ICacheBuilder::getData()
*/
public function getData(array $cacheResource) {
$data = array();
list(, $groupIDsStr) = explode('-', $cacheResource['cache']);
$groupIDs = explode(',', $groupIDsStr);
if (count($groupIDs)) {
$conditionBuilder = new \wcf\system\database\util\PreparedStatementConditionBuilder();
$conditionBuilder->add('acl_option.packageID IN (?)', array(\wcf\system\package\PackageDependencyHandler::getDependencies()));
$conditionBuilder->add('acl_option.objectTypeID = ?', array(\wcf\system\acl\ACLHandler::getInstance()->getObjectTypeID('timwolla.wcf.chat.room')));
$conditionBuilder->add('option_to_group.optionID = acl_option.optionID');
$conditionBuilder->add('option_to_group.groupID IN (?)', array($groupIDs));
$sql = "SELECT option_to_group.groupID, option_to_group.objectID AS roomID, option_to_group.optionValue,
acl_option.optionName AS permission
FROM wcf".WCF_N."_acl_option acl_option,
wcf".WCF_N."_acl_option_to_group option_to_group
".$conditionBuilder;
$statement = WCF::getDB()->prepareStatement($sql);
$statement->execute($conditionBuilder->getParameters());
while ($row = $statement->fetchArray()) {
if (!isset($data[$row['roomID']][$row['permission']])) $data[$row['roomID']][$row['permission']] = $row['optionValue'];
else $data[$row['roomID']][$row['permission']] = $row['optionValue'] || $data[$row['roomID']][$row['permission']];
}
}
return $data;
}
}

View File

@ -12,7 +12,7 @@ namespace wcf\system\cache\builder;
*/ */
class ChatRoomCacheBuilder implements ICacheBuilder { class ChatRoomCacheBuilder implements ICacheBuilder {
/** /**
* @see wcf\system\cache\ICacheBuilder::getData() * @see \wcf\system\cache\ICacheBuilder::getData()
*/ */
public function getData(array $cacheResource) { public function getData(array $cacheResource) {
// get all chat rooms // get all chat rooms

View File

@ -1,6 +1,7 @@
<?php <?php
namespace wcf\system\chat\permissions; namespace wcf\system\chat\permission;
use \wcf\system\acl\ACLHandler; use \wcf\system\acl\ACLHandler;
use \wcf\system\cache\CacheHandler;
use \wcf\system\package\PackageDependencyHandler; use \wcf\system\package\PackageDependencyHandler;
use \wcf\system\WCF; use \wcf\system\WCF;
@ -15,14 +16,25 @@ use \wcf\system\WCF;
*/ */
class ChatPermissionHandler extends \wcf\system\SingletonFactory { class ChatPermissionHandler extends \wcf\system\SingletonFactory {
protected $chatPermissions = array(); protected $chatPermissions = array();
protected static $defaults = array(
'user.canEnter' => true,
'user.canWrite' => true,
'mod.canAlwaysEnter' => false,
'mod.canAlwaysWrite' => false
);
/** /**
* @see wcf\system\SingletonFactory::init() * @see \wcf\system\SingletonFactory::init()
*/ */
protected function init() { protected function init() {
$packageID = PackageDependencyHandler::getPackageID('timwolla.wcf.chat'); $packageID = PackageDependencyHandler::getPackageID('timwolla.wcf.chat');
$ush = \wcf\system\user\storage\UserStorageHandler::getInstance(); $ush = \wcf\system\user\storage\UserStorageHandler::getInstance();
// TODO: get groups permissions
// get groups permissions
$groups = implode(',', WCF::getUser()->getGroupIDs());
$groupsFileName = \wcf\util\StringUtil::getHash(implode('-', WCF::getUser()->getGroupIDs()));
CacheHandler::getInstance()->addResource('chatPermission-'.$groups, WCF_DIR.'cache/cache.chatPermission-'.$groupsFileName.'.php', 'wcf\system\cache\builder\ChatPermissionCacheBuilder');
$this->chatPermissions = CacheHandler::getInstance()->get('chatPermission-'.$groups);
// get user permissions // get user permissions
if (WCF::getUser()->userID) { if (WCF::getUser()->userID) {
@ -75,7 +87,20 @@ class ChatPermissionHandler extends \wcf\system\SingletonFactory {
* @return boolean * @return boolean
*/ */
public function getPermission(\wcf\data\chat\room\ChatRoom $room, $permission) { public function getPermission(\wcf\data\chat\room\ChatRoom $room, $permission) {
if (!isset($this->chatPermissions[$room->roomID][$permission])) return true; if (!isset($this->chatPermissions[$room->roomID][$permission])) {
return isset(self::$defaults[$permission]) ? self::$defaults[$permission] : false;
}
return (boolean) $this->chatPermissions[$room->roomID][$permission]; return (boolean) $this->chatPermissions[$room->roomID][$permission];
} }
/**
* Clears the cache.
*/
public static function clearCache() {
$packageID = PackageDependencyHandler::getPackageID('timwolla.wcf.chat');
$ush = \wcf\system\user\storage\UserStorageHandler::getInstance();
$ush->resetAll('chatUserPermissions', $packageID);
\wcf\system\cache\CacheHandler::getInstance()->clear(WCF_DIR.'cache', 'cache.chatPermission-[a-f0-9]{40}.php');
}
} }

View File

@ -8,17 +8,17 @@ namespace wcf\system\event\listener;
* @copyright 2010-2012 Tim Düsterhus * @copyright 2010-2012 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode> * @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package timwolla.wcf.chat * @package timwolla.wcf.chat
* @subpackage system.event.listener * @subpackage system.event.listener
*/ */
class ChatRouteListener implements \wcf\system\event\IEventListener { class ChatRouteListener implements \wcf\system\event\IEventListener {
/** /**
* @see wcf\system\event\IEventListener::execute() * @see \wcf\system\event\IEventListener::execute()
*/ */
public function execute($eventObj, $className, $eventName) { public function execute($eventObj, $className, $eventName) {
$route = new \wcf\system\request\Route('chatAction'); $route = new \wcf\system\request\Route('chatAction');
$route->setSchema('/{controller}/{action}'); $route->setSchema('/{controller}/{action}');
$route->setParameterOption('controller', null, 'Chat'); $route->setParameterOption('controller', null, 'Chat');
$route->setParameterOption('action', null, '(Message|Log|Send|RefreshRoomList|Copyright)'); $route->setParameterOption('action', null, '(Message|Log|Send|RefreshRoomList|Copyright|Leave)');
$eventObj->addRoute($route); $eventObj->addRoute($route);
} }
} }

View File

@ -17,7 +17,7 @@ class ChatPageMenuItemProvider extends DefaultPageMenuItemProvider {
/** /**
* Hides the button when there is no valid room * Hides the button when there is no valid room
* *
* @see \wcf\system\menu\page\PageMenuItemProvider::isVisible() * @see \wcf\system\menu\page\PageMenuItemProvider::isVisible()
*/ */
public function isVisible() { public function isVisible() {
// guests are not supported // guests are not supported
@ -43,7 +43,7 @@ class ChatPageMenuItemProvider extends DefaultPageMenuItemProvider {
/** /**
* Modifies the link to show the Link we would be redirect to. * Modifies the link to show the Link we would be redirect to.
* *
* @see \wcf\system\menu\page\PageMenuItemProvider::getLink() * @see \wcf\system\menu\page\PageMenuItemProvider::getLink()
*/ */
public function getLink() { public function getLink() {
return \wcf\system\request\LinkHandler::getInstance()->getLink('Chat', array( return \wcf\system\request\LinkHandler::getInstance()->getLink('Chat', array(

View File

@ -12,14 +12,14 @@ namespace wcf\system\option;
*/ */
class TimeIntervalOptionType extends TextOptionType { class TimeIntervalOptionType extends TextOptionType {
/** /**
* @see wcf\system\option\IOptionType::getData() * @see \wcf\system\option\IOptionType::getData()
*/ */
public function getData(\wcf\data\option\Option $option, $newValue) { public function getData(\wcf\data\option\Option $option, $newValue) {
return \wcf\util\ChatUtil::timeModifier($newValue); return \wcf\util\ChatUtil::timeModifier($newValue);
} }
/** /**
* @see wcf\system\option\TextOptionType::getFormElement() * @see \wcf\system\option\TextOptionType::getFormElement()
*/ */
public function getFormElement(\wcf\data\option\Option $option, $value) { public function getFormElement(\wcf\data\option\Option $option, $value) {
$tmp = (int) ($value / 60); $tmp = (int) ($value / 60);

View File

@ -67,7 +67,7 @@ class ChatUtil {
if ($data[WCF::getUser()->userID] === null) { if ($data[WCF::getUser()->userID] === null) {
switch ($field) { switch ($field) {
case 'color': case 'color':
$data[WCF::getUser()->userID] = array(1 => 0xFF0000, 2 => 0x00FF00); $data[WCF::getUser()->userID] = array(1 => self::getRandomNumber(), 2 => self::getRandomNumber() * 0xFFFF);
break; break;
} }
static::writeUserData(array($field => $data[WCF::getUser()->userID])); static::writeUserData(array($field => $data[WCF::getUser()->userID]));
@ -79,6 +79,16 @@ class ChatUtil {
else return $data[WCF::getUser()->userID]; else return $data[WCF::getUser()->userID];
} }
/**
* Returns a random number.
*
* @return integer
*/
public static function /* int */ getRandomNumber() {
return 4; // chosen by a fair dice roll
// guaranteed to be random
}
/** /**
* Writes user data * Writes user data
* *

View File

@ -11,7 +11,7 @@
text-align: left; text-align: left;
} }
.sidebar { .wcf-sidebar {
margin-bottom: -20px !important; margin-bottom: -20px !important;
overflow: auto; overflow: auto;
padding: 0; padding: 0;
@ -32,7 +32,7 @@
} }
} }
#timsChatTopic, #smileyList, #timsChatOptions { #timsChatTopic, #smileyList {
padding: 5px; padding: 5px;
} }
@ -43,7 +43,7 @@
padding-left: 7px !important; padding-left: 7px !important;
} }
#smileyList .smilies, .smallButtons { #smileyList .smilies {
li { li {
display: inline; display: inline;
margin: 5px 5px 0 0; margin: 5px 5px 0 0;
@ -63,7 +63,9 @@
} }
#timsChatOptions { #timsChatOptions {
display: inline-block; > ul {
text-align: right;
}
} }
#timsChatUserList { #timsChatUserList {
@ -100,7 +102,7 @@
} }
} }
.unloaded { &.unloaded {
opacity: 0.4; opacity: 0.4;
} }
} }
@ -178,7 +180,7 @@
} }
} }
&.active .badge { &.active .wcf-badge {
font-size: 65% !important; font-size: 65% !important;
color: #fff; color: #fff;
background-color: #369; background-color: #369;

View File

@ -1,46 +1,47 @@
DROP TABLE IF EXISTS wcf1_chat_message; DROP TABLE IF EXISTS wcf1_chat_message;
CREATE TABLE wcf1_chat_message ( CREATE TABLE wcf1_chat_message (
messageID int(10) NOT NULL AUTO_INCREMENT, messageID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
roomID int(10) NOT NULL, roomID INT(10) NOT NULL,
sender int(10) DEFAULT NULL, sender INT(10) DEFAULT NULL,
username varchar(255) DEFAULT NULL, username VARCHAR(255) DEFAULT NULL,
receiver int(10) DEFAULT NULL, receiver INT(10) DEFAULT NULL,
time int(10) NOT NULL, time INT(10) NOT NULL,
type tinyint(3) NOT NULL DEFAULT 1, type TINYINT(3) NOT NULL DEFAULT 1,
message mediumtext NOT NULL, message MEDIUMTEXT NOT NULL,
enableSmilies tinyint(1) NOT NULL DEFAULT 1, enableSmilies TINYINT(1) NOT NULL DEFAULT 1,
enableHTML tinyint(1) NOT NULL DEFAULT 0, enableHTML TINYINT(1) NOT NULL DEFAULT 0,
color1 int(10) NOT NULL DEFAULT 0, color1 INT(10) NOT NULL DEFAULT 0,
color2 int(10) NOT NULL DEFAULT 0, color2 INT(10) NOT NULL DEFAULT 0,
PRIMARY KEY (messageID),
KEY roomID (roomID), KEY roomID (roomID),
KEY sender (sender), KEY sender (sender),
KEY receiver (receiver) KEY receiver (receiver)
); );
DROP TABLE IF EXISTS wcf1_chat_room; DROP TABLE IF EXISTS wcf1_chat_room;
CREATE TABLE wcf1_chat_room ( CREATE TABLE wcf1_chat_room (
roomID int(10) NOT NULL AUTO_INCREMENT, roomID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
title varchar(25) NOT NULL, title VARCHAR(255) NOT NULL,
topic varchar(255) NOT NULL, topic VARCHAR(255) NOT NULL,
position int(10) NOT NULL DEFAULT 0, position INT(10) NOT NULL DEFAULT 0,
permanent tinyint(1) NOT NULL DEFAULT 1, permanent TINYINT(1) NOT NULL DEFAULT 1,
owner int(10) DEFAULT NULL, owner INT(10) DEFAULT NULL,
PRIMARY KEY (roomID),
KEY positionKey (position), KEY positionKey (position),
KEY owner (owner) KEY owner (owner)
); );
DROP TABLE IF EXISTS wcf1_chat_room_suspension; DROP TABLE IF EXISTS wcf1_chat_room_suspension;
CREATE TABLE wcf1_chat_room_suspension ( CREATE TABLE wcf1_chat_room_suspension (
roomID int(10) NOT NULL, userID INT(10) NOT NULL,
userID int(10) NOT NULL, roomID INT(10) DEFAULT NULL,
type tinyint(3) NOT NULL, type TINYINT(3) NOT NULL,
time int(10) NOT NULL, time INT(10) NOT NULL,
PRIMARY KEY (roomID, userID),
KEY userID (userID), UNIQUE KEY main (userID, roomID),
KEY type (type, time), KEY roomID (roomID),
KEY time (time) KEY type (type, time),
KEY time (time)
); );
ALTER TABLE wcf1_chat_message ADD FOREIGN KEY (receiver) REFERENCES wcf1_user (userID) ON DELETE CASCADE; ALTER TABLE wcf1_chat_message ADD FOREIGN KEY (receiver) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
@ -52,7 +53,7 @@ ALTER TABLE wcf1_chat_room ADD FOREIGN KEY (owner) REFERENCES wcf1_user (userID)
ALTER TABLE wcf1_chat_room_suspension ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE; ALTER TABLE wcf1_chat_room_suspension ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
ALTER TABLE wcf1_chat_room_suspension ADD FOREIGN KEY (roomID) REFERENCES wcf1_chat_room (roomID) ON DELETE CASCADE; ALTER TABLE wcf1_chat_room_suspension ADD FOREIGN KEY (roomID) REFERENCES wcf1_chat_room (roomID) ON DELETE CASCADE;
INSERT INTO wcf1_chat_room (title, topic, position) VALUES ('Testroom 1', 'Topic of Testroom 1', 1); INSERT INTO wcf1_chat_room (title, topic, position) VALUES ('wcf.chat.room.title1', 'wcf.chat.room.topic1', 1);
INSERT INTO wcf1_chat_room (title, topic, position) VALUES ('Testroom 2', 'Topic of Testroom 2', 2); INSERT INTO wcf1_chat_room (title, topic, position) VALUES ('Testroom 2', 'Topic of Testroom 2', 2);
INSERT INTO wcf1_chat_room (title, topic, position) VALUES ('Testroom with a very long', 'The topic of this room is rather loing as well!', 3); INSERT INTO wcf1_chat_room (title, topic, position) VALUES ('Testroom with a very long', 'The topic of this room is rather loing as well!', 3);
INSERT INTO wcf1_chat_room (title, topic, position) VALUES ('Room w/o topic', '', 4); INSERT INTO wcf1_chat_room (title, topic, position) VALUES ('Room w/o topic', '', 4);

View File

@ -1,5 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<language xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/language.xsd" languagecode="de"> <language xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/language.xsd" languagecode="de">
<category name="wcf.acp.chat">
<item name="wcf.acp.chat.room.list"><![CDATA[Chaträume]]></item>
<item name="wcf.acp.chat.room.add"><![CDATA[Chatraum hinzufügen]]></item>
<item name="wcf.acp.chat.room.edit"><![CDATA[Chatraum bearbeiten]]></item>
</category>
<category name="wcf.acp.menu">
<item name="wcf.acp.menu.link.chat"><![CDATA[Chat]]></item>
<item name="wcf.acp.menu.link.chat.room.list"><![CDATA[Chaträume auflisten]]></item>
<item name="wcf.acp.menu.link.chat.room.add"><![CDATA[Chatraum hinzufügen]]></item>
</category>
<category name="wcf.acp.option"> <category name="wcf.acp.option">
<item name="wcf.acp.option.category.chat"><![CDATA[Chat]]></item> <item name="wcf.acp.option.category.chat"><![CDATA[Chat]]></item>
<item name="wcf.acp.option.category.chat.general"><![CDATA[Allgemein]]></item> <item name="wcf.acp.option.category.chat.general"><![CDATA[Allgemein]]></item>
@ -14,8 +26,8 @@
<item name="wcf.acp.option.chat_display_clock.description"><![CDATA[Aktiviert die Anzeige der Uhrzeit von Nachrichten]]></item> <item name="wcf.acp.option.chat_display_clock.description"><![CDATA[Aktiviert die Anzeige der Uhrzeit von Nachrichten]]></item>
<item name="wcf.acp.option.chat_animations"><![CDATA[Animationen aktivieren]]></item> <item name="wcf.acp.option.chat_animations"><![CDATA[Animationen aktivieren]]></item>
<item name="wcf.acp.option.chat_animations.description"><![CDATA[Aktiviert die Animation neuer Nachrichten.]]></item> <item name="wcf.acp.option.chat_animations.description"><![CDATA[Aktiviert die Animation neuer Nachrichten.]]></item>
<item name="wcf.acp.option.chat_length"><![CDATA[Maximale Textlänge]]></item> <item name="wcf.acp.option.chat_max_length"><![CDATA[Maximale Textlänge]]></item>
<item name="wcf.acp.option.chat_length.description"><![CDATA[Gibt die maximale Länge einer Chatnachricht an.]]></item> <item name="wcf.acp.option.chat_max_length.description"><![CDATA[Gibt die maximale Länge einer Chatnachricht an.]]></item>
<item name="wcf.acp.option.chat_show_version"><![CDATA[Versionsnummer anzeigen]]></item> <item name="wcf.acp.option.chat_show_version"><![CDATA[Versionsnummer anzeigen]]></item>
<item name="wcf.acp.option.chat_show_version.description"><![CDATA[Zeigt die Versionsnummer im Copyrighthinweis an.]]></item> <item name="wcf.acp.option.chat_show_version.description"><![CDATA[Zeigt die Versionsnummer im Copyrighthinweis an.]]></item>
@ -25,10 +37,6 @@
<item name="wcf.acp.option.chat_log_archivetime.description"><![CDATA[Gibt die Zeit in Minuten an, die eine Nachricht im Protokoll gespeichert bleibt.]]></item> <item name="wcf.acp.option.chat_log_archivetime.description"><![CDATA[Gibt die Zeit in Minuten an, die eine Nachricht im Protokoll gespeichert bleibt.]]></item>
</category> </category>
<category name="wcf.header">
<item name="wcf.header.menu.chat"><![CDATA[Chat]]></item>
</category>
<category name="wcf.chat"> <category name="wcf.chat">
<item name="wcf.chat.title"><![CDATA[Chat]]></item> <item name="wcf.chat.title"><![CDATA[Chat]]></item>
<item name="wcf.chat.protocol"><![CDATA[Protokoll]]></item> <item name="wcf.chat.protocol"><![CDATA[Protokoll]]></item>
@ -55,4 +63,14 @@
<!-- 2 = TYPE_LEAVE --> <!-- 2 = TYPE_LEAVE -->
<item name="wcf.chat.message.2"><![CDATA[hat den Chat verlassen.]]></item> <item name="wcf.chat.message.2"><![CDATA[hat den Chat verlassen.]]></item>
</category> </category>
<!-- I18N Values -->
<category name="wcf.chat.room">
<item name="wcf.chat.room.title1"><![CDATA[Hauptchat]]></item>
<item name="wcf.chat.room.topic1"><![CDATA[Vielen Dank, dass Sie sich für Tims Chat entschieden haben. Bitte besuchen Sie das ACP um diesen Text anzupassen.]]></item>
</category>
<category name="wcf.header">
<item name="wcf.header.menu.chat"><![CDATA[Chat]]></item>
</category>
</language> </language>

View File

@ -49,7 +49,7 @@
<defaultvalue>1</defaultvalue> <defaultvalue>1</defaultvalue>
<showorder>7</showorder> <showorder>7</showorder>
</option> </option>
<option name="chat_length"> <option name="chat_max_length">
<categoryname>chat.general</categoryname> <categoryname>chat.general</categoryname>
<optiontype>integer</optiontype> <optiontype>integer</optiontype>
<defaultvalue>500</defaultvalue> <defaultvalue>500</defaultvalue>

View File

@ -5,7 +5,7 @@
<packagedescription><![CDATA[Chat for WoltLab Community Framework™]]></packagedescription> <packagedescription><![CDATA[Chat for WoltLab Community Framework™]]></packagedescription>
<standalone>0</standalone> <standalone>0</standalone>
<isunique>1</isunique> <isunique>1</isunique>
<version>3.0.0 Alpha 3</version> <version>3.0.0 Alpha 8</version>
<date>2011-11-26</date> <date>2011-11-26</date>
<plugin>com.woltlab.wcf.bbcode</plugin> <!-- TODO: Correct me --> <plugin>com.woltlab.wcf.bbcode</plugin> <!-- TODO: Correct me -->
</packageinformation> </packageinformation>
@ -19,30 +19,36 @@
<requiredpackage minversion="2.0.0 Alpha 1">com.woltlab.wcf</requiredpackage> <requiredpackage minversion="2.0.0 Alpha 1">com.woltlab.wcf</requiredpackage>
<requiredpackage minversion="1.0.0 Alpha 1">com.woltlab.wcf.bbcode</requiredpackage> <requiredpackage minversion="1.0.0 Alpha 1">com.woltlab.wcf.bbcode</requiredpackage>
<requiredpackage minversion="1.0.0 Alpha 1">com.woltlab.wcf.acl</requiredpackage> <requiredpackage minversion="1.0.0 Alpha 1">com.woltlab.wcf.acl</requiredpackage>
<requiredpackage minversion="1.0.0 Alpha 1">com.woltlab.wcf.message</requiredpackage>
</requiredpackages> </requiredpackages>
<instructions type="install"> <instructions type="install">
<instruction type="language">language/*.xml</instruction> <instruction type="language">language/*.xml</instruction>
<instruction type="file">file.tar</instruction> <instruction type="file">file.tar</instruction>
<instruction type="template">template.tar</instruction> <instruction type="template">template.tar</instruction>
<instruction type="acpTemplate">acptemplate.tar</instruction>
<instruction type="sql">install.sql</instruction> <instruction type="sql">install.sql</instruction>
<instruction type="objectType">objectType.xml</instruction> <instruction type="objectType">objectType.xml</instruction>
<instruction type="option">option.xml</instruction> <instruction type="option">option.xml</instruction>
<instruction type="pageMenu">pagemenu.xml</instruction> <instruction type="pageMenu">pagemenu.xml</instruction>
<instruction type="eventListener">eventListener.xml</instruction> <instruction type="eventListener">eventListener.xml</instruction>
<instruction type="templateListener">templatelistener.xml</instruction>
<instruction type="aclOption">acloptions.xml</instruction> <instruction type="aclOption">acloptions.xml</instruction>
<instruction type="acpMenu">acpMenu.xml</instruction>
<instruction type="userGroupOption">userGroupOption.xml</instruction>
</instructions> </instructions>
<instructions type="update" fromversion="3.0.0 Alpha *"> <instructions type="update" fromversion="3.0.0 Alpha *">
<instruction type="language">language/*.xml</instruction> <instruction type="language">language/*.xml</instruction>
<instruction type="file">file.tar</instruction> <instruction type="file">file.tar</instruction>
<instruction type="template">template.tar</instruction> <instruction type="template">template.tar</instruction>
<instruction type="acpTemplate">acptemplate.tar</instruction>
<instruction type="objectType">objectType.xml</instruction> <instruction type="objectType">objectType.xml</instruction>
<instruction type="option">option.xml</instruction> <instruction type="option">option.xml</instruction>
<instruction type="pageMenu">pagemenu.xml</instruction> <instruction type="pageMenu">pagemenu.xml</instruction>
<instruction type="eventListener">eventListener.xml</instruction> <instruction type="eventListener">eventListener.xml</instruction>
<instruction type="templateListener">templatelistener.xml</instruction>
<instruction type="aclOption">acloptions.xml</instruction> <instruction type="aclOption">acloptions.xml</instruction>
<instruction type="acpMenu">acpMenu.xml</instruction>
<instruction type="userGroupOption">userGroupOption.xml</instruction>
<instruction type="script">acp/timwolla.wcf.chat.update.php</instruction>
</instructions> </instructions>
</package> </package>

View File

@ -5,7 +5,7 @@
{include file='headInclude' sandbox=false} {include file='headInclude' sandbox=false}
<style type="text/css"> <style type="text/css">
@import url("{@RELATIVE_WCF_DIR}style/timwolla.wcf.chat.css"); @import url("{@$__wcf->getPath('wcf')}style/timwolla.wcf.chat.css");
#timsChatCopyrightDialog { #timsChatCopyrightDialog {
background-image: url("{link controller='Chat' action='Copyright' sheep=1}{/link}"); background-image: url("{link controller='Chat' action='Copyright' sheep=1}{/link}");
background-position: right 45px; background-position: right 45px;
@ -56,6 +56,7 @@
.jsCounterInput { .jsCounterInput {
height: 16px; height: 16px;
box-sizing: content-box !important;
} }
.jsCounterInput, .jsCounter { .jsCounterInput, .jsCounter {
@ -89,103 +90,63 @@
</head> </head>
<body id="tpl{$templateName|ucfirst}"> <body id="tpl{$templateName|ucfirst}">
{capture assign='sidebar'} {capture assign='sidebar'}{include file='chatSidebar'}{/capture}
<div id="sidebarContent" class="sidebarContent"> {capture assign='headerNavigation'}{include file='chatNavigationInclude'}{/capture}
<nav class="timsChatSidebarTabs">
<ul>
<li id="toggleUsers" class="active"><a href="javascript:;" title="{lang}wcf.chat.users{/lang}">{lang}wcf.chat.users{/lang} <span class="badge">0</span></a></li>
<li id="toggleRooms"><a href="javascript:;" title="{lang}wcf.chat.rooms{/lang}" data-refresh-url="{link controller="Chat" action="RefreshRoomList"}{/link}">{lang}wcf.chat.rooms{/lang} <span class="badge">{#$rooms|count}</span></a></li>
</ul>
</nav>
<div id="sidebarContainer">
<ul id="timsChatUserList">
{*section name=user start=1 loop=26}
<li class="timsChatUser">
<a href="javascript:;">User {$user}</a>
<ul class="timsChatUserMenu">
<li>
<a href="javascript:;">{lang}wcf.chat.query{/lang}</a>
<a href="javascript:;">{lang}wcf.chat.kick{/lang}</a>
<a href="javascript:;">{lang}wcf.chat.ban{/lang}</a>
<a href="{link controller="User" id=$user}{/link}">{lang}wcf.chat.profile{/lang}</a>
</li>
</ul>
</li>
{/section*}
</ul>
<nav id="timsChatRoomList" class="sidebarMenu" style="display: none;">
<div>
<ul>
{foreach from=$rooms item='roomListRoom'}
{if $roomListRoom->canEnter()}
<li{if $roomListRoom->roomID == $room->roomID} class="activeMenuItem"{/if}>
<a href="{link controller='Chat' object=$roomListRoom}{/link}" class="timsChatRoom">{$roomListRoom}</a>
</li>
{/if}
{/foreach}
</ul>
<div style="text-align: center;"><button type="button">Force Refresh</button></div>
</div>
</nav>
</div>
</div>
{/capture}
{include file='header' sandbox=false sidebarOrientation='right'} {include file='header' sandbox=false sidebarOrientation='right'}
<div id="timsChatRoomContent"> <div id="timsChatRoomContent">
<div id="timsChatTopic" class="border"{if $room->topic|language === ''} style="display: none;"{/if}>{$room->topic|language}</div> <div id="timsChatTopic" class="wcf-border"{if $room->topic|language === ''} style="display: none;"{/if}>{$room->topic|language}</div>
<div class="timsChatMessageContainer border content"> <div class="timsChatMessageContainer wcf-border wcf-content">
<ul> <ul>
<noscript><li class="error">{lang}wcf.chat.noJs{/lang}</li></noscript> <noscript><li class="wcf-error">{lang}wcf.chat.noJs{/lang}</li></noscript>
</ul> </ul>
</div> </div>
<form id="timsChatForm" action="{link controller="Chat" action="Send"}{/link}" method="post"> <form id="timsChatForm" action="{link controller="Chat" action="Send"}{/link}" method="post">
<input type="text" id="timsChatInput" class="inputText long jsCounterInput" name="text" autocomplete="off" maxlength="{CHAT_LENGTH}" disabled="disabled" required="required" placeholder="{lang}wcf.chat.submit.default{/lang}" /> <input type="text" id="timsChatInput" class="inputText long jsCounterInput" name="text" autocomplete="off" maxlength="{@CHAT_MAX_LENGTH}" disabled="disabled" required="required" placeholder="{lang}wcf.chat.submit.default{/lang}" />
</form> </form>
<div id="timsChatControls"> <div id="timsChatControls" class="wcf-border">
<div id="smileyList" class="border"> {if MODULE_SMILEY}
<ul class="smilies"> <div id="smileyList">
{foreach from=$smilies item='smiley'} <ul class="smilies">
<li> {foreach from=$smilies item='smiley'}
<img src="{$smiley->getURL()}" alt="{$smiley->smileyCode}" title="{$smiley->smileyCode}" class="smiley" /> <li>
</li> <img src="{$smiley->getURL()}" alt="{$smiley->smileyCode}" title="{$smiley->smileyTitle}" class="jsSmiley jsTooltip" />
{/foreach} </li>
</ul> {/foreach}
</div>
<div id="timsChatOptions" class="border">
<div class="smallButtons">
<ul>
<li>
<a id="timsChatAutoscroll" href="javascript:;" class="timsChatToggle balloonTooltip" title="{lang}wcf.global.button.disable{/lang}" data-disable-message="{lang}wcf.global.button.disable{/lang}" data-enable-message="{lang}wcf.global.button.enable{/lang}" data-status="1">
<img alt="" src="{icon size='S'}enabled1{/icon}" /> <span>{lang}wcf.chat.scroll{/lang}</span>
</a>
</li>
<li>
<a id="timsChatNotify" href="javascript:;" class="timsChatToggle balloonTooltip" title="{lang}wcf.global.button.enable{/lang}" data-disable-message="{lang}wcf.global.button.disable{/lang}" data-enable-message="{lang}wcf.global.button.enable{/lang}" data-status="0">
<img alt="" src="{icon size='S'}disabled1{/icon}" /> <span>{lang}wcf.chat.notify{/lang}</span>
</a>
</li>
<li>
<a id="timsChatSmilies" href="javascript:;" class="timsChatToggle balloonTooltip" title="{lang}wcf.global.button.disable{/lang}" data-disable-message="{lang}wcf.global.button.disable{/lang}" data-enable-message="{lang}wcf.global.button.enable{/lang}" data-status="1">
<img alt="" src="{icon size='S'}enabled1{/icon}" /> <span>{lang}wcf.chat.smilies{/lang}</span>
</a>
</li>
<li>
<a id="timsChatClear" href="javascript:;" class="balloonTooltip" title="{lang}wcf.chat.clear.description{/lang}">
<img alt="" src="{icon size='S'}delete1{/icon}" /> <span>{lang}wcf.chat.clear{/lang}</span>
</a>
</li>
<li>
<a id="timsChatMark" href="javascript:;" class="balloonTooltip" title="{lang}wcf.chat.mark.description{/lang}">
<img alt="" src="{icon size='S'}check1{/icon}" /> <span>{lang}wcf.chat.mark{/lang}</span>
</a>
</li>
</ul> </ul>
</div> </div>
</div> {/if}
<nav id="timsChatOptions">
<ul class="wcf-smallButtons">
<li>
<a id="timsChatAutoscroll" href="javascript:;" class="timsChatToggle jsTooltip wcf-button" title="{lang}wcf.global.button.disable{/lang}" data-disable-message="{lang}wcf.global.button.disable{/lang}" data-enable-message="{lang}wcf.global.button.enable{/lang}" data-status="1">
<img alt="" src="{icon size='S'}enabled1{/icon}" /> <span>{lang}wcf.chat.scroll{/lang}</span>
</a>
</li>
<li>
<a id="timsChatNotify" href="javascript:;" class="timsChatToggle jsTooltip wcf-button" title="{lang}wcf.global.button.enable{/lang}" data-disable-message="{lang}wcf.global.button.disable{/lang}" data-enable-message="{lang}wcf.global.button.enable{/lang}" data-status="0">
<img alt="" src="{icon size='S'}disabled1{/icon}" /> <span>{lang}wcf.chat.notify{/lang}</span>
</a>
</li>
<li{if !MODULE_SMILEY} style="display: none;"{/if}>
<a id="timsChatSmilies" href="javascript:;" class="timsChatToggle jsTooltip wcf-button" title="{lang}wcf.global.button.disable{/lang}" data-disable-message="{lang}wcf.global.button.disable{/lang}" data-enable-message="{lang}wcf.global.button.enable{/lang}" data-status="1">
<img alt="" src="{icon size='S'}enabled1{/icon}" /> <span>{lang}wcf.chat.smilies{/lang}</span>
</a>
</li>
<li>
<a id="timsChatClear" href="javascript:;" class="jsTooltip wcf-button" title="{lang}wcf.chat.clear.description{/lang}">
<img alt="" src="{icon size='S'}delete1{/icon}" /> <span>{lang}wcf.chat.clear{/lang}</span>
</a>
</li>
<li>
<a id="timsChatMark" href="javascript:;" class="jsTooltip wcf-button" title="{lang}wcf.chat.mark.description{/lang}">
<img alt="" src="{icon size='S'}check1{/icon}" /> <span>{lang}wcf.chat.mark{/lang}</span>
</a>
</li>
</ul>
</nav>
{include file='chatCopyright'} {include file='chatCopyright'}
</div> </div>
</div> </div>
@ -201,8 +162,7 @@
// populate config // populate config
TimWolla.WCF.Chat.config = { TimWolla.WCF.Chat.config = {
reloadTime: {@CHAT_RELOADTIME}, reloadTime: {@CHAT_RELOADTIME},
animations: {@CHAT_ANIMATIONS}, unloadURL: '{link controller='Chat' action='Leave'}{/link}'
maxTextLength: {@CHAT_LENGTH}
} }
WCF.Language.addObject({ WCF.Language.addObject({
'wcf.chat.query': '{lang}wcf.chat.query{/lang}', 'wcf.chat.query': '{lang}wcf.chat.query{/lang}',

View File

@ -1,2 +1,2 @@
<script type="text/javascript" src="{@RELATIVE_WCF_DIR}js/TimWolla.WCF.Chat.js{if $chatVersion|isset}?version={$chatVersion|urlencode}{/if}"></script> <script type="text/javascript" src="{@$__wcf->getPath('wcf')}js/TimWolla.WCF.Chat.js{if $chatVersion|isset}?version={$chatVersion|urlencode}{/if}"></script>
<script type="text/javascript" src="{@RELATIVE_WCF_DIR}js/jCounter.jQuery.js"></script> <script type="text/javascript" src="{@$__wcf->getPath('wcf')}js/jCounter.jQuery.js"></script>

View File

@ -1 +1 @@
{literal}<dl style="margin: 0;"><dt style="width: 145px; margin: 0;"><time style="float: left;">{@$formattedTime}</time> {@$formattedUsername}{$separator}</dt> <dd style="padding: 0; margin-left: 150px;">{@$formattedMessage}</dd></dl>{/literal} <dl style="margin: 0;"><dt style="width: 145px; margin: 0;">{if CHAT_DISPLAY_CLOCK}<time style="float: left;">{ldelim}@$formattedTime}</time> {/if}{literal}{@$formattedUsername}{$separator}</dt> <dd style="padding: 0; margin-left: 150px;">{@$formattedMessage}</dd></dl>{/literal}

View File

@ -1,4 +1,5 @@
{if $templateName == 'chat'} <li>
<li><a href="{link controller="Chat" action="Log"}{/link}" title="{lang}wcf.chat.protocol{/lang}" class="balloonTooltip"><img src="{icon size='S'}session1{/icon}" alt="" /> <span>{lang}wcf.chat.protocol{/lang}</span></a></li> <a href="{link controller="Chat" action="Log"}{/link}" title="{lang}wcf.chat.protocol{/lang}" class="jsTooltip">
<li><a href="{link controller="Chat"}{/link}" title="{lang}wcf.chat.title{/lang}" class="balloonTooltip"><img src="{icon size='S'}chat1{/icon}" alt="" /> <span>{lang}wcf.chat.title{/lang}</span></a></li> <img src="{icon size='S'}session1{/icon}" alt="" /> <span>{lang}wcf.chat.protocol{/lang}</span>
{/if} </a>
</li>

27
template/chatSidebar.tpl Normal file
View File

@ -0,0 +1,27 @@
<div id="sidebarContent" class="wcf-sidebarContent">
<nav class="timsChatSidebarTabs">
<ul>
<li id="toggleUsers" class="active"><a href="javascript:;" title="{lang}wcf.chat.users{/lang}">{lang}wcf.chat.users{/lang} <span class="wcf-badge">0</span></a></li>
<li id="toggleRooms"><a href="javascript:;" title="{lang}wcf.chat.rooms{/lang}" data-refresh-url="{link controller="Chat" action="RefreshRoomList"}{/link}">{lang}wcf.chat.rooms{/lang} <span class="wcf-badge">{#$rooms|count}</span></a></li>
</ul>
</nav>
<div id="sidebarContainer">
<ul id="timsChatUserList">
</ul>
<nav id="timsChatRoomList" class="wcf-sidebarMenu" style="display: none;">
<div>
<ul>
{foreach from=$rooms item='roomListRoom'}
{if $roomListRoom->canEnter()}
<li{if $roomListRoom->roomID == $room->roomID} class="activeMenuItem"{/if}>
<a href="{link controller='Chat' object=$roomListRoom}{/link}" class="timsChatRoom">{$roomListRoom}</a>
</li>
{/if}
{/foreach}
</ul>
<div style="text-align: center;"><button type="button">{lang}wcf.chat.forceRefresh{/lang}</button></div>
</div>
</nav>
</div>
</div>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/templatelistener.xsd">
<import>
<templatelistener name="chatHeaderNavigation">
<environment>user</environment>
<templatename>header</templatename>
<eventname>headerNavigation</eventname>
<templatecode><![CDATA[{include file='chatNavigationInclude'}]]></templatecode>
</templatelistener>
</import>
</data>

31
userGroupOption.xml Normal file
View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<data xmlns="http://www.woltlab.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.woltlab.com http://www.woltlab.com/XSD/maelstrom/userGroupOption.xsd">
<import>
<categories>
<category name="admin.content.chat">
<parent>admin.content</parent>
</category>
</categories>
<options>
<option name="admin.content.chat.canAddRoom">
<categoryname>admin.content.chat</categoryname>
<optiontype>boolean</optiontype>
<defaultvalue>0</defaultvalue>
<admindefaultvalue>1</admindefaultvalue>
</option>
<option name="admin.content.chat.canEditRoom">
<categoryname>admin.content.chat</categoryname>
<optiontype>boolean</optiontype>
<defaultvalue>0</defaultvalue>
<admindefaultvalue>1</admindefaultvalue>
</option>
<option name="admin.content.chat.canDeleteRoom">
<categoryname>admin.content.chat</categoryname>
<optiontype>boolean</optiontype>
<defaultvalue>0</defaultvalue>
<admindefaultvalue>1</admindefaultvalue>
</option>
</options>
</import>
</data>