Merge pull request #730 from nextcloud/feature/noid/disply-single-note

display single note
pull/758/head
Maxence Lange 2019-09-25 14:37:59 +02:00 zatwierdzone przez GitHub
commit 34f8b1d8cc
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
27 zmienionych plików z 2237 dodań i 1577 usunięć

Wyświetl plik

@ -60,7 +60,7 @@ return [
['name' => 'OStatus#followRemote', 'url' => '/api/v1/ostatus/followRemote/{local}', 'verb' => 'GET'],
['name' => 'OStatus#getLink', 'url' => '/api/v1/ostatus/link/{local}/{account}', 'verb' => 'GET'],
['name' => 'SocialPub#displayPost', 'url' => '/@{username}/{postId}', 'verb' => 'GET'],
['name' => 'ActivityPub#displayPost', 'url' => '/@{username}/{token}', 'verb' => 'GET'],
['name' => 'Local#streamHome', 'url' => '/api/v1/stream/home', 'verb' => 'GET'],
['name' => 'Local#streamNotifications', 'url' => '/api/v1/stream/notifications', 'verb' => 'GET'],
@ -71,7 +71,8 @@ return [
['name' => 'Local#streamLiked', 'url' => '/api/v1/stream/liked', 'verb' => 'GET'],
['name' => 'Local#streamAccount', 'url' => '/api/v1/account/{username}/stream', 'verb' => 'GET'],
['name' => 'Local#postData', 'url' => '/local/v1/post', 'verb' => 'GET'],
['name' => 'Local#postGet', 'url' => '/local/v1/post', 'verb' => 'GET'],
['name' => 'Local#postReplies', 'url' => '/local/v1/post/replies', 'verb' => 'GET'],
['name' => 'Local#postCreate', 'url' => '/api/v1/post', 'verb' => 'POST'],
['name' => 'Local#postDelete', 'url' => '/api/v1/post', 'verb' => 'DELETE'],

71
composer.lock wygenerowano
Wyświetl plik

@ -1,10 +1,10 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "f5575fb747924736058725f236d7fdae",
"content-hash": "f93a783c86bad53b0b8486db3fc61380",
"packages": [
{
"name": "daita/my-small-php-tools",
@ -12,12 +12,12 @@
"source": {
"type": "git",
"url": "https://github.com/daita/my-small-php-tools.git",
"reference": "bea3a148a427d446511c23b1512686fa03835754"
"reference": "ffc91a81c84ec679379b4b8a0a34434f3697c6e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/bea3a148a427d446511c23b1512686fa03835754",
"reference": "bea3a148a427d446511c23b1512686fa03835754",
"url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/ffc91a81c84ec679379b4b8a0a34434f3697c6e7",
"reference": "ffc91a81c84ec679379b4b8a0a34434f3697c6e7",
"shasum": ""
},
"require": {
@ -40,7 +40,7 @@
}
],
"description": "My small PHP Tools",
"time": "2019-08-20T20:23:09+00:00"
"time": "2019-09-15T08:55:12+00:00"
},
{
"name": "friendica/json-ld",
@ -448,26 +448,26 @@
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "4.3.1",
"version": "4.3.2",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c"
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c",
"reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
"reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
"shasum": ""
},
"require": {
"php": "^7.0",
"phpdocumentor/reflection-common": "^1.0.0",
"phpdocumentor/type-resolver": "^0.4.0",
"phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0",
"phpdocumentor/type-resolver": "~0.4 || ^1.0.0",
"webmozart/assert": "^1.0"
},
"require-dev": {
"doctrine/instantiator": "~1.0.5",
"doctrine/instantiator": "^1.0.5",
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^6.4"
},
@ -495,29 +495,29 @@
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"time": "2019-04-30T17:48:53+00:00"
"time": "2019-09-12T14:27:41+00:00"
},
{
"name": "phpdocumentor/type-resolver",
"version": "0.4.0",
"version": "0.5.1",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
"reference": "cf842904952e64e703800d094cdf34e715a8a3ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
"reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/cf842904952e64e703800d094cdf34e715a8a3ae",
"reference": "cf842904952e64e703800d094cdf34e715a8a3ae",
"shasum": ""
},
"require": {
"php": "^5.5 || ^7.0",
"php": "^7.0",
"phpdocumentor/reflection-common": "^1.0"
},
"require-dev": {
"mockery/mockery": "^0.9.4",
"phpunit/phpunit": "^5.2||^4.8.24"
"mockery/mockery": "^1.0",
"phpunit/phpunit": "^6.4"
},
"type": "library",
"extra": {
@ -527,9 +527,7 @@
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": [
"src/"
]
"phpDocumentor\\Reflection\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -542,7 +540,7 @@
"email": "me@mikevanriel.com"
}
],
"time": "2017-07-14T14:27:02+00:00"
"time": "2017-12-30T13:23:38+00:00"
},
{
"name": "phpspec/prophecy",
@ -1213,16 +1211,16 @@
},
{
"name": "sebastian/exporter",
"version": "3.1.1",
"version": "3.1.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "06a9a5947f47b3029d76118eb5c22802e5869687"
"reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/06a9a5947f47b3029d76118eb5c22802e5869687",
"reference": "06a9a5947f47b3029d76118eb5c22802e5869687",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e",
"reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e",
"shasum": ""
},
"require": {
@ -1276,7 +1274,7 @@
"export",
"exporter"
],
"time": "2019-08-11T12:43:14+00:00"
"time": "2019-09-14T09:02:43+00:00"
},
{
"name": "sebastian/global-state",
@ -1659,16 +1657,16 @@
},
{
"name": "webmozart/assert",
"version": "1.4.0",
"version": "1.5.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
"reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
"reference": "88e6d84706d09a236046d686bbea96f07b3a34f4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
"reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
"url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4",
"reference": "88e6d84706d09a236046d686bbea96f07b3a34f4",
"shasum": ""
},
"require": {
@ -1676,8 +1674,7 @@
"symfony/polyfill-ctype": "^1.8"
},
"require-dev": {
"phpunit/phpunit": "^4.6",
"sebastian/version": "^1.0.1"
"phpunit/phpunit": "^4.8.36 || ^7.5.13"
},
"type": "library",
"extra": {
@ -1706,7 +1703,7 @@
"check",
"validate"
],
"time": "2018-12-25T11:19:39+00:00"
"time": "2019-08-24T08:43:50+00:00"
}
],
"aliases": [],

Wyświetl plik

@ -38,14 +38,18 @@ use OC\AppFramework\Http;
use OCA\Social\AppInfo\Application;
use OCA\Social\Exceptions\ItemUnknownException;
use OCA\Social\Exceptions\SignatureIsGoneException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\StreamNotFoundException;
use OCA\Social\Exceptions\UrlCloudException;
use OCA\Social\Service\CacheActorService;
use OCA\Social\Service\ConfigService;
use OCA\Social\Service\FediverseService;
use OCA\Social\Service\FollowService;
use OCA\Social\Service\ImportService;
use OCA\Social\Service\MiscService;
use OCA\Social\Service\SignatureService;
use OCA\Social\Service\StreamQueueService;
use OCA\Social\Service\StreamService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Response;
use OCP\IRequest;
@ -80,6 +84,12 @@ class ActivityPubController extends Controller {
/** @var FollowService */
private $followService;
/** @var StreamService */
private $streamService;
/** @var ConfigService */
private $configService;
/** @var MiscService */
private $miscService;
@ -95,13 +105,16 @@ class ActivityPubController extends Controller {
* @param StreamQueueService $streamQueueService
* @param ImportService $importService
* @param FollowService $followService
* @param StreamService $streamService
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
IRequest $request, SocialPubController $socialPubController,
FediverseService $fediverseService, CacheActorService $cacheActorService,
SignatureService $signatureService, StreamQueueService $streamQueueService,
ImportService $importService, FollowService $followService, MiscService $miscService
ImportService $importService, FollowService $followService, StreamService $streamService,
ConfigService $configService, MiscService $miscService
) {
parent::__construct(Application::APP_NAME, $request);
@ -112,6 +125,8 @@ class ActivityPubController extends Controller {
$this->streamQueueService = $streamQueueService;
$this->importService = $importService;
$this->followService = $followService;
$this->streamService = $streamService;
$this->configService = $configService;
$this->miscService = $miscService;
}
@ -131,6 +146,7 @@ class ActivityPubController extends Controller {
*
* @return Response
* @throws UrlCloudException
* @throws SocialAppConfigException
*/
public function actor(string $username): Response {
if (!$this->checkSourceActivityStreams()) {
@ -162,6 +178,7 @@ class ActivityPubController extends Controller {
*
* @return Response
* @throws UrlCloudException
* @throws SocialAppConfigException
*/
public function actorAlias(string $username): Response {
return $this->actor($username);
@ -227,7 +244,7 @@ class ActivityPubController extends Controller {
$this->miscService->log('[<<] inbox: ' . $body, 1);
$requestTime = 0;
$origin = $this->signatureService->checkRequest($this->request, $body,$requestTime);
$origin = $this->signatureService->checkRequest($this->request, $body, $requestTime);
$this->fediverseService->authorized($origin);
// TODO - check the recipient <-> username
@ -281,9 +298,9 @@ class ActivityPubController extends Controller {
*
* @return Response
* @throws UrlCloudException
* @throws SocialAppConfigException
*/
public function followers(string $username): Response {
if (!$this->checkSourceActivityStreams()) {
return $this->socialPubController->followers($username);
}
@ -311,6 +328,7 @@ class ActivityPubController extends Controller {
*
* @return Response
* @throws UrlCloudException
* @throws SocialAppConfigException
*/
public function following(string $username): Response {
if (!$this->checkSourceActivityStreams()) {
@ -328,16 +346,24 @@ class ActivityPubController extends Controller {
* @PublicPage
*
* @param string $username
* @param $postId
* @param string $token
*
* @return Response
* @throws SocialAppConfigException
* @throws StreamNotFoundException
*/
public function displayPost($username, $postId) {
public function displayPost(string $username, string $token): Response {
if (!$this->checkSourceActivityStreams()) {
return $this->socialPubController->displayPost($username, $postId);
return $this->socialPubController->displayPost($username, $token);
}
return $this->success([$username, $postId]);
// TODO - check viewer rights !
$postId = $this->configService->getSocialUrl() . '@' . $username . '/' . $token;
$stream = $this->streamService->getStreamById($postId, false);
$stream->setCompleteDetails(false);
return $this->directSuccess($stream);
}

Wyświetl plik

@ -27,6 +27,7 @@ declare(strict_types=1);
*
*/
namespace OCA\Social\Controller;
@ -130,10 +131,10 @@ class LocalController extends Controller {
* @param MiscService $miscService
*/
public function __construct(
IRequest $request, $userId, AccountService $accountService,
CacheActorService $cacheActorService, HashtagService $hashtagService,
FollowService $followService,
PostService $postService, StreamService $streamService, SearchService $searchService,
IRequest $request, $userId, AccountService $accountService, CacheActorService $cacheActorService,
HashtagService $hashtagService,
FollowService $followService, PostService $postService, StreamService $streamService,
SearchService $searchService,
BoostService $boostService, LikeService $likeService, DocumentService $documentService,
MiscService $miscService
) {
@ -194,16 +195,42 @@ class LocalController extends Controller {
* get info about a post (limited to viewer rights).
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $id
*
* @return DataResponse
*/
public function postData(string $id): DataResponse {
public function postGet(string $id): DataResponse {
try {
$this->initViewer(true);
return $this->directSuccess($this->streamService->getStreamById($id, true));
$stream = $this->streamService->getStreamById($id, true);
return $this->directSuccess($stream);
} catch (Exception $e) {
return $this->fail($e);
}
}
/**
* get replies about a post (limited to viewer rights).
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @param string $id
* @param int $since
* @param int $limit
*
* @return DataResponse
*/
public function postReplies(string $id, int $since = 0, int $limit = 5): DataResponse {
try {
$this->initViewer(true);
return $this->success($this->streamService->getRepliesByParentId($id, $since, $limit, true));
} catch (Exception $e) {
return $this->fail($e);
}

Wyświetl plik

@ -55,6 +55,12 @@ use OCP\IL10N;
use OCP\IRequest;
use OCP\IURLGenerator;
/**
* Class NavigationController
*
* @package OCA\Social\Controller
*/
class NavigationController extends Controller {
@ -88,6 +94,7 @@ class NavigationController extends Controller {
/** @var CheckService */
private $checkService;
/**
* NavigationController constructor.
*
@ -261,6 +268,7 @@ class NavigationController extends Controller {
*
* @return TemplateResponse
* @throws UrlCloudException
* @throws SocialAppConfigException
*/
public function timeline(string $path = ''): TemplateResponse {
return $this->navigate();
@ -276,6 +284,7 @@ class NavigationController extends Controller {
*
* @return TemplateResponse
* @throws UrlCloudException
* @throws SocialAppConfigException
*/
public function account(string $path = ''): TemplateResponse {
return $this->navigate();

Wyświetl plik

@ -31,14 +31,15 @@ namespace OCA\Social\Controller;
use daita\MySmallPhpTools\Traits\Nextcloud\TNCDataResponse;
use Exception;
use OCA\Social\AppInfo\Application;
use OCA\Social\Exceptions\CacheActorDoesNotExistException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\StreamNotFoundException;
use OCA\Social\Exceptions\UrlCloudException;
use OCA\Social\Service\AccountService;
use OCA\Social\Service\CacheActorService;
use OCA\Social\Service\FollowService;
use OCA\Social\Service\ConfigService;
use OCA\Social\Service\StreamService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Response;
@ -47,28 +48,35 @@ use OCP\AppFramework\Http\TemplateResponse;
use OCP\IL10N;
use OCP\IRequest;
/**
* Class SocialPubController
*
* @package OCA\Social\Controller
*/
class SocialPubController extends Controller {
use TNCDataResponse;
/** @var string */
private $userId;
/** @var IL10N */
private $l10n;
/** @var AccountService */
private $accountService;
/** @var NavigationController */
private $navigationController;
/** @var CacheActorService */
private $cacheActorService;
/** @var FollowService */
private $followService;
/** @var StreamService */
private $streamService;
/** @var NavigationController */
private $navigationController;
/** @var ConfigService */
private $configService;
/**
@ -77,36 +85,39 @@ class SocialPubController extends Controller {
* @param $userId
* @param IRequest $request
* @param IL10N $l10n
* @param CacheActorService $cacheActorService
* @param NavigationController $navigationController
* @param CacheActorService $cacheActorService
* @param StreamService $streamService
* @param ConfigService $configService
*/
public function __construct(
$userId,
IRequest $request,
IL10N $l10n,
CacheActorService $cacheActorService,
NavigationController $navigationController
$userId, IRequest $request, IL10N $l10n, NavigationController $navigationController,
CacheActorService $cacheActorService, StreamService $streamService, ConfigService $configService
) {
parent::__construct(Application::APP_NAME, $request);
$this->userId = $userId;
$this->l10n = $l10n;
$this->cacheActorService = $cacheActorService;
$this->navigationController = $navigationController;
$this->cacheActorService = $cacheActorService;
$this->streamService = $streamService;
$this->configService = $configService;
}
/**
* @param $username
*
* @return Response
* @throws UrlCloudException
* @throws SocialAppConfigException
*/
private function renderPage($username): Response {
if ($this->userId) {
return $this->navigationController->navigate('');
}
$data = [
'serverData' => [
'serverData' => [
'public' => true,
],
'application' => 'Social'
@ -141,6 +152,7 @@ class SocialPubController extends Controller {
*
* @return Response
* @throws UrlCloudException
* @throws SocialAppConfigException
*/
public function actor(string $username): Response {
return $this->renderPage($username);
@ -158,6 +170,7 @@ class SocialPubController extends Controller {
*
* @return TemplateResponse
* @throws UrlCloudException
* @throws SocialAppConfigException
*/
public function followers(string $username): Response {
return $this->renderPage($username);
@ -175,6 +188,7 @@ class SocialPubController extends Controller {
*
* @return TemplateResponse
* @throws UrlCloudException
* @throws SocialAppConfigException
*/
public function following(string $username): Response {
return $this->renderPage($username);
@ -182,19 +196,34 @@ class SocialPubController extends Controller {
/**
* Should return post, do nothing.
* Display the navigation page of the Social app.
*
* @NoCSRFRequired
* @PublicPage
*
* @param string $username
* @param $postId
* @param string $token
*
* @return Response
* @return TemplateResponse
* @throws StreamNotFoundException
* @throws SocialAppConfigException
*/
public function displayPost(string $username, int $postId) {
return $this->success([$username, $postId]);
public function displayPost(string $username, string $token): TemplateResponse {
// TODO - check viewer rights !
$postId = $this->configService->getSocialUrl() . '@' . $username . '/' . $token;
$stream = $this->streamService->getStreamById($postId, false);
$data = [
'id' => $postId,
'item' => $stream,
'serverData' => [
'public' => true,
],
'application' => 'Social'
];
return new TemplateResponse(Application::APP_NAME, 'stream', $data);
}
}

Wyświetl plik

@ -43,20 +43,6 @@ use OCP\IDBConnection;
class ActorsRequest extends ActorsRequestBuilder {
/**
* ActorsRequest constructor.
*
* @param IDBConnection $connection
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
IDBConnection $connection, ConfigService $configService, MiscService $miscService
) {
parent::__construct($connection, $configService, $miscService);
}
/**
* create a new Person in the database.
*

Wyświetl plik

@ -35,29 +35,13 @@ use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Exception;
use OCA\Social\Exceptions\CacheActorDoesNotExistException;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Service\ConfigService;
use OCA\Social\Service\MiscService;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
class CacheActorsRequest extends CacheActorsRequestBuilder {
const CACHE_TTL = 60 * 24; // 1d
/**
* CacheActorsRequest constructor.
*
* @param IDBConnection $connection
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
IDBConnection $connection, ConfigService $configService, MiscService $miscService
) {
parent::__construct($connection, $configService, $miscService);
}
/**
* insert cache about an Actor in database.

Wyświetl plik

@ -35,6 +35,7 @@ use DateInterval;
use DateTime;
use Doctrine\DBAL\Query\QueryBuilder;
use Exception;
use OC;
use OC\DB\SchemaWrapper;
use OCA\Social\AP;
use OCA\Social\Exceptions\DateTimeException;
@ -48,6 +49,7 @@ use OCA\Social\Service\ConfigService;
use OCA\Social\Service\MiscService;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\ILogger;
/**
@ -88,6 +90,8 @@ class CoreRequestBuilder {
self::TABLE_STREAM_ACTIONS
];
protected $logger;
/** @var IDBConnection */
protected $dbConnection;
@ -98,29 +102,53 @@ class CoreRequestBuilder {
protected $miscService;
/** @var string */
protected $defaultSelectAlias;
/** @var Person */
protected $viewer = null;
/** @var string */
protected $defaultSelectAlias;
/**
* CoreRequestBuilder constructor.
*
* @param IDBConnection $connection
* @param ILogger $logger
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
IDBConnection $connection, ConfigService $configService, MiscService $miscService
IDBConnection $connection, ILogger $logger, ConfigService $configService, MiscService $miscService
) {
$this->dbConnection = $connection;
$this->logger = $logger;
$this->configService = $configService;
$this->miscService = $miscService;
}
/**
* @return SocialQueryBuilder
*/
public function getQueryBuilder(): SocialQueryBuilder {
$qb = new SocialQueryBuilder(
$this->dbConnection,
OC::$server->getSystemConfig(),
$this->logger
);
return $qb;
}
/**
* @return IDBConnection
*/
public function getConnection(): IDBConnection {
return $this->dbConnection;
}
/**
* @param Person $viewer
*/
@ -198,6 +226,17 @@ class CoreRequestBuilder {
}
/**
* Limit the request to the Id (string)
*
* @param IQueryBuilder $qb
* @param string $id
*/
protected function limitToInReplyTo(IQueryBuilder &$qb, string $id) {
$this->limitToDBField($qb, 'in_reply_to', $id, false);
}
/**
* Limit the request to the StreamId
*
@ -257,7 +296,7 @@ class CoreRequestBuilder {
* @param string $username
*/
protected function searchInPreferredUsername(IQueryBuilder &$qb, string $username) {
$dbConn = $this->dbConnection;
$dbConn = $this->getConnection();
$this->searchInDBField(
$qb, 'preferred_username', $dbConn->escapeLikeParameter($username) . '%'
);
@ -314,7 +353,7 @@ class CoreRequestBuilder {
* @param bool $all
*/
protected function searchInHashtag(IQueryBuilder &$qb, string $hashtag, bool $all = false) {
$dbConn = $this->dbConnection;
$dbConn = $this->getConnection();
$this->searchInDBField(
$qb, 'hashtag', (($all) ? '%' : '') . $dbConn->escapeLikeParameter($hashtag) . '%'
);
@ -385,7 +424,7 @@ class CoreRequestBuilder {
* @param string $account
*/
protected function searchInAccount(IQueryBuilder &$qb, string $account) {
$dbConn = $this->dbConnection;
$dbConn = $this->getConnection();
$this->searchInDBField($qb, 'account', $dbConn->escapeLikeParameter($account) . '%');
}
@ -493,13 +532,17 @@ class CoreRequestBuilder {
* @param int $since
* @param int $limit
*
* @throws Exception
* @throws DateTimeException
*/
protected function limitPaginate(IQueryBuilder &$qb, int $since = 0, int $limit = 5) {
if ($since > 0) {
$dTime = new DateTime();
$dTime->setTimestamp($since);
$this->limitToDBFieldDateTime($qb, 'published_time', $dTime);
try {
if ($since > 0) {
$dTime = new DateTime();
$dTime->setTimestamp($since);
$this->limitToDBFieldDateTime($qb, 'published_time', $dTime);
}
} catch (Exception $e) {
throw new DateTimeException();
}
$qb->setMaxResults($limit);
@ -507,6 +550,8 @@ class CoreRequestBuilder {
$qb->orderBy($pf . '.published_time', 'desc');
}
//
//
/**
* @param IQueryBuilder $qb
@ -1160,9 +1205,7 @@ class CoreRequestBuilder {
* @param string $fieldActorId
* @param string $pf
*/
protected function leftJoinDetails(
IQueryBuilder $qb, string $fieldActorId = 'id', string $pf = ''
) {
protected function leftJoinDetails(IQueryBuilder $qb, string $fieldActorId = 'id', string $pf = '') {
$this->leftJoinFollowAsViewer($qb, $fieldActorId, true, 'as_follower', $pf);
$this->leftJoinFollowAsViewer($qb, $fieldActorId, false, 'as_followed', $pf);
}

Wyświetl plik

@ -0,0 +1,343 @@
<?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\Db\ExtendedQueryBuilder;
use DateInterval;
use DateTime;
use Exception;
use OCA\Social\Model\ActivityPub\Actor\Person;
/**
* Class SocialQueryBuilder
*
* @package OCA\Push\Db
*/
class SocialQueryBuilder extends ExtendedQueryBuilder {
/** @var Person */
private $viewer = null;
/**
* @return bool
*/
public function hasViewer(): bool {
return ($this->viewer !== null);
}
/**
* @param Person $viewer
*/
public function setViewer(Person $viewer): void {
$this->viewer = $viewer;
}
/**
* @return Person
*/
public function getViewer(): Person {
return $this->viewer;
}
/**
* @param string $id
*/
public function generatePrimaryKey(string $id) {
$this->setValue('id_prim', $this->createNamedParameter(hash('sha512', $id)));
}
/**
* Limit the request to the Type
*
* @param string $type
*
* @return SocialQueryBuilder
*/
public function limitToType(string $type): self {
$this->limitToDBField('type', $type, false);
return $this;
}
/**
* Limit the request to the ActivityId
*
* @param string $activityId
*/
protected function limitToActivityId(string $activityId) {
$this->limitToDBField('activity_id', $activityId, false);
}
/**
* Limit the request to the Id (string)
*
* @param string $id
*/
protected function limitToInReplyTo(string $id) {
$this->limitToDBField('in_reply_to', $id, false);
}
/**
* Limit the request to the sub-type
*
* @param string $subType
*/
protected function limitToSubType(string $subType) {
$this->limitToDBField('subtype', $subType);
}
/**
* @param string $type
*/
protected function filterType(string $type) {
$this->filterDBField('type', $type);
}
/**
* Limit the request to the Preferred Username
*
* @param string $username
*/
protected function limitToPreferredUsername(string $username) {
$this->limitToDBField('preferred_username', $username, false);
}
/**
* search using username
*
* @param string $username
*/
protected function searchInPreferredUsername(string $username) {
$dbConn = $this->getConnection();
$this->searchInDBField('preferred_username', $dbConn->escapeLikeParameter($username) . '%');
}
/**
* Limit the request to the ActorId
*/
protected function limitToPublic() {
$this->limitToDBFieldInt('public', 1);
}
/**
* Limit the request to the token
*
* @param string $token
*/
protected function limitToToken(string $token) {
$this->limitToDBField('token', $token);
}
/**
* Limit the results to a given number
*
* @param int $limit
*/
protected function limitResults(int $limit) {
$this->setMaxResults($limit);
}
/**
* Limit the request to the ActorId
*
* @param string $hashtag
*/
protected function limitToHashtag(string $hashtag) {
$this->limitToDBField('hashtag', $hashtag, false);
}
/**
* Limit the request to the ActorId
*
* @param string $hashtag
* @param bool $all
*/
protected function searchInHashtag(string $hashtag, bool $all = false) {
$dbConn = $this->getConnection();
$this->searchInDBField('hashtag', (($all) ? '%' : '') . $dbConn->escapeLikeParameter($hashtag) . '%');
}
/**
* Limit the request to the ActorId
*
* @param string $actorId
* @param string $alias
*/
protected function limitToActorId(string $actorId, string $alias = '') {
$this->limitToDBField('actor_id', $actorId, false, $alias);
}
/**
* Limit the request to the FollowId
*
* @param string $followId
*/
protected function limitToFollowId(string $followId) {
$this->limitToDBField('follow_id', $followId, false);
}
/**
* Limit the request to the FollowId
*
* @param bool $accepted
* @param string $alias
*/
protected function limitToAccepted(bool $accepted, string $alias = '') {
$this->limitToDBField('accepted', ($accepted) ? '1' : '0', true, $alias);
}
/**
* Limit the request to the ServiceId
*
* @param string $objectId
*/
protected function limitToObjectId(string $objectId) {
$this->limitToDBField('object_id', $objectId, false);
}
/**
* Limit the request to the account
*
* @param string $account
*/
protected function limitToAccount(string $account) {
$this->limitToDBField('account', $account, false);
}
/**
* Limit the request to the account
*
* @param string $account
*/
protected function searchInAccount(string $account) {
$dbConn = $this->getConnection();
$this->searchInDBField('account', $dbConn->escapeLikeParameter($account) . '%');
}
/**
* Limit the request to the creation
*
* @param int $delay
*
* @throws Exception
*/
protected function limitToCaching(int $delay = 0) {
$date = new DateTime('now');
$date->sub(new DateInterval('PT' . $delay . 'M'));
$this->limitToDBFieldDateTime('caching', $date, true);
}
/**
* Limit the request to the url
*
* @param string $url
*/
protected function limitToUrl(string $url) {
$this->limitToDBField('url', $url);
}
/**
* Limit the request to the url
*
* @param string $actorId
*/
protected function limitToAttributedTo(string $actorId) {
$this->limitToDBField('attributed_to', $actorId, false);
}
/**
* Limit the request to the status
*
* @param int $status
*/
protected function limitToStatus(int $status) {
$this->limitToDBFieldInt('status', $status);
}
/**
* Limit the request to the instance
*
* @param string $address
*/
protected function limitToAddress(string $address) {
$this->limitToDBField('address', $address);
}
/**
* Limit the request to the instance
*
* @param bool $local
*/
protected function limitToLocal(bool $local) {
$this->limitToDBField('local', ($local) ? '1' : '0');
}
/**
* Limit the request to the parent_id
*
* @param string $parentId
*/
protected function limitToParentId(string $parentId) {
$this->limitToDBField('parent_id', $parentId);
}
}

Wyświetl plik

@ -51,6 +51,7 @@ use OCA\Social\Service\ConfigService;
use OCA\Social\Service\MiscService;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\ILogger;
/**
@ -69,15 +70,16 @@ class StreamRequest extends StreamRequestBuilder {
* StreamRequest constructor.
*
* @param IDBConnection $connection
* @param ILogger $logger
* @param StreamDestRequest $streamDestRequest
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
IDBConnection $connection, StreamDestRequest $streamDestRequest, ConfigService $configService,
MiscService $miscService
IDBConnection $connection, ILogger $logger, StreamDestRequest $streamDestRequest,
ConfigService $configService, MiscService $miscService
) {
parent::__construct($connection, $configService, $miscService);
parent::__construct($connection, $logger, $configService, $miscService);
$this->streamDestRequest = $streamDestRequest;
}
@ -171,7 +173,7 @@ class StreamRequest extends StreamRequestBuilder {
* @param Document $document
* @param array $attachments
*
* @return array
* @return Document[]
*/
private function updateAttachmentInList(Document $document, array $attachments): array {
$new = [];
@ -216,17 +218,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->limitToType($qb, $type);
}
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -236,6 +228,7 @@ class StreamRequest extends StreamRequestBuilder {
*
* @return Stream
* @throws StreamNotFoundException
* @throws SocialAppConfigException
*/
public function getStreamById(string $id, bool $asViewer = false): Stream {
if ($id === '') {
@ -251,23 +244,46 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinStreamAction($qb);
}
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
try {
return $this->getStreamFromRequest($qb);
} catch (ItemUnknownException $e) {
throw new StreamNotFoundException('Malformed Stream');
} catch (StreamNotFoundException $e) {
throw new StreamNotFoundException(
'Stream (ById) not found - ' . $id . ' (asViewer: ' . $asViewer . ')'
);
}
}
try {
$stream = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
throw new StreamNotFoundException('Malformed Stream');
/**
* @param string $id
* @param int $since
* @param int $limit
* @param bool $asViewer
*
* @return Stream[]
* @throws StreamNotFoundException
* @throws DateTimeException
*/
public function getRepliesByParentId(string $id, int $since = 0, int $limit = 5, bool $asViewer = false
): array {
if ($id === '') {
throw new StreamNotFoundException();
};
$qb = $this->getStreamSelectSql();
$this->limitToInReplyTo($qb, $id);
$this->limitPaginate($qb, $since, $limit);
$this->leftJoinCacheActors($qb, 'attributed_to');
if ($asViewer) {
$this->limitToViewer($qb);
$this->leftJoinStreamAction($qb);
}
return $stream;
return $this->getStreamsFromRequest($qb);
}
@ -286,15 +302,7 @@ class StreamRequest extends StreamRequestBuilder {
$qb = $this->getStreamSelectSql();
$this->limitToActivityId($qb, $id);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new StreamNotFoundException('Stream (ByActivityId) not found - ' . $id);
}
return $this->parseStreamSelectSql($data);
return $this->getStreamFromRequest($qb);
}
@ -319,17 +327,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->limitToType($qb, $type);
$this->limitToSubType($qb, $subType);
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new StreamNotFoundException(
'StreamByObjectId not found - ' . $type . ' - ' . $objectId
);
}
return $this->parseStreamSelectSql($data);
return $this->getStreamFromRequest($qb);
}
@ -362,7 +360,7 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $limit
*
* @return Stream[]
* @throws Exception
* @throws DateTimeException
*/
public function getTimelineHome(Person $actor, int $since = 0, int $limit = 5): array {
$qb = $this->getStreamSelectSql();
@ -379,17 +377,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinStreamAction($qb);
$this->filterDuplicate($qb);
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -405,8 +393,8 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $since
* @param int $limit
*
* @return array
* @throws Exception
* @return Stream[]
* @throws DateTimeException
*/
public function getTimelineNotifications(Person $actor, int $since = 0, int $limit = 5): array {
$qb = $this->getStreamSelectSql();
@ -418,17 +406,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinCacheActors($qb, 'attributed_to');
$this->leftJoinStreamAction($qb);
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -441,8 +419,8 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $since
* @param int $limit
*
* @return array
* @throws Exception
* @return Stream[]
* @throws DateTimeException
*/
public function getTimelineAccount(string $actorId, int $since = 0, int $limit = 5): array {
$qb = $this->getStreamSelectSql();
@ -454,17 +432,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinCacheActors($qb, 'attributed_to');
$this->leftJoinStreamAction($qb);
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -477,8 +445,8 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $since
* @param int $limit
*
* @return array
* @throws Exception
* @return Stream[]
* @throws DateTimeException
*/
public function getTimelineDirect(Person $actor, int $since = 0, int $limit = 5): array {
$qb = $this->getStreamSelectSql();
@ -492,17 +460,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinCacheActors($qb, 'attributed_to');
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -514,8 +472,8 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $limit
* @param bool $localOnly
*
* @return array
* @throws Exception
* @return Stream[]
* @throws DateTimeException
*/
public function getTimelineGlobal(int $since = 0, int $limit = 5, bool $localOnly = true
): array {
@ -531,17 +489,7 @@ class StreamRequest extends StreamRequestBuilder {
// TODO: to: = real public, cc: = unlisted !?
$this->limitToRecipient($qb, ACore::CONTEXT_PUBLIC, true, ['to']);
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -553,8 +501,8 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $limit
* @param bool $localOnly
*
* @return array
* @throws Exception
* @return Stream[]
* @throws DateTimeException
*/
public function getTimelineLiked(int $since = 0, int $limit = 5, bool $localOnly = true): array {
$qb = $this->getStreamSelectSql();
@ -567,17 +515,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinActions($qb, Like::TYPE);
$this->filterDBField($qb, 'id', '', false, 'a');
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
try {
$streams[] = $this->parseStreamSelectSql($data);
} catch (Exception $e) {
}
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -592,8 +530,8 @@ class StreamRequest extends StreamRequestBuilder {
* @param int $since
* @param int $limit
*
* @return array
* @throws Exception
* @return Stream[]
* @throws DateTimeException
*/
public function getTimelineTag(Person $actor, string $hashtag, int $since = 0, int $limit = 5
): array {
@ -612,14 +550,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->leftJoinCacheActors($qb, 'attributed_to');
$this->leftJoinStreamAction($qb);
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$streams[] = $this->parseStreamSelectSql($data);
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}
@ -628,8 +559,6 @@ class StreamRequest extends StreamRequestBuilder {
*
* @return Stream[]
* @throws DateTimeException
* @throws ItemUnknownException
* @throws SocialAppConfigException
*/
public function getNoteSince(int $since): array {
$qb = $this->getStreamSelectSql();
@ -637,14 +566,7 @@ class StreamRequest extends StreamRequestBuilder {
$this->limitToType($qb, Note::TYPE);
$this->leftJoinStreamAction($qb);
$streams = [];
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$streams[] = $this->parseStreamSelectSql($data, Note::TYPE);
}
$cursor->closeCursor();
return $streams;
return $this->getStreamsFromRequest($qb);
}

Wyświetl plik

@ -31,12 +31,14 @@ namespace OCA\Social\Db;
use daita\MySmallPhpTools\Exceptions\CacheItemNotFoundException;
use daita\MySmallPhpTools\Exceptions\RowNotFoundException;
use daita\MySmallPhpTools\Traits\TArrayTools;
use Doctrine\DBAL\Query\QueryBuilder;
use OCA\Social\AP;
use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Exceptions\ItemUnknownException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\StreamNotFoundException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityPub\Object\Announce;
@ -60,11 +62,11 @@ class StreamRequestBuilder extends CoreRequestBuilder {
/**
* Base of the Sql Insert request
*
* @return IQueryBuilder
* @return SocialQueryBuilder
*/
protected function getStreamInsertSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::TABLE_STREAM);
protected function getStreamInsertSql(): SocialQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->insert(self::TABLE_STREAMS);
return $qb;
}
@ -73,11 +75,11 @@ class StreamRequestBuilder extends CoreRequestBuilder {
/**
* Base of the Sql Update request
*
* @return IQueryBuilder
* @return SocialQueryBuilder
*/
protected function getStreamUpdateSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->update(self::TABLE_STREAM);
protected function getStreamUpdateSql(): SocialQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->update(self::TABLE_STREAMS);
return $qb;
}
@ -86,10 +88,10 @@ class StreamRequestBuilder extends CoreRequestBuilder {
/**
* Base of the Sql Select request for Shares
*
* @return IQueryBuilder
* @return SocialQueryBuilder
*/
protected function getStreamSelectSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
protected function getStreamSelectSql(): SocialQueryBuilder {
$qb = $this->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->selectDistinct('s.id')
@ -110,10 +112,10 @@ class StreamRequestBuilder extends CoreRequestBuilder {
/**
* Base of the Sql Select request for Shares
*
* @return IQueryBuilder
* @return SocialQueryBuilder
*/
protected function countNotesSelectSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
protected function countNotesSelectSql(): SocialQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->selectAlias($qb->createFunction('COUNT(*)'), 'count')
->from(self::TABLE_STREAM, 's');
@ -126,11 +128,11 @@ class StreamRequestBuilder extends CoreRequestBuilder {
/**
* Base of the Sql Delete request
*
* @return IQueryBuilder
* @return SocialQueryBuilder
*/
protected function getStreamDeleteSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::TABLE_STREAM);
protected function getStreamDeleteSql(): SocialQueryBuilder {
$qb = $this->getQueryBuilder();
$qb->delete(self::TABLE_STREAMS);
return $qb;
}
@ -321,9 +323,8 @@ class StreamRequestBuilder extends CoreRequestBuilder {
*
* @return string
*/
protected function exprValueWithinJsonFormat(IQueryBuilder $qb, string $field, string $value
): string {
$dbConn = $this->dbConnection;
protected function exprValueWithinJsonFormat(IQueryBuilder $qb, string $field, string $value): string {
$dbConn = $this->getConnection();
$expr = $qb->expr();
return $expr->iLike(
@ -340,9 +341,8 @@ class StreamRequestBuilder extends CoreRequestBuilder {
*
* @return string
*/
protected function exprValueNotWithinJsonFormat(IQueryBuilder $qb, string $field, string $value
): string {
$dbConn = $this->dbConnection;
protected function exprValueNotWithinJsonFormat(IQueryBuilder $qb, string $field, string $value): string {
$dbConn = $this->getConnection();
$expr = $qb->expr();
$func = $qb->func();
@ -447,6 +447,37 @@ class StreamRequestBuilder extends CoreRequestBuilder {
}
/**
* @param SocialQueryBuilder $qb
*
* @return Stream
* @throws StreamNotFoundException
*/
protected function getStreamFromRequest(SocialQueryBuilder $qb): Stream {
/** @var Stream $result */
try {
$result = $qb->getRow([$this, 'parseStreamSelectSql']);
} catch (RowNotFoundException $e) {
throw new StreamNotFoundException($e->getMessage());
}
return $result;
}
/**
* @param SocialQueryBuilder $qb
*
* @return Stream[]
*/
public function getStreamsFromRequest(SocialQueryBuilder $qb): array {
/** @var Stream[] $result */
$result = $qb->getRows([$this, 'parseStreamSelectSql']);
return $result;
}
/**
* @param array $data
* @param string $as
@ -455,7 +486,7 @@ class StreamRequestBuilder extends CoreRequestBuilder {
* @throws ItemUnknownException
* @throws SocialAppConfigException
*/
protected function parseStreamSelectSql(array $data, string $as = Stream::TYPE): Stream {
public function parseStreamSelectSql(array $data, string $as = Stream::TYPE): Stream {
if ($as === Stream::TYPE) {
$as = $this->get('type', $data, Stream::TYPE);
}

Wyświetl plik

@ -30,6 +30,7 @@ declare(strict_types=1);
namespace OCA\Social\Model\ActivityPub;
use daita\MySmallPhpTools\IQueryRow;
use daita\MySmallPhpTools\Model\Cache;
use daita\MySmallPhpTools\Model\CacheItem;
use DateTime;
@ -39,7 +40,12 @@ use OCA\Social\Model\StreamAction;
use OCA\Social\Traits\TDetails;
class Stream extends ACore implements JsonSerializable {
/**
* Class Stream
*
* @package OCA\Social\Model\ActivityPub
*/
class Stream extends ACore implements IQueryRow, JsonSerializable {
use TDetails;
@ -396,8 +402,7 @@ class Stream extends ACore implements JsonSerializable {
[
'content' => $this->getContent(),
'attributedTo' => ($this->getAttributedTo() !== '') ? $this->getUrlSocial()
. $this->getAttributedTo(
) : '',
. $this->getAttributedTo() : '',
'inReplyTo' => $this->getInReplyTo(),
'sensitive' => $this->isSensitive(),
'conversation' => $this->getConversation()

Wyświetl plik

@ -38,6 +38,7 @@ use OCA\Social\Db\FollowsRequest;
use OCA\Social\Db\StreamRequest;
use OCA\Social\Exceptions\AccountAlreadyExistsException;
use OCA\Social\Exceptions\ActorDoesNotExistException;
use OCA\Social\Exceptions\ItemAlreadyExistsException;
use OCA\Social\Exceptions\ItemUnknownException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\UrlCloudException;
@ -161,6 +162,7 @@ class AccountService {
* @throws NoUserException
* @throws SocialAppConfigException
* @throws UrlCloudException
* @throws ItemAlreadyExistsException
*/
public function getActorFromUserId(string $userId, bool $create = false): Person {
$this->miscService->confirmUserId($userId);
@ -192,6 +194,7 @@ class AccountService {
* @param string $username
*
* @throws AccountAlreadyExistsException
* @throws ItemAlreadyExistsException
* @throws NoUserException
* @throws SocialAppConfigException
* @throws UrlCloudException
@ -238,6 +241,7 @@ class AccountService {
*
* @throws SocialAppConfigException
* @throws UrlCloudException
* @throws ItemAlreadyExistsException
*/
public function cacheLocalActorByUsername(string $username) {
try {

Wyświetl plik

@ -72,6 +72,10 @@ class FediverseService {
* @throws SocialAppConfigException
*/
public function authorized(string $address): bool {
if ($address === '') {
throw new UnauthorizedFediverseException('Empty Origin');
}
if ($this->getAccessType() ===
$this->configService->accessTypeList['BLACKLIST']
&& !$this->isListed($address)) {

Wyświetl plik

@ -262,6 +262,8 @@ class SignatureService {
throw new SignatureIsGoneException();
} catch (SignatureException $e) {
}
return '';
}

Wyświetl plik

@ -36,7 +36,6 @@ use OCA\Social\Db\StreamRequest;
use OCA\Social\Exceptions\InvalidOriginException;
use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Exceptions\ItemUnknownException;
use OCA\Social\Exceptions\StreamNotFoundException;
use OCA\Social\Exceptions\RedundancyLimitException;
use OCA\Social\Exceptions\RequestContentException;
use OCA\Social\Exceptions\RequestNetworkException;
@ -44,6 +43,7 @@ use OCA\Social\Exceptions\RequestResultNotJsonException;
use OCA\Social\Exceptions\RequestResultSizeException;
use OCA\Social\Exceptions\RequestServerException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\StreamNotFoundException;
use OCA\Social\Exceptions\UnauthorizedFediverseException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Actor\Person;
@ -383,12 +383,27 @@ class StreamService {
*
* @return Stream
* @throws StreamNotFoundException
* @throws SocialAppConfigException
*/
public function getStreamById(string $id, bool $asViewer = false): Stream {
return $this->streamRequest->getStreamById($id, $asViewer);
}
/**
* @param string $id
* @param int $since
* @param int $limit
* @param bool $asViewer
*
* @return Stream[]
* @throws StreamNotFoundException
*/
public function getRepliesByParentId(string $id, int $since = 0, int $limit = 5, bool $asViewer = false): array {
return $this->streamRequest->getRepliesByParentId($id, $since, $limit, $asViewer);
}
/**
* @param Person $actor
* @param int $since

2615
package-lock.json wygenerowano

Plik diff jest za duży Load Diff

Wyświetl plik

@ -31,6 +31,8 @@
"linkifyjs": "^2.1.8",
"nextcloud-axios": "^0.2.1",
"nextcloud-vue": "^0.12.3",
"nextcloud-logger": "0.0.6",
"nextcloud-auth": "0.0.3",
"tributejs": "^3.7.3",
"twemoji": "^12.0.1",
"uuid": "^3.3.3",

Wyświetl plik

@ -202,7 +202,7 @@ export default {
// importing server data into the store
const serverDataElmt = document.getElementById('serverData')
if (serverDataElmt !== null) {
this.$store.commit('setServerData', JSON.parse(document.getElementById('serverData').dataset.server))
this.$store.commit('setServerData', JSON.parse(serverDataElmt.dataset.server))
}
if (!this.serverData.public) {

Wyświetl plik

@ -1,5 +1,5 @@
<template>
<div class="timeline-entry">
<div class="timeline-entry" @click="getSinglePostTimeline">
<div v-if="item.type === 'SocialAppNotification'">
{{ actionSummary }}
</div>
@ -30,6 +30,7 @@
</template>
<script>
import Logger from '../logger'
import TimelinePost from './TimelinePost.vue'
export default {
@ -94,6 +95,35 @@ export default {
}
},
methods: {
getSinglePostTimeline(e) {
// Do not call the single-post view when clicking on a link, a post attachment miniature or the post's author
if (e.target.tagName === 'A' || e.target.tagName === 'IMG' || e.target.className.startsWith('post-author')) {
Logger.debug('will not call single-post', { event: e })
return
}
// Display internal or external post
if (!this.item.local) {
if (this.item.type === 'Note') {
window.open(this.item.id)
} else if (this.item.type === 'Announce') {
window.open(this.item.object)
} else {
Logger.warn("Don't know what to do with posts of type " + this.item.type, { post: this.item })
}
} else {
this.$router.push({ name: 'single-post',
params: {
account: this.item.actor_info.preferredUsername,
id: this.item.id,
localId: this.item.id.split('/')[this.item.id.split('/').length - 1],
type: 'single-post'
}
})
}
},
userDisplayName(actorInfo) {
return actorInfo.name !== '' ? actorInfo.name : actorInfo.preferredUsername
}
@ -105,6 +135,9 @@ export default {
padding: 10px;
margin-bottom: 10px;
}
.timeline-entry:hover {
background-color: #F5F5F5;
}
.container-icon-boost {
display: inline-block;

28
src/logger.js 100644
Wyświetl plik

@ -0,0 +1,28 @@
/*
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @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/>.
*/
import { getLoggerBuilder } from 'nextcloud-logger'
import { getCurrentUser } from 'nextcloud-auth'
export default getLoggerBuilder()
.setApp('social')
.setUid(getCurrentUser().uid)
.build()

Wyświetl plik

@ -26,6 +26,7 @@ import Router from 'vue-router'
// Dynamic loading
const Timeline = () => import('./views/Timeline')
const TimelineSinglePost = () => import('./views/TimelineSinglePost')
const Profile = () => import(/* webpackChunkName: "profile" */'./views/Profile')
const ProfileTimeline = () => import(/* webpackChunkName: "profile" */'./views/ProfileTimeline')
const ProfileFollowers = () => import(/* webpackChunkName: "profile" */'./views/ProfileFollowers')
@ -57,6 +58,14 @@ export default new Router({
}
]
},
{
path: '/:index(index.php/)?apps/social/@:account/:localId',
components: {
default: TimelineSinglePost
},
props: true,
name: 'single-post'
},
{
path: '/:index(index.php/)?apps/social/@:account',
components: {

Wyświetl plik

@ -21,6 +21,7 @@
*
*/
import Logger from '../logger'
import axios from 'nextcloud-axios'
import Vue from 'vue'
@ -92,6 +93,15 @@ const getters = {
return Object.values(state.timeline).sort(function(a, b) {
return b.publishedTime - a.publishedTime
})
},
getPostFromTimeline(state) {
return (postId) => {
if (typeof state.timeline[postId] !== 'undefined') {
return state.timeline[postId]
} else {
Logger.warn('Could not find post in timeline', { postId: postId })
}
}
}
}
const actions = {
@ -181,22 +191,47 @@ const actions = {
return this.dispatch('fetchTimeline', { sinceTimestamp: Math.floor(Date.now() / 1000) + 1 })
},
fetchTimeline(context, { sinceTimestamp }) {
if (typeof sinceTimestamp === 'undefined') {
sinceTimestamp = state.since - 1
}
let url
// Compute URl to get the data
let url = ''
if (state.type === 'account') {
url = OC.generateUrl(`apps/social/api/v1/account/${state.account}/stream?limit=25&since=` + sinceTimestamp)
} else if (state.type === 'tags') {
url = OC.generateUrl(`apps/social/api/v1/stream/tag/${state.params.tag}?limit=25&since=` + sinceTimestamp)
} else if (state.type === 'single-post') {
url = OC.generateUrl(`apps/social/local/v1/post/replies?id=${state.params.id}&limit=5&since=` + sinceTimestamp)
} else {
url = OC.generateUrl(`apps/social/api/v1/stream/${state.type}?limit=25&since=` + sinceTimestamp)
}
// Get the data and add them to the timeline
return axios.get(url).then((response) => {
if (response.status === -1) {
throw response.message
}
context.commit('addToTimeline', response.data.result)
let result = []
// Also load replies when displaying a single post timeline
if (state.type === 'single-post') {
result.push(response.data)
// axios.get(OC.generateUrl(``)).then((response) => {
// if (response.status !== -1) {
// result.concat(response.data.result)
// }
// }
} else {
result = response.data.result
}
// Add results to timeline
context.commit('addToTimeline', result)
return response.data
})
},

Wyświetl plik

@ -21,7 +21,7 @@
</div>
</div>
</transition>
<composer v-if="type !== 'notifications'" />
<composer v-if="type !== 'notifications' && type !== 'single-post'" />
<h2 v-if="type === 'tags'">
#{{ this.$route.params.tag }}
</h2>
@ -117,6 +117,8 @@ export default {
params() {
if (this.$route.name === 'tags') {
return { tag: this.$route.params.tag }
} else if (this.$route.name === 'single-post') {
return this.$route.params
}
return {}
},

Wyświetl plik

@ -0,0 +1,67 @@
<template>
<div class="social__wrapper">
<timeline-entry :item="mainPost" />
<timeline-list :type="$route.params.type" />
</div>
</template>
<style scoped>
.social__timeline {
max-width: 600px;
margin: 15px auto;
}
#app-content {
position: relative;
}
</style>
<script>
import Logger from '../logger'
import TimelineEntry from './../components/TimelineEntry.vue'
import TimelineList from './../components/TimelineList.vue'
export default {
name: 'TimelineSinglePost',
components: {
TimelineEntry,
TimelineList
},
mixins: [
],
data() {
return {
mainPost: {}
}
},
computed: {
},
beforeMount: function() {
// Get data of post clicked on
if (typeof this.$route.params.id === 'undefined') {
Logger.debug('displaying the single post timeline for a non logged-in user')
this.mainPost = JSON.parse(document.getElementById('postData').dataset.server)
} else {
this.mainPost = this.$store.getters.getPostFromTimeline(this.$route.params.id)
}
// Set params for the TimelineList component
let params = {
account: window.location.href.split('/')[window.location.href.split('/').length - 2].substr(1),
id: window.location.href,
localId: window.location.href.split('/')[window.location.href.split('/').length - 1],
type: 'single-post'
}
this.$store.dispatch('changeTimelineType', {
type: 'single-post',
params: params
})
},
methods: {
}
}
</script>

Wyświetl plik

@ -0,0 +1,7 @@
<?php
script('social', 'social');
style('social', 'style');
?>
<span id="postData" data-server="<?php p(json_encode($_['item']));?>"></span>
<span id="serverData" data-server="<?php p(json_encode($_['serverData']));?>"></span>
<div id="vue-content"></div>