From 88ebb11dbd499361e4bbe0fdd5b729aaa4fb68ed Mon Sep 17 00:00:00 2001
From: Maxence Lange <maxence@artificial-owl.com>
Date: Mon, 12 Nov 2018 21:55:39 -0100
Subject: [PATCH] rework database

Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
---
 appinfo/database.xml                 | 169 +++++++++++++++++++--------
 lib/Db/ActorsRequest.php             |  26 +++--
 lib/Db/ActorsRequestBuilder.php      |  32 +++--
 lib/Db/CacheActorsRequest.php        |  79 +++++++------
 lib/Db/CacheActorsRequestBuilder.php |  18 +--
 lib/Db/CoreRequestBuilder.php        |  30 ++++-
 lib/Db/FollowsRequest.php            |  93 +++++++++++++++
 lib/Db/FollowsRequestBuilder.php     | 122 +++++++++++++++++++
 lib/Db/NotesRequest.php              |  61 +++++++++-
 lib/Db/NotesRequestBuilder.php       |  33 +++++-
 10 files changed, 530 insertions(+), 133 deletions(-)
 create mode 100644 lib/Db/FollowsRequest.php
 create mode 100644 lib/Db/FollowsRequestBuilder.php

diff --git a/appinfo/database.xml b/appinfo/database.xml
index 5d750abb..7c99fbc5 100644
--- a/appinfo/database.xml
+++ b/appinfo/database.xml
@@ -5,59 +5,16 @@
 	<overwrite>false</overwrite>
 	<charset>utf8</charset>
 
-	<table>
-		<name>*dbprefix*social_accounts</name>
-		<declaration>
-
-			<field>
-				<name>id</name>
-				<type>integer</type>
-				<length>7</length>
-				<unsigned>true</unsigned>
-				<notnull>true</notnull>
-				<autoincrement>true</autoincrement>
-				<primary>true</primary>
-			</field>
-
-			<field>
-				<name>user_id</name>
-				<type>text</type>
-				<length>63</length>
-			</field>
-
-			<field>
-				<name>account</name>
-				<type>text</type>
-				<length>63</length>
-			</field>
-
-			<field>
-				<name>creation</name>
-				<type>timestamp</type>
-			</field>
-
-		</declaration>
-	</table>
-
 	<table>
 		<name>*dbprefix*social_server_actors</name>
 		<declaration>
 
 			<field>
 				<name>id</name>
-				<type>integer</type>
-				<length>7</length>
-				<unsigned>true</unsigned>
-				<notnull>true</notnull>
-				<autoincrement>true</autoincrement>
-				<primary>true</primary>
-			</field>
-
-			<field>
-				<name>type</name>
 				<type>text</type>
-				<length>15</length>
+				<length>127</length>
 				<notnull>true</notnull>
+				<primary>true</primary>
 			</field>
 
 			<field>
@@ -74,6 +31,20 @@
 				<notnull>true</notnull>
 			</field>
 
+			<field>
+				<name>name</name>
+				<type>text</type>
+				<length>127</length>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>summary</name>
+				<type>text</type>
+				<length>500</length>
+				<notnull>true</notnull>
+			</field>
+
 			<field>
 				<name>public_key</name>
 				<type>text</type>
@@ -94,6 +65,40 @@
 		</declaration>
 	</table>
 
+	<table>
+		<name>*dbprefix*social_server_follows</name>
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<type>text</type>
+				<length>127</length>
+				<notnull>true</notnull>
+				<primary>true</primary>
+			</field>
+
+			<field>
+				<name>actor_id</name>
+				<type>text</type>
+				<length>127</length>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>object_id</name>
+				<type>text</type>
+				<length>127</length>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>creation</name>
+				<type>timestamp</type>
+			</field>
+
+		</declaration>
+	</table>
+
 	<table>
 		<name>*dbprefix*social_server_activities</name>
 		<declaration>
@@ -131,6 +136,7 @@
 				<type>text</type>
 				<length>127</length>
 				<notnull>true</notnull>
+				<primary>true</primary>
 			</field>
 
 			<!--<field>-->
@@ -215,11 +221,9 @@
 
 			<field>
 				<name>id</name>
-				<type>integer</type>
-				<length>8</length>
-				<unsigned>true</unsigned>
+				<type>string</type>
+				<length>127</length>
 				<notnull>true</notnull>
-				<autoincrement>true</autoincrement>
 				<primary>true</primary>
 			</field>
 
@@ -230,6 +234,48 @@
 				<notnull>true</notnull>
 			</field>
 
+			<field>
+				<name>following</name>
+				<type>text</type>
+				<length>127</length>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>followers</name>
+				<type>text</type>
+				<length>127</length>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>inbox</name>
+				<type>text</type>
+				<length>127</length>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>shared_inbox</name>
+				<type>text</type>
+				<length>127</length>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>outbox</name>
+				<type>text</type>
+				<length>127</length>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>featured</name>
+				<type>text</type>
+				<length>127</length>
+				<notnull>true</notnull>
+			</field>
+
 			<field>
 				<name>url</name>
 				<type>text</type>
@@ -238,9 +284,30 @@
 			</field>
 
 			<field>
-				<name>actor</name>
+				<name>preferred_username</name>
 				<type>text</type>
-				<length>8000</length>
+				<length>127</length>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>name</name>
+				<type>text</type>
+				<length>127</length>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>summary</name>
+				<type>text</type>
+				<length>500</length>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>public_key</name>
+				<type>text</type>
+				<length>500</length>
 				<notnull>true</notnull>
 			</field>
 
diff --git a/lib/Db/ActorsRequest.php b/lib/Db/ActorsRequest.php
index 46e30187..9cfdc578 100644
--- a/lib/Db/ActorsRequest.php
+++ b/lib/Db/ActorsRequest.php
@@ -31,7 +31,7 @@ namespace OCA\Social\Db;
 
 
 use OCA\Social\Exceptions\ActorDoesNotExistException;
-use OCA\Social\Model\ActivityPub\Actor;
+use OCA\Social\Model\ActivityPub\Person;
 use OCA\Social\Service\ConfigService;
 use OCA\Social\Service\MiscService;
 use OCP\IDBConnection;
@@ -54,19 +54,25 @@ class ActorsRequest extends ActorsRequestBuilder {
 
 
 	/**
-	 * create a new Actor in the database.
+	 * create a new Person in the database.
 	 *
-	 * @param Actor $actor
+	 * @param Person $actor
 	 *
 	 * @return int
 	 * @throws \Exception
 	 */
-	public function create(Actor $actor): int {
+	public function create(Person $actor) {
+
+		$id = $this->configService->getRoot() . '@' . $actor->getPreferredUsername();
 
 		try {
 			$qb = $this->getActorsInsertSql();
-			$qb->setValue('type', $qb->createNamedParameter($actor->getType()))
+
+			$qb->setValue('id', $qb->createNamedParameter($id))
+//			   ->setValue('type', $qb->createNamedParameter($actor->getType()))
 			   ->setValue('user_id', $qb->createNamedParameter($actor->getUserId()))
+			   ->setValue('name', $qb->createNamedParameter($actor->getName()))
+			   ->setValue('summary', $qb->createNamedParameter($actor->getSummary()))
 			   ->setValue(
 				   'preferred_username', $qb->createNamedParameter($actor->getPreferredUsername())
 			   )
@@ -74,8 +80,6 @@ class ActorsRequest extends ActorsRequestBuilder {
 			   ->setValue('private_key', $qb->createNamedParameter($actor->getPrivateKey()));
 
 			$qb->execute();
-
-			return $qb->getLastInsertId();
 		} catch (\Exception $e) {
 			throw $e;
 		}
@@ -87,10 +91,10 @@ class ActorsRequest extends ActorsRequestBuilder {
 	 *
 	 * @param string $username
 	 *
-	 * @return Actor
+	 * @return Person
 	 * @throws ActorDoesNotExistException
 	 */
-	public function getFromUsername(string $username): Actor {
+	public function getFromUsername(string $username): Person {
 		$qb = $this->getActorsSelectSql();
 		$this->limitToPreferredUsername($qb, $username);
 
@@ -111,10 +115,10 @@ class ActorsRequest extends ActorsRequestBuilder {
 	 *
 	 * @param string $userId
 	 *
-	 * @return Actor
+	 * @return Person
 	 * @throws ActorDoesNotExistException
 	 */
-	public function getFromUserId(string $userId): Actor {
+	public function getFromUserId(string $userId): Person {
 		$qb = $this->getActorsSelectSql();
 		$this->limitToUserId($qb, $userId);
 
diff --git a/lib/Db/ActorsRequestBuilder.php b/lib/Db/ActorsRequestBuilder.php
index 4f480817..f19b44f5 100644
--- a/lib/Db/ActorsRequestBuilder.php
+++ b/lib/Db/ActorsRequestBuilder.php
@@ -31,7 +31,7 @@ namespace OCA\Social\Db;
 
 
 use daita\MySmallPhpTools\Traits\TArrayTools;
-use OCA\Social\Model\ActivityPub\Actor;
+use OCA\Social\Model\ActivityPub\Person;
 use OCP\DB\QueryBuilder\IQueryBuilder;
 
 class ActorsRequestBuilder extends CoreRequestBuilder {
@@ -76,7 +76,7 @@ class ActorsRequestBuilder extends CoreRequestBuilder {
 
 		/** @noinspection PhpMethodParametersCountMismatchInspection */
 		$qb->select(
-			'sa.id', 'sa.type', 'sa.user_id', 'sa.preferred_username', 'sa.public_key',
+			'sa.id', 'sa.user_id', 'sa.preferred_username', 'sa.name', 'sa.summary', 'sa.public_key',
 			'sa.private_key', 'sa.creation'
 		)
 		   ->from(self::TABLE_SERVER_ACTORS, 'sa');
@@ -103,24 +103,18 @@ class ActorsRequestBuilder extends CoreRequestBuilder {
 	/**
 	 * @param array $data
 	 *
-	 * @return Actor
+	 * @return Person
 	 */
-	protected function parseActorsSelectSql($data): Actor {
-		$id = $this->configService->getRoot() . '@' . $data['preferred_username'];
-		$actor = new Actor();
-		$actor->setId($id)
-			  ->setType($this->get('type', $data, ''))
-			  ->setRoot($this->configService->getRoot());
-		$actor->setUserId($data['user_id'])
-			  ->setPreferredUsername($data['preferred_username'])
-			  ->setPublicKey($data['public_key'])
-			  ->setPrivateKey($data['private_key'])
-			  ->setInbox($id . '/inbox')
-			  ->setOutbox($id . '/outbox')
-			  ->setFollowers($id . '/followers')
-			  ->setFollowing($id . '/following')
-			  ->setSharedInbox($this->configService->getRoot() . 'inbox')
-			  ->setCreation($this->getInt('creation', $data, 0));
+	protected function parseActorsSelectSql($data): Person {
+		$root = $this->configService->getRoot();
+
+		$actor = new Person();
+		$actor->import($data);
+		$actor->setInbox($actor->getId() . '/inbox')
+			  ->setOutbox($actor->getId() . '/outbox')
+			  ->setFollowers($actor->getId() . '/followers')
+			  ->setFollowing($actor->getId() . '/following')
+			  ->setSharedInbox($root . 'inbox');
 
 		return $actor;
 	}
diff --git a/lib/Db/CacheActorsRequest.php b/lib/Db/CacheActorsRequest.php
index afb60261..0c730160 100644
--- a/lib/Db/CacheActorsRequest.php
+++ b/lib/Db/CacheActorsRequest.php
@@ -31,8 +31,8 @@ namespace OCA\Social\Db;
 
 
 use OCA\Social\Exceptions\CacheActorDoesNotExistException;
-use OCA\Social\Model\ActivityPub\Actor;
 use OCA\Social\Model\ActivityPub\Cache\CacheActor;
+use OCA\Social\Model\ActivityPub\Person;
 use OCA\Social\Service\ConfigService;
 use OCA\Social\Service\MiscService;
 use OCP\IDBConnection;
@@ -57,19 +57,30 @@ class CacheActorsRequest extends CacheActorsRequestBuilder {
 	/**
 	 * insert cache about an Actor in database.
 	 *
-	 * @param Actor $actor
-	 * @param array $object
+	 * @param Person $actor
 	 *
 	 * @return int
 	 * @throws \Exception
 	 */
-	public function create(Actor $actor, array $object): int {
+	public function save(Person $actor): int {
 
 		try {
 			$qb = $this->getCacheActorsInsertSql();
-			$qb->setValue('account', $qb->createNamedParameter($actor->getAccount()))
-			   ->setValue('url', $qb->createNamedParameter($actor->getId()))
-			   ->setValue('actor', $qb->createNamedParameter(json_encode($object)));
+			$qb->setValue('id', $qb->createNamedParameter($actor->getId()))
+			   ->setValue('account', $qb->createNamedParameter($actor->getAccount()))
+			   ->setValue('following', $qb->createNamedParameter($actor->getFollowing()))
+			   ->setValue('followers', $qb->createNamedParameter($actor->getFollowers()))
+			   ->setValue('inbox', $qb->createNamedParameter($actor->getInbox()))
+			   ->setValue('shared_inbox', $qb->createNamedParameter($actor->getSharedInbox()))
+			   ->setValue('outbox', $qb->createNamedParameter($actor->getOutbox()))
+			   ->setValue('featured', $qb->createNamedParameter($actor->getFeatured()))
+			   ->setValue('url', $qb->createNamedParameter($actor->getUrl()))
+			   ->setValue(
+				   'preferred_username', $qb->createNamedParameter($actor->getPreferredUsername())
+			   )
+			   ->setValue('name', $qb->createNamedParameter($actor->getName()))
+			   ->setValue('summary', $qb->createNamedParameter($actor->getSummary()))
+			   ->setValue('public_key', $qb->createNamedParameter($actor->getPublicKey()));
 
 			$qb->execute();
 
@@ -80,15 +91,39 @@ class CacheActorsRequest extends CacheActorsRequestBuilder {
 	}
 
 
+//	/**
+//	 * get Cached value about an Actor, based on the account.
+//	 *
+//	 * @param string $account
+//	 *
+//	 * @return CacheActor
+//	 * @throws CacheActorDoesNotExistException
+//	 */
+//	public function getFromAccount(string $account): CacheActor {
+//		$qb = $this->getCacheActorsSelectSql();
+//		$this->limitToAccount($qb, $account);
+//
+//		$cursor = $qb->execute();
+//		$data = $cursor->fetch();
+//		$cursor->closeCursor();
+//
+//		if ($data === false) {
+//			throw new CacheActorDoesNotExistException();
+//		}
+//
+//		return $this->parseCacheActorsSelectSql($data);
+//	}
+
+
 	/**
-	 * get Cached value about an Actor, based on the account.
+	 * get Cached version of an Actor, based on the UriId
 	 *
 	 * @param string $account
 	 *
-	 * @return CacheActor
+	 * @return Person
 	 * @throws CacheActorDoesNotExistException
 	 */
-	public function getFromAccount(string $account): CacheActor {
+	public function getFromAccount(string $account): Person {
 		$qb = $this->getCacheActorsSelectSql();
 		$this->limitToAccount($qb, $account);
 
@@ -104,29 +139,5 @@ class CacheActorsRequest extends CacheActorsRequestBuilder {
 	}
 
 
-	/**
-	 * get Cached version of an Actor, based on the UriId
-	 *
-	 * @param string $url
-	 *
-	 * @return CacheActor
-	 * @throws CacheActorDoesNotExistException
-	 */
-	public function getFromUrl(string $url): CacheActor {
-		$qb = $this->getCacheActorsSelectSql();
-		$this->limitToUrl($qb, $url);
-
-		$cursor = $qb->execute();
-		$data = $cursor->fetch();
-		$cursor->closeCursor();
-
-		if ($data === false) {
-			throw new CacheActorDoesNotExistException();
-		}
-
-		return $this->parseCacheActorsSelectSql($data);
-	}
-
-
 }
 
diff --git a/lib/Db/CacheActorsRequestBuilder.php b/lib/Db/CacheActorsRequestBuilder.php
index 29e7b80b..69fe2e47 100644
--- a/lib/Db/CacheActorsRequestBuilder.php
+++ b/lib/Db/CacheActorsRequestBuilder.php
@@ -32,6 +32,7 @@ namespace OCA\Social\Db;
 
 use daita\MySmallPhpTools\Traits\TArrayTools;
 use OCA\Social\Model\ActivityPub\Cache\CacheActor;
+use OCA\Social\Model\ActivityPub\Person;
 use OCP\DB\QueryBuilder\IQueryBuilder;
 
 class CacheActorsRequestBuilder extends CoreRequestBuilder {
@@ -76,7 +77,10 @@ class CacheActorsRequestBuilder extends CoreRequestBuilder {
 
 		/** @noinspection PhpMethodParametersCountMismatchInspection */
 		$qb->select(
-			'ca.id', 'ca.account', 'ca.url', 'ca.actor', 'ca.creation'
+			'ca.id', 'ca.account', 'ca.following', 'ca.followers', 'ca.inbox',
+			'ca.shared_inbox', 'ca.outbox', 'ca.featured', 'ca.url',
+			'ca.preferred_username', 'ca.name', 'ca.summary',
+			'ca.public_key', 'ca.creation'
 		)
 		   ->from(self::TABLE_CACHE_ACTORS, 'ca');
 
@@ -102,15 +106,11 @@ class CacheActorsRequestBuilder extends CoreRequestBuilder {
 	/**
 	 * @param array $data
 	 *
-	 * @return CacheActor
+	 * @return Person
 	 */
-	protected function parseCacheActorsSelectSql($data): CacheActor {
-		$actor = new CacheActor();
-		$actor->setId($this->getInt('id', $data))
-			  ->setAccount($data['account'])
-			  ->setUrl($data['url'])
-			  ->setActor(json_decode($data['actor'], true))
-			  ->setCreation($this->getInt('creation', $data, 0));
+	protected function parseCacheActorsSelectSql(array $data): Person {
+		$actor = new Person();
+		$actor->import($data);
 
 		return $actor;
 	}
diff --git a/lib/Db/CoreRequestBuilder.php b/lib/Db/CoreRequestBuilder.php
index 7f389cbd..d9f13693 100644
--- a/lib/Db/CoreRequestBuilder.php
+++ b/lib/Db/CoreRequestBuilder.php
@@ -40,6 +40,7 @@ class CoreRequestBuilder {
 
 	const TABLE_SERVER_ACTORS = 'social_server_actors';
 	const TABLE_SERVER_NOTES = 'social_server_notes';
+	const TABLE_SERVER_FOLLOWS = 'social_server_follows';
 
 	const TABLE_CACHE_ACTORS = 'social_cache_actors';
 
@@ -80,7 +81,18 @@ class CoreRequestBuilder {
 	 * @param IQueryBuilder $qb
 	 * @param int $id
 	 */
-	protected function limitToId(IQueryBuilder &$qb, $id) {
+	protected function limitToId(IQueryBuilder &$qb, int $id) {
+		$this->limitToDBField($qb, 'id', $id);
+	}
+
+
+	/**
+	 * Limit the request to the Id
+	 *
+	 * @param IQueryBuilder $qb
+	 * @param string $id
+	 */
+	protected function limitToIdString(IQueryBuilder &$qb, string $id) {
 		$this->limitToDBField($qb, 'id', $id);
 	}
 
@@ -173,6 +185,22 @@ class CoreRequestBuilder {
 	}
 
 
+	/**
+	 * @param IQueryBuilder $qb
+	 * @param string $recipient
+	 */
+	protected function limitToRecipient(IQueryBuilder &$qb, string $recipient) {
+		$expr = $qb->expr();
+		$orX = $expr->orX();
+
+		$orX->add($expr->eq('to', $qb->createNamedParameter($recipient)));
+		$orX->add($expr->like('to_array', $qb->createNamedParameter('%"' . $recipient . '"%')));
+		$orX->add($expr->like('cc', $qb->createNamedParameter('%"' . $recipient . '"%')));
+		$orX->add($expr->like('bcc', $qb->createNamedParameter('%"' . $recipient . '"%')));
+
+		$qb->andWhere($orX);
+	}
+
 	/**
 	 * @param IQueryBuilder $qb
 	 * @param string $field
diff --git a/lib/Db/FollowsRequest.php b/lib/Db/FollowsRequest.php
new file mode 100644
index 00000000..994fd2ed
--- /dev/null
+++ b/lib/Db/FollowsRequest.php
@@ -0,0 +1,93 @@
+<?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 Exception;
+use OCA\Social\Model\ActivityPub\Follow;
+
+
+/**
+ * Class FollowsRequest
+ *
+ * @package OCA\Social\Db
+ */
+class FollowsRequest extends FollowsRequestBuilder {
+
+
+	/**
+	 * Insert a new Note in the database.
+	 *
+	 * @param Follow $follow
+	 *
+	 * @return int
+	 * @throws Exception
+	 */
+	public function save(Follow $follow): int {
+
+		try {
+			$qb = $this->getFollowsInsertSql();
+			$qb->setValue('id', $qb->createNamedParameter($follow->getId()))
+			   ->setValue('actor_id', $qb->createNamedParameter($follow->getActorId()))
+			   ->setValue('object_id', $qb->createNamedParameter($follow->getObjectId()));
+
+			$qb->execute();
+
+			return $qb->getLastInsertId();
+		} catch (Exception $e) {
+			throw $e;
+		}
+	}
+
+
+	/**
+	 * Insert a new Note in the database.
+	 *
+	 * @param Follow $follow
+	 *
+	 * @return int
+	 * @throws Exception
+	 */
+	public function delete(Follow $follow) {
+
+		try {
+			$qb = $this->getFollowsDeleteSql();
+			$this->limitToIdString($qb, $follow->getId());
+
+			$qb->execute();
+		} catch (Exception $e) {
+			throw $e;
+		}
+	}
+
+
+}
+
diff --git a/lib/Db/FollowsRequestBuilder.php b/lib/Db/FollowsRequestBuilder.php
new file mode 100644
index 00000000..b50e5e9c
--- /dev/null
+++ b/lib/Db/FollowsRequestBuilder.php
@@ -0,0 +1,122 @@
+<?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\ActivityPub\Follow;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+
+
+/**
+ * Class FollowsRequestBuilder
+ *
+ * @package OCA\Social\Db
+ */
+class FollowsRequestBuilder extends CoreRequestBuilder {
+
+
+	use TArrayTools;
+
+
+	/**
+	 * Base of the Sql Insert request
+	 *
+	 * @return IQueryBuilder
+	 */
+	protected function getFollowsInsertSql(): IQueryBuilder {
+		$qb = $this->dbConnection->getQueryBuilder();
+		$qb->insert(self::TABLE_SERVER_FOLLOWS);
+
+		return $qb;
+	}
+
+
+	/**
+	 * Base of the Sql Update request
+	 *
+	 * @return IQueryBuilder
+	 */
+	protected function getFollowsUpdateSql(): IQueryBuilder {
+		$qb = $this->dbConnection->getQueryBuilder();
+		$qb->update(self::TABLE_SERVER_FOLLOWS);
+
+		return $qb;
+	}
+
+
+	/**
+	 * Base of the Sql Select request for Shares
+	 *
+	 * @return IQueryBuilder
+	 */
+	protected function getFollowsSelectSql(): IQueryBuilder {
+		$qb = $this->dbConnection->getQueryBuilder();
+
+		/** @noinspection PhpMethodParametersCountMismatchInspection */
+		$qb->select('f.id', 'f.actor_id', 'f.object_id', 'f.creation')
+		   ->from(self::TABLE_SERVER_FOLLOWS, 'f');
+
+		$this->defaultSelectAlias = 'f';
+
+		return $qb;
+	}
+
+
+	/**
+	 * Base of the Sql Delete request
+	 *
+	 * @return IQueryBuilder
+	 */
+	protected function getFollowsDeleteSql(): IQueryBuilder {
+		$qb = $this->dbConnection->getQueryBuilder();
+		$qb->delete(self::TABLE_SERVER_FOLLOWS);
+
+		return $qb;
+	}
+
+
+	/**
+	 * @param array $data
+	 *
+	 * @return Follow
+	 */
+	protected function parseFollowsSelectSql($data): Follow {
+		$follow = new Follow();
+		$follow->setId($data['id'])
+			   ->setActorId($data['actor_id'])
+			   ->setObjectId($data['object_id']);
+
+		return $follow;
+	}
+
+}
+
diff --git a/lib/Db/NotesRequest.php b/lib/Db/NotesRequest.php
index 2b9ed2bd..1a55a270 100644
--- a/lib/Db/NotesRequest.php
+++ b/lib/Db/NotesRequest.php
@@ -31,6 +31,7 @@ namespace OCA\Social\Db;
 
 
 use OCA\Social\Model\ActivityPub\Note;
+use OCA\Social\Service\ActivityService;
 use OCA\Social\Service\ConfigService;
 use OCA\Social\Service\MiscService;
 use OCP\IDBConnection;
@@ -60,15 +61,27 @@ class NotesRequest extends NotesRequestBuilder {
 	 * @return int
 	 * @throws \Exception
 	 */
-	public function create(Note $note): int {
+	public function save(Note $note): int {
 
 		try {
 			$qb = $this->getNotesInsertSql();
 			$qb->setValue('id', $qb->createNamedParameter($note->getId()))
 			   ->setValue('to', $qb->createNamedParameter($note->getTo()))
-			   ->setValue('to_array', $qb->createNamedParameter(json_encode($note->getToArray())))
-			   ->setValue('cc', $qb->createNamedParameter(json_encode($note->getCc())))
-			   ->setValue('bcc', $qb->createNamedParameter(json_encode($note->getBcc())))
+			   ->setValue(
+				   'to_array', $qb->createNamedParameter(
+				   json_encode($note->getToArray(), JSON_UNESCAPED_SLASHES)
+			   )
+			   )
+			   ->setValue(
+				   'cc', $qb->createNamedParameter(
+				   json_encode($note->getCcArray(), JSON_UNESCAPED_SLASHES)
+			   )
+			   )
+			   ->setValue(
+				   'bcc', $qb->createNamedParameter(
+				   json_encode($note->getBccArray()), JSON_UNESCAPED_SLASHES
+			   )
+			   )
 			   ->setValue('content', $qb->createNamedParameter($note->getContent()))
 			   ->setValue('summary', $qb->createNamedParameter($note->getSummary()))
 			   ->setValue('published', $qb->createNamedParameter($note->getPublished()))
@@ -83,4 +96,44 @@ class NotesRequest extends NotesRequestBuilder {
 		}
 	}
 
+
+	/**
+	 * @param string $actorId
+	 *
+	 * @return array
+	 */
+	public function getPublicNotes(): array {
+		$qb = $this->getNotesSelectSql();
+		$this->limitToRecipient($qb, ActivityService::TO_PUBLIC);
+
+		$notes = [];
+		$cursor = $qb->execute();
+		while ($data = $cursor->fetch()) {
+			$notes[] = $this->parseNotesSelectSql($data);
+		}
+		$cursor->closeCursor();
+
+		return $notes;
+	}
+
+	/**
+	 * @param string $actorId
+	 *
+	 * @return array
+	 */
+	public function getNotesForActorId(string $actorId): array {
+		$qb = $this->getNotesSelectSql();
+		$this->limitToRecipient($qb, $actorId);
+
+		$notes = [];
+		$cursor = $qb->execute();
+		while ($data = $cursor->fetch()) {
+			$notes[] = $this->parseNotesSelectSql($data);
+		}
+		$cursor->closeCursor();
+
+		return $notes;
+	}
+
+
 }
diff --git a/lib/Db/NotesRequestBuilder.php b/lib/Db/NotesRequestBuilder.php
index a4de22a6..26bfb0ad 100644
--- a/lib/Db/NotesRequestBuilder.php
+++ b/lib/Db/NotesRequestBuilder.php
@@ -32,6 +32,7 @@ namespace OCA\Social\Db;
 
 use daita\MySmallPhpTools\Traits\TArrayTools;
 use OCA\Social\Model\ActivityPub\Note;
+use OCA\Social\Model\Post;
 use OCP\DB\QueryBuilder\IQueryBuilder;
 
 class NotesRequestBuilder extends CoreRequestBuilder {
@@ -76,7 +77,7 @@ class NotesRequestBuilder extends CoreRequestBuilder {
 
 		/** @noinspection PhpMethodParametersCountMismatchInspection */
 		$qb->select(
-			'sn.id', 'sn.to', 'sn.to_array', 'sn.cc', 'sn.bcc', 'sn.content', 'sn_summary',
+			'sn.id', 'sn.to', 'sn.to_array', 'sn.cc', 'sn.bcc', 'sn.content', 'sn.summary',
 			'sn.published', 'sn.attributed_to', 'sn.in_reply_to', 'sn.creation'
 		)
 		   ->from(self::TABLE_SERVER_NOTES, 'sn');
@@ -110,15 +111,39 @@ class NotesRequestBuilder extends CoreRequestBuilder {
 		$note->setId($data['id'])
 			 ->setTo($data['to'])
 			 ->setToArray(json_decode($data['to_array'], true))
-			 ->setCc(json_decode($data['cc'], true))
-			 ->setBcc(json_decode($data['bcc']));
+			 ->setCcArray(json_decode($data['cc'], true))
+			 ->setBccArray(json_decode($data['bcc']))
+			 ->setPublished($data['published']);
 		$note->setContent($data['content'])
-			 ->setPublished($data['published'])
 			 ->setAttributedTo($data['attributed_to'])
 			 ->setInReplyTo($data['in_reply_to']);
 
 		return $note;
 	}
 
+
+	/**
+	 * @param array $data
+	 *
+	 * @return Post
+	 */
+	protected function parsePostsSelectSql($userId, $data): Note {
+		$post = new Post($userId);
+
+		$post->setContent($data['content']);
+
+//		$note->setId($data['id'])
+//			 ->setTo($data['to'])
+//			 ->setToArray(json_decode($data['to_array'], true))
+//			 ->setCc(json_decode($data['cc'], true))
+//			 ->setBcc(json_decode($data['bcc']));
+//		$note->setContent($data['content'])
+//			 ->setPublished($data['published'])
+//			 ->setAttributedTo($data['attributed_to'])
+//			 ->setInReplyTo($data['in_reply_to']);
+
+		return $post;
+	}
+
 }