--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Contact\LocalRelationship\Entity;
+
+use Friendica\Core\Protocol;
+use Friendica\Model\Contact;
+
+/**
+ * @property-read int $userId
+ * @property-read int $contactId
+ * @property-read int $uriId
+ * @property-read bool $blocked
+ * @property-read bool $ignored
+ * @property-read bool $collapsed
+ * @property-read bool $hidden
+ * @property-read bool $pending
+ * @property-read int $rel
+ * @property-read string $info
+ * @property-read bool $notifyNewPosts
+ * @property-read bool $isRemoteSelf
+ * @property-read int $fetchFurtherInformation
+ * @property-read string $ffiKeywordDenylist
+ * @property-read bool $subhub
+ * @property-read string $hubVerify
+ * @property-read string $protocol
+ * @property-read int $rating
+ * @property-read int $priority
+ */
+class LocalRelationship extends \Friendica\BaseEntity
+{
+ /** @var int */
+ protected $userId;
+ /** @var int */
+ protected $contactId;
+ /** @var bool */
+ protected $blocked;
+ /** @var bool */
+ protected $ignored;
+ /** @var bool */
+ protected $collapsed;
+ /** @var bool */
+ protected $hidden;
+ /** @var bool */
+ protected $pending;
+ /** @var int */
+ protected $rel;
+ /** @var string */
+ protected $info;
+ /** @var bool */
+ protected $notifyNewPosts;
+ /** @var bool */
+ protected $isRemoteSelf;
+ /** @var int */
+ protected $fetchFurtherInformation;
+ /** @var string */
+ protected $ffiKeywordDenylist;
+ /** @var bool */
+ protected $subhub;
+ /** @var string */
+ protected $hubVerify;
+ /** @var string */
+ protected $protocol;
+ /** @var int */
+ protected $rating;
+ /** @var int */
+ protected $priority;
+
+ public function __construct(int $userId, int $contactId, bool $blocked = false, bool $ignored = false, bool $collapsed = false, bool $hidden = false, bool $pending = false, int $rel = Contact::NOTHING, string $info = '', bool $notifyNewPosts = false, bool $isRemoteSelf = false, int $fetchFurtherInformation = 0, string $ffiKeywordDenylist = '', bool $subhub = false, string $hubVerify = '', string $protocol = Protocol::PHANTOM, ?int $rating = null, ?int $priority = null)
+ {
+ $this->userId = $userId;
+ $this->contactId = $contactId;
+ $this->blocked = $blocked;
+ $this->ignored = $ignored;
+ $this->collapsed = $collapsed;
+ $this->hidden = $hidden;
+ $this->pending = $pending;
+ $this->rel = $rel;
+ $this->info = $info;
+ $this->notifyNewPosts = $notifyNewPosts;
+ $this->isRemoteSelf = $isRemoteSelf;
+ $this->fetchFurtherInformation = $fetchFurtherInformation;
+ $this->ffiKeywordDenylist = $ffiKeywordDenylist;
+ $this->subhub = $subhub;
+ $this->hubVerify = $hubVerify;
+ $this->protocol = $protocol;
+ $this->rating = $rating;
+ $this->priority = $priority;
+ }
+
+ public function addFollow()
+ {
+ $this->rel = in_array($this->rel, [Contact::FOLLOWER, Contact::FRIEND]) ? Contact::FRIEND : Contact::SHARING;
+ }
+
+ public function removeFollow()
+ {
+ $this->rel = in_array($this->rel, [Contact::FOLLOWER, Contact::FRIEND]) ? Contact::FOLLOWER : Contact::NOTHING;
+ }
+
+ public function addFollower()
+ {
+ $this->rel = in_array($this->rel, [Contact::SHARING, Contact::FRIEND]) ? Contact::FRIEND : Contact::FOLLOWER;
+ }
+
+ public function removeFollower()
+ {
+ $this->rel = in_array($this->rel, [Contact::SHARING, Contact::FRIEND]) ? Contact::SHARING : Contact::NOTHING;
+ }
+}
--- /dev/null
+<?php
+
+namespace Friendica\Contact\LocalRelationship\Exception;
+
+class LocalRelationshipPersistenceException extends \RuntimeException
+{
+ public function __construct($message = '', \Throwable $previous = null)
+ {
+ parent::__construct($message, 500, $previous);
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Contact\LocalRelationship\Factory;
+
+use Friendica\BaseFactory;
+use Friendica\Capabilities\ICanCreateFromTableRow;
+use Friendica\Contact\LocalRelationship\Entity;
+use Friendica\Core\Protocol;
+use Friendica\Model\Contact;
+
+class LocalRelationship extends BaseFactory implements ICanCreateFromTableRow
+{
+ /**
+ * @inheritDoc
+ */
+ public function createFromTableRow(array $row): Entity\LocalRelationship
+ {
+ return new Entity\LocalRelationship(
+ $row['uid'],
+ $row['cid'],
+ $row['blocked'] ?? false,
+ $row['ignored'] ?? false,
+ $row['collapsed'] ?? false,
+ $row['hidden'] ?? false,
+ $row['pending'] ?? false,
+ $row['rel'] ?? Contact::NOTHING,
+ $row['info'] ?? '',
+ $row['notify_new_posts'] ?? false,
+ $row['remote_self'] ?? false,
+ $row['fetch_further_information'] ?? 0,
+ $row['ffi_keyword_denylist'] ?? '',
+ $row['subhub'] ?? false,
+ $row['hub-verify'] ?? '',
+ $row['protocol'] ?? Protocol::PHANTOM,
+ $row['rating'] ?? null,
+ $row['priority'] ?? null
+ );
+ }
+}
--- /dev/null
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Contact\LocalRelationship\Repository;
+
+use Friendica\Contact\LocalRelationship\Entity;
+use Friendica\Contact\LocalRelationship\Exception;
+use Friendica\Contact\LocalRelationship\Factory;
+use Friendica\Database\Database;
+use Friendica\Network\HTTPException;
+use Psr\Log\LoggerInterface;
+
+class LocalRelationship extends \Friendica\BaseRepository
+{
+ protected static $table_name = 'user-contact';
+
+ /** @var Factory\LocalRelationship */
+ protected $factory;
+
+ public function __construct(Database $database, LoggerInterface $logger, Factory\LocalRelationship $factory)
+ {
+ parent::__construct($database, $logger, $factory);
+ }
+
+ /**
+ * @param int $uid
+ * @param int $cid
+ * @return Entity\LocalRelationship
+ * @throws HTTPException\NotFoundException
+ */
+ public function selectForUserContact(int $uid, int $cid): Entity\LocalRelationship
+ {
+ return $this->_selectOne(['uid' => $uid, 'cid' => $cid]);
+ }
+
+ /**
+ * Returns the existing local relationship between a user and a public contact or a default
+ * relationship if it doesn't.
+ *
+ * @param int $uid
+ * @param int $cid
+ * @return Entity\LocalRelationship
+ * @throws HTTPException\NotFoundException
+ */
+ public function getForUserContact(int $uid, int $cid): Entity\LocalRelationship
+ {
+ try {
+ return $this->selectForUserContact($uid, $cid);
+ } catch (HTTPException\NotFoundException $e) {
+ return $this->factory->createFromTableRow(['uid' => $uid, 'cid' => $cid]);
+ }
+ }
+
+ /**
+ * @param int $uid
+ * @param int $cid
+ * @return bool
+ * @throws \Exception
+ */
+ public function existsForUserContact(int $uid, int $cid): bool
+ {
+ return $this->exists(['uid' => $uid, 'cid' => $cid]);
+ }
+
+ /**
+ * Converts a given local relationship into a DB compatible row array
+ *
+ * @param Entity\LocalRelationship $localRelationship
+ *
+ * @return array
+ */
+ protected function convertToTableRow(Entity\LocalRelationship $localRelationship): array
+ {
+ return [
+ 'uid' => $localRelationship->userId,
+ 'cid' => $localRelationship->contactId,
+ 'uri-id' => $localRelationship->uriId,
+ 'blocked' => $localRelationship->blocked,
+ 'ignored' => $localRelationship->ignored,
+ 'collapsed' => $localRelationship->collapsed,
+ 'pending' => $localRelationship->pending,
+ 'rel' => $localRelationship->rel,
+ 'info' => $localRelationship->info,
+ 'notify_new_posts' => $localRelationship->notifyNewPosts,
+ 'remote_self' => $localRelationship->isRemoteSelf,
+ 'fetch_further_information' => $localRelationship->fetchFurtherInformation,
+ 'ffi_keyword_denylist' => $localRelationship->ffiKeywordDenylist,
+ 'subhub' => $localRelationship->subhub,
+ 'hub-verify' => $localRelationship->hubVerify,
+ 'protocol' => $localRelationship->protocol,
+ 'rating' => $localRelationship->rating,
+ 'priority' => $localRelationship->priority,
+ ];
+ }
+
+ /**
+ * @param Entity\LocalRelationship $localRelationship
+ *
+ * @return Entity\LocalRelationship
+ *
+ * @throws Exception\LocalRelationshipPersistenceException In case the underlying storage cannot save the LocalRelationship
+ */
+ public function save(Entity\LocalRelationship $localRelationship): Entity\LocalRelationship
+ {
+ try {
+ $fields = $this->convertToTableRow($localRelationship);
+
+ $this->db->insert(self::$table_name, $fields, Database::INSERT_UPDATE);
+
+ return $localRelationship;
+ } catch (\Exception $exception) {
+ throw new Exception\LocalRelationshipPersistenceException(sprintf('Cannot insert/update the local relationship %d for user %d', $localRelationship->contactId, $localRelationship->userId), $exception);
+ }
+ }
+}