diff --git a/appinfo/info.xml b/appinfo/info.xml index 27810482..bf032c47 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -45,6 +45,7 @@ OCA\Social\Command\AccountCreate + OCA\Social\Command\AccountDelete OCA\Social\Command\AccountFollowing OCA\Social\Command\CacheRefresh OCA\Social\Command\CheckInstall diff --git a/lib/Command/AccountDelete.php b/lib/Command/AccountDelete.php new file mode 100644 index 00000000..0877edc2 --- /dev/null +++ b/lib/Command/AccountDelete.php @@ -0,0 +1,88 @@ + + * @copyright 2018, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +namespace OCA\Social\Command; + +use Exception; +use OC\Core\Command\Base; +use OCA\Social\Interfaces\Actor\PersonInterface; +use OCA\Social\Service\AccountService; +use OCA\Social\Service\CacheActorService; +use OCA\Social\Service\ConfigService; +use OCP\IUserManager; +use OCP\Server; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class AccountDelete extends Base { + private IUserManager $userManager; + private AccountService $accountService; + private CacheActorService $cacheActorService; + private ConfigService $configService; + + public function __construct( + IUserManager $userManager, + AccountService $accountService, + CacheActorService $cacheActorService, + ConfigService $configService + ) { + parent::__construct(); + + $this->userManager = $userManager; + $this->accountService = $accountService; + $this->cacheActorService = $cacheActorService; + $this->configService = $configService; + } + + protected function configure() { + parent::configure(); + $this->setName('social:account:delete') + ->addArgument('account', InputArgument::REQUIRED, 'Social Local Account') + ->setDescription('Delete a local social account'); + } + + /** + * @throws Exception + */ + protected function execute(InputInterface $input, OutputInterface $output): int { + $account = $input->getArgument('account'); + + // TODO: broadcast to other instance + throw new Exception('not fully available'); + + $actor = $this->cacheActorService->getFromLocalAccount($account); + $personInterface = Server::get(PersonInterface::class); + $personInterface->deleteActor($actor->getId()); + + return 0; + } +} diff --git a/lib/Db/ActionsRequest.php b/lib/Db/ActionsRequest.php index a678a8f6..8fd6130b 100644 --- a/lib/Db/ActionsRequest.php +++ b/lib/Db/ActionsRequest.php @@ -153,13 +153,21 @@ class ActionsRequest extends ActionsRequestBuilder { } -// /** -// * @param string $objectId -// */ -// public function deleteLikes(string $objectId) { -// $qb = $this->getActionsDeleteSql(); -// $this->limitToObjectId($qb, $objectId); -// -// $qb->execute(); -// } + public function deleteByActor(string $actorId): void { + $qb = $this->getActionsDeleteSql(); + $qb->limitToDBField('actor_id_prim', $qb->prim($actorId)); + + $qb->execute(); + } + + + public function moveAccount(string $actorId, string $newId): void { + $qb = $this->getActionsUpdateSql(); + $qb->set('actor_id', $qb->createNamedParameter($newId)) + ->set('actor_id_prim', $qb->createNamedParameter($qb->prim($newId))); + + $qb->limitToDBField('actor_id_prim', $qb->prim($actorId)); + + $qb->execute(); + } } diff --git a/lib/Db/ActionsRequestBuilder.php b/lib/Db/ActionsRequestBuilder.php index bf139db9..9e068b85 100644 --- a/lib/Db/ActionsRequestBuilder.php +++ b/lib/Db/ActionsRequestBuilder.php @@ -31,11 +31,11 @@ declare(strict_types=1); namespace OCA\Social\Db; -use OCA\Social\Tools\Exceptions\RowNotFoundException; -use OCA\Social\Tools\Traits\TArrayTools; use OCA\Social\Exceptions\ActionDoesNotExistException; use OCA\Social\Exceptions\InvalidResourceException; use OCA\Social\Model\ActivityPub\ACore; +use OCA\Social\Tools\Exceptions\RowNotFoundException; +use OCA\Social\Tools\Traits\TArrayTools; /** * Class ActionsRequestBuilder @@ -105,7 +105,8 @@ class ActionsRequestBuilder extends CoreRequestBuilder { */ protected function getActionsDeleteSql(): SocialQueryBuilder { $qb = $this->getQueryBuilder(); - $qb->delete(self::TABLE_ACTIONS); + $qb->delete(self::TABLE_ACTIONS) + ->setDefaultSelectAlias('a'); return $qb; } diff --git a/lib/Db/CacheActorsRequest.php b/lib/Db/CacheActorsRequest.php index ecab744e..520a8250 100644 --- a/lib/Db/CacheActorsRequest.php +++ b/lib/Db/CacheActorsRequest.php @@ -241,7 +241,7 @@ class CacheActorsRequest extends CacheActorsRequestBuilder { */ public function deleteCacheById(string $id) { $qb = $this->getCacheActorsDeleteSql(); - $this->limitToIdString($qb, $id); + $qb->limitToIdPrim($qb->prim($id)); $qb->execute(); } diff --git a/lib/Db/CacheActorsRequestBuilder.php b/lib/Db/CacheActorsRequestBuilder.php index 11992cae..b79e5348 100644 --- a/lib/Db/CacheActorsRequestBuilder.php +++ b/lib/Db/CacheActorsRequestBuilder.php @@ -96,9 +96,9 @@ class CacheActorsRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Delete request * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getCacheActorsDeleteSql(): IQueryBuilder { + protected function getCacheActorsDeleteSql(): SocialQueryBuilder { $qb = $this->getQueryBuilder(); $qb->delete(self::TABLE_CACHE_ACTORS); diff --git a/lib/Db/CacheDocumentsRequest.php b/lib/Db/CacheDocumentsRequest.php index 88e6e284..c435f9d7 100644 --- a/lib/Db/CacheDocumentsRequest.php +++ b/lib/Db/CacheDocumentsRequest.php @@ -56,6 +56,7 @@ class CacheDocumentsRequest extends CacheDocumentsRequestBuilder { ->setValue('local_copy', $qb->createNamedParameter($document->getLocalCopy())) ->setValue('resized_copy', $qb->createNamedParameter($document->getResizedCopy())) ->setValue('parent_id', $qb->createNamedParameter($document->getParentId())) + ->setValue('parent_id_prim', $qb->createNamedParameter($qb->prim($document->getParentId()))) ->setValue('public', $qb->createNamedParameter(($document->isPublic()) ? '1' : '0')); try { @@ -83,6 +84,7 @@ class CacheDocumentsRequest extends CacheDocumentsRequestBuilder { ->set('local_copy', $qb->createNamedParameter($document->getLocalCopy())) ->set('resized_copy', $qb->createNamedParameter($document->getResizedCopy())) ->set('parent_id', $qb->createNamedParameter($document->getParentId())) + ->set('parent_id_prim', $qb->createNamedParameter($qb->prim($document->getParentId()))) ->set('public', $qb->createNamedParameter(($document->isPublic()) ? '1' : '0')); try { @@ -239,4 +241,22 @@ class CacheDocumentsRequest extends CacheDocumentsRequestBuilder { $qb->execute(); } + + public function deleteByParent(string $parentId) { + $qb = $this->getCacheDocumentsDeleteSql(); + $qb->limitToDBField('parent_id_prim', $qb->prim($parentId)); + + $qb->executeStatement(); + } + + + public function moveAccount(string $actorId, string $newId): void { + $qb = $this->getCacheDocumentsUpdateSql(); + $qb->set('parent_id', $qb->createNamedParameter($newId)) + ->set('parent_id_prim', $qb->createNamedParameter($qb->prim($newId))); + + $qb->limitToDBField('parent_id_prim', $qb->prim($actorId)); + + $qb->execute(); + } } diff --git a/lib/Db/CoreRequestBuilder.php b/lib/Db/CoreRequestBuilder.php index a7c93c11..99fe2289 100644 --- a/lib/Db/CoreRequestBuilder.php +++ b/lib/Db/CoreRequestBuilder.php @@ -36,6 +36,7 @@ use DateTime; use Doctrine\DBAL\Query\QueryBuilder; use Exception; use OC; +use OC\DB\Connection; use OC\DB\SchemaWrapper; use OCA\Social\Exceptions\InvalidResourceException; use OCA\Social\Model\ActivityPub\Actor\Person; @@ -1263,10 +1264,10 @@ class CoreRequestBuilder { * this just empty all tables from the app. */ public function emptyAll() { - $schema = new SchemaWrapper(Server::get(IDBConnection::class)); + $schema = new SchemaWrapper(Server::get(Connection::class)); foreach (array_keys(self::$tables) as $table) { if ($schema->hasTable($table)) { - $qb = $this->dbConnection->getQueryBuilder(); + $qb = $this->getQueryBuilder(); $qb->delete($table); $qb->execute(); } @@ -1278,7 +1279,7 @@ class CoreRequestBuilder { * this just empty all tables from the app. */ public function uninstallSocialTables() { - $schema = new SchemaWrapper(Server::get(IDBConnection::class)); + $schema = new SchemaWrapper(Server::get(Connection::class)); foreach (array_keys(self::$tables) as $table) { if ($schema->hasTable($table)) { $schema->dropTable($table); @@ -1293,7 +1294,7 @@ class CoreRequestBuilder { * */ public function uninstallFromMigrations() { - $qb = $this->dbConnection->getQueryBuilder(); + $qb = $this->getQueryBuilder(); $qb->delete('migrations'); $qb->where($this->exprLimitToDBField($qb, 'app', 'social', true, true)); @@ -1304,12 +1305,12 @@ class CoreRequestBuilder { * */ public function uninstallFromJobs() { - $qb = $this->dbConnection->getQueryBuilder(); + $qb = $this->getQueryBuilder(); $qb->delete('jobs'); $qb->where($this->exprLimitToDBField($qb, 'class', 'OCA\Social\Cron\Cache', true, true)); $qb->execute(); - $qb = $this->dbConnection->getQueryBuilder(); + $qb = $this->getQueryBuilder(); $qb->delete('jobs'); $qb->where($this->exprLimitToDBField($qb, 'class', 'OCA\Social\Cron\Queue', true, true)); $qb->execute(); diff --git a/lib/Db/FollowsRequest.php b/lib/Db/FollowsRequest.php index 7c799dab..4e6ccd00 100644 --- a/lib/Db/FollowsRequest.php +++ b/lib/Db/FollowsRequest.php @@ -31,12 +31,12 @@ declare(strict_types=1); namespace OCA\Social\Db; -use OCA\Social\Tools\Traits\TArrayTools; use DateTime; use Exception; use OCA\Social\Exceptions\FollowNotFoundException; use OCA\Social\Model\ActivityPub\Actor\Person; use OCA\Social\Model\ActivityPub\Object\Follow; +use OCA\Social\Tools\Traits\TArrayTools; use OCP\DB\QueryBuilder\IQueryBuilder; /** @@ -285,13 +285,10 @@ class FollowsRequest extends FollowsRequestBuilder { */ public function deleteRelatedId(string $actorId) { $qb = $this->getFollowsDeleteSql(); - $this->limitToActorId($qb, $actorId); - - $qb->execute(); - - $qb = $this->getFollowsDeleteSql(); - $this->limitToObjectId($qb, $actorId); - + $orX = $qb->expr()->orX(); + $orX->add($qb->exprLimitToDBField('actor_id_prim', $qb->prim($actorId))); + $orX->add($qb->exprLimitToDBField('object_id_prim', $qb->prim($actorId))); + $qb->where($orX); $qb->execute(); } @@ -304,4 +301,36 @@ class FollowsRequest extends FollowsRequestBuilder { $qb->execute(); } + + + /** + * @param string $actorId + * @param Person $new + */ + public function moveAccountFollowers(string $actorId, Person $new) { + $qb = $this->getFollowsUpdateSql(); + $qb->set('object_id', $qb->createNamedParameter($new->getId())) + ->set('object_id_prim', $qb->createNamedParameter($qb->prim($new->getId()))) + ->set('follow_id', $qb->createNamedParameter($new->getFollowers())) + ->set('follow_id_prim', $qb->createNamedParameter($qb->prim($new->getFollowers()))); + + $qb->limitToObjectIdPrim($qb->prim($actorId)); + + $qb->executeStatement(); + } + + + /** + * @param string $actorId + * @param Person $new + */ + public function moveAccountFollowing(string $actorId, Person $new) { + $qb = $this->getFollowsUpdateSql(); + $qb->set('actor_id', $qb->createNamedParameter($new->getId())) + ->set('actor_id_prim', $qb->createNamedParameter($qb->prim($new->getId()))); + + $qb->limitToActorIdPrim($qb->prim($actorId)); + + $qb->executeStatement(); + } } diff --git a/lib/Db/FollowsRequestBuilder.php b/lib/Db/FollowsRequestBuilder.php index a2d2b82e..a7918c72 100644 --- a/lib/Db/FollowsRequestBuilder.php +++ b/lib/Db/FollowsRequestBuilder.php @@ -36,7 +36,6 @@ use OCA\Social\Tools\Traits\TArrayTools; use OCA\Social\Exceptions\FollowNotFoundException; use OCA\Social\Exceptions\InvalidResourceException; use OCA\Social\Model\ActivityPub\Object\Follow; -use OCP\DB\QueryBuilder\IQueryBuilder; /** * Class FollowsRequestBuilder @@ -63,9 +62,9 @@ class FollowsRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Update request * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getFollowsUpdateSql(): IQueryBuilder { + protected function getFollowsUpdateSql(): SocialQueryBuilder { $qb = $this->getQueryBuilder(); $qb->update(self::TABLE_FOLLOWS); @@ -114,9 +113,9 @@ class FollowsRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Delete request * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getFollowsDeleteSql(): IQueryBuilder { + protected function getFollowsDeleteSql(): SocialQueryBuilder { $qb = $this->getQueryBuilder(); $qb->delete(self::TABLE_FOLLOWS); diff --git a/lib/Db/RequestQueueRequest.php b/lib/Db/RequestQueueRequest.php index 4da8fbe2..1a637fb6 100644 --- a/lib/Db/RequestQueueRequest.php +++ b/lib/Db/RequestQueueRequest.php @@ -47,6 +47,7 @@ class RequestQueueRequest extends RequestQueueRequestBuilder { * Create a new Queue in the database. * * @param RequestQueue[] $queues + * * @throws Exception */ public function multiple(array $queues): void { @@ -64,6 +65,7 @@ class RequestQueueRequest extends RequestQueueRequestBuilder { $qb = $this->getRequestQueueInsertSql(); $qb->setValue('token', $qb->createNamedParameter($queue->getToken())) ->setValue('author', $qb->createNamedParameter($queue->getAuthor())) + ->setValue('author_prim', $qb->createNamedParameter($qb->prim($queue->getAuthor()))) ->setValue('activity', $qb->createNamedParameter($queue->getActivity())) ->setValue( 'instance', $qb->createNamedParameter( @@ -197,4 +199,21 @@ class RequestQueueRequest extends RequestQueueRequestBuilder { $qb->executeStatement(); } + + public function deleteByAuthor(string $actorId) { + $qb = $this->getRequestQueueDeleteSql(); + $qb->limitToDBField('author_prim', $qb->prim($actorId)); + + $qb->executeStatement(); + } + +// public function moveAccount(string $actorId, string $newId, string $instance): void { +// $qb = $this->getRequestQueueUpdateSql(); +// $qb->set('author', $qb->createNamedParameter($newId)) +// ->set('author_prim', $qb->createNamedParameter($qb->prim($newId))) +// ->set('instance', $qb->createNamedParameter($instance)); +// $qb->limitToDBField('author_prim', $qb->prim($actorId)); +// +// $qb->execute(); +// } } diff --git a/lib/Db/SocialCrossQueryBuilder.php b/lib/Db/SocialCrossQueryBuilder.php index 3bc16b12..239e0404 100644 --- a/lib/Db/SocialCrossQueryBuilder.php +++ b/lib/Db/SocialCrossQueryBuilder.php @@ -293,7 +293,7 @@ class SocialCrossQueryBuilder extends SocialCoreQueryBuilder { * @param string $aliasDest * @param string $alias */ - public function innerJoinSteamDest( + public function innerJoinStreamDest( string $type, string $field = 'id_prim', string $aliasDest = 'sd', string $alias = '' ) { $this->andWhere($this->exprInnerJoinStreamDest($type, $field, $aliasDest, $alias)); diff --git a/lib/Db/SocialLimitsQueryBuilder.php b/lib/Db/SocialLimitsQueryBuilder.php index e025c740..88bc4369 100644 --- a/lib/Db/SocialLimitsQueryBuilder.php +++ b/lib/Db/SocialLimitsQueryBuilder.php @@ -189,7 +189,7 @@ class SocialLimitsQueryBuilder extends SocialCrossQueryBuilder { * @param string $alias */ public function limitToActorIdPrim(string $actorId, string $alias = '') { - $this->limitToDBField('actor_id', $actorId, false, $alias); + $this->limitToDBField('actor_id_prim', $actorId, false, $alias); } @@ -278,7 +278,7 @@ class SocialLimitsQueryBuilder extends SocialCrossQueryBuilder { */ public function limitToAttributedTo(string $actorId, bool $prim = false) { if ($prim) { - $this->limitToDBField('attributed_to_prim', $this->prim($actorId), false); + $this->limitToDBField('attributed_to_prim', $this->prim($actorId)); return; } @@ -437,7 +437,7 @@ class SocialLimitsQueryBuilder extends SocialCrossQueryBuilder { ) { if (!$this->hasViewer()) { $this->selectDestFollowing($aliasDest); - $this->innerJoinSteamDest('recipient', 'id_prim', 'sd', 's'); + $this->innerJoinStreamDest('recipient', 'id_prim', 'sd', 's'); $this->limitToDest(ACore::CONTEXT_PUBLIC, 'recipient', '', $aliasDest); return; diff --git a/lib/Db/StreamDestRequest.php b/lib/Db/StreamDestRequest.php index 95f23758..2281d9d9 100644 --- a/lib/Db/StreamDestRequest.php +++ b/lib/Db/StreamDestRequest.php @@ -31,14 +31,16 @@ declare(strict_types=1); namespace OCA\Social\Db; -use OCA\Social\Tools\Traits\TStringTools; use Exception; -use OCP\DB\Exception as DBException; +use OCA\Social\Model\ActivityPub\Actor\Person; use OCA\Social\Model\ActivityPub\Internal\SocialAppNotification; use OCA\Social\Model\ActivityPub\Stream; +use OCA\Social\Model\StreamDest; use OCA\Social\Service\CacheActorService; use OCA\Social\Service\ConfigService; use OCA\Social\Service\MiscService; +use OCA\Social\Tools\Traits\TStringTools; +use OCP\DB\Exception as DBException; use OCP\IDBConnection; use OCP\IURLGenerator; use Psr\Log\LoggerInterface; @@ -54,7 +56,8 @@ class StreamDestRequest extends StreamDestRequestBuilder { private CacheActorService $cacheActorService; public function __construct( - IDBConnection $connection, LoggerInterface $logger, IURLGenerator $urlGenerator, CacheActorService $cacheActorService, + IDBConnection $connection, LoggerInterface $logger, IURLGenerator $urlGenerator, + CacheActorService $cacheActorService, ConfigService $configService, MiscService $miscService ) { parent::__construct($connection, $logger, $urlGenerator, $configService, $miscService); @@ -153,9 +156,50 @@ class StreamDestRequest extends StreamDestRequestBuilder { } public function emptyStreamDest(): void { - $qb = $this->dbConnection->getQueryBuilder(); + $qb = $this->getQueryBuilder(); $qb->delete(self::TABLE_STREAM_DEST); $qb->executeStatement(); } + + + /** + * @param string $actorId + * + * @return StreamDest[] + */ + public function getRelatedToActor(Person $actor): array { + $qb = $this->getStreamDestSelectSql(); + $orX = $qb->expr()->orX(); + $orX->add($qb->exprLimitToDBField('actor_id', $qb->prim($actor->getId()))); + $orX->add($qb->exprLimitToDBField('actor_id', $qb->prim($actor->getFollowers()))); + $orX->add($qb->exprLimitToDBField('actor_id', $qb->prim($actor->getFollowing()))); + $qb->where($orX); + + return $this->getStreamDestsFromRequest($qb); + } + + + /** + * @param string $actorId + */ + public function deleteRelatedToActor(string $actorId): void { + $qb = $this->getStreamDestDeleteSql(); + $qb->limitToActorId($qb->prim($actorId)); + + $qb->executeStatement(); + } + + + + /** + * @param string $actorId + */ + public function moveActor(string $actorId, string $newId): void { + $qb = $this->getStreamDestUpdateSql(); + $qb->set('actor_id', $qb->createNamedParameter($qb->prim($newId))); + $qb->limitToActorId($qb->prim($actorId)); + + $qb->executeStatement(); + } } diff --git a/lib/Db/StreamDestRequestBuilder.php b/lib/Db/StreamDestRequestBuilder.php index 61bac94e..5087b6a0 100644 --- a/lib/Db/StreamDestRequestBuilder.php +++ b/lib/Db/StreamDestRequestBuilder.php @@ -30,8 +30,10 @@ declare(strict_types=1); namespace OCA\Social\Db; +use OCA\Social\Exceptions\StreamDestDoesNotExistException; +use OCA\Social\Model\StreamDest; +use OCA\Social\Tools\Exceptions\RowNotFoundException; use OCA\Social\Tools\Traits\TArrayTools; -use OCP\DB\QueryBuilder\IQueryBuilder; /** * Class StreamDestRequestBuilder @@ -56,9 +58,9 @@ class StreamDestRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Update request * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getStreamDestUpdateSql(): IQueryBuilder { + protected function getStreamDestUpdateSql(): SocialQueryBuilder { $qb = $this->getQueryBuilder(); $qb->update(self::TABLE_STREAM_DEST); @@ -75,7 +77,7 @@ class StreamDestRequestBuilder extends CoreRequestBuilder { $qb = $this->getQueryBuilder(); /** @noinspection PhpMethodParametersCountMismatchInspection */ - $qb->select('sd.actor_id', 'sd.stream_id', 'sd.type') + $qb->select('sd.actor_id', 'sd.stream_id', 'sd.type', 'sd.subtype') ->from(self::TABLE_STREAM_DEST, 'sd'); $this->defaultSelectAlias = 'sd'; @@ -88,9 +90,9 @@ class StreamDestRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Delete request * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getStreamDestDeleteSql(): IQueryBuilder { + protected function getStreamDestDeleteSql(): SocialQueryBuilder { $qb = $this->getQueryBuilder(); $qb->delete(self::TABLE_STREAM_DEST); @@ -113,4 +115,44 @@ class StreamDestRequestBuilder extends CoreRequestBuilder { return $qb; } + + + /** + * @param SocialQueryBuilder $qb + * + * @return StreamDest + * @throws StreamDestDoesNotExistException + */ + public function getStreamDestFromRequest(SocialQueryBuilder $qb): StreamDest { + /** @var StreamDest $result */ + try { + $result = $qb->getRow([$this, 'parseStreamDestSelectSql']); + } catch (RowNotFoundException $e) { + throw new StreamDestDoesNotExistException(); + } + + return $result; + } + + /** + * @param SocialQueryBuilder $qb + * + * @return StreamDest[] + */ + public function getStreamDestsFromRequest(SocialQueryBuilder $qb): array { + return $qb->getRows([$this, 'parseStreamDestSelectSql']); + } + + + /** + * @param array $data + * + * @return StreamDest + */ + public function parseStreamDestSelectSql(array $data): StreamDest { + $streamDest = new StreamDest(); + $streamDest->importFromDatabase($data); + + return $streamDest; + } } diff --git a/lib/Db/StreamRequest.php b/lib/Db/StreamRequest.php index 156dfa2d..6bf43db8 100644 --- a/lib/Db/StreamRequest.php +++ b/lib/Db/StreamRequest.php @@ -97,19 +97,23 @@ class StreamRequest extends StreamRequestBuilder { } } - public function update(Stream $stream): void { + public function update(Stream $stream, bool $generateDest = false): void { $qb = $this->getStreamUpdateSql(); $qb->set('details', $qb->createNamedParameter(json_encode($stream->getDetailsAll()))); + $qb->set('to', $qb->createNamedParameter($stream->getTo())); $qb->set( - 'cc', $qb->createNamedParameter( - json_encode($stream->getCcArray(), JSON_UNESCAPED_SLASHES) - ) + 'cc', $qb->createNamedParameter(json_encode($stream->getCcArray(), JSON_UNESCAPED_SLASHES)) + ); + $qb->set( + 'to_array', $qb->createNamedParameter(json_encode($stream->getToArray(), JSON_UNESCAPED_SLASHES)) ); $qb->limitToIdPrim($qb->prim($stream->getId())); $qb->executeStatement(); - $this->streamDestRequest->generateStreamDest($stream); + if ($generateDest) { + $this->streamDestRequest->generateStreamDest($stream); + } } public function updateCache(Stream $stream, Cache $cache): void { @@ -223,6 +227,19 @@ class StreamRequest extends StreamRequestBuilder { } + /** + * @param string $idPrim + * + * @return Stream + * @throws StreamNotFoundException + */ + public function getStream(string $idPrim): Stream { + $qb = $this->getStreamSelectSql(); + $qb->limitToIdPrim($idPrim); + + return $this->getStreamFromRequest($qb); + } + /** * @param string $id * @param int $since @@ -306,7 +323,7 @@ class StreamRequest extends StreamRequestBuilder { $qb->limitToType(Note::TYPE); $qb->selectDestFollowing('sd', ''); - $qb->innerJoinSteamDest('recipient', 'id_prim', 'sd', 's'); + $qb->innerJoinStreamDest('recipient', 'id_prim', 'sd', 's'); $qb->limitToDest(ACore::CONTEXT_PUBLIC, 'recipient', '', 'sd'); $cursor = $qb->execute(); @@ -329,7 +346,7 @@ class StreamRequest extends StreamRequestBuilder { $qb->limitToType(Note::TYPE); $qb->selectDestFollowing('sd', ''); - $qb->innerJoinSteamDest('recipient', 'id_prim', 'sd', 's'); + $qb->innerJoinStreamDest('recipient', 'id_prim', 'sd', 's'); $qb->limitToDest(ACore::CONTEXT_PUBLIC, 'recipient', '', 'sd'); $qb->orderBy('id', 'desc'); @@ -567,7 +584,7 @@ class StreamRequest extends StreamRequestBuilder { $qb->limitToAttributedTo($actorId); $qb->selectDestFollowing('sd', ''); - $qb->innerJoinSteamDest('recipient', 'id_prim', 'sd', 's'); + $qb->innerJoinStreamDest('recipient', 'id_prim', 'sd', 's'); $accountIsViewer = ($qb->hasViewer() && $qb->getViewer()->getId() === $actorId); $qb->limitToDest($accountIsViewer ? '' : ACore::CONTEXT_PUBLIC, 'recipient', '', 'sd'); @@ -625,7 +642,7 @@ class StreamRequest extends StreamRequestBuilder { $qb->leftJoinStreamAction(); $qb->selectDestFollowing('sd', ''); - $qb->innerJoinSteamDest('recipient', 'id_prim', 'sd', 's'); + $qb->innerJoinStreamDest('recipient', 'id_prim', 'sd', 's'); $qb->limitToDest(ACore::CONTEXT_PUBLIC, 'recipient', 'to', 'sd'); return $this->getStreamsFromRequest($qb); @@ -656,7 +673,7 @@ class StreamRequest extends StreamRequestBuilder { $qb->leftJoinStreamAction(); $qb->selectDestFollowing('sd', ''); - $qb->innerJoinSteamDest('recipient', 'id_prim', 'sd', 's'); + $qb->innerJoinStreamDest('recipient', 'id_prim', 'sd', 's'); $qb->limitToDest(ACore::CONTEXT_PUBLIC, 'recipient', 'to', 'sd'); return $this->getStreamsFromRequest($qb); @@ -772,6 +789,19 @@ class StreamRequest extends StreamRequestBuilder { } + /** + * @param string $actorId + */ + public function updateAuthor(string $actorId, string $newId) { + $qb = $this->getStreamUpdateSql(); + $qb->set('attributed_to', $qb->createNamedParameter($newId)) + ->set('attributed_to_prim', $qb->createNamedParameter($qb->prim($newId))); + $qb->limitToAttributedTo($actorId, true); + + $qb->execute(); + } + + /** * Insert a new Stream in the database. * @@ -863,4 +893,8 @@ class StreamRequest extends StreamRequestBuilder { return $qb; } + + + public function getRelatedToActor(string $actorId) { + } } diff --git a/lib/Db/StreamTagsRequest.php b/lib/Db/StreamTagsRequest.php index 24697aed..8cbe57c2 100644 --- a/lib/Db/StreamTagsRequest.php +++ b/lib/Db/StreamTagsRequest.php @@ -67,7 +67,7 @@ class StreamTagsRequest extends StreamTagsRequestBuilder { } public function emptyStreamTags(): void { - $qb = $this->dbConnection->getQueryBuilder(); + $qb = $this->getQueryBuilder(); $qb->delete(self::TABLE_STREAM_TAGS); $qb->executeStatement(); diff --git a/lib/Exceptions/StreamDestDoesNotExistException.php b/lib/Exceptions/StreamDestDoesNotExistException.php new file mode 100644 index 00000000..ee3352f1 --- /dev/null +++ b/lib/Exceptions/StreamDestDoesNotExistException.php @@ -0,0 +1,33 @@ + + * @copyright 2022, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Social\Exceptions; + +use Exception; + +class StreamDestDoesNotExistException extends Exception { +} diff --git a/lib/Interfaces/Activity/MoveInterface.php b/lib/Interfaces/Activity/MoveInterface.php new file mode 100644 index 00000000..cf61ff53 --- /dev/null +++ b/lib/Interfaces/Activity/MoveInterface.php @@ -0,0 +1,171 @@ + + * @copyright 2022, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +namespace OCA\Social\Interfaces\Activity; + +use OCA\Social\Db\ActionsRequest; +use OCA\Social\Db\CacheDocumentsRequest; +use OCA\Social\Db\FollowsRequest; +use OCA\Social\Db\StreamDestRequest; +use OCA\Social\Db\StreamRequest; +use OCA\Social\Exceptions\CacheActorDoesNotExistException; +use OCA\Social\Exceptions\InvalidOriginException; +use OCA\Social\Exceptions\StreamNotFoundException; +use OCA\Social\Interfaces\IActivityPubInterface; +use OCA\Social\Model\ActivityPub\ACore; +use OCA\Social\Model\ActivityPub\Actor\Person; +use OCA\Social\Service\CacheActorService; + +class MoveInterface extends AbstractActivityPubInterface implements IActivityPubInterface { + private ActionsRequest $actionsRequest; + private CacheDocumentsRequest $cacheDocumentsRequest; + private FollowsRequest $followsRequest; + private StreamRequest $streamRequest; + private StreamDestRequest $streamDestRequest; + private CacheActorService $cacheActorService; + + public function __construct( + ActionsRequest $actionsRequest, + CacheDocumentsRequest $cacheDocumentsRequest, + FollowsRequest $followsRequest, + StreamRequest $streamRequest, + StreamDestRequest $streamDestRequest, + CacheActorService $cacheActorService + ) { + $this->actionsRequest = $actionsRequest; + $this->cacheDocumentsRequest = $cacheDocumentsRequest; + $this->streamRequest = $streamRequest; + $this->streamDestRequest = $streamDestRequest; + $this->followsRequest = $followsRequest; + $this->cacheActorService = $cacheActorService; + } + + + /** + * @throws InvalidOriginException + */ + public function processIncomingRequest(ACore $item): void { + $item->checkOrigin($item->getId()); + $item->checkOrigin($item->getObjectId()); + $item->checkOrigin($item->getActorId()); + + try { + $old = $this->cacheActorService->getFromAccount($item->getActorId(), false); + } catch (CacheActorDoesNotExistException $e) { + return; + } + + $new = $this->cacheActorService->getFromAccount($item->getTarget()); + $this->moveAccount($old, $new); + } + + + public function moveAccount(Person $actor, Person $target): void { + $this->actionsRequest->moveAccount($actor->getId(), $target->getId()); + $this->cacheDocumentsRequest->moveAccount($actor->getId(), $target->getId()); + + $this->followsRequest->moveAccountFollowers($actor->getId(), $target); + $this->followsRequest->moveAccountFollowing($actor->getId(), $target); + + $this->updateStreamFromActor($actor, $target); + } + + + /** + * @param Person $actor + */ + private function updateStreamFromActor(Person $actor, Person $new): void { + // first, we delete all post generate by actor + $this->streamRequest->updateAuthor($actor->getId(), $new->getId()); + + // then we look for link to the actor as dest + foreach ($this->streamDestRequest->getRelatedToActor($actor) as $streamDest) { + if ($streamDest->getType() !== 'recipient') { + continue; + } + + try { + $stream = $this->streamRequest->getStream($streamDest->getStreamId()); + } catch (StreamNotFoundException $e) { + continue; + } + + // upgrading to[] and cc[] based on old actorId and followId with new uri + $changed = false; + switch ($streamDest->getSubtype()) { + case 'to': + if ($stream->getTo() === $actor->getId()) { + $stream->setTo($new->getId()); + $changed = true; + } + + foreach ( + [ + $actor->getId() => $new->getId(), + $actor->getFollowers() => $new->getFollowers(), + $actor->getFollowing() => $new->getFollowing() + ] as $itemId => $newId + ) { + $arr = $stream->getToArray(); + if (in_array($itemId, $arr)) { + $stream->setToArray(array_unique(array_merge(array_diff($arr, [$itemId]), [$newId]))); + $changed = true; + } + } + break; + + case 'cc': + $changed = false; + foreach ( + [ + $actor->getId() => $new->getId(), + $actor->getFollowers() => $new->getFollowers(), + $actor->getFollowing() => $new->getFollowing() + ] as $itemId => $newId + ) { + $arr = $stream->getCcArray(); + if (in_array($itemId, $arr)) { + $stream->setCcArray(array_unique(array_merge(array_diff($arr, [$itemId]), [$newId]))); + $changed = true; + } + } + break; + } + + if ($changed) { + $this->streamRequest->update($stream); + } + } + + $this->streamDestRequest->moveActor($actor->getId(), $new->getId()); + $this->streamDestRequest->moveActor($actor->getFollowing(), $new->getFollowing()); + $this->streamDestRequest->moveActor($actor->getFollowers(), $new->getFollowers()); + } +} diff --git a/lib/Interfaces/Actor/PersonInterface.php b/lib/Interfaces/Actor/PersonInterface.php index 5a5c1c63..256d5389 100644 --- a/lib/Interfaces/Actor/PersonInterface.php +++ b/lib/Interfaces/Actor/PersonInterface.php @@ -31,21 +31,26 @@ declare(strict_types=1); namespace OCA\Social\Interfaces\Actor; -use OCA\Social\Tools\Traits\TArrayTools; +use OCA\Social\Db\ActionsRequest; use OCA\Social\Db\CacheActorsRequest; +use OCA\Social\Db\CacheDocumentsRequest; use OCA\Social\Db\FollowsRequest; +use OCA\Social\Db\RequestQueueRequest; +use OCA\Social\Db\StreamDestRequest; use OCA\Social\Db\StreamRequest; use OCA\Social\Exceptions\CacheActorDoesNotExistException; use OCA\Social\Exceptions\InvalidOriginException; use OCA\Social\Exceptions\ItemNotFoundException; +use OCA\Social\Exceptions\StreamNotFoundException; use OCA\Social\Interfaces\Activity\AbstractActivityPubInterface; use OCA\Social\Interfaces\IActivityPubInterface; use OCA\Social\Model\ActivityPub\ACore; +use OCA\Social\Model\ActivityPub\Activity\Delete; use OCA\Social\Model\ActivityPub\Activity\Update; use OCA\Social\Model\ActivityPub\Actor\Person; use OCA\Social\Service\ActorService; use OCA\Social\Service\ConfigService; -use OCA\Social\Service\MiscService; +use OCA\Social\Tools\Traits\TArrayTools; /** * Class PersonService @@ -55,24 +60,36 @@ use OCA\Social\Service\MiscService; class PersonInterface extends AbstractActivityPubInterface implements IActivityPubInterface { use TArrayTools; + private ActionsRequest $actionsRequest; private CacheActorsRequest $cacheActorsRequest; - private StreamRequest $streamRequest; + private CacheDocumentsRequest $cacheDocumentsRequest; private FollowsRequest $followsRequest; + private RequestQueueRequest $requestQueueRequest; + private StreamRequest $streamRequest; + private StreamDestRequest $streamDestRequest; private ActorService $actorService; private ConfigService $configService; - private MiscService $miscService; public function __construct( - CacheActorsRequest $cacheActorsRequest, StreamRequest $streamRequest, - FollowsRequest $followsRequest, ActorService $actorService, ConfigService $configService, - MiscService $miscService + ActionsRequest $actionsRequest, + CacheActorsRequest $cacheActorsRequest, + CacheDocumentsRequest $cacheDocumentsRequest, + FollowsRequest $followsRequest, + RequestQueueRequest $requestQueueRequest, + StreamRequest $streamRequest, + StreamDestRequest $streamDestRequest, + ActorService $actorService, + ConfigService $configService ) { + $this->actionsRequest = $actionsRequest; $this->cacheActorsRequest = $cacheActorsRequest; - $this->streamRequest = $streamRequest; + $this->cacheDocumentsRequest = $cacheDocumentsRequest; $this->followsRequest = $followsRequest; + $this->requestQueueRequest = $requestQueueRequest; + $this->streamRequest = $streamRequest; + $this->streamDestRequest = $streamDestRequest; $this->actorService = $actorService; $this->configService = $configService; - $this->miscService = $miscService; } /** @@ -102,8 +119,14 @@ class PersonInterface extends AbstractActivityPubInterface implements IActivityP /** @var Person $item */ $activity->checkOrigin($item->getId()); - if ($activity->getType() === Update::TYPE) { - $this->updateActor($item, $activity); + switch ($activity->getType()) { + case Update::TYPE: + $this->updateActor($item, $activity); + break; + + case Delete::TYPE: + $this->deleteActor($item); + break; } } @@ -118,13 +141,75 @@ class PersonInterface extends AbstractActivityPubInterface implements IActivityP } } - public function delete(ACore $item): void { - /** @var Person $item */ - $this->cacheActorsRequest->deleteCacheById($item->getId()); - $this->streamRequest->deleteByAuthor($item->getId()); - $this->followsRequest->deleteRelatedId($item->getId()); + public function deleteActor(Person $actor): void { + $this->actionsRequest->deleteByActor($actor->getId()); + $this->cacheActorsRequest->deleteCacheById($actor->getId()); + $this->cacheDocumentsRequest->deleteByParent($actor->getId()); + $this->requestQueueRequest->deleteByAuthor($actor->getId()); + $this->followsRequest->deleteRelatedId($actor->getId()); + + $this->deleteStreamFromActor($actor); } + + /** + * @param Person $actor + */ + private function deleteStreamFromActor(Person $actor): void { + // first, we delete all post generate by actor + $this->streamRequest->deleteByAuthor($actor->getId()); + + // then we look for link to the actor as dest + foreach ($this->streamDestRequest->getRelatedToActor($actor) as $streamDest) { + if ($streamDest->getType() !== 'recipient') { + continue; + } + + try { + $stream = $this->streamRequest->getStream($streamDest->getStreamId()); + } catch (StreamNotFoundException $e) { + continue; + } + + // upgrading to[] and cc[] without the deleted actor and follow uri + switch ($streamDest->getSubtype()) { + case 'to': + if ($stream->getTo() === $actor->getId()) { + $this->removeStreamAndRelated($streamDest->getStreamId()); + } + + $arr = array_diff( + $stream->getToArray(), + [$actor->getId(), $actor->getFollowers(), $actor->getFollowing()] + ); + if (!empty(array_diff($stream->getToArray(), $arr))) { + $stream->setToArray($arr); + $this->streamRequest->update($stream); + } + break; + case 'cc': + $arr = array_diff( + $stream->getCcArray(), + [$actor->getId(), $actor->getFollowers(), $actor->getFollowing()] + ); + if (!empty(array_diff($stream->getCcArray(), $arr))) { + $stream->setCcArray($arr); + $this->streamRequest->update($stream); + } + break; + } + } + + $this->streamDestRequest->deleteRelatedToActor($actor->getId()); + } + + + // get stream's relative and remove everything + private function removeStreamAndRelated(string $idPrim): void { + $this->streamRequest->deleteById($idPrim); + } + + private function updateActor(Person $actor, ACore $activity) { $actor->setCreation($activity->getOriginCreationTime()); diff --git a/lib/Interfaces/Internal/SocialAppNotificationInterface.php b/lib/Interfaces/Internal/SocialAppNotificationInterface.php index 8a155abe..88eaca2c 100644 --- a/lib/Interfaces/Internal/SocialAppNotificationInterface.php +++ b/lib/Interfaces/Internal/SocialAppNotificationInterface.php @@ -70,7 +70,7 @@ class SocialAppNotificationInterface extends AbstractActivityPubInterface implem $this->miscService->log( 'Updating notification: ' . json_encode($notification, JSON_UNESCAPED_SLASHES), 1 ); - $this->streamRequest->update($notification); + $this->streamRequest->update($notification, true); } public function delete(ACore $item): void { diff --git a/lib/Interfaces/Object/AnnounceInterface.php b/lib/Interfaces/Object/AnnounceInterface.php index 411f22b2..31495534 100644 --- a/lib/Interfaces/Object/AnnounceInterface.php +++ b/lib/Interfaces/Object/AnnounceInterface.php @@ -174,7 +174,7 @@ class AnnounceInterface extends AbstractActivityPubInterface implements IActivit $knownItem->setAttributedTo($actor->getId()); if (!$knownItem->hasCc($actor->getFollowers())) { $knownItem->addCc($actor->getFollowers()); - $this->streamRequest->update($knownItem); + $this->streamRequest->update($knownItem, true); } try { @@ -226,7 +226,7 @@ class AnnounceInterface extends AbstractActivityPubInterface implements IActivit if (empty($knownItem->getCcArray())) { $this->streamRequest->deleteById($knownItem->getId(), Announce::TYPE); } else { - $this->streamRequest->update($knownItem); + $this->streamRequest->update($knownItem, true); } } catch (StreamNotFoundException|ItemUnknownException|SocialAppConfigException $e) { } @@ -292,7 +292,7 @@ class AnnounceInterface extends AbstractActivityPubInterface implements IActivit 'boosts', $this->actionsRequest->countActions($post->getId(), Announce::TYPE) ); - $this->streamRequest->update($post); + $this->streamRequest->update($post, true); } /** diff --git a/lib/Interfaces/Object/LikeInterface.php b/lib/Interfaces/Object/LikeInterface.php index e58a462d..4f2dbb25 100644 --- a/lib/Interfaces/Object/LikeInterface.php +++ b/lib/Interfaces/Object/LikeInterface.php @@ -173,7 +173,7 @@ class LikeInterface extends AbstractActivityPubInterface implements IActivityPub 'likes', $this->actionsRequest->countActions($post->getId(), Like::TYPE) ); - $this->streamRequest->update($post); + $this->streamRequest->update($post, true); } diff --git a/lib/Migration/Version1000Date20221118000001.php b/lib/Migration/Version1000Date20221118000001.php index b38d7f3f..aa5e33da 100644 --- a/lib/Migration/Version1000Date20221118000001.php +++ b/lib/Migration/Version1000Date20221118000001.php @@ -918,6 +918,14 @@ class Version1000Date20221118000001 extends SimpleMigrationStep { 'default' => '', ] ); + $table->addColumn( + 'parent_id_prim', Types::STRING, + [ + 'notnull' => false, + 'length' => 32, + 'default' => '', + ] + ); $table->addColumn( 'media_type', Types::STRING, [ @@ -1141,6 +1149,14 @@ class Version1000Date20221118000001 extends SimpleMigrationStep { 'default' => '' ] ); + $table->addColumn( + 'author_prim', Types::STRING, + [ + 'notnull' => false, + 'length' => 32, + 'default' => '' + ] + ); $table->addColumn( 'activity', Types::TEXT, [ diff --git a/lib/Model/ActivityPub/Activity/Move.php b/lib/Model/ActivityPub/Activity/Move.php new file mode 100644 index 00000000..8ae917b0 --- /dev/null +++ b/lib/Model/ActivityPub/Activity/Move.php @@ -0,0 +1,62 @@ + + * @copyright 2018, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Social\Model\ActivityPub\Activity; + +use JsonSerializable; +use OCA\Social\Model\ActivityPub\ACore; + +class Move extends ACore implements JsonSerializable { + public const TYPE = 'Move'; + + public function __construct($parent = null) { + parent::__construct($parent); + + $this->setType(self::TYPE); + } + + + /** + * @param array $data + */ + public function import(array $data) { + parent::import($data); + $this->setActorId($this->validate(ACore::AS_ID, 'actor', $data, '')); + $this->setObjectId($this->validate(ACore::AS_ID, 'object', $data, '')); + $this->setTarget($this->validate(ACore::AS_ID, 'target', $data, '')); + } + + + /** + * @return array + */ + public function jsonSerialize(): array { + return parent::jsonSerialize(); + } +} diff --git a/lib/Model/ActivityPub/Item.php b/lib/Model/ActivityPub/Item.php index 38483c5e..aedf37b7 100644 --- a/lib/Model/ActivityPub/Item.php +++ b/lib/Model/ActivityPub/Item.php @@ -30,9 +30,9 @@ declare(strict_types=1); namespace OCA\Social\Model\ActivityPub; -use OCA\Social\Tools\Traits\TArrayTools; use OCA\Social\Model\ActivityPub\Actor\Person; use OCA\Social\Model\InstancePath; +use OCA\Social\Tools\Traits\TArrayTools; class Item { use TArrayTools; @@ -59,6 +59,7 @@ class Item { private string $actorId = ''; private string $iconId = ''; private string $objectId = ''; + private string $target = ''; private bool $completeDetails = false; private string $source = ''; private bool $local = false; @@ -529,6 +530,15 @@ class Item { return $this; } + public function getTarget(): string { + return $this->target; + } + + public function setTarget(string $target): Item { + $this->target = $target; + + return $this; + } /** * @return string diff --git a/lib/Model/StreamDest.php b/lib/Model/StreamDest.php new file mode 100644 index 00000000..c88bd2c7 --- /dev/null +++ b/lib/Model/StreamDest.php @@ -0,0 +1,102 @@ + + * @copyright 2022, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Social\Model; + +use JsonSerializable; +use OCA\Social\Tools\IQueryRow; +use OCA\Social\Tools\Traits\TArrayTools; + +class StreamDest implements IQueryRow, JsonSerializable { + use TArrayTools; + + private string $streamId = ''; + private string $actorId = ''; + private string $type = ''; + private string $subtype = ''; + + public function __construct() { + } + + public function setStreamId(string $streamId): self { + $this->streamId = $streamId; + + return $this; + } + + public function getStreamId(): string { + return $this->streamId; + } + + public function setActorId(string $actorId): self { + $this->actorId = $actorId; + + return $this; + } + + public function getActorId(): string { + return $this->actorId; + } + + public function setType(string $type): self { + $this->type = $type; + + return $this; + } + + public function getType(): string { + return $this->type; + } + + public function setSubtype(string $subtype): self { + $this->subtype = $subtype; + + return $this; + } + + public function getSubtype(): string { + return $this->subtype; + } + + public function importFromDatabase(array $data): void { + $this->setStreamId($this->get('stream_id', $data)); + $this->setActorId($this->get('actor_id', $data)); + $this->setType($this->get('type', $data)); + $this->setSubtype($this->get('subtype', $data)); + } + + public function jsonSerialize(): array { + return [ + 'streamId' => $this->getStreamId(), + 'actorId' => $this->getActorId(), + 'type' => $this->getType(), + 'subtype' => $this->getSubtype(), + ]; + } +}