proof of concept

Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
pull/985/head
Maxence Lange 2020-08-25 02:06:43 -01:00
rodzic db9a804de0
commit e986ca6a4f
20 zmienionych plików z 1402 dodań i 14 usunięć

Wyświetl plik

@ -52,6 +52,7 @@ return [
['name' => 'ActivityPub#actor', 'url' => '/users/{username}', 'verb' => 'GET'],
['name' => 'ActivityPub#actorAlias', 'url' => '/@{username}/', 'verb' => 'GET'],
['name' => 'ActivityPub#inbox', 'url' => '/@{username}/inbox', 'verb' => 'POST'],
['name' => 'ActivityPub#getInbox', 'url' => '/@{username}/inbox', 'verb' => 'GET'],
['name' => 'ActivityPub#sharedInbox', 'url' => '/inbox', 'verb' => 'POST'],
['name' => 'ActivityPub#outbox', 'url' => '/@{username}/outbox', 'verb' => 'GET'],
@ -65,6 +66,17 @@ return [
['name' => 'OStatus#followRemote', 'url' => '/api/v1/ostatus/followRemote/{local}', 'verb' => 'GET'],
['name' => 'OStatus#getLink', 'url' => '/api/v1/ostatus/link/{local}/{account}', 'verb' => 'GET'],
// OAuth
['name' => 'ActivityStream#apps', 'url' => '/api/v1/apps', 'verb' => 'POST'],
['name' => 'ActivityStream#authorize', 'url' => '/oauth/authorize', 'verb' => 'GET'],
['name' => 'ActivityStream#token', 'url' => '/oauth/token', 'verb' => 'POST'],
['name' => 'ActivityStream#appsCredentials', 'url' => '/api/v1/apps/verify_credentials', 'verb' => 'GET'],
['name' => 'ActivityStream#accountsCredentials', 'url' => '/api/v1/accounts/verify_credentials', 'verb' => 'GET'],
// New Local
['name' => 'ActivityStream#timelines', 'url' => '/api/v1/timelines/{timeline}/', 'verb' => 'GET'],
['name' => 'Local#streamHome', 'url' => '/api/v1/stream/home', 'verb' => 'GET'],
['name' => 'Local#streamNotifications', 'url' => '/api/v1/stream/notifications', 'verb' => 'GET'],
['name' => 'Local#streamTimeline', 'url' => '/api/v1/stream/timeline', 'verb' => 'GET'],

Wyświetl plik

@ -203,7 +203,6 @@ class ActivityPubController extends Controller {
* @return Response
*/
public function sharedInbox(): Response {
try {
$body = file_get_contents('php://input');
$this->miscService->log('[<<] sharedInbox: ' . $body, 1);
@ -281,6 +280,29 @@ class ActivityPubController extends Controller {
}
/**
* Method is called when a remote ActivityPub server wants to GET in the INBOX of a USER
* Checking that the user exists, and that the header is properly signed.
*
* @NoCSRFRequired
* @PublicPage
*
* @param string $username
*
* @return Response
*/
public function getInbox(string $username): Response {
try {
$body = file_get_contents('php://input');
$actor = $this->cacheActorService->getFromLocalAccount($username);
return $this->success();
} catch (Exception $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
}
/**
* Outbox. does nothing.
*

Wyświetl plik

@ -0,0 +1,348 @@
<?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\Controller;
use daita\MySmallPhpTools\Traits\Nextcloud\TNCDataResponse;
use Exception;
use OC\AppFramework\Http;
use OC\User\NoUserException;
use OCA\Social\AppInfo\Application;
use OCA\Social\Exceptions\AccountAlreadyExistsException;
use OCA\Social\Exceptions\AccountDoesNotExistException;
use OCA\Social\Exceptions\ActorDoesNotExistException;
use OCA\Social\Exceptions\ItemAlreadyExistsException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\UrlCloudException;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityPub\Stream;
use OCA\Social\Model\ActivityStream\ClientApp;
use OCA\Social\Service\AccountService;
use OCA\Social\Service\CacheActorService;
use OCA\Social\Service\ClientService;
use OCA\Social\Service\ConfigService;
use OCA\Social\Service\FollowService;
use OCA\Social\Service\MiscService;
use OCA\Social\Service\StreamService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\Response;
use OCP\IRequest;
class ActivityStreamController extends Controller {
use TNCDataResponse;
/** @var AccountService */
private $accountService;
/** @var CacheActorService */
private $cacheActorService;
/** @var ClientService */
private $clientService;
/** @var FollowService */
private $followService;
/** @var StreamService */
private $streamService;
/** @var ConfigService */
private $configService;
/** @var MiscService */
private $miscService;
/** @var string */
private $bearer = '';
/** @var Person */
private $viewer;
/**
* ActivityStreamController constructor.
*
* @param IRequest $request
* @param AccountService $accountService
* @param CacheActorService $cacheActorService
* @param ClientService $clientService
* @param FollowService $followService
* @param StreamService $streamService
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
IRequest $request, AccountService $accountService, CacheActorService $cacheActorService,
ClientService $clientService, FollowService $followService, StreamService $streamService,
ConfigService $configService,
MiscService $miscService
) {
parent::__construct(Application::APP_NAME, $request);
$this->accountService = $accountService;
$this->cacheActorService = $cacheActorService;
$this->clientService = $clientService;
$this->followService = $followService;
$this->streamService = $streamService;
$this->configService = $configService;
$this->miscService = $miscService;
$authHeader = trim($this->request->getHeader('Authorization'));
list($authType, $authToken) = explode(' ', $authHeader);
if (strtolower($authType) === 'bearer') {
$this->bearer = $authToken;
}
}
/**
* @NoCSRFRequired
* @PublicPage
*
* @param string $website
* @param string $redirect_uris
* @param string $scopes
* @param string $client_name
*
* @return Response
*/
public function apps(
string $website = '', string $redirect_uris = '', string $scopes = '', string $client_name = ''
): Response {
$clientApp = new ClientApp();
$clientApp->setWebsite($website);
$clientApp->setRedirectUri($redirect_uris);
$clientApp->setScopesFromString($scopes);
$clientApp->setName($client_name);
$this->clientService->createClient($clientApp);
$this->miscService->log('### ' . json_encode($clientApp));
return $this->directSuccess($clientApp);
}
/**
* @NoCSRFRequired
* @NoAdminRequired
* @PublicPage
*
* @return DataResponse
* @throws SocialAppConfigException
* @throws AccountAlreadyExistsException
* @throws ActorDoesNotExistException
* @throws ItemAlreadyExistsException
* @throws UrlCloudException
* @throws NoUserException
*/
public function authorize(): DataResponse {
$userId = 'cult';
$account = $this->accountService->getActorFromUserId($userId);
$clientId = (string)$this->request->getParam('client_id', '');
$responseType = (string)$this->request->getParam('response_type', '');
$redirectUri = (string)$this->request->getParam('redirect_uri', '');
if ($responseType !== 'code') {
return new DataResponse(['error' => 'invalid_type'], Http::STATUS_BAD_REQUEST);
}
// $this->clientService->assignAccount($clientId, $account);
$code = 'test1234';
if ($redirectUri !== '') {
header('Location: ' . $redirectUri . '?code=' . $code);
exit();
}
// return new DataResponse(
// [
// 'access_token' => '',
// "token_type" => "Bearer",
// "scope" => "read write follow push",
// "created_at" => 1573979017
// ], Http::STATUS_OK
// );
}
/**
* @NoCSRFRequired
* @NoAdminRequired
* @PublicPage
*
* @return DataResponse
*/
public function token() {
$body = file_get_contents('php://input');
$this->miscService->log('[<<] : ' . $body, 1);
////code=test1234&grant_type=authorization_code&
//client_secret=amJWTrlnZEhe44aXHsW2xlsTLD8g0DqabDDJ7jdp&
//redirect_uri=https%3A%2F%2Forg.mariotaku.twidere%2Fauth%2Fcallback%2Fmastodon&
//client_id=ihyiNapjftENlY2dZCbbfLHYoloB1HbpWQyLGtvr
return new DataResponse(
[
"access_token" => "ZA-Yj3aBD8U8Cm7lKUp-lm9O9BmDgdhHzDeqsY8tlL0",
"token_type" => "Bearer",
"scope" => "read write follow push",
"created_at" => time()
], 200
);
}
/**
* @NoCSRFRequired
* @NoAdminRequired
* @PublicPage
*
* @return DataResponse
*/
public function appsCredentials() {
return new DataResponse(
[
'name' => 'Twidere for Android',
'website' => 'https://github.com/TwidereProject/'
], 200
);
}
/**
* @NoCSRFRequired
* @NoAdminRequired
* @PublicPage
*
* @return DataResponse
*/
public function accountsCredentials() {
return new DataResponse(
[
"id" => "137148",
"username" => "cult",
"acct" => "cult",
"display_name" => "Maxence Lange",
"locked" => false,
"bot" => false,
"discoverable" => null,
"group" => false,
"created_at" => "2017-05-11T09=>20=>28.055Z",
"note" => "\u003cp\u003e\u003c/p\u003e",
"url" => "https://test.artificial-owl.com/index.php/apps/social/@cult",
"avatar" => "https://mastodon.social/avatars/original/missing.png",
"avatar_static" => "https://mastodon.social/avatars/original/missing.png",
"header" => "https://mastodon.social/headers/original/missing.png",
"header_static" => "https://mastodon.social/headers/original/missing.png",
"followers_count" => 9,
"following_count" => 5,
"statuses_count" => 13,
"last_status_at" => "2019-09-15",
"source" => [
"privacy" => "public",
"sensitive" => false,
"language" => null,
"note" => "",
"fields" => [],
"follow_requests_count" => 0
],
"emojis" => [],
"fields" => []
], 200
);
}
/**
* @NoCSRFRequired
* @NoAdminRequired
* @PublicPage
*
* @param string $timeline
* @param int $limit
*
* @return DataResponse
*/
public function timelines(string $timeline, int $limit = 20): DataResponse {
try {
$this->initViewer(true);
$posts = $this->streamService->getStreamHome(0, $limit, Stream::FORMAT_LOCAL);
return new DataResponse($posts, 200);
} catch (Exception $e) {
return $this->fail($e);
}
}
/**
*
* @param bool $exception
*
* @return bool
* @throws AccountDoesNotExistException
*/
private function initViewer(bool $exception = false): bool {
if ($this->bearer === '') {
// if ($exception) {
// throw new AccountDoesNotExistException('userId not defined');
// }
//
// return false;
}
try {
$this->viewer = $this->cacheActorService->getFromLocalAccount('cult');
$this->streamService->setViewer($this->viewer);
$this->followService->setViewer($this->viewer);
$this->cacheActorService->setViewer($this->viewer);
} catch (Exception $e) {
if ($exception) {
throw new AccountDoesNotExistException(
'unable to initViewer - ' . get_class($e) . ' - ' . $e->getMessage()
);
}
return false;
}
return true;
}
}

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\TArrayTools;
use DateTime;
use Exception;
use OCA\Social\Exceptions\ClientAppDoesNotExistException;
use OCA\Social\Model\ActivityStream\ClientApp;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* Class ActionsRequest
*
* @package OCA\Social\Db
*/
class ClientAppRequest extends ClientAppRequestBuilder {
use TArrayTools;
/**
* Insert a new OAuth client in the database.
*
* @param ClientApp $clientApp
*/
public function save(ClientApp $clientApp) {
$qb = $this->getClientAppInsertSql();
$qb->setValue('name', $qb->createNamedParameter($clientApp->getName()))
->setValue('website', $qb->createNamedParameter($clientApp->getWebsite()))
->setValue('redirect_uri', $qb->createNamedParameter($clientApp->getRedirectUri()))
->setValue('client_id', $qb->createNamedParameter($clientApp->getClientId()))
->setValue('client_secret', $qb->createNamedParameter($clientApp->getClientSecret()));
try {
$qb->setValue(
'creation',
$qb->createNamedParameter(new DateTime('now'), IQueryBuilder::PARAM_DATE)
);
} catch (Exception $e) {
}
$qb->execute();
$clientApp->setId($qb->getLastInsertId());
}
/**
* @param string $clientId
* @param string $account
*/
public function assignAccount(string $clientId, string $account): void {
$qb = $this->getClientAppUpdateSql();
$qb->set('account', $qb->createNamedParameter($account));
$qb->limitToClientId($clientId);
$qb->limitToAccount('');
$qb->execute();
}
/**
* @param string $clientId
*
* @return ClientApp
* @throws ClientAppDoesNotExistException
*/
public function getByClientId(string $clientId): ClientApp {
$qb = $this->getClientAppSelectSql();
$qb->limitToClientId($clientId);
return $this->getClientAppFromRequest($qb);
}
}

Wyświetl plik

@ -0,0 +1,156 @@
<?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\Exceptions\RowNotFoundException;
use daita\MySmallPhpTools\Traits\TArrayTools;
use OCA\Social\Exceptions\ClientAppDoesNotExistException;
use OCA\Social\Model\ActivityStream\ClientApp;
/**
* Class ClientAppRequestBuilder
*
* @package OCA\Social\Db
*/
class ClientAppRequestBuilder extends CoreRequestBuilder {
use TArrayTools;
/**
* Base of the Sql Insert request
*
* @return SocialQueryBuilder
*/
protected function getClientAppInsertSql(): SocialQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->insert(self::TABLE_CLIENT_APP);
return $qb;
}
/**
* Base of the Sql Update request
*
* @return SocialQueryBuilder
*/
protected function getClientAppUpdateSql(): SocialQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->update(self::TABLE_CLIENT_APP);
return $qb;
}
/**
* Base of the Sql Select request for Shares
*
* @return SocialQueryBuilder
*/
protected function getClientAppSelectSql(): SocialQueryBuilder {
$qb = $this->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select(
'ca.id', 'ca.name', 'ca.website', 'ca.redirect_uri', 'ca.client_id', 'ca.client_secret',
'ca.creation'
)
->from(self::TABLE_CLIENT_APP, 'ca');
$this->defaultSelectAlias = 'ca';
$qb->setDefaultSelectAlias('ca');
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return SocialQueryBuilder
*/
protected function getClientAppDeleteSql(): SocialQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->delete(self::TABLE_CLIENT_APP);
return $qb;
}
/**
* @param SocialQueryBuilder $qb
*
* @return ClientApp
* @throws ClientAppDoesNotExistException
*/
public function getClientAppFromRequest(SocialQueryBuilder $qb): ClientApp {
/** @var ClientApp $result */
try {
$result = $qb->getRow([$this, 'parseClientAppSelectSql']);
} catch (RowNotFoundException $e) {
throw new ClientAppDoesNotExistException($e->getMessage());
}
return $result;
}
/**
* @param SocialQueryBuilder $qb
*
* @return ClientApp[]
*/
public function getClientAppsFromRequest(SocialQueryBuilder $qb): array {
/** @var ClientApp[] $result */
$result = $qb->getRows([$this, 'parseClientAppSelectSql']);
return $result;
}
/**
* @param array $data
*
* @return ClientApp
*/
public function parseClientAppSelectSql($data): ClientApp {
$item = new ClientApp();
$item->importFromDatabase($data);
return $item;
}
}

Wyświetl plik

@ -76,6 +76,9 @@ class CoreRequestBuilder {
const TABLE_CACHE_ACTORS = 'social_3_cache_actor';
const TABLE_CACHE_DOCUMENTS = 'social_3_cache_doc';
const TABLE_CLIENT_APP = 'social_3_client_app';
/** @var array */
private $tables = [
self::TABLE_REQUEST_QUEUE,
@ -89,9 +92,11 @@ class CoreRequestBuilder {
self::TABLE_STREAM_QUEUE,
self::TABLE_STREAM_DEST,
self::TABLE_STREAM_TAGS,
self::TABLE_STREAM_ACTIONS
self::TABLE_STREAM_ACTIONS,
self::TABLE_CLIENT_APP
];
/** @var ILogger */
protected $logger;

Wyświetl plik

@ -91,6 +91,16 @@ class SocialLimitsQueryBuilder extends SocialCrossQueryBuilder {
}
/**
* Limit the request to clientId
*
* @param string $clientId
*/
public function limitToClientId(string $clientId) {
$this->limitToDBField('client_id', $clientId);
}
/**
* @param string $type
*/

Wyświetl plik

@ -39,6 +39,25 @@ namespace OCA\Social\Db;
class SocialQueryBuilder extends SocialFiltersQueryBuilder {
/** @var int */
private $format = 1;
/**
* @param int $format
*/
public function setFormat(int $format) {
$this->format = $format;
}
/**
* @return int
*/
public function getFormat(): int {
return $this->format;
}
/**
* @param string $id
* @param string $field

Wyświetl plik

@ -364,12 +364,13 @@ class StreamRequest extends StreamRequestBuilder {
*
* @param int $since
* @param int $limit
* @param int $format
*
* @return Stream[]
* @throws DateTimeException
*/
public function getTimelineHome(int $since = 0, int $limit = 5): array {
$qb = $this->getStreamSelectSql();
public function getTimelineHome(int $since = 0, int $limit = 5, int $format = Stream::FORMAT_ACTIVITYPUB): array {
$qb = $this->getStreamSelectSql($format);
$qb->setChunk(1);
$qb->filterType(SocialAppNotification::TYPE);

Wyświetl plik

@ -84,10 +84,13 @@ class StreamRequestBuilder extends CoreRequestBuilder {
/**
* Base of the Sql Select request for Shares
*
* @param int $format
*
* @return SocialQueryBuilder
*/
protected function getStreamSelectSql(): SocialQueryBuilder {
protected function getStreamSelectSql(int $format = Stream::FORMAT_ACTIVITYPUB): SocialQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->setFormat($format);
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->selectDistinct('s.id')
@ -195,17 +198,19 @@ class StreamRequestBuilder extends CoreRequestBuilder {
/**
* @param array $data
*
* @param SocialQueryBuilder $qb
*
* @return Stream
* @throws ItemUnknownException
* @throws SocialAppConfigException
*/
public function parseStreamSelectSql(array $data): Stream {
public function parseStreamSelectSql(array $data, SocialQueryBuilder $qb): Stream {
$as = $this->get('type', $data, Stream::TYPE);
/** @var Stream $item */
$item = AP::$activityPub->getItemFromType($as);
$item->importFromDatabase($data);
$item->setExportFormat($qb->getFormat());
$instances = json_decode($this->get('instances', $data, '[]'), true);
if (is_array($instances)) {
foreach ($instances as $instance) {

Wyświetl plik

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

Wyświetl plik

@ -33,9 +33,11 @@ namespace OCA\Social\Interfaces\Object;
use OCA\Social\Db\CacheDocumentsRequest;
use OCA\Social\Exceptions\CacheDocumentDoesNotExistException;
use OCA\Social\Exceptions\InvalidOriginException;
use OCA\Social\Exceptions\ItemNotFoundException;
use OCA\Social\Interfaces\IActivityPubInterface;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityPub\Object\Document;
use OCA\Social\Service\MiscService;
@ -67,8 +69,13 @@ class DocumentInterface implements IActivityPubInterface {
/**
* @param ACore $activity
* @param ACore $item
*
* @throws InvalidOriginException
*/
public function activity(Acore $activity, ACore $item) {
if ($activity->getType() === Person::TYPE) {
$activity->checkOrigin($item->getId());
}
}

Wyświetl plik

@ -76,6 +76,7 @@ class Version0003Date20200611000001 extends SimpleMigrationStep {
$this->createActors($schema);
$this->createCacheActors($schema);
$this->createCacheDocuments($schema);
$this->createClientOauth($schema);
$this->createFollows($schema);
$this->createHashtags($schema);
$this->createRequestQueue($schema);
@ -85,6 +86,7 @@ class Version0003Date20200611000001 extends SimpleMigrationStep {
$this->createStreamQueue($schema);
$this->createStreamTags($schema);
return $schema;
}
@ -902,6 +904,75 @@ class Version0003Date20200611000001 extends SimpleMigrationStep {
}
/**
* @param ISchemaWrapper $schema
*/
private function createClientOauth(ISchemaWrapper $schema) {
if ($schema->hasTable('social_3_client_app')) {
return;
}
$table = $schema->createTable('social_3_client_app');
$table->addColumn(
'id', 'integer',
[
'autoincrement' => true,
'notnull' => true,
'length' => 7,
'unsigned' => true,
]
);
$table->addColumn(
'name', 'string',
[
'notnull' => false,
'length' => 127,
'default' => ''
]
);
$table->addColumn(
'website', 'string',
[
'notnull' => false,
'length' => 255,
'default' => ''
]
);
$table->addColumn(
'redirect_uri', 'string',
[
'notnull' => false,
'length' => 255,
'default' => ''
]
);
$table->addColumn(
'client_id', 'string',
[
'notnull' => false,
'length' => 63,
'default' => ''
]
);
$table->addColumn(
'client_secret', 'string',
[
'notnull' => false,
'length' => 63,
'default' => ''
]
);
$table->addColumn(
'creation', 'datetime',
[
'notnull' => false,
]
);
$table->setPrimaryKey(['id']);
}
/**
* @param ISchemaWrapper $schema
*/

Wyświetl plik

@ -45,7 +45,7 @@ use OCP\Migration\SimpleMigrationStep;
*
* @package OCA\Social\Migration
*/
class Version0003Date20200730213528 extends SimpleMigrationStep {
class Version0003Date20200823023911 extends SimpleMigrationStep {
/** @var IDBConnection */
@ -73,6 +73,8 @@ class Version0003Date20200730213528 extends SimpleMigrationStep {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$this->createClientOauth($schema);
$this->addChunkToTable($schema, 'social_3_stream', '');
$this->addChunkToTable($schema, 'social_3_stream_act', '_act');
$this->addChunkToTable($schema, 'social_3_stream_dest', '_dest');
@ -81,9 +83,79 @@ class Version0003Date20200730213528 extends SimpleMigrationStep {
}
/**
* @param ISchemaWrapper $schema
*/
private function createClientOauth(ISchemaWrapper $schema) {
if ($schema->hasTable('social_3_client_app')) {
return;
}
$table = $schema->createTable('social_3_client_app');
$table->addColumn(
'id', 'integer',
[
'autoincrement' => true,
'notnull' => true,
'length' => 7,
'unsigned' => true,
]
);
$table->addColumn(
'name', 'string',
[
'notnull' => false,
'length' => 127,
'default' => ''
]
);
$table->addColumn(
'website', 'string',
[
'notnull' => false,
'length' => 255,
'default' => ''
]
);
$table->addColumn(
'redirect_uri', 'string',
[
'notnull' => false,
'length' => 255,
'default' => ''
]
);
$table->addColumn(
'client_id', 'string',
[
'notnull' => false,
'length' => 63,
'default' => ''
]
);
$table->addColumn(
'client_secret', 'string',
[
'notnull' => false,
'length' => 63,
'default' => ''
]
);
$table->addColumn(
'creation', 'datetime',
[
'notnull' => false,
]
);
$table->setPrimaryKey(['id']);
}
/**
* @param ISchemaWrapper $schema
* @param string $tableName
* @param string $indexName
*
* @throws SchemaException
*/

Wyświetl plik

@ -63,6 +63,9 @@ class ACore extends Item implements JsonSerializable {
const AS_STRING = 7;
const AS_TAGS = 10;
const FORMAT_ACTIVITYPUB = 1;
const FORMAT_LOCAL = 2;
/** @var null Item */
private $parent = null;
@ -85,6 +88,9 @@ class ACore extends Item implements JsonSerializable {
/** @var LinkedDataSignature */
private $signature = null;
/** @var int */
private $format = self::FORMAT_ACTIVITYPUB;
/**
* Core constructor.
@ -647,11 +653,41 @@ class ACore extends Item implements JsonSerializable {
}
/**
* @param int $format
*
* @return $this
*/
public function setExportFormat(int $format): self {
$this->format = $format;
return $this;
}
/**
* @return int
*/
public function getExportFormat(): int {
return $this->format;
}
/**
* @return array
*/
public function jsonSerialize(): array {
if ($this->getExportFormat() === self::FORMAT_LOCAL) {
return $this->exportAsLocal();
}
return $this->exportAsActivityPub();
}
/**
* @return array
*/
public function exportAsActivityPub(): array {
if ($this->gotSignature()) {
$this->entries['signature'] = $this->getSignature();
}
@ -715,6 +751,104 @@ class ACore extends Item implements JsonSerializable {
return $result;
}
/**
* @return array
*/
public function exportAsLocal(): array {
return [
"id" => "104745311773345735",
"created_at" => "2020-08-24T16=>29=>13.000Z",
"in_reply_to_id" => null,
"in_reply_to_account_id" => null,
"sensitive" => false,
"spoiler_text" => "",
"visibility" => "unlisted",
"language" => "de",
// 'id' => $this->getId(),
// 'uri' => $this->getId(),
// 'url' => $this->getId(),
'replies_count' => 0,
'reblogs_count' => 0,
'favourites_count' => 0,
'favourited' => false,
'reblogged' => false,
'muted' => false,
'bookmarked' => false,
"content" => "\u003cp\u003e\"Mit den frisch freigegebenen Versionen 3.0 und 3.13 aktualisiert Nextcloud seine Desktop- und Android-Clients. Zentrale Neuerung ist die nun für den produktiven Einsatz einsatzbereite Ende-zu-Ende-Verschlüsselung.\" | iX Magazin @heiseonline \u003cbr\u003e\u003ca href=\"https://www.heise.de/news/Neue-Nextcloud-Clients-mit-Ende-zu-Ende-Verschluesselung-4873632.html\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"ellipsis\"\u003eheise.de/news/Neue-Nextcloud-C\u003c/span\u003e\u003cspan class=\"invisible\"\u003elients-mit-Ende-zu-Ende-Verschluesselung-4873632.html\u003c/span\u003e\u003c/a\u003e\u003c/p\u003e",
"reblog" => null,
"account" => [
"id" => "126222",
"username" => "nextcloud",
"acct" => "nextcloud@mastodon.xyz",
"display_name" => "Nextcloud 📱☁️💻",
"locked" => false,
"bot" => false,
"discoverable" => false,
"group" => false,
"created_at" => "2017-05-02T09=>56=>41.951Z",
"note" => "\u003cp\u003eA safe home for all your data, community-driven \u0026amp; open source!\u003cbr\u003e\u003ca href=\"https=>//nextcloud.com\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps=>//\u003c/span\u003e\u003cspan class=\"\"\u003enextcloud.com\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e\u003cbr\u003eJoin us at \u003ca href=\"https=>//help.nextcloud.com\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps=>//\u003c/span\u003e\u003cspan class=\"\"\u003ehelp.nextcloud.com\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e and \u003ca href=\"https=>//github.com/nextcloud\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps=>//\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/nextcloud\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e 😊\u003c/p\u003e",
"url" => "https=>//mastodon.xyz/@nextcloud",
"avatar" => "https=>//files.mastodon.social/accounts/avatars/000/126/222/original/50785214e44d10cc.jpeg",
"avatar_static" => "https=>//files.mastodon.social/accounts/avatars/000/126/222/original/50785214e44d10cc.jpeg",
"header" => "https=>//files.mastodon.social/accounts/headers/000/126/222/original/6d7b41fdd92cfd6f.jpeg",
"header_static" => "https=>//files.mastodon.social/accounts/headers/000/126/222/original/6d7b41fdd92cfd6f.jpeg",
"followers_count" => 9451,
"following_count" => 132,
"statuses_count" => 3020,
"last_status_at" => "2020-08-24",
"emojis" => []
]
// "fields": [
// {
// "name": "💻 Website",
// "value": "\u003ca href=\"https://nextcloud.com\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003enextcloud.com\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e",
// "verified_at": null
// },
// {
// "name": "😍 Contribute",
// "value": "\u003ca href=\"https://nextcloud.com/contribute\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003enextcloud.com/contribute\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e",
// "verified_at": null
// },
// {
// "name": "🌈 Include initiative",
// "value": "\u003ca href=\"https://nextcloud.com/include\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003enextcloud.com/include\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e",
// "verified_at": null
// },
// {
// "name": "💬 Forum",
// "value": "\u003ca href=\"https://help.nextcloud.com\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003ehelp.nextcloud.com\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e",
// "verified_at": null
// }
// ]
// }
];
// "media_attachments": [],
// "mentions": [],
// "tags": [],
// "emojis": [],
// "card": {
// "url": "https://www.heise.de/news/Neue-Nextcloud-Clients-mit-Ende-zu-Ende-Verschluesselung-4873632.html",
// "title": "Neue Nextcloud-Clients mit Ende-zu-Ende-Verschlüsselung",
// "description": "Nextcloud aktualisiert seine Desktop- und Android-Clients. Zentrale Neuerung ist die Ende-zu-Ende-Verschlüsselung, die aber nicht für alle Dateien gedacht ist.",
// "type": "link",
// "author_name": "",
// "author_url": "",
// "provider_name": "",
// "provider_url": "",
// "html": "",
// "width": 400,
// "height": 225,
// "image": "https://files.mastodon.social/cache/preview_cards/images/023/249/328/original/3710f7a1b54f0319.png",
// "embed_url": "",
// "blurhash": "UbNwWf_3%L00Rkayt6ofRkIVjYxut7fkoej["
// },
// "poll": null
// },
}
}

Wyświetl plik

@ -396,9 +396,9 @@ class Stream extends ACore implements IQueryRow, JsonSerializable {
/**
* @return array
*/
public function jsonSerialize(): array {
public function exportAsActivityPub(): array {
$result = array_merge(
parent::jsonSerialize(),
parent::exportAsActivityPub(),
[
'content' => $this->getContent(),
'attributedTo' => ($this->getAttributedTo() !== '') ? $this->getUrlSocial()
@ -409,6 +409,7 @@ class Stream extends ACore implements IQueryRow, JsonSerializable {
]
);
// TODO: use exportFormat
if ($this->isCompleteDetails()) {
$result = array_merge(
$result,
@ -430,5 +431,16 @@ class Stream extends ACore implements IQueryRow, JsonSerializable {
return $result;
}
}
/**
* @return array
*/
public function exportAsLocal(): array {
$result = array_merge(
parent::exportAsLocal(),
[]);
return $result;
}
}

Wyświetl plik

@ -0,0 +1,257 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Model\ActivityStream;
use daita\MySmallPhpTools\IQueryRow;
use daita\MySmallPhpTools\Traits\TArrayTools;
use JsonSerializable;
/**
* Class ClientApp
*
* @package OCA\Social\Model\ActivityStream
*/
class ClientApp implements IQueryRow, JsonSerializable {
use TArrayTools;
/** @var int */
private $id = 0;
/** @var string */
private $name = '';
/** @var string */
private $website = '';
/** @var string */
private $redirectUri = '';
/** @var array */
private $scopes = [];
/** @var string */
private $clientId = '';
/** @var string */
private $clientSecret = '';
/**
* ClientApp constructor.
*/
public function __construct() {
}
/**
* @return int
*/
public function getId(): int {
return $this->id;
}
/**
* @param int $id
*
* @return ClientApp
*/
public function setId(int $id): self {
$this->id = $id;
return $this;
}
/**
* @return string
*/
public function getName(): string {
return $this->name;
}
/**
* @param string $name
*
* @return ClientApp
*/
public function setName(string $name): self {
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getWebsite(): string {
return $this->website;
}
/**
* @param string $website
*
* @return ClientApp
*/
public function setWebsite(string $website): self {
$this->website = $website;
return $this;
}
/**
* @return string
*/
public function getRedirectUri(): string {
return $this->redirectUri;
}
/**
* @param string $redirectUri
*
* @return ClientApp
*/
public function setRedirectUri(string $redirectUri): self {
$this->redirectUri = $redirectUri;
return $this;
}
/**
* @return array
*/
public function getScopes(): array {
return $this->scopes;
}
/**
* @param array $scopes
*
* @return ClientApp
*/
public function setScopes(array $scopes): self {
$this->scopes = $scopes;
return $this;
}
/**
* @param string $scopes
*
* @return ClientApp
*/
public function setScopesFromString(string $scopes): self {
$this->scopes = explode(' ', $scopes);
return $this;
}
/**
* @return string
*/
public function getClientId(): string {
return $this->clientId;
}
/**
* @param string $clientId
*
* @return ClientApp
*/
public function setClientId(string $clientId): self {
$this->clientId = $clientId;
return $this;
}
/**
* @return string
*/
public function getClientSecret(): string {
return $this->clientSecret;
}
/**
* @param string $clientSecret
*
* @return ClientApp
*/
public function setClientSecret(string $clientSecret): self {
$this->clientSecret = $clientSecret;
return $this;
}
/**
* @param array $data
*
* @return ClientApp
*/
public function importFromDatabase(array $data): self {
$this->setId($this->getInt('id', $data));
$this->setName($this->get('name', $data));
$this->setWebsite($this->get('website', $data));
$this->setRedirectUri($this->get('redirect_uri', $data));
$this->setClientId($this->get('client_id', $data));
$this->setClientSecret($this->get('client_secret', $data));
return $this;
}
/**
* @return array
*/
public function jsonSerialize(): array {
$arr = [
'id' => $this->getId(),
'name' => $this->getName(),
'website' => $this->getWebsite(),
'redirect_uri' => $this->getRedirectUri(),
'client_id' => $this->getClientId(),
'client_secret' => $this->getClientSecret()
];
return array_filter($arr);
}
}

Wyświetl plik

@ -0,0 +1,101 @@
<?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\Service;
use daita\MySmallPhpTools\Traits\TStringTools;
use OCA\Social\Db\ClientAppRequest;
use OCA\Social\Exceptions\ClientAppDoesNotExistException;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityStream\ClientApp;
/**
* Class ClientService
*
* @package OCA\Social\Service
*/
class ClientService {
use TStringTools;
/** @var ClientAppRequest */
private $clientAppRequest;
/** @var MiscService */
private $miscService;
/**
* BoostService constructor.
*
* @param ClientAppRequest $clientAppRequest
* @param MiscService $miscService
*/
public function __construct(ClientAppRequest $clientAppRequest, MiscService $miscService) {
$this->clientAppRequest = $clientAppRequest;
$this->miscService = $miscService;
}
/**
* @param ClientApp $clientApp
*/
public function createClient(ClientApp $clientApp): void {
$clientApp->setClientId($this->token(40));
$clientApp->setClientSecret($this->token(40));
$this->clientAppRequest->save($clientApp);
}
/**
* @param string $clientId
* @param Person $account
*/
public function assignAccount(string $clientId, Person $account) {
$this->clientAppRequest->assignAccount($clientId, $account->getPreferredUsername());
}
/**
* @param string $clientId
*
* @return ClientApp
* @throws ClientAppDoesNotExistException
*/
public function getByClientId(string $clientId): ClientApp {
return $this->clientAppRequest->getByClientId($clientId);
}
}

Wyświetl plik

@ -138,6 +138,8 @@ class PostService {
$token = $this->activityService->createActivity($actor, $note, $activity);
$this->accountService->cacheLocalActorDetailCount($actor);
$this->miscService->log('Activity: ' . json_encode($activity));
return $activity;
}

Wyświetl plik

@ -285,6 +285,10 @@ class StreamService {
'name' => '#' . $hashtag
]
);
\OC::$server->getLogger()
->log(
3, '___' . $this->configService->getSocialUrl() . 'tag/' . strtolower($hashtag)
);
} catch (SocialAppConfigException $e) {
}
}
@ -405,12 +409,13 @@ class StreamService {
/**
* @param int $since
* @param int $limit
* @param int $format
*
* @return Note[]
* @throws Exception
* @throws DateTimeException
*/
public function getStreamHome(int $since = 0, int $limit = 5): array {
return $this->streamRequest->getTimelineHome($since, $limit);
public function getStreamHome(int $since = 0, int $limit = 5, int $format = Stream::FORMAT_ACTIVITYPUB): array {
return $this->streamRequest->getTimelineHome($since, $limit, $format);
}