Create MatrixChat entity, chat rooms for clients
This commit is contained in:
parent
940c7bed6e
commit
10a54cf6ac
|
@ -3,6 +3,7 @@
|
||||||
namespace OCA\UPschooling\Controller;
|
namespace OCA\UPschooling\Controller;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use OCA\UPschooling\Exceptions\RoomNotFoundException;
|
||||||
use OCA\UPschooling\Exceptions\TicketNotFoundException;
|
use OCA\UPschooling\Exceptions\TicketNotFoundException;
|
||||||
use OCP\AppFramework\Http;
|
use OCP\AppFramework\Http;
|
||||||
use OCP\AppFramework\Http\DataResponse;
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
|
@ -11,9 +12,9 @@ trait Errors {
|
||||||
protected function handleNotFound(Closure $callback): DataResponse {
|
protected function handleNotFound(Closure $callback): DataResponse {
|
||||||
try {
|
try {
|
||||||
return new DataResponse($callback());
|
return new DataResponse($callback());
|
||||||
} catch (TicketNotFoundException $e) {
|
} catch (TicketNotFoundException|RoomNotFoundException $e) {
|
||||||
$message = ['message' => $e->getMessage()];
|
$result = $e->getResult();
|
||||||
return new DataResponse($message, Http::STATUS_NOT_FOUND);
|
return new DataResponse($result->getMeta(), $result->getStatusCode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,14 @@ namespace OCA\UPschooling\Controller;
|
||||||
|
|
||||||
use OCA\UPschooling\AppInfo\Application;
|
use OCA\UPschooling\AppInfo\Application;
|
||||||
use OCA\UPschooling\Db\TicketMapper;
|
use OCA\UPschooling\Db\TicketMapper;
|
||||||
|
use OCA\UPschooling\Exceptions\RoomNotFoundException;
|
||||||
|
use OCA\UPschooling\Service\ChatService;
|
||||||
use OCA\UPschooling\Service\MatrixService;
|
use OCA\UPschooling\Service\MatrixService;
|
||||||
use OCA\UPschooling\Service\TicketService;
|
use OCA\UPschooling\Service\TicketService;
|
||||||
use OCP\AppFramework\ApiController;
|
use OCP\AppFramework\ApiController;
|
||||||
use OCP\AppFramework\Http\DataResponse;
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Punic\Data;
|
|
||||||
|
|
||||||
class TicketApiController extends ApiController
|
class TicketApiController extends ApiController
|
||||||
{
|
{
|
||||||
|
@ -24,6 +25,9 @@ class TicketApiController extends ApiController
|
||||||
/** @var MatrixService */
|
/** @var MatrixService */
|
||||||
private $matrixService;
|
private $matrixService;
|
||||||
|
|
||||||
|
/** @var ChatService */
|
||||||
|
private $chatService;
|
||||||
|
|
||||||
/** @var TicketMapper */
|
/** @var TicketMapper */
|
||||||
private $ticketMapper;
|
private $ticketMapper;
|
||||||
|
|
||||||
|
@ -38,6 +42,7 @@ class TicketApiController extends ApiController
|
||||||
TicketService $ticketService,
|
TicketService $ticketService,
|
||||||
TicketMapper $ticketMapper,
|
TicketMapper $ticketMapper,
|
||||||
MatrixService $matrixService,
|
MatrixService $matrixService,
|
||||||
|
ChatService $chatService,
|
||||||
string $userId
|
string $userId
|
||||||
) {
|
) {
|
||||||
parent::__construct(Application::APP_ID, $request);
|
parent::__construct(Application::APP_ID, $request);
|
||||||
|
@ -45,6 +50,7 @@ class TicketApiController extends ApiController
|
||||||
$this->ticketService = $ticketService;
|
$this->ticketService = $ticketService;
|
||||||
$this->ticketMapper = $ticketMapper;
|
$this->ticketMapper = $ticketMapper;
|
||||||
$this->matrixService = $matrixService;
|
$this->matrixService = $matrixService;
|
||||||
|
$this->chatService = $chatService;
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,9 +80,12 @@ class TicketApiController extends ApiController
|
||||||
return $this->handleNotFound(function () use ($id) {
|
return $this->handleNotFound(function () use ($id) {
|
||||||
$matrixUser = $this->ticketService->getOrCreateUser($this->userId);
|
$matrixUser = $this->ticketService->getOrCreateUser($this->userId);
|
||||||
$ticket = $this->ticketMapper->findForUser($id, $matrixUser);
|
$ticket = $this->ticketMapper->findForUser($id, $matrixUser);
|
||||||
$this->logger->debug("fetchChat found matrix data for room " . $ticket->getMatrixRoom());
|
$this->logger->debug("fetchChat found matrix data for ticket " . print_r($ticket->jsonSerialize(), true));
|
||||||
|
// returns 404 RoomNotFoundException if no room could/should be created
|
||||||
|
$chatRoom = $this->chatService->getOrCreateChatRoom($ticket);
|
||||||
|
$this->logger->debug("fetchChat got chat room " . print_r($chatRoom->jsonSerialize(), true));
|
||||||
return array(
|
return array(
|
||||||
'matrixRoom' => $ticket->getMatrixRoom(), // FIXME: wrong room, create one for helper and user
|
'matrixChatRoom' => $chatRoom->getMatrixRoomId(),
|
||||||
'matrixAccessToken' => $matrixUser->getMatrixToken(),
|
'matrixAccessToken' => $matrixUser->getMatrixToken(),
|
||||||
'matrixServerUrl' => $this->matrixService->getServerUrl(),
|
'matrixServerUrl' => $this->matrixService->getServerUrl(),
|
||||||
);
|
);
|
||||||
|
|
63
lib/Db/ChatMapper.php
Normal file
63
lib/Db/ChatMapper.php
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\UPschooling\Db;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Types\Type;
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use OCP\AppFramework\Db\Entity;
|
||||||
|
use OCP\AppFramework\Db\QBMapper;
|
||||||
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||||
|
use OCP\IDBConnection;
|
||||||
|
|
||||||
|
class ChatMapper extends QBMapper
|
||||||
|
{
|
||||||
|
public function __construct(IDBConnection $db)
|
||||||
|
{
|
||||||
|
parent::__construct($db, 'upschooling_chats', MatrixChat::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $ticketId
|
||||||
|
* @return array array of Entity|MatrixChat
|
||||||
|
*/
|
||||||
|
public function findForTicket(int $ticketId): array
|
||||||
|
{
|
||||||
|
/* @var $qb IQueryBuilder */
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
$qb->select('*')
|
||||||
|
->from('upschooling_chats')
|
||||||
|
->where($qb->expr()->eq('ticket_id', $qb->createNamedParameter($ticketId, IQueryBuilder::PARAM_INT)));
|
||||||
|
return $this->findEntities($qb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $ticketId
|
||||||
|
* @return Entity|MatrixChat
|
||||||
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
||||||
|
* @throws DoesNotExistException
|
||||||
|
*/
|
||||||
|
public function findCurrent(int $ticketId): MatrixChat
|
||||||
|
{
|
||||||
|
/* @var $qb IQueryBuilder */
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
$qb->select('*')
|
||||||
|
->from('upschooling_chats')
|
||||||
|
->where(
|
||||||
|
$qb->expr()->eq('ticket_id', $qb->createNamedParameter($ticketId, IQueryBuilder::PARAM_INT)),
|
||||||
|
$qb->expr()->isNull('end_date')
|
||||||
|
);
|
||||||
|
return $this->findEntity($qb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DateTime $dateTime
|
||||||
|
* @return mixed The database representation of the datetime with timezone.
|
||||||
|
* @throws \Doctrine\DBAL\Exception
|
||||||
|
* @throws \Doctrine\DBAL\Types\ConversionException
|
||||||
|
*/
|
||||||
|
public function convertDateTimeTz(\DateTime $dateTime)
|
||||||
|
{
|
||||||
|
$dateTimeTzType = Type::getType("datetimetz");
|
||||||
|
return $dateTimeTzType->convertToDatabaseValue($dateTime, $this->db->getDatabasePlatform());
|
||||||
|
}
|
||||||
|
}
|
29
lib/Db/MatrixChat.php
Normal file
29
lib/Db/MatrixChat.php
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\UPschooling\Db;
|
||||||
|
|
||||||
|
use JsonSerializable;
|
||||||
|
use OCP\AppFramework\Db\Entity;
|
||||||
|
|
||||||
|
class MatrixChat extends Entity implements JsonSerializable
|
||||||
|
{
|
||||||
|
protected $ticketId;
|
||||||
|
protected $matrixRoomId;
|
||||||
|
protected $matrixHelperUser;
|
||||||
|
protected $dateStart;
|
||||||
|
protected $dateEnd;
|
||||||
|
protected $version;
|
||||||
|
|
||||||
|
public function jsonSerialize(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'ticketId' => $this->ticketId,
|
||||||
|
'matrixRoomId' => $this->matrixRoomId,
|
||||||
|
'matrixHelperUser' => $this->matrixHelperUser,
|
||||||
|
'dateStart' => $this->dateStart,
|
||||||
|
'dateEnd' => $this->dateEnd,
|
||||||
|
'version' => $this->version,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -79,6 +79,37 @@ class Version000000Date20210918151800 extends SimpleMigrationStep {
|
||||||
$table->addUniqueConstraint(['matrix_user'], 'upschooling_mx_user_mx_uniq');
|
$table->addUniqueConstraint(['matrix_user'], 'upschooling_mx_user_mx_uniq');
|
||||||
$table->addForeignKeyConstraint('users', ['user_id'], ['uid'], [], 'upschooling_mx_user_nc_fk');
|
$table->addForeignKeyConstraint('users', ['user_id'], ['uid'], [], 'upschooling_mx_user_nc_fk');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$schema->hasTable('upschooling_chats')) {
|
||||||
|
$table = $schema->createTable('upschooling_chats');
|
||||||
|
$table->addColumn('id', 'integer', [
|
||||||
|
'autoincrement' => true,
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
$table->addColumn('ticket_id', 'integer', [
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
$table->addColumn('matrix_room_id', 'string', [
|
||||||
|
'notnull' => true,
|
||||||
|
'length' => 200,
|
||||||
|
]);
|
||||||
|
$table->addColumn('matrix_helper_user', 'string', [
|
||||||
|
'notnull' => true,
|
||||||
|
'length' => 200,
|
||||||
|
]);
|
||||||
|
$table->addColumn('date_start', 'datetimetz', [
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
$table->addColumn('date_end', 'datetimetz', [
|
||||||
|
'notnull' => false,
|
||||||
|
]);
|
||||||
|
$table->addColumn('version', 'integer', [
|
||||||
|
'notnull' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$table->setPrimaryKey(['id']);
|
||||||
|
$table->addForeignKeyConstraint('upschooling_tickets', ['ticket_id'], ['id'], [], 'upschooling_tckt_id_fk');
|
||||||
|
}
|
||||||
return $schema;
|
return $schema;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
62
lib/Service/ChatService.php
Normal file
62
lib/Service/ChatService.php
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace OCA\UPschooling\Service;
|
||||||
|
|
||||||
|
use Aryess\PhpMatrixSdk\Room;
|
||||||
|
use OCA\UPschooling\Db\ChatMapper;
|
||||||
|
use OCA\UPschooling\Db\MatrixChat;
|
||||||
|
use OCA\UPschooling\Db\MatrixTicket;
|
||||||
|
use OCA\UPschooling\Db\TicketMapper;
|
||||||
|
use OCA\UPschooling\Exceptions\RoomNotFoundException;
|
||||||
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
class ChatService
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var ChatMapper */
|
||||||
|
private $chatMapper;
|
||||||
|
|
||||||
|
/** @var MatrixService */
|
||||||
|
private $matrixService;
|
||||||
|
|
||||||
|
/** @var LoggerInterface */
|
||||||
|
private $logger;
|
||||||
|
|
||||||
|
public function __construct(ChatMapper $chatMapper, MatrixService $matrixService, LoggerInterface $logger)
|
||||||
|
{
|
||||||
|
$this->chatMapper = $chatMapper;
|
||||||
|
$this->matrixService = $matrixService;
|
||||||
|
$this->logger = $logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $ticket MatrixTicket Support ticket
|
||||||
|
* @throws RoomNotFoundException
|
||||||
|
*/
|
||||||
|
public function getOrCreateChatRoom(MatrixTicket $ticket): MatrixChat
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $this->chatMapper->findCurrent($ticket->getId());
|
||||||
|
} catch (DoesNotExistException $e) {
|
||||||
|
if ($ticket->getMatrixHelperUser() == null) {
|
||||||
|
$this->logger->debug("No helper assigned to ticket " . $ticket->getId() . ". Not creating chat room");
|
||||||
|
throw new RoomNotFoundException();
|
||||||
|
}
|
||||||
|
$this->logger->debug("Chat room for ticket " . $ticket->getId() . " does not exist. Creating room..");
|
||||||
|
$roomId = $this->matrixService->createRoom();
|
||||||
|
$startDate = new \DateTime('now', new \DateTimeZone("utc"));
|
||||||
|
$matrixChat = new MatrixChat();
|
||||||
|
$matrixChat->setTicketId($ticket->getId());
|
||||||
|
$matrixChat->setMatrixRoomId($roomId);
|
||||||
|
$matrixChat->setMatrixHelperUser($ticket->getMatrixHelperUser());
|
||||||
|
$matrixChat->setDateStart($this->chatMapper->convertDateTimeTz($startDate));
|
||||||
|
$matrixChat->setVersion(1);
|
||||||
|
|
||||||
|
$this->matrixService->inviteUser($roomId, $ticket->getMatrixHelperUser());
|
||||||
|
$this->matrixService->inviteUser($roomId, $ticket->getMatrixAssistedUser());
|
||||||
|
|
||||||
|
return $this->chatMapper->insert($matrixChat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -255,4 +255,10 @@ class MatrixService
|
||||||
$this->logger->debug("No ratelimiting for " . $this->superuser);
|
$this->logger->debug("No ratelimiting for " . $this->superuser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function inviteUser(string $roomId, string $matrixUserId)
|
||||||
|
{
|
||||||
|
$room = $this->client->joinRoom($roomId);
|
||||||
|
$room->inviteUser($matrixUserId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ use OCA\UPschooling\Exceptions\TicketNotFoundException;
|
||||||
use OCP\AppFramework\Db\DoesNotExistException;
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||||
use OCP\IUserManager;
|
use OCP\IUserManager;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
class TicketService {
|
class TicketService {
|
||||||
|
|
||||||
|
@ -30,16 +31,21 @@ class TicketService {
|
||||||
/** @var UserMapper */
|
/** @var UserMapper */
|
||||||
private $userMapper;
|
private $userMapper;
|
||||||
|
|
||||||
|
/** @var LoggerInterface */
|
||||||
|
private $logger;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IUserManager $userManager,
|
IUserManager $userManager,
|
||||||
MatrixService $matrix,
|
MatrixService $matrix,
|
||||||
TicketMapper $ticketMapper,
|
TicketMapper $ticketMapper,
|
||||||
UserMapper $userMapper
|
UserMapper $userMapper,
|
||||||
|
LoggerInterface $logger
|
||||||
) {
|
) {
|
||||||
$this->userManager = $userManager;
|
$this->userManager = $userManager;
|
||||||
$this->matrix = $matrix;
|
$this->matrix = $matrix;
|
||||||
$this->ticketMapper = $ticketMapper;
|
$this->ticketMapper = $ticketMapper;
|
||||||
$this->userMapper = $userMapper;
|
$this->userMapper = $userMapper;
|
||||||
|
$this->logger = $logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findAll(string $userId): array {
|
public function findAll(string $userId): array {
|
||||||
|
@ -83,6 +89,7 @@ class TicketService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function assign($id, $userId): array{
|
public function assign($id, $userId): array{
|
||||||
|
$this->logger->debug("Assigning $userId to ticket $id..");
|
||||||
$matrixUser = $this->getOrCreateUser($userId);
|
$matrixUser = $this->getOrCreateUser($userId);
|
||||||
$ticket = $this->ticketMapper->findTicket($id);
|
$ticket = $this->ticketMapper->findTicket($id);
|
||||||
$roomID = $ticket->getMatrixControlRoom();
|
$roomID = $ticket->getMatrixControlRoom();
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import KeyValueTable from './components/KeyValueTable'
|
import KeyValueTable from './components/KeyValueTable'
|
||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
|
|
||||||
|
@ -50,6 +51,16 @@ export default {
|
||||||
/** @type {HTMLIFrameElement} */
|
/** @type {HTMLIFrameElement} */
|
||||||
const elementWebFrame = document.getElementById('element-web-frame')
|
const elementWebFrame = document.getElementById('element-web-frame')
|
||||||
this.loadChat(elementWebFrame).catch((err) => {
|
this.loadChat(elementWebFrame).catch((err) => {
|
||||||
|
if (err.message === 'Room not found') {
|
||||||
|
console.debug('No chat room assigned to ticket')
|
||||||
|
elementWebFrame.src = 'about:blank'
|
||||||
|
elementWebFrame.onload = function() {
|
||||||
|
const textElement = elementWebFrame.contentDocument.createElement('strong')
|
||||||
|
textElement.innerText = 'Textchat nicht verfügbar. Es ist noch kein Helfer zugewiesen.'
|
||||||
|
elementWebFrame.contentDocument.body.appendChild(textElement)
|
||||||
|
elementWebFrame.onload = undefined
|
||||||
|
}
|
||||||
|
} else {
|
||||||
console.error('Could not load Element Web in iframe', err)
|
console.error('Could not load Element Web in iframe', err)
|
||||||
elementWebFrame.src = 'about:blank'
|
elementWebFrame.src = 'about:blank'
|
||||||
elementWebFrame.onload = function() {
|
elementWebFrame.onload = function() {
|
||||||
|
@ -58,6 +69,7 @@ export default {
|
||||||
elementWebFrame.contentDocument.body.appendChild(textElement)
|
elementWebFrame.contentDocument.body.appendChild(textElement)
|
||||||
elementWebFrame.onload = undefined
|
elementWebFrame.onload = undefined
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -77,8 +89,11 @@ export default {
|
||||||
async loadChat(elementWebFrame) {
|
async loadChat(elementWebFrame) {
|
||||||
const matrixInfoResponse = await axios.get(
|
const matrixInfoResponse = await axios.get(
|
||||||
`api/v1/tickets/${this.ticket.ticketId}/chat`,
|
`api/v1/tickets/${this.ticket.ticketId}/chat`,
|
||||||
{ headers: { Accept: 'application/json' } },
|
{ headers: { Accept: 'application/json' }, validateStatus: status => status === 200 || status === 404 },
|
||||||
)
|
)
|
||||||
|
if (matrixInfoResponse.status === 404 && matrixInfoResponse.data.message === 'Room not found') {
|
||||||
|
throw Error(matrixInfoResponse.data.message)
|
||||||
|
}
|
||||||
if (matrixInfoResponse.status !== 200) {
|
if (matrixInfoResponse.status !== 200) {
|
||||||
throw Error(`Received unexpected status code ${matrixInfoResponse.status} for fetching matrix chat data`)
|
throw Error(`Received unexpected status code ${matrixInfoResponse.status} for fetching matrix chat data`)
|
||||||
}
|
}
|
||||||
|
@ -113,15 +128,12 @@ export default {
|
||||||
// load Element Web
|
// load Element Web
|
||||||
elementWebFrame.src = '/upschooling/element-web/'
|
elementWebFrame.src = '/upschooling/element-web/'
|
||||||
|
|
||||||
loginPromise.then(() => {
|
loginPromise.then(async () => {
|
||||||
console.warn('LOGGED IN')
|
const matrixClient = elementWebFrame.contentWindow.mxMatrixClientPeg.get()
|
||||||
elementWebFrame.contentWindow.mxDispatcher.dispatch({
|
await matrixClient.joinRoom(matrixInfoResponse.data.matrixChatRoom)
|
||||||
action: 'view_home_page',
|
|
||||||
justRegistered: false,
|
|
||||||
})
|
|
||||||
elementWebFrame.contentWindow.mxDispatcher.dispatch({
|
elementWebFrame.contentWindow.mxDispatcher.dispatch({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
room_id: matrixInfoResponse.data.matrixRoom,
|
room_id: matrixInfoResponse.data.matrixChatRoom,
|
||||||
})
|
})
|
||||||
}).catch(console.error)
|
}).catch(console.error)
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue