timeline home sql optimization

Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
pull/752/head
Maxence Lange 2019-09-23 12:34:21 +02:00
rodzic d14392dea8
commit 961da41c47
16 zmienionych plików z 774 dodań i 109 usunięć

Wyświetl plik

@ -34,6 +34,7 @@ namespace OCA\Social\Command;
use Exception;
use OC\Core\Command\Base;
use OCA\Social\Db\CoreRequestBuilder;
use OCA\Social\Service\CheckService;
use OCA\Social\Service\ConfigService;
use OCA\Social\Service\MiscService;
use Symfony\Component\Console\Input\InputInterface;
@ -48,6 +49,9 @@ class Reset extends Base {
private $coreRequestBuilder;
/** @var CheckService */
private $checkService;
/** @var ConfigService */
private $configService;
@ -59,15 +63,17 @@ class Reset extends Base {
* CacheUpdate constructor.
*
* @param CoreRequestBuilder $coreRequestBuilder
* @param CheckService $checkService
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
CoreRequestBuilder $coreRequestBuilder, ConfigService $configService,
CoreRequestBuilder $coreRequestBuilder, CheckService $checkService, ConfigService $configService,
MiscService $miscService
) {
parent::__construct();
$this->checkService = $checkService;
$this->coreRequestBuilder = $coreRequestBuilder;
$this->configService = $configService;
$this->miscService = $miscService;
@ -131,7 +137,7 @@ class Reset extends Base {
return;
}
$this->checkService->checkInstallationStatus(true);
$output->writeln('');
$cloudAddress = $this->configService->getCloudUrl();
@ -148,6 +154,7 @@ class Reset extends Base {
}
$this->configService->setCloudUrl($newCloudAddress);
$output->writeln('');
$output->writeln('New address: <info>' . $newCloudAddress . '</info>');
}
@ -177,8 +184,7 @@ class Reset extends Base {
if ($this->configService->getCoreValue('public_host-meta') === 'social/lib/hostmeta.php') {
$this->configService->unsetCoreValue('public_host-meta');
}
if ($this->configService->getCoreValue('public_host-meta-json')
=== 'social/lib/hostmeta.php') {
if ($this->configService->getCoreValue('public_host-meta-json') === 'social/lib/hostmeta.php') {
$this->configService->unsetCoreValue('public_host-meta-json');
}
}

Wyświetl plik

@ -62,16 +62,15 @@ class ActorsRequest extends ActorsRequestBuilder {
*
* @param Person $actor
*
* @return string
* @throws SocialAppConfigException
*/
public function create(Person $actor): string {
public function create(Person $actor) {
$id = $this->configService->getSocialUrl() . '@' . $actor->getPreferredUsername();
$actor->setId($this->configService->getSocialUrl() . '@' . $actor->getPreferredUsername());
$qb = $this->getActorsInsertSql();
$qb->setValue('id', $qb->createNamedParameter($id))
// ->setValue('type', $qb->createNamedParameter($actor->getType()))
$qb->setValue('id', $qb->createNamedParameter($actor->getId()))
->setValue('id_prim', $qb->createNamedParameter($this->prim($actor->getId())))
->setValue('user_id', $qb->createNamedParameter($actor->getUserId()))
->setValue('name', $qb->createNamedParameter($actor->getName()))
->setValue('summary', $qb->createNamedParameter($actor->getSummary()))
@ -86,11 +85,7 @@ class ActorsRequest extends ActorsRequestBuilder {
$qb->createNamedParameter(new DateTime('now'), IQueryBuilder::PARAM_DATE)
);
$this->generatePrimaryKey($qb, $id);
$qb->execute();
return $id;
}

Wyświetl plik

@ -77,12 +77,12 @@ class ActorsRequestBuilder extends CoreRequestBuilder {
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select(
'sa.id', 'sa.user_id', 'sa.preferred_username', 'sa.name', 'sa.summary',
'sa.public_key', 'sa.avatar_version', 'sa.private_key', 'sa.creation'
'a.id', 'a.id_prim', 'a.user_id', 'a.preferred_username', 'a.name', 'a.summary',
'a.public_key', 'a.avatar_version', 'a.private_key', 'a.creation'
)
->from(self::TABLE_ACTORS, 'sa');
->from(self::TABLE_ACTORS, 'a');
$this->defaultSelectAlias = 'sa';
$this->defaultSelectAlias = 'a';
return $qb;
}

Wyświetl plik

@ -65,9 +65,9 @@ class CacheActorsRequest extends CacheActorsRequestBuilder {
* @param Person $actor
*/
public function save(Person $actor) {
$qb = $this->getCacheActorsInsertSql();
$qb->setValue('id', $qb->createNamedParameter($actor->getId()))
->setValue('id_prim', $qb->createNamedParameter($this->prim($actor->getId())))
->setValue('account', $qb->createNamedParameter($actor->getAccount()))
->setValue('type', $qb->createNamedParameter($actor->getType()))
->setValue('local', $qb->createNamedParameter(($actor->isLocal()) ? '1' : '0'))

Wyświetl plik

@ -58,19 +58,6 @@ use OCP\IDBConnection;
class CoreRequestBuilder {
// const TABLE_REQUEST_QUEUE = 'social_request_queue';
//
// const TABLE_SERVER_ACTORS = 'social_server_actors';
// const TABLE_SERVER_NOTES = 'social_server_notes';
// const TABLE_SERVER_HASHTAGS = 'social_server_hashtags';
// const TABLE_SERVER_FOLLOWS = 'social_server_follows';
//
// const TABLE_CACHE_ACTORS = 'social_cache_actors';
// const TABLE_CACHE_DOCUMENTS = 'social_cache_documents';
//
// const TABLE_QUEUE_STREAM = 'social_queue_stream';
// const TABLE_STREAM_ACTIONS = 'social_stream_actions';
const TABLE_REQUEST_QUEUE = 'social_a2_request_queue';
const TABLE_ACTORS = 'social_a2_actors';
@ -97,6 +84,7 @@ class CoreRequestBuilder {
self::TABLE_CACHE_ACTORS,
self::TABLE_CACHE_DOCUMENTS,
self::TABLE_STREAM_QUEUE,
self::TABLE_STREAM_DEST,
self::TABLE_STREAM_ACTIONS
];
@ -141,12 +129,28 @@ class CoreRequestBuilder {
}
/**
* @param string $id
*
* @return string
*/
public function prim(string $id): string {
if ($id === '') {
return '';
}
return hash('sha512', $id);
}
/**
* @param IQueryBuilder $qb
* @param string $id
*
* @deprecated - not that useful, the raw line should be implemented instead of calling this method !
*/
public function generatePrimaryKey(IQueryBuilder $qb, string $id) {
$qb->setValue('id_prim', $qb->createNamedParameter(hash('sha512', $id)));
$qb->setValue('id_prim', $qb->createNamedParameter($this->prim($id)));
}
@ -545,7 +549,7 @@ class CoreRequestBuilder {
* @return string
*/
protected function exprLimitToDBField(
IQueryBuilder &$qb, string $field, string $value, bool $eq, bool $cs = true,
IQueryBuilder &$qb, string $field, string $value, bool $eq = true, bool $cs = true,
string $alias = ''
): string {
$expr = $qb->expr();
@ -713,11 +717,44 @@ class CoreRequestBuilder {
}
/**
* @param IQueryBuilder $qb
* @param string $alias
*/
protected function selectCacheActors(IQueryBuilder &$qb, string $alias = 'ca') {
if ($qb->getType() !== QueryBuilder::SELECT) {
return;
}
$pf = (($alias === '') ? $this->defaultSelectAlias : $alias);
$qb->from(self::TABLE_CACHE_ACTORS, $pf);
$qb->selectAlias($pf . '.id', 'cacheactor_id')
->selectAlias($pf . '.type', 'cacheactor_type')
->selectAlias($pf . '.account', 'cacheactor_account')
->selectAlias($pf . '.following', 'cacheactor_following')
->selectAlias($pf . '.followers', 'cacheactor_followers')
->selectAlias($pf . '.inbox', 'cacheactor_inbox')
->selectAlias($pf . '.shared_inbox', 'cacheactor_shared_inbox')
->selectAlias($pf . '.outbox', 'cacheactor_outbox')
->selectAlias($pf . '.featured', 'cacheactor_featured')
->selectAlias($pf . '.url', 'cacheactor_url')
->selectAlias($pf . '.preferred_username', 'cacheactor_preferred_username')
->selectAlias($pf . '.name', 'cacheactor_name')
->selectAlias($pf . '.summary', 'cacheactor_summary')
->selectAlias($pf . '.public_key', 'cacheactor_public_key')
->selectAlias($pf . '.source', 'cacheactor_source')
->selectAlias($pf . '.creation', 'cacheactor_creation')
->selectAlias($pf . '.local', 'cacheactor_local');
}
/**
* @param IQueryBuilder $qb
* @param string $fieldActorId
* @param Person $author
* @param string $alias
*
* @deprecated ?
*/
protected function leftJoinCacheActors(
IQueryBuilder &$qb, string $fieldActorId, Person $author = null, string $alias = ''
@ -864,10 +901,8 @@ class CoreRequestBuilder {
return;
}
$expr = $qb->expr();
$func = $qb->func();
$pf = $this->defaultSelectAlias;
$expr = $qb->expr();
$qb->selectAlias('sa.id', 'streamaction_id')
->selectAlias('sa.actor_id', 'streamaction_actor_id')
@ -875,21 +910,20 @@ class CoreRequestBuilder {
->selectAlias('sa.values', 'streamaction_values');
$orX = $expr->orX();
$orX->add($expr->eq($func->lower($pf . '.id'), $func->lower('sa.stream_id')));
$orX->add($expr->eq($func->lower($pf . '.object_id'), $func->lower('sa.stream_id')));
$orX->add($expr->eq('sa.stream_id_prim', $pf . '.id_prim'));
$orX->add($expr->eq('sa.stream_id_prim', $pf . '.object_id_prim'));
$andX = $expr->andX();
$andX->add($orX);
$andX->add(
$on = $expr->andX();
$on->add(
$expr->eq(
$func->lower('sa.actor_id'),
$qb->createNamedParameter(strtolower($this->viewer->getId()))
'sa.actor_id_prim', $qb->createNamedParameter($this->prim($this->viewer->getId()))
)
);
$on->add($orX);
$qb->leftJoin(
$this->defaultSelectAlias, CoreRequestBuilder::TABLE_STREAM_ACTIONS, 'sa',
$andX
$on
);
}

Wyświetl plik

@ -35,6 +35,7 @@ use daita\MySmallPhpTools\Traits\TArrayTools;
use DateTime;
use Exception;
use OCA\Social\Exceptions\FollowDoesNotExistException;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityPub\Object\Follow;
use OCP\DB\QueryBuilder\IQueryBuilder;
@ -62,7 +63,10 @@ class FollowsRequest extends FollowsRequestBuilder {
->setValue('type', $qb->createNamedParameter($follow->getType()))
->setValue('object_id', $qb->createNamedParameter($follow->getObjectId()))
->setValue('follow_id', $qb->createNamedParameter($follow->getFollowId()))
->setValue('accepted', $qb->createNamedParameter(($follow->isAccepted()) ? '1' : '0'));
->setValue('accepted', $qb->createNamedParameter(($follow->isAccepted()) ? '1' : '0'))
->setValue('actor_id_prim', $qb->createNamedParameter($this->prim($follow->getActorId())))
->setValue('object_id_prim', $qb->createNamedParameter($this->prim($follow->getObjectId())))
->setValue('follow_id_prim', $qb->createNamedParameter($this->prim($follow->getFollowId())));
try {
$qb->setValue(
@ -78,6 +82,32 @@ class FollowsRequest extends FollowsRequestBuilder {
}
public function generateLoopbackAccount(Person $actor) {
$qb = $this->getFollowsInsertSql();
$qb->setValue('id', $qb->createNamedParameter($actor->getId()))
->setValue('actor_id', $qb->createNamedParameter($actor->getId()))
->setValue('type', $qb->createNamedParameter('Loopback'))
->setValue('object_id', $qb->createNamedParameter($actor->getId()))
->setValue('follow_id', $qb->createNamedParameter($actor->getId()))
->setValue('accepted', $qb->createNamedParameter('1'))
->setValue('actor_id_prim', $qb->createNamedParameter($this->prim($actor->getId())))
->setValue('object_id_prim', $qb->createNamedParameter($this->prim($actor->getId())))
->setValue('follow_id_prim', $qb->createNamedParameter($this->prim($actor->getId())));
try {
$qb->setValue(
'creation',
$qb->createNamedParameter(new DateTime('now'), IQueryBuilder::PARAM_DATE)
);
} catch (Exception $e) {
}
$this->generatePrimaryKey($qb, $actor->getId());
$qb->execute();
}
/**
* @param Follow $follow
*/

Wyświetl plik

@ -51,7 +51,9 @@ class StreamActionsRequest extends StreamActionsRequestBuilder {
public function create(StreamAction $action) {
$qb = $this->getStreamActionInsertSql();
$qb->setValue('actor_id', $qb->createNamedParameter($action->getActorId()))
->setValue('actor_id_prim', $qb->createNamedParameter($this->prim($action->getActorId())))
->setValue('stream_id', $qb->createNamedParameter($action->getStreamId()))
->setValue('stream_id_prim', $qb->createNamedParameter($this->prim($action->getStreamId())))
->setValue(
'values', $qb->createNamedParameter(
json_encode($action->getValues(), JSON_UNESCAPED_SLASHES)

Wyświetl plik

@ -0,0 +1,110 @@
<?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\Db;
use daita\MySmallPhpTools\Traits\TStringTools;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OCA\Social\Model\ActivityPub\Stream;
/**
* Class StreamDestRequest
*
* @package OCA\Social\Db
*/
class StreamDestRequest extends StreamDestRequestBuilder {
use TStringTools;
/**
* @return int
*/
public function countStreamDest(): int {
$qb = $this->countStreamDestSelectSql();
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
return $this->getInt('count', $data, 0);
}
/**
* @param Stream $stream
*/
public function generateStreamDest(Stream $stream) {
$recipients = [
'to' => $stream->getToAll(),
'cc' => $stream->getCcArray(),
'bcc' => $stream->getBccArray(),
'attributed_to' => [$stream->getAttributedTo()]
];
$streamId = $this->prim($stream->getId());
foreach (array_keys($recipients) as $dest) {
$type = $dest;
foreach ($recipients[$dest] as $actorId) {
$qb = $this->getStreamDestInsertSql();
$qb->setValue('stream_id', $qb->createNamedParameter($streamId));
$qb->setValue('actor_id', $qb->createNamedParameter($this->prim($actorId)));
$qb->setValue('type', $qb->createNamedParameter($type));
try {
$qb->execute();
} catch (UniqueConstraintViolationException $e) {
\OC::$server->getLogger()
->log(3, 'Social - Duplicate recipient on Stream ' . json_encode($stream));
}
}
}
}
/**
*
*/
public function generateRandomDest() {
$qb = $this->getStreamDestInsertSql();
$qb->setValue('actor_id', $qb->createNamedParameter($this->uuid()));
$qb->setValue('stream_id', $qb->createNamedParameter($this->uuid()));
$qb->setValue('type', $qb->createNamedParameter('unk'));
$qb->execute();
}
}

Wyświetl plik

@ -0,0 +1,122 @@
<?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\Db;
use daita\MySmallPhpTools\Traits\TArrayTools;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* Class StreamDestRequestBuilder
*
* @package OCA\Social\Db
*/
class StreamDestRequestBuilder extends CoreRequestBuilder {
use TArrayTools;
/**
* Base of the Sql Insert request
*
* @return IQueryBuilder
*/
protected function getStreamDestInsertSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::TABLE_STREAM_DEST);
return $qb;
}
/**
* Base of the Sql Update request
*
* @return IQueryBuilder
*/
protected function getStreamDestUpdateSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->update(self::TABLE_STREAM_DEST);
return $qb;
}
/**
* Base of the Sql Select request for Shares
*
* @return IQueryBuilder
*/
protected function getStreamDestSelectSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select('sd.actor_id', 'sd.stream_id', 'sd.type')
->from(self::TABLE_STREAM_DEST, 'sd');
$this->defaultSelectAlias = 'sd';
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return IQueryBuilder
*/
protected function getStreamDestDeleteSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::TABLE_STREAM_DEST);
return $qb;
}
/**
* Base of the Sql Select request for Shares
*
* @return IQueryBuilder
*/
protected function countStreamDestSelectSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->selectAlias($qb->createFunction('COUNT(*)'), 'count')
->from(self::TABLE_STREAM_DEST, 'sd');
$this->defaultSelectAlias = 'sd';
return $qb;
}
}

Wyświetl plik

@ -61,17 +61,25 @@ use OCP\IDBConnection;
class StreamRequest extends StreamRequestBuilder {
/** @var StreamDestRequest */
private $streamDestRequest;
/**
* StreamRequest constructor.
*
* @param IDBConnection $connection
* @param StreamDestRequest $streamDestRequest
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
IDBConnection $connection, ConfigService $configService, MiscService $miscService
IDBConnection $connection, StreamDestRequest $streamDestRequest, ConfigService $configService,
MiscService $miscService
) {
parent::__construct($connection, $configService, $miscService);
$this->streamDestRequest = $streamDestRequest;
}
@ -82,9 +90,7 @@ class StreamRequest extends StreamRequestBuilder {
$qb = $this->saveStream($stream);
if ($stream->getType() === Note::TYPE) {
/** @var Note $stream */
$qb->setValue(
'hashtags', $qb->createNamedParameter(json_encode($stream->getHashtags()))
)
$qb->setValue('hashtags', $qb->createNamedParameter(json_encode($stream->getHashtags())))
->setValue(
'attachments', $qb->createNamedParameter(
json_encode($stream->getAttachments(), JSON_UNESCAPED_SLASHES)
@ -94,6 +100,8 @@ class StreamRequest extends StreamRequestBuilder {
try {
$qb->execute();
$this->streamDestRequest->generateStreamDest($stream);
} catch (UniqueConstraintViolationException $e) {
}
}
@ -188,6 +196,7 @@ class StreamRequest extends StreamRequestBuilder {
public function updateAttributedTo(string $itemId, string $to) {
$qb = $this->getStreamUpdateSql();
$qb->set('attributed_to', $qb->createNamedParameter($to));
$qb->set('attributed_to_prim', $qb->createNamedParameter($this->prim($to)));
$this->limitToIdString($qb, $itemId);
@ -357,14 +366,17 @@ class StreamRequest extends StreamRequestBuilder {
*/
public function getTimelineHome(Person $actor, int $since = 0, int $limit = 5): array {
$qb = $this->getStreamSelectSql();
$expr = $qb->expr();
$this->leftJoinFollowing($qb, $actor);
$this->limitToFollowing($qb, $actor);
$this->selectCacheActors($qb, 'ca');
$this->selectDestFollowing($qb, 'sd', 'f');
$this->limitPaginate($qb, $since, $limit);
$this->leftJoinCacheActors($qb, 'object_id', $actor, 'f');
$this->leftJoinStreamAction($qb);
$qb->andWhere($this->exprLimitToDBField($qb, 'type', SocialAppNotification::TYPE, false));
$qb->andWhere($this->exprInnerJoinDestFollowing($qb, $actor, 'id_prim', 'sd', 'f'));
$qb->andWhere($expr->eq('f.object_id_prim', 'ca.id_prim'));
$this->leftJoinStreamAction($qb);
$this->filterDuplicate($qb);
$streams = [];
@ -713,10 +725,12 @@ class StreamRequest extends StreamRequestBuilder {
->setValue('summary', $qb->createNamedParameter($stream->getSummary()))
->setValue('published', $qb->createNamedParameter($stream->getPublished()))
->setValue('attributed_to', $qb->createNamedParameter($attributedTo))
->setValue('attributed_to_prim', $qb->createNamedParameter($this->prim($attributedTo)))
->setValue('in_reply_to', $qb->createNamedParameter($stream->getInReplyTo()))
->setValue('source', $qb->createNamedParameter($stream->getSource()))
->setValue('activity_id', $qb->createNamedParameter($stream->getActivityId()))
->setValue('object_id', $qb->createNamedParameter($stream->getObjectId()))
->setValue('object_id_prim', $qb->createNamedParameter($this->prim($stream->getObjectId())))
->setValue('details', $qb->createNamedParameter(json_encode($stream->getDetailsAll())))
->setValue('cache', $qb->createNamedParameter($cache))
->setValue(
@ -759,13 +773,14 @@ class StreamRequest extends StreamRequestBuilder {
}
$expr = $qb->expr();
$func = $qb->func();
$pf = $this->defaultSelectAlias . '.';
$on = $expr->andX();
$on->add($this->exprLimitToDBField($qb, 'actor_id', $actor->getId(), true, false, 'fs'));
$on->add($expr->eq($func->lower($pf . 'attributed_to'), $func->lower('fs.object_id')));
$on->add($this->exprLimitToDBFieldInt($qb, 'accepted', 1, 'fs'));
$on->add(
$this->exprLimitToDBField($qb, 'actor_id_prim', $this->prim($actor->getId()), true, true, 'fs')
);
$on->add($expr->eq($pf . 'attributed_to_prim', 'fs.object_id_prim'));
$qb->leftJoin($this->defaultSelectAlias, CoreRequestBuilder::TABLE_FOLLOWS, 'fs', $on);
}
@ -776,37 +791,22 @@ class StreamRequest extends StreamRequestBuilder {
*/
private function filterDuplicate(IQueryBuilder $qb) {
$actor = $this->viewer;
if ($actor === null) {
return;
}
// NEEDED ? use the follow 'f' ?
$this->leftJoinFollowStatus($qb, $actor);
$func = $qb->func();
$expr = $qb->expr();
$filter = $expr->orX();
$filter->add($this->exprLimitToDBFieldInt($qb, 'hidden_on_timeline', 0, 's'));
$follower = $expr->andX();
$follower->add(
$expr->neq(
$func->lower('attributed_to'),
$func->lower($qb->createNamedParameter($actor->getId()))
)
$this->exprLimitToDBField($qb, 'attributed_to_prim', $this->prim($actor->getId()), false)
);
$follower->add($expr->isNull('fs.id'));
// $follower->add(
// $expr->eq(
// $func->lower('f.object_id'),
// $func->lower('s.attributed_to')
// )
// );
// needed ?
// $follower->add($this->exprLimitToDBField($qb, 'actor_id', $actor->getId(), false, 'f'));
// $follower->add($this->exprLimitToDBFieldInt($qb, 'accepted', 1, 'f'));
$follower->add($expr->isNull('fs.id_prim'));
$filter->add($follower);
$qb->andWhere($filter);

Wyświetl plik

@ -188,6 +188,56 @@ class StreamRequestBuilder extends CoreRequestBuilder {
}
/**
* @param IQueryBuilder $qb
* @param string $aliasDest
* @param string $aliasFollowing
*/
protected function selectDestFollowing(
IQueryBuilder $qb, string $aliasDest = 'sd', string $aliasFollowing = 'f'
) {
if ($qb->getType() !== QueryBuilder::SELECT) {
return;
}
$qb->from(self::TABLE_STREAM_DEST, $aliasDest);
$qb->from(self::TABLE_FOLLOWS, $aliasFollowing);
}
/**
* @param IQueryBuilder $qb
* @param Person $actor
* @param string $field
* @param string $aliasDest
* @param string $aliasFollowing
*
* @param string $alias
*
* @return ICompositeExpression
*/
protected function exprInnerJoinDestFollowing(
IQueryBuilder $qb, Person $actor, string $field = 'id_prim', string $aliasDest = 'sd',
string $aliasFollowing = 'f', string $alias = ''
): ICompositeExpression {
$expr = $qb->expr();
$andX = $expr->andX();
$pf = (($alias === '') ? $this->defaultSelectAlias : $alias) . '.';
$andX->add(
$this->exprLimitToDBField(
$qb, 'actor_id_prim', $this->prim($actor->getId()), true, true, $aliasFollowing
)
);
$andX->add($this->exprLimitToDBFieldInt($qb, 'accepted', 1, $aliasFollowing));
$andX->add($expr->eq($aliasFollowing . '.follow_id_prim', $aliasDest . '.actor_id'));
$andX->add($expr->eq($aliasDest . '.stream_id', $pf . $field));
return $andX;
}
/**
* @param IQueryBuilder $qb
* @param Person $actor
@ -203,29 +253,12 @@ class StreamRequestBuilder extends CoreRequestBuilder {
}
/**
* @param IQueryBuilder $qb
* @param Person $actor
*/
protected function limitToFollowing(IQueryBuilder $qb, Person $actor) {
$expr = $qb->expr();
$andX = $expr->andX();
$andX->add($this->exprLimitToDBField($qb, 'attributed_to', $actor->getId(), true, false));
$andX->add($this->exprLimitToDBField($qb, 'cc', '[]', false));
$orX = $expr->orX();
$orX->add($andX);
$orX->add($expr->isNotNull('f.object_id'));
$qb->andWhere($orX);
}
/**
* @param IQueryBuilder $qb
* @param Person $actor
*
* @return ICompositeExpression
* @deprecated - use the new table social_stream_dest
*/
protected function exprJoinFollowing(IQueryBuilder $qb, Person $actor) {
$expr = $qb->expr();

Wyświetl plik

@ -93,6 +93,15 @@ class Version0002Date20190916000001 extends SimpleMigrationStep {
]
);
}
if (!$table->hasColumn('object_id_prim')) {
$table->addColumn(
'object_id_prim', 'string',
[
'notnull' => true,
'length' => 128,
]
);
}
if (!$schema->hasTable('social_a2_stream_dest')) {
$table = $schema->createTable('social_a2_stream_dest');
@ -115,12 +124,56 @@ class Version0002Date20190916000001 extends SimpleMigrationStep {
'type', 'string',
[
'notnull' => false,
'length' => 7,
'length' => 15,
]
);
$table->addUniqueIndex(['stream_id', 'actor_id', 'type'], 'recipient');
$table->setPrimaryKey(['stream_id', 'actor_id', 'type']);
if (!$table->hasIndex('sat')) {
$table->addUniqueIndex(['stream_id', 'actor_id', 'type'], 'sat');
$table->addUniqueIndex(['stream_id', 'actor_id'], 'sa');
}
}
$table = $schema->getTable('social_a2_stream');
if (!$table->hasColumn('object_id_prim')) {
$table->addColumn(
'object_id_prim', 'string',
[
'notnull' => true,
'length' => 128,
]
);
}
if (!$table->hasColumn('attributed_to_prim')) {
$table->addColumn(
'attributed_to_prim', 'string',
[
'notnull' => true,
'length' => 128,
]
);
}
$table = $schema->getTable('social_a2_stream_action');
if (!$table->hasColumn('actor_id_prim')) {
$table->addColumn(
'actor_id_prim', 'string',
[
'notnull' => true,
'length' => 128,
]
);
}
if (!$table->hasColumn('stream_id_prim')) {
$table->addColumn(
'stream_id_prim', 'string',
[
'notnull' => true,
'length' => 128,
]
);
}
return $schema;
@ -138,7 +191,9 @@ class Version0002Date20190916000001 extends SimpleMigrationStep {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$this->updateTableStream($schema);
$this->updateTableFollows($schema);
$this->updateTableStreamActions($schema);
$this->fillTableStreamDest($schema);
}
@ -174,6 +229,7 @@ class Version0002Date20190916000001 extends SimpleMigrationStep {
$update = $this->connection->getQueryBuilder();
$update->update('social_a2_follows');
$update->set('follow_id_prim', $update->createNamedParameter(hash('sha512', $data['follow_id'])));
$update->set('object_id_prim', $update->createNamedParameter(hash('sha512', $data['object_id'])));
$update->set('actor_id_prim', $update->createNamedParameter(hash('sha512', $data['actor_id'])));
$expr = $update->expr();
@ -183,6 +239,90 @@ class Version0002Date20190916000001 extends SimpleMigrationStep {
}
/**
* @param ISchemaWrapper $schema
*/
private function updateTableStreamActions(ISchemaWrapper $schema) {
if (!$schema->hasTable('social_a2_stream_action')) {
return;
}
$qb = $this->connection->getQueryBuilder();
$qb->select('*')
->from('social_a2_stream_action');
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$this->updateStreamActionsPrim($data);
}
$cursor->closeCursor();
}
/**
* @param array $data
*/
private function updateStreamActionsPrim(array $data) {
if ($data['actor_id_prim'] !== '') {
return;
}
$update = $this->connection->getQueryBuilder();
$update->update('social_a2_stream_action');
$update->set('stream_id_prim', $update->createNamedParameter(hash('sha512', $data['stream_id'])));
$update->set('actor_id_prim', $update->createNamedParameter(hash('sha512', $data['actor_id'])));
$expr = $update->expr();
$update->where($expr->eq('stream_id', $update->createNamedParameter($data['stream_id'])));
$update->andWhere($expr->eq('actor_id', $update->createNamedParameter($data['actor_id'])));
$update->execute();
}
/**
* @param ISchemaWrapper $schema
*/
private function updateTableStream(ISchemaWrapper $schema) {
if (!$schema->hasTable('social_a2_stream')) {
return;
}
$qb = $this->connection->getQueryBuilder();
$qb->select('id_prim', 'object_id', 'attributed_to', 'object_id_prim')
->from('social_a2_stream');
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$this->updateStreamPrim($data);
}
$cursor->closeCursor();
}
/**
* @param array $data
*/
private function updateStreamPrim(array $data) {
if ($data['object_id_prim'] !== '') {
return;
}
$update = $this->connection->getQueryBuilder();
$update->update('social_a2_stream');
$update->set('object_id_prim', $update->createNamedParameter(hash('sha512', $data['object_id'])));
$update->set(
'attributed_to_prim', $update->createNamedParameter(hash('sha512', $data['attributed_to']))
);
$expr = $update->expr();
$update->where($expr->eq('id_prim', $update->createNamedParameter($data['id_prim'])));
$update->execute();
}
/**
* @param ISchemaWrapper $schema
*/
@ -195,7 +335,7 @@ class Version0002Date20190916000001 extends SimpleMigrationStep {
$limit = 1000;
while (true) {
$qb = $this->connection->getQueryBuilder();
$qb->select('id_prim', 'to_array', 'cc', 'bcc')
$qb->select('id_prim', 'to', 'to_array', 'cc', 'bcc')
->from('social_a2_stream')
->setMaxResults(1000)
->setFirstResult($start);
@ -218,7 +358,7 @@ class Version0002Date20190916000001 extends SimpleMigrationStep {
private function insertStreamDest($data) {
$recipients = [];
$recipients['to'] = json_decode($data['to_array'], true);
$recipients['to'] = array_merge(json_decode($data['to_array'], true), [$data['to']]);
$recipients['cc'] = json_decode($data['cc'], true);
$recipients['bcc'] = json_decode($data['bcc'], true);
@ -226,6 +366,9 @@ class Version0002Date20190916000001 extends SimpleMigrationStep {
foreach (array_keys($recipients) as $dest) {
$type = $dest;
foreach ($recipients[$dest] as $actorId) {
if ($actorId === '') {
continue;
}
$insert = $this->connection->getQueryBuilder();
$insert->insert('social_a2_stream_dest');

Wyświetl plik

@ -0,0 +1,135 @@
<?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\Migration;
use Closure;
use Doctrine\DBAL\Schema\SchemaException;
use Exception;
use OCP\DB\ISchemaWrapper;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
/**
* Class Version0002Date20190916000002
*
* @package OCA\Social\Migration
*/
class Version0002Date20190916000002 extends SimpleMigrationStep {
/** @var IDBConnection */
private $connection;
/**
* @param IDBConnection $connection
*/
public function __construct(IDBConnection $connection) {
$this->connection = $connection;
}
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*
* @return ISchemaWrapper
* @throws SchemaException
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options
): ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->getTable('social_a2_follows');
if (!$table->hasIndex('afoa')) {
$table->addUniqueIndex(['accepted', 'follow_id_prim', 'object_id_prim', 'actor_id_prim'], 'afoa');
}
if (!$table->hasIndex('aoa')) {
$table->addUniqueIndex(['accepted', 'object_id_prim', 'actor_id_prim'], 'aoa');
}
$table = $schema->getTable('social_a2_stream');
if (!$table->hasIndex('ipoha')) {
$table->addUniqueIndex(
['id_prim', 'published_time', 'object_id_prim', 'hidden_on_timeline', 'attributed_to_prim'],
'ipoha'
);
}
if (!$table->hasIndex('id_prim')) {
$table->addUniqueIndex(
['id_prim'],
'id_prim'
);
}
if (!$table->hasIndex('object_id_prim')) {
$table->addUniqueIndex(
['object_id_prim'],
'object_id_prim'
);
}
if (!$table->hasIndex('attributed_to_prim')) {
$table->addUniqueIndex(
['attributed_to_prim'],
'attributed_to_prim'
);
}
$table = $schema->getTable('social_a2_cache_actors');
if (!$table->hasIndex('i')) {
$table->addUniqueIndex(['id_prim'], 'i');
}
$table = $schema->getTable('social_a2_stream_action');
if (!$table->hasIndex('sa')) {
$table->addIndex(['stream_id_prim', 'actor_id_prim'], 'sa');
}
return $schema;
}
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*
* @throws Exception
*/
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
}
}

Wyświetl plik

@ -388,9 +388,7 @@ class Person extends ACore implements JsonSerializable {
*/
public function import(array $data) {
parent::import($data);
$this->setPreferredUsername(
$this->validate(ACore::AS_USERNAME, 'preferredUsername', $data, '')
)
$this->setPreferredUsername($this->validate(ACore::AS_USERNAME, 'preferredUsername', $data, ''))
->setPublicKey($this->get('publicKey.publicKeyPem', $data))
->setSharedInbox($this->validate(ACore::AS_URL, 'endpoints.sharedInbox', $data))
->setName($this->validate(ACore::AS_USERNAME, 'name', $data, ''))
@ -417,10 +415,7 @@ class Person extends ACore implements JsonSerializable {
*/
public function importFromDatabase(array $data) {
parent::importFromDatabase($data);
$this->setPreferredUsername(
$this->validate(self::AS_USERNAME, 'preferred_username', $data, '')
)
$this->setPreferredUsername($this->validate(self::AS_USERNAME, 'preferred_username', $data, ''))
->setUserId($this->get('user_id', $data, ''))
->setName($this->validate(self::AS_USERNAME, 'name', $data, ''))
->setAccount($this->validate(self::AS_ACCOUNT, 'account', $data, ''))

Wyświetl plik

@ -227,6 +227,9 @@ class AccountService {
// generate cache.
$this->cacheLocalActorByUsername($username);
// generate loopback
$this->followsRequest->generateLoopbackAccount($actor);
}

Wyświetl plik

@ -28,10 +28,16 @@ use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TStringTools;
use Exception;
use GuzzleHttp\Exception\ClientException;
use OC\User\NoUserException;
use OCA\Social\Db\CacheActorsRequest;
use OCA\Social\Db\FollowsRequest;
use OCA\Social\Db\StreamDestRequest;
use OCA\Social\Db\StreamRequest;
use OCA\Social\Exceptions\AccountAlreadyExistsException;
use OCA\Social\Exceptions\ActorDoesNotExistException;
use OCA\Social\Exceptions\CacheActorDoesNotExistException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\UrlCloudException;
use OCA\Social\Model\ActivityPub\Object\Follow;
use OCA\Social\Model\ActivityPub\Object\Note;
use OCP\AppFramework\Http;
@ -40,6 +46,7 @@ use OCP\ICache;
use OCP\IConfig;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUserManager;
/**
@ -56,6 +63,8 @@ class CheckService {
const CACHE_PREFIX = 'social_check_';
/** @var IUserManager */
private $userManager;
/** @var ICache */
private $cache;
@ -78,9 +87,15 @@ class CheckService {
/** @var CacheActorsRequest */
private $cacheActorsRequest;
/** @var StreamDestRequest */
private $streamDestRequest;
/** @var StreamRequest */
private $streamRequest;
/** @var AccountService */
private $accountService;
/** @var ConfigService */
private $configService;
@ -91,6 +106,7 @@ class CheckService {
/**
* CheckService constructor.
*
* @param IUserManager $userManager
* @param ICache $cache
* @param IConfig $config
* @param IClientService $clientService
@ -98,17 +114,20 @@ class CheckService {
* @param IURLGenerator $urlGenerator
* @param FollowsRequest $followRequest
* @param CacheActorsRequest $cacheActorsRequest
* @param StreamDestRequest $streamDestRequest
* @param StreamRequest $streamRequest
* @param AccountService $accountService
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
ICache $cache, IConfig $config, IClientService $clientService, IRequest $request,
IURLGenerator $urlGenerator, FollowsRequest $followRequest,
CacheActorsRequest $cacheActorsRequest, StreamRequest $streamRequest,
ConfigService $configService,
IUserManager $userManager, ICache $cache, IConfig $config, IClientService $clientService,
IRequest $request, IURLGenerator $urlGenerator, FollowsRequest $followRequest,
CacheActorsRequest $cacheActorsRequest, StreamDestRequest $streamDestRequest,
StreamRequest $streamRequest, AccountService $accountService, ConfigService $configService,
MiscService $miscService
) {
$this->userManager = $userManager;
$this->cache = $cache;
$this->config = $config;
$this->clientService = $clientService;
@ -116,7 +135,9 @@ class CheckService {
$this->urlGenerator = $urlGenerator;
$this->followRequest = $followRequest;
$this->cacheActorsRequest = $cacheActorsRequest;
$this->streamDestRequest = $streamDestRequest;
$this->streamRequest = $streamRequest;
$this->accountService = $accountService;
$this->configService = $configService;
$this->miscService = $miscService;
}
@ -182,7 +203,6 @@ class CheckService {
$this->configService->setCoreValue('public_host-meta', 'social/lib/hostmeta.php');
$result = [];
if (!$light) {
$result = [
'invalidFollows' => $this->removeInvalidFollows(),
@ -191,6 +211,8 @@ class CheckService {
}
$this->checkStatusTableFollows();
$this->checkStatusTableStreamDest();
$this->checkLocalAccountFollowingItself();
return $result;
}
@ -215,6 +237,41 @@ class CheckService {
}
/**
* create a fake follow entry. Mandatory to have Home Stream working.
*/
public function checkStatusTableStreamDest() {
if ($this->streamDestRequest->countStreamDest() > 0) {
return;
}
$this->streamDestRequest->generateRandomDest();
}
/**
* create entries in follows so that user follows itself.
*
* @throws AccountAlreadyExistsException
* @throws NoUserException
* @throws SocialAppConfigException
* @throws UrlCloudException
*/
public function checkLocalAccountFollowingItself() {
$users = $this->userManager->search('');
foreach ($users as $user) {
try {
$actor = $this->accountService->getActorFromUserId($user->getUID());
} catch (ActorDoesNotExistException $e) {
continue;
}
$this->followRequest->generateLoopbackAccount($actor);
}
}
/**
* @return int
*/