UPSTREAM BASELINE: nextcloud/spreed v22.0.12 (без изменений)
Type checking / changes (push) Has been cancelled
Type checking / test (push) Has been cancelled
Type checking / typescript-summary (push) Has been cancelled
Node tests / changes (push) Has been cancelled
Node tests / test (push) Has been cancelled
Node tests / test-summary (push) Has been cancelled
Type checking / changes (push) Has been cancelled
Type checking / test (push) Has been cancelled
Type checking / typescript-summary (push) Has been cancelled
Node tests / changes (push) Has been cancelled
Node tests / test (push) Has been cancelled
Node tests / test-summary (push) Has been cancelled
Источник: https://github.com/nextcloud/spreed/archive/refs/tags/v22.0.12.tar.gz С этого коммита ветка официального Nextcloud Talk отрезана (решение владельца 2026-07-06). Все дальнейшие изменения — только наши; версии релизов: 22.0.12-f7.N.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @method void setRoomId(int $roomId)
|
||||
* @method int getRoomId()
|
||||
* @method void setMessageId(int $messageId)
|
||||
* @method int getMessageId()
|
||||
* @method void setMessageTime(int $messageTime)
|
||||
* @method int getMessageTime()
|
||||
* @method void setObjectType(string $objectType)
|
||||
* @method string getObjectType()
|
||||
* @method void setActorType(string $actorType)
|
||||
* @method string getActorType()
|
||||
* @method void setActorId(string $actorId)
|
||||
* @method string getActorId()
|
||||
*/
|
||||
class Attachment extends Entity {
|
||||
public const TYPE_AUDIO = 'audio';
|
||||
public const TYPE_DECK_CARD = 'deckcard';
|
||||
public const TYPE_FILE = 'file';
|
||||
public const TYPE_LOCATION = 'location';
|
||||
public const TYPE_MEDIA = 'media';
|
||||
public const TYPE_OTHER = 'other';
|
||||
public const TYPE_POLL = 'poll';
|
||||
public const TYPE_RECORDING = 'recording';
|
||||
public const TYPE_VOICE = 'voice';
|
||||
|
||||
public const ATTACHMENTS_NONE = 0;
|
||||
public const ATTACHMENTS_ATLEAST_ONE = 1;
|
||||
|
||||
/** @var int */
|
||||
protected $roomId;
|
||||
|
||||
/** @var int */
|
||||
protected $messageId;
|
||||
|
||||
/** @var int */
|
||||
protected $messageTime;
|
||||
|
||||
/** @var string */
|
||||
protected $objectType;
|
||||
|
||||
/** @var string */
|
||||
protected $actorType;
|
||||
|
||||
/** @var string */
|
||||
protected $actorId;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('roomId', Types::BIGINT);
|
||||
$this->addType('messageId', Types::BIGINT);
|
||||
$this->addType('messageTime', Types::BIGINT);
|
||||
$this->addType('objectType', Types::STRING);
|
||||
$this->addType('actorType', Types::STRING);
|
||||
$this->addType('actorId', Types::STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function asArray(): array {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'room_id' => $this->getRoomId(),
|
||||
'message_id' => $this->getMessageId(),
|
||||
'message_time' => $this->getMessageTime(),
|
||||
'object_type' => $this->getObjectType(),
|
||||
'actor_type' => $this->getActorType(),
|
||||
'actor_id' => $this->getActorId(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\AppFramework\Db\TTransactional;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method Attachment mapRowToEntity(array $row)
|
||||
* @method Attachment findEntity(IQueryBuilder $query)
|
||||
* @method list<Attachment> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<Attachment>
|
||||
*/
|
||||
class AttachmentMapper extends QBMapper {
|
||||
use TTransactional;
|
||||
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'talk_attachments', Attachment::class);
|
||||
}
|
||||
|
||||
public function createAttachmentFromRow(array $row): Attachment {
|
||||
return $this->mapRowToEntity([
|
||||
'id' => (int)$row['id'],
|
||||
'room_id' => (int)$row['room_id'],
|
||||
'message_id' => (int)$row['message_id'],
|
||||
'message_time' => (int)$row['message_time'],
|
||||
'object_type' => (string)$row['object_type'],
|
||||
'actor_type' => (string)$row['actor_type'],
|
||||
'actor_id' => (string)$row['actor_id'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Attachment>
|
||||
* @throws \OCP\DB\Exception
|
||||
*/
|
||||
public function getAttachmentsByType(int $roomId, string $objectType, int $offset, int $limit): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->eq('object_type', $query->createNamedParameter($objectType)))
|
||||
->setMaxResults($limit)
|
||||
->orderBy('id', 'DESC');
|
||||
|
||||
if ($offset > 0) {
|
||||
$query->andWhere($query->expr()->lt('message_id', $query->createNamedParameter($offset)));
|
||||
}
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
public function deleteByMessageId(int $messageId): void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('message_id', $query->createNamedParameter($messageId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
$query->executeStatement();
|
||||
}
|
||||
|
||||
public function deleteByRoomId(int $roomId): void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
$this->atomic(static function () use ($query): void {
|
||||
$query->executeStatement();
|
||||
}, $this->db);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @method void setRoomId(int $roomId)
|
||||
* @method int getRoomId()
|
||||
* @method void setActorType(string $actorType)
|
||||
* @method string getActorType()
|
||||
* @method void setActorId(string $actorId)
|
||||
* @method string getActorId()
|
||||
* @method void setDisplayName(string $displayName)
|
||||
* @method void setPin(string $pin)
|
||||
* @method null|string getPin()
|
||||
* @method void setParticipantType(int $participantType)
|
||||
* @method int getParticipantType()
|
||||
* @method void setFavorite(bool $favorite)
|
||||
* @method bool isFavorite()
|
||||
* @method void setNotificationLevel(int $notificationLevel)
|
||||
* @method int getNotificationLevel()
|
||||
* @method void setNotificationCalls(int $notificationCalls)
|
||||
* @method int getNotificationCalls()
|
||||
* @method void setLastJoinedCall(int $lastJoinedCall)
|
||||
* @method int getLastJoinedCall()
|
||||
* @method void setLastReadMessage(int $lastReadMessage)
|
||||
* @method int getLastReadMessage()
|
||||
* @method void setLastMentionMessage(int $lastMentionMessage)
|
||||
* @method int getLastMentionMessage()
|
||||
* @method void setLastMentionDirect(int $lastMentionDirect)
|
||||
* @method int getLastMentionDirect()
|
||||
* @method void setReadPrivacy(int $readPrivacy)
|
||||
* @method int getReadPrivacy()
|
||||
* @method void setPermissions(int $permissions)
|
||||
* @method void setArchived(bool $archived)
|
||||
* @method bool isArchived()
|
||||
* @method void setImportant(bool $important)
|
||||
* @method bool isImportant()
|
||||
* @method void setSensitive(bool $sensitive)
|
||||
* @method bool isSensitive()
|
||||
* @internal
|
||||
* @method int getPermissions()
|
||||
* @method void setAccessToken(string $accessToken)
|
||||
* @method null|string getAccessToken()
|
||||
* @method void setRemoteId(string $remoteId)
|
||||
* @method string getRemoteId()
|
||||
* @method void setInvitedCloudId(string $invitedCloudId)
|
||||
* @method string getInvitedCloudId()
|
||||
* @method void setPhoneNumber(?string $phoneNumber)
|
||||
* @method null|string getPhoneNumber()
|
||||
* @method void setCallId(?string $callId)
|
||||
* @method null|string getCallId()
|
||||
* @method void setState(int $state)
|
||||
* @method int getState()
|
||||
* @method void setUnreadMessages(int $unreadMessages)
|
||||
* @method int getUnreadMessages()
|
||||
* @method void setLastAttendeeActivity(int $lastAttendeeActivity)
|
||||
* @method int getLastAttendeeActivity()
|
||||
* @method void setHasUnreadThreads(bool $hasUnreadThreads)
|
||||
* @method bool getHasUnreadThreads()
|
||||
* @method void setHasUnreadThreadMentions(bool $hasUnreadThreadMentions)
|
||||
* @method bool getHasUnreadThreadMentions()
|
||||
* @method void setHasUnreadThreadDirects(bool $hasUnreadThreadDirects)
|
||||
* @method bool getHasUnreadThreadDirects()
|
||||
*/
|
||||
class Attendee extends Entity {
|
||||
public const ACTOR_USERS = 'users';
|
||||
public const ACTOR_GROUPS = 'groups';
|
||||
public const ACTOR_GUESTS = 'guests';
|
||||
public const ACTOR_EMAILS = 'emails';
|
||||
public const ACTOR_CIRCLES = 'circles';
|
||||
public const ACTOR_BRIDGED = 'bridged';
|
||||
public const ACTOR_BOTS = 'bots';
|
||||
public const ACTOR_FEDERATED_USERS = 'federated_users';
|
||||
public const ACTOR_PHONES = 'phones';
|
||||
|
||||
// Special actor IDs
|
||||
public const ACTOR_BOT_PREFIX = 'bot-';
|
||||
public const ACTOR_ID_CLI = 'cli';
|
||||
public const ACTOR_ID_SYSTEM = 'system';
|
||||
public const ACTOR_ID_SAMPLE = 'sample';
|
||||
public const ACTOR_ID_CHANGELOG = 'changelog';
|
||||
|
||||
public const PERMISSIONS_DEFAULT = 0;
|
||||
public const PERMISSIONS_CUSTOM = 1;
|
||||
public const PERMISSIONS_CALL_START = 2;
|
||||
public const PERMISSIONS_CALL_JOIN = 4;
|
||||
public const PERMISSIONS_LOBBY_IGNORE = 8;
|
||||
public const PERMISSIONS_PUBLISH_AUDIO = 16;
|
||||
public const PERMISSIONS_PUBLISH_VIDEO = 32;
|
||||
public const PERMISSIONS_PUBLISH_SCREEN = 64;
|
||||
public const PERMISSIONS_CHAT = 128;
|
||||
public const PERMISSIONS_MAX_DEFAULT // Max int (when all permissions are granted as default)
|
||||
= self::PERMISSIONS_CALL_START
|
||||
| self::PERMISSIONS_CALL_JOIN
|
||||
| self::PERMISSIONS_LOBBY_IGNORE
|
||||
| self::PERMISSIONS_PUBLISH_AUDIO
|
||||
| self::PERMISSIONS_PUBLISH_VIDEO
|
||||
| self::PERMISSIONS_PUBLISH_SCREEN
|
||||
| self::PERMISSIONS_CHAT
|
||||
;
|
||||
public const PERMISSIONS_MAX_CUSTOM = self::PERMISSIONS_MAX_DEFAULT | self::PERMISSIONS_CUSTOM; // Max int (when all permissions are granted as custom)
|
||||
|
||||
public const PERMISSIONS_MODIFY_SET = 'set';
|
||||
public const PERMISSIONS_MODIFY_REMOVE = 'remove';
|
||||
public const PERMISSIONS_MODIFY_ADD = 'add';
|
||||
|
||||
protected int $roomId = 0;
|
||||
protected string $actorType = '';
|
||||
protected string $actorId = '';
|
||||
protected ?string $displayName = null;
|
||||
protected ?string $pin = null;
|
||||
protected int $participantType = 0;
|
||||
protected bool $favorite = false;
|
||||
protected int $notificationLevel = 0;
|
||||
protected int $notificationCalls = 0;
|
||||
protected bool $archived = false;
|
||||
protected bool $important = false;
|
||||
protected bool $sensitive = false;
|
||||
protected int $lastJoinedCall = 0;
|
||||
protected int $lastReadMessage = 0;
|
||||
protected int $lastMentionMessage = 0;
|
||||
protected int $lastMentionDirect = 0;
|
||||
protected int $readPrivacy = 0;
|
||||
protected int $permissions = 0;
|
||||
protected ?string $accessToken = null;
|
||||
protected ?string $remoteId = null;
|
||||
protected ?string $invitedCloudId = null;
|
||||
protected ?string $phoneNumber = null;
|
||||
protected ?string $callId = null;
|
||||
protected int $state = 0;
|
||||
protected int $unreadMessages = 0;
|
||||
protected int $lastAttendeeActivity = 0;
|
||||
protected bool $hasUnreadThreads = false;
|
||||
protected bool $hasUnreadThreadMentions = false;
|
||||
protected bool $hasUnreadThreadDirects = false;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('roomId', Types::BIGINT);
|
||||
$this->addType('actorType', Types::STRING);
|
||||
$this->addType('actorId', Types::STRING);
|
||||
$this->addType('displayName', Types::STRING);
|
||||
$this->addType('pin', Types::STRING);
|
||||
$this->addType('participantType', Types::SMALLINT);
|
||||
$this->addType('favorite', Types::BOOLEAN);
|
||||
$this->addType('archived', Types::BOOLEAN);
|
||||
$this->addType('important', Types::BOOLEAN);
|
||||
$this->addType('sensitive', Types::BOOLEAN);
|
||||
$this->addType('notificationLevel', Types::INTEGER);
|
||||
$this->addType('notificationCalls', Types::INTEGER);
|
||||
$this->addType('lastJoinedCall', Types::INTEGER);
|
||||
$this->addType('lastReadMessage', Types::INTEGER);
|
||||
$this->addType('lastMentionMessage', Types::INTEGER);
|
||||
$this->addType('lastMentionDirect', Types::BIGINT);
|
||||
$this->addType('readPrivacy', Types::SMALLINT);
|
||||
$this->addType('permissions', Types::INTEGER);
|
||||
$this->addType('accessToken', Types::STRING);
|
||||
$this->addType('remoteId', Types::STRING);
|
||||
$this->addType('invitedCloudId', Types::STRING);
|
||||
$this->addType('phoneNumber', Types::STRING);
|
||||
$this->addType('callId', Types::STRING);
|
||||
$this->addType('state', Types::SMALLINT);
|
||||
$this->addType('unreadMessages', Types::BIGINT);
|
||||
$this->addType('lastAttendeeActivity', Types::BIGINT);
|
||||
$this->addType('hasUnreadThreads', Types::BOOLEAN);
|
||||
$this->addType('hasUnreadThreadMentions', Types::BOOLEAN);
|
||||
$this->addType('hasUnreadThreadDirects', Types::BOOLEAN);
|
||||
}
|
||||
|
||||
public function getDisplayName(): string {
|
||||
return (string)$this->displayName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\Exception as DBException;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method Attendee mapRowToEntity(array $row)
|
||||
* @method Attendee findEntity(IQueryBuilder $query)
|
||||
* @method list<Attendee> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<Attendee>
|
||||
*/
|
||||
class AttendeeMapper extends QBMapper {
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'talk_attendees', Attendee::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findByActor(int $roomId, string $actorType, string $actorId): Attendee {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('actor_type', $query->createNamedParameter($actorType)))
|
||||
->andWhere($query->expr()->eq('actor_id', $query->createNamedParameter($actorId)))
|
||||
->andWhere($query->expr()->eq('room_id', $query->createNamedParameter($roomId)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
* @throws MultipleObjectsReturnedException
|
||||
*/
|
||||
public function getById(int $id): Attendee {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Attendee>
|
||||
*/
|
||||
public function getByAccessToken(string $accessToken): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('access_token', $query->createNamedParameter($accessToken)));
|
||||
// There could be multiple in case of local federation,
|
||||
// so we have to get all and afterwards check
|
||||
// the actor id for the serverUrl.
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
* @throws MultipleObjectsReturnedException
|
||||
*/
|
||||
public function getByRemoteIdAndToken(int $id, string $token): Attendee {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('remote_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_STR)))
|
||||
->andWhere($query->expr()->eq('access_token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Attendee>
|
||||
*/
|
||||
public function getActorsByType(int $roomId, string $actorType, ?int $lastJoinedCall = null): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->eq('actor_type', $query->createNamedParameter($actorType)));
|
||||
|
||||
if ($lastJoinedCall !== null) {
|
||||
$query->andWhere($query->expr()->gte('last_joined_call', $query->createNamedParameter($lastJoinedCall, IQueryBuilder::PARAM_INT)));
|
||||
}
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Attendee>
|
||||
*/
|
||||
public function getActorsByTypes(int $roomId, array $actorTypes, ?int $lastJoinedCall = null): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->in('actor_type', $query->createNamedParameter($actorTypes, IQueryBuilder::PARAM_STR_ARRAY)));
|
||||
|
||||
if ($lastJoinedCall !== null) {
|
||||
$query->andWhere($query->expr()->gte('last_joined_call', $query->createNamedParameter($lastJoinedCall, IQueryBuilder::PARAM_INT)));
|
||||
}
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Attendee>
|
||||
* @throws DBException
|
||||
*/
|
||||
public function getActorsByParticipantTypes(int $roomId, array $participantType): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
if (!empty($participantType)) {
|
||||
$query->andWhere($query->expr()->in('participant_type', $query->createNamedParameter($participantType, IQueryBuilder::PARAM_INT_ARRAY)));
|
||||
}
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
public function getActorsCountByType(int $roomId, string $actorType, ?int $lastJoinedCall = null): int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select($query->func()->count('*', 'num_actors'))
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->eq('actor_type', $query->createNamedParameter($actorType)));
|
||||
|
||||
if ($lastJoinedCall !== null) {
|
||||
$query->andWhere($query->expr()->gte('last_joined_call', $query->createNamedParameter($lastJoinedCall, IQueryBuilder::PARAM_INT)));
|
||||
}
|
||||
|
||||
$result = $query->executeQuery();
|
||||
$count = (int)$result->fetchOne();
|
||||
$result->closeCursor();
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $participantType
|
||||
*/
|
||||
public function countActorsByParticipantType(int $roomId, array $participantType): int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select($query->func()->count('*', 'num_actors'))
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->notIn('actor_type', $query->createNamedParameter([
|
||||
Attendee::ACTOR_CIRCLES,
|
||||
Attendee::ACTOR_GROUPS,
|
||||
], IQueryBuilder::PARAM_STR_ARRAY)));
|
||||
|
||||
if (!empty($participantType)) {
|
||||
$query->andWhere($query->expr()->in('participant_type', $query->createNamedParameter($participantType, IQueryBuilder::PARAM_INT_ARRAY)));
|
||||
}
|
||||
|
||||
$result = $query->executeQuery();
|
||||
$row = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
return (int)($row['num_actors'] ?? 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $ids
|
||||
* @return int Number of deleted entities
|
||||
*/
|
||||
public function deleteByIds(array $ids): int {
|
||||
$delete = $this->db->getQueryBuilder();
|
||||
$delete->delete($this->getTableName())
|
||||
->where($delete->expr()->in('id', $delete->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)));
|
||||
|
||||
return $delete->executeStatement();
|
||||
}
|
||||
|
||||
public function modifyPermissions(int $roomId, string $mode, int $newState): void {
|
||||
if ($mode === Attendee::PERMISSIONS_MODIFY_SET) {
|
||||
if ($newState !== Attendee::PERMISSIONS_DEFAULT) {
|
||||
$newState |= Attendee::PERMISSIONS_CUSTOM;
|
||||
}
|
||||
|
||||
$query = $this->getModifyPermissionsBaseQuery($roomId);
|
||||
$query->set('permissions', $query->createNamedParameter($newState, IQueryBuilder::PARAM_INT));
|
||||
$query->executeStatement();
|
||||
} else {
|
||||
foreach ([
|
||||
Attendee::PERMISSIONS_CALL_JOIN,
|
||||
Attendee::PERMISSIONS_CALL_START,
|
||||
Attendee::PERMISSIONS_PUBLISH_AUDIO,
|
||||
Attendee::PERMISSIONS_PUBLISH_VIDEO,
|
||||
Attendee::PERMISSIONS_PUBLISH_SCREEN,
|
||||
Attendee::PERMISSIONS_LOBBY_IGNORE,
|
||||
] as $permission) {
|
||||
if ($permission & $newState) {
|
||||
$query = $this->getModifyPermissionsBaseQuery($roomId);
|
||||
|
||||
if ($mode === Attendee::PERMISSIONS_MODIFY_ADD) {
|
||||
$this->addSinglePermission($query, $permission);
|
||||
} elseif ($mode === Attendee::PERMISSIONS_MODIFY_REMOVE) {
|
||||
$this->removeSinglePermission($query, $permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getModifyPermissionsBaseQuery(int $roomId): IQueryBuilder {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->update($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->notIn('actor_type', $query->createNamedParameter([
|
||||
Attendee::ACTOR_CIRCLES,
|
||||
Attendee::ACTOR_GROUPS,
|
||||
], IQueryBuilder::PARAM_STR_ARRAY)));
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function addSinglePermission(IQueryBuilder $query, int $permission): void {
|
||||
$query->set('permissions', $query->func()->add(
|
||||
'permissions',
|
||||
$query->createNamedParameter($permission, IQueryBuilder::PARAM_INT)
|
||||
));
|
||||
|
||||
$query->andWhere(
|
||||
$query->expr()->neq(
|
||||
$query->expr()->castColumn(
|
||||
$query->expr()->bitwiseAnd(
|
||||
'permissions',
|
||||
$permission
|
||||
),
|
||||
IQueryBuilder::PARAM_INT
|
||||
),
|
||||
$query->createNamedParameter($permission, IQueryBuilder::PARAM_INT)
|
||||
)
|
||||
);
|
||||
$query->andWhere(
|
||||
$query->expr()->neq(
|
||||
'permissions',
|
||||
$query->createNamedParameter(Attendee::PERMISSIONS_DEFAULT, IQueryBuilder::PARAM_INT)
|
||||
)
|
||||
);
|
||||
|
||||
$query->executeStatement();
|
||||
}
|
||||
|
||||
protected function removeSinglePermission(IQueryBuilder $query, int $permission): void {
|
||||
$query->set('permissions', $query->func()->subtract(
|
||||
'permissions',
|
||||
$query->createNamedParameter($permission, IQueryBuilder::PARAM_INT)
|
||||
));
|
||||
|
||||
$query->andWhere(
|
||||
$query->expr()->eq(
|
||||
$query->expr()->castColumn(
|
||||
$query->expr()->bitwiseAnd(
|
||||
'permissions',
|
||||
$permission
|
||||
),
|
||||
IQueryBuilder::PARAM_INT
|
||||
),
|
||||
$query->createNamedParameter($permission, IQueryBuilder::PARAM_INT)
|
||||
)
|
||||
);
|
||||
// Removing permissions does not need to be explicitly prevented when
|
||||
// the attendee has default permissions, as in that case it will not be
|
||||
// possible to remove the permissions anyway.
|
||||
|
||||
$query->executeStatement();
|
||||
}
|
||||
|
||||
public function createAttendeeFromRow(array $row): Attendee {
|
||||
return $this->mapRowToEntity([
|
||||
'id' => $row['a_id'],
|
||||
'room_id' => $row['room_id'],
|
||||
'actor_type' => $row['actor_type'],
|
||||
'actor_id' => $row['actor_id'],
|
||||
'display_name' => (string)$row['display_name'],
|
||||
'pin' => $row['pin'],
|
||||
'participant_type' => (int)$row['participant_type'],
|
||||
'favorite' => (bool)$row['favorite'],
|
||||
'notification_level' => (int)$row['notification_level'],
|
||||
'notification_calls' => (int)$row['notification_calls'],
|
||||
'last_joined_call' => (int)$row['last_joined_call'],
|
||||
'last_read_message' => (int)$row['last_read_message'],
|
||||
'last_mention_message' => (int)$row['last_mention_message'],
|
||||
'last_mention_direct' => (int)$row['last_mention_direct'],
|
||||
'read_privacy' => (int)$row['read_privacy'],
|
||||
'permissions' => (int)$row['permissions'],
|
||||
'access_token' => (string)$row['access_token'],
|
||||
'remote_id' => (string)$row['remote_id'],
|
||||
'invited_cloud_id' => (string)$row['invited_cloud_id'],
|
||||
'phone_number' => $row['phone_number'],
|
||||
'call_id' => $row['call_id'],
|
||||
'state' => (int)$row['state'],
|
||||
'unread_messages' => (int)$row['unread_messages'],
|
||||
'last_attendee_activity' => (int)$row['last_attendee_activity'],
|
||||
'archived' => (bool)$row['archived'],
|
||||
'important' => (bool)$row['important'],
|
||||
'sensitive' => (bool)$row['sensitive'],
|
||||
'has_unread_threads' => (bool)$row['has_unread_threads'],
|
||||
'has_unread_thread_mentions' => (bool)$row['has_unread_thread_mentions'],
|
||||
'has_unread_thread_directs' => (bool)$row['has_unread_thread_directs'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @psalm-import-type TalkBan from ResponseDefinitions
|
||||
*
|
||||
* @method void setId(int $id)
|
||||
* @method int getId()
|
||||
* @method void setModeratorActorType(string $moderatorActorType)
|
||||
* @method string getModeratorActorType()
|
||||
* @method void setModeratorActorId(string $moderatorActorId)
|
||||
* @method string getModeratorActorId()
|
||||
* @method void setModeratorDisplayname(?string $moderatorDisplayname)
|
||||
* @method null|string getModeratorDisplayname()
|
||||
* @method void setRoomId(int $roomId)
|
||||
* @method int getRoomId()
|
||||
* @method void setBannedActorType(string $bannedActorType)
|
||||
* @method string getBannedActorType()
|
||||
* @method void setBannedActorId(string $bannedActorId)
|
||||
* @method string getBannedActorId()
|
||||
* @method void setBannedDisplayname(?string $bannedDisplayname)
|
||||
* @method null|string getBannedDisplayname()
|
||||
* @method void setBannedTime(\DateTime $bannedTime)
|
||||
* @method \DateTime getBannedTime()
|
||||
* @method void setInternalNote(null|string $internalNote)
|
||||
* @method null|string getInternalNote()
|
||||
*/
|
||||
class Ban extends Entity implements \JsonSerializable {
|
||||
public const NOTE_MAX_LENGTH = 4000;
|
||||
|
||||
protected string $moderatorActorType = '';
|
||||
protected string $moderatorActorId = '';
|
||||
protected ?string $moderatorDisplayname = null;
|
||||
protected int $roomId = 0;
|
||||
protected string $bannedActorType = '';
|
||||
protected string $bannedActorId = '';
|
||||
protected ?string $bannedDisplayname = null;
|
||||
protected ?\DateTime $bannedTime = null;
|
||||
protected ?string $internalNote = null;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('id', Types::BIGINT);
|
||||
$this->addType('moderatorActorType', Types::STRING);
|
||||
$this->addType('moderatorActorId', Types::STRING);
|
||||
$this->addType('moderatorDisplayname', Types::STRING);
|
||||
$this->addType('roomId', Types::BIGINT);
|
||||
$this->addType('bannedActorType', Types::STRING);
|
||||
$this->addType('bannedActorId', Types::STRING);
|
||||
$this->addType('bannedDisplayname', Types::STRING);
|
||||
$this->addType('bannedTime', Types::DATETIME);
|
||||
$this->addType('internalNote', Types::TEXT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TalkBan
|
||||
*/
|
||||
#[\Override]
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'moderatorActorType' => $this->getModeratorActorType(),
|
||||
'moderatorActorId' => $this->getModeratorActorId(),
|
||||
'moderatorDisplayName' => $this->getModeratorDisplayname() ?? $this->getModeratorActorId(),
|
||||
'bannedActorType' => $this->getBannedActorType(),
|
||||
'bannedActorId' => $this->getBannedActorId(),
|
||||
'bannedDisplayName' => $this->getBannedDisplayname() ?? $this->getBannedActorId(),
|
||||
'bannedTime' => $this->getBannedTime()->getTimestamp(),
|
||||
'internalNote' => $this->getInternalNote() ?? '',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method Ban mapRowToEntity(array $row)
|
||||
* @method Ban findEntity(IQueryBuilder $query)
|
||||
* @method list<Ban> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<Ban>
|
||||
*/
|
||||
class BanMapper extends QBMapper {
|
||||
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'talk_bans', Ban::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findForBannedActorAndRoom(string $bannedActorType, string $bannedActorId, int $roomId): Ban {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('banned_actor_type', $query->createNamedParameter($bannedActorType, IQueryBuilder::PARAM_STR)))
|
||||
->andWhere($query->expr()->eq('banned_actor_id', $query->createNamedParameter($bannedActorId, IQueryBuilder::PARAM_STR)))
|
||||
->andWhere($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Ban>
|
||||
*/
|
||||
public function findByRoomId(int $roomId, ?string $bannedActorType = null): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)))
|
||||
->orderBy('id', 'ASC');
|
||||
|
||||
if ($bannedActorType !== null) {
|
||||
$query->andWhere($query->expr()->eq('banned_actor_type', $query->createNamedParameter($bannedActorType, IQueryBuilder::PARAM_STR)));
|
||||
}
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
public function findByUserId(string $userId): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('banned_actor_type', $query->createNamedParameter(Attendee::ACTOR_USERS, IQueryBuilder::PARAM_STR)))
|
||||
->andWhere($query->expr()->eq('banned_actor_id', $query->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
|
||||
->orderBy('id', 'ASC');
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findByBanIdAndRoom(int $banId, int $roomId): Ban {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('id', $query->createNamedParameter($banId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
public function updateDisplayNameForActor(string $actorType, string $actorId, string $displayName): void {
|
||||
$update = $this->db->getQueryBuilder();
|
||||
$update->update($this->getTableName())
|
||||
->set('moderator_displayname', $update->createNamedParameter($displayName))
|
||||
->where($update->expr()->eq('moderator_actor_type', $update->createNamedParameter($actorType)))
|
||||
->andWhere($update->expr()->eq('moderator_actor_id', $update->createNamedParameter($actorId)));
|
||||
$update->executeStatement();
|
||||
|
||||
$update = $this->db->getQueryBuilder();
|
||||
$update->update($this->getTableName())
|
||||
->set('banned_displayname', $update->createNamedParameter($displayName))
|
||||
->where($update->expr()->eq('banned_actor_type', $update->createNamedParameter($actorType)))
|
||||
->andWhere($update->expr()->eq('banned_actor_id', $update->createNamedParameter($actorId)));
|
||||
$update->executeStatement();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
class Bot {
|
||||
public const STATE_DISABLED = 0;
|
||||
public const STATE_ENABLED = 1;
|
||||
public const STATE_NO_SETUP = 2;
|
||||
public const STATE_UNAVAILABLE = 3;
|
||||
|
||||
public const FEATURE_NONE = 0;
|
||||
public const FEATURE_WEBHOOK = 1;
|
||||
public const FEATURE_RESPONSE = 2;
|
||||
public const FEATURE_EVENT = 4;
|
||||
public const FEATURE_REACTION = 8;
|
||||
|
||||
public const FEATURE_LABEL_NONE = 'none';
|
||||
public const FEATURE_LABEL_WEBHOOK = 'webhook';
|
||||
public const FEATURE_LABEL_RESPONSE = 'response';
|
||||
public const FEATURE_LABEL_EVENT = 'event';
|
||||
public const FEATURE_LABEL_REACTION = 'reaction';
|
||||
public const URL_APP_PREFIX = 'nextcloudapp://';
|
||||
public const URL_RESPONSE_ONLY_PREFIX = 'responseonly://';
|
||||
|
||||
public const FEATURE_MAP = [
|
||||
self::FEATURE_NONE => self::FEATURE_LABEL_NONE,
|
||||
self::FEATURE_WEBHOOK => self::FEATURE_LABEL_WEBHOOK,
|
||||
self::FEATURE_RESPONSE => self::FEATURE_LABEL_RESPONSE,
|
||||
self::FEATURE_EVENT => self::FEATURE_LABEL_EVENT,
|
||||
self::FEATURE_REACTION => self::FEATURE_LABEL_REACTION,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
protected BotServer $botServer,
|
||||
protected BotConversation $botConversation,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getBotServer(): BotServer {
|
||||
return $this->botServer;
|
||||
}
|
||||
|
||||
public function getBotConversation(): BotConversation {
|
||||
return $this->botConversation;
|
||||
}
|
||||
|
||||
public function isEnabled(): bool {
|
||||
return $this->botServer->getState() !== self::STATE_DISABLED
|
||||
&& $this->botConversation->getState() !== self::STATE_DISABLED;
|
||||
}
|
||||
|
||||
public static function featureFlagsToLabels(int $flags): string {
|
||||
if ($flags === self::FEATURE_NONE) {
|
||||
return self::FEATURE_LABEL_NONE;
|
||||
}
|
||||
|
||||
$features = [];
|
||||
foreach (self::FEATURE_MAP as $flag => $label) {
|
||||
if ($flags & $flag) {
|
||||
$features[] = $label;
|
||||
}
|
||||
}
|
||||
return implode(', ', $features);
|
||||
}
|
||||
|
||||
public static function featureLabelsToFlags(array $labels): int {
|
||||
$reverseMap = array_flip(self::FEATURE_MAP);
|
||||
$flags = 0;
|
||||
foreach ($labels as $label) {
|
||||
if ($label === self::FEATURE_LABEL_NONE) {
|
||||
return self::FEATURE_NONE;
|
||||
}
|
||||
if (isset($reverseMap[$label])) {
|
||||
$flags += $reverseMap[$label];
|
||||
}
|
||||
}
|
||||
|
||||
return $flags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @method void setBotId(int $botId)
|
||||
* @method int getBotId()
|
||||
* @method void setToken(string $token)
|
||||
* @method string getToken()
|
||||
* @method void setState(int $state)
|
||||
* @method int getState()
|
||||
*/
|
||||
class BotConversation extends Entity implements \JsonSerializable {
|
||||
protected int $botId = 0;
|
||||
protected string $token = '';
|
||||
protected int $state = Bot::STATE_DISABLED;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('bot_id', Types::BIGINT);
|
||||
$this->addType('token', Types::STRING);
|
||||
$this->addType('state', Types::SMALLINT);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'bot_id' => $this->getBotId(),
|
||||
'token' => $this->getToken(),
|
||||
'state' => $this->getState(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\AppFramework\Db\TTransactional;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method BotConversation mapRowToEntity(array $row)
|
||||
* @method BotConversation findEntity(IQueryBuilder $query)
|
||||
* @method list<BotConversation> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<BotConversation>
|
||||
*/
|
||||
class BotConversationMapper extends QBMapper {
|
||||
use TTransactional;
|
||||
|
||||
public function __construct(
|
||||
IDBConnection $db,
|
||||
) {
|
||||
parent::__construct($db, 'talk_bots_conversation', BotConversation::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<BotConversation>
|
||||
*/
|
||||
public function findForToken(string $token): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('token', $query->createNamedParameter($token)));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
public function deleteByBotId(int $botId): int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('bot_id', $query->createNamedParameter($botId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $query->executeStatement();
|
||||
}
|
||||
|
||||
public function deleteByBotIdAndTokens(int $botId, array $tokens): int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('bot_id', $query->createNamedParameter($botId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->in('token', $query->createNamedParameter($tokens, IQueryBuilder::PARAM_STR_ARRAY)));
|
||||
|
||||
return $query->executeStatement();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @method void setName(string $name)
|
||||
* @method string getName()
|
||||
* @method void setUrl(string $url)
|
||||
* @method string getUrl()
|
||||
* @method void setUrlHash(string $urlHash)
|
||||
* @method string getUrlHash()
|
||||
* @method void setDescription(?string $description)
|
||||
* @method null|string getDescription()
|
||||
* @method void setSecret(string $secret)
|
||||
* @method string getSecret()
|
||||
* @method void setErrorCount(int $errorCount)
|
||||
* @method int getErrorCount()
|
||||
* @method void setLastErrorDate(?\DateTimeImmutable $lastErrorDate)
|
||||
* @method ?\DateTimeImmutable getLastErrorDate()
|
||||
* @method void setLastErrorMessage(string $lastErrorMessage)
|
||||
* @method string getLastErrorMessage()
|
||||
* @method void setState(int $state)
|
||||
* @method int getState()
|
||||
* @method void setFeatures(int $features)
|
||||
* @method int getFeatures()
|
||||
*/
|
||||
class BotServer extends Entity implements \JsonSerializable {
|
||||
protected string $name = '';
|
||||
protected string $url = '';
|
||||
protected string $urlHash = '';
|
||||
protected ?string $description = null;
|
||||
protected string $secret = '';
|
||||
protected int $errorCount = 0;
|
||||
protected ?\DateTimeImmutable $lastErrorDate = null;
|
||||
protected ?string $lastErrorMessage = null;
|
||||
protected int $state = Bot::STATE_DISABLED;
|
||||
protected int $features = Bot::FEATURE_NONE;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('name', Types::STRING);
|
||||
$this->addType('url', Types::STRING);
|
||||
$this->addType('url_hash', Types::STRING);
|
||||
$this->addType('description', Types::STRING);
|
||||
$this->addType('secret', Types::STRING);
|
||||
$this->addType('error_count', Types::BIGINT);
|
||||
$this->addType('last_error_date', Types::DATETIME);
|
||||
$this->addType('last_error_message', Types::STRING);
|
||||
$this->addType('state', Types::SMALLINT);
|
||||
$this->addType('features', Types::INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* id: int,
|
||||
* name: string,
|
||||
* url: string,
|
||||
* url_hash: string,
|
||||
* description: ?string,
|
||||
* secret: string,
|
||||
* error_count: int,
|
||||
* last_error_date: int,
|
||||
* last_error_message: string,
|
||||
* state: int,
|
||||
* features: int,
|
||||
* }
|
||||
*/
|
||||
#[\Override]
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'name' => $this->getName(),
|
||||
'url' => $this->getUrl(),
|
||||
'url_hash' => $this->getUrlHash(),
|
||||
'description' => $this->getDescription(),
|
||||
'secret' => $this->getSecret(),
|
||||
'error_count' => $this->getErrorCount(),
|
||||
'last_error_date' => $this->getLastErrorDate() ? $this->getLastErrorDate()->getTimestamp() : 0,
|
||||
'last_error_message' => $this->getLastErrorMessage(),
|
||||
'state' => $this->getState(),
|
||||
'features' => $this->getFeatures(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\AppFramework\Db\TTransactional;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method BotServer mapRowToEntity(array $row)
|
||||
* @method BotServer findEntity(IQueryBuilder $query)
|
||||
* @method list<BotServer> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<BotServer>
|
||||
*/
|
||||
class BotServerMapper extends QBMapper {
|
||||
use TTransactional;
|
||||
|
||||
public function __construct(
|
||||
IDBConnection $db,
|
||||
) {
|
||||
parent::__construct($db, 'talk_bots_server', BotServer::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findById(int $botId): BotServer {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('id', $query->createNamedParameter($botId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findByUrlAndSecret(string $url, string $secret): BotServer {
|
||||
$urlHash = sha1($url);
|
||||
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('url_hash', $query->createNamedParameter($urlHash)))
|
||||
->andWhere($query->expr()->eq('secret', $query->createNamedParameter($secret)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findByUrl(string $url): BotServer {
|
||||
return $this->findByUrlHash(sha1($url));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findByUrlHash(string $urlHash): BotServer {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('url_hash', $query->createNamedParameter($urlHash)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
public function deleteById(int $botId): int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('id', $query->createNamedParameter($botId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $query->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<BotServer>
|
||||
*/
|
||||
public function findByIds(array $botIds): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->in('id', $query->createNamedParameter($botIds, IQueryBuilder::PARAM_INT_ARRAY)));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<BotServer>
|
||||
*/
|
||||
public function getAllBots(): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName());
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
class BreakoutRoom {
|
||||
public const MODE_NOT_CONFIGURED = 0;
|
||||
public const MODE_AUTOMATIC = 1;
|
||||
public const MODE_MANUAL = 2;
|
||||
public const MODE_FREE = 3;
|
||||
|
||||
public const STATUS_STOPPED = 0;
|
||||
public const STATUS_STARTED = 1;
|
||||
public const STATUS_ASSISTANCE_RESET = 0;
|
||||
public const STATUS_ASSISTANCE_REQUESTED = 2;
|
||||
|
||||
public const MINIMUM_ROOM_AMOUNT = 1;
|
||||
public const MAXIMUM_ROOM_AMOUNT = 20;
|
||||
|
||||
public const PARENT_OBJECT_TYPE = 'room';
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @method void setToken(string $token)
|
||||
* @method string getToken()
|
||||
* @method void setActorType(string $actorType)
|
||||
* @method string getActorType()
|
||||
* @method void setActorId(string $actorId)
|
||||
* @method string getActorId()
|
||||
* @method void setDateTime(\DateTime $dateTime)
|
||||
* @method \DateTime getDateTime()
|
||||
*/
|
||||
class Consent extends Entity implements \JsonSerializable {
|
||||
protected string $token = '';
|
||||
protected string $actorType = '';
|
||||
protected string $actorId = '';
|
||||
protected ?\DateTime $dateTime = null;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('token', Types::STRING);
|
||||
$this->addType('actorType', Types::STRING);
|
||||
$this->addType('actorId', Types::STRING);
|
||||
$this->addType('dateTime', Types::DATETIME);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
'token' => $this->getToken(),
|
||||
'actorType' => $this->getActorType(),
|
||||
'actorId' => $this->getActorId(),
|
||||
'timestamp' => $this->getDateTime()->getTimestamp(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method Consent mapRowToEntity(array $row)
|
||||
* @method Consent findEntity(IQueryBuilder $query)
|
||||
* @method list<Consent> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<Consent>
|
||||
*/
|
||||
class ConsentMapper extends QBMapper {
|
||||
public function __construct(
|
||||
IDBConnection $db,
|
||||
) {
|
||||
parent::__construct($db, 'talk_consent', Consent::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Consent>
|
||||
*/
|
||||
public function findForToken(string $token): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('token', $query->createNamedParameter($token)));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
public function deleteByToken(string $token): int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('token', $query->createNamedParameter($token)));
|
||||
|
||||
return $query->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Consent>
|
||||
*/
|
||||
public function findForActor(string $actorType, string $actorId): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('actor_type', $query->createNamedParameter($actorType)))
|
||||
->andWhere($query->expr()->eq('actor_id', $query->createNamedParameter($actorId)));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
public function deleteByActor(string $actorType, string $actorId): int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('actor_type', $query->createNamedParameter($actorType)))
|
||||
->andWhere($query->expr()->eq('actor_id', $query->createNamedParameter($actorId)));
|
||||
|
||||
return $query->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Consent>
|
||||
*/
|
||||
public function findForTokenByActor(string $token, string $actorType, string $actorId): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('token', $query->createNamedParameter($token)))
|
||||
->andWhere($query->expr()->eq('actor_type', $query->createNamedParameter($actorType)))
|
||||
->andWhere($query->expr()->eq('actor_id', $query->createNamedParameter($actorId)));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @method void setUserId(string $userId)
|
||||
* @method string getUserId()
|
||||
* @method void setState(int $state)
|
||||
* @method int getState()
|
||||
* @method void setLocalRoomId(int $localRoomId)
|
||||
* @method int getLocalRoomId()
|
||||
* @method void setAccessToken(string $accessToken)
|
||||
* @method string getAccessToken()
|
||||
* @method void setRemoteServerUrl(string $remoteServerUrl)
|
||||
* @method string getRemoteServerUrl()
|
||||
* @method void setRemoteToken(string $remoteToken)
|
||||
* @method string getRemoteToken()
|
||||
* @method void setRemoteAttendeeId(int $remoteAttendeeId)
|
||||
* @method int getRemoteAttendeeId()
|
||||
* @method void setInviterCloudId(string $inviterCloudId)
|
||||
* @method string getInviterCloudId()
|
||||
* @method void setInviterDisplayName(string $inviterDisplayName)
|
||||
* @method string getInviterDisplayName()
|
||||
* @method void setLocalCloudId(string $localCloudId)
|
||||
* @method string getLocalCloudId()
|
||||
*/
|
||||
class Invitation extends Entity implements \JsonSerializable {
|
||||
public const STATE_PENDING = 0;
|
||||
public const STATE_ACCEPTED = 1;
|
||||
|
||||
protected string $userId = '';
|
||||
protected int $state = self::STATE_PENDING;
|
||||
protected int $localRoomId = 0;
|
||||
protected string $accessToken = '';
|
||||
protected string $remoteServerUrl = '';
|
||||
protected string $remoteToken = '';
|
||||
protected int $remoteAttendeeId = 0;
|
||||
protected string $inviterCloudId = '';
|
||||
protected string $inviterDisplayName = '';
|
||||
protected string $localCloudId = '';
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('userId', Types::STRING);
|
||||
$this->addType('state', Types::SMALLINT);
|
||||
$this->addType('localRoomId', Types::BIGINT);
|
||||
$this->addType('accessToken', Types::STRING);
|
||||
$this->addType('remoteServerUrl', Types::STRING);
|
||||
$this->addType('remoteToken', Types::STRING);
|
||||
$this->addType('remoteAttendeeId', Types::BIGINT);
|
||||
$this->addType('inviterCloudId', Types::STRING);
|
||||
$this->addType('inviterDisplayName', Types::STRING);
|
||||
$this->addType('localCloudId', Types::STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{id: int, localCloudId: string, remoteAttendeeId: int, remoteServerUrl: string, remoteToken: string, state: int, userId: string, inviterCloudId: string, inviterDisplayName: string}
|
||||
*/
|
||||
#[\Override]
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'userId' => $this->getUserId(),
|
||||
'state' => $this->getState(),
|
||||
'localCloudId' => $this->getLocalCloudId(),
|
||||
'remoteServerUrl' => $this->getRemoteServerUrl(),
|
||||
'remoteToken' => $this->getRemoteToken(),
|
||||
'remoteAttendeeId' => $this->getRemoteAttendeeId(),
|
||||
'inviterCloudId' => $this->getInviterCloudId(),
|
||||
'inviterDisplayName' => $this->getInviterDisplayName() ?: $this->getInviterCloudId(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Circles\Model\Circle;
|
||||
use OCP\Federation\ICloudId;
|
||||
use OCP\IGroup;
|
||||
use OCP\IUser;
|
||||
|
||||
class InvitationList {
|
||||
|
||||
/** @var array<string, IUser> */
|
||||
protected array $validUsers = [];
|
||||
/** @var list<string> */
|
||||
protected array $invalidUsers = [];
|
||||
|
||||
/** @var array<string, ICloudId> */
|
||||
protected array $validFederatedUsers = [];
|
||||
/** @var list<string> */
|
||||
protected array $invalidFederatedUsers = [];
|
||||
|
||||
/** @var array<string, IGroup> */
|
||||
protected array $validGroups = [];
|
||||
/** @var list<string> */
|
||||
protected array $invalidGroups = [];
|
||||
|
||||
/** @var array<string, Circle> */
|
||||
protected array $validTeams = [];
|
||||
/** @var list<string> */
|
||||
protected array $invalidTeams = [];
|
||||
|
||||
/** @var array<string, string> */
|
||||
protected array $validEmails = [];
|
||||
/** @var list<string> */
|
||||
protected array $invalidEmails = [];
|
||||
|
||||
/** @var array<string, string> */
|
||||
protected array $validPhoneNumbers = [];
|
||||
/** @var list<string> */
|
||||
protected array $invalidPhoneNumbers = [];
|
||||
|
||||
/**
|
||||
* @param array<string, IUser> $valid
|
||||
* @param list<string> $invalid
|
||||
*/
|
||||
public function setUserResults(array $valid, array $invalid): void {
|
||||
$this->validUsers = $valid;
|
||||
$this->invalidUsers = $invalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, ICloudId> $valid
|
||||
* @param list<string> $invalid
|
||||
*/
|
||||
public function setFederatedUserResults(array $valid, array $invalid): void {
|
||||
$this->validFederatedUsers = $valid;
|
||||
$this->invalidFederatedUsers = $invalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, IGroup> $valid
|
||||
* @param list<string> $invalid
|
||||
*/
|
||||
public function setGroupResults(array $valid, array $invalid): void {
|
||||
$this->validGroups = $valid;
|
||||
$this->invalidGroups = $invalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, Circle> $valid
|
||||
* @param list<string> $invalid
|
||||
*/
|
||||
public function setTeamResults(array $valid, array $invalid): void {
|
||||
$this->validTeams = $valid;
|
||||
$this->invalidTeams = $invalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $valid
|
||||
* @param list<string> $invalid
|
||||
*/
|
||||
public function setEmailResults(array $valid, array $invalid): void {
|
||||
$this->validEmails = $valid;
|
||||
$this->invalidEmails = $invalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $valid
|
||||
* @param list<string> $invalid
|
||||
*/
|
||||
public function setPhoneNumberResults(array $valid, array $invalid): void {
|
||||
$this->validPhoneNumbers = $valid;
|
||||
$this->invalidPhoneNumbers = $invalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, IUser>
|
||||
*/
|
||||
public function getUsers(): array {
|
||||
return $this->validUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, ICloudId>
|
||||
*/
|
||||
public function getFederatedUsers(): array {
|
||||
return $this->validFederatedUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, IGroup>
|
||||
*/
|
||||
public function getGroup(): array {
|
||||
return $this->validGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, Circle>
|
||||
*/
|
||||
public function getTeams(): array {
|
||||
return $this->validTeams;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getEmails(): array {
|
||||
return $this->validEmails;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getPhoneNumbers(): array {
|
||||
return $this->validPhoneNumbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<'users'|'federated_users'|'groups'|'emails'|'phones'|'teams', list<string>>
|
||||
*/
|
||||
public function getInvalidList(): array {
|
||||
$response = [
|
||||
'users' => $this->invalidUsers,
|
||||
'federated_users' => $this->invalidFederatedUsers,
|
||||
'groups' => $this->invalidGroups,
|
||||
'teams' => $this->invalidTeams,
|
||||
'emails' => $this->invalidEmails,
|
||||
'phones' => $this->invalidPhoneNumbers,
|
||||
];
|
||||
return array_filter($response);
|
||||
}
|
||||
|
||||
public function hasValidInvitations(): bool {
|
||||
return !empty($this->validUsers)
|
||||
|| !empty($this->validFederatedUsers)
|
||||
|| !empty($this->validGroups)
|
||||
|| !empty($this->validTeams)
|
||||
|| !empty($this->validEmails)
|
||||
|| !empty($this->validPhoneNumbers);
|
||||
}
|
||||
|
||||
public function hasInvalidInvitations(): bool {
|
||||
return !empty($this->invalidUsers)
|
||||
|| !empty($this->invalidFederatedUsers)
|
||||
|| !empty($this->invalidGroups)
|
||||
|| !empty($this->invalidTeams)
|
||||
|| !empty($this->invalidEmails)
|
||||
|| !empty($this->invalidPhoneNumbers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Talk\Room;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IUser;
|
||||
use SensitiveParameter;
|
||||
|
||||
/**
|
||||
* Class InvitationMapper
|
||||
*
|
||||
* @package OCA\Talk\Model
|
||||
*
|
||||
* @method Invitation mapRowToEntity(array $row)
|
||||
* @method Invitation findEntity(IQueryBuilder $query)
|
||||
* @method list<Invitation> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<Invitation>
|
||||
*/
|
||||
class InvitationMapper extends QBMapper {
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'talk_invitations', Invitation::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function getInvitationById(int $id): Invitation {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
|
||||
|
||||
return $this->findEntity($qb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
* @internal Does not check user relation
|
||||
*/
|
||||
public function getByRemoteServerAndAccessToken(
|
||||
string $remoteServerUrl,
|
||||
#[SensitiveParameter]
|
||||
string $accessToken,
|
||||
): Invitation {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('remote_server_url', $qb->createNamedParameter($remoteServerUrl)))
|
||||
->andWhere($qb->expr()->eq('access_token', $qb->createNamedParameter($accessToken)));
|
||||
|
||||
return $this->findEntity($qb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function getByRemoteAndAccessToken(
|
||||
string $remoteServerUrl,
|
||||
int $remoteAttendeeId,
|
||||
#[SensitiveParameter]
|
||||
string $accessToken,
|
||||
): Invitation {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('remote_server_url', $qb->createNamedParameter($remoteServerUrl)))
|
||||
->andWhere($qb->expr()->eq('remote_attendee_id', $qb->createNamedParameter($remoteAttendeeId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($qb->expr()->eq('access_token', $qb->createNamedParameter($accessToken)));
|
||||
|
||||
return $this->findEntity($qb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IUser $user
|
||||
* @return list<Invitation>
|
||||
*/
|
||||
public function getInvitationsForUser(IUser $user): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
|
||||
$qb->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID())));
|
||||
|
||||
return $this->findEntities($qb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param Invitation::STATE_*|null $state
|
||||
*/
|
||||
public function countInvitationsForUser(IUser $user, ?int $state = null): int {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
|
||||
$qb->select($qb->func()->count('*'))
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('user_id', $qb->createNamedParameter($user->getUID())));
|
||||
|
||||
if ($state !== null) {
|
||||
$qb->andWhere($qb->expr()->eq('state', $qb->createNamedParameter($state)));
|
||||
}
|
||||
|
||||
$result = $qb->executeQuery();
|
||||
$count = (int)$result->fetchOne();
|
||||
$result->closeCursor();
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function getInvitationForUserByLocalRoom(Room $room, string $userId, bool $caseInsensitive = false): Invitation {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('local_room_id', $query->createNamedParameter($room->getId())));
|
||||
|
||||
if ($caseInsensitive) {
|
||||
$query->andWhere($query->expr()->eq($query->func()->lower('user_id'), $query->createNamedParameter(strtolower($userId))));
|
||||
} else {
|
||||
$query->andWhere($query->expr()->eq('user_id', $query->createNamedParameter($userId)));
|
||||
}
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
public function countInvitationsForLocalRoom(Room $room): int {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
|
||||
$qb->select($qb->func()->count('*', 'num_invitations'))
|
||||
->from($this->getTableName())
|
||||
->where($qb->expr()->eq('local_room_id', $qb->createNamedParameter($room->getId())));
|
||||
|
||||
$result = $qb->executeQuery();
|
||||
$row = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
return (int)($row['num_invitations'] ?? 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Talk\Chat\ChatManager;
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCA\Talk\Room;
|
||||
use OCP\Comments\IComment;
|
||||
use OCP\IL10N;
|
||||
|
||||
/**
|
||||
* @psalm-import-type TalkChatMessage from ResponseDefinitions
|
||||
*/
|
||||
class Message {
|
||||
public const METADATA_LAST_EDITED_BY_TYPE = 'last_edited_by_type';
|
||||
public const METADATA_LAST_EDITED_BY_ID = 'last_edited_by_id';
|
||||
public const METADATA_LAST_EDITED_TIME = 'last_edited_time';
|
||||
public const METADATA_SILENT = 'silent';
|
||||
public const METADATA_CAN_MENTION_ALL = 'can_mention_all';
|
||||
public const METADATA_THREAD_ID = 'thread_id';
|
||||
|
||||
/** @var bool */
|
||||
protected $visible = true;
|
||||
|
||||
/** @var string */
|
||||
protected $type = '';
|
||||
|
||||
/** @var string */
|
||||
protected $message = '';
|
||||
|
||||
/** @var string */
|
||||
protected $rawMessage = '';
|
||||
|
||||
/** @var array */
|
||||
protected $parameters = [];
|
||||
|
||||
/** @var string */
|
||||
protected $actorType = '';
|
||||
|
||||
/** @var string */
|
||||
protected $actorId = '';
|
||||
|
||||
/** @var string */
|
||||
protected $actorDisplayName = '';
|
||||
|
||||
/** @var string */
|
||||
protected $lastEditActorType = '';
|
||||
|
||||
/** @var string */
|
||||
protected $lastEditActorId = '';
|
||||
|
||||
/** @var string */
|
||||
protected $lastEditActorDisplayName = '';
|
||||
|
||||
/** @var int */
|
||||
protected $lastEditTimestamp = 0;
|
||||
|
||||
public function __construct(
|
||||
protected Room $room,
|
||||
protected ?Participant $participant,
|
||||
protected ?IComment $comment,
|
||||
protected IL10N $l,
|
||||
protected ?ProxyCacheMessage $proxy = null,
|
||||
) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Meta information
|
||||
*/
|
||||
|
||||
public function getRoom(): Room {
|
||||
return $this->room;
|
||||
}
|
||||
|
||||
public function getComment(): ?IComment {
|
||||
return $this->comment;
|
||||
}
|
||||
|
||||
public function getL10n(): IL10N {
|
||||
return $this->l;
|
||||
}
|
||||
|
||||
public function getParticipant(): ?Participant {
|
||||
return $this->participant;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parsed message information
|
||||
*/
|
||||
|
||||
public function getMessageId(): int {
|
||||
return $this->comment ? (int)$this->comment->getId() : $this->proxy->getRemoteMessageId();
|
||||
}
|
||||
|
||||
public function getExpirationDateTime(): ?\DateTimeInterface {
|
||||
return $this->comment ? $this->comment->getExpireDate() : $this->proxy->getExpirationDatetime();
|
||||
}
|
||||
|
||||
public function setVisibility(bool $visible): void {
|
||||
$this->visible = $visible;
|
||||
}
|
||||
|
||||
public function getVisibility(): bool {
|
||||
return $this->visible;
|
||||
}
|
||||
|
||||
public function setMessage(string $message, array $parameters, string $rawMessage = ''): void {
|
||||
$this->message = $message;
|
||||
$this->parameters = $parameters;
|
||||
$this->rawMessage = $rawMessage;
|
||||
}
|
||||
|
||||
public function getMessage(): string {
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function getMessageParameters(): array {
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
public function getMessageRaw(): string {
|
||||
return $this->rawMessage;
|
||||
}
|
||||
|
||||
public function setMessageType(string $type): void {
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function getMessageType(): string {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setActor(string $type, string $id, string $displayName): void {
|
||||
$this->actorType = $type;
|
||||
$this->actorId = $id;
|
||||
$this->actorDisplayName = $displayName;
|
||||
}
|
||||
|
||||
public function setLastEdit(string $type, string $id, string $displayName, int $timestamp): void {
|
||||
$this->lastEditActorType = $type;
|
||||
$this->lastEditActorId = $id;
|
||||
$this->lastEditActorDisplayName = $displayName;
|
||||
$this->lastEditTimestamp = $timestamp;
|
||||
}
|
||||
|
||||
public function getActorType(): string {
|
||||
return $this->actorType;
|
||||
}
|
||||
|
||||
public function getActorId(): string {
|
||||
return $this->actorId;
|
||||
}
|
||||
|
||||
public function getActorDisplayName(): string {
|
||||
return $this->actorDisplayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether a message can be replied to
|
||||
*/
|
||||
public function isReplyable(): bool {
|
||||
return $this->getMessageType() !== ChatManager::VERB_SYSTEM
|
||||
&& $this->getMessageType() !== ChatManager::VERB_COMMAND
|
||||
&& $this->getMessageType() !== ChatManager::VERB_MESSAGE_DELETED
|
||||
&& $this->getMessageType() !== ChatManager::VERB_REACTION
|
||||
&& $this->getMessageType() !== ChatManager::VERB_REACTION_DELETED
|
||||
&& \in_array($this->getActorType(), [
|
||||
Attendee::ACTOR_USERS,
|
||||
Attendee::ACTOR_FEDERATED_USERS,
|
||||
Attendee::ACTOR_GUESTS,
|
||||
Attendee::ACTOR_EMAILS,
|
||||
Attendee::ACTOR_BOTS,
|
||||
], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $format
|
||||
* @psalm-param 'json'|'xml' $format
|
||||
* @return TalkChatMessage
|
||||
*/
|
||||
public function toArray(string $format, ?Thread $thread): array {
|
||||
$expireDate = $this->getComment()->getExpireDate();
|
||||
|
||||
$reactions = $this->getComment()->getReactions();
|
||||
if ($format === 'json' && empty($reactions)) {
|
||||
// Cheating here to make sure the reactions array is always a
|
||||
// JSON object on the API, even when there is no reaction at all.
|
||||
$reactions = new \stdClass();
|
||||
}
|
||||
|
||||
$id = (int)$this->getComment()->getId();
|
||||
$threadId = (int)$this->getComment()->getTopmostParentId() ?: $id;
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'token' => $this->getRoom()->getToken(),
|
||||
'actorType' => $this->getActorType(),
|
||||
'actorId' => $this->getActorId(),
|
||||
'actorDisplayName' => $this->getActorDisplayName(),
|
||||
'timestamp' => $this->getComment()->getCreationDateTime()->getTimestamp(),
|
||||
'message' => $this->getMessage(),
|
||||
'messageParameters' => $this->getMessageParameters(),
|
||||
'systemMessage' => $this->getMessageType() === ChatManager::VERB_SYSTEM ? $this->getMessageRaw() : '',
|
||||
'messageType' => $this->getMessageType(),
|
||||
'isReplyable' => $this->isReplyable(),
|
||||
'referenceId' => (string)$this->getComment()->getReferenceId(),
|
||||
'reactions' => $reactions,
|
||||
'expirationTimestamp' => $expireDate ? $expireDate->getTimestamp() : 0,
|
||||
'markdown' => $this->getMessageType() === ChatManager::VERB_SYSTEM ? false : true,
|
||||
'threadId' => $threadId,
|
||||
];
|
||||
if ($thread !== null) {
|
||||
$data['isThread'] = true;
|
||||
$data['threadTitle'] = $thread->getName();
|
||||
$data['threadReplies'] = $thread->getNumReplies();
|
||||
}
|
||||
|
||||
if ($this->lastEditActorType && $this->lastEditActorId && $this->lastEditTimestamp) {
|
||||
$data['lastEditActorType'] = $this->lastEditActorType;
|
||||
$data['lastEditActorId'] = $this->lastEditActorId;
|
||||
$data['lastEditActorDisplayName'] = $this->lastEditActorDisplayName;
|
||||
$data['lastEditTimestamp'] = $this->lastEditTimestamp;
|
||||
}
|
||||
|
||||
if ($this->getMessageType() === ChatManager::VERB_MESSAGE_DELETED) {
|
||||
$data['deleted'] = true;
|
||||
}
|
||||
|
||||
$metaData = $this->getComment()->getMetaData() ?? [];
|
||||
if (!empty($metaData[self::METADATA_SILENT])) {
|
||||
$data[self::METADATA_SILENT] = true;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @psalm-method int<1, max> getId()
|
||||
* @method void setPhoneNumber(string $phoneNumber)
|
||||
* @method string getPhoneNumber()
|
||||
* @method void setActorId(string $actorId)
|
||||
* @method string getActorId()
|
||||
*/
|
||||
class PhoneNumber extends Entity {
|
||||
protected string $phoneNumber = '';
|
||||
protected string $actorId = '';
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('phoneNumber', Types::STRING);
|
||||
$this->addType('actorId', Types::STRING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\AppFramework\Db\TTransactional;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method PhoneNumber mapRowToEntity(array $row)
|
||||
* @method PhoneNumber findEntity(IQueryBuilder $query)
|
||||
* @method list<PhoneNumber> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<PhoneNumber>
|
||||
*/
|
||||
class PhoneNumberMapper extends QBMapper {
|
||||
use TTransactional;
|
||||
|
||||
public function __construct(
|
||||
IDBConnection $db,
|
||||
) {
|
||||
parent::__construct($db, 'talk_phone_numbers', PhoneNumber::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findByPhoneNumber(string $phoneNumber): PhoneNumber {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('phone_number', $query->createNamedParameter($phoneNumber, IQueryBuilder::PARAM_STR)))
|
||||
->orderBy('id', 'ASC');
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<PhoneNumber>
|
||||
*/
|
||||
public function findByUser(string $userId): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('actor_id', $query->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<PhoneNumber>
|
||||
*/
|
||||
public function findByPhoneNumbers(array $phoneNumbers): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->in('phone_number', $query->createNamedParameter($phoneNumbers, IQueryBuilder::PARAM_STR_ARRAY)));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
public function deleteByPhoneNumber(string $phoneNumber): void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('phone_number', $query->createNamedParameter($phoneNumber, IQueryBuilder::PARAM_STR)));
|
||||
|
||||
$query->executeStatement();
|
||||
}
|
||||
|
||||
public function deleteByUser(string $userId): void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('actor_id', $query->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
|
||||
|
||||
$query->executeStatement();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Talk\Exceptions\PollPropertyException;
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @psalm-method int<1, max> getId()
|
||||
* @method void setRoomId(int $roomId)
|
||||
* @method int getRoomId()
|
||||
* @psalm-method int<1, max> getRoomId()
|
||||
* @method string getQuestion()
|
||||
* @psalm-method non-empty-string getQuestion()
|
||||
* @method string getOptions()
|
||||
* @method void setVotes(string $votes)
|
||||
* @method string getVotes()
|
||||
* @method void setNumVoters(int $numVoters)
|
||||
* @method int getNumVoters()
|
||||
* @psalm-method int<0, max> getNumVoters()
|
||||
* @method void setActorType(string $actorType)
|
||||
* @method string getActorType()
|
||||
* @psalm-method TalkActorTypes getActorType()
|
||||
* @method void setActorId(string $actorId)
|
||||
* @method string getActorId()
|
||||
* @psalm-method non-empty-string getActorId()
|
||||
* @method void setDisplayName(string $displayName)
|
||||
* @method string getDisplayName()
|
||||
* @method void setStatus(int $status)
|
||||
* @method int getStatus()
|
||||
* @psalm-method self::STATUS_* getStatus()
|
||||
* @method void setResultMode(int $resultMode)
|
||||
* @method int getResultMode()
|
||||
* @psalm-method self::MODE_* getResultMode()
|
||||
* @method void setMaxVotes(int $maxVotes)
|
||||
* @method int getMaxVotes()
|
||||
* @psalm-method int<0, max> getMaxVotes()
|
||||
*
|
||||
* @psalm-import-type TalkActorTypes from ResponseDefinitions
|
||||
* @psalm-import-type TalkPoll from ResponseDefinitions
|
||||
* @psalm-import-type TalkPollDraft from ResponseDefinitions
|
||||
*/
|
||||
class Poll extends Entity {
|
||||
public const STATUS_OPEN = 0;
|
||||
public const STATUS_CLOSED = 1;
|
||||
public const STATUS_DRAFT = 2;
|
||||
public const MODE_PUBLIC = 0;
|
||||
public const MODE_HIDDEN = 1;
|
||||
public const MAX_VOTES_UNLIMITED = 0;
|
||||
|
||||
protected int $roomId = 0;
|
||||
protected string $question = '';
|
||||
protected string $options = '';
|
||||
protected string $votes = '';
|
||||
protected int $numVoters = 0;
|
||||
protected string $actorType = '';
|
||||
protected string $actorId = '';
|
||||
protected ?string $displayName = null;
|
||||
protected int $status = self::STATUS_OPEN;
|
||||
protected int $resultMode = self::MODE_PUBLIC;
|
||||
protected int $maxVotes = self::MAX_VOTES_UNLIMITED;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('roomId', Types::BIGINT);
|
||||
$this->addType('question', Types::TEXT);
|
||||
$this->addType('options', Types::TEXT);
|
||||
$this->addType('votes', Types::TEXT);
|
||||
$this->addType('numVoters', Types::BIGINT);
|
||||
$this->addType('actorType', Types::STRING);
|
||||
$this->addType('actorId', Types::STRING);
|
||||
$this->addType('displayName', Types::STRING);
|
||||
$this->addType('status', Types::SMALLINT);
|
||||
$this->addType('resultMode', Types::SMALLINT);
|
||||
$this->addType('maxVotes', Types::INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TalkPoll
|
||||
*/
|
||||
public function renderAsPoll(): array {
|
||||
$data = $this->renderAsDraft();
|
||||
$votes = json_decode($this->getVotes(), true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
// Because PHP is turning arrays with sequent numeric keys "{"0":x,"1":y,"2":z}" into "[x,y,z]"
|
||||
// when json_encode() is used we have to prefix the keys with a string,
|
||||
// to prevent breaking in the mobile apps.
|
||||
$data['votes'] = [];
|
||||
foreach ($votes as $option => $count) {
|
||||
$data['votes']['option-' . $option] = $count;
|
||||
}
|
||||
$data['numVoters'] = $this->getNumVoters();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TalkPollDraft
|
||||
*/
|
||||
public function renderAsDraft(): array {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
// The room id is not needed on the API level but only internally for optimising database queries
|
||||
// 'roomId' => $this->getRoomId(),
|
||||
'question' => $this->getQuestion(),
|
||||
'options' => json_decode($this->getOptions(), true, 512, JSON_THROW_ON_ERROR),
|
||||
'actorType' => $this->getActorType(),
|
||||
'actorId' => $this->getActorId(),
|
||||
'actorDisplayName' => $this->getDisplayName(),
|
||||
'status' => $this->getStatus(),
|
||||
'resultMode' => $this->getResultMode(),
|
||||
'maxVotes' => $this->getMaxVotes(),
|
||||
];
|
||||
}
|
||||
|
||||
public function isDraft(): bool {
|
||||
return $this->getStatus() === self::STATUS_DRAFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return void
|
||||
* @throws PollPropertyException
|
||||
*/
|
||||
public function setOptions(array $options): void {
|
||||
try {
|
||||
$jsonOptions = json_encode($options, JSON_THROW_ON_ERROR, 1);
|
||||
} catch (\Exception) {
|
||||
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
|
||||
}
|
||||
|
||||
$validOptions = [];
|
||||
foreach ($options as $option) {
|
||||
if (!is_string($option)) {
|
||||
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
|
||||
}
|
||||
|
||||
$option = trim($option);
|
||||
if ($option !== '') {
|
||||
$validOptions[] = $option;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($validOptions) < 2) {
|
||||
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
|
||||
}
|
||||
|
||||
if (strlen($jsonOptions) > 60_000) {
|
||||
throw new PollPropertyException(PollPropertyException::REASON_OPTIONS);
|
||||
}
|
||||
|
||||
$this->setter('options', [$jsonOptions]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $question
|
||||
* @return void
|
||||
* @throws PollPropertyException
|
||||
*/
|
||||
public function setQuestion(string $question): void {
|
||||
$question = trim($question);
|
||||
if ($question === '' || strlen($question) > 32_000) {
|
||||
throw new PollPropertyException(PollPropertyException::REASON_QUESTION);
|
||||
}
|
||||
|
||||
$this->setter('question', [$question]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method Poll findEntity(IQueryBuilder $query)
|
||||
* @method list<Poll> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<Poll>
|
||||
*/
|
||||
class PollMapper extends QBMapper {
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'talk_polls', Poll::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Poll>
|
||||
*/
|
||||
public function getDraftsByRoomId(int $roomId): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->eq('status', $query->createNamedParameter(Poll::STATUS_DRAFT, IQueryBuilder::PARAM_INT)))
|
||||
->orderBy('id', 'ASC');
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
* @throws MultipleObjectsReturnedException
|
||||
*/
|
||||
public function getPollByRoomIdAndPollId(int $roomId, int $pollId): Poll {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('id', $query->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
public function deleteByRoomId(int $roomId): void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
$query->executeStatement();
|
||||
}
|
||||
|
||||
public function deleteByPollId(int $pollId): void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('id', $query->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
$query->executeStatement();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @method void setLocalToken(string $localToken)
|
||||
* @method string getLocalToken()
|
||||
* @method void setRemoteServerUrl(string $remoteServerUrl)
|
||||
* @method string getRemoteServerUrl()
|
||||
* @method void setRemoteToken(string $remoteToken)
|
||||
* @method string getRemoteToken()
|
||||
* @method void setRemoteMessageId(int $remoteMessageId)
|
||||
* @method int getRemoteMessageId()
|
||||
* @method void setActorType(string $actorType)
|
||||
* @method string getActorType()
|
||||
* @method void setActorId(string $actorId)
|
||||
* @method string getActorId()
|
||||
* @method void setActorDisplayName(?string $actorDisplayName)
|
||||
* @method string|null getActorDisplayName()
|
||||
* @method void setMessageType(string $messageType)
|
||||
* @method string getMessageType()
|
||||
* @method void setSystemMessage(?string $systemMessage)
|
||||
* @method string|null getSystemMessage()
|
||||
* @method void setExpirationDatetime(?\DateTime $expirationDatetime)
|
||||
* @method \DateTime|null getExpirationDatetime()
|
||||
* @method void setMessage(?string $message)
|
||||
* @method string|null getMessage()
|
||||
* @method void setMessageParameters(?string $messageParameters)
|
||||
* @method string|null getMessageParameters()
|
||||
* @method void setCreationDatetime(?\DateTime $creationDatetime)
|
||||
* @method \DateTime|null getCreationDatetime()
|
||||
* @method void setMetaData(?string $metaData)
|
||||
* @method string|null getMetaData()
|
||||
*
|
||||
* @psalm-import-type TalkChatProxyMessage from ResponseDefinitions
|
||||
*/
|
||||
class ProxyCacheMessage extends Entity implements \JsonSerializable {
|
||||
public const METADATA_REPLY_TO_ACTOR_TYPE = 'replyToActorType';
|
||||
public const METADATA_REPLY_TO_ACTOR_ID = 'replyToActorId';
|
||||
public const METADATA_REPLY_TO_MESSAGE_ID = 'replyToMessageId';
|
||||
|
||||
|
||||
protected string $localToken = '';
|
||||
protected string $remoteServerUrl = '';
|
||||
protected string $remoteToken = '';
|
||||
protected int $remoteMessageId = 0;
|
||||
protected string $actorType = '';
|
||||
protected string $actorId = '';
|
||||
protected ?string $actorDisplayName = null;
|
||||
protected ?string $messageType = null;
|
||||
protected ?string $systemMessage = null;
|
||||
protected ?\DateTime $expirationDatetime = null;
|
||||
protected ?string $message = null;
|
||||
protected ?string $messageParameters = null;
|
||||
protected ?\DateTime $creationDatetime = null;
|
||||
protected ?string $metaData = null;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('localToken', Types::STRING);
|
||||
$this->addType('remoteServerUrl', Types::STRING);
|
||||
$this->addType('remoteToken', Types::STRING);
|
||||
$this->addType('remoteMessageId', Types::BIGINT);
|
||||
$this->addType('actorType', Types::STRING);
|
||||
$this->addType('actorId', Types::STRING);
|
||||
$this->addType('actorDisplayName', Types::STRING);
|
||||
$this->addType('messageType', Types::STRING);
|
||||
$this->addType('systemMessage', Types::STRING);
|
||||
$this->addType('expirationDatetime', Types::DATETIME);
|
||||
$this->addType('message', Types::TEXT);
|
||||
$this->addType('messageParameters', Types::TEXT);
|
||||
$this->addType('creationDatetime', Types::DATETIME);
|
||||
$this->addType('metaData', Types::TEXT);
|
||||
}
|
||||
|
||||
public function getParsedMessageParameters(): array {
|
||||
return json_decode($this->getMessageParameters() ?? '[]', true);
|
||||
}
|
||||
|
||||
public function getParsedMetaData(): array {
|
||||
return json_decode($this->getMetaData() ?? '[]', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TalkChatProxyMessage
|
||||
*/
|
||||
#[\Override]
|
||||
public function jsonSerialize(): array {
|
||||
$expirationTimestamp = 0;
|
||||
if ($this->getExpirationDatetime()) {
|
||||
$expirationTimestamp = $this->getExpirationDatetime()->getTimestamp();
|
||||
}
|
||||
|
||||
return [
|
||||
'actorType' => $this->getActorType(),
|
||||
'actorId' => $this->getActorId(),
|
||||
'actorDisplayName' => $this->getActorDisplayName() ?? '',
|
||||
'timestamp' => $this->getCreationDatetime()->getTimestamp(),
|
||||
'expirationTimestamp' => $expirationTimestamp,
|
||||
'messageType' => $this->getMessageType(),
|
||||
'systemMessage' => $this->getSystemMessage() ?? '',
|
||||
'message' => $this->getMessage() ?? '',
|
||||
'messageParameters' => $this->getParsedMessageParameters(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Talk\Exceptions\InvalidRoomException;
|
||||
use OCA\Talk\Room;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\AppFramework\Db\TTransactional;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method ProxyCacheMessage mapRowToEntity(array $row)
|
||||
* @method ProxyCacheMessage findEntity(IQueryBuilder $query)
|
||||
* @method list<ProxyCacheMessage> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<ProxyCacheMessage>
|
||||
*/
|
||||
class ProxyCacheMessageMapper extends QBMapper {
|
||||
use TTransactional;
|
||||
|
||||
public function __construct(
|
||||
IDBConnection $db,
|
||||
) {
|
||||
parent::__construct($db, 'talk_proxy_messages', ProxyCacheMessage::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findById(Room $chat, int $proxyId): ProxyCacheMessage {
|
||||
if (!$chat->isFederatedConversation()) {
|
||||
throw new InvalidRoomException('Can not call ProxyCacheMessageMapper::findById() with a non-federated chat.');
|
||||
}
|
||||
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('id', $query->createNamedParameter($proxyId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findByRemote(string $remoteServerUrl, string $remoteToken, int $remoteMessageId): ProxyCacheMessage {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('remote_server_url', $query->createNamedParameter($remoteServerUrl, IQueryBuilder::PARAM_STR)))
|
||||
->andWhere($query->expr()->eq('remote_token', $query->createNamedParameter($remoteToken, IQueryBuilder::PARAM_STR)))
|
||||
->andWhere($query->expr()->eq('remote_message_id', $query->createNamedParameter($remoteMessageId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
public function deleteExpiredMessages(\DateTimeInterface $dateTime): int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->isNotNull('expiration_datetime'))
|
||||
->andWhere($query->expr()->lte('expiration_datetime', $query->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATE)));
|
||||
|
||||
return $query->executeStatement();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @method void setUserId(string $userId)
|
||||
* @method string getUserId()
|
||||
* @method void setToken(string $token)
|
||||
* @method string getToken()
|
||||
* @method void setMessageId(int $messageId)
|
||||
* @method int getMessageId()
|
||||
* @method void setDateTime(\DateTime $dateTime)
|
||||
* @method \DateTime getDateTime()
|
||||
*
|
||||
* @psalm-import-type TalkChatReminder from ResponseDefinitions
|
||||
*/
|
||||
class Reminder extends Entity implements \JsonSerializable {
|
||||
public const NUM_UPCOMING_REMINDERS = 10;
|
||||
|
||||
protected string $userId = '';
|
||||
protected string $token = '';
|
||||
protected int $messageId = 0;
|
||||
protected ?\DateTime $dateTime = null;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('userId', Types::STRING);
|
||||
$this->addType('token', Types::STRING);
|
||||
$this->addType('messageId', Types::BIGINT);
|
||||
$this->addType('dateTime', Types::DATETIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TalkChatReminder
|
||||
*/
|
||||
#[\Override]
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
'userId' => $this->getUserId(),
|
||||
'token' => $this->getToken(),
|
||||
'messageId' => $this->getMessageId(),
|
||||
'timestamp' => $this->getDateTime()->getTimestamp(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\AppFramework\Db\TTransactional;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method Reminder mapRowToEntity(array $row)
|
||||
* @method Reminder findEntity(IQueryBuilder $query)
|
||||
* @method list<Reminder> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<Reminder>
|
||||
*/
|
||||
class ReminderMapper extends QBMapper {
|
||||
use TTransactional;
|
||||
|
||||
public function __construct(
|
||||
IDBConnection $db,
|
||||
) {
|
||||
parent::__construct($db, 'talk_reminders', Reminder::class);
|
||||
}
|
||||
|
||||
public function findForUser(string $userId, int $limit): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('user_id', $query->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
|
||||
->orderBy('date_time', 'ASC')
|
||||
->setMaxResults($limit);
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findForUserAndMessage(string $userId, string $token, int $messageId): Reminder {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('user_id', $query->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
|
||||
->andWhere($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR)))
|
||||
->andWhere($query->expr()->eq('message_id', $query->createNamedParameter($messageId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Reminder>
|
||||
*/
|
||||
public function findRemindersToExecute(\DateTime $dateTime): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->lt('date_time', $query->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATE), IQueryBuilder::PARAM_DATE));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
public function deleteExecutedReminders(\DateTime $dateTime): void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->lt('date_time', $query->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATE), IQueryBuilder::PARAM_DATE));
|
||||
|
||||
$query->executeStatement();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @method void setRemoteServer(string $remoteServer)
|
||||
* @method string getRemoteServer()
|
||||
* @method void setNumAttempts(int $numAttempts)
|
||||
* @method int getNumAttempts()
|
||||
* @method void setNextRetry(\DateTime $nextRetry)
|
||||
* @method \DateTime getNextRetry()
|
||||
* @method void setNotificationType(string $notificationType)
|
||||
* @method string getNotificationType()
|
||||
* @method void setResourceType(string $resourceType)
|
||||
* @method string getResourceType()
|
||||
* @method void setProviderId(string $providerId)
|
||||
* @method string getProviderId()
|
||||
* @method void setNotification(string $notification)
|
||||
* @method string getNotification()
|
||||
*/
|
||||
class RetryNotification extends Entity {
|
||||
public const MAX_NUM_ATTEMPTS = 20;
|
||||
|
||||
protected string $remoteServer = '';
|
||||
protected int $numAttempts = 0;
|
||||
protected ?\DateTime $nextRetry = null;
|
||||
protected string $notificationType = '';
|
||||
protected string $resourceType = '';
|
||||
protected string $providerId = '';
|
||||
protected string $notification = '';
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('remoteServer', Types::STRING);
|
||||
$this->addType('numAttempts', Types::INTEGER);
|
||||
$this->addType('nextRetry', Types::DATETIME);
|
||||
$this->addType('notificationType', Types::STRING);
|
||||
$this->addType('resourceType', Types::STRING);
|
||||
$this->addType('providerId', Types::STRING);
|
||||
$this->addType('notification', Types::TEXT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method RetryNotification mapRowToEntity(array $row)
|
||||
* @method RetryNotification findEntity(IQueryBuilder $query)
|
||||
* @method list<RetryNotification> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<RetryNotification>
|
||||
*/
|
||||
class RetryNotificationMapper extends QBMapper {
|
||||
public function __construct(
|
||||
IDBConnection $db,
|
||||
) {
|
||||
parent::__construct($db, 'talk_retry_ocm', RetryNotification::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<RetryNotification>
|
||||
*/
|
||||
public function getAllDue(\DateTimeInterface $dueDateTime, ?int $limit = 500): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->lte('next_retry', $query->createNamedParameter($dueDateTime, IQueryBuilder::PARAM_DATE), IQueryBuilder::PARAM_DATE));
|
||||
|
||||
if ($limit !== null) {
|
||||
$query->setMaxResults($limit)
|
||||
->orderBy('next_retry', 'ASC')
|
||||
->addOrderBy('id', 'ASC');
|
||||
}
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
public function deleteByProviderId($providerId): void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('provider_id', $query->createNamedParameter($providerId)));
|
||||
$query->executeStatement();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
|
||||
class SelectHelper {
|
||||
public function selectRoomsTable(IQueryBuilder $query, string $alias = 'r'): void {
|
||||
if ($alias !== '') {
|
||||
$alias .= '.';
|
||||
}
|
||||
|
||||
$query->addSelect($alias . 'type')
|
||||
->addSelect($alias . 'read_only')
|
||||
->addSelect($alias . 'lobby_state')
|
||||
->addSelect($alias . 'sip_enabled')
|
||||
->addSelect($alias . 'assigned_hpb')
|
||||
->addSelect($alias . 'token')
|
||||
->addSelect($alias . 'name')
|
||||
->addSelect($alias . 'description')
|
||||
->addSelect($alias . 'password')
|
||||
->addSelect($alias . 'avatar')
|
||||
->addSelect($alias . 'active_since')
|
||||
->addSelect($alias . 'default_permissions')
|
||||
->addSelect($alias . 'call_permissions')
|
||||
->addSelect($alias . 'call_flag')
|
||||
->addSelect($alias . 'last_activity')
|
||||
->addSelect($alias . 'last_message')
|
||||
->addSelect($alias . 'lobby_timer')
|
||||
->addSelect($alias . 'object_type')
|
||||
->addSelect($alias . 'object_id')
|
||||
->addSelect($alias . 'listable')
|
||||
->addSelect($alias . 'message_expiration')
|
||||
->addSelect($alias . 'remote_server')
|
||||
->addSelect($alias . 'remote_token')
|
||||
->addSelect($alias . 'breakout_room_mode')
|
||||
->addSelect($alias . 'breakout_room_status')
|
||||
->addSelect($alias . 'call_recording')
|
||||
->addSelect($alias . 'recording_consent')
|
||||
->addSelect($alias . 'has_federation')
|
||||
->addSelect($alias . 'mention_permissions')
|
||||
->addSelect($alias . 'transcription_language')
|
||||
->selectAlias($alias . 'id', 'r_id');
|
||||
}
|
||||
|
||||
public function selectAttendeesTable(IQueryBuilder $query, string $alias = 'a'): void {
|
||||
if ($alias !== '') {
|
||||
$alias .= '.';
|
||||
}
|
||||
|
||||
$query->addSelect($alias . 'room_id')
|
||||
->addSelect($alias . 'actor_type')
|
||||
->addSelect($alias . 'actor_id')
|
||||
->addSelect($alias . 'display_name')
|
||||
->addSelect($alias . 'pin')
|
||||
->addSelect($alias . 'participant_type')
|
||||
->addSelect($alias . 'favorite')
|
||||
->addSelect($alias . 'notification_level')
|
||||
->addSelect($alias . 'notification_calls')
|
||||
->addSelect($alias . 'last_joined_call')
|
||||
->addSelect($alias . 'last_read_message')
|
||||
->addSelect($alias . 'last_mention_message')
|
||||
->addSelect($alias . 'last_mention_direct')
|
||||
->addSelect($alias . 'read_privacy')
|
||||
->addSelect($alias . 'permissions')
|
||||
->addSelect($alias . 'access_token')
|
||||
->addSelect($alias . 'remote_id')
|
||||
->addSelect($alias . 'invited_cloud_id')
|
||||
->addSelect($alias . 'phone_number')
|
||||
->addSelect($alias . 'call_id')
|
||||
->addSelect($alias . 'state')
|
||||
->addSelect($alias . 'unread_messages')
|
||||
->addSelect($alias . 'last_attendee_activity')
|
||||
->addSelect($alias . 'archived')
|
||||
->addSelect($alias . 'important')
|
||||
->addSelect($alias . 'sensitive')
|
||||
->addSelect($alias . 'has_unread_threads')
|
||||
->addSelect($alias . 'has_unread_thread_mentions')
|
||||
->addSelect($alias . 'has_unread_thread_directs')
|
||||
->selectAlias($alias . 'id', 'a_id');
|
||||
}
|
||||
|
||||
public function selectSessionsTable(IQueryBuilder $query, string $alias = 's'): void {
|
||||
if ($alias !== '') {
|
||||
$alias .= '.';
|
||||
}
|
||||
|
||||
$query->addSelect($alias . 'attendee_id')
|
||||
->addSelect($alias . 'session_id')
|
||||
->addSelect($alias . 'in_call')
|
||||
->addSelect($alias . 'last_ping')
|
||||
->selectAlias($alias . 'state', 's_state')
|
||||
->selectAlias($alias . 'id', 's_id');
|
||||
}
|
||||
|
||||
public function selectSessionsTableMax(IQueryBuilder $query, string $alias = 's'): void {
|
||||
if ($alias !== '') {
|
||||
$alias .= '.';
|
||||
}
|
||||
|
||||
$query->selectAlias($query->func()->max($alias . 'attendee_id'), 'attendee_id')
|
||||
->selectAlias($query->func()->max($alias . 'session_id'), 'session_id')
|
||||
// BIT_OR would be better, but SQLite does not support something like it.
|
||||
->selectAlias($query->func()->max($alias . 'in_call'), 'in_call')
|
||||
->selectAlias($query->func()->max($alias . 'last_ping'), 'last_ping')
|
||||
->selectAlias($query->func()->max($alias . 'state'), 's_state')
|
||||
->selectAlias($query->func()->max($alias . 'id'), 's_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* A session is the "I'm online in this conversation" state of Talk, you get one
|
||||
* when opening the conversation while the inCall flag tells if you are just
|
||||
* online (chatting), or in a call (with audio, camera or even sip).
|
||||
*
|
||||
* @method void setAttendeeId(int $attendeeId)
|
||||
* @method string getAttendeeId()
|
||||
* @method void setSessionId(string $sessionId)
|
||||
* @method string getSessionId()
|
||||
* @method void setInCall(int $inCall)
|
||||
* @method int getInCall()
|
||||
* @method void setLastPing(int $lastPing)
|
||||
* @method int getLastPing()
|
||||
* @method void setState(int $state)
|
||||
* @method int getState()
|
||||
*/
|
||||
class Session extends Entity {
|
||||
public const STATE_INACTIVE = 0;
|
||||
public const STATE_ACTIVE = 1;
|
||||
|
||||
public const SESSION_TIMEOUT = 30;
|
||||
public const SESSION_TIMEOUT_KILL = self::SESSION_TIMEOUT * 3 + 10;
|
||||
|
||||
/** @var int */
|
||||
protected $attendeeId;
|
||||
|
||||
/** @var string */
|
||||
protected $sessionId;
|
||||
|
||||
/** @var int */
|
||||
protected $inCall;
|
||||
|
||||
/** @var int */
|
||||
protected $lastPing;
|
||||
|
||||
/** @var int */
|
||||
protected $state;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('attendeeId', Types::BIGINT);
|
||||
$this->addType('sessionId', Types::STRING);
|
||||
$this->addType('inCall', Types::INTEGER);
|
||||
$this->addType('lastPing', Types::INTEGER);
|
||||
$this->addType('state', Types::SMALLINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function asArray(): array {
|
||||
return [
|
||||
'id' => $this->getId(),
|
||||
'attendee_id' => $this->getAttendeeId(),
|
||||
'session_id' => $this->getSessionId(),
|
||||
'in_call' => $this->getInCall(),
|
||||
'last_ping' => $this->getLastPing(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Talk\Participant;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method Session mapRowToEntity(array $row)
|
||||
* @method Session findEntity(IQueryBuilder $query)
|
||||
* @method list<Session> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<Session>
|
||||
*/
|
||||
class SessionMapper extends QBMapper {
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'talk_sessions', Session::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findBySessionId(string $sessionId): Session {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('session_id', $query->createNamedParameter($sessionId)));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Session>
|
||||
*/
|
||||
public function findByAttendeeId(int $attendeeId): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('attendee_id', $query->createNamedParameter($attendeeId)));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int Number of deleted entities
|
||||
*/
|
||||
public function deleteByAttendeeId(int $attendeeId): int {
|
||||
$delete = $this->db->getQueryBuilder();
|
||||
$delete->delete($this->getTableName())
|
||||
->where($delete->expr()->eq('attendee_id', $delete->createNamedParameter($attendeeId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
return $delete->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $ids
|
||||
* @return int Number of deleted entities
|
||||
*/
|
||||
public function deleteByIds(array $ids): int {
|
||||
$delete = $this->db->getQueryBuilder();
|
||||
$delete->delete($this->getTableName())
|
||||
->where($delete->expr()->in('id', $delete->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)));
|
||||
|
||||
return $delete->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $sessionIds
|
||||
*/
|
||||
public function resetInCallByIds(array $sessionIds): void {
|
||||
$update = $this->db->getQueryBuilder();
|
||||
$update->update($this->getTableName())
|
||||
->set('in_call', $update->createNamedParameter(Participant::FLAG_DISCONNECTED, IQueryBuilder::PARAM_INT))
|
||||
->where($update->expr()->in('session_id', $update->createNamedParameter($sessionIds, IQueryBuilder::PARAM_STR_ARRAY)));
|
||||
$update->executeStatement();
|
||||
}
|
||||
|
||||
public function createSessionFromRow(array $row): Session {
|
||||
return $this->mapRowToEntity([
|
||||
'id' => $row['s_id'],
|
||||
'session_id' => $row['session_id'],
|
||||
'attendee_id' => (int)$row['a_id'],
|
||||
'in_call' => (int)$row['in_call'],
|
||||
'last_ping' => (int)$row['last_ping'],
|
||||
'state' => (int)$row['s_state'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCA\Talk\Room;
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @method void setRoomId(int $roomId)
|
||||
* @method int getRoomId()
|
||||
* @method void setLastMessageId(int $lastMessageId)
|
||||
* @method int getLastMessageId()
|
||||
* @method void setNumReplies(int $numReplies)
|
||||
* @method int getNumReplies()
|
||||
* @method void setLastActivity(\DateTime $lastActivity)
|
||||
* @method \DateTime|null getLastActivity()
|
||||
* @method void setName(string $name)
|
||||
*
|
||||
* @psalm-import-type TalkThread from ResponseDefinitions
|
||||
*/
|
||||
class Thread extends Entity {
|
||||
public const THREAD_NONE = 0;
|
||||
public const THREAD_CREATE = -1;
|
||||
protected int $roomId = 0;
|
||||
protected int $lastMessageId = 0;
|
||||
protected int $numReplies = 0;
|
||||
protected ?\DateTime $lastActivity = null;
|
||||
protected string $name = '';
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('roomId', Types::BIGINT);
|
||||
$this->addType('lastMessageId', Types::BIGINT);
|
||||
$this->addType('numReplies', Types::BIGINT);
|
||||
$this->addType('lastActivity', Types::DATETIME);
|
||||
$this->addType('name', Types::STRING);
|
||||
}
|
||||
|
||||
public static function createFromRow(array $row): Thread {
|
||||
$thread = new Thread();
|
||||
$thread->setId((int)$row['t_id']);
|
||||
$thread->setRoomId((int)$row['room_id']);
|
||||
$thread->setLastMessageId((int)$row['last_message_id']);
|
||||
$thread->setNumReplies((int)$row['num_replies']);
|
||||
$thread->setLastActivity(new \DateTime($row['last_activity']));
|
||||
$thread->setName($row['name']);
|
||||
return $thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $json
|
||||
* @return Thread
|
||||
* @throws \JsonException
|
||||
*/
|
||||
public static function fromJson(string $json): Thread {
|
||||
$row = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
|
||||
$thread = new Thread();
|
||||
$thread->setId((int)$row['id']);
|
||||
$thread->setRoomId((int)$row['room_id']);
|
||||
$thread->setLastMessageId((int)$row['last_message_id']);
|
||||
$thread->setNumReplies((int)$row['num_replies']);
|
||||
$thread->setLastActivity(new \DateTime('@' . $row['last_activity']));
|
||||
$thread->setName($row['name']);
|
||||
return $thread;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws \JsonException
|
||||
*/
|
||||
public function toJson(): string {
|
||||
return json_encode([
|
||||
'id' => $this->getId(),
|
||||
'room_id' => $this->getRoomId(),
|
||||
'last_message_id' => $this->getLastMessageId(),
|
||||
'num_replies' => $this->getNumReplies(),
|
||||
'last_activity' => $this->getLastActivity()?->getTimestamp() ?? 0,
|
||||
'name' => $this->getName(),
|
||||
], flags: JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
if ($this->name !== '') {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
// FIXME temporary workaround against empty titles
|
||||
return 'Thread #' . $this->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TalkThread
|
||||
*/
|
||||
public function toArray(Room $room): array {
|
||||
return [
|
||||
'id' => max(1, $this->getId()),
|
||||
// 'roomId' => max(1, $this->getRoomId()),
|
||||
'roomToken' => $room->getToken(),
|
||||
'lastMessageId' => max(0, $this->getLastMessageId()),
|
||||
'numReplies' => max(0, $this->getNumReplies()),
|
||||
'lastActivity' => max(0, $this->getLastActivity()?->getTimestamp() ?? 0),
|
||||
'title' => $this->getName(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Talk\Participant;
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @method void setRoomId(int $roomId)
|
||||
* @method int getRoomId()
|
||||
* @method void setThreadId(int $threadId)
|
||||
* @method int getThreadId()
|
||||
* @method void setAttendeeId(int $attendeeId)
|
||||
* @method int getAttendeeId()
|
||||
* @method void setActorType(string $actorType)
|
||||
* @method string getActorType()
|
||||
* @method void setActorId(string $actorId)
|
||||
* @method string getActorId()
|
||||
* @method void setNotificationLevel(int $notificationLevel)
|
||||
* @method int getNotificationLevel()
|
||||
*
|
||||
* @psalm-import-type TalkThreadAttendee from ResponseDefinitions
|
||||
*/
|
||||
class ThreadAttendee extends Entity implements \JsonSerializable {
|
||||
protected int $roomId = 0;
|
||||
protected int $threadId = 0;
|
||||
protected int $attendeeId = 0;
|
||||
protected string $actorType = '';
|
||||
protected string $actorId = '';
|
||||
protected int $notificationLevel = 0;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('roomId', Types::BIGINT);
|
||||
$this->addType('threadId', Types::BIGINT);
|
||||
$this->addType('attendeeId', Types::BIGINT);
|
||||
$this->addType('actorType', Types::STRING);
|
||||
$this->addType('actorId', Types::STRING);
|
||||
$this->addType('notificationLevel', Types::INTEGER);
|
||||
}
|
||||
|
||||
public static function createFromRow(array $row): ThreadAttendee {
|
||||
$attendee = new ThreadAttendee();
|
||||
$attendee->setRoomId((int)$row['room_id']);
|
||||
$attendee->setThreadId((int)$row['thread_id']);
|
||||
$attendee->setAttendeeId((int)$row['attendee_id']);
|
||||
$attendee->setNotificationLevel((int)$row['notification_level']);
|
||||
$attendee->setActorType($row['actor_type']);
|
||||
$attendee->setActorId($row['actor_id']);
|
||||
return $attendee;
|
||||
}
|
||||
|
||||
public static function createFromParticipant(int $threadId, Participant $participant): ThreadAttendee {
|
||||
$attendee = new ThreadAttendee();
|
||||
$attendee->setRoomId($participant->getRoom()->getId());
|
||||
$attendee->setThreadId($threadId);
|
||||
$attendee->setAttendeeId($participant->getAttendee()->getId());
|
||||
$attendee->setNotificationLevel(Participant::NOTIFY_DEFAULT);
|
||||
$attendee->setActorType($participant->getAttendee()->getActorType());
|
||||
$attendee->setActorId($participant->getAttendee()->getActorId());
|
||||
return $attendee;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TalkThreadAttendee
|
||||
*/
|
||||
#[\Override]
|
||||
public function jsonSerialize(): array {
|
||||
return [
|
||||
'notificationLevel' => min(3, max(0, $this->getNotificationLevel())),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Talk\Participant;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method ThreadAttendee mapRowToEntity(array $row)
|
||||
* @method ThreadAttendee findEntity(IQueryBuilder $query)
|
||||
* @method list<ThreadAttendee> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<ThreadAttendee>
|
||||
*/
|
||||
class ThreadAttendeeMapper extends QBMapper {
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'talk_thread_attendees', ThreadAttendee::class);
|
||||
}
|
||||
|
||||
public function deleteByRoomId(int $roomId): int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq(
|
||||
'room_id',
|
||||
$query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT),
|
||||
IQueryBuilder::PARAM_INT,
|
||||
));
|
||||
|
||||
return $query->executeStatement();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<int> $threadIds
|
||||
* @return list<ThreadAttendee>
|
||||
*/
|
||||
public function findAttendeeByThreadIds(string $actorType, string $actorId, int $roomId, array $threadIds): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq(
|
||||
'actor_type',
|
||||
$query->createNamedParameter($actorType),
|
||||
))
|
||||
->andWhere($query->expr()->eq(
|
||||
'actor_id',
|
||||
$query->createNamedParameter($actorId),
|
||||
))
|
||||
->andWhere($query->expr()->eq(
|
||||
'room_id',
|
||||
$query->createNamedParameter($roomId),
|
||||
))
|
||||
->andWhere($query->expr()->in(
|
||||
'thread_id',
|
||||
$query->createNamedParameter($threadIds, IQueryBuilder::PARAM_INT_ARRAY),
|
||||
IQueryBuilder::PARAM_INT_ARRAY,
|
||||
));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException if the item does not exist
|
||||
*/
|
||||
public function findAttendeeByThreadId(string $actorType, string $actorId, int $roomId, int $threadId): ThreadAttendee {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq(
|
||||
'actor_type',
|
||||
$query->createNamedParameter($actorType),
|
||||
))
|
||||
->andWhere($query->expr()->eq(
|
||||
'actor_id',
|
||||
$query->createNamedParameter($actorId),
|
||||
))
|
||||
->andWhere($query->expr()->eq(
|
||||
'room_id',
|
||||
$query->createNamedParameter($roomId),
|
||||
))
|
||||
->andWhere($query->expr()->eq(
|
||||
'thread_id',
|
||||
$query->createNamedParameter($threadId),
|
||||
));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<ThreadAttendee>
|
||||
*/
|
||||
public function findAttendeesForNotification(int $roomId, int $threadId): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq(
|
||||
'room_id',
|
||||
$query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT),
|
||||
))
|
||||
->andWhere($query->expr()->eq(
|
||||
'thread_id',
|
||||
$query->createNamedParameter($threadId, IQueryBuilder::PARAM_INT),
|
||||
))
|
||||
->andWhere($query->expr()->neq(
|
||||
'notification_level',
|
||||
$query->createNamedParameter(Participant::NOTIFY_DEFAULT, IQueryBuilder::PARAM_INT),
|
||||
));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method Thread findEntity(IQueryBuilder $query)
|
||||
* @method list<Thread> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<Thread>
|
||||
*/
|
||||
class ThreadMapper extends QBMapper {
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'talk_threads', Thread::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-negative-int $roomId
|
||||
* @param non-negative-int $threadId
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function findById(int $roomId, int $threadId): Thread {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq(
|
||||
'id',
|
||||
$query->createNamedParameter($threadId, IQueryBuilder::PARAM_INT),
|
||||
IQueryBuilder::PARAM_INT,
|
||||
))
|
||||
->andWhere($query->expr()->eq(
|
||||
'room_id',
|
||||
$query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT),
|
||||
IQueryBuilder::PARAM_INT,
|
||||
));
|
||||
|
||||
return $this->findEntity($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-negative-int $roomId
|
||||
* @param list<non-negative-int> $threadIds
|
||||
* @return list<Thread>
|
||||
*/
|
||||
public function findByIds(int $roomId, array $threadIds): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->in(
|
||||
'id',
|
||||
$query->createNamedParameter($threadIds, IQueryBuilder::PARAM_INT_ARRAY),
|
||||
IQueryBuilder::PARAM_INT,
|
||||
))
|
||||
->andWhere($query->expr()->eq(
|
||||
'room_id',
|
||||
$query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT),
|
||||
IQueryBuilder::PARAM_INT,
|
||||
));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<non-negative-int> $threadIds
|
||||
* @return list<Thread>
|
||||
*/
|
||||
public function getForIds(array $threadIds): array {
|
||||
$threads = [];
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName());
|
||||
foreach (array_chunk($threadIds, 1000) as $ids) {
|
||||
$query->where($query->expr()->in(
|
||||
'id',
|
||||
$query->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY),
|
||||
IQueryBuilder::PARAM_INT,
|
||||
));
|
||||
$threads[] = $this->findEntities($query);
|
||||
}
|
||||
|
||||
|
||||
return array_merge(...$threads);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int<1, 50> $limit
|
||||
* @return list<Thread>
|
||||
*/
|
||||
public function getRecentByRoomId(int $roomId, int $limit): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq(
|
||||
'room_id',
|
||||
$query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT),
|
||||
IQueryBuilder::PARAM_INT,
|
||||
))
|
||||
->orderBy('last_activity', 'DESC')
|
||||
->setMaxResults($limit);
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
public function deleteByRoomId(int $roomId): int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq(
|
||||
'room_id',
|
||||
$query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT),
|
||||
IQueryBuilder::PARAM_INT,
|
||||
));
|
||||
|
||||
return $query->executeStatement();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCA\Talk\ResponseDefinitions;
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* @method void setPollId(int $pollId)
|
||||
* @method int getPollId()
|
||||
* @method void setRoomId(int $roomId)
|
||||
* @method int getRoomId()
|
||||
* @method void setActorType(string $actorType)
|
||||
* @method string getActorType()
|
||||
* @method void setActorId(string $actorId)
|
||||
* @method string getActorId()
|
||||
* @method void setDisplayName(string $displayName)
|
||||
* @method string getDisplayName()
|
||||
* @method void setOptionId(int $optionId)
|
||||
* @method int getOptionId()
|
||||
*
|
||||
* @psalm-import-type TalkPollVote from ResponseDefinitions
|
||||
*/
|
||||
class Vote extends Entity {
|
||||
protected int $pollId = 0;
|
||||
protected int $roomId = 0;
|
||||
protected string $actorType = '';
|
||||
protected string $actorId = '';
|
||||
protected ?string $displayName = null;
|
||||
protected ?int $optionId = null;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('pollId', Types::BIGINT);
|
||||
$this->addType('roomId', Types::BIGINT);
|
||||
$this->addType('actorType', Types::STRING);
|
||||
$this->addType('actorId', Types::STRING);
|
||||
$this->addType('displayName', Types::STRING);
|
||||
$this->addType('optionId', Types::INTEGER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TalkPollVote
|
||||
*/
|
||||
public function asArray(): array {
|
||||
return [
|
||||
// The ids are not needed on the API level but only internally for optimising database queries
|
||||
// 'id' => $this->getId(),
|
||||
// 'pollId' => $this->getPollId(),
|
||||
// 'roomId' => $this->getRoomId(),
|
||||
'actorType' => $this->getActorType(),
|
||||
'actorId' => $this->getActorId(),
|
||||
'actorDisplayName' => $this->getDisplayName(),
|
||||
'optionId' => $this->getOptionId(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Talk\Model;
|
||||
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @method Vote findEntity(IQueryBuilder $query)
|
||||
* @method list<Vote> findEntities(IQueryBuilder $query)
|
||||
* @template-extends QBMapper<Vote>
|
||||
*/
|
||||
class VoteMapper extends QBMapper {
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'talk_poll_votes', Vote::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Vote>
|
||||
*/
|
||||
public function findByPollId(int $pollId): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('poll_id', $query->createNamedParameter($pollId)));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Vote>
|
||||
*/
|
||||
public function findByPollIdForActor(int $pollId, string $actorType, string $actorId): array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from($this->getTableName())
|
||||
->where($query->expr()->eq('poll_id', $query->createNamedParameter($pollId)))
|
||||
->andWhere($query->expr()->eq('actor_type', $query->createNamedParameter($actorType)))
|
||||
->andWhere($query->expr()->eq('actor_id', $query->createNamedParameter($actorId)));
|
||||
|
||||
return $this->findEntities($query);
|
||||
}
|
||||
|
||||
public function deleteByRoomId(int $roomId): void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('room_id', $query->createNamedParameter($roomId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
$query->executeStatement();
|
||||
}
|
||||
|
||||
public function deleteByPollId(int $pollId): void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('poll_id', $query->createNamedParameter($pollId, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
$query->executeStatement();
|
||||
}
|
||||
|
||||
public function deleteVotesByActor(int $pollId, string $actorType, string $actorId): void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
||||
$query->delete($this->getTableName())
|
||||
->where($query->expr()->eq('poll_id', $query->createNamedParameter($pollId)))
|
||||
->andWhere($query->expr()->eq('actor_type', $query->createNamedParameter($actorType)))
|
||||
->andWhere($query->expr()->eq('actor_id', $query->createNamedParameter($actorId)));
|
||||
|
||||
$query->executeStatement();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user