mirror of
https://github.com/wbbaddons/Tims-Chat.git
synced 2024-12-21 21:30:08 +00:00
Merge branch 'attachment'
This commit is contained in:
commit
b49ac97e10
@ -1,11 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010-2018 Tim Düsterhus.
|
* Copyright (c) 2010-2020 Tim Düsterhus.
|
||||||
*
|
*
|
||||||
* Use of this software is governed by the Business Source License
|
* Use of this software is governed by the Business Source License
|
||||||
* included in the LICENSE file.
|
* included in the LICENSE file.
|
||||||
*
|
*
|
||||||
* Change Date: 2024-10-20
|
* Change Date: 2024-11-01
|
||||||
*
|
*
|
||||||
* On the date above, in accordance with the Business Source
|
* On the date above, in accordance with the Business Source
|
||||||
* License, use of this software will be governed by version 2
|
* License, use of this software will be governed by version 2
|
||||||
@ -17,6 +17,7 @@
|
|||||||
use \chat\data\command\CommandCache;
|
use \chat\data\command\CommandCache;
|
||||||
use \chat\data\room\RoomCache;
|
use \chat\data\room\RoomCache;
|
||||||
use \wcf\data\object\type\ObjectTypeCache;
|
use \wcf\data\object\type\ObjectTypeCache;
|
||||||
|
use \wcf\system\attachment\AttachmentHandler;
|
||||||
use \wcf\system\exception\PermissionDeniedException;
|
use \wcf\system\exception\PermissionDeniedException;
|
||||||
use \wcf\system\exception\UserInputException;
|
use \wcf\system\exception\UserInputException;
|
||||||
use \wcf\system\user\activity\point\UserActivityPointHandler;
|
use \wcf\system\user\activity\point\UserActivityPointHandler;
|
||||||
@ -304,4 +305,66 @@ public function push() {
|
|||||||
$processor->validate($this->parameters['parameters'], $room);
|
$processor->validate($this->parameters['parameters'], $room);
|
||||||
$processor->execute($this->parameters['parameters'], $room);
|
$processor->execute($this->parameters['parameters'], $room);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates parameters and permissions.
|
||||||
|
*/
|
||||||
|
public function validatePushAttachment() {
|
||||||
|
$this->readInteger('roomID');
|
||||||
|
|
||||||
|
$room = RoomCache::getInstance()->getRoom($this->parameters['roomID']);
|
||||||
|
if ($room === null) throw new UserInputException('roomID');
|
||||||
|
if (!$room->canSee($user = null, $reason)) throw $reason;
|
||||||
|
$user = new \chat\data\user\User(WCF::getUser());
|
||||||
|
if (!$user->isInRoom($room)) throw new PermissionDeniedException();
|
||||||
|
|
||||||
|
$this->readString('tmpHash');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes a new attachment into the given room.
|
||||||
|
*/
|
||||||
|
public function pushAttachment() {
|
||||||
|
$objectTypeID = ObjectTypeCache::getInstance()->getObjectTypeIDByName('be.bastelstu.chat.messageType', 'be.bastelstu.chat.messageType.attachment');
|
||||||
|
if (!$objectTypeID) throw new \LogicException('Missing object type');
|
||||||
|
|
||||||
|
$room = RoomCache::getInstance()->getRoom($this->parameters['roomID']);
|
||||||
|
if ($room === null) throw new UserInputException('roomID');
|
||||||
|
|
||||||
|
$attachmentHandler = new AttachmentHandler('be.bastelstu.chat.message', 0, $this->parameters['tmpHash'], $room->roomID);
|
||||||
|
$attachments = $attachmentHandler->getAttachmentList();
|
||||||
|
$attachmentIDs = [];
|
||||||
|
foreach ($attachments as $attachment) {
|
||||||
|
$attachmentIDs[] = $attachment->attachmentID;
|
||||||
|
}
|
||||||
|
|
||||||
|
$processor = new \wcf\system\html\input\HtmlInputProcessor();
|
||||||
|
$processor->process(implode(' ', array_map(function ($attachmentID) {
|
||||||
|
return '[attach='.$attachmentID.',none,true][/attach]';
|
||||||
|
}, $attachmentIDs)), 'be.bastelstu.chat.message', 0);
|
||||||
|
|
||||||
|
WCF::getDB()->beginTransaction();
|
||||||
|
/** @var Message $message */
|
||||||
|
$message = (new MessageAction([ ], 'create', [ 'data' => [ 'roomID' => $room->roomID
|
||||||
|
, 'userID' => WCF::getUser()->userID
|
||||||
|
, 'username' => WCF::getUser()->username
|
||||||
|
, 'time' => TIME_NOW
|
||||||
|
, 'objectTypeID' => $objectTypeID
|
||||||
|
, 'payload' => serialize([ 'attachmentIDs' => $attachmentIDs
|
||||||
|
, 'message' => $processor->getHtml()
|
||||||
|
])
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)->executeAction()['returnValues'];
|
||||||
|
|
||||||
|
$attachmentHandler->updateObjectID($message->messageID);
|
||||||
|
$processor->setObjectID($message->messageID);
|
||||||
|
if (\wcf\system\message\embedded\object\MessageEmbeddedObjectManager::getInstance()->registerObjects($processor)) {
|
||||||
|
(new MessageEditor($message))->update([
|
||||||
|
'hasEmbeddedObjects' => 1
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
WCF::getDB()->commitTransaction();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010-2018 Tim Düsterhus.
|
* Copyright (c) 2010-2020 Tim Düsterhus.
|
||||||
*
|
*
|
||||||
* Use of this software is governed by the Business Source License
|
* Use of this software is governed by the Business Source License
|
||||||
* included in the LICENSE file.
|
* included in the LICENSE file.
|
||||||
*
|
*
|
||||||
* Change Date: 2024-10-20
|
* Change Date: 2024-11-01
|
||||||
*
|
*
|
||||||
* On the date above, in accordance with the Business Source
|
* On the date above, in accordance with the Business Source
|
||||||
* License, use of this software will be governed by version 2
|
* License, use of this software will be governed by version 2
|
||||||
@ -14,6 +14,9 @@
|
|||||||
|
|
||||||
namespace chat\data\message;
|
namespace chat\data\message;
|
||||||
|
|
||||||
|
use \wcf\system\attachment\AttachmentHandler;
|
||||||
|
use \wcf\system\WCF;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a chat message editor.
|
* Represents a chat message editor.
|
||||||
*/
|
*/
|
||||||
@ -22,4 +25,20 @@ class MessageEditor extends \wcf\data\DatabaseObjectEditor {
|
|||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
protected static $baseClass = Message::class;
|
protected static $baseClass = Message::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public static function deleteAll(array $messageIDs = []) {
|
||||||
|
WCF::getDB()->beginTransaction();
|
||||||
|
|
||||||
|
$result = parent::deleteAll($messageIDs);
|
||||||
|
if (!empty($messageIDs)) {
|
||||||
|
AttachmentHandler::removeAttachments('be.bastelstu.chat.message', $messageIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
WCF::getDB()->commitTransaction();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,13 @@
|
|||||||
class RoomPage extends \wcf\page\AbstractPage {
|
class RoomPage extends \wcf\page\AbstractPage {
|
||||||
use TConfiguredPage;
|
use TConfiguredPage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Almost dummy attachment handler (used in language variable)
|
||||||
|
*
|
||||||
|
* @var \wcf\system\attachment\AttachmentHandler
|
||||||
|
*/
|
||||||
|
public $attachmentHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@ -91,6 +98,9 @@ public function readData() {
|
|||||||
|
|
||||||
parent::readData();
|
parent::readData();
|
||||||
|
|
||||||
|
// This attachment handler gets only used for the language variable `wcf.attachment.upload.limits`!
|
||||||
|
$this->attachmentHandler = new \wcf\system\attachment\AttachmentHandler('be.bastelstu.chat.message', 0, 'DEADC0DE00000000DEADC0DE00000000DEADC0DE', $this->room->roomID);
|
||||||
|
|
||||||
$pushHandler = \wcf\system\push\PushHandler::getInstance();
|
$pushHandler = \wcf\system\push\PushHandler::getInstance();
|
||||||
$pushHandler->joinChannel('be.bastelstu.chat');
|
$pushHandler->joinChannel('be.bastelstu.chat');
|
||||||
$pushHandler->joinChannel('be.bastelstu.chat.room-'.$this->room->roomID);
|
$pushHandler->joinChannel('be.bastelstu.chat.room-'.$this->room->roomID);
|
||||||
@ -104,6 +114,7 @@ public function assignVariables() {
|
|||||||
|
|
||||||
WCF::getTPL()->assign([ 'room' => $this->room
|
WCF::getTPL()->assign([ 'room' => $this->room
|
||||||
, 'config' => $this->getConfig()
|
, 'config' => $this->getConfig()
|
||||||
|
, 'attachmentHandler' => $this->attachmentHandler
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,105 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2020 Tim Düsterhus.
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License
|
||||||
|
* included in the LICENSE file.
|
||||||
|
*
|
||||||
|
* Change Date: 2024-10-31
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source
|
||||||
|
* License, use of this software will be governed by version 2
|
||||||
|
* or later of the General Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace chat\system\attachment;
|
||||||
|
|
||||||
|
use \chat\data\message\Message;
|
||||||
|
use \chat\data\message\MessageList;
|
||||||
|
use \chat\data\room\RoomCache;
|
||||||
|
use \wcf\system\WCF;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attachment object type implementation for messages.
|
||||||
|
*/
|
||||||
|
class MessageAttachmentObjectType extends \wcf\system\attachment\AbstractAttachmentObjectType {
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function canDownload($objectID) {
|
||||||
|
if ($objectID) {
|
||||||
|
$message = new Message($objectID);
|
||||||
|
|
||||||
|
if ($message->getMessageType()->objectType !== 'be.bastelstu.chat.messageType.attachment') {
|
||||||
|
throw new \LogicException('Unreachable');
|
||||||
|
}
|
||||||
|
$room = $message->getRoom();
|
||||||
|
|
||||||
|
return $room->canSee();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function canUpload($objectID, $parentObjectID = 0) {
|
||||||
|
if ($objectID) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WCF::getSession()->getPermission('user.chat.canAttach')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$room = null;
|
||||||
|
if ($parentObjectID) {
|
||||||
|
$room = RoomCache::getInstance()->getRoom($parentObjectID);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($room !== null) {
|
||||||
|
return $room->canSee();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function canDelete($objectID) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getMaxCount() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getAllowedExtensions() {
|
||||||
|
return [ 'png'
|
||||||
|
, 'gif'
|
||||||
|
, 'jpg'
|
||||||
|
, 'jpeg'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function cacheObjects(array $objectIDs) {
|
||||||
|
$messageList = new MessageList();
|
||||||
|
$messageList->setObjectIDs($objectIDs);
|
||||||
|
$messageList->readObjects();
|
||||||
|
|
||||||
|
foreach ($messageList->getObjects() as $objectID => $object) {
|
||||||
|
$this->cachedObjects[$objectID] = $object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2020 Tim Düsterhus.
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License
|
||||||
|
* included in the LICENSE file.
|
||||||
|
*
|
||||||
|
* Change Date: 2024-10-31
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source
|
||||||
|
* License, use of this software will be governed by version 2
|
||||||
|
* or later of the General Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace chat\system\message\type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AttachmentMessageType represents a message with an attached file.
|
||||||
|
*/
|
||||||
|
class AttachmentMessageType implements IMessageType, IDeletableMessageType {
|
||||||
|
use TCanSeeInSameRoom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HtmlOutputProcessor to use.
|
||||||
|
* @var \wcf\system\html\output\HtmlOutputProcessor
|
||||||
|
*/
|
||||||
|
protected $processor = null;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->processor = new \wcf\system\html\output\HtmlOutputProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getJavaScriptModuleName() {
|
||||||
|
return 'Bastelstu.be/Chat/MessageType/Plain';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function canDelete(\chat\data\message\Message $message, \wcf\data\user\UserProfile $user = null) {
|
||||||
|
if ($user === null) $user = new \wcf\data\user\UserProfile(\wcf\system\WCF::getUser());
|
||||||
|
|
||||||
|
return $user->getPermission('mod.chat.canDelete');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see \chat\system\message\type\IMessageType::getPayload()
|
||||||
|
*/
|
||||||
|
public function getPayload(\chat\data\message\Message $message, \wcf\data\user\UserProfile $user = null) {
|
||||||
|
if ($user === null) $user = new \wcf\data\user\UserProfile(\wcf\system\WCF::getUser());
|
||||||
|
|
||||||
|
$payload = $message->payload;
|
||||||
|
$payload['formattedMessage'] = null;
|
||||||
|
$payload['plaintextMessage'] = null;
|
||||||
|
|
||||||
|
$parameters = [ 'message' => $message
|
||||||
|
, 'user' => $user
|
||||||
|
, 'payload' => $payload
|
||||||
|
];
|
||||||
|
\wcf\system\event\EventHandler::getInstance()->fireAction($this, 'getPayload', $parameters);
|
||||||
|
|
||||||
|
if ($parameters['payload']['formattedMessage'] === null) {
|
||||||
|
$this->processor->setOutputType('text/html');
|
||||||
|
$this->processor->process($parameters['payload']['message'], 'be.bastelstu.chat.message', $message->messageID);
|
||||||
|
$parameters['payload']['formattedMessage'] = $this->processor->getHtml();
|
||||||
|
}
|
||||||
|
if ($parameters['payload']['plaintextMessage'] === null) {
|
||||||
|
$this->processor->setOutputType('text/plain');
|
||||||
|
$this->processor->process($parameters['payload']['message'], 'be.bastelstu.chat.message', $message->messageID);
|
||||||
|
$parameters['payload']['plaintextMessage'] = $this->processor->getHtml();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parameters['payload'];
|
||||||
|
}
|
||||||
|
}
|
@ -57,6 +57,7 @@ #tpl_chat_log {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
|
margin-left: 20px;
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,15 +379,46 @@ #tpl_chat_log {
|
|||||||
|
|
||||||
> div {
|
> div {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
> .flexibleTextarea {
|
> div.chatAttachButton {
|
||||||
flex: 1 0 auto;
|
flex-grow: 0;
|
||||||
max-width: 100%;
|
flex-shrink: 0;
|
||||||
|
margin-right: 5px;
|
||||||
|
|
||||||
|
@include screen-xs {
|
||||||
|
> .disabled {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon16 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include screen-lg {
|
||||||
|
.icon16 {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon24 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> #chatQuickSettings {
|
> div.chatInputWrapper {
|
||||||
flex: 0 0 auto;
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> .flexibleTextarea {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> #chatQuickSettings {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* Use of this software is governed by the Business Source License
|
* Use of this software is governed by the Business Source License
|
||||||
* included in the LICENSE file.
|
* included in the LICENSE file.
|
||||||
*
|
*
|
||||||
* Change Date: 2024-10-31
|
* Change Date: 2024-11-01
|
||||||
*
|
*
|
||||||
* On the date above, in accordance with the Business Source
|
* On the date above, in accordance with the Business Source
|
||||||
* License, use of this software will be governed by version 2
|
* License, use of this software will be governed by version 2
|
||||||
@ -27,6 +27,7 @@ define([ './Chat/console'
|
|||||||
, './Chat/ProfileStore'
|
, './Chat/ProfileStore'
|
||||||
, './Chat/Room'
|
, './Chat/Room'
|
||||||
, './Chat/Template'
|
, './Chat/Template'
|
||||||
|
, './Chat/Ui/Attachment/Upload'
|
||||||
, './Chat/Ui/AutoAway'
|
, './Chat/Ui/AutoAway'
|
||||||
, './Chat/Ui/Chat'
|
, './Chat/Ui/Chat'
|
||||||
, './Chat/Ui/ConnectionWarning'
|
, './Chat/Ui/ConnectionWarning'
|
||||||
@ -43,7 +44,7 @@ define([ './Chat/console'
|
|||||||
, './Chat/Ui/UserActionDropdownHandler'
|
, './Chat/Ui/UserActionDropdownHandler'
|
||||||
, './Chat/Ui/UserList'
|
, './Chat/Ui/UserList'
|
||||||
], function (console, Bottle, Push, Core, Language, RepeatingTimer, CoreUser, Autocompleter,
|
], function (console, Bottle, Push, Core, Language, RepeatingTimer, CoreUser, Autocompleter,
|
||||||
CommandHandler, Throttle, Message, Messenger, ParseError, ProfileStore, Room, Template, UiAutoAway, Ui,
|
CommandHandler, Throttle, Message, Messenger, ParseError, ProfileStore, Room, Template, UiAttachmentUpload, UiAutoAway, Ui,
|
||||||
UiConnectionWarning, ErrorDialog, UiInput, UiInputAutocompleter, UiMessageStream, UiMessageActionDelete, UiMobile, UiNotification,
|
UiConnectionWarning, ErrorDialog, UiInput, UiInputAutocompleter, UiMessageStream, UiMessageActionDelete, UiMobile, UiNotification,
|
||||||
UiReadMarker, UiSettings, UiTopic, UiUserActionDropdownHandler, UiUserList) {
|
UiReadMarker, UiSettings, UiTopic, UiUserActionDropdownHandler, UiUserList) {
|
||||||
"use strict";
|
"use strict";
|
||||||
@ -85,6 +86,7 @@ define([ './Chat/console'
|
|||||||
this.service('UiTopic', UiTopic)
|
this.service('UiTopic', UiTopic)
|
||||||
this.service('UiUserActionDropdownHandler', UiUserActionDropdownHandler)
|
this.service('UiUserActionDropdownHandler', UiUserActionDropdownHandler)
|
||||||
this.service('UiUserList', UiUserList)
|
this.service('UiUserList', UiUserList)
|
||||||
|
this.service('UiAttachmentUpload', UiAttachmentUpload)
|
||||||
|
|
||||||
// Register Models
|
// Register Models
|
||||||
this.bottle.instanceFactory('Message', (container, m) => {
|
this.bottle.instanceFactory('Message', (container, m) => {
|
||||||
@ -176,8 +178,11 @@ define([ './Chat/console'
|
|||||||
this.ui = this.bottle.container.Ui
|
this.ui = this.bottle.container.Ui
|
||||||
this.ui.bootstrap()
|
this.ui.bootstrap()
|
||||||
|
|
||||||
this.bottle.container.UiInput.on('submit', this.onSubmit.bind(this))
|
this.ui.input.on('submit', this.onSubmit.bind(this))
|
||||||
this.bottle.container.UiInput.on('autocomplete', this.onAutocomplete.bind(this))
|
this.ui.input.on('autocomplete', this.onAutocomplete.bind(this))
|
||||||
|
this.ui.attachmentUpload.on('send', (event) => {
|
||||||
|
event.detail.promise = this.onSendAttachment(event)
|
||||||
|
})
|
||||||
|
|
||||||
await this.bottle.container.Room.join()
|
await this.bottle.container.Room.join()
|
||||||
|
|
||||||
@ -328,6 +333,10 @@ define([ './Chat/console'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async onSendAttachment(event) {
|
||||||
|
return this.bottle.container.Messenger.pushAttachment(event.detail.tmpHash)
|
||||||
|
}
|
||||||
|
|
||||||
onAutocomplete(event) {
|
onAutocomplete(event) {
|
||||||
const input = event.target
|
const input = event.target
|
||||||
const value = input.getText(true)
|
const value = input.getText(true)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2010-2018 Tim Düsterhus.
|
* Copyright (c) 2010-2020 Tim Düsterhus.
|
||||||
*
|
*
|
||||||
* Use of this software is governed by the Business Source License
|
* Use of this software is governed by the Business Source License
|
||||||
* included in the LICENSE file.
|
* included in the LICENSE file.
|
||||||
*
|
*
|
||||||
* Change Date: 2024-10-20
|
* Change Date: 2024-11-01
|
||||||
*
|
*
|
||||||
* On the date above, in accordance with the Business Source
|
* On the date above, in accordance with the Business Source
|
||||||
* License, use of this software will be governed by version 2
|
* License, use of this software will be governed by version 2
|
||||||
@ -56,6 +56,14 @@ define([ './console'
|
|||||||
return Ajax.api(this, payload)
|
return Ajax.api(this, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async pushAttachment(tmpHash) {
|
||||||
|
const payload = { actionName: 'pushAttachment'
|
||||||
|
, parameters: { tmpHash }
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ajax.api(this, payload)
|
||||||
|
}
|
||||||
|
|
||||||
_ajaxSetup() {
|
_ajaxSetup() {
|
||||||
return { silent: true
|
return { silent: true
|
||||||
, ignoreError: true
|
, ignoreError: true
|
||||||
|
207
files_wcf/js/Bastelstu.be/Chat/Ui/Attachment/Upload.js
Normal file
207
files_wcf/js/Bastelstu.be/Chat/Ui/Attachment/Upload.js
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2020 Tim Düsterhus.
|
||||||
|
*
|
||||||
|
* Use of this software is governed by the Business Source License
|
||||||
|
* included in the LICENSE file.
|
||||||
|
*
|
||||||
|
* Change Date: 2024-11-01
|
||||||
|
*
|
||||||
|
* On the date above, in accordance with the Business Source
|
||||||
|
* License, use of this software will be governed by version 2
|
||||||
|
* or later of the General Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
define([ 'WoltLabSuite/Core/Language'
|
||||||
|
, 'WoltLabSuite/Core/Upload'
|
||||||
|
, 'WoltLabSuite/Core/Dom/Change/Listener'
|
||||||
|
, 'WoltLabSuite/Core/Dom/Util'
|
||||||
|
, 'WoltLabSuite/Core/Ui/Dialog'
|
||||||
|
, '../../DataStructure/EventEmitter'
|
||||||
|
], function(Language, Upload, DomChangeListener, DomUtil, Dialog, EventEmitter) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const DIALOG_BUTTON_ID = 'chatAttachmentUploadButton'
|
||||||
|
const DIALOG_CONTAINER_ID = 'chatAttachmentUploadDialog'
|
||||||
|
|
||||||
|
const DEPENDENCIES = [ 'UiInput', 'Room' ];
|
||||||
|
class UiAttachmentUpload extends Upload {
|
||||||
|
constructor(input, room) {
|
||||||
|
const buttonContainer = document.querySelector(`#${DIALOG_CONTAINER_ID} > .upload`)
|
||||||
|
const buttonContainerId = DomUtil.identify(buttonContainer)
|
||||||
|
|
||||||
|
const previewContainer = document.querySelector(`#${DIALOG_CONTAINER_ID} > .attachmentPreview`)
|
||||||
|
const previewContainerId = DomUtil.identify(previewContainer)
|
||||||
|
|
||||||
|
super(buttonContainerId, previewContainerId, {
|
||||||
|
className: 'wcf\\data\\attachment\\AttachmentAction',
|
||||||
|
acceptableFiles: [ '.png', '.gif', '.jpg', '.jpeg' ]
|
||||||
|
})
|
||||||
|
|
||||||
|
this.input = input
|
||||||
|
this.room = room
|
||||||
|
this.previewContainer = previewContainer
|
||||||
|
this.tmpHash = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstrap() {
|
||||||
|
this.uploadDescription = document.querySelector(`#${DIALOG_CONTAINER_ID} > small`)
|
||||||
|
|
||||||
|
const button = document.getElementById(DIALOG_BUTTON_ID)
|
||||||
|
const container = document.getElementById(DIALOG_CONTAINER_ID)
|
||||||
|
|
||||||
|
elHide(container)
|
||||||
|
container.classList.remove('jsStaticDialogContent')
|
||||||
|
container.dataset.isStaticDialog = 'true'
|
||||||
|
|
||||||
|
if (button) {
|
||||||
|
button.addEventListener('click', (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
Dialog.openStatic(container.id, null, {
|
||||||
|
title: elData(container, 'title'),
|
||||||
|
onShow: () => this.showDialog()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const deleteAction = new WCF.Action.Delete('wcf\\data\\attachment\\AttachmentAction', `#${this.previewContainer.id} > p`)
|
||||||
|
deleteAction.setCallback(() => this.closeDialog())
|
||||||
|
|
||||||
|
this.input.on('input', (event) => {
|
||||||
|
if (event.target.input.value.length == 0) {
|
||||||
|
button.classList.remove('disabled')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
button.classList.add('disabled')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closeDialog() {
|
||||||
|
if (Dialog.getDialog(DIALOG_CONTAINER_ID)) {
|
||||||
|
Dialog.close(DIALOG_CONTAINER_ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showDialog() {
|
||||||
|
if (this._button.parentNode) {
|
||||||
|
this._removeButton()
|
||||||
|
}
|
||||||
|
|
||||||
|
this._target.innerHTML = ''
|
||||||
|
this._createButton()
|
||||||
|
elShow(this.uploadDescription)
|
||||||
|
}
|
||||||
|
|
||||||
|
async send(tmpHash, event) {
|
||||||
|
event.preventDefault()
|
||||||
|
const parameters = { promise: Promise.resolve()
|
||||||
|
, tmpHash
|
||||||
|
}
|
||||||
|
this.emit('send', parameters)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await parameters.promise
|
||||||
|
this.closeDialog()
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
// TODO: Error handling
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createButtonGroup(uploadId, objectId, tmpHash) {
|
||||||
|
const buttonGroup = document.createElement('ul')
|
||||||
|
buttonGroup.classList.add('buttonGroup')
|
||||||
|
|
||||||
|
let li = document.createElement('li')
|
||||||
|
const cancelButton = document.createElement('span')
|
||||||
|
cancelButton.classList.add('button', 'jsDeleteButton')
|
||||||
|
cancelButton.dataset.objectId = objectId
|
||||||
|
cancelButton.dataset.eventName = 'attachment'
|
||||||
|
cancelButton.innerText = Language.get('wcf.global.button.cancel')
|
||||||
|
li.appendChild(cancelButton)
|
||||||
|
buttonGroup.appendChild(li)
|
||||||
|
|
||||||
|
li = document.createElement('li')
|
||||||
|
const sendButton = document.createElement('span')
|
||||||
|
sendButton.classList.add('button')
|
||||||
|
sendButton.innerText = Language.get('wcf.global.button.submit')
|
||||||
|
sendButton.addEventListener('click', (e) => this.send(tmpHash, e))
|
||||||
|
li.appendChild(sendButton)
|
||||||
|
buttonGroup.appendChild(li)
|
||||||
|
|
||||||
|
const target = this._fileElements[uploadId][0]
|
||||||
|
target.appendChild(buttonGroup)
|
||||||
|
|
||||||
|
DomChangeListener.trigger()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see WoltLabSuite/Core/Upload#_getParameters
|
||||||
|
*/
|
||||||
|
_getParameters() {
|
||||||
|
this.tmpHash = [ ...crypto.getRandomValues(new Uint8Array(20)) ]
|
||||||
|
.map(m => ('0' + m.toString(16)).slice(-2))
|
||||||
|
.join('')
|
||||||
|
|
||||||
|
return { objectType: "be.bastelstu.chat.message"
|
||||||
|
, parentObjectID: this.room.roomID
|
||||||
|
, tmpHash: this.tmpHash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see WoltLabSuite/Core/Upload#_success
|
||||||
|
*/
|
||||||
|
_success(uploadId, data, responseText, xhr, requestOptions) {
|
||||||
|
if (data.returnValues?.errors?.[0]) {
|
||||||
|
const error = data.returnValues.errors[0]
|
||||||
|
|
||||||
|
elInnerError(this._button, Language.get(`wcf.attachment.upload.error.${error.errorType}`, {
|
||||||
|
filename: error.filename
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
elInnerError(this._button, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.returnValues?.attachments?.[uploadId]) {
|
||||||
|
this._removeButton()
|
||||||
|
elHide(this.uploadDescription)
|
||||||
|
|
||||||
|
const attachment = data.returnValues.attachments[uploadId]
|
||||||
|
const url = attachment.thumbnailURL || attachment.tinyURL || attachment.url
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
throw new Error('Missing image URL')
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = this._fileElements[uploadId][0]
|
||||||
|
const progress = target.querySelector(':scope > progress')
|
||||||
|
|
||||||
|
const img = document.createElement('img')
|
||||||
|
img.setAttribute('src', url)
|
||||||
|
img.setAttribute('alt', '')
|
||||||
|
|
||||||
|
if (url === attachment.thumbnailURL) {
|
||||||
|
img.classList.add('attachmentThumbnail')
|
||||||
|
}
|
||||||
|
else if (url === attachment.tinyURL) {
|
||||||
|
img.classList.add('attachmentTinyThumbnail')
|
||||||
|
}
|
||||||
|
|
||||||
|
img.dataset.width = attachment.width
|
||||||
|
img.dataset.height = attachment.height
|
||||||
|
|
||||||
|
DomUtil.replaceElement(progress, img)
|
||||||
|
|
||||||
|
this.createButtonGroup(uploadId, attachment.attachmentID, this.tmpHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiAttachmentUpload.DEPENDENCIES = DEPENDENCIES
|
||||||
|
EventEmitter(UiAttachmentUpload.prototype)
|
||||||
|
|
||||||
|
return UiAttachmentUpload
|
||||||
|
})
|
@ -14,12 +14,13 @@
|
|||||||
define([ '../Ui' ], function (Ui) {
|
define([ '../Ui' ], function (Ui) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const DEPENDENCIES = [ 'UiAutoAway'
|
const DEPENDENCIES = [ 'UiAttachmentUpload'
|
||||||
|
, 'UiAutoAway'
|
||||||
, 'UiConnectionWarning'
|
, 'UiConnectionWarning'
|
||||||
, 'UiInput'
|
, 'UiInput'
|
||||||
, 'UiInputAutocompleter'
|
, 'UiInputAutocompleter'
|
||||||
, 'UiMessageStream'
|
|
||||||
, 'UiMessageActionDelete'
|
, 'UiMessageActionDelete'
|
||||||
|
, 'UiMessageStream'
|
||||||
, 'UiMobile'
|
, 'UiMobile'
|
||||||
, 'UiNotification'
|
, 'UiNotification'
|
||||||
, 'UiReadMarker'
|
, 'UiReadMarker'
|
||||||
@ -29,16 +30,17 @@ define([ '../Ui' ], function (Ui) {
|
|||||||
, 'UiUserList'
|
, 'UiUserList'
|
||||||
]
|
]
|
||||||
class Chat extends Ui {
|
class Chat extends Ui {
|
||||||
constructor(autoAway, connectionWarning, input, autocompleter, messageStream, messageActionDelete, mobile, notification, readMarker, settings, topic, userActionDropdownHandler, userList) {
|
constructor(attachmentUpload, autoAway, connectionWarning, input, autocompleter, messageActionDelete, messageStream, mobile, notification, readMarker, settings, topic, userActionDropdownHandler, userList) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.actionDropdownHandler = userActionDropdownHandler
|
this.actionDropdownHandler = userActionDropdownHandler
|
||||||
|
this.attachmentUpload = attachmentUpload
|
||||||
this.autoAway = autoAway
|
this.autoAway = autoAway
|
||||||
this.autocompleter = autocompleter
|
this.autocompleter = autocompleter
|
||||||
this.connectionWarning = connectionWarning
|
this.connectionWarning = connectionWarning
|
||||||
this.input = input
|
this.input = input
|
||||||
this.messageStream = messageStream
|
|
||||||
this.messageActionDelete = messageActionDelete
|
this.messageActionDelete = messageActionDelete
|
||||||
|
this.messageStream = messageStream
|
||||||
this.mobile = mobile
|
this.mobile = mobile
|
||||||
this.notification = notification
|
this.notification = notification
|
||||||
this.readMarker = readMarker
|
this.readMarker = readMarker
|
||||||
@ -49,12 +51,13 @@ define([ '../Ui' ], function (Ui) {
|
|||||||
|
|
||||||
bootstrap() {
|
bootstrap() {
|
||||||
this.actionDropdownHandler.bootstrap()
|
this.actionDropdownHandler.bootstrap()
|
||||||
|
this.attachmentUpload.bootstrap()
|
||||||
this.autoAway.bootstrap()
|
this.autoAway.bootstrap()
|
||||||
this.autocompleter.bootstrap()
|
this.autocompleter.bootstrap()
|
||||||
this.connectionWarning.bootstrap()
|
this.connectionWarning.bootstrap()
|
||||||
this.input.bootstrap()
|
this.input.bootstrap()
|
||||||
this.messageStream.bootstrap()
|
|
||||||
this.messageActionDelete.bootstrap()
|
this.messageActionDelete.bootstrap()
|
||||||
|
this.messageStream.bootstrap()
|
||||||
this.mobile.bootstrap()
|
this.mobile.bootstrap()
|
||||||
this.notification.bootstrap()
|
this.notification.bootstrap()
|
||||||
this.readMarker.bootstrap()
|
this.readMarker.bootstrap()
|
||||||
|
@ -63,7 +63,7 @@ define([ './ToggleButton'
|
|||||||
*/
|
*/
|
||||||
setupMobile() {
|
setupMobile() {
|
||||||
this.shadowToggleButton = document.createElement('span')
|
this.shadowToggleButton = document.createElement('span')
|
||||||
this.shadowToggleButton.classList.add('smiliesToggleMobileButton')
|
this.shadowToggleButton.classList.add('smiliesToggleMobileButton', 'button', 'small')
|
||||||
this.shadowToggleButton.innerHTML = '<span class="icon icon24 fa-smile-o"></span>'
|
this.shadowToggleButton.innerHTML = '<span class="icon icon24 fa-smile-o"></span>'
|
||||||
this.shadowToggleButton.addEventListener('mousedown', this.onClick.bind(this))
|
this.shadowToggleButton.addEventListener('mousedown', this.onClick.bind(this))
|
||||||
|
|
||||||
|
@ -198,6 +198,7 @@
|
|||||||
<item name="wcf.acp.group.option.mod.chat.canMute"><![CDATA[Kann knebeln]]></item>
|
<item name="wcf.acp.group.option.mod.chat.canMute"><![CDATA[Kann knebeln]]></item>
|
||||||
<item name="wcf.acp.group.option.mod.chat.canMute.description"><![CDATA[Achtung: Diese Berechtigung kann nicht über Raumspezifische Rechte entzogen werden.]]></item>
|
<item name="wcf.acp.group.option.mod.chat.canMute.description"><![CDATA[Achtung: Diese Berechtigung kann nicht über Raumspezifische Rechte entzogen werden.]]></item>
|
||||||
<item name="wcf.acp.group.option.mod.chat.canTeam"><![CDATA[Kann Teamnachrichten versenden]]></item>
|
<item name="wcf.acp.group.option.mod.chat.canTeam"><![CDATA[Kann Teamnachrichten versenden]]></item>
|
||||||
|
<item name="wcf.acp.group.option.user.chat.canAttach"><![CDATA[Kann Dateianhänge hochladen]]></item>
|
||||||
<item name="wcf.acp.group.option.user.chat.canSee"><![CDATA[Kann Chaträume sehen]]></item>
|
<item name="wcf.acp.group.option.user.chat.canSee"><![CDATA[Kann Chaträume sehen]]></item>
|
||||||
<item name="wcf.acp.group.option.user.chat.canSeeLog"><![CDATA[Kann das Protokoll sehen]]></item>
|
<item name="wcf.acp.group.option.user.chat.canSeeLog"><![CDATA[Kann das Protokoll sehen]]></item>
|
||||||
<item name="wcf.acp.group.option.user.chat.canSetColor"><![CDATA[Kann den Benutzernamen färben]]></item>
|
<item name="wcf.acp.group.option.user.chat.canSetColor"><![CDATA[Kann den Benutzernamen färben]]></item>
|
||||||
|
@ -198,6 +198,7 @@
|
|||||||
<item name="wcf.acp.group.option.mod.chat.canMute"><![CDATA[Can mute]]></item>
|
<item name="wcf.acp.group.option.mod.chat.canMute"><![CDATA[Can mute]]></item>
|
||||||
<item name="wcf.acp.group.option.mod.chat.canMute.description"><![CDATA[Note: If this permission is granted it cannot be revoked in the room specific permissions.]]></item>
|
<item name="wcf.acp.group.option.mod.chat.canMute.description"><![CDATA[Note: If this permission is granted it cannot be revoked in the room specific permissions.]]></item>
|
||||||
<item name="wcf.acp.group.option.mod.chat.canTeam"><![CDATA[Can use team internal messages]]></item>
|
<item name="wcf.acp.group.option.mod.chat.canTeam"><![CDATA[Can use team internal messages]]></item>
|
||||||
|
<item name="wcf.acp.group.option.user.chat.canAttach"><![CDATA[Can upload attachments]]></item>
|
||||||
<item name="wcf.acp.group.option.user.chat.canSee"><![CDATA[Can see chat rooms]]></item>
|
<item name="wcf.acp.group.option.user.chat.canSee"><![CDATA[Can see chat rooms]]></item>
|
||||||
<item name="wcf.acp.group.option.user.chat.canSeeLog"><![CDATA[Can see chat log]]></item>
|
<item name="wcf.acp.group.option.user.chat.canSeeLog"><![CDATA[Can see chat log]]></item>
|
||||||
<item name="wcf.acp.group.option.user.chat.canSetColor"><![CDATA[Can choose to color their name]]></item>
|
<item name="wcf.acp.group.option.user.chat.canSetColor"><![CDATA[Can choose to color their name]]></item>
|
||||||
|
@ -132,6 +132,12 @@
|
|||||||
<definitionname>be.bastelstu.chat.messageType</definitionname>
|
<definitionname>be.bastelstu.chat.messageType</definitionname>
|
||||||
<classname>chat\system\message\type\WhisperMessageType</classname>
|
<classname>chat\system\message\type\WhisperMessageType</classname>
|
||||||
</type>
|
</type>
|
||||||
|
|
||||||
|
<type>
|
||||||
|
<name>be.bastelstu.chat.messageType.attachment</name>
|
||||||
|
<definitionname>be.bastelstu.chat.messageType</definitionname>
|
||||||
|
<classname>chat\system\message\type\AttachmentMessageType</classname>
|
||||||
|
</type>
|
||||||
<!-- /message types -->
|
<!-- /message types -->
|
||||||
|
|
||||||
<!-- suspensions -->
|
<!-- suspensions -->
|
||||||
@ -168,5 +174,13 @@
|
|||||||
<points>1</points>
|
<points>1</points>
|
||||||
</type>
|
</type>
|
||||||
<!-- /activity points -->
|
<!-- /activity points -->
|
||||||
|
|
||||||
|
<!-- attachments -->
|
||||||
|
<type>
|
||||||
|
<name>be.bastelstu.chat.message</name>
|
||||||
|
<definitionname>com.woltlab.wcf.attachment.objectType</definitionname>
|
||||||
|
<classname>chat\system\attachment\MessageAttachmentObjectType</classname>
|
||||||
|
</type>
|
||||||
|
<!-- attachments -->
|
||||||
</import>
|
</import>
|
||||||
</data>
|
</data>
|
||||||
|
19
templates/__attachmentDialog.tpl
Normal file
19
templates/__attachmentDialog.tpl
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<div id="chatAttachmentUploadDialog" class="jsStaticDialogContent" data-title="{lang}wcf.attachment.attachments{/lang}">
|
||||||
|
<div class="attachmentPreview"></div>
|
||||||
|
|
||||||
|
{* placeholder for the upload button *}
|
||||||
|
<div class="upload"></div>
|
||||||
|
<small>{lang}wcf.attachment.upload.limits{/lang}</small>
|
||||||
|
</div>
|
||||||
|
<script data-relocate="true">
|
||||||
|
require([ 'Language' ], function (Language) {
|
||||||
|
Language.addObject({
|
||||||
|
'wcf.attachment.upload.error.invalidExtension': '{lang}wcf.attachment.upload.error.invalidExtension{/lang}',
|
||||||
|
'wcf.attachment.upload.error.tooLarge': '{lang}wcf.attachment.upload.error.tooLarge{/lang}',
|
||||||
|
'wcf.attachment.upload.error.reachedLimit': '{lang}wcf.attachment.upload.error.reachedLimit{/lang}',
|
||||||
|
'wcf.attachment.upload.error.reachedRemainingLimit': '{lang}wcf.attachment.upload.error.reachedRemainingLimit{/lang}',
|
||||||
|
'wcf.attachment.upload.error.uploadFailed': '{lang}wcf.attachment.upload.error.uploadFailed{/lang}',
|
||||||
|
'wcf.attachment.upload.error.uploadPhpLimit': '{lang}wcf.attachment.upload.error.uploadPhpLimit{/lang}',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
@ -433,6 +433,37 @@
|
|||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script type="x-text/template" data-application="be.bastelstu.chat" data-template-name="be-bastelstu-chat-messageType-attachment" data-template-includes="DeleteButton">
|
||||||
|
<div class="chatMessageContainer">
|
||||||
|
<div class="chatMessageSide">
|
||||||
|
<div class="chatUserAvatar jsUserActionDropdown" data-user-id="{$author.userID}">
|
||||||
|
<a href="{$author.link}">{@$author.image32}</a>
|
||||||
|
</div>
|
||||||
|
<time><a href="{$message.link}">{$message.formattedTime}</a></time>
|
||||||
|
</div>
|
||||||
|
<div class="chatMessageContent">
|
||||||
|
<div class="chatMessageHeader">
|
||||||
|
<span class="username">
|
||||||
|
<a href="{$author.link}" class="jsUserActionDropdown" data-user-id="{$author.userID}">
|
||||||
|
{@$author.coloredUsername}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<small class="separatorLeft">
|
||||||
|
<time><a href="{$message.link}">{$message.formattedTime}</a></time>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="chatMessage htmlContent">{@$message.payload.formattedMessage}</div>
|
||||||
|
</div>
|
||||||
|
<ul class="buttonGroup buttonList smallButtons">
|
||||||
|
{/literal}
|
||||||
|
{if $__wcf->session->getPermission('mod.chat.canDelete')}
|
||||||
|
{ldelim}include file=$t.DeleteButton}
|
||||||
|
{/if}
|
||||||
|
{literal}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
<script type="x-text/template" data-application="be.bastelstu.chat" data-template-name="be-bastelstu-chat-messageType-color">
|
<script type="x-text/template" data-application="be.bastelstu.chat" data-template-name="be-bastelstu-chat-messageType-color">
|
||||||
<div class="chatMessageContainer inline">
|
<div class="chatMessageContainer inline">
|
||||||
<div class="chatMessageSide">
|
<div class="chatMessageSide">
|
||||||
|
@ -54,10 +54,20 @@
|
|||||||
|
|
||||||
<div id="chatInputContainer">
|
<div id="chatInputContainer">
|
||||||
<div>
|
<div>
|
||||||
<textarea maxlength="{CHAT_MAX_LENGTH}" class="long"></textarea>
|
{if $__wcf->getSession()->getPermission('user.chat.canAttach')}
|
||||||
<span id="chatQuickSettings">
|
<div class="chatAttachButton">
|
||||||
<span class="icon icon24 fa-ellipsis-v"></span>
|
<span id="chatAttachmentUploadButton" class="button small" title="{lang}wcf.attachment.attachments{/lang}">
|
||||||
</span>
|
<span class="icon icon16 fa-paperclip"></span>
|
||||||
|
<span class="icon icon24 fa-paperclip"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div class="chatInputWrapper">
|
||||||
|
<textarea maxlength="{CHAT_MAX_LENGTH}" class="long"></textarea>
|
||||||
|
<span id="chatQuickSettings">
|
||||||
|
<span class="icon icon24 fa-ellipsis-v"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<small class="innerError" style="display: none"></small>
|
<small class="innerError" style="display: none"></small>
|
||||||
<span class="charCounter dimmed"></span>
|
<span class="charCounter dimmed"></span>
|
||||||
@ -71,6 +81,7 @@
|
|||||||
{include file='messageTypes' application='chat'}
|
{include file='messageTypes' application='chat'}
|
||||||
{include file='userList' application='chat'}
|
{include file='userList' application='chat'}
|
||||||
{include file='userListDropdownMenuItems' application='chat'}
|
{include file='userListDropdownMenuItems' application='chat'}
|
||||||
|
{include file='__attachmentDialog' application='chat'}
|
||||||
|
|
||||||
{if !ENABLE_DEBUG_MODE}{js application='wcf' file='Bastelstu.be.Chat'}{/if}
|
{if !ENABLE_DEBUG_MODE}{js application='wcf' file='Bastelstu.be.Chat'}{/if}
|
||||||
<script data-relocate="true">
|
<script data-relocate="true">
|
||||||
|
@ -49,6 +49,12 @@
|
|||||||
<admindefaultvalue>1</admindefaultvalue>
|
<admindefaultvalue>1</admindefaultvalue>
|
||||||
<usersonly>1</usersonly>
|
<usersonly>1</usersonly>
|
||||||
</option>
|
</option>
|
||||||
|
<option name="user.chat.canAttach">
|
||||||
|
<categoryname>user.chat</categoryname>
|
||||||
|
<optiontype>boolean</optiontype>
|
||||||
|
<defaultvalue>1</defaultvalue>
|
||||||
|
<usersonly>1</usersonly>
|
||||||
|
</option>
|
||||||
<option name="user.chat.disallowedBBCodes">
|
<option name="user.chat.disallowedBBCodes">
|
||||||
<categoryname>user.chat</categoryname>
|
<categoryname>user.chat</categoryname>
|
||||||
<optiontype>BBCodeSelect</optiontype>
|
<optiontype>BBCodeSelect</optiontype>
|
||||||
|
Loading…
Reference in New Issue
Block a user