Merge pull request #82 from nextcloud-gmbh/more-for-local-accounts

links between accounts
pull/89/head
Maxence Lange 2018-11-30 10:21:57 -01:00 zatwierdzone przez GitHub
commit 049aeec99b
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
34 zmienionych plików z 647 dodań i 142 usunięć

Wyświetl plik

@ -77,6 +77,13 @@
<primary>true</primary>
</field>
<field>
<name>type</name>
<type>text</type>
<length>31</length>
<notnull>false</notnull>
</field>
<field>
<name>actor_id</name>
<type>text</type>
@ -380,6 +387,13 @@
<notnull>true</notnull>
</field>
<field>
<name>details</name>
<type>text</type>
<length>3000</length>
<notnull>false</notnull>
</field>
<field>
<name>creation</name>
<type>timestamp</type>

Wyświetl plik

@ -5,7 +5,7 @@
<name>Social</name>
<summary>🎉 Nextcloud becomes part of the federated social networks!</summary>
<description><![CDATA[test]]></description>
<version>0.0.52</version>
<version>0.0.60</version>
<licence>agpl</licence>
<author mail="maxence@artificial-owl.com">Maxence Lange</author>
<author mail="jus@bitgrid.net">Julius Härtl</author>

8
composer.lock wygenerowano
Wyświetl plik

@ -12,12 +12,12 @@
"source": {
"type": "git",
"url": "https://github.com/daita/my-small-php-tools.git",
"reference": "39ea0ce3f9442cb507c0bfaa4be36c3e90d6903e"
"reference": "36ea85a58ceb57a521c8f5a43effb4a7330e7b4c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/39ea0ce3f9442cb507c0bfaa4be36c3e90d6903e",
"reference": "39ea0ce3f9442cb507c0bfaa4be36c3e90d6903e",
"url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/36ea85a58ceb57a521c8f5a43effb4a7330e7b4c",
"reference": "36ea85a58ceb57a521c8f5a43effb4a7330e7b4c",
"shasum": ""
},
"require": {
@ -40,7 +40,7 @@
}
],
"description": "My small PHP Tools",
"time": "2018-11-29T12:56:09+00:00"
"time": "2018-11-29T16:46:38+00:00"
}
],
"packages-dev": [],

Wyświetl plik

@ -32,7 +32,6 @@ namespace OCA\Social\AppInfo;
use OCP\AppFramework\App;
use OCP\AppFramework\IAppContainer;
class Application extends App {

Wyświetl plik

@ -105,6 +105,9 @@ class CacheRefresh extends Base {
$result = $this->actorService->manageCacheLocalActors();
$output->writeLn($result . ' local accounts regenerated');
$result = $this->personService->missingCacheRemoteActors();
$output->writeLn($result . ' remote accounts created');
$result = $this->personService->manageCacheRemoteActors();
$output->writeLn($result . ' remote accounts updated');

Wyświetl plik

@ -31,9 +31,7 @@ declare(strict_types=1);
namespace OCA\Social\Command;
use Exception;
use OC\Core\Command\Base;
use OCA\Social\Exceptions\ActorDoesNotExistException;
use OCA\Social\Exceptions\RequestException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Service\ActivityService;

Wyświetl plik

@ -74,9 +74,6 @@ class ActivityPubController extends Controller {
/** @var MiscService */
private $miscService;
/** @var NavigationController */
private $navigationController;
/**
* ActivityPubController constructor.
@ -94,13 +91,11 @@ class ActivityPubController extends Controller {
IRequest $request, SocialPubController $socialPubController,
ActivityService $activityService, ImportService $importService,
FollowService $followService, ActorService $actorService, NotesRequest $notesRequest,
NavigationController $navigationController,
MiscService $miscService
) {
parent::__construct(Application::APP_NAME, $request);
$this->socialPubController = $socialPubController;
$this->navigationController = $navigationController;
$this->activityService = $activityService;
$this->importService = $importService;
@ -128,7 +123,7 @@ class ActivityPubController extends Controller {
*/
public function actor(string $username): Response {
if (!$this->checkSourceActivityStreams()) {
return $this->navigationController->public($username);
return $this->socialPubController->actor($username);
}
try {
@ -258,7 +253,7 @@ class ActivityPubController extends Controller {
*
* @return Response
*/
public function followers(string $username, $data): Response {
public function followers(string $username): Response {
if (!$this->checkSourceActivityStreams()) {
return $this->socialPubController->followers($username);

Wyświetl plik

@ -25,11 +25,8 @@ declare(strict_types=1);
namespace OCA\Social\Controller;
use OCA\Activity\Data;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Service\ConfigService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
@ -46,16 +43,12 @@ class ConfigController extends Controller {
/**
* @param string $cloudAddress
*
* @return DataResponse
*/
public function setCloudAddress(string $cloudAddress): DataResponse {
try {
$this->configService->setCloudAddress($cloudAddress);
return new DataResponse([]);
} catch (SocialAppConfigException $e) {
return new DataResponse([
'message' => $e->getMessage()
], Http::STATUS_BAD_REQUEST);
}
$this->configService->setCloudAddress($cloudAddress);
return new DataResponse([]);
}
}
}

Wyświetl plik

@ -219,6 +219,9 @@ class LocalController extends Controller {
* @NoAdminRequired
* @NoSubAdminRequired
*
* @param int $since
* @param int $limit
*
* @return DataResponse
*/
public function streamDirect(int $since = 0, int $limit = 5): DataResponse {
@ -294,12 +297,22 @@ class LocalController extends Controller {
* @param string $search
*
* @return DataResponse
* @throws Exception
*/
public function accountsSearch(string $search): DataResponse {
try {
$viewer = $this->actorService->getActorFromUserId($this->userId, true);
} catch (Exception $e) {
throw new Exception();
}
$this->personService->setViewerId($viewer->getId());
/* Look for an exactly matching account */
$match = null;
try {
$match = $this->personService->getFromAccount($search, false);
$match->setCompleteDetails(true);
} catch (Exception $e) {
}
@ -410,6 +423,7 @@ class LocalController extends Controller {
* @NoCSRFRequired
* @NoAdminRequired
* @NoSubAdminRequired
*
* @param string $id
* @return DataResponse
*/
@ -419,8 +433,10 @@ class LocalController extends Controller {
if ($actor->gotIcon()) {
$avatar = $actor->getIcon();
$document = $this->documentService->getFromCache($avatar->getId());
return new FileDisplayResponse($document);
}
return new NotFoundResponse();
} catch (Exception $e) {
return $this->fail($e);

Wyświetl plik

@ -30,11 +30,9 @@ declare(strict_types=1);
namespace OCA\Social\Controller;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\Nextcloud\TNCDataResponse;
use daita\MySmallPhpTools\Traits\TArrayTools;
use Exception;
use OC\Files\Node\File;
use OC\Files\SimpleFS\SimpleFile;
use OC\User\NoUserException;
use OCA\Social\AppInfo\Application;
use OCA\Social\Exceptions\AccountAlreadyExistsException;
@ -48,9 +46,7 @@ use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\Template\PublicTemplateResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IConfig;
use OCP\IL10N;
@ -132,9 +128,11 @@ class NavigationController extends Controller {
* @NoAdminRequired
* @NoSubAdminRequired
*
* @param string $path
*
* @return TemplateResponse
*/
public function navigate($path = ''): TemplateResponse {
public function navigate(string $path = ''): TemplateResponse {
$data = [
'serverData' => [
'public' => false,
@ -230,10 +228,11 @@ class NavigationController extends Controller {
* @NoAdminRequired
* @NoSubAdminRequired
*
* @param string $path
*
* @return TemplateResponse
* @throws NoUserException
*/
public function timeline($path = ''): TemplateResponse {
public function timeline(string $path = ''): TemplateResponse {
return $this->navigate();
}
@ -244,43 +243,14 @@ class NavigationController extends Controller {
* @NoAdminRequired
* @NoSubAdminRequired
*
* @param string $path
*
* @return TemplateResponse
* @throws NoUserException
*/
public function account($path = ''): TemplateResponse {
public function account(string $path = ''): TemplateResponse {
return $this->navigate();
}
/**
* @NoCSRFRequired
* @PublicPage
*
* @param $username
*
* @return RedirectResponse|PublicTemplateResponse
*/
public function public($username) {
// Redirect to external instances
if (preg_match('/@[\w._-]+@[\w._-]+/', $username) === 1) {
$actor = $this->personService->getFromAccount(substr($username, 1));
return new RedirectResponse($actor->getUrl());
}
if (\OC::$server->getUserSession()
->isLoggedIn()) {
return $this->navigate();
}
$data = [
'serverData' => [
'public' => true,
]
];
$page = new PublicTemplateResponse(Application::APP_NAME, 'main', $data);
$page->setHeaderTitle($this->l10n->t('Social') . ' ' . $username);
return $page;
}
/**
*

Wyświetl plik

@ -32,12 +32,10 @@ namespace OCA\Social\Controller;
use daita\MySmallPhpTools\Traits\TAsync;
use OCA\Social\AppInfo\Application;
use OCA\Social\Exceptions\ActorDoesNotExistException;
use OCA\Social\Exceptions\RequestException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Model\RequestQueue;
use OCA\Social\Service\ActivityService;
use OCA\Social\Service\CurlService;
use OCA\Social\Service\MiscService;
use OCA\Social\Service\QueueService;
use OCP\AppFramework\Controller;

Wyświetl plik

@ -31,13 +31,19 @@ namespace OCA\Social\Controller;
use daita\MySmallPhpTools\Traits\Nextcloud\TNCDataResponse;
use Exception;
use OCA\Social\AppInfo\Application;
use OCA\Social\Service\ActivityService;
use OCA\Social\Exceptions\CacheActorDoesNotExistException;
use OCA\Social\Model\ActivityPub\Person;
use OCA\Social\Service\ActivityPub\PersonService;
use OCA\Social\Service\ActorService;
use OCA\Social\Service\MiscService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\Template\PublicTemplateResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IL10N;
use OCP\IRequest;
class SocialPubController extends Controller {
@ -45,12 +51,18 @@ class SocialPubController extends Controller {
use TNCDataResponse;
/** @var ActivityService */
private $activityService;
/** @var string */
private $userId;
/** @var IL10N */
private $l10n;
/** @var ActorService */
private $actorService;
/** @var PersonService */
private $personService;
/** @var MiscService */
private $miscService;
@ -58,19 +70,23 @@ class SocialPubController extends Controller {
/**
* SocialPubController constructor.
*
* @param ActivityService $activityService
* @param ActorService $actorService
* @param $userId
* @param IRequest $request
* @param IL10N $l10n
* @param ActorService $actorService
* @param PersonService $personService
* @param MiscService $miscService
*/
public function __construct(
ActivityService $activityService, ActorService $actorService, IRequest $request,
MiscService $miscService
$userId, IRequest $request, IL10N $l10n, ActorService $actorService,
PersonService $personService, MiscService $miscService
) {
parent::__construct(Application::APP_NAME, $request);
$this->activityService = $activityService;
$this->userId = $userId;
$this->l10n = $l10n;
$this->actorService = $actorService;
$this->personService = $personService;
$this->miscService = $miscService;
}
@ -81,14 +97,48 @@ class SocialPubController extends Controller {
*
* @NoCSRFRequired
* @PublicPage
* e*
*
* @param string $username
*
* @return TemplateResponse
* @return Response
*/
public function actor(string $username): TemplateResponse {
return new TemplateResponse(Application::APP_NAME, 'actor', [], 'blank');
public function actor(string $username): Response {
try {
$actor = $this->personService->getFromLocalAccount($username);
$actor->setCompleteDetails(true);
$logged = false;
$ownAccount = false;
if ($this->userId !== null) {
$logged = true;
$local = $this->actorService->getActorFromUserId($this->userId, true);
if ($local->getId() === $actor->getId()) {
$ownAccount = true;
} else {
$this->fillActorWithLinks($actor, $local);
}
}
$data = [
'serverData' => [
'public' => true,
],
'actor' => $actor,
'logged' => $logged,
'ownAccount' => $ownAccount
];
$page = new PublicTemplateResponse(Application::APP_NAME, 'main', $data);
$page->setHeaderTitle($this->l10n->t('Social') . ' ' . $username);
return $page;
} catch (CacheActorDoesNotExistException $e) {
return new NotFoundResponse();
} catch (Exception $e) {
return $this->fail($e);
}
}
@ -139,6 +189,15 @@ class SocialPubController extends Controller {
return $this->success([$username, $postId]);
}
/**
* @param Person $actor
* @param Person $local
*/
private function fillActorWithLinks(Person $actor, Person $local) {
$links = $this->actorService->getLinksBetweenPersons($local, $actor);
$actor->addDetailArray('link', $links);
}
}

Wyświetl plik

@ -31,18 +31,11 @@ declare(strict_types=1);
namespace OCA\Social\Cron;
use Exception;
use OC\BackgroundJob\TimedJob;
use OCA\Social\AppInfo\Application;
use OCA\Social\Exceptions\ActorDoesNotExistException;
use OCA\Social\Exceptions\RequestException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Service\ActivityPub\DocumentService;
use OCA\Social\Service\ActivityPub\PersonService;
use OCA\Social\Service\ActivityService;
use OCA\Social\Service\ActorService;
use OCA\Social\Service\CacheService;
use OCA\Social\Service\ConfigService;
use OCA\Social\Service\MiscService;
use OCA\Social\Service\QueueService;
use OCP\AppFramework\QueryException;

Wyświetl plik

@ -33,6 +33,7 @@ namespace OCA\Social\Db;
use DateTime;
use Exception;
use OCA\Social\Exceptions\CacheActorDoesNotExistException;
use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Model\ActivityPub\Person;
use OCA\Social\Service\ConfigService;
use OCA\Social\Service\MiscService;
@ -85,6 +86,7 @@ class CacheActorsRequest extends CacheActorsRequestBuilder {
->setValue('summary', $qb->createNamedParameter($actor->getSummary()))
->setValue('public_key', $qb->createNamedParameter($actor->getPublicKey()))
->setValue('source', $qb->createNamedParameter($actor->getSource()))
->setValue('details', $qb->createNamedParameter(json_encode($actor->getDetails())))
->setValue(
'creation',
$qb->createNamedParameter(new DateTime('now'), IQueryBuilder::PARAM_DATE)
@ -132,14 +134,61 @@ class CacheActorsRequest extends CacheActorsRequestBuilder {
*
* @param string $account
*
* @param string $viewerId
*
* @return Person
* @throws CacheActorDoesNotExistException
*/
public function getFromAccount(string $account): Person {
public function getFromAccount(string $account, string $viewerId = ''): Person {
$qb = $this->getCacheActorsSelectSql();
$this->limitToAccount($qb, $account);
$this->leftJoinCacheDocuments($qb, 'icon_id');
if ($viewerId !== '') {
$this->leftJoinFollowAsViewer($qb, 'id', $viewerId, true, 'as_follower');
$this->leftJoinFollowAsViewer($qb, 'id', $viewerId, false, 'as_followed');
}
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new CacheActorDoesNotExistException();
}
$account = $this->parseCacheActorsSelectSql($data);
try {
$this->parseFollowLeftJoin($data, 'as_follower');
$account->addDetailBool('follower', true);
} catch (InvalidResourceException $e) {
}
try {
$this->parseFollowLeftJoin($data, 'as_followed');
$account->addDetailBool('following', true);
} catch (InvalidResourceException $e) {
}
return $account;
}
/**
* get Cached version of a local Actor, based on the preferred username
*
* @param string $account
*
* @return Person
* @throws CacheActorDoesNotExistException
*/
public function getFromLocalAccount(string $account): Person {
$qb = $this->getCacheActorsSelectSql();
$this->limitToPreferredUsername($qb, $account);
$this->limitToLocal($qb, true);
$this->leftJoinCacheDocuments($qb, 'icon_id');
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
@ -154,18 +203,42 @@ class CacheActorsRequest extends CacheActorsRequestBuilder {
/**
* @param string $search
* @param string $viewerId
*
* @return Person[]
*/
public function searchAccounts(string $search): array {
public function searchAccounts(string $search, string $viewerId = ''): array {
$qb = $this->getCacheActorsSelectSql();
$this->searchInAccount($qb, $search);
$this->leftJoinCacheDocuments($qb, 'icon_id');
if ($viewerId !== '') {
$this->leftJoinFollowAsViewer($qb, 'id', $viewerId, true, 'as_follower');
$this->leftJoinFollowAsViewer($qb, 'id', $viewerId, false, 'as_followed');
}
$accounts = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$accounts[] = $this->parseCacheActorsSelectSql($data);
$account = $this->parseCacheActorsSelectSql($data);
if ($viewerId !== '') {
try {
$this->parseFollowLeftJoin($data, 'as_follower');
$account->addDetailBool('following', true);
} catch (InvalidResourceException $e) {
}
try {
$this->parseFollowLeftJoin($data, 'as_followed');
$account->addDetailBool('follower', true);
} catch (InvalidResourceException $e) {
}
$account->setCompleteDetails(true);
}
$accounts[] = $account;
}
$cursor->closeCursor();

Wyświetl plik

@ -80,7 +80,7 @@ class CacheActorsRequestBuilder extends CoreRequestBuilder {
'ca.id', 'ca.account', 'ca.following', 'ca.followers', 'ca.inbox',
'ca.shared_inbox', 'ca.outbox', 'ca.featured', 'ca.url', 'ca.type',
'ca.preferred_username', 'ca.name', 'ca.summary',
'ca.public_key', 'ca.local', 'ca.source', 'ca.creation'
'ca.public_key', 'ca.local', 'ca.details', 'ca.source', 'ca.creation'
)
->from(self::TABLE_CACHE_ACTORS, 'ca');

Wyświetl plik

@ -37,6 +37,7 @@ use Doctrine\DBAL\Query\QueryBuilder;
use Exception;
use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Model\ActivityPub\Document;
use OCA\Social\Model\ActivityPub\Follow;
use OCA\Social\Model\ActivityPub\Image;
use OCA\Social\Model\ActivityPub\Person;
use OCA\Social\Service\ConfigService;
@ -134,7 +135,7 @@ class CoreRequestBuilder {
* @param string $username
*/
protected function limitToPreferredUsername(IQueryBuilder &$qb, string $username) {
$this->limitToDBField($qb, 'preferred_username', $username);
$this->limitToDBField($qb, 'preferred_username', $username, false);
}
/**
@ -195,6 +196,18 @@ class CoreRequestBuilder {
}
/**
* Limit the request to the FollowId
*
* @param IQueryBuilder $qb
* @param bool $accepted
*/
protected function limitToAccepted(IQueryBuilder &$qb, bool $accepted) {
$this->limitToDBField($qb, 'accepted', ($accepted) ? '1' : '0');
}
/**
* Limit the request to the ServiceId
*
@ -272,6 +285,17 @@ class CoreRequestBuilder {
}
/**
* Limit the request to the url
*
* @param IQueryBuilder $qb
* @param string $actorId
*/
protected function limitToAttributedTo(IQueryBuilder &$qb, string $actorId) {
$this->limitToDBField($qb, 'attributed_to', $actorId, false);
}
/**
* Limit the request to the status
*
@ -507,7 +531,6 @@ class CoreRequestBuilder {
$expr = $qb->expr();
$pf = $this->defaultSelectAlias;
// /** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->selectAlias('ca.id', 'cacheactor_id')
->selectAlias('ca.type', 'cacheactor_type')
->selectAlias('ca.account', 'cacheactor_account')
@ -569,7 +592,6 @@ class CoreRequestBuilder {
$expr = $qb->expr();
$pf = $this->defaultSelectAlias;
// /** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->selectAlias('cd.id', 'cachedocument_id')
->selectAlias('cd.type', 'cachedocument_type')
->selectAlias('cd.mime_type', 'cachedocument_mime_type')
@ -612,6 +634,75 @@ class CoreRequestBuilder {
return $document;
}
/**
* @param IQueryBuilder $qb
* @param string $fieldActorId
* @param string $viewerId
* @param bool $asFollower
* @param string $prefix
*/
protected function leftJoinFollowAsViewer(
IQueryBuilder &$qb, string $fieldActorId, string $viewerId, bool $asFollower = true,
string $prefix = 'follow'
) {
if ($qb->getType() !== QueryBuilder::SELECT) {
return;
}
$expr = $qb->expr();
$pf = $this->defaultSelectAlias;
$andX = $expr->andX();
if ($asFollower === true) {
$andX->add($expr->eq($pf . '.' . $fieldActorId, $prefix . '_f.object_id'));
$andX->add($expr->eq($prefix . '_f.actor_id', $qb->createNamedParameter($viewerId)));
} else {
$andX->add($expr->eq($pf . '.' . $fieldActorId, $prefix . '_f.actor_id'));
$andX->add($expr->eq($prefix . '_f.object_id', $qb->createNamedParameter($viewerId)));
}
$qb->selectAlias($prefix . '_f.id', $prefix . '_id')
->selectAlias($prefix . '_f.type', $prefix . '_type')
->selectAlias($prefix . '_f.actor_id', $prefix . '_actor_id')
->selectAlias($prefix . '_f.object_id', $prefix . '_object_id')
->selectAlias($prefix . '_f.follow_id', $prefix . '_follow_id')
->selectAlias($prefix . '_f.creation', $prefix . '_creation')
->leftJoin(
$this->defaultSelectAlias, CoreRequestBuilder::TABLE_SERVER_FOLLOWS, $prefix . '_f',
$andX
);
}
/**
* @param array $data
* @param string $prefix
*
* @return Follow
* @throws InvalidResourceException
*/
protected function parseFollowLeftJoin(array $data, string $prefix): Follow {
$new = [];
$length = strlen($prefix) + 1;
foreach ($data as $k => $v) {
if (substr($k, 0, $length) === $prefix . '_') {
$new[substr($k, $length)] = $v;
}
}
$follow = new Follow();
$follow->importFromDatabase($new);
if ($follow->getType() !== Follow::TYPE) {
throw new InvalidResourceException();
}
return $follow;
}
}

Wyświetl plik

@ -31,6 +31,7 @@ declare(strict_types=1);
namespace OCA\Social\Db;
use daita\MySmallPhpTools\Traits\TArrayTools;
use OCA\Social\Exceptions\FollowDoesNotExistException;
use OCA\Social\Model\ActivityPub\Follow;
@ -43,6 +44,9 @@ use OCA\Social\Model\ActivityPub\Follow;
class FollowsRequest extends FollowsRequestBuilder {
use TArrayTools;
/**
* Insert a new Note in the database.
*
@ -52,6 +56,7 @@ class FollowsRequest extends FollowsRequestBuilder {
$qb = $this->getFollowsInsertSql();
$qb->setValue('id', $qb->createNamedParameter($follow->getId()))
->setValue('actor_id', $qb->createNamedParameter($follow->getActorId()))
->setValue('type', $qb->createNamedParameter($follow->getType()))
->setValue('object_id', $qb->createNamedParameter($follow->getObjectId()))
->setValue('follow_id', $qb->createNamedParameter($follow->getFollowId()));
@ -96,6 +101,42 @@ class FollowsRequest extends FollowsRequestBuilder {
}
/**
* @param string $actorId
*
* @return int
*/
public function countFollowers(string $actorId): int {
$qb = $this->countFollowsSelectSql();
$this->limitToObjectId($qb, $actorId);
$this->limitToAccepted($qb, true);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
return $this->getInt('count', $data, 0);
}
/**
* @param string $actorId
*
* @return int
*/
public function countFollowing(string $actorId): int {
$qb = $this->countFollowsSelectSql();
$this->limitToActorId($qb, $actorId);
$this->limitToAccepted($qb, true);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
return $this->getInt('count', $data, 0);
}
/**
* @param string $followId
*

Wyświetl plik

@ -83,7 +83,23 @@ class FollowsRequestBuilder extends CoreRequestBuilder {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select('f.id', 'f.actor_id', 'f.object_id', 'f.follow_id', 'f.creation')
$qb->select('f.id', 'f.type', 'f.actor_id', 'f.object_id', 'f.follow_id', 'f.creation')
->from(self::TABLE_SERVER_FOLLOWS, 'f');
$this->defaultSelectAlias = 'f';
return $qb;
}
/**
* Base of the Sql Select request for Shares
*
* @return IQueryBuilder
*/
protected function countFollowsSelectSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->selectAlias($qb->createFunction('COUNT(*)'), 'count')
->from(self::TABLE_SERVER_FOLLOWS, 'f');
$this->defaultSelectAlias = 'f';
@ -112,10 +128,7 @@ class FollowsRequestBuilder extends CoreRequestBuilder {
*/
protected function parseFollowsSelectSql($data): Follow {
$follow = new Follow();
$follow->setId($data['id'])
->setActorId($data['actor_id'])
->setObjectId($data['object_id']);
$follow->setFollowId($data['follow_id']);
$follow->importFromDatabase($data);
try {
$actor = $this->parseCacheActorsLeftJoin($data);

Wyświetl plik

@ -137,6 +137,23 @@ class NotesRequest extends NotesRequestBuilder {
}
/**
* @param string $actorId
*
* @return int
*/
public function countNotesFromActorId(string $actorId): int {
$qb = $this->countNotesSelectSql();
$this->limitToAttributedTo($qb, $actorId);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
return $this->getInt('count', $data, 0);
}
/**
* @param string $actorId
* @param int $since

Wyświetl plik

@ -92,6 +92,22 @@ class NotesRequestBuilder extends CoreRequestBuilder {
}
/**
* Base of the Sql Select request for Shares
*
* @return IQueryBuilder
*/
protected function countNotesSelectSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->selectAlias($qb->createFunction('COUNT(*)'), 'count')
->from(self::TABLE_SERVER_NOTES, 'sn');
$this->defaultSelectAlias = 'sn';
return $qb;
}
/**
* Base of the Sql Delete request
*

Wyświetl plik

@ -35,7 +35,6 @@ use DateTime;
use Exception;
use OCA\Social\Exceptions\QueueStatusException;
use OCA\Social\Model\RequestQueue;
use OCA\Social\Service\QueueService;
use OCP\DB\QueryBuilder\IQueryBuilder;

Wyświetl plik

@ -872,7 +872,6 @@ abstract class ACore implements JsonSerializable {
$this->setPublished($this->get('published', $data, ''));
$this->setActorId($this->get('actor', $data, ''));
$this->setObjectId($this->get('object', $data, ''));
$this->setLocal(($this->getInt('local', $data, 0) === 1));
}

Wyświetl plik

@ -88,6 +88,16 @@ class Follow extends ACore implements JsonSerializable {
}
/**
* @param array $data
*/
public function importFromDatabase(array $data) {
parent::importFromDatabase($data);
$this->setFollowId($this->get('follow_id', $data, ''));
}
/**
* @return array
*/

Wyświetl plik

@ -85,6 +85,10 @@ class Person extends ACore implements JsonSerializable {
/** @var string */
private $featured = '';
/** @var array */
private $details = [];
/**
* Person constructor.
*
@ -339,6 +343,73 @@ class Person extends ACore implements JsonSerializable {
}
/**
* @return array
*/
public function getDetails(): array {
return $this->details;
}
/**
* @param string $detail
* @param string $value
*
* @return Person
*/
public function addDetail(string $detail, string $value): Person {
$this->details[$detail] = $value;
return $this;
}
/**
* @param string $detail
* @param int $value
*
* @return Person
*/
public function addDetailInt(string $detail, int $value): Person {
$this->details[$detail] = $value;
return $this;
}
/**
* @param string $detail
* @param array $value
*
* @return Person
*/
public function addDetailArray(string $detail, array $value): Person {
$this->details[$detail] = $value;
return $this;
}
/**
* @param string $detail
* @param bool $value
*
* @return Person
*/
public function addDetailBool(string $detail, bool $value): Person {
$this->details[$detail] = $value;
return $this;
}
/**
* @param array $details
*
* @return Person
*/
public function setDetails(array $details): Person {
$this->details = $details;
return $this;
}
/**
* @param array $data
*
@ -365,12 +436,6 @@ class Person extends ACore implements JsonSerializable {
$this->setIcon($icon);
}
// ->setCreation($this->getInt('creation', $data, 0));
// if ($this->getPreferredUsername() === '') {
// $this->setType('Invalid');
// }
}
@ -390,11 +455,8 @@ class Person extends ACore implements JsonSerializable {
->setFollowing($this->get('following', $data, ''))
->setSharedInbox($this->get('shared_inbox', $data, ''))
->setFeatured($this->get('featured', $data, ''))
->setDetails($this->getArray('details', $data, []))
->setCreation($this->getInt('creation', $data, 0));
// if ($this->getPreferredUsername() === '') {
// $this->setType('Invalid');
// }
}
@ -402,7 +464,7 @@ class Person extends ACore implements JsonSerializable {
* @return array
*/
public function jsonSerialize(): array {
return array_merge(
$result = array_merge(
parent::jsonSerialize(),
[
'aliases' => [
@ -425,8 +487,12 @@ class Person extends ACore implements JsonSerializable {
]
]
);
if ($this->isCompleteDetails()) {
$result['details'] = $this->getDetails();
}
return $result;
}
}

Wyświetl plik

@ -336,9 +336,12 @@ class NoteService implements ICoreService {
/**
* @param Person $actor
*
* @param int $since
* @param int $limit
*
* @return Note[]
*/
public function getDirectNotesForActor(Person $actor, $since, $limit): array {
public function getDirectNotesForActor(Person $actor, int $since = 0, int $limit = 5): array {
return $this->notesRequest->getDirectNotesForActorId($actor->getId(), $since, $limit);
}

Wyświetl plik

@ -76,6 +76,10 @@ class PersonService implements ICoreService {
private $miscService;
/** @var string */
private $viewerId = '';
/**
* UndoService constructor.
*
@ -97,6 +101,18 @@ class PersonService implements ICoreService {
}
/**
* @param string $viewerId
*/
public function setViewerId(string $viewerId) {
$this->viewerId = $viewerId;
}
public function getViewerId(): string {
return $this->viewerId;
}
/**
* @param Person $actor
* @param bool $refresh
@ -169,7 +185,7 @@ class PersonService implements ICoreService {
public function getFromAccount(string $account, bool $retrieve = true): Person {
try {
$actor = $this->cacheActorsRequest->getFromAccount($account);
$actor = $this->cacheActorsRequest->getFromAccount($account, $this->getViewerId());
} catch (CacheActorDoesNotExistException $e) {
if (!$retrieve) {
throw new CacheActorDoesNotExistException();
@ -189,6 +205,17 @@ class PersonService implements ICoreService {
}
/**
* @param string $account
*
* @return Person
* @throws CacheActorDoesNotExistException
*/
public function getFromLocalAccount(string $account): Person {
return $this->cacheActorsRequest->getFromLocalAccount($account);
}
/**
* @param array $object
*
@ -208,18 +235,7 @@ class PersonService implements ICoreService {
}
$actor->setSource(json_encode($object, JSON_UNESCAPED_SLASHES));
// $actor->setPreferredUsername($this->get('preferredUsername', $object, ''));
// $actor->setPublicKey($this->get('publicKey.publicKeyPem', $object));
// $actor->setSharedInbox($this->get('endpoints.sharedInbox', $object));
// $actor->setAccount($actor->getPreferredUsername() . '@' . $this->get('_host', $object));
//
// $icon = new Image($actor);
// $icon->setUrlCloud($this->configService->getCloudAddress());
// $icon->import($this->getArray('icon', $object, []));
// if ($icon->getType() === Image::TYPE) {
// $actor->setIcon($icon);
// }
//
return $actor;
}
@ -229,7 +245,7 @@ class PersonService implements ICoreService {
* @return Person[]
*/
public function searchCachedAccounts(string $search): array {
return $this->cacheActorsRequest->searchAccounts($search);
return $this->cacheActorsRequest->searchAccounts($search, $this->getViewerId());
}
@ -260,6 +276,25 @@ class PersonService implements ICoreService {
}
/**
* @throws Exception
* @return int
*/
public function missingCacheRemoteActors(): int {
// TODO - looking for missing cache remote actors...
$missing = [];
foreach ($missing as $item) {
try {
$this->getFromId($item->getId());
} catch (Exception $e) {
}
}
return sizeof($missing);
}
/**
* @throws Exception
* @return int

Wyświetl plik

@ -284,8 +284,12 @@ class ActivityService {
);
} catch (ActorDoesNotExistException $e) {
$this->queueService->deleteRequest($queue);
return;
} catch (Request410Exception $e) {
$this->queueService->deleteRequest($queue);
return;
}
try {

Wyświetl plik

@ -34,8 +34,11 @@ use daita\MySmallPhpTools\Traits\TArrayTools;
use Exception;
use OC\User\NoUserException;
use OCA\Social\Db\ActorsRequest;
use OCA\Social\Db\FollowsRequest;
use OCA\Social\Db\NotesRequest;
use OCA\Social\Exceptions\AccountAlreadyExistsException;
use OCA\Social\Exceptions\ActorDoesNotExistException;
use OCA\Social\Exceptions\FollowDoesNotExistException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Model\ActivityPub\Person;
use OCA\Social\Service\ActivityPub\PersonService;
@ -55,6 +58,12 @@ class ActorService {
/** @var ActorsRequest */
private $actorsRequest;
/** @var FollowsRequest */
private $followsRequest;
/** @var NotesRequest */
private $notesRequest;
/** @var PersonService */
private $personService;
@ -69,15 +78,19 @@ class ActorService {
* ActorService constructor.
*
* @param ActorsRequest $actorsRequest
* @param FollowsRequest $followsRequest
* @param NotesRequest $notesRequest
* @param PersonService $personService
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
ActorsRequest $actorsRequest, PersonService $personService, ConfigService $configService,
MiscService $miscService
ActorsRequest $actorsRequest, FollowsRequest $followsRequest, NotesRequest $notesRequest,
PersonService $personService, ConfigService $configService, MiscService $miscService
) {
$this->actorsRequest = $actorsRequest;
$this->followsRequest = $followsRequest;
$this->notesRequest = $notesRequest;
$this->personService = $personService;
$this->configService = $configService;
$this->miscService = $miscService;
@ -114,15 +127,26 @@ class ActorService {
/**
* @param string $userId
* @param bool $create
*
* @return Person
* @throws AccountAlreadyExistsException
* @throws ActorDoesNotExistException
* @throws NoUserException
* @throws SocialAppConfigException
*/
public function getActorFromUserId(string $userId): Person {
public function getActorFromUserId(string $userId, bool $create = false): Person {
$this->miscService->confirmUserId($userId);
$actor = $this->actorsRequest->getFromUserId($userId);
try {
$actor = $this->actorsRequest->getFromUserId($userId);
} catch (ActorDoesNotExistException $e) {
if ($create) {
$this->createActor($userId, $userId);
$actor = $this->actorsRequest->getFromUserId($userId);
} else {
throw new ActorDoesNotExistException();
}
}
return $actor;
}
@ -177,6 +201,35 @@ class ActorService {
}
/**
* @param Person $local
* @param Person $actor
*
* @return array
*/
public function getLinksBetweenPersons(Person $local, Person $actor): array {
$links = [
'follower' => false,
'following' => false
];
try {
$this->followsRequest->getByPersons($local->getId(), $actor->getId());
$links['following'] = true;
} catch (FollowDoesNotExistException $e) {
}
try {
$this->followsRequest->getByPersons($actor->getId(), $local->getId());
$links['follower'] = true;
} catch (FollowDoesNotExistException $e) {
}
return $links;
}
/**
* @param string $username
* @param bool $refresh
@ -185,7 +238,14 @@ class ActorService {
*/
public function cacheLocalActorByUsername(string $username, bool $refresh = false) {
try {
$actor = $this->getActor($username);
$actor = $this->getActor($username);;
$count = [
'followers', $this->followsRequest->countFollowers($actor->getId()),
'following', $this->followsRequest->countFollowing($actor->getId()),
'post', $this->notesRequest->countNotesFromActorId($actor->getId())
];
$actor->addDetailArray('count', $count);
$this->personService->cacheLocalActor($actor, $refresh);
} catch (ActorDoesNotExistException $e) {
}

Wyświetl plik

@ -237,8 +237,6 @@ class ConfigService {
/**
* @param string $cloudAddress
*
* @return string
*/
public function setCloudAddress(string $cloudAddress) {
$this->setAppValue(self::SOCIAL_ADDRESS, $cloudAddress);

Wyświetl plik

@ -249,8 +249,6 @@ class CurlService {
/**
* @param int $code
*
* @param Request $request
*
* @throws Request410Exception
*/
private function parseRequestResultCode410(int $code) {

Wyświetl plik

@ -31,7 +31,6 @@ declare(strict_types=1);
namespace OCA\Social\Service;
use Exception;
use OC\User\NoUserException;
use OCA\Social\AppInfo\Application;
use OCP\ILogger;

Wyświetl plik

@ -33,7 +33,6 @@ namespace OCA\Social\Service;
use Exception;
use OC\User\NoUserException;
use OCA\Social\Exceptions\ActorDoesNotExistException;
use OCA\Social\Exceptions\RequestException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\Post;

Wyświetl plik

@ -78,6 +78,7 @@ class QueueService {
/**
* @param array $instancePaths
* @param ACore $item
* @param string $author
*
* @return string
* @throws Exception

Wyświetl plik

@ -0,0 +1,45 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"sensitive": "as:sensitive",
"movedTo": {
"@id": "as:movedTo",
"@type": "@id"
},
"Hashtag": "as:Hashtag",
"ostatus": "http://ostatus.org#",
"atomUri": "ostatus:atomUri",
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"conversation": "ostatus:conversation",
"toot": "http://joinmastodon.org/ns#",
"Emoji": "toot:Emoji",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
},
"featured": {
"@id": "toot:featured",
"@type": "@id"
},
"schema": "http://schema.org#",
"PropertyValue": "schema:PropertyValue",
"value": "schema:value"
}
],
"id": "https://mastodon.social/users/twospirit#delete",
"type": "Delete",
"actor": "https://mastodon.social/users/twospirit",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"object": "https://mastodon.social/users/twospirit",
"signature": {
"type": "RsaSignature2017",
"creator": "https://mastodon.social/users/twospirit#main-key",
"created": "2018-11-26T14:33:30Z",
"signatureValue": "e20O0AYwAGLgHyb/xD1fO+v7gnE8aONFR3iMb/4ULd+Qjz4fV1/ay0IRFdatWGRP4uG/XzjdW6hJTR69wjQUag9k0JOvHkOssUHIXkmBEsKZURyEVWrjT1+Dyx1ZFwsbhjSXCkvbz70mq+1JPx2zDK+fTsG8TPIKBpvj9LYmQbF3Z4n7wRRxibCCL8oGlrHlwWwABYkZKoruLYJya9a6eVvbCD1P5TO+M1CUdMHhqez8k3ll50ZsGRlHqnojR0V9AjMixuMNMx4qvcC+wwJb4QBorfPJDh843Pw4lybwbs5/bXwErxR+Infc61w/dkyw1MvZdPYD2dy+uOHdndRRdQ=="
}
}