diff options
-rw-r--r-- | appinfo/info.xml | 1 | ||||
-rw-r--r-- | lib/Command/CheckInstall.php | 44 | ||||
-rw-r--r-- | lib/Command/ExtendedBase.php | 134 | ||||
-rw-r--r-- | lib/Command/NoteBoost.php | 14 | ||||
-rw-r--r-- | lib/Command/NoteLike.php | 14 | ||||
-rw-r--r-- | lib/Command/StreamDetails.php | 145 | ||||
-rw-r--r-- | lib/Command/Timeline.php | 103 | ||||
-rw-r--r-- | lib/Db/CoreRequestBuilder.php | 58 | ||||
-rw-r--r-- | lib/Db/FollowsRequest.php | 22 | ||||
-rw-r--r-- | lib/Db/FollowsRequestBuilder.php | 8 | ||||
-rw-r--r-- | lib/Interfaces/Object/NoteInterface.php | 11 | ||||
-rw-r--r-- | lib/Model/ActivityPub/ACore.php | 24 | ||||
-rw-r--r-- | lib/Model/ActivityPub/Actor/Person.php | 1 | ||||
-rw-r--r-- | lib/Model/ActivityPub/Item.php | 11 | ||||
-rw-r--r-- | lib/Model/StreamDetails.php | 208 | ||||
-rw-r--r-- | lib/Service/BoostService.php | 18 | ||||
-rw-r--r-- | lib/Service/ConfigService.php | 1 | ||||
-rw-r--r-- | lib/Service/DetailsService.php | 131 | ||||
-rw-r--r-- | lib/Service/FollowService.php | 19 | ||||
-rw-r--r-- | lib/Service/MiscService.php | 11 | ||||
-rw-r--r-- | lib/Service/PushService.php | 127 | ||||
-rw-r--r-- | package.json | 2 |
22 files changed, 978 insertions, 129 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml index 44f40255..a713e873 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -56,6 +56,7 @@ <command>OCA\Social\Command\NoteCreate</command> <command>OCA\Social\Command\NoteBoost</command> <command>OCA\Social\Command\Reset</command> + <command>OCA\Social\Command\StreamDetails</command> <command>OCA\Social\Command\Timeline</command> <command>OCA\Social\Command\QueueStatus</command> <command>OCA\Social\Command\QueueProcess</command> diff --git a/lib/Command/CheckInstall.php b/lib/Command/CheckInstall.php index fcd06aa0..d761cf42 100644 --- a/lib/Command/CheckInstall.php +++ b/lib/Command/CheckInstall.php @@ -35,13 +35,20 @@ use Exception; use OC\Core\Command\Base; use OCA\Social\Service\CheckService; use OCA\Social\Service\MiscService; +use OCA\Social\Service\PushService; +use OCP\IUserManager; +use OCP\Stratos\Exceptions\StratosInstallException; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class CheckInstall extends Base { + /** @var IUserManager */ + private $userManager; + /** @var CheckService */ private $checkService; @@ -52,16 +59,25 @@ class CheckInstall extends Base { /** * CacheUpdate constructor. * + * @param IUserManager $userManager * @param CheckService $checkService * @param MiscService $miscService + * @param PushService $pushService */ - public function __construct(CheckService $checkService, MiscService $miscService) { + public function __construct( + IUserManager $userManager, CheckService $checkService, MiscService $miscService, + PushService $pushService + ) { parent::__construct(); + $this->userManager = $userManager; $this->checkService = $checkService; $this->miscService = $miscService; + $this->pushService = $pushService; } + /** @var PushService */ + private $pushService; /** * @@ -69,6 +85,10 @@ class CheckInstall extends Base { protected function configure() { parent::configure(); $this->setName('social:check:install') + ->addOption( + 'stratos', '', InputOption::VALUE_REQUIRED, 'a local account used to test Stratos', + '' + ) ->setDescription('Check the integrity of the installation'); } @@ -81,8 +101,30 @@ class CheckInstall extends Base { */ protected function execute(InputInterface $input, OutputInterface $output) { $this->checkService->checkInstallationStatus(); + + $this->checkStratos($input, $output); } + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @throws Exception + */ + private function checkStratos(InputInterface $input, OutputInterface $output) { + $userId = $input->getOption('stratos'); + if ($userId !== '') { + $user = $this->userManager->get($userId); + if ($user === null) { + throw new Exception('unknown user'); + } + + $wrapper = $this->pushService->testOnAccount($userId); + + $output->writeln(json_encode($wrapper, JSON_PRETTY_PRINT)); + } + } + } diff --git a/lib/Command/ExtendedBase.php b/lib/Command/ExtendedBase.php new file mode 100644 index 00000000..743c6fb5 --- /dev/null +++ b/lib/Command/ExtendedBase.php @@ -0,0 +1,134 @@ +<?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 daita\MySmallPhpTools\Exceptions\CacheItemNotFoundException; +use OC\Core\Command\Base; +use OCA\Social\AP; +use OCA\Social\Exceptions\ItemUnknownException; +use OCA\Social\Exceptions\RedundancyLimitException; +use OCA\Social\Exceptions\SocialAppConfigException; +use OCA\Social\Model\ActivityPub\Actor\Person; +use OCA\Social\Model\ActivityPub\Stream; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Output\OutputInterface; + + +class ExtendedBase extends Base { + + /** @var OutputInterface */ + protected $output; + + /** @var bool */ + protected $asJson = false; + + + /** + * @param Person $actor + */ + protected function outputActor(Person $actor) { + if ($this->asJson) { + $this->output->writeln(json_encode($actor, JSON_PRETTY_PRINT)); + } + + $this->output->writeln('<info>Account</info>: ' . $actor->getAccount()); + $this->output->writeln('<info>Id</info>: ' . $actor->getId()); + $this->output->writeln(''); + + } + + + /** + * @param Stream[] $streams + */ + protected function outputStreams(array $streams) { + if ($this->asJson) { + $this->output->writeln(json_encode($streams, JSON_PRETTY_PRINT)); + } + + $table = new Table($this->output); + $table->setHeaders(['Id', 'Source', 'Type', 'Author', 'Content']); + $table->render(); + $this->output->writeln(''); + + foreach ($streams as $item) { + $objectId = $item->getObjectId(); + $cache = $item->getCache(); + $content = ''; + $author = ''; + if ($objectId !== '' && $cache->hasItem($objectId)) { + try { + $cachedObject = $cache->getItem($objectId) + ->getObject(); + + /** @var Stream $cachedItem */ + $cachedItem = AP::$activityPub->getItemFromData($cachedObject); + $content = $cachedItem->getContent(); + $author = $cachedItem->getActor() + ->getAccount(); + } catch (CacheItemNotFoundException $e) { + } catch (ItemUnknownException $e) { + } catch (RedundancyLimitException $e) { + } catch (SocialAppConfigException $e) { + } + } else { + $content = $item->getContent(); + $author = $item->getActor() + ->getAccount(); + } + + $table->appendRow( + [ + '<comment>' . $item->getId() . '</comment>', + '<info>' . $item->getActor() + ->getAccount() . '</info>', + $item->getType(), + '<info>' . $author . '</info>', + $content, + ] + ); + } + } + + + /** + * @param Stream $stream + */ + protected function outputStream(Stream $stream) { + $actor = $stream->getActor(); + $this->output->writeln('id: <comment>' . $stream->getId() . '</comment>'); + $this->output->writeln( + 'author: <comment>' . $actor->getAccount() . '</comment>' + ); + $this->output->writeln('type: <info>' . $stream->getType() . '</info>'); + } + +} diff --git a/lib/Command/NoteBoost.php b/lib/Command/NoteBoost.php index 3df7a515..499672ce 100644 --- a/lib/Command/NoteBoost.php +++ b/lib/Command/NoteBoost.php @@ -36,7 +36,7 @@ use OC\Core\Command\Base; use OCA\Social\Service\AccountService; use OCA\Social\Service\BoostService; use OCA\Social\Service\MiscService; -use OCA\Social\Service\NoteService; +use OCA\Social\Service\StreamService; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -50,8 +50,8 @@ use Symfony\Component\Console\Output\OutputInterface; */ class NoteBoost extends Base { - /** @var NoteService */ - private $noteService; + /** @var StreamService */ + private $streamService; /** @var AccountService */ private $accountService; @@ -67,17 +67,17 @@ class NoteBoost extends Base { * NoteBoost constructor. * * @param AccountService $accountService - * @param NoteService $noteService + * @param StreamService $streamService * @param BoostService $boostService * @param MiscService $miscService */ public function __construct( - AccountService $accountService, NoteService $noteService, BoostService $boostService, + AccountService $accountService, StreamService $streamService, BoostService $boostService, MiscService $miscService ) { parent::__construct(); - $this->noteService = $noteService; + $this->streamService = $streamService; $this->boostService = $boostService; $this->accountService = $accountService; $this->miscService = $miscService; @@ -108,7 +108,7 @@ class NoteBoost extends Base { $noteId = $input->getArgument('note_id'); $actor = $this->accountService->getActorFromUserId($userId); - $this->noteService->setViewer($actor); + $this->streamService->setViewer($actor); if (!$input->getOption('unboost')) { $activity = $this->boostService->create($actor, $noteId, $token); diff --git a/lib/Command/NoteLike.php b/lib/Command/NoteLike.php index d2618c0d..6c45bb95 100644 --- a/lib/Command/NoteLike.php +++ b/lib/Command/NoteLike.php @@ -36,7 +36,7 @@ use OC\Core\Command\Base; use OCA\Social\Service\AccountService; use OCA\Social\Service\LikeService; use OCA\Social\Service\MiscService; -use OCA\Social\Service\NoteService; +use OCA\Social\Service\StreamService; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -51,8 +51,8 @@ use Symfony\Component\Console\Output\OutputInterface; class NoteLike extends Base { - /** @var NoteService */ - private $noteService; + /** @var StreamService */ + private $streamService; /** @var AccountService */ private $accountService; @@ -68,17 +68,17 @@ class NoteLike extends Base { * NoteBoost constructor. * * @param AccountService $accountService - * @param NoteService $noteService + * @param StreamService $streamService * @param LikeService $likeService * @param MiscService $miscService */ public function __construct( - AccountService $accountService, NoteService $noteService, LikeService $likeService, + AccountService $accountService, StreamService $streamService, LikeService $likeService, MiscService $miscService ) { parent::__construct(); - $this->noteService = $noteService; + $this->streamService = $streamService; $this->likeService = $likeService; $this->accountService = $accountService; $this->miscService = $miscService; @@ -109,7 +109,7 @@ class NoteLike extends Base { $noteId = $input->getArgument('note_id'); $actor = $this->accountService->getActorFromUserId($userId); - $this->noteService->setViewer($actor); + $this->streamService->setViewer($actor); if (!$input->getOption('unlike')) { $activity = $this->likeService->create($actor, $noteId, $token); diff --git a/lib/Command/StreamDetails.php b/lib/Command/StreamDetails.php new file mode 100644 index 00000000..f6e952b5 --- /dev/null +++ b/lib/Command/StreamDetails.php @@ -0,0 +1,145 @@ +<?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 OCA\Social\Exceptions\StreamNotFoundException; +use OCA\Social\Model\ActivityPub\Actor\Person; +use OCA\Social\Service\DetailsService; +use OCA\Social\Service\MiscService; +use OCA\Social\Service\StreamService; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\OutputInterface; + + +/** + * Class StreamDetails + * + * @package OCA\Social\Command + */ +class StreamDetails extends ExtendedBase { + + + /** @var StreamService */ + private $streamService; + + /** @var DetailsService */ + private $detailsService; + + /** @var MiscService */ + private $miscService; + + + /** + * StreamDetails constructor. + * + * @param StreamService $streamService + * @param DetailsService $detailsService + * @param MiscService $miscService + */ + public function __construct( + StreamService $streamService, DetailsService $detailsService, MiscService $miscService + ) { + parent::__construct(); + + $this->streamService = $streamService; + $this->detailsService = $detailsService; + $this->miscService = $miscService; + } + + + /** + * + */ + protected function configure() { + parent::configure(); + $this->setName('social:details') + ->addArgument('streamId', InputArgument::REQUIRED, 'Id of the Stream item') + ->addOption('json', '', InputOption::VALUE_NONE, 'return JSON format') + ->setDescription('Get details about a Stream item'); + } + + + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @throws Exception + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $output = new ConsoleOutput(); + $this->output = $output->section(); + + $this->asJson = $input->getOption('json'); + $streamId = $input->getArgument('streamId'); + + try { + $stream = $this->streamService->getStreamById($streamId); + } catch (StreamNotFoundException $e) { + throw new Exception('Unknown item'); + } + + $details = $this->detailsService->generateDetailsFromStream($stream); + + if ($this->asJson) { + $this->output->writeln(json_encode($details, JSON_PRETTY_PRINT)); + + return; + } + + $this->outputStream($stream); + $this->output->writeln(''); + + $this->output->writeln('<comment>Affected Timelines</comment>:'); + $home = array_map( + function(Person $item): string { + return $item->getUserId(); + }, $details->getHomeViewers() + ); + + $this->output->writeln('* <info>Home</info>: ' . json_encode($home, JSON_PRETTY_PRINT)); + $direct = array_map( + function(Person $item): string { + return $item->getUserId(); + }, $details->getDirectViewers() + ); + + $this->output->writeln('* <info>Direct</info>: ' . json_encode($direct, JSON_PRETTY_PRINT)); + $this->output->writeln('* <info>Public</info>: ' . ($details->isPublic() ? 'true' : 'false')); + $this->output->writeln('* <info>Federated</info>: ' . ($details->isFederated() ? 'true' : 'true')); + } + +} + diff --git a/lib/Command/Timeline.php b/lib/Command/Timeline.php index d2120ead..2379cb93 100644 --- a/lib/Command/Timeline.php +++ b/lib/Command/Timeline.php @@ -31,22 +31,13 @@ declare(strict_types=1); namespace OCA\Social\Command; -use daita\MySmallPhpTools\Exceptions\CacheItemNotFoundException; use Exception; -use OC\Core\Command\Base; -use OCA\Social\AP; use OCA\Social\Db\StreamRequest; -use OCA\Social\Exceptions\ItemUnknownException; -use OCA\Social\Exceptions\RedundancyLimitException; -use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Model\ActivityPub\Actor\Person; -use OCA\Social\Model\ActivityPub\Stream; use OCA\Social\Service\AccountService; -use OCA\Social\Service\CacheActorService; use OCA\Social\Service\ConfigService; use OCA\Social\Service\MiscService; use OCP\IUserManager; -use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -59,7 +50,7 @@ use Symfony\Component\Console\Output\OutputInterface; * * @package OCA\Social\Command */ -class Timeline extends Base { +class Timeline extends ExtendedBase { /** @var IUserManager */ private $userManager; @@ -77,18 +68,12 @@ class Timeline extends Base { private $miscService; - /** @var OutputInterface */ - private $output; - - /** @var bool */ - private $asJson; - /** @var int */ private $count; /** - * Stream constructor. + * Timeline constructor. * * @param IUserManager $userManager * @param StreamRequest $streamRequest @@ -147,25 +132,13 @@ class Timeline extends Base { $actor = $this->accountService->getActor($userId); - $this->outputActor($actor); + if (!$this->asJson) { + $this->outputActor($actor); + } $this->displayStream($actor, $timeline); } - /** - * @param Person $actor - */ - private function outputActor(Person $actor) { - if ($this->asJson) { - return; - } - - $this->output->writeln('<info>Account</info>: ' . $actor->getAccount()); - $this->output->writeln('<info>Id</info>: ' . $actor->getId()); - $this->output->writeln(''); - - } - /** * @param Person $actor @@ -177,27 +150,27 @@ class Timeline extends Base { switch ($timeline) { case 'home': $stream = $this->streamRequest->getTimelineHome($actor, 0, $this->count); - $this->outputStream($stream); + $this->outputStreams($stream); break; case 'direct': $stream = $this->streamRequest->getTimelineDirect($actor, 0, $this->count); - $this->outputStream($stream); + $this->outputStreams($stream); break; case 'notifications': $stream = $this->streamRequest->getTimelineNotifications($actor, 0, $this->count); - $this->outputStream($stream); + $this->outputStreams($stream); break; case 'local': $stream = $this->streamRequest->getTimelineGlobal(0, $this->count, true); - $this->outputStream($stream); + $this->outputStreams($stream); break; case 'global': $stream = $this->streamRequest->getTimelineGlobal(0, $this->count, false); - $this->outputStream($stream); + $this->outputStreams($stream); break; default: @@ -207,61 +180,5 @@ class Timeline extends Base { } } - - /** - * @param Stream[] $stream - */ - private function outputStream(array $stream) { - if ($this->asJson) { - $this->output->writeln(json_encode($stream, JSON_PRETTY_PRINT)); - } - - $table = new Table($this->output); - $table->setHeaders(['Id', 'Source', 'Type', 'Author', 'Content']); - $table->render(); - $this->output->writeln(''); - - foreach ($stream as $item) { - $objectId = $item->getObjectId(); - $cache = $item->getCache(); - $content = ''; - $author = ''; - if ($objectId !== '' && $cache->hasItem($objectId)) { - try { - $cachedObject = $cache->getItem($objectId) - ->getObject(); - - /** @var Stream $cachedItem */ - $cachedItem = AP::$activityPub->getItemFromData($cachedObject); - $content = $cachedItem->getContent(); - $author = $cachedItem->getActor() - ->getAccount(); - } catch (CacheItemNotFoundException $e) { - } catch (ItemUnknownException $e) { - } catch (RedundancyLimitException $e) { - } catch (SocialAppConfigException $e) { - } - } else { - $content = $item->getContent(); - $author = $item->getActor() - ->getAccount(); - } - - $table->appendRow( - [ - '<comment>' . $item->getId() . '</comment>', - '<info>' . $item->getActor() - ->getAccount() . '</info>', - $item->getType(), - '<info>' . $author . '</info>', - $content, - ] - ); - } - - - } - - } diff --git a/lib/Db/CoreRequestBuilder.php b/lib/Db/CoreRequestBuilder.php index 7210e19c..040c1736 100644 --- a/lib/Db/CoreRequestBuilder.php +++ b/lib/Db/CoreRequestBuilder.php @@ -767,7 +767,40 @@ class CoreRequestBuilder { $qb->leftJoin( $this->defaultSelectAlias, CoreRequestBuilder::TABLE_CACHE_ACTORS, 'ca', $orX ); + } + + + /** + * @param IQueryBuilder $qb + * @param string $fieldActorId + * @param string $alias + */ + protected function leftJoinAccounts(IQueryBuilder &$qb, string $fieldActorId, string $alias = '' + ) { + if ($qb->getType() !== QueryBuilder::SELECT) { + return; + } + + $expr = $qb->expr(); + $func = $qb->func(); + + $pf = ($alias === '') ? $this->defaultSelectAlias : $alias; + $qb->selectAlias('lja.id', 'accounts_id') + ->selectAlias('lja.user_id', 'accounts_user_id') + ->selectAlias('lja.preferred_username', 'accounts_preferred_username') + ->selectAlias('lja.name', 'accounts_name') + ->selectAlias('lja.summary', 'accounts_summary') + ->selectAlias('lja.public_key', 'accounts_public_key'); + + $on = $expr->eq( + $func->lower($pf . '.' . $fieldActorId), + $func->lower('lja.id') + ); + + $qb->leftJoin( + $this->defaultSelectAlias, CoreRequestBuilder::TABLE_ACTORS, 'lja', $on + ); } @@ -798,6 +831,31 @@ class CoreRequestBuilder { /** + * @param array $data + * + * @return Person + * @throws InvalidResourceException + */ + protected function parseAccountsLeftJoin(array $data): Person { + $new = []; + foreach ($data as $k => $v) { + if (substr($k, 0, 9) === 'accounts_') { + $new[substr($k, 9)] = $v; + } + } + + $actor = new Person(); + $actor->importFromDatabase($new); + + if (!$actor->getUserId()) { + throw new InvalidResourceException(); + } + + return $actor; + } + + + /** * @param IQueryBuilder $qb */ protected function leftJoinStreamAction(IQueryBuilder &$qb) { diff --git a/lib/Db/FollowsRequest.php b/lib/Db/FollowsRequest.php index 8e478caf..77ab6894 100644 --- a/lib/Db/FollowsRequest.php +++ b/lib/Db/FollowsRequest.php @@ -236,6 +236,28 @@ class FollowsRequest extends FollowsRequestBuilder { /** + * @param string $followId + * + * @return Follow[] + */ + public function getFollowersByFollowId(string $followId): array { + $qb = $this->getFollowsSelectSql(); + $this->limitToFollowId($qb, $followId); + $this->limitToAccepted($qb, true); + $this->leftJoinAccounts($qb, 'actor_id'); + + $follows = []; + $cursor = $qb->execute(); + while ($data = $cursor->fetch()) { + $follows[] = $this->parseFollowsSelectSql($data); + } + $cursor->closeCursor(); + + return $follows; + } + + + /** * @param Follow $follow */ public function delete(Follow $follow) { |