diff --git a/database.sql b/database.sql index 7305e65d2..a6dc45dac 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2023.09-dev (Giant Rhubarb) --- DB_UPDATE_VERSION 1525 +-- DB_UPDATE_VERSION 1526 -- ------------------------------------------ @@ -101,6 +101,18 @@ CREATE TABLE IF NOT EXISTS `user` ( FOREIGN KEY (`parent-uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='The local users'; +-- +-- TABLE user-gserver +-- +CREATE TABLE IF NOT EXISTS `user-gserver` ( + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id', + `gsid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Gserver id', + `ignored` boolean NOT NULL DEFAULT '0' COMMENT 'server accounts are ignored for the user', + PRIMARY KEY(`uid`,`gsid`), + FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE, + FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User settings about remote servers'; + -- -- TABLE item-uri -- diff --git a/doc/database.md b/doc/database.md index b92859157..284870657 100644 --- a/doc/database.md +++ b/doc/database.md @@ -86,6 +86,7 @@ Database Tables | [tag](help/database/db_tag) | tags and mentions | | [user](help/database/db_user) | The local users | | [user-contact](help/database/db_user-contact) | User specific public contact data | +| [user-gserver](help/database/db_user-gserver) | User settings about remote servers | | [userd](help/database/db_userd) | Deleted usernames | | [verb](help/database/db_verb) | Activity Verbs | | [worker-ipc](help/database/db_worker-ipc) | Inter process communication between the frontend and the worker | diff --git a/doc/database/db_user-gserver.md b/doc/database/db_user-gserver.md new file mode 100644 index 000000000..b49a288f4 --- /dev/null +++ b/doc/database/db_user-gserver.md @@ -0,0 +1,30 @@ +Table user-gserver +=========== + +User settings about remote servers + +Fields +------ + +| Field | Description | Type | Null | Key | Default | Extra | +| ------- | ---------------------------------------- | ------------------ | ---- | --- | ------- | ----- | +| uid | Owner User id | mediumint unsigned | NO | | 0 | | +| gsid | Gserver id | int unsigned | NO | | 0 | | +| ignored | server accounts are ignored for the user | boolean | NO | | 0 | | + +Indexes +------------ + +| Name | Fields | +| ------- | --------- | +| PRIMARY | uid, gsid | + +Foreign Keys +------------ + +| Field | Target Table | Target Field | +|-------|--------------|--------------| +| uid | [user](help/database/db_user) | uid | +| gsid | [gserver](help/database/db_gserver) | id | + +Return to [database documentation](help/database) diff --git a/src/User/Settings/Collection/UserGServers.php b/src/User/Settings/Collection/UserGServers.php new file mode 100644 index 000000000..689b801cf --- /dev/null +++ b/src/User/Settings/Collection/UserGServers.php @@ -0,0 +1,30 @@ +. + * + */ + +namespace Friendica\User\Settings\Collection; + +class UserGServers extends \Friendica\BaseCollection +{ + public function current(): \Friendica\User\Settings\Entity\UserGServer + { + return parent::current(); + } +} diff --git a/src/User/Settings/Entity/UserGServer.php b/src/User/Settings/Entity/UserGServer.php new file mode 100644 index 000000000..eb32ccc83 --- /dev/null +++ b/src/User/Settings/Entity/UserGServer.php @@ -0,0 +1,50 @@ +. + * + */ + +namespace Friendica\User\Settings\Entity; + +use Friendica\Federation\Entity\GServer; + +/** + * @property-read int $uid + * @property-read int $gsid + * @property-read bool $ignored + * @property-read ?GServer $gserver + */ +class UserGServer extends \Friendica\BaseEntity +{ + /** @var int User id */ + protected $uid; + /** @var int GServer id */ + protected $gsid; + /** @var bool Whether the user ignored this server */ + protected $ignored; + /** @var ?GServer */ + protected $gserver; + + public function __construct(int $uid, int $gsid, bool $ignored = false, ?GServer $gserver = null) + { + $this->uid = $uid; + $this->gsid = $gsid; + $this->ignored = $ignored; + $this->gserver = $gserver; + } +} diff --git a/src/User/Settings/Factory/UserGServer.php b/src/User/Settings/Factory/UserGServer.php new file mode 100644 index 000000000..61abe28cc --- /dev/null +++ b/src/User/Settings/Factory/UserGServer.php @@ -0,0 +1,60 @@ +. + * + */ + +namespace Friendica\User\Settings\Factory; + +use Friendica\Capabilities\ICanCreateFromTableRow; +use Friendica\Federation\Entity\GServer; +use Friendica\User\Settings\Entity; + +class UserGServer extends \Friendica\BaseFactory implements ICanCreateFromTableRow +{ + /** + * @param array $row `user-gserver` table row + * @param GServer|null $server Corresponding GServer entity + * @return Entity\UserGServer + */ + public function createFromTableRow(array $row, GServer $server = null): Entity\UserGServer + { + return new Entity\UserGServer( + $row['uid'], + $row['gsid'], + $row['ignored'], + $server, + ); + } + + /** + * @param int $uid + * @param int $gsid + * @param GServer|null $gserver Corresponding GServer entity + * @return Entity\UserGServer + */ + public function createFromUserAndServer(int $uid, int $gsid, GServer $gserver = null): Entity\UserGServer + { + return new Entity\UserGServer( + $uid, + $gsid, + false, + $gserver, + ); + } +} diff --git a/src/User/Settings/Repository/UserGServer.php b/src/User/Settings/Repository/UserGServer.php new file mode 100644 index 000000000..d7a0b3c27 --- /dev/null +++ b/src/User/Settings/Repository/UserGServer.php @@ -0,0 +1,146 @@ +. + * + */ + +namespace Friendica\User\Settings\Repository; + +use Exception; +use Friendica\BaseCollection; +use Friendica\BaseEntity; +use Friendica\Content\Pager; +use Friendica\Database\Database; +use Friendica\Federation\Repository\GServer; +use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Network\HTTPException\NotFoundException; +use Friendica\User\Settings\Collection; +use Friendica\User\Settings\Entity; +use Friendica\User\Settings\Factory; +use Psr\Log\LoggerInterface; + +class UserGServer extends \Friendica\BaseRepository +{ + protected static $table_name = 'user-gserver'; + + /** @var Factory\UserGServer */ + protected $factory; + /** @var GServer */ + protected $gserverRepository; + + public function __construct(GServer $gserverRepository, Database $database, LoggerInterface $logger, Factory\UserGServer $factory) + { + parent::__construct($database, $logger, $factory); + + $this->gserverRepository = $gserverRepository; + } + + /** + * Returns an existing UserGServer entity or create one on the fly + * + * @param int $uid + * @param int $gsid + * @param bool $hydrate Populate the related GServer entity + * @return Entity\UserGServer + */ + public function getOneByUserAndServer(int $uid, int $gsid, bool $hydrate = true): Entity\UserGServer + { + try { + return $this->selectOneByUserAndServer($uid, $gsid, $hydrate); + } catch (NotFoundException $e) { + return $this->factory->createFromUserAndServer($uid, $gsid, $hydrate ? $this->gserverRepository->selectOneById($gsid) : null); + } + } + + /** + * @param int $uid + * @param int $gsid + * @param bool $hydrate Populate the related GServer entity + * @return Entity\UserGServer + * @throws NotFoundException + */ + public function selectOneByUserAndServer(int $uid, int $gsid, bool $hydrate = true): Entity\UserGServer + { + return $this->_selectOne(['uid' => $uid, 'gsid' => $gsid], [], $hydrate); + } + + public function save(Entity\UserGServer $userGServer): Entity\UserGServer + { + $fields = [ + 'uid' => $userGServer->uid, + 'gsid' => $userGServer->gsid, + 'ignored' => $userGServer->ignored, + ]; + + $this->db->insert(static::$table_name, $fields, Database::INSERT_UPDATE); + + return $userGServer; + } + + public function selectByUserWithPagination(int $uid, Pager $pager): Collection\UserGServers + { + return $this->_select(['uid' => $uid], ['limit' => [$pager->getStart(), $pager->getItemsPerPage()]]); + } + + public function countByUser(int $uid): int + { + return $this->count(['uid' => $uid]); + } + + /** + * @param Entity\UserGServer $userGServer + * @return bool + * @throws InternalServerErrorException in case the underlying storage cannot delete the record + */ + public function delete(Entity\UserGServer $userGServer): bool + { + try { + return $this->db->delete(self::$table_name, ['uid' => $userGServer->uid, 'gsid' => $userGServer->gsid]); + } catch (\Exception $exception) { + throw new InternalServerErrorException('Cannot delete the UserGServer', $exception); + } + } + + protected function _selectOne(array $condition, array $params = [], bool $hydrate = true): BaseEntity + { + $fields = $this->db->selectFirst(static::$table_name, [], $condition, $params); + if (!$this->db->isResult($fields)) { + throw new NotFoundException(); + } + + return $this->factory->createFromTableRow($fields, $hydrate ? $this->gserverRepository->selectOneById($fields['gsid']) : null); + } + + /** + * @param array $condition + * @param array $params + * @return Collection\UserGServers + * @throws Exception + */ + protected function _select(array $condition, array $params = []): BaseCollection + { + $rows = $this->db->selectToArray(static::$table_name, [], $condition, $params); + + $Entities = new Collection\UserGServers(); + foreach ($rows as $fields) { + $Entities[] = $this->factory->createFromTableRow($fields, $this->gserverRepository->selectOneById($fields['gsid'])); + } + + return $Entities; + } +} diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index c7575528e..82b84e8a6 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -56,7 +56,7 @@ use Friendica\Database\DBA; // This file is required several times during the test in DbaDefinition which justifies this condition if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1525); + define('DB_UPDATE_VERSION', 1526); } return [ @@ -159,6 +159,17 @@ return [ "email" => ["email(64)"], ] ], + "user-gserver" => [ + "comment" => "User settings about remote servers", + "fields" => [ + "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "foreign" => ["user" => "uid"], "comment" => "Owner User id"], + "gsid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["gserver" => "id"], "comment" => "Gserver id"], + "ignored" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "server accounts are ignored for the user"], + ], + "indexes" => [ + "PRIMARY" => ["uid", "gsid"], + ], + ], "item-uri" => [ "comment" => "URI and GUID for items", "fields" => [