1
0
mirror of https://github.com/wbbaddons/Tims-Chat.git synced 2024-12-22 21:40:08 +00:00

Merge branch 'master' into commands

Conflicts:
	file/lib/data/chat/message/ChatMessage.class.php
This commit is contained in:
Tim Düsterhus 2012-01-22 14:10:10 +01:00
commit 94a48df147
19 changed files with 550 additions and 145 deletions

View File

@ -1,4 +0,0 @@
language: php
php:
- 5.3
script: php build.php

33
README.md Normal file
View File

@ -0,0 +1,33 @@
Tims Chat 3.0
=============
Tims Chat is a chat-plugin for WoltLab Community Framework.
Version notes
-------------
The currently available source code represents an early alpha-version of Tims Chat, even though it may be installable, we cannot guarantee a working installation at any time. You MUST NOT install and/or use Tims Chat 3.0 in a production environment.
Contribution
------------
Developers are always welcome to fork Tims Chat and provide features or bug fixes using pull requests. If you make changes or add classes it is mandatory to follow the requirements below:
* Testing is key, you MUST try out your changes before submitting pull requests
* You MUST save your files with Unix-style line endings (\n)
* You MUST NOT include the closing tag of a PHP block at the end of file, provide an empty newline instead
* You MUST use tabs for indentation
* Tab size of 8 is required
* Empty lines MUST be indented equal to previous line
* All comments within source code MUST be written in English language
* Use a sensible number of commits. In most cases one commit should be enough.
* Split huge changes into several logical groups
* Rebase small changes that consist of several commits into one commit
Follow the above conventions if you want your pull requests accepted.
License
-------
For licensing information refer to the LICENSE file in this folder.

82
build.php Normal file → Executable file
View File

@ -1,16 +1,74 @@
#!/usr/bin/env php
<?php
@unlink('file.tar');
@unlink('template.tar');
@unlink('timwolla.wcf.chat.tar');
exec('coffee -cb file/js/*.coffee');
/**
* Builds the 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
*/
echo <<<EOT
Cleaning up
-----------
EOT;
if (file_exists('file.tar')) unlink('file.tar');
if (file_exists('template.tar')) unlink('template.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');
echo <<<EOT
Building JavaScript
-------------------
EOT;
foreach (glob('file/js/*.coffee') as $coffeeFile) {
echo $coffeeFile."\n";
passthru('coffee -cb '.escapeshellarg($coffeeFile), $code);
if ($code != 0) exit($code);
}
echo <<<EOT
Building CSS
------------
EOT;
foreach (glob('file/style/*.scss') as $sassFile) {
echo $sassFile."\n";
passthru('scss '.escapeshellarg($sassFile).' '.escapeshellarg(substr($sassFile, 0, -4).'css'), $code);
if ($code != 0) exit($code);
}
echo <<<EOT
Building file.tar
-----------------
EOT;
chdir('file');
exec('tar cvf ../file.tar * --exclude=*.coffee');
passthru('tar cvf ../file.tar * --exclude=*.coffee --exclude=*.scss', $code);
if ($code != 0) exit($code);
echo <<<EOT
Building template.tar
---------------------
EOT;
chdir('../template');
passthru('tar cvf ../template.tar *', $code);
if ($code != 0) exit($code);
echo <<<EOT
Building timwolla.wcf.chat.tar
------------------------------
EOT;
chdir('..');
chdir('template');
exec('tar cvf ../template.tar *');
chdir('..');
exec('tar cvf timwolla.wcf.chat.tar * --exclude=file --exclude=template --exclude=build.php');
@unlink('file.tar');
@unlink('template.tar');
exec('rm file/js/*.js');
passthru('tar cvf timwolla.wcf.chat.tar * --exclude=file --exclude=template --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');
foreach (glob('file/js/*.js') as $jsFile) unlink($jsFile);
foreach (glob('file/style/*.css') as $cssFile) unlink($cssFile);

View File

@ -10,39 +10,53 @@
TimWolla ?= {}
TimWolla.WCF ?= {}
(($) ->
(($, window) ->
TimWolla.WCF.Chat =
titleTemplate: null
messageTemplate: null
newMessageCount: null
events:
newMessage: $.Callbacks()
userMenu: $.Callbacks()
init: () ->
console.log('[TimWolla.WCF.Chat] Initializing');
@bindEvents()
@refreshRoomList()
new WCF.PeriodicalExecuter $.proxy(@refreshRoomList, this), 60e3
new WCF.PeriodicalExecuter $.proxy(@getMessages, this), @config.reloadTime * 1000
@events.newMessage.add $.proxy @notify, @
$('#chatInput').focus()
new WCF.PeriodicalExecuter $.proxy(@refreshRoomList, @), 60e3
new WCF.PeriodicalExecuter $.proxy(@getMessages, @), @config.reloadTime * 1000
@refreshRoomList()
@getMessages()
console.log '[TimWolla.WCF.Chat] Finished initializing'
###
# Binds all the events needed for Tims Chat.
###
bindEvents: () ->
@isActive = true
$(window).focus $.proxy () ->
document.title = @titleTemplate.fetch({ title: $('#chatRoomList .activeMenuItem a').text() })
@newMessageCount = 0
@isActive = true
, @
$(window).blur $.proxy () ->
@isActive = false
, @
$('.smiley').click $.proxy (event) ->
@insertText ' ' + $(event.target).attr('alt') + ' '
, this
, @
$('.chatSidebarTabs li').click $.proxy (event) ->
event.preventDefault()
@toggleSidebarContents $ event.target
, this
$('.chatUser .chatUserLink').click $.proxy (event) ->
event.preventDefault()
@toggleUserMenu $ event.target
, this
, @
$('#chatForm').submit $.proxy (event) ->
event.preventDefault()
@submit $ event.target
, this
, @
$('#chatClear').click (event) ->
event.preventDefault()
@ -50,7 +64,7 @@ TimWolla.WCF ?= {}
$('#chatInput').focus()
$('.chatToggle').click (event) ->
element = $ this
element = $ @
icon = element.find 'img'
if element.data('status') is 1
element.data 'status', 0
@ -60,6 +74,10 @@ TimWolla.WCF ?= {}
element.data 'status', 1
icon.attr 'src', icon.attr('src').replace /disabled(\d?).([a-z]{3})$/, 'enabled$1.$2'
element.attr 'title', element.data 'disableMessage'
if typeof window.webkitNotifications isnt 'undefined'
$('#chatNotify').click (event) ->
window.webkitNotifications.requestPermission()
###
# Changes the chat-room.
#
@ -86,13 +104,14 @@ TimWolla.WCF ?= {}
return if $('#topic').text().trim() is ''
$('#topic').wcfBlindOut 'vertical', () ->
$(this).text ''
$(@).text ''
else
$('#topic').text data.topic
$('#topic').wcfBlindIn() if $('#topic').text().trim() isnt ''
$('#topic').wcfBlindIn() if $('#topic').text().trim() isnt '' and $('#topic').is(':hidden')
$('title').text @titleTemplate.fetch(data)
, this)
@getMessages()
, @)
error: () ->
# reload page to change the room the old fashion-way
# inclusive the error-message :)
@ -102,12 +121,13 @@ TimWolla.WCF ?= {}
@loading = true
target.parent().addClass 'ajaxLoad'
, this)
, @)
###
# Frees the fish
###
freeTheFish: () ->
return if $.wcfIsset('fish')
console.warn '[TimWolla.WCF.Chat] Freeing the fish'
fish = $ '<div id="fish">' + WCF.String.escapeHTML('><((((°>') + '</div>'
fish.css
position: 'absolute'
@ -120,15 +140,20 @@ TimWolla.WCF ?= {}
fish.appendTo $ 'body'
new WCF.PeriodicalExecuter(() ->
left = (Math.random() * 100 - 50)
top = (Math.random() * 100 - 50)
fish = $('#fish')
$('#fish').text('><((((°>') if (left > 0)
$('#fish').text('<°))))><') if (left < 0)
left *= -1 if((fish.position().left + left) < (0 + fish.width()) or (fish.position().left + left) > ($(document).width() - fish.width()))
top *= -1 if((fish.position().top + top) < (0 + fish.height()) or (fish.position().top + top) > ($(document).height() - fish.height()))
$('#fish').animate
top: '+=' + (Math.random() * 100 - 50)
fish.text('><((((°>') if (left > 0)
fish.text('<°))))><') if (left < 0)
fish.animate
top: '+=' + top
left: '+=' + left
, 1000
, 3e3);
, 1.5e3);
###
# Loads new messages.
###
@ -138,7 +163,8 @@ TimWolla.WCF ?= {}
type: 'POST'
success: $.proxy((data, textStatus, jqXHR) ->
@handleMessages(data.messages)
, this)
@handleUsers(data.users)
, @)
###
# Inserts the new messages.
#
@ -146,6 +172,8 @@ TimWolla.WCF ?= {}
###
handleMessages: (messages) ->
for message in messages
@events.newMessage.fire message
output = @messageTemplate.fetch message
li = $ '<li></li>'
li.addClass 'chatMessage chatMessage'+message.type
@ -156,6 +184,43 @@ TimWolla.WCF ?= {}
$('.chatMessageContainer').animate
scrollTop: $('.chatMessageContainer ul').height()
, 1000
handleUsers: (users) ->
foundUsers = {}
for user in users
id = 'chatUser-'+user.userID
element = $('#'+id)
if element[0]
console.log '[TimWolla.WCF.Chat] Shifting user ' + user.userID
element = element.detach()
$('#chatUserList').append element
else
console.log '[TimWolla.WCF.Chat] Inserting user ' + user.userID
li = $ '<li></li>'
li.attr 'id', id
li.addClass 'chatUser'
a = $ '<a href="javascript:;">'+user.username+'</a>'
a.click $.proxy (event) ->
event.preventDefault()
@toggleUserMenu $ event.target
, @
li.append a
menu = $ '<ul></ul>'
menu.addClass 'chatUserMenu'
menu.append $ '<li><a href="javascript:;">' + WCF.Language.get('wcf.chat.query') + '</a></li>'
menu.append $ '<li><a href="javascript:;">' + WCF.Language.get('wcf.chat.kick') + '</a></li>'
menu.append $ '<li><a href="javascript:;">' + WCF.Language.get('wcf.chat.ban') + '</a></li>'
menu.append $ '<li><a href="index.php/User/' + user.userID + '">' + WCF.Language.get('wcf.chat.profile') + '</a></li>'
@events.userMenu.fire user, menu
li.append menu
li.appendTo $ '#chatUserList'
foundUsers[id] = true
$('.chatUser').each () ->
if typeof foundUsers[$(@).attr('id')] is 'undefined'
$(@).remove()
$('#toggleUsers .badge').text(users.length);
###
# Inserts text into our input.
#
@ -177,29 +242,52 @@ TimWolla.WCF ?= {}
else
$('#chatInput').focus()
###
# Sends a notification about a message.
#
# @param object message
###
notify: (message) ->
return if (@isActive or $('#chatNotify').data('status') is 0)
@newMessageCount++
document.title = '(' + @newMessageCount + ') ' + @titleTemplate.fetch({ title: $('#chatRoomList .activeMenuItem a').text() })
if typeof window.webkitNotifications isnt 'undefined'
if window.webkitNotifications.checkPermission() is 0
notification = window.webkitNotifications.createNotification WCF.Icon.get('timwolla.wcf.chat.chat'), WCF.Language.get('wcf.chat.newMessages'), message.username + ' ' + message.message
notification.show()
setTimeout(() ->
notification.cancel()
, 5000)
###
# Refreshes the room-list.
###
refreshRoomList: () ->
console.log '[TimWolla.WCF.Chat] Refreshing the room-list'
$('#toggleRooms a').addClass 'ajaxLoad'
$.ajax $('#toggleRooms a').data('refreshUrl'),
dataType: 'json'
type: 'POST'
success: $.proxy((data, textStatus, jqXHR) ->
$('.chatRoom').unbind 'click'
$('#chatRoomList li').remove()
$('#toggleRooms a').removeClass 'ajaxLoad'
$('#toggleRooms .badge').text(data.length);
for room in data
li = $ '<li></li>'
li.addClass 'activeMenuItem' if room.active
$('<a href="' + room.link + '">' + room.title + '</a>').addClass('chatRoom').appendTo li
$('#chatRoomList ul').append li
$('.chatRoom').click $.proxy (event) ->
return if typeof window.history.replaceState is 'undefined'
event.preventDefault()
@changeRoom $ event.target
, this
, this)
, @
console.log '[TimWolla.WCF.Chat] Found ' + data.length + ' rooms'
, @)
###
# Handles submitting of messages.
#
@ -222,7 +310,7 @@ TimWolla.WCF ?= {}
@getMessages()
$('#chatInput').val('').focus()
$('#chatInput').keyup()
, this)
, @)
complete: () ->
$('#chatInput').removeClass 'ajaxLoad'
###
@ -231,15 +319,15 @@ TimWolla.WCF ?= {}
# @param jQuery-object target
###
toggleSidebarContents: (target) ->
return if target.parent().hasClass 'active'
return if target.parents('li').hasClass 'active'
if target.parent().attr('id') is 'toggleUsers'
if target.parents('li').attr('id') is 'toggleUsers'
$('#toggleUsers').addClass 'active'
$('#toggleRooms').removeClass 'active'
$('#chatRoomList').hide()
$('#chatUserList').show()
else if target.parent().attr('id') is 'toggleRooms'
else if target.parents('li').attr('id') is 'toggleRooms'
$('#toggleRooms').addClass 'active'
$('#toggleUsers').removeClass 'active'
@ -251,12 +339,12 @@ TimWolla.WCF ?= {}
# @param jQuery-object target
###
toggleUserMenu: (target) ->
liUserID = '#' + target.parent().parent().attr 'id'
li = target.parent()
if $(liUserID).hasClass 'activeMenuItem'
$(liUserID + ' .chatUserMenu').wcfBlindOut 'vertical', () ->
$(liUserID).removeClass 'activeMenuItem'
if li.hasClass 'activeMenuItem'
li.find('.chatUserMenu').wcfBlindOut 'vertical', () ->
li.removeClass 'activeMenuItem'
else
$(liUserID).addClass 'activeMenuItem'
$(liUserID + ' .chatUserMenu').wcfBlindIn()
)(jQuery)
li.addClass 'activeMenuItem'
li.find('.chatUserMenu').wcfBlindIn 'vertical'
)(jQuery, @)

View File

@ -48,7 +48,7 @@ public function __toString() {
*
* @return string
*/
public function getFormattedMessage() {
public function getFormattedMessage($outputType = 'text/html') {
$message = $this->message;
switch ($this->type) {
case self::TYPE_JOIN:
@ -59,7 +59,7 @@ public function getFormattedMessage() {
case self::TYPE_NORMAL:
case self::TYPE_ME:
case self::TYPE_WHISPER:
if (!$this->enableHTML) {
if (!$this->enableHTML && $outputType == 'text/html') {
$message = \wcf\system\bbcode\SimpleMessageParser::getInstance()->parse($message, true, $this->enableSmilies);
}
break;
@ -104,6 +104,7 @@ public function jsonify($raw = false) {
'formattedMessage' => (string) $this,
'formattedTime' => \wcf\util\DateUtil::format(\wcf\util\DateUtil::getDateTimeByTimestamp($this->time), 'H:i:s'),
'separator' => ($this->type == self::TYPE_NORMAL) ? ': ' : ' ',
'message' => $this->getFormattedMessage('text/plain'),
'sender' => $this->sender,
'username' => $this->getUsername(),
'time' => $this->time,

View File

@ -0,0 +1,101 @@
<?php
namespace wcf\page;
use \wcf\system\WCF;
/**
* Shows information about Tims chat.
*
* @author Tim Düsterhus
* @copyright 2010-2011 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package timwolla.wcf.chat
* @subpackage page
*/
class ChatCopyrightPage extends AbstractPage {
public $neededModules = array('CHAT_ACTIVE');
//public $neededPermissions = array('user.chat.canEnter');
/**
* @see \wcf\page\IPage::readParameters()
*/
public function readParameters() {
// ###
// ## ##
// # #
// # ##### #
// # # # #
// # # * * # #
// # # # #
// # # # #
// # ### #
// # #
// #######
// # # # #
// # # # #
// # # # #
if (isset($_GET['sheep'])) $this->useTemplate = false;
}
/**
* @see \wcf\page\IPage::show()
*/
public function show() {
// guests are not supported
if (!WCF::getUser()->userID) {
throw new \wcf\system\exception\PermissionDeniedException();
}
parent::show();
if ($this->useTemplate) exit;
@header('Content-type: image/png');
\wcf\util\HeaderUtil::sendNoCacheHeaders();
$images = explode("\n\n", file_get_contents(__FILE__, null, null, __COMPILER_HALT_OFFSET__+2));
echo base64_decode($images[array_rand($images)]);
exit;
}
}
__halt_compiler();/*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
TnGiAAAAAElFTkSuQmCC

View File

@ -18,24 +18,68 @@ class ChatMessagePage extends AbstractPage {
//public $neededPermissions = array('user.chat.canEnter');
public $room = null;
public $roomID = 0;
public $users = array();
public $useTemplate = false;
/**
* Reads room data.
* @see \wcf\page\Page::readData()
*/
public function readData() {
parent::readData();
$this->readRoom();
$this->readMessages();
$this->readUsers();
}
public function readMessages() {
$this->messages = chat\message\ChatMessageList::getMessagesSince($this->room, \wcf\util\ChatUtil::readUserData('lastSeen'));
// update last seen message
$sql = "SELECT
max(messageID) as messageID
FROM
wcf".WCF_N."_chat_message";
$stmt = WCF::getDB()->prepareStatement($sql);
$stmt->execute();
$row = $stmt->fetchArray();
\wcf\util\ChatUtil::writeUserData(array('lastSeen' => $row['messageID']));
}
public function readRoom() {
$this->roomID = \wcf\util\ChatUtil::readUserData('roomID');
$this->room = chat\room\ChatRoom::getCache()->search($this->roomID);
if (!$this->room) throw new \wcf\system\exception\IllegalLinkException();
if (!$this->room->canEnter()) throw new \wcf\system\exception\PermissionDeniedException();
}
$this->messages = chat\message\ChatMessageList::getMessagesSince($this->room, \wcf\util\ChatUtil::readUserData('lastSeen'));
$stmt = WCF::getDB()->prepareStatement("SELECT max(messageID) as messageID FROM wcf".WCF_N."_chat_message");
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();
$row = $stmt->fetchArray();
\wcf\util\ChatUtil::writeUserData(array('lastSeen' => $row['messageID']));
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');
}
/**
@ -55,6 +99,13 @@ public function show() {
foreach ($this->messages as $message) {
$json['messages'][] = $message->jsonify(true);
}
foreach ($this->users as $user) {
$json['users'][] = array(
'userID' => $user->userID,
'username' => $user->username
);
}
echo \wcf\util\JSON::encode($json);
exit;
}

View File

@ -66,6 +66,8 @@ public function readData() {
$this->readRoom();
$this->userData['color'] = \wcf\util\ChatUtil::readUserData('color');
\wcf\util\ChatUtil::writeUserData(array('roomID' => $this->room->roomID));
$this->newestMessages = chat\message\ChatMessageList::getNewestMessages($this->room, CHAT_LASTMESSAGES);
\wcf\util\ChatUtil::writeUserData(array('lastSeen' => count($this->newestMessages) ? end($this->newestMessages)->messageID : 0));
if (CHAT_DISPLAY_JOIN_LEAVE) {
$messageAction = new chat\message\ChatMessageAction(array(), 'create', array(
@ -82,14 +84,10 @@ public function readData() {
));
$messageAction->executeAction();
$return = $messageAction->getReturnValues();
\wcf\util\ChatUtil::writeUserData(array('lastSeen' => $return['returnValues'] -> messageID));
}
$this->readDefaultSmileys();
$this->readChatVersion();
$this->newestMessages = chat\message\ChatMessageList::getNewestMessages($this->room, CHAT_LASTMESSAGES);
}
/**
@ -122,6 +120,10 @@ public function readParameters() {
new \wcf\form\ChatForm();
exit;
}
else if ($this->action == 'Copyright') {
new ChatCopyrightPage();
exit;
}
if (isset($_REQUEST['id'])) $this->roomID = (int) $_REQUEST['id'];
if (isset($_REQUEST['ajax'])) $this->useTemplate = false;

View File

@ -18,7 +18,7 @@ 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)');
$route->setParameterOption('action', null, '(Message|Log|Send|RefreshRoomList|Copyright)');
$eventObj->addRoute($route);
}
}

View File

@ -29,7 +29,7 @@ public function isVisible() {
do {
$cache->seek($i++);
$this->room = $cache->search($cache->key());
$this->room = $cache->current();
}
while (!$this->room->canEnter());

View File

@ -1,20 +1,18 @@
#chatBox {
padding: 0;
/**
* Chat-Styles
*
* @author Tim Düsterhus, Maximilian Mader
* @copyright 2010-2011 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package timwolla.wcf.chat
*/
div {
text-align: center;
}
}
#chatBox aside, #chatRoomContent {
#chatRoomContent {
text-align: left;
}
.sidebar {
margin-bottom: -20px !important;
}
aside {
overflow: auto;
padding: 0;
@ -32,18 +30,8 @@ aside {
}
}
}
& aside {
padding-right: 1px;
}
& aside {
padding-left: 1px;
}
}
#topic, #smileyList, #chatOptions {
padding: 5px;
}
@ -51,6 +39,7 @@ #topic, #smileyList, #chatOptions {
.chatMessageContainer {
height: 200px;
overflow-y: scroll;
overflow-x: hidden;
padding-left: 7px !important;
}
@ -196,6 +185,18 @@ .chatSidebarTabs {
margin-right: -1px;
}
}
&.active .badge {
font-size: 65% !important;
color: #fff;
background-color: #369;
-webkit-box-shadow: 0 0 1px rgba(255, 255, 255, 1);
-moz-box-shadow: 0 0 1px rgba(255, 255, 255, 1);
-ms-box-shadow: 0 0 1px rgba(255, 255, 255, 1);
-o-box-shadow: 0 0 1px rgba(255, 255, 255, 1);
box-shadow: 0 0 1px rgba(255, 255, 255, 1);
}
}
}
}
@ -209,3 +210,37 @@ #sidebarContainer {
height: 420px;
width: 100%;
}
#sidebarContainer a {
outline: none;
}
#chatCopyright {
bottom: 5px;
position: absolute;
}
.sidebarContent {
> div {
ul {
> li {
margin-top: 5px;
background-color: transparent !important;
box-shadow: none !important;
}
}
}
}
.sidebarContent {
> div {
ul {
> li.activeMenuItem {
> a {
background-color: #FFFFFF;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
}
}
}
}
}

View File

@ -51,3 +51,8 @@ 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 ('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

@ -34,7 +34,7 @@
<item name="wcf.chat.protocol"><![CDATA[Protokoll]]></item>
<item name="wcf.chat.rooms"><![CDATA[Räume]]></item>
<item name="wcf.chat.users"><![CDATA[Nutzer]]></item>
<item name="wcf.chat.copyright"><![CDATA[<a href="http://timwolla.wbbaddons.de">Chat{if CHAT_SHOW_VERSION} Version {$chatVersion}{/if} entwickelt von TimWolla</a>]]></item>
<item name="wcf.chat.copyright"><![CDATA[<a href="http://timwolla.wbbaddons.de">Chat: <strong>Tims Chat</strong>{if CHAT_SHOW_VERSION} {$chatVersion}{/if}, entwickelt von <strong>TimWolla</strong></a>]]></item>
<item name="wcf.chat.submit.default"><![CDATA[Zum Senden Enter drücken]]></item>
<item name="wcf.chat.scroll"><![CDATA[Scrollen]]></item>

View File

@ -28,9 +28,9 @@
<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="pageMenu">pagemenu.xml</instruction>
<instruction type="eventListener">eventListener.xml</instruction>
<instruction type="templatelistener">templatelistener.xml</instruction>
<instruction type="templateListener">templatelistener.xml</instruction>
<instruction type="aclOption">acloptions.xml</instruction>
</instructions>
@ -40,9 +40,9 @@
<instruction type="template">template.tar</instruction>
<instruction type="objectType">objectType.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="templatelistener">templatelistener.xml</instruction>
<instruction type="templateListener">templatelistener.xml</instruction>
<instruction type="aclOption">acloptions.xml</instruction>
</instructions>
</package>

2
pagemenu.xml Normal file → Executable file
View File

@ -1,5 +1,5 @@
<?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/pagemnu.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/pagemenu.xsd">
<import>
<pagemenuitem name="wcf.header.menu.chat">
<link>index.php/Chat</link>

68
template/chat.tpl Normal file → Executable file
View File

@ -6,11 +6,19 @@
{include file='headInclude' sandbox=false}
<style type="text/css">
@import url("{@RELATIVE_WCF_DIR}style/timwolla.wcf.chat.css");
#chatUserList > li > .bgFix a {
#chatCopyrightDialog {
background-image: url("{link controller='Chat' action='Copyright' sheep=1}{/link}");
background-position: right 45px;
background-repeat: no-repeat;
min-height: 50%;
}
#chatUserList > li > a {
background-image: url({icon size='S'}arrowRight{/icon});
background-position: 15px center;
background-repeat: no-repeat;
}
#chatUserList > li.activeMenuItem > .bgFix a {
#chatUserList > li.activeMenuItem > a {
background-image: url({icon size='S'}arrowDown{/icon});
}
@ -76,19 +84,19 @@
<body id="tpl{$templateName|ucfirst}">
{capture assign='sidebar'}
<div id="sidebarMenu">
<div id="sidebarContent" class="sidebarContent">
<nav class="chatSidebarTabs">
<ul>
<li id="toggleUsers" class="active"><a href="javascript:;" title="{lang}wcf.chat.users{/lang}">{lang}wcf.chat.users{/lang}</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}</a></li>
<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="chatUserList">
{section name=user start=1 loop=26}
<li id="user-{$user}" class="chatUser">
<span class="bgFix"><a class="chatUserLink" href="javascript:;">User {$user}</a></span>
{*section name=user start=1 loop=26}
<li class="chatUser">
<a href="javascript:;">User {$user}</a>
<ul class="chatUserMenu">
<li>
<a href="javascript:;">{lang}wcf.chat.query{/lang}</a>
@ -98,7 +106,7 @@
</li>
</ul>
</li>
{/section}
{/section*}
</ul>
<nav id="chatRoomList" class="sidebarMenu" style="display: none;">
<div>
@ -116,16 +124,18 @@
</div>
</div>
{/capture}
{include file='header' sandbox=false sidebarDirection='right'}
{include file='header' sandbox=false sidebarOrientation='right'}
<div id="chatRoomContent">
<div id="topic" class="border"{if $room->topic|language === ''} style="display: none;"{/if}>{$room->topic|language}</div>
<div class="chatMessageContainer border content">
<ul></ul>
<ul>
<noscript><li class="error">{lang}wcf.chat.noJs{/lang}</li></noscript>
</ul>
</div>
<form id="chatForm" action="{link controller="Chat" action="Send"}{/link}" method="post">
<input type="text" id="chatInput" class="inputText long counterInput" name="text" autocomplete="off" maxlength="{CHAT_LENGTH}" required="required" placeholder="{lang}wcf.chat.submit.default{/lang}" />
<input type="text" id="chatInput" class="inputText long counterInput" name="text" autocomplete="off" maxlength="{CHAT_LENGTH}" disabled="disabled" required="required" placeholder="{lang}wcf.chat.submit.default{/lang}" />
</form>
<div id="chatControls">
@ -169,27 +179,59 @@
</ul>
</div>
</div>
{include file='chatCopyright'}
</div>
</div>
{include file='chatJavascriptInclude'}
<script type="text/javascript">
//<![CDATA[
(function ($, window) {
// populate templates
TimWolla.WCF.Chat.titleTemplate = new WCF.Template('{ldelim}$title} - {'wcf.chat.title'|language|encodeJS} - {PAGE_TITLE|language|encodeJS}');
{capture assign='chatMessageTemplate'}{include file='chatMessage'}{/capture}
TimWolla.WCF.Chat.messageTemplate = new WCF.Template('{@$chatMessageTemplate|encodeJS}');
// populate config
TimWolla.WCF.Chat.config = {
reloadTime: {CHAT_RELOADTIME},
animations: {CHAT_ANIMATIONS},
maxTextLength: {CHAT_LENGTH}
}
WCF.Language.addObject({
'wcf.chat.query': '{lang}wcf.chat.query{/lang}',
'wcf.chat.kick': '{lang}wcf.chat.kick{/lang}',
'wcf.chat.ban': '{lang}wcf.chat.ban{/lang}',
'wcf.chat.profile': '{lang}wcf.chat.profile{/lang}',
'wcf.chat.newMessages': '{lang}wcf.chat.newMessages{/lang}'
});
WCF.Icon.addObject({
'timwolla.wcf.chat.chat': '{icon size='L'}chat1{/icon}'
});
{event name='shouldInit'}
// Boot the chat
TimWolla.WCF.Chat.init();
{event name='didInit'}
// show the last X messages
TimWolla.WCF.Chat.handleMessages([
{implode from=$newestMessages item='message'}
{@$message->jsonify()}
{/implode}
]);
$('#chatInput').jCounter();
// enable user-interface
$('#chatInput').enable().jCounter().focus();
$('#chatControls .copyright').click(function (event) {
event.preventDefault();
if ($.wcfIsset('chatCopyrightDialog')) return WCF.showDialog('chatCopyrightDialog', true, { title: 'Tims Chat{if CHAT_SHOW_VERSION} {$chatVersion}{/if}' });
var container = $('<div id="chatCopyrightDialog"></div>');
container.load('{link controller='Chat' action='Copyright'}{/link}', function() {
$('body').append(container);
WCF.showDialog('chatCopyrightDialog', true, { title: 'Tims Chat{if CHAT_SHOW_VERSION} {$chatVersion}{/if}' });
});
});
})(jQuery, this)
//]]>
</script>

View File

@ -1 +1,8 @@
{if $templateName == 'chat'}<address class="copyright">{lang}wcf.chat.copyright{/lang}</address>{/if}
{if $templateName == 'chat'}<address id="chatCopyright" class="copyright">{lang}wcf.chat.copyright{/lang}</address>
{elseif $templateName == 'chatCopyright'}
<dl><dt>Project-Leader</dt><dd><ul><li><a href="http://timwolla.wbbaddons.de/">Tim Düsterhus</a></li></ul></dd></dl>
<dl><dt>Developer</dt><dd><ul><li><a href="http://timwolla.wbbaddons.de/">Tim Düsterhus</a></li><li><a href="https://github.com/max-m">Maximilian Mader</a></li></ul></dd></dl>
<dl><dt>Graphics</dt><dd><ul><li>Tom</li></ul></dd></dl>
<dl><dt>Translation</dt><dd><ul><li>Riccardo Vianello (it)</li></ul></dd></dl>
<dl><dt>Thanks</dt><dd><ul><li>-noone-</li><li>Alexander Ebert</li><li>Gabi</li><li>Stefan Hahn</li><li><a href="http://packageforge.de">Oliver Kliebisch</a></li><li>Christian Kubandt</li><li><a href="http://www.wbbaddons.de">Martin Schwendowius</a></li></ul></dd></dl>
{/if}

View File

@ -1,4 +1,2 @@
{if $templateName == 'chat'}
<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>
{/if}

View File

@ -1,18 +1,6 @@
<?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="chatJavascriptInclude">
<environment>user</environment>
<templatename>headInclude</templatename>
<eventname>javascriptInclude</eventname>
<templatecode><![CDATA[{include file='chatJavascriptInclude'}]]></templatecode>
</templatelistener>
<templatelistener name="chatCopyright">
<environment>user</environment>
<templatename>footer</templatename>
<eventname>copyright</eventname>
<templatecode><![CDATA[{include file='chatCopyright'}]]></templatecode>
</templatelistener>
<templatelistener name="chatHeaderNavigation">
<environment>user</environment>
<templatename>header</templatename>