diff --git a/appinfo/database.xml b/appinfo/database.xml index 7c99fbc5..9bb2db8a 100644 --- a/appinfo/database.xml +++ b/appinfo/database.xml @@ -234,6 +234,13 @@ true + + local + text + 7 + true + + following text diff --git a/appinfo/info.xml b/appinfo/info.xml index 0563c624..0e284287 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -5,7 +5,7 @@ Social 🎉 Nextcloud becomes part of the federated social networks! - 0.0.28 + 0.0.29 agpl Maxence Lange Julius Härtl diff --git a/appinfo/routes.php b/appinfo/routes.php index f8cce095..307f59c0 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -10,6 +10,8 @@ return [ 'routes' => [ ['name' => 'Navigation#navigate', 'url' => '/', 'verb' => 'GET'], + ['name' => 'Navigation#test', 'url' => '/test', 'verb' => 'GET'], + ['name' => 'Navigation#timeline', 'url' => '/timeline/{path}', 'verb' => 'GET', 'requirements' => ['path' => '.+'], 'defaults' => ['path' => '']], ['name' => 'Navigation#account', 'url' => '/account/{path}', 'verb' => 'GET', 'requirements' => ['path' => '.+'], 'defaults' => ['path' => '']], // ['name' => 'Navigation#public', 'url' => '/{username}', 'verb' => 'GET'], diff --git a/lib/Controller/LocalController.php b/lib/Controller/LocalController.php index f91504f9..2510c028 100644 --- a/lib/Controller/LocalController.php +++ b/lib/Controller/LocalController.php @@ -191,12 +191,7 @@ class LocalController extends Controller { */ public function accountSearch(string $search): DataResponse { try { - $local = $this->actorService->searchLocalAccounts($search); - $remote = $this->personService->searchCachedAccounts($search); - $accounts = [ - 'local' => $local, - 'remote' => $remote - ]; + $accounts = $this->personService->searchCachedAccounts($search); return $this->success(['accounts' => $accounts]); } catch (Exception $e) { diff --git a/lib/Controller/NavigationController.php b/lib/Controller/NavigationController.php index 76b84a47..b1cc0782 100644 --- a/lib/Controller/NavigationController.php +++ b/lib/Controller/NavigationController.php @@ -31,13 +31,16 @@ namespace OCA\Social\Controller; use daita\MySmallPhpTools\Traits\TArrayTools; -use OC\Accounts\AccountManager; +use daita\MySmallPhpTools\Traits\TNCDataResponse; use OC\User\NoUserException; use OCA\Social\AppInfo\Application; use OCA\Social\Exceptions\AccountAlreadyExistsException; +use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Service\ActorService; +use OCA\Social\Service\ConfigService; use OCA\Social\Service\MiscService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\Template\PublicTemplateResponse; use OCP\AppFramework\Http\TemplateResponse; @@ -50,6 +53,8 @@ class NavigationController extends Controller { use TArrayTools; + use TNCDataResponse; + /** @var string */ private $userId; @@ -63,6 +68,9 @@ class NavigationController extends Controller { /** @var ActorService */ private $actorService; + /** @var ConfigService */ + private $configService; + /** @var MiscService */ private $miscService; @@ -77,11 +85,14 @@ class NavigationController extends Controller { * @param IConfig $config * @param IURLGenerator $urlGenerator * @param ActorService $actorService + * @param ConfigService $configService * @param MiscService $miscService + * @param IL10N $l10n */ public function __construct( IRequest $request, $userId, IConfig $config, IURLGenerator $urlGenerator, - ActorService $actorService, MiscService $miscService, IL10N $l10n + ActorService $actorService, ConfigService $configService, MiscService $miscService, + IL10N $l10n ) { parent::__construct(Application::APP_NAME, $request); @@ -90,6 +101,7 @@ class NavigationController extends Controller { $this->urlGenerator = $urlGenerator; $this->actorService = $actorService; + $this->configService = $configService; $this->miscService = $miscService; $this->l10n = $l10n; } @@ -108,11 +120,18 @@ class NavigationController extends Controller { public function navigate($path = ''): TemplateResponse { $data = [ 'serverData' => [ - 'public' => false, + 'public' => false, 'firstrun' => false, + 'setup' => false ] ]; + try { + $this->configService->getCloudAddress(); + $data['serverData']['setup'] = true; + } catch (SocialAppConfigException $e) { + } + try { $this->actorService->createActor($this->userId, $this->userId); $data['serverData']['firstrun'] = true; @@ -123,6 +142,35 @@ class NavigationController extends Controller { return new TemplateResponse(Application::APP_NAME, 'main', $data); } + + /** + * Display the navigation page of the Social app. + * + * @NoCSRFRequired + * @PublicPage + * @NoAdminRequired + * @NoSubAdminRequired + * + * @return DataResponse + */ + public function test(): DataResponse { + + $setup = false; + try { + $address = $this->configService->getCloudAddress(true); + $setup = true; + } catch (SocialAppConfigException $e) { + } + + return $this->success( + [ + 'version' => $this->configService->getAppValue('installed_version'), + 'setup' => $setup + ] + ); + } + + /** * Display the navigation page of the Social app. * @@ -158,6 +206,7 @@ class NavigationController extends Controller { * @param $username * * @return RedirectResponse|PublicTemplateResponse + * @throws NoUserException */ public function public($username) { if (\OC::$server->getUserSession() diff --git a/lib/Db/ActorsRequest.php b/lib/Db/ActorsRequest.php index e33c43c6..eca61466 100644 --- a/lib/Db/ActorsRequest.php +++ b/lib/Db/ActorsRequest.php @@ -31,6 +31,7 @@ namespace OCA\Social\Db; use OCA\Social\Exceptions\ActorDoesNotExistException; +use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Model\ActivityPub\Person; use OCA\Social\Service\ConfigService; use OCA\Social\Service\MiscService; @@ -95,6 +96,7 @@ class ActorsRequest extends ActorsRequestBuilder { * * @return Person * @throws ActorDoesNotExistException + * @throws SocialAppConfigException */ public function getFromUsername(string $username): Person { $qb = $this->getActorsSelectSql(); @@ -116,6 +118,7 @@ class ActorsRequest extends ActorsRequestBuilder { * * @return Person * @throws ActorDoesNotExistException + * @throws SocialAppConfigException */ public function getFromId(string $id): Person { $qb = $this->getActorsSelectSql(); @@ -140,6 +143,7 @@ class ActorsRequest extends ActorsRequestBuilder { * * @return Person * @throws ActorDoesNotExistException + * @throws SocialAppConfigException */ public function getFromUserId(string $userId): Person { $qb = $this->getActorsSelectSql(); @@ -161,6 +165,7 @@ class ActorsRequest extends ActorsRequestBuilder { * @param string $search * * @return Person[] + * @throws SocialAppConfigException */ public function searchFromUsername(string $search): array { $qb = $this->getActorsSelectSql(); diff --git a/lib/Db/ActorsRequestBuilder.php b/lib/Db/ActorsRequestBuilder.php index 9172681b..613eb074 100644 --- a/lib/Db/ActorsRequestBuilder.php +++ b/lib/Db/ActorsRequestBuilder.php @@ -31,6 +31,7 @@ namespace OCA\Social\Db; use daita\MySmallPhpTools\Traits\TArrayTools; +use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Model\ActivityPub\Person; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -105,6 +106,7 @@ class ActorsRequestBuilder extends CoreRequestBuilder { * @param array $data * * @return Person + * @throws SocialAppConfigException */ protected function parseActorsSelectSql($data): Person { $root = $this->configService->getRoot(); @@ -116,6 +118,10 @@ class ActorsRequestBuilder extends CoreRequestBuilder { ->setFollowers($actor->getId() . '/followers') ->setFollowing($actor->getId() . '/following') ->setSharedInbox($root . 'inbox'); + $actor->setAccount( + $actor->getPreferredUsername() . '@' . $this->configService->getCloudAddress(true) + ); + $actor->setUrl($actor->getId()); return $actor; } diff --git a/lib/Db/CacheActorsRequest.php b/lib/Db/CacheActorsRequest.php index 4e0ef561..4cefb756 100644 --- a/lib/Db/CacheActorsRequest.php +++ b/lib/Db/CacheActorsRequest.php @@ -58,16 +58,18 @@ class CacheActorsRequest extends CacheActorsRequestBuilder { * insert cache about an Actor in database. * * @param Person $actor + * @param bool $local * * @return int * @throws \Exception */ - public function save(Person $actor): int { + public function save(Person $actor, bool $local = false): int { try { $qb = $this->getCacheActorsInsertSql(); $qb->setValue('id', $qb->createNamedParameter($actor->getId())) ->setValue('account', $qb->createNamedParameter($actor->getAccount())) + ->setValue('local', $qb->createNamedParameter(($local) ? '1' : '0')) ->setValue('following', $qb->createNamedParameter($actor->getFollowing())) ->setValue('followers', $qb->createNamedParameter($actor->getFollowers())) ->setValue('inbox', $qb->createNamedParameter($actor->getInbox())) diff --git a/lib/Db/CacheActorsRequestBuilder.php b/lib/Db/CacheActorsRequestBuilder.php index 69fe2e47..a03d26f6 100644 --- a/lib/Db/CacheActorsRequestBuilder.php +++ b/lib/Db/CacheActorsRequestBuilder.php @@ -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.preferred_username', 'ca.name', 'ca.summary', - 'ca.public_key', 'ca.creation' + 'ca.public_key', 'ca.local', 'ca.creation' ) ->from(self::TABLE_CACHE_ACTORS, 'ca'); diff --git a/lib/Db/FollowsRequest.php b/lib/Db/FollowsRequest.php index 994fd2ed..8b3ee499 100644 --- a/lib/Db/FollowsRequest.php +++ b/lib/Db/FollowsRequest.php @@ -69,11 +69,8 @@ class FollowsRequest extends FollowsRequestBuilder { /** - * Insert a new Note in the database. - * * @param Follow $follow * - * @return int * @throws Exception */ public function delete(Follow $follow) { diff --git a/lib/Exceptions/SocialAppConfigException.php b/lib/Exceptions/SocialAppConfigException.php new file mode 100644 index 00000000..df72da13 --- /dev/null +++ b/lib/Exceptions/SocialAppConfigException.php @@ -0,0 +1,8 @@ +local; + } + + /** + * @param bool $local + */ + public function setLocal(bool $local) { + $this->local = $local; + } + + + + + + /** * @param array $data */ @@ -352,6 +373,7 @@ class Person extends ACore implements JsonSerializable { ->setFollowing($this->get('following', $data, '')) ->setSharedInbox($this->get('shared_inbox', $data, '')) ->setFeatured($this->get('featured', $data, '')) + ->setLocal(($this->get('local', $data, '') === '1')) ->setCreation($this->getInt('creation', $data, 0)); if ($this->getPreferredUsername() === '') { @@ -384,7 +406,8 @@ class Person extends ACore implements JsonSerializable { 'id' => $this->getId() . '#main-key', 'owner' => $this->getId(), 'publicKeyPem' => $this->getPublicKey() - ] + ], + 'local' => $this->isLocal() ] ); } diff --git a/lib/Service/ActivityPub/NoteService.php b/lib/Service/ActivityPub/NoteService.php index 12502e72..30d9e9d7 100644 --- a/lib/Service/ActivityPub/NoteService.php +++ b/lib/Service/ActivityPub/NoteService.php @@ -35,6 +35,7 @@ use OC\User\NoUserException; use OCA\Social\Db\NotesRequest; 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\ActivityPub\Note; use OCA\Social\Model\ActivityPub\Person; @@ -106,6 +107,7 @@ class NoteService implements ICoreService { * @return Note * @throws ActorDoesNotExistException * @throws NoUserException + * @throws SocialAppConfigException */ public function generateNote(string $userId, string $content, string $type) { $note = new Note(); diff --git a/lib/Service/ActivityPub/PersonService.php b/lib/Service/ActivityPub/PersonService.php index 2c8f2803..665a7211 100644 --- a/lib/Service/ActivityPub/PersonService.php +++ b/lib/Service/ActivityPub/PersonService.php @@ -82,6 +82,22 @@ class PersonService implements ICoreService { } + /** + * @param Person $actor + * @param bool $refresh + */ + public function cacheLocalActor(Person $actor, bool $refresh = false) { + if ($refresh) { + $this->cacheActorsRequest->deleteFromId($actor->getId()); + } + + try { + $this->save($actor, true); + } catch (Exception $e) { + } + } + + /** * @param string $id * @@ -150,7 +166,7 @@ class PersonService implements ICoreService { $actor->setPreferredUsername($this->get('preferredUsername', $object, '')); $actor->setPublicKey($this->get('publicKey.publicKeyPem', $object)); $actor->setSharedInbox($this->get('endpoints.sharedInbox', $object)); - + if ($actor->getType() !== 'Person') { throw new InvalidResourceException(); } @@ -180,12 +196,13 @@ class PersonService implements ICoreService { * This method is called when saving the Follow object * * @param ACore $person + * @param bool $local * * @throws Exception */ - public function save(ACore $person) { + public function save(ACore $person, bool $local = false) { /** @var Person $person */ - $this->cacheActorsRequest->save($person); + $this->cacheActorsRequest->save($person, $local); } } diff --git a/lib/Service/ActivityService.php b/lib/Service/ActivityService.php index 1564065f..4ca380e5 100644 --- a/lib/Service/ActivityService.php +++ b/lib/Service/ActivityService.php @@ -37,7 +37,9 @@ use Exception; use OC\User\NoUserException; use OCA\Social\Db\ActorsRequest; use OCA\Social\Exceptions\ActorDoesNotExistException; +use OCA\Social\Exceptions\InvalidResourceException; use OCA\Social\Exceptions\RequestException; +use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Exceptions\UnknownItemException; use OCA\Social\Model\ActivityPub\ACore; use OCA\Social\Model\ActivityPub\Activity; @@ -178,8 +180,11 @@ class ActivityService { /** * @param ACore $activity * + * @param int $type + * * @return array * @throws RequestException + * @throws SocialAppConfigException */ public function request(ACore $activity, int $type) { @@ -199,20 +204,18 @@ class ActivityService { /** * @param string $address * @param InstancePath $path + * @param int $type * @param ACore $activity * * @return Request[] * @throws RequestException + * @throws SocialAppConfigException */ public function generateRequest(string $address, InstancePath $path, int $type, ACore $activity ): array { $document = json_encode($activity); $date = gmdate(self::DATE_FORMAT); -//$date = str_replace('UTC', 'GMT', $date); $localActor = $activity->getActor(); -// $remoteActor = $this->getRemoteActor($path->getUri()); - -// $remotePath = $this->personService->getPathFromActor($remoteActor, $path->getType()); $localActorLink = $this->configService->getRoot() . '@' . $localActor->getPreferredUsername(); @@ -392,14 +395,11 @@ class ActivityService { * * @return string * @throws RequestException + * @throws InvalidResourceException */ private function retrieveKey($keyId): string { - //check cache here - $actor = $this->personService->getFromId($keyId); -// $actor = $this->instanceService->retrieveObject($keyId); - return $actor->getPublicKey(); } diff --git a/lib/Service/ActorService.php b/lib/Service/ActorService.php index 74f06e7a..25758d89 100644 --- a/lib/Service/ActorService.php +++ b/lib/Service/ActorService.php @@ -36,8 +36,8 @@ use OC\User\NoUserException; use OCA\Social\Db\ActorsRequest; use OCA\Social\Exceptions\AccountAlreadyExistsException; use OCA\Social\Exceptions\ActorDoesNotExistException; +use OCA\Social\Exceptions\SocialAppConfigException; use OCA\Social\Model\ActivityPub\Person; -use OCA\Social\Model\InstancePath; use OCA\Social\Service\ActivityPub\PersonService; @@ -89,6 +89,7 @@ class ActorService { * * @return Person * @throws ActorDoesNotExistException + * @throws SocialAppConfigException */ public function getActor(string $username): Person { @@ -102,6 +103,7 @@ class ActorService { * * @return Person * @throws ActorDoesNotExistException + * @throws SocialAppConfigException */ public function getActorById(string $id): Person { $actor = $this->actorsRequest->getFromId($id); @@ -116,6 +118,7 @@ class ActorService { * @return Person * @throws ActorDoesNotExistException * @throws NoUserException + * @throws SocialAppConfigException */ public function getActorFromUserId(string $userId): Person { $this->miscService->confirmUserId($userId); @@ -128,7 +131,9 @@ class ActorService { /** * @param string $search * + * @deprecated - used !? * @return Person[] + * @throws SocialAppConfigException */ public function searchLocalAccounts(string $search): array { return $this->actorsRequest->searchFromUsername($search); @@ -177,10 +182,25 @@ class ActorService { $actor->setPreferredUsername($username); $this->generateKeys($actor); - $id = $this->actorsRequest->create($actor); + $this->actorsRequest->create($actor); // generate cache. - $this->personService->getFromId($id, true); + $this->cacheLocalActorByUsername($username, true); + } + + + /** + * @param string $username + * @param bool $refresh + * + * @throws SocialAppConfigException + */ + public function cacheLocalActorByUsername(string $username, bool $refresh = false) { + try { + $actor = $this->getActor($username); + $this->personService->cacheLocalActor($actor, $refresh); + } catch (ActorDoesNotExistException $e) { + } } diff --git a/lib/Service/ConfigService.php b/lib/Service/ConfigService.php index 86bae751..5316da7f 100644 --- a/lib/Service/ConfigService.php +++ b/lib/Service/ConfigService.php @@ -29,8 +29,10 @@ declare(strict_types=1); namespace OCA\Social\Service; +use daita\MySmallPhpTools\Traits\TArrayTools; use daita\MySmallPhpTools\Traits\TPathTools; use OCA\Social\AppInfo\Application; +use OCA\Social\Exceptions\SocialAppConfigException; use OCP\IConfig; use OCP\IRequest; use OCP\IURLGenerator; @@ -46,10 +48,14 @@ class ConfigService { use TPathTools; + use TArrayTools; + const SOCIAL_ADDRESS = 'address'; + /** @var array */ public $defaults = [ + self::SOCIAL_ADDRESS => '' ]; /** @var string */ @@ -204,19 +210,40 @@ class ConfigService { return $this->config->getSystemValue($key, ''); } - public function getCloudAddress() { - return $this->request->getServerHost(); + + /** + * @param bool $host + * + * @return string + * @throws SocialAppConfigException + */ + public function getCloudAddress(bool $host = false) { + $address = $this->getAppValue(self::SOCIAL_ADDRESS); + if ($address === '') { + throw new SocialAppConfigException(); + } + + if ($host === true) { + $parsed = parse_url($address); + $result = $this->get('host', $parsed, ''); + $port = $this->get('port', $parsed, ''); +// if ($port !== '') { +// $result .= ':' . $port; +// } + + return $result; + } + + return $address; } /** * @return string - * // TODO: improve this ! + * @throws SocialAppConfigException */ public function getRoot(): string { -// $this->urlGenerator->linkToRoute('social.Navigation.navigate'); - return $this->withoutEndSlash($this->getSystemValue('overwrite.cli.url'), false, false) - . '/apps/social/'; + return $this->withoutEndSlash($this->getCloudAddress(), false, false) . '/apps/social/'; } @@ -225,6 +252,7 @@ class ConfigService { * @param bool $generateId * * @return string + * @throws SocialAppConfigException */ public function generateId(string $path = '', $generateId = true): string { $path = $this->withoutBeginSlash($this->withEndSlash($path));