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"?>
<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>
<option name="canEnter">
<objecttype>timwolla.wcf.chat.room</objecttype>
</option>
<categories>
<category name="user">
<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>
</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;
if (file_exists('file.tar')) unlink('file.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/style/*.css') as $cssFile) unlink($cssFile);
if (file_exists('timwolla.wcf.chat.tar')) unlink('timwolla.wcf.chat.tar');
@ -60,15 +61,25 @@ EOT;
if ($code != 0) exit($code);
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
------------------------------
EOT;
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 (file_exists('file.tar')) unlink('file.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/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.WCF ?= {}
consoleMock = console
consoleMock ?=
log: () ->,
warn: () ->
warn: () ->,
error: () ->
(($, window, console) ->
TimWolla.WCF.Chat =
# Tims Chat stops loading when this reaches zero
# TODO: We need an explosion animation
shields: 3
# Templates
titleTemplate: null
messageTemplate: null
@ -34,17 +40,21 @@ consoleMock ?=
events:
newMessage: $.Callbacks()
userMenu: $.Callbacks()
pe:
getMessages: null
refreshRoomList: null
fish: null
init: () ->
console.log '[TimWolla.WCF.Chat] Initializing'
@bindEvents()
@events.newMessage.add $.proxy @notify, @
new WCF.PeriodicalExecuter $.proxy(@refreshRoomList, @), 60e3
new WCF.PeriodicalExecuter $.proxy(@getMessages, @), @config.reloadTime * 1e3
@pe.refreshRoomList = new WCF.PeriodicalExecuter $.proxy(@refreshRoomList, @), 60e3
@pe.getMessages = new WCF.PeriodicalExecuter $.proxy(@getMessages, @), @config.reloadTime * 1e3
@refreshRoomList()
@getMessages()
console.log '[TimWolla.WCF.Chat] Finished initializing'
console.log '[TimWolla.WCF.Chat] Finished initializing - Shields at 104 percent'
###
# Autocompletes a username
###
@ -64,6 +74,7 @@ consoleMock ?=
# Binds all the events needed for Tims Chat.
###
bindEvents: () ->
# Mark window as focused
$(window).focus $.proxy () ->
document.title = @titleTemplate.fetch
title: $('#timsChatRoomList .activeMenuItem a').text()
@ -71,12 +82,19 @@ consoleMock ?=
@isActive = true
, @
# Mark window as blurred
$(window).blur $.proxy () ->
@isActive = false
, @
# Unload the chat
window.onbeforeunload = $.proxy () ->
@unload()
return undefined
, @
# Insert a smiley
$('.smiley').click $.proxy (event) ->
$('.jsSmiley').click $.proxy (event) ->
@insertText ' ' + $(event.target).attr('alt') + ' '
, @
@ -118,11 +136,11 @@ consoleMock ?=
# Clears the stream
$('#timsChatClear').click (event) ->
event.preventDefault()
$('.timsChatMessage').remove()
@oldScrollTop = $('.timsChatMessageContainer').scrollTop()
$('.timsChatMessageContainer').scrollTop $('.timsChatMessageContainer ul').height()
$('#timsChatInput').focus()
event.preventDefault()
$('.timsChatMessage').remove()
@oldScrollTop = null
$('.timsChatMessageContainer').scrollTop $('.timsChatMessageContainer ul').height()
$('#timsChatInput').focus()
# Toggle Buttons
$('.timsChatToggle').click (event) ->
@ -139,7 +157,7 @@ consoleMock ?=
# Immediatly scroll down when activating autoscroll
$('#timsChatAutoscroll').click (event) ->
$(this).removeClass('hot')
$(this).parent().removeClass('default')
if $(this).data 'status'
$('.timsChatMessageContainer').scrollTop $('.timsChatMessageContainer ul').height()
@oldScrollTop = $('.timsChatMessageContainer').scrollTop()
@ -172,15 +190,15 @@ consoleMock ?=
# Set new topic
if data.topic is ''
return if $('#topic').text().trim() is ''
return if $('#timsChatTopic').text().trim() is ''
$('#topic').wcfBlindOut 'vertical', () ->
$('#timsChatTopic').wcfBlindOut 'vertical', () ->
$(@).text ''
else
$('#topic').text data.topic
$('#topic').wcfBlindIn() if $('#topic').text().trim() isnt '' and $('#topic').is(':hidden')
$('#timsChatTopic').text data.topic
$('#timsChatTopic').wcfBlindIn() if $('#timsChatTopic').text().trim() isnt '' and $('#timsChatTopic').is(':hidden')
$('.timsChatMessage').animate('opacity', .8);
$('.timsChatMessage').addClass('unloaded', 800);
@handleMessages data.messages
document.title = @titleTemplate.fetch data
, @)
@ -198,7 +216,7 @@ consoleMock ?=
# Frees the fish
###
freeTheFish: () ->
return if $.wcfIsset('fish')
return if $.wcfIsset 'fish'
console.warn '[TimWolla.WCF.Chat] Freeing the fish'
fish = $ '<div id="fish">' + WCF.String.escapeHTML('><((((\u00B0>') + '</div>'
fish.css
@ -210,16 +228,16 @@ consoleMock ?=
zIndex: 9999
fish.appendTo $ 'body'
new WCF.PeriodicalExecuter(() ->
@pe.fish = new WCF.PeriodicalExecuter(() ->
left = 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())
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
top: '+=' + top
@ -237,6 +255,15 @@ consoleMock ?=
@handleMessages(data.messages)
@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.
#
@ -248,14 +275,16 @@ consoleMock ?=
if $('.timsChatMessageContainer').scrollTop() < @oldScrollTop
if $('#timsChatAutoscroll').data('status') is 1
$('#timsChatAutoscroll').click()
$('#timsChatAutoscroll').addClass('hot').fadeOut('slow').fadeIn('slow')
$('#timsChatAutoscroll').parent().addClass('default').fadeOut('slow').fadeIn('slow')
# Insert the messages
for message in messages
continue if $.wcfIsset 'timsChatMessage'+message.messageID # Prevent problems with race condition
@events.newMessage.fire message
output = @messageTemplate.fetch message
li = $ '<li></li>'
li.attr 'id', 'timsChatMessage'+message.messageID
li.addClass 'timsChatMessage timsChatMessage'+message.type
li.addClass 'ownMessage' if message.sender is WCF.User.userID
li.append output
@ -314,7 +343,7 @@ consoleMock ?=
$(@).remove();
$('#toggleUsers .badge').text(users.length);
$('#toggleUsers .wcf-badge').text(users.length);
###
# Inserts text into our input.
#
@ -371,7 +400,7 @@ consoleMock ?=
success: $.proxy((data, textStatus, jqXHR) ->
$('#timsChatRoomList li').remove()
$('#toggleRooms a').removeClass 'ajaxLoad'
$('#toggleRooms .badge').text(data.length);
$('#toggleRooms .wcf-badge').text(data.length);
for room in data
li = $ '<li></li>'
@ -447,4 +476,11 @@ consoleMock ?=
else
li.addClass 'activeMenuItem'
li.find('.timsChatUserMenu').wcfBlindIn 'vertical'
###
# Unloads the chat.
###
unload: () ->
$.ajax @config.unloadURL,
type: 'POST'
async: false
)(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 {
/**
* @see wcf\data\DatabaseObject::$databaseTableName
* @see \wcf\data\DatabaseObject::$databaseTableName
*/
protected static $databaseTableName = 'chat_message';
/**
* @see wcf\data\DatabaseObject::$databaseTableIndexName
* @see \wcf\data\DatabaseObject::$databaseTableIndexName
*/
protected static $databaseTableIndexName = 'messageID';
@ -110,7 +110,8 @@ class ChatMessage extends \wcf\data\DatabaseObject {
'time' => $this->time,
'receiver' => $this->receiver,
'type' => $this->type,
'roomID' => $this->roomID
'roomID' => $this->roomID,
'messageID' => $this->messageID
);
if ($raw) return $array;

View File

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

View File

@ -12,7 +12,7 @@ namespace wcf\data\chat\message;
*/
class ChatMessageEditor extends \wcf\data\DatabaseObjectEditor {
/**
* @see wcf\data\DatabaseObjectDecorator::$baseClass
* @see \wcf\data\DatabaseObjectDecorator::$baseClass
*/
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 {
/**
* @see wcf\data\DatabaseObjectList::$className
* @see \wcf\data\DatabaseObjectList::$className
*/
public $className = 'wcf\data\chat\message\ChatMessage';

View File

@ -1,6 +1,7 @@
<?php
namespace wcf\data\chat\room;
use \wcf\system\cache\CacheHandler;
use \wcf\system\WCF;
/**
* Represents a chat room.
@ -13,12 +14,12 @@ use \wcf\system\cache\CacheHandler;
*/
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';
/**
* @see wcf\data\DatabaseObject::$databaseTableIndexName
* @see \wcf\data\DatabaseObject::$databaseTableIndexName
*/
protected static $databaseTableIndexName = 'roomID';
@ -29,6 +30,36 @@ class ChatRoom extends \wcf\data\DatabaseObject implements \wcf\system\request\I
*/
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.
*/
@ -37,7 +68,7 @@ class ChatRoom extends \wcf\data\DatabaseObject implements \wcf\system\request\I
CacheHandler::getInstance()->addResource(
'chatrooms',
WCF_DIR.'cache/cache.chatrooms.php',
'wcf\system\cache\builder\ChatRoomCacheBuilder'
'\wcf\system\cache\builder\ChatRoomCacheBuilder'
);
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() {
return $this->getTitle();
public function getID() {
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() {
return $this->roomID;
public function getUsers() {
$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
*/
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
namespace wcf\data\chat\room;
use \wcf\system\WCF;
/**
* 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 {
/**
* @see wcf\data\DatabaseObjectDecorator::$baseClass
* @see \wcf\data\DatabaseObjectDecorator::$baseClass
*/
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.
*/
public static function resetCache() {
self::getCache();
CacheHandler::getInstance()->clearResource('chatrooms');
\wcf\system\cache\CacheHandler::getInstance()->clear(WCF_DIR.'cache', 'cache.chatrooms.php');
}
/**
* @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 {
/**
* @see wcf\data\DatabaseObjectList::$className
* @see \wcf\data\DatabaseObjectList::$className
*/
public $className = 'wcf\data\chat\room\ChatRoom';
}

View File

@ -1,7 +1,6 @@
<?php
namespace wcf\form;
use \wcf\data\chat;
use \wcf\system\exception\PermissionDeniedException;
use \wcf\system\exception\UserInputException;
use \wcf\system\WCF;
use \wcf\util\StringUtil;
@ -9,7 +8,7 @@ use \wcf\util\StringUtil;
/**
* Inserts a message
*
* @author Tim Düsterhus
* @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
@ -20,10 +19,14 @@ class ChatForm extends AbstractForm {
public $message = '';
public $room = null;
public $userData = array();
/**
* @see \wcf\page\AbstractForm::$useTemplate
*/
public $useTemplate = false;
/**
* @see \wcf\page\AbstractPage::readData()
* @see \wcf\page\IPage::readData()
*/
public function readData() {
$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() {
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']);
}
/**
* @see \wcf\form\AbstractForm::validate()
* @see \wcf\form\IForm::validate()
*/
public function validate() {
parent::validate();
@ -55,10 +58,14 @@ class ChatForm extends AbstractForm {
if ($this->message === '') {
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() {
parent::save();
@ -107,4 +114,12 @@ class ChatForm extends AbstractForm {
$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;
/**
* @see \wcf\page\Page::readData()
* @see \wcf\page\Page::readData()
*/
public function readData() {
parent::readData();
$this->readRoom();
$this->readMessages();
$this->readUsers();
$this->users = $this->room->getUsers();
}
public function readMessages() {
@ -54,34 +54,6 @@ class ChatMessagePage extends AbstractPage {
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()
*/
@ -94,6 +66,11 @@ class ChatMessagePage extends AbstractPage {
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();
}
$json = array('users' => array(), 'messages' => array());
foreach ($this->messages as $message) {

View File

@ -105,25 +105,24 @@ class ChatPage extends AbstractPage {
public function readParameters() {
parent::readParameters();
if ($this->action == 'Message') {
new ChatMessagePage();
exit;
}
else if ($this->action == 'Log') {
//TODO: Initialise LogPage
exit;
}
else if ($this->action == 'RefreshRoomList') {
new ChatRefreshRoomListPage();
exit;
}
else if ($this->action == 'Send') {
new \wcf\form\ChatForm();
exit;
}
else if ($this->action == 'Copyright') {
new ChatCopyrightPage();
exit;
switch ($this->action) {
case 'Message':
new ChatMessagePage();
exit;
case 'Log':
exit;
case 'RefreshRoomList':
new ChatRefreshRoomListPage();
exit;
case 'Send':
new \wcf\form\ChatForm();
exit;
case 'Leave':
new \wcf\action\ChatLeaveAction();
exit;
case 'Copyright':
new ChatCopyrightPage();
exit;
}
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 {
/**
* @see wcf\system\cache\ICacheBuilder::getData()
* @see \wcf\system\cache\ICacheBuilder::getData()
*/
public function getData(array $cacheResource) {
// get all chat rooms

View File

@ -1,6 +1,7 @@
<?php
namespace wcf\system\chat\permissions;
namespace wcf\system\chat\permission;
use \wcf\system\acl\ACLHandler;
use \wcf\system\cache\CacheHandler;
use \wcf\system\package\PackageDependencyHandler;
use \wcf\system\WCF;
@ -15,14 +16,25 @@ use \wcf\system\WCF;
*/
class ChatPermissionHandler extends \wcf\system\SingletonFactory {
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() {
$packageID = PackageDependencyHandler::getPackageID('timwolla.wcf.chat');
$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
if (WCF::getUser()->userID) {
@ -75,7 +87,20 @@ class ChatPermissionHandler extends \wcf\system\SingletonFactory {
* @return boolean
*/
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];
}
/**
* 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
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package timwolla.wcf.chat
* @subpackage system.event.listener
* @subpackage system.event.listener
*/
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) {
$route = new \wcf\system\request\Route('chatAction');
$route->setSchema('/{controller}/{action}');
$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);
}
}

View File

@ -17,7 +17,7 @@ class ChatPageMenuItemProvider extends DefaultPageMenuItemProvider {
/**
* 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() {
// guests are not supported
@ -43,7 +43,7 @@ class ChatPageMenuItemProvider extends DefaultPageMenuItemProvider {
/**
* 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() {
return \wcf\system\request\LinkHandler::getInstance()->getLink('Chat', array(

View File

@ -12,14 +12,14 @@ namespace wcf\system\option;
*/
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) {
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) {
$tmp = (int) ($value / 60);

View File

@ -67,7 +67,7 @@ class ChatUtil {
if ($data[WCF::getUser()->userID] === null) {
switch ($field) {
case 'color':
$data[WCF::getUser()->userID] = array(1 => 0xFF0000, 2 => 0x00FF00);
$data[WCF::getUser()->userID] = array(1 => self::getRandomNumber(), 2 => self::getRandomNumber() * 0xFFFF);
break;
}
static::writeUserData(array($field => $data[WCF::getUser()->userID]));
@ -79,6 +79,16 @@ class ChatUtil {
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
*

View File

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

View File

@ -1,46 +1,47 @@
DROP TABLE IF EXISTS wcf1_chat_message;
CREATE TABLE wcf1_chat_message (
messageID int(10) NOT NULL AUTO_INCREMENT,
roomID int(10) NOT NULL,
sender int(10) DEFAULT NULL,
username varchar(255) DEFAULT NULL,
receiver int(10) DEFAULT NULL,
time int(10) NOT NULL,
type tinyint(3) NOT NULL DEFAULT 1,
message mediumtext NOT NULL,
enableSmilies tinyint(1) NOT NULL DEFAULT 1,
enableHTML tinyint(1) NOT NULL DEFAULT 0,
color1 int(10) NOT NULL DEFAULT 0,
color2 int(10) NOT NULL DEFAULT 0,
PRIMARY KEY (messageID),
KEY roomID (roomID),
KEY sender (sender),
KEY receiver (receiver)
messageID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
roomID INT(10) NOT NULL,
sender INT(10) DEFAULT NULL,
username VARCHAR(255) DEFAULT NULL,
receiver INT(10) DEFAULT NULL,
time INT(10) NOT NULL,
type TINYINT(3) NOT NULL DEFAULT 1,
message MEDIUMTEXT NOT NULL,
enableSmilies TINYINT(1) NOT NULL DEFAULT 1,
enableHTML TINYINT(1) NOT NULL DEFAULT 0,
color1 INT(10) NOT NULL DEFAULT 0,
color2 INT(10) NOT NULL DEFAULT 0,
KEY roomID (roomID),
KEY sender (sender),
KEY receiver (receiver)
);
DROP TABLE IF EXISTS wcf1_chat_room;
CREATE TABLE wcf1_chat_room (
roomID int(10) NOT NULL AUTO_INCREMENT,
title varchar(25) NOT NULL,
topic varchar(255) NOT NULL,
position int(10) NOT NULL DEFAULT 0,
permanent tinyint(1) NOT NULL DEFAULT 1,
owner int(10) DEFAULT NULL,
PRIMARY KEY (roomID),
KEY positionKey (position),
KEY owner (owner)
roomID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
topic VARCHAR(255) NOT NULL,
position INT(10) NOT NULL DEFAULT 0,
permanent TINYINT(1) NOT NULL DEFAULT 1,
owner INT(10) DEFAULT NULL,
KEY positionKey (position),
KEY owner (owner)
);
DROP TABLE IF EXISTS wcf1_chat_room_suspension;
CREATE TABLE wcf1_chat_room_suspension (
roomID int(10) NOT NULL,
userID int(10) NOT NULL,
type tinyint(3) NOT NULL,
time int(10) NOT NULL,
PRIMARY KEY (roomID, userID),
KEY userID (userID),
KEY type (type, time),
KEY time (time)
userID INT(10) NOT NULL,
roomID INT(10) DEFAULT NULL,
type TINYINT(3) NOT NULL,
time INT(10) NOT NULL,
UNIQUE KEY main (userID, roomID),
KEY roomID (roomID),
KEY type (type, time),
KEY time (time)
);
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 (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 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);

View File

@ -1,5 +1,17 @@
<?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">
<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">
<item name="wcf.acp.option.category.chat"><![CDATA[Chat]]></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_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_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"><![CDATA[Maximale Textlänge]]></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.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>
</category>
<category name="wcf.header">
<item name="wcf.header.menu.chat"><![CDATA[Chat]]></item>
</category>
<category name="wcf.chat">
<item name="wcf.chat.title"><![CDATA[Chat]]></item>
<item name="wcf.chat.protocol"><![CDATA[Protokoll]]></item>
@ -55,4 +63,14 @@
<!-- 2 = TYPE_LEAVE -->
<item name="wcf.chat.message.2"><![CDATA[hat den Chat verlassen.]]></item>
</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>

View File

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

View File

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

View File

@ -5,7 +5,7 @@
{include file='headInclude' sandbox=false}
<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 {
background-image: url("{link controller='Chat' action='Copyright' sheep=1}{/link}");
background-position: right 45px;
@ -56,6 +56,7 @@
.jsCounterInput {
height: 16px;
box-sizing: content-box !important;
}
.jsCounterInput, .jsCounter {
@ -89,103 +90,63 @@
</head>
<body id="tpl{$templateName|ucfirst}">
{capture assign='sidebar'}
<div id="sidebarContent" class="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="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}
{capture assign='sidebar'}{include file='chatSidebar'}{/capture}
{capture assign='headerNavigation'}{include file='chatNavigationInclude'}{/capture}
{include file='header' sandbox=false sidebarOrientation='right'}
<div id="timsChatRoomContent">
<div id="timsChatTopic" class="border"{if $room->topic|language === ''} style="display: none;"{/if}>{$room->topic|language}</div>
<div class="timsChatMessageContainer border content">
<div id="timsChatTopic" class="wcf-border"{if $room->topic|language === ''} style="display: none;"{/if}>{$room->topic|language}</div>
<div class="timsChatMessageContainer wcf-border wcf-content">
<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>
</div>
<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>
<div id="timsChatControls">
<div id="smileyList" class="border">
<ul class="smilies">
{foreach from=$smilies item='smiley'}
<li>
<img src="{$smiley->getURL()}" alt="{$smiley->smileyCode}" title="{$smiley->smileyCode}" class="smiley" />
</li>
{/foreach}
</ul>
</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>
<div id="timsChatControls" class="wcf-border">
{if MODULE_SMILEY}
<div id="smileyList">
<ul class="smilies">
{foreach from=$smilies item='smiley'}
<li>
<img src="{$smiley->getURL()}" alt="{$smiley->smileyCode}" title="{$smiley->smileyTitle}" class="jsSmiley jsTooltip" />
</li>
{/foreach}
</ul>
</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'}
</div>
</div>
@ -201,8 +162,7 @@
// populate config
TimWolla.WCF.Chat.config = {
reloadTime: {@CHAT_RELOADTIME},
animations: {@CHAT_ANIMATIONS},
maxTextLength: {@CHAT_LENGTH}
unloadURL: '{link controller='Chat' action='Leave'}{/link}'
}
WCF.Language.addObject({
'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="{@RELATIVE_WCF_DIR}js/jCounter.jQuery.js"></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="{@$__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><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>
<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>
{/if}
<li>
<a href="{link controller="Chat" action="Log"}{/link}" title="{lang}wcf.chat.protocol{/lang}" class="jsTooltip">
<img src="{icon size='S'}session1{/icon}" alt="" /> <span>{lang}wcf.chat.protocol{/lang}</span>
</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>