Returns if a post is boosted, and allow unboost

Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
pull/462/head
Maxence Lange 2019-04-08 15:52:03 -01:00
rodzic 36f7b7290b
commit b4d773940c
18 zmienionych plików z 1180 dodań i 77 usunięć

Wyświetl plik

@ -72,6 +72,7 @@ return [
['name' => 'Local#postDelete', 'url' => '/api/v1/post', 'verb' => 'DELETE'],
['name' => 'Local#postBoost', 'url' => '/api/v1/post/boost', 'verb' => 'POST'],
['name' => 'Local#postUnboost', 'url' => '/api/v1/post/boost', 'verb' => 'DELETE'],
['name' => 'Local#actionFollow', 'url' => '/api/v1/current/follow', 'verb' => 'PUT'],
['name' => 'Local#actionUnfollow', 'url' => '/api/v1/current/follow', 'verb' => 'DELETE'],

Wyświetl plik

@ -30,10 +30,12 @@ declare(strict_types=1);
namespace OCA\Social\Command;
use Exception;
use OC\Core\Command\Base;
use OCA\Social\Service\AccountService;
use OCA\Social\Service\ActivityService;
use OCA\Social\Service\BoostService;
use OCA\Social\Service\ConfigService;
use OCA\Social\Service\CurlService;
use OCA\Social\Service\MiscService;
@ -44,6 +46,11 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class NoteBoost
*
* @package OCA\Social\Command
*/
class NoteBoost extends Base {
@ -59,6 +66,9 @@ class NoteBoost extends Base {
/** @var AccountService */
private $accountService;
/** @var BoostService */
private $boostService;
/** @var PostService */
private $postService;
@ -70,11 +80,12 @@ class NoteBoost extends Base {
/**
* NoteCreate constructor.
* NoteBoost constructor.
*
* @param ActivityService $activityService
* @param AccountService $accountService
* @param NoteService $noteService
* @param BoostService $boostService
* @param PostService $postService
* @param CurlService $curlService
* @param ConfigService $configService
@ -82,13 +93,14 @@ class NoteBoost extends Base {
*/
public function __construct(
ActivityService $activityService, AccountService $accountService,
NoteService $noteService, PostService $postService, CurlService $curlService,
ConfigService $configService, MiscService $miscService
NoteService $noteService, BoostService $boostService, PostService $postService,
CurlService $curlService, ConfigService $configService, MiscService $miscService
) {
parent::__construct();
$this->activityService = $activityService;
$this->noteService = $noteService;
$this->boostService = $boostService;
$this->accountService = $accountService;
$this->postService = $postService;
$this->curlService = $curlService;
@ -105,6 +117,7 @@ class NoteBoost extends Base {
$this->setName('social:note:boost')
->addArgument('userid', InputArgument::REQUIRED, 'userId of the author')
->addArgument('note', InputArgument::REQUIRED, 'Note to boost')
->addOption('unboost', '',InputOption::VALUE_NONE, 'Unboost')
->setDescription('Boost a note');
}
@ -121,7 +134,12 @@ class NoteBoost extends Base {
$actor = $this->accountService->getActorFromUserId($userId);
$this->noteService->setViewer($actor);
$token = $this->noteService->createBoost($actor, $noteId, $activity);
if ($input->getOption('unboost') === null) {
$activity = $this->boostService->create($actor, $noteId, $token);
} else {
$activity= $this->boostService->delete($actor, $noteId, $token );
}
echo 'object: ' . json_encode($activity, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
echo 'token: ' . $token . "\n";

Wyświetl plik

@ -30,6 +30,7 @@ declare(strict_types=1);
namespace OCA\Social\Command;
use Exception;
use OC\Core\Command\Base;
use OCA\Social\Model\Post;
@ -45,6 +46,11 @@ use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class NoteCreate
*
* @package OCA\Social\Command
*/
class NoteCreate extends Base {
@ -78,9 +84,8 @@ class NoteCreate extends Base {
* @param MiscService $miscService
*/
public function __construct(
ActivityService $activityService, AccountService $accountService,
PostService $postService, CurlService $curlService,
ConfigService $configService, MiscService $miscService
ActivityService $activityService, AccountService $accountService, PostService $postService,
CurlService $curlService, ConfigService $configService, MiscService $miscService
) {
parent::__construct();
@ -142,7 +147,7 @@ class NoteCreate extends Base {
$post->addTo(($to === null) ? '' : $to);
$post->setHashtags(($hashtag === null) ? [] : [$hashtag]);
$token = $this->postService->createPost($post, $activity);
$activity = $this->postService->createPost($post, $token);
echo 'object: ' . json_encode($activity, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
echo 'token: ' . $token . "\n";

Wyświetl plik

@ -41,6 +41,7 @@ use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityPub\Stream;
use OCA\Social\Model\Post;
use OCA\Social\Service\AccountService;
use OCA\Social\Service\BoostService;
use OCA\Social\Service\CacheActorService;
use OCA\Social\Service\DocumentService;
use OCA\Social\Service\FollowService;
@ -77,6 +78,9 @@ class LocalController extends Controller {
/** @var FollowService */
private $followService;
/** @var BoostService */
private $boostService;
/** @var PostService */
private $postService;
@ -111,6 +115,7 @@ class LocalController extends Controller {
* @param PostService $postService
* @param NoteService $noteService
* @param SearchService $searchService
* @param BoostService $boostService
* @param DocumentService $documentService
* @param MiscService $miscService
*/
@ -118,7 +123,7 @@ class LocalController extends Controller {
IRequest $request, $userId, AccountService $accountService,
CacheActorService $cacheActorService, FollowService $followService,
PostService $postService, NoteService $noteService, SearchService $searchService,
DocumentService $documentService, MiscService $miscService
BoostService $boostService, DocumentService $documentService, MiscService $miscService
) {
parent::__construct(Application::APP_NAME, $request);
@ -129,6 +134,7 @@ class LocalController extends Controller {
$this->searchService = $searchService;
$this->postService = $postService;
$this->followService = $followService;
$this->boostService = $boostService;
$this->documentService = $documentService;
$this->miscService = $miscService;
}
@ -156,7 +162,7 @@ class LocalController extends Controller {
$post->setHashtags($this->getArray('hashtags', $data, []));
/** @var ACore $activity */
$token = $this->postService->createPost($post, $activity);
$activity = $this->postService->createPost($post, $token);
return $this->success(
[
@ -208,8 +214,34 @@ class LocalController extends Controller {
public function postBoost(string $postId): DataResponse {
try {
$this->initViewer(true);
$announce = $this->boostService->create($this->viewer, $postId, $token);
$token = $this->noteService->createBoost($this->viewer, $postId, $announce);
return $this->success(
[
'boost' => $announce,
'token' => $token
]
);
} catch (Exception $e) {
return $this->fail($e);
}
}
/**
* Delete a boost.
*
* @NoAdminRequired
*
* @param string $postId
*
* @return DataResponse
*/
public function postUnboost(string $postId): DataResponse {
try {
$this->initViewer(true);
$token = $this->boostService->delete($this->viewer, $postId, $announce);
return $this->success(
[

Wyświetl plik

@ -36,10 +36,11 @@ use DateTime;
use Doctrine\DBAL\Query\QueryBuilder;
use Exception;
use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityPub\Object\Document;
use OCA\Social\Model\ActivityPub\Object\Follow;
use OCA\Social\Model\ActivityPub\Object\Image;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\StreamAction;
use OCA\Social\Service\ConfigService;
use OCA\Social\Service\MiscService;
use OCP\DB\QueryBuilder\IQueryBuilder;
@ -65,6 +66,8 @@ class CoreRequestBuilder {
const TABLE_CACHE_DOCUMENTS = 'social_cache_documents';
const TABLE_QUEUE_STREAM = 'social_queue_stream';
const TABLE_STREAM_ACTIONS = 'social_stream_actions';
/** @var IDBConnection */
@ -161,6 +164,28 @@ class CoreRequestBuilder {
}
/**
* Limit the request to the StreamId
*
* @param IQueryBuilder $qb
* @param string $streamId
*/
protected function limitToStreamId(IQueryBuilder &$qb, string $streamId) {
$this->limitToDBField($qb, 'user_id', $streamId, false);
}
/**
* Limit the request to the Type
*
* @param IQueryBuilder $qb
* @param string $type
*/
protected function limitToType(IQueryBuilder &$qb, string $type) {
$this->limitToDBField($qb, 'type', $type);
}
/**
* Limit the request to the Preferred Username
*
@ -410,6 +435,8 @@ class CoreRequestBuilder {
* @param IQueryBuilder $qb
* @param int $since
* @param int $limit
*
* @throws Exception
*/
protected function limitPaginate(IQueryBuilder &$qb, int $since = 0, int $limit = 5) {
if ($since > 0) {
@ -547,6 +574,8 @@ class CoreRequestBuilder {
* @param IQueryBuilder $qb
* @param int $timestamp
* @param string $field
*
* @throws Exception
*/
protected function limitToSince(IQueryBuilder $qb, int $timestamp, string $field) {
$dTime = new \DateTime();
@ -666,6 +695,65 @@ class CoreRequestBuilder {
}
/**
* @param IQueryBuilder $qb
*/
protected function leftJoinStreamAction(IQueryBuilder &$qb) {
if ($qb->getType() !== QueryBuilder::SELECT || $this->viewer === null) {
return;
}
$expr = $qb->expr();
$func = $qb->func();
$pf = $this->defaultSelectAlias;
$qb->selectAlias('sa.id', 'streamaction_id')
->selectAlias('sa.actor_id', 'streamaction_actor_id')
->selectAlias('sa.stream_id', 'streamaction_stream_id')
->selectAlias('sa.values', 'streamaction_values');
$andX = $expr->andX();
$andX->add($expr->eq($func->lower($pf . '.id'), $func->lower('sa.stream_id')));
$andX->add(
$expr->eq(
$func->lower('sa.actor_id'),
$qb->createNamedParameter(strtolower($this->viewer->getId()))
)
);
$qb->leftJoin(
$this->defaultSelectAlias, CoreRequestBuilder::TABLE_STREAM_ACTIONS, 'sa',
$andX
);
}
/**
* @param array $data
*
* @return StreamAction
* @throws InvalidResourceException
*/
protected function parseStreamActionsLeftJoin(array $data): StreamAction {
$new = [];
foreach ($data as $k => $v) {
if (substr($k, 0, 13) === 'streamaction_') {
$new[substr($k, 13)] = $v;
}
}
$action = new StreamAction();
$action->importFromDatabase($new);
if ($action->getId() === 0) {
throw new InvalidResourceException();
}
return $action;
}
/**
* @param IQueryBuilder $qb
* @param string $fieldDocumentId
@ -704,12 +792,12 @@ class CoreRequestBuilder {
*/
protected function parseCacheDocumentsLeftJoin(array $data): Document {
$new = [];
foreach ($data as $k => $v) {
if (substr($k, 0, 14) === 'cachedocument_') {
$new[substr($k, 14)] = $v;
}
}
$document = new Document();
$document->importFromDatabase($new);

Wyświetl plik

@ -33,6 +33,7 @@ namespace OCA\Social\Db;
use daita\MySmallPhpTools\Model\Cache;
use DateTime;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Exception;
use OCA\Social\Exceptions\NoteNotFoundException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Actor\Person;
@ -62,6 +63,8 @@ class NotesRequest extends NotesRequestBuilder {
/**
* @param Stream $stream
*
* @throws Exception
*/
public function save(Stream $stream) {
$qb = $this->saveStream($stream);
@ -103,10 +106,10 @@ class NotesRequest extends NotesRequestBuilder {
* @param string $id
* @param bool $asViewer
*
* @return Note
* @return Stream
* @throws NoteNotFoundException
*/
public function getNoteById(string $id, bool $asViewer = false): Note {
public function getNoteById(string $id, bool $asViewer = false): Stream {
if ($id === '') {
throw new NoteNotFoundException();
};
@ -116,6 +119,7 @@ class NotesRequest extends NotesRequestBuilder {
if ($asViewer) {
$this->limitToViewer($qb);
$this->leftJoinStreamAction($qb);
}
$cursor = $qb->execute();
@ -133,10 +137,10 @@ class NotesRequest extends NotesRequestBuilder {
/**
* @param string $id
*
* @return Note
* @return Stream
* @throws NoteNotFoundException
*/
public function getNoteByActivityId(string $id): Note {
public function getNoteByActivityId(string $id): Stream {
if ($id === '') {
throw new NoteNotFoundException();
};
@ -156,6 +160,37 @@ class NotesRequest extends NotesRequestBuilder {
}
/**
* @param Person $actor
* @param string $type
*
* @param string $objectId
*
* @return Stream
* @throws NoteNotFoundException
*/
public function getNoteByObjectId(Person $actor, string $type, string $objectId): Stream {
if ($objectId === '') {
throw new NoteNotFoundException('missing objectId');
};
$qb = $this->getNotesSelectSql();
$this->limitToObjectId($qb, $objectId);
$this->limitToType($qb, $type);
$this->limitToActorId($qb, $actor->getId());
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
if ($data === false) {
throw new NoteNotFoundException('Post not found');
}
return $this->parseNotesSelectSql($data);
}
/**
* @param string $actorId
*
@ -182,7 +217,8 @@ class NotesRequest extends NotesRequestBuilder {
* @param int $since
* @param int $limit
*
* @return array
* @return Stream[]
* @throws Exception
*/
public function getStreamHome(Person $actor, int $since = 0, int $limit = 5): array {
$qb = $this->getNotesSelectSql();
@ -190,6 +226,7 @@ class NotesRequest extends NotesRequestBuilder {
$this->joinFollowing($qb, $actor);
$this->limitPaginate($qb, $since, $limit);
$this->leftJoinCacheActors($qb, 'attributed_to');
$this->leftJoinStreamAction($qb);
$notes = [];
$cursor = $qb->execute();
@ -215,6 +252,7 @@ class NotesRequest extends NotesRequestBuilder {
* @param int $limit
*
* @return array
* @throws Exception
*/
public function getStreamNotifications(Person $actor, int $since = 0, int $limit = 5): array {
$qb = $this->getNotesSelectSql();
@ -244,6 +282,7 @@ class NotesRequest extends NotesRequestBuilder {
* @param int $limit
*
* @return array
* @throws Exception
*/
public function getStreamAccount(string $actorId, int $since = 0, int $limit = 5): array {
$qb = $this->getNotesSelectSql();
@ -273,6 +312,7 @@ class NotesRequest extends NotesRequestBuilder {
* @param int $limit
*
* @return array
* @throws Exception
*/
public function getStreamDirect(Person $actor, int $since = 0, int $limit = 5): array {
$qb = $this->getNotesSelectSql();
@ -304,6 +344,7 @@ class NotesRequest extends NotesRequestBuilder {
* @param bool $localOnly
*
* @return array
* @throws Exception
*/
public function getStreamTimeline(int $since = 0, int $limit = 5, bool $localOnly = true
): array {
@ -339,6 +380,7 @@ class NotesRequest extends NotesRequestBuilder {
* @param int $limit
*
* @return array
* @throws Exception
*/
public function getStreamTag(Person $actor, string $hashtag, int $since = 0, int $limit = 5
): array {
@ -369,6 +411,7 @@ class NotesRequest extends NotesRequestBuilder {
* @param int $since
*
* @return Note[]
* @throws Exception
*/
public function getNotesSince(int $since): array {
$qb = $this->getNotesSelectSql();
@ -413,6 +456,7 @@ class NotesRequest extends NotesRequestBuilder {
* @param Stream $note
*
* @return IQueryBuilder
* @throws Exception
*/
public function saveStream(Stream $note): IQueryBuilder {
$dTime = new DateTime();
@ -423,6 +467,12 @@ class NotesRequest extends NotesRequestBuilder {
$cache = json_encode($note->getCache(), JSON_UNESCAPED_SLASHES);
}
$attributedTo = $note->getAttributedTo();
if ($attributedTo === '' && $note->isLocal()) {
$attributedTo = $note->getActor()
->getId();
}
$qb = $this->getNotesInsertSql();
$qb->setValue('id', $qb->createNamedParameter($note->getId()))
->setValue('type', $qb->createNamedParameter($note->getType()))
@ -448,7 +498,7 @@ class NotesRequest extends NotesRequestBuilder {
->setValue(
'published_time', $qb->createNamedParameter($dTime, IQueryBuilder::PARAM_DATE)
)
->setValue('attributed_to', $qb->createNamedParameter($note->getAttributedTo()))
->setValue('attributed_to', $qb->createNamedParameter($attributedTo))
->setValue('in_reply_to', $qb->createNamedParameter($note->getInReplyTo()))
->setValue('source', $qb->createNamedParameter($note->getSource()))
->setValue('object_id', $qb->createNamedParameter($note->getObjectId()))

Wyświetl plik

@ -36,6 +36,7 @@ use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityPub\Object\Note;
use OCA\Social\Model\ActivityPub\Stream;
use OCA\Social\Model\InstancePath;
use OCP\DB\QueryBuilder\ICompositeExpression;
use OCP\DB\QueryBuilder\IQueryBuilder;
@ -353,9 +354,9 @@ class NotesRequestBuilder extends CoreRequestBuilder {
/**
* @param array $data
*
* @return Note
* @return Stream
*/
protected function parseNotesSelectSql($data): Note {
protected function parseNotesSelectSql($data): Stream {
$note = new Note();
$note->importFromDatabase($data);
@ -375,6 +376,12 @@ class NotesRequestBuilder extends CoreRequestBuilder {
} catch (InvalidResourceException $e) {
}
try {
$action = $this->parseStreamActionsLeftJoin($data);
$note->setAction($action);
} catch (InvalidResourceException $e) {
}
return $note;
}

Wyświetl plik

@ -0,0 +1,118 @@
<?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 OCA\Social\Exceptions\StreamActionDoesNotExistException;
use OCA\Social\Model\StreamAction;
/**
* Class StreamActionsRequest
*
* @package OCA\Social\Db
*/
class StreamActionsRequest extends StreamActionsRequestBuilder {
/**
* create a new Queue in the database.
*
* @param StreamAction $action
*/
public function create(StreamAction $action) {
$qb = $this->getStreamActionInsertSql();
$qb->setValue('actor_id', $qb->createNamedParameter($action->getActorId()))
->setValue('stream_id', $qb->createNamedParameter($action->getStreamId()))
->setValue(
'values', $qb->createNamedParameter(
json_encode($action->getValues(), JSON_UNESCAPED_SLASHES)
)
);
$qb->execute();
}
/**
* create a new Queue in the database.
*
* @param StreamAction $action
*/
public function update(StreamAction $action) {
$qb = $this->getStreamActionUpdateSql();
$values = json_encode($action->getValues(), JSON_UNESCAPED_SLASHES);
$qb->set('values', $qb->createNamedParameter($values));
$this->limitToActorId($qb, $action->getActorId());
$this->limitToStreamId($qb, $action->getStreamId());
$qb->execute();
}
/**
* @param string $actorId
* @param string $streamId
*
* @return StreamAction
* @throws StreamActionDoesNotExistException
*/
public function getAction(string $actorId, string $streamId): StreamAction {
$qb = $this->getStreamActionDeleteSql();
$this->limitToActorId($qb, $actorId);
$this->limitToStreamId($qb, $streamId);
$cursor = $qb->execute();
$data = $cursor->fetch();
if ($data === false) {
throw new StreamActionDoesNotExistException();
}
$cursor->closeCursor();
return $this->parseStreamActionsSelectSql($data);
}
/**
* @param StreamAction $action
*/
public function delete(StreamAction $action) {
$qb = $this->getStreamActionDeleteSql();
$this->limitToActorId($qb, $action->getActorId());
$this->limitToStreamId($qb, $action->getStreamId());
$qb->execute();
}
}

Wyświetl plik

@ -0,0 +1,120 @@
<?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 OCA\Social\Model\RequestQueue;
use OCA\Social\Model\StreamAction;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* Class StreamActionsRequestBuilder
*
* @package OCA\Social\Db
*/
class StreamActionsRequestBuilder extends CoreRequestBuilder {
use TArrayTools;
/**
* Base of the Sql Insert request
*
* @return IQueryBuilder
*/
protected function getStreamActionInsertSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::TABLE_STREAM_ACTIONS);
return $qb;
}
/**
* Base of the Sql Update request
*
* @return IQueryBuilder
*/
protected function getStreamActionUpdateSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->update(self::TABLE_STREAM_ACTIONS);
return $qb;
}
/**
* Base of the Sql Select request for Shares
*
* @return IQueryBuilder
*/
protected function getStreamActionSelectSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
/** @noinspection PhpMethodParametersCountMismatchInspection */
$qb->select('sa.id', 'sa.actor_id', 'sa.stream_id', 'sa.values')
->from(self::TABLE_STREAM_ACTIONS, 'sa');
$this->defaultSelectAlias = 'sa';
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return IQueryBuilder
*/
protected function getStreamActionDeleteSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::TABLE_STREAM_ACTIONS);
return $qb;
}
/**
* @param array $data
*
* @return StreamAction
*/
protected function parseStreamActionsSelectSql($data): StreamAction {
$action = new StreamAction();
$action->importFromDatabase($data);
return $action;
}
}

Wyświetl plik

@ -0,0 +1,8 @@
<?php
namespace OCA\Social\Exceptions;
class StreamActionDoesNotExistException extends \Exception {
}

Wyświetl plik

@ -31,12 +31,14 @@ declare(strict_types=1);
namespace OCA\Social\Interfaces\Object;
use Exception;
use OCA\Social\Db\NotesRequest;
use OCA\Social\Exceptions\InvalidOriginException;
use OCA\Social\Exceptions\ItemNotFoundException;
use OCA\Social\Exceptions\NoteNotFoundException;
use OCA\Social\Interfaces\IActivityPubInterface;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Activity\Undo;
use OCA\Social\Model\ActivityPub\Object\Announce;
use OCA\Social\Model\ActivityPub\Stream;
use OCA\Social\Model\StreamQueue;
@ -81,11 +83,16 @@ class AnnounceInterface implements IActivityPubInterface {
/**
* @param ACore $activity
* @param ACore $item
*
* @throws InvalidOriginException
*/
public function activity(Acore $activity, ACore $item) {
/** Stream $item */
// TODO: Manage Undo Activity
$this->miscService->log('activity: ' . json_encode($activity));
$item->checkOrigin($activity->getId());
if ($activity->getType() === Undo::TYPE) {
$item->checkOrigin($item->getId());
$this->delete($item);
}
}
@ -93,6 +100,7 @@ class AnnounceInterface implements IActivityPubInterface {
* @param ACore $item
*
* @throws InvalidOriginException
* @throws Exception
*/
public function processIncomingRequest(ACore $item) {
/** @var Stream $item */
@ -119,8 +127,11 @@ class AnnounceInterface implements IActivityPubInterface {
throw new ItemNotFoundException();
}
/**
* @param ACore $item
*
* @throws Exception
*/
public function save(ACore $item) {
/** @var Announce $item */
@ -142,8 +153,14 @@ class AnnounceInterface implements IActivityPubInterface {
* @param ACore $item
*/
public function delete(ACore $item) {
try {
$stream = $this->notesRequest->getNoteById($item->getId());
if ($stream->getType() === Announce::TYPE) {
$this->notesRequest->deleteNoteById($item->getId());
}
} catch (NoteNotFoundException $e) {
}
}
}

Wyświetl plik

@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Migration;
use Closure;
use Doctrine\DBAL\Types\Type;
use OCP\DB\ISchemaWrapper;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
/**
* Class Version0002Date20190313133046
*
* @package OCA\Social\Migration
*/
class Version0002Date20190313133046 extends SimpleMigrationStep {
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*
* @return null|ISchemaWrapper
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
if (!$schema->hasTable('social_stream_actions')) {
$table = $schema->createTable('social_stream_actions');
$table->addColumn(
'id', Type::INTEGER, [
'autoincrement' => true,
'notnull' => true,
'length' => 11,
'unsigned' => true
]
);
$table->addColumn(
'actor_id', 'string', [
'notnull' => true,
'length' => 127,
]
);
$table->addColumn(
'stream_id', 'string', [
'notnull' => true,
'length' => 1000,
]
);
$table->addColumn(
'values', Type::TEXT, [
'notnull' => false
]
);
$table->setPrimaryKey(['id']);
}
return $schema;
}
}

Wyświetl plik

@ -33,7 +33,9 @@ namespace OCA\Social\Model\ActivityPub;
use daita\MySmallPhpTools\Model\Cache;
use daita\MySmallPhpTools\Model\CacheItem;
use DateTime;
use Exception;
use JsonSerializable;
use OCA\Social\Model\StreamAction;
class Stream extends ACore implements JsonSerializable {
@ -69,6 +71,9 @@ class Stream extends ACore implements JsonSerializable {
/** @var int */
private $publishedTime = 0;
/** @var StreamAction */
private $action = null;
public function __construct($parent = null) {
parent::__construct($parent);
@ -208,6 +213,7 @@ class Stream extends ACore implements JsonSerializable {
/**
*
* @throws Exception
*/
public function convertPublished() {
$dTime = new DateTime($this->getPublished());
@ -255,6 +261,37 @@ class Stream extends ACore implements JsonSerializable {
}
/**
* @return StreamAction
*/
public function getAction(): StreamAction {
return $this->action;
}
/**
* @param StreamAction $action
*
* @return Stream
*/
public function setAction(StreamAction $action): Stream {
$this->action = $action;
return $this;
}
/**
* @return bool
*/
public function hasAction(): bool {
return ($this->action !== null);
}
/**
* @param array $data
*
* @throws Exception
*/
public function import(array $data) {
parent::import($data);
@ -270,6 +307,8 @@ class Stream extends ACore implements JsonSerializable {
/**
* @param array $data
*
* @throws Exception
*/
public function importFromDatabase(array $data) {
parent::importFromDatabase($data);
@ -310,6 +349,7 @@ class Stream extends ACore implements JsonSerializable {
$result = array_merge(
$result,
[
'action' => ($this->hasAction()) ? $this->getAction() : [],
'cache' => ($this->gotCache()) ? $this->getCache() : '',
'publishedTime' => $this->getPublishedTime()
]

Wyświetl plik

@ -0,0 +1,214 @@
<?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;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TStringTools;
use JsonSerializable;
/**
* Class StreamAction
*
* @package OCA\Social\Model
*/
class StreamAction implements JsonSerializable {
use TArrayTools;
use TStringTools;
/** @var integer */
private $id = 0;
/** @var string */
private $actorId = '';
/** @var string */
private $streamId = '';
/** @var array */
private $values = [];
/**
* StreamAction constructor.
*/
public function __construct() {
}
/**
* @return int
*/
public function getId(): int {
return $this->id;
}
/**
* @param int $id
*
* @return StreamAction
*/
public function setId(int $id): StreamAction {
$this->id = $id;
return $this;
}
/**
* @return string
*/
public function getActorId(): string {
return $this->actorId;
}
/**
* @param string $actorId
*
* @return StreamAction
*/
public function setActorId(string $actorId): StreamAction {
$this->actorId = $actorId;
return $this;
}
/**
* @return string
*/
public function getStreamId(): string {
return $this->streamId;
}
/**
* @param string $streamId
*
* @return StreamAction
*/
public function setStreamId(string $streamId): StreamAction {
$this->streamId = $streamId;
return $this;
}
/**
* @param string $key
* @param string $value
*/
public function updateValue(string $key, string $value) {
$this->values[$key] = $value;
}
/**
* @param string $key
* @param int $value
*/
public function updateValueInt(string $key, int $value) {
$this->values[$key] = $value;
}
/**
* @param string $key
*
* @return bool
*/
public function hasValue(string $key): bool {
return (array_key_exists($key, $this->values));
}
/**
* @param string $key
*
* @return string
*/
public function getValue(string $key): string {
return $this->values[$key];
}
/**
* @param string $key
*
* @return int
*/
public function getValueInt(string $key): int {
return $this->values[$key];
}
/**
* @return array
*/
public function getValues(): array {
return $this->values;
}
/**
* @param array $values
*
* @return StreamAction
*/
public function setValues(array $values): StreamAction {
$this->values = $values;
return $this;
}
/**
* @param array $data
*/
public function importFromDatabase(array $data) {
$this->setId($this->getInt('id', $data, 0));
$this->setActorId($this->get('actor_id', $data, ''));
$this->setStreamId($this->get('stream_id', $data, ''));
$this->setValues($this->getArray('values', $data, []));
}
/**
* @return array
*/
public function jsonSerialize(): array {
return [
'id' => $this->getId(),
'actorId' => $this->getActorId(),
'streamId' => $this->getStreamId(),
'values' => $this->getValues(),
];
}
}

Wyświetl plik

@ -0,0 +1,192 @@
<?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 Exception;
use OCA\Social\Db\NotesRequest;
use OCA\Social\Exceptions\NoteNotFoundException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Activity\Undo;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityPub\Object\Announce;
use OCA\Social\Model\ActivityPub\Object\Note;
use OCA\Social\Model\ActivityPub\Stream;
/**
* Class BoostService
*
* @package OCA\Social\Service
*/
class BoostService {
/** @var NotesRequest */
private $notesRequest;
/** @var NoteService */
private $noteService;
/** @var SignatureService */
private $signatureService;
/** @var ActivityService */
private $activityService;
/** @var StreamActionService */
private $streamActionService;
/** @var StreamQueueService */
private $streamQueueService;
/** @var MiscService */
private $miscService;
/**
* BoostService constructor.
*
* @param NotesRequest $notesRequest
* @param NoteService $noteService
* @param SignatureService $signatureService
* @param ActivityService $activityService
* @param StreamActionService $streamActionService
* @param StreamQueueService $streamQueueService
* @param MiscService $miscService
*/
public function __construct(
NotesRequest $notesRequest, NoteService $noteService, SignatureService $signatureService,
ActivityService $activityService, StreamActionService $streamActionService,
StreamQueueService $streamQueueService, MiscService $miscService
) {
$this->notesRequest = $notesRequest;
$this->noteService = $noteService;
$this->signatureService = $signatureService;
$this->activityService = $activityService;
$this->streamActionService = $streamActionService;
$this->streamQueueService = $streamQueueService;
$this->miscService = $miscService;
}
/**
* @param Person $actor
* @param string $postId
* @param string $token
*
* @return ACore
* @throws NoteNotFoundException
* @throws SocialAppConfigException
*/
public function create(Person $actor, string $postId, string &$token = ''): ACore {
try {
return $this->get($actor, $postId);
} catch (NoteNotFoundException $e) {
}
$announce = new Announce();
$this->noteService->assignItem($announce, $actor, Stream::TYPE_PUBLIC);
$announce->setActor($actor);
$note = $this->noteService->getNoteById($postId, true);
if ($note->getType() !== Note::TYPE) {
throw new NoteNotFoundException('Stream is not a Note');
}
$announce->addCc($note->getAttributedTo());
if ($note->isLocal()) {
$announce->setObject($note);
} else {
$announce->setObjectId($note->getId());
$announce->addCacheItem($note->getId());
}
$this->notesRequest->save($announce);
$this->streamActionService->setActionBool($actor->getActorId(), $postId, 'boosted', true);
$this->signatureService->signObject($actor, $announce);
$token = $this->activityService->request($announce);
$this->streamQueueService->cacheStreamByToken($token);
return $announce;
}
/**
* @param Person $actor
* @param string $postId
*
* @return Stream
* @throws NoteNotFoundException
*/
public function get(Person $actor, string $postId): Stream {
$stream = $this->notesRequest->getNoteByObjectId($actor, Announce::TYPE, $postId);
return $stream;
}
/**
* @param Person $actor
* @param string $postId
* @param ACore $undo
*
* @return string
* @throws SocialAppConfigException
* @throws NoteNotFoundException
*/
public function delete(Person $actor, string $postId, ACore &$undo = null): string {
$undo = new Undo();
$this->noteService->assignItem($undo, $actor, Stream::TYPE_PUBLIC);
$undo->setActor($actor);
$note = $this->noteService->getNoteById($postId, true);
if ($note->getType() !== Note::TYPE) {
throw new NoteNotFoundException('Stream is not a Note');
}
$announce = $this->notesRequest->getNoteByObjectId($actor, Announce::TYPE, $postId);
$undo->setObject($announce);
$undo->setCcArray($announce->getCcArray());
$this->notesRequest->deleteNoteById($announce->getId());
$this->streamActionService->setActionBool($actor->getActorId(), $postId, 'boosted', false);
$this->signatureService->signObject($actor, $undo);
$token = $this->activityService->request($undo);
return $token;
}
}

Wyświetl plik

@ -46,7 +46,6 @@ use OCA\Social\Exceptions\RequestServerException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityPub\Object\Announce;
use OCA\Social\Model\ActivityPub\Object\Note;
use OCA\Social\Model\ActivityPub\Stream;
use OCA\Social\Model\InstancePath;
@ -122,66 +121,42 @@ class NoteService {
/**
* @param Person $actor
* @param string $postId
* @param ACore|null $announce
*
* @return string
* @throws NoteNotFoundException
* @throws SocialAppConfigException
* @throws Exception
*/
public function createBoost(Person $actor, string $postId, ACore &$announce = null): string {
$announce = new Announce();
$this->assignStream($announce, $actor, Stream::TYPE_PUBLIC);
$announce->setActor($actor);
$note = $this->getNoteById($postId, true);
if ($note->getType() !== Note::TYPE) {
throw new NoteNotFoundException('Stream is not a Note');
}
$announce->addCc($note->getAttributedTo());
if ($note->isLocal()) {
$announce->setObject($note);
} else {
$announce->setObjectId($note->getId());
$announce->addCacheItem($note->getId());
}
$this->signatureService->signObject($actor, $announce);
$token = $this->activityService->request($announce);
$this->streamQueueService->cacheStreamByToken($token);
return $token;
}
/**
* @param Stream $stream
* @param ACore $stream
* @param Person $actor
* @param string $type
*
* @throws SocialAppConfigException
* @throws Exception
*/
public function assignStream(Stream &$stream, Person $actor, string $type) {
public function assignItem(Acore &$stream, Person $actor, string $type) {
$stream->setId($this->configService->generateId('@' . $actor->getPreferredUsername()));
$stream->setPublished(date("c"));
$this->setRecipient($stream, $actor, $type);
$stream->convertPublished();
$stream->setLocal(true);
if ($stream instanceof Stream) {
$this->assignStream($stream);
}
}
/**
* @param Stream $stream
*
* @throws Exception
*/
public function assignStream(Stream &$stream) {
$stream->convertPublished();
}
/**
* @param ACore $stream
* @param Person $actor
* @param string $type
*/
private function setRecipient(Stream $stream, Person $actor, string $type) {
private function setRecipient(ACore $stream, Person $actor, string $type) {
switch ($type) {
case Note::TYPE_UNLISTED:
$stream->setTo($actor->getFollowers());
@ -370,6 +345,7 @@ class NoteService {
* @param int $limit
*
* @return Note[]
* @throws Exception
*/
public function getStreamHome(Person $actor, int $since = 0, int $limit = 5): array {
return $this->notesRequest->getStreamHome($actor, $since, $limit);
@ -382,6 +358,7 @@ class NoteService {
* @param int $limit
*
* @return Note[]
* @throws Exception
*/
public function getStreamNotifications(Person $actor, int $since = 0, int $limit = 5): array {
return $this->notesRequest->getStreamNotifications($actor, $since, $limit);
@ -394,6 +371,7 @@ class NoteService {
* @param int $limit
*
* @return Note[]
* @throws Exception
*/
public function getStreamAccount(string $actorId, int $since = 0, int $limit = 5): array {
return $this->notesRequest->getStreamAccount($actorId, $since, $limit);
@ -406,6 +384,7 @@ class NoteService {
* @param int $limit
*
* @return Note[]
* @throws Exception
*/
public function getStreamDirect(Person $actor, int $since = 0, int $limit = 5): array {
return $this->notesRequest->getStreamDirect($actor, $since, $limit);
@ -417,6 +396,7 @@ class NoteService {
* @param int $limit
*
* @return Note[]
* @throws Exception
*/
public function getStreamLocalTimeline(int $since = 0, int $limit = 5): array {
return $this->notesRequest->getStreamTimeline($since, $limit, true);
@ -430,6 +410,7 @@ class NoteService {
* @param int $limit
*
* @return Note[]
* @throws Exception
*/
public function getStreamLocalTag(Person $actor, string $hashtag, int $since = 0, int $limit = 5
): array {
@ -455,6 +436,7 @@ class NoteService {
* @param int $limit
*
* @return Note[]
* @throws Exception
*/
public function getStreamGlobalTimeline(int $since = 0, int $limit = 5): array {
return $this->notesRequest->getStreamTimeline($since, $limit, false);

Wyświetl plik

@ -88,26 +88,26 @@ class PostService {
/**
* @param Post $post
* @param ACore $activity
* @param string $token
*
* @return string
* @throws SocialAppConfigException
* @return ACore
* @throws InvalidOriginException
* @throws InvalidResourceException
* @throws ItemUnknownException
* @throws MalformedArrayException
* @throws NoteNotFoundException
* @throws RedundancyLimitException
* @throws RequestContentException
* @throws RequestNetworkException
* @throws RequestResultNotJsonException
* @throws RequestResultSizeException
* @throws RequestServerException
* @throws MalformedArrayException
* @throws RequestResultNotJsonException
* @throws SocialAppConfigException
*/
public function createPost(Post $post, ACore &$activity = null): string {
public function createPost(Post $post, string &$token = ''): ACore {
$note = new Note();
$actor = $post->getActor();
$this->noteService->assignStream($note, $actor, $post->getType());
$this->noteService->assignItem($note, $actor, $post->getType());
$note->setAttributedTo(
$this->configService->getUrlSocial() . '@' . $actor->getPreferredUsername()
@ -119,10 +119,10 @@ class PostService {
$this->noteService->addRecipients($note, $post->getType(), $post->getTo());
$this->noteService->addHashtags($note, $post->getHashtags());
$result = $this->activityService->createActivity($actor, $note, $activity);
$token = $this->activityService->createActivity($actor, $note, $activity);
$this->accountService->cacheLocalActorDetailCount($actor);
return $result;
return $activity;
}

Wyświetl plik

@ -0,0 +1,117 @@
<?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\Exceptions\MalformedArrayException;
use Exception;
use OCA\Social\Db\NotesRequest;
use OCA\Social\Db\StreamActionsRequest;
use OCA\Social\Exceptions\InvalidOriginException;
use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Exceptions\ItemUnknownException;
use OCA\Social\Exceptions\NoteNotFoundException;
use OCA\Social\Exceptions\RedundancyLimitException;
use OCA\Social\Exceptions\RequestContentException;
use OCA\Social\Exceptions\RequestNetworkException;
use OCA\Social\Exceptions\RequestResultNotJsonException;
use OCA\Social\Exceptions\RequestResultSizeException;
use OCA\Social\Exceptions\RequestServerException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\ActivityPub\Actor\Person;
use OCA\Social\Model\ActivityPub\Object\Announce;
use OCA\Social\Model\ActivityPub\Object\Note;
use OCA\Social\Model\ActivityPub\Stream;
use OCA\Social\Model\InstancePath;
/**
* Class StreamActionService
*
* @package OCA\Social\Service
*/
class StreamActionService {
/** @var StreamActionsRequest */
private $streamActionsRequest;
/** @var MiscService */
private $miscService;
/**
* StreamActionService constructor.
*
* @param StreamActionsRequest $streamActionsRequest
* @param MiscService $miscService
*/
public function __construct(StreamActionsRequest $streamActionsRequest, MiscService $miscService
) {
$this->streamActionsRequest = $streamActionsRequest;
$this->miscService = $miscService;
}
/**
* @param string $actorId
* @param string $streamId
* @param string $key
* @param string $value
*/
public function setAction(string $actorId, string $streamId, string $key, string $value) {
}
/**
* @param string $actorId
* @param string $streamId
* @param string $key
* @param int $value
*/
public function setActionInt(string $actorId, string $streamId, string $key, int $value) {
}
/**
* @param string $actorId
* @param string $streamId
* @param string $key
* @param bool $value
*/
public function setActionBool(string $actorId, string $streamId, string $key, bool $value) {
}
}