delete and move

Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
pull/1546/head
Maxence Lange 2022-12-06 23:14:12 -01:00
rodzic ca96750071
commit 7ff583eb26
27 zmienionych plików z 846 dodań i 81 usunięć

Wyświetl plik

@ -45,6 +45,7 @@
<commands>
<command>OCA\Social\Command\AccountCreate</command>
<command>OCA\Social\Command\AccountDelete</command>
<command>OCA\Social\Command\AccountFollowing</command>
<command>OCA\Social\Command\CacheRefresh</command>
<command>OCA\Social\Command\CheckInstall</command>

Wyświetl plik

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
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;
}
}

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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();
}

Wyświetl plik

@ -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);

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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();

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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);

Wyświetl plik

@ -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();
// }
}

Wyświetl plik

@ -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));

Wyświetl plik

@ -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;

Wyświetl plik

@ -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();
}
}

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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) {
}
}

Wyświetl plik

@ -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();

Wyświetl plik

@ -0,0 +1,33 @@
<?php
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Exceptions;
use Exception;
class StreamDestDoesNotExistException extends Exception {
}

Wyświetl plik

@ -0,0 +1,171 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
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());
}
}

Wyświetl plik

@ -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());

Wyświetl plik

@ -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 {

Wyświetl plik

@ -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);
}
/**

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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,
[

Wyświetl plik

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
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();
}
}

Wyświetl plik

@ -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

Wyświetl plik

@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2022, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
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(),
];
}
}