From: Philipp Date: Thu, 21 Oct 2021 21:18:08 +0000 (+0200) Subject: Rename Depository to Repository X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=5d92713a8eb8467385387fb2d2a3ac20f6eda06c;p=friendica.git Rename Depository to Repository --- diff --git a/src/BaseDepository.php b/src/BaseDepository.php deleted file mode 100644 index 75118dd8cb..0000000000 --- a/src/BaseDepository.php +++ /dev/null @@ -1,163 +0,0 @@ -db = $database; - $this->logger = $logger; - $this->factory = $factory; - } - - /** - * Populates the collection according to the condition. Retrieves a limited subset of entities depending on the - * boundaries and the limit. The total count of rows matching the condition is stored in the collection. - * - * Depends on the corresponding table featuring a numerical auto incremented column called `id`. - * - * max_id and min_id are susceptible to the query order: - * - min_id alone only reliably works with ASC order - * - max_id alone only reliably works with DESC order - * If the wrong order is detected in either case, we reverse the query order and the entity list order after the query - * - * Chainable. - * - * @param array $condition - * @param array $params - * @param int|null $min_id Retrieve models with an id no fewer than this, as close to it as possible - * @param int|null $max_id Retrieve models with an id no greater than this, as close to it as possible - * @param int $limit - * @return BaseCollection - * @throws \Exception - */ - protected function _selectByBoundaries( - array $condition = [], - array $params = [], - int $min_id = null, - int $max_id = null, - int $limit = self::LIMIT - ): BaseCollection { - $totalCount = $this->count($condition); - - $boundCondition = $condition; - - $reverseOrder = false; - - if (isset($min_id)) { - $boundCondition = DBA::mergeConditions($boundCondition, ['`id` > ?', $min_id]); - if (!isset($max_id) && isset($params['order']['id']) && ($params['order']['id'] === true || $params['order']['id'] === 'DESC')) { - $reverseOrder = true; - - $params['order']['id'] = 'ASC'; - } - } - - if (isset($max_id) && $max_id > 0) { - $boundCondition = DBA::mergeConditions($boundCondition, ['`id` < ?', $max_id]); - if (!isset($min_id) && (!isset($params['order']['id']) || $params['order']['id'] === false || $params['order']['id'] === 'ASC')) { - $reverseOrder = true; - - $params['order']['id'] = 'DESC'; - } - } - - $params['limit'] = $limit; - - $Entities = $this->_select($boundCondition, $params); - if ($reverseOrder) { - $Entities->reverse(); - } - - return new BaseCollection($Entities->getArrayCopy(), $totalCount); - } - - /** - * @param array $condition - * @param array $params - * @return BaseCollection - * @throws Exception - */ - protected function _select(array $condition, array $params = []): BaseCollection - { - $rows = $this->db->selectToArray(static::$table_name, [], $condition, $params); - - $Entities = new BaseCollection(); - foreach ($rows as $fields) { - $Entities[] = $this->factory->createFromTableRow($fields); - } - - return $Entities; - } - - /** - * @param array $condition - * @param array $params - * @return BaseEntity - * @throws NotFoundException - */ - protected function _selectOne(array $condition, array $params = []): BaseEntity - { - $fields = $this->db->selectFirst(static::$table_name, [], $condition, $params); - if (!$this->db->isResult($fields)) { - throw new NotFoundException(); - } - - return $this->factory->createFromTableRow($fields); - } - - /** - * @param array $condition - * @param array $params - * @return int - * @throws Exception - */ - public function count(array $condition, array $params = []): int - { - return $this->db->count(static::$table_name, $condition, $params); - } - - /** - * @param array $condition - * @return bool - * @throws Exception - */ - public function exists(array $condition): bool - { - return $this->db->exists(static::$table_name, $condition); - } -} diff --git a/src/BaseRepository.php b/src/BaseRepository.php new file mode 100644 index 0000000000..dccfe7fdc7 --- /dev/null +++ b/src/BaseRepository.php @@ -0,0 +1,163 @@ +db = $database; + $this->logger = $logger; + $this->factory = $factory; + } + + /** + * Populates the collection according to the condition. Retrieves a limited subset of entities depending on the + * boundaries and the limit. The total count of rows matching the condition is stored in the collection. + * + * Depends on the corresponding table featuring a numerical auto incremented column called `id`. + * + * max_id and min_id are susceptible to the query order: + * - min_id alone only reliably works with ASC order + * - max_id alone only reliably works with DESC order + * If the wrong order is detected in either case, we reverse the query order and the entity list order after the query + * + * Chainable. + * + * @param array $condition + * @param array $params + * @param int|null $min_id Retrieve models with an id no fewer than this, as close to it as possible + * @param int|null $max_id Retrieve models with an id no greater than this, as close to it as possible + * @param int $limit + * @return BaseCollection + * @throws \Exception + */ + protected function _selectByBoundaries( + array $condition = [], + array $params = [], + int $min_id = null, + int $max_id = null, + int $limit = self::LIMIT + ): BaseCollection { + $totalCount = $this->count($condition); + + $boundCondition = $condition; + + $reverseOrder = false; + + if (isset($min_id)) { + $boundCondition = DBA::mergeConditions($boundCondition, ['`id` > ?', $min_id]); + if (!isset($max_id) && isset($params['order']['id']) && ($params['order']['id'] === true || $params['order']['id'] === 'DESC')) { + $reverseOrder = true; + + $params['order']['id'] = 'ASC'; + } + } + + if (isset($max_id) && $max_id > 0) { + $boundCondition = DBA::mergeConditions($boundCondition, ['`id` < ?', $max_id]); + if (!isset($min_id) && (!isset($params['order']['id']) || $params['order']['id'] === false || $params['order']['id'] === 'ASC')) { + $reverseOrder = true; + + $params['order']['id'] = 'DESC'; + } + } + + $params['limit'] = $limit; + + $Entities = $this->_select($boundCondition, $params); + if ($reverseOrder) { + $Entities->reverse(); + } + + return new BaseCollection($Entities->getArrayCopy(), $totalCount); + } + + /** + * @param array $condition + * @param array $params + * @return BaseCollection + * @throws Exception + */ + protected function _select(array $condition, array $params = []): BaseCollection + { + $rows = $this->db->selectToArray(static::$table_name, [], $condition, $params); + + $Entities = new BaseCollection(); + foreach ($rows as $fields) { + $Entities[] = $this->factory->createFromTableRow($fields); + } + + return $Entities; + } + + /** + * @param array $condition + * @param array $params + * @return BaseEntity + * @throws NotFoundException + */ + protected function _selectOne(array $condition, array $params = []): BaseEntity + { + $fields = $this->db->selectFirst(static::$table_name, [], $condition, $params); + if (!$this->db->isResult($fields)) { + throw new NotFoundException(); + } + + return $this->factory->createFromTableRow($fields); + } + + /** + * @param array $condition + * @param array $params + * @return int + * @throws Exception + */ + public function count(array $condition, array $params = []): int + { + return $this->db->count(static::$table_name, $condition, $params); + } + + /** + * @param array $condition + * @return bool + * @throws Exception + */ + public function exists(array $condition): bool + { + return $this->db->exists(static::$table_name, $condition); + } +} diff --git a/src/Contact/FriendSuggest/Depository/FriendSuggest.php b/src/Contact/FriendSuggest/Depository/FriendSuggest.php deleted file mode 100644 index 76f96bdf04..0000000000 --- a/src/Contact/FriendSuggest/Depository/FriendSuggest.php +++ /dev/null @@ -1,139 +0,0 @@ - $fsuggest->uid, - 'cid' => $fsuggest->cid, - 'name' => $fsuggest->name, - 'url' => $fsuggest->url, - 'request' => $fsuggest->request, - 'photo' => $fsuggest->photo, - 'note' => $fsuggest->note, - ]; - } - - /** - * @param array $condition - * @param array $params - * - * @return Entity\FriendSuggest - * - * @throws NotFoundException The underlying exception if there's no FriendSuggest with the given conditions - */ - private function selectOne(array $condition, array $params = []): Entity\FriendSuggest - { - return parent::_selectOne($condition, $params); - } - - /** - * @param array $condition - * @param array $params - * - * @return Collection\FriendSuggests - * - * @throws \Exception - */ - private function select(array $condition, array $params = []): Collection\FriendSuggests - { - return parent::_select($condition, $params); - } - - /** - * @param int $id - * - * @return Entity\FriendSuggest - * - * @throws FriendSuggestNotFoundException in case there's no suggestion for this id - */ - public function selectOneById(int $id): Entity\FriendSuggest - { - try { - return $this->selectOne(['id' => $id]); - } catch (NotFoundException $e) { - throw new FriendSuggestNotFoundException(sprintf('No FriendSuggest found for id %d', $id)); - } - } - - /** - * @param int $cid - * - * @return Collection\FriendSuggests - * - * @throws FriendSuggestPersistenceException In case the underlying storage cannot select the suggestion - */ - public function selectForContact(int $cid): Collection\FriendSuggests - { - try { - return $this->select(['cid' => $cid]); - } catch (\Exception $e) { - throw new FriendSuggestPersistenceException(sprintf('Cannot select FriendSuggestion for contact %d', $cid)); - } - } - - /** - * @param Entity\FriendSuggest $fsuggest - * - * @return Entity\FriendSuggest - * - * @throws FriendSuggestNotFoundException in case the underlying storage cannot save the suggestion - */ - public function save(Entity\FriendSuggest $fsuggest): Entity\FriendSuggest - { - try { - $fields = $this->convertToTableRow($fsuggest); - - if ($fsuggest->id) { - $this->db->update(self::$table_name, $fields, ['id' => $fsuggest->id]); - return $this->factory->createFromTableRow($fields); - } else { - $this->db->insert(self::$table_name, $fields); - return $this->selectOneById($this->db->lastInsertId()); - } - } catch (\Exception $exception) { - throw new FriendSuggestNotFoundException(sprintf('Cannot insert/update the FriendSuggestion %d for user %d', $fsuggest->id, $fsuggest->uid), $exception); - } - } - - /** - * @param Collection\FriendSuggest $fsuggests - * - * @return bool - * - * @throws FriendSuggestNotFoundException in case the underlying storage cannot delete the suggestion - */ - public function delete(Collection\FriendSuggests $fsuggests): bool - { - try { - $ids = $fsuggests->column('id'); - return $this->db->delete(self::$table_name, ['id' => $ids]); - } catch (\Exception $exception) { - throw new FriendSuggestNotFoundException('Cannot delete the FriendSuggestions', $exception); - } - } -} diff --git a/src/Contact/FriendSuggest/Repository/FriendSuggest.php b/src/Contact/FriendSuggest/Repository/FriendSuggest.php new file mode 100644 index 0000000000..77abd73537 --- /dev/null +++ b/src/Contact/FriendSuggest/Repository/FriendSuggest.php @@ -0,0 +1,138 @@ + $fsuggest->uid, + 'cid' => $fsuggest->cid, + 'name' => $fsuggest->name, + 'url' => $fsuggest->url, + 'request' => $fsuggest->request, + 'photo' => $fsuggest->photo, + 'note' => $fsuggest->note, + ]; + } + + /** + * @param array $condition + * @param array $params + * + * @return Entity\FriendSuggest + * + * @throws NotFoundException The underlying exception if there's no FriendSuggest with the given conditions + */ + private function selectOne(array $condition, array $params = []): Entity\FriendSuggest + { + return parent::_selectOne($condition, $params); + } + + /** + * @param array $condition + * @param array $params + * + * @return Collection\FriendSuggests + * + * @throws \Exception + */ + private function select(array $condition, array $params = []): Collection\FriendSuggests + { + return parent::_select($condition, $params); + } + + /** + * @param int $id + * + * @return Entity\FriendSuggest + * + * @throws FriendSuggestNotFoundException in case there's no suggestion for this id + */ + public function selectOneById(int $id): Entity\FriendSuggest + { + try { + return $this->selectOne(['id' => $id]); + } catch (NotFoundException $e) { + throw new FriendSuggestNotFoundException(sprintf('No FriendSuggest found for id %d', $id)); + } + } + + /** + * @param int $cid + * + * @return Collection\FriendSuggests + * + * @throws FriendSuggestPersistenceException In case the underlying storage cannot select the suggestion + */ + public function selectForContact(int $cid): Collection\FriendSuggests + { + try { + return $this->select(['cid' => $cid]); + } catch (\Exception $e) { + throw new FriendSuggestPersistenceException(sprintf('Cannot select FriendSuggestion for contact %d', $cid)); + } + } + + /** + * @param Entity\FriendSuggest $fsuggest + * + * @return Entity\FriendSuggest + * + * @throws FriendSuggestNotFoundException in case the underlying storage cannot save the suggestion + */ + public function save(Entity\FriendSuggest $fsuggest): Entity\FriendSuggest + { + try { + $fields = $this->convertToTableRow($fsuggest); + + if ($fsuggest->id) { + $this->db->update(self::$table_name, $fields, ['id' => $fsuggest->id]); + return $this->factory->createFromTableRow($fields); + } else { + $this->db->insert(self::$table_name, $fields); + return $this->selectOneById($this->db->lastInsertId()); + } + } catch (\Exception $exception) { + throw new FriendSuggestNotFoundException(sprintf('Cannot insert/update the FriendSuggestion %d for user %d', $fsuggest->id, $fsuggest->uid), $exception); + } + } + + /** + * @param Collection\FriendSuggest $fsuggests + * + * @return bool + * + * @throws FriendSuggestNotFoundException in case the underlying storage cannot delete the suggestion + */ + public function delete(Collection\FriendSuggests $fsuggests): bool + { + try { + $ids = $fsuggests->column('id'); + return $this->db->delete(self::$table_name, ['id' => $ids]); + } catch (\Exception $exception) { + throw new FriendSuggestNotFoundException('Cannot delete the FriendSuggestions', $exception); + } + } +} diff --git a/src/Contact/Introduction/Depository/Introduction.php b/src/Contact/Introduction/Depository/Introduction.php deleted file mode 100644 index 792673f23b..0000000000 --- a/src/Contact/Introduction/Depository/Introduction.php +++ /dev/null @@ -1,209 +0,0 @@ -. - * - */ - -namespace Friendica\Contact\Introduction\Depository; - -use Friendica\BaseDepository; -use Friendica\Contact\Introduction\Exception\IntroductionNotFoundException; -use Friendica\Contact\Introduction\Exception\IntroductionPersistenceException; -use Friendica\Contact\Introduction\Collection; -use Friendica\Contact\Introduction\Entity; -use Friendica\Contact\Introduction\Factory; -use Friendica\Database\Database; -use Friendica\Network\HTTPException\NotFoundException; -use Friendica\Util\DateTimeFormat; -use Psr\Log\LoggerInterface; - -class Introduction extends BaseDepository -{ - /** @var Factory\Introduction */ - protected $factory; - - protected static $table_name = 'intro'; - - public function __construct(Database $database, LoggerInterface $logger, Factory\Introduction $factory) - { - parent::__construct($database, $logger, $factory); - } - - /** - * @param array $condition - * @param array $params - * - * @return Entity\Introduction - * - * @throws NotFoundException the underlying exception if there's no Introduction with the given conditions - */ - private function selectOne(array $condition, array $params = []): Entity\Introduction - { - return parent::_selectOne($condition, $params); - } - - /** - * Converts a given Introduction into a DB compatible row array - * - * @param Entity\Introduction $introduction - * - * @return array - */ - protected function convertToTableRow(Entity\Introduction $introduction): array - { - return [ - 'uid' => $introduction->uid, - 'contact-id' => $introduction->cid, - 'suggest-cid' => $introduction->sid, - 'knowyou' => $introduction->knowyou ? 1 : 0, - 'note' => $introduction->note, - 'hash' => $introduction->hash, - 'ignore' => $introduction->ignore ? 1 : 0, - 'datetime' => $introduction->datetime->format(DateTimeFormat::MYSQL), - ]; - } - - /** - * @param int $id - * @param int $uid - * - * @return Entity\Introduction - * - * @throws IntroductionNotFoundException in case there is no Introduction with this id - */ - public function selectOneById(int $id, int $uid): Entity\Introduction - { - try { - return $this->selectOne(['id' => $id, 'uid' => $uid]); - } catch (NotFoundException $exception) { - throw new IntroductionNotFoundException(sprintf('There is no Introduction with the ID %d for the user %d', $id, $uid), $exception); - } - } - - /** - * Selects introductions for a given user - * - * @param int $uid - * @param int|null $min_id - * @param int|null $max_id - * @param int $limit - * - * @return Collection\Introductions - */ - public function selectForUser(int $uid, int $min_id = null, int $max_id = null, int $limit = self::LIMIT): Collection\Introductions - { - try { - $BaseCollection = parent::_selectByBoundaries( - ['`uid = ?` AND NOT `ignore`',$uid], - ['order' => ['id' => 'DESC']], - $min_id, $max_id, $limit); - } catch (\Exception $e) { - throw new IntroductionPersistenceException(sprintf('Cannot select Introductions for used %d', $uid), $e); - } - - return new Collection\Introductions($BaseCollection->getArrayCopy(), $BaseCollection->getTotalCount()); - } - - /** - * Selects the introduction for a given contact - * - * @param int $cid - * - * @return Entity\Introduction - * - * @throws IntroductionNotFoundException in case there is not Introduction for this contact - */ - public function selectForContact(int $cid): Entity\Introduction - { - try { - return $this->selectOne(['contact-id' => $cid]); - } catch (NotFoundException $exception) { - throw new IntroductionNotFoundException(sprintf('There is no Introduction for the contact %d', $cid), $exception); - } - } - - public function countActiveForUser($uid, array $params = []): int - { - try { - return $this->count(['ignore' => false, 'uid' => $uid], $params); - } catch (\Exception $e) { - throw new IntroductionPersistenceException(sprintf('Cannot count Introductions for used %d', $uid), $e); - } - } - - /** - * Checks, if the suggested contact already exists for the user - * - * @param int $sid - * @param int $uid - * - * @return bool - */ - public function suggestionExistsForUser(int $sid, int $uid): bool - { - try { - return $this->exists(['uid' => $uid, 'suggest-cid' => $sid]); - } catch (\Exception $e) { - throw new IntroductionPersistenceException(sprintf('Cannot check suggested Introduction for contact %d and user %d', $sid, $uid), $e); - } - } - - /** - * @param Entity\Introduction $introduction - * - * @return bool - * - * @throws IntroductionPersistenceException in case the underlying storage cannot delete the Introduction - */ - public function delete(Entity\Introduction $introduction): bool - { - if (!$introduction->id) { - return false; - } - - try { - return $this->db->delete(self::$table_name, ['id' => $introduction->id]); - } catch (\Exception $e) { - throw new IntroductionPersistenceException(sprintf('Cannot delete Introduction with id %d', $introduction->id), $e); - } - } - - /** - * @param Entity\Introduction $introduction - * - * @return Entity\Introduction - * - * @throws IntroductionPersistenceException In case the underlying storage cannot save the Introduction - */ - public function save(Entity\Introduction $introduction): Entity\Introduction - { - try { - $fields = $this->convertToTableRow($introduction); - - if ($introduction->id) { - $this->db->update(self::$table_name, $fields, ['id' => $introduction->id]); - return $this->factory->createFromTableRow($fields); - } else { - $this->db->insert(self::$table_name, $fields); - return $this->selectOneById($this->db->lastInsertId(), $introduction->uid); - } - } catch (\Exception $exception) { - throw new IntroductionPersistenceException(sprintf('Cannot insert/update the Introduction %d for user %d', $introduction->id, $introduction->uid), $exception); - } - } -} diff --git a/src/Contact/Introduction/Repository/Introduction.php b/src/Contact/Introduction/Repository/Introduction.php new file mode 100644 index 0000000000..6121d3aabc --- /dev/null +++ b/src/Contact/Introduction/Repository/Introduction.php @@ -0,0 +1,209 @@ +. + * + */ + +namespace Friendica\Contact\Introduction\Repository; + +use Friendica\BaseRepository; +use Friendica\Contact\Introduction\Exception\IntroductionNotFoundException; +use Friendica\Contact\Introduction\Exception\IntroductionPersistenceException; +use Friendica\Contact\Introduction\Collection; +use Friendica\Contact\Introduction\Entity; +use Friendica\Contact\Introduction\Factory; +use Friendica\Database\Database; +use Friendica\Network\HTTPException\NotFoundException; +use Friendica\Util\DateTimeFormat; +use Psr\Log\LoggerInterface; + +class Introduction extends BaseRepository +{ + /** @var Factory\Introduction */ + protected $factory; + + protected static $table_name = 'intro'; + + public function __construct(Database $database, LoggerInterface $logger, Factory\Introduction $factory) + { + parent::__construct($database, $logger, $factory); + } + + /** + * @param array $condition + * @param array $params + * + * @return Entity\Introduction + * + * @throws NotFoundException the underlying exception if there's no Introduction with the given conditions + */ + private function selectOne(array $condition, array $params = []): Entity\Introduction + { + return parent::_selectOne($condition, $params); + } + + /** + * Converts a given Introduction into a DB compatible row array + * + * @param Entity\Introduction $introduction + * + * @return array + */ + protected function convertToTableRow(Entity\Introduction $introduction): array + { + return [ + 'uid' => $introduction->uid, + 'contact-id' => $introduction->cid, + 'suggest-cid' => $introduction->sid, + 'knowyou' => $introduction->knowyou ? 1 : 0, + 'note' => $introduction->note, + 'hash' => $introduction->hash, + 'ignore' => $introduction->ignore ? 1 : 0, + 'datetime' => $introduction->datetime->format(DateTimeFormat::MYSQL), + ]; + } + + /** + * @param int $id + * @param int $uid + * + * @return Entity\Introduction + * + * @throws IntroductionNotFoundException in case there is no Introduction with this id + */ + public function selectOneById(int $id, int $uid): Entity\Introduction + { + try { + return $this->selectOne(['id' => $id, 'uid' => $uid]); + } catch (NotFoundException $exception) { + throw new IntroductionNotFoundException(sprintf('There is no Introduction with the ID %d for the user %d', $id, $uid), $exception); + } + } + + /** + * Selects introductions for a given user + * + * @param int $uid + * @param int|null $min_id + * @param int|null $max_id + * @param int $limit + * + * @return Collection\Introductions + */ + public function selectForUser(int $uid, int $min_id = null, int $max_id = null, int $limit = self::LIMIT): Collection\Introductions + { + try { + $BaseCollection = parent::_selectByBoundaries( + ['`uid = ?` AND NOT `ignore`',$uid], + ['order' => ['id' => 'DESC']], + $min_id, $max_id, $limit); + } catch (\Exception $e) { + throw new IntroductionPersistenceException(sprintf('Cannot select Introductions for used %d', $uid), $e); + } + + return new Collection\Introductions($BaseCollection->getArrayCopy(), $BaseCollection->getTotalCount()); + } + + /** + * Selects the introduction for a given contact + * + * @param int $cid + * + * @return Entity\Introduction + * + * @throws IntroductionNotFoundException in case there is not Introduction for this contact + */ + public function selectForContact(int $cid): Entity\Introduction + { + try { + return $this->selectOne(['contact-id' => $cid]); + } catch (NotFoundException $exception) { + throw new IntroductionNotFoundException(sprintf('There is no Introduction for the contact %d', $cid), $exception); + } + } + + public function countActiveForUser($uid, array $params = []): int + { + try { + return $this->count(['ignore' => false, 'uid' => $uid], $params); + } catch (\Exception $e) { + throw new IntroductionPersistenceException(sprintf('Cannot count Introductions for used %d', $uid), $e); + } + } + + /** + * Checks, if the suggested contact already exists for the user + * + * @param int $sid + * @param int $uid + * + * @return bool + */ + public function suggestionExistsForUser(int $sid, int $uid): bool + { + try { + return $this->exists(['uid' => $uid, 'suggest-cid' => $sid]); + } catch (\Exception $e) { + throw new IntroductionPersistenceException(sprintf('Cannot check suggested Introduction for contact %d and user %d', $sid, $uid), $e); + } + } + + /** + * @param Entity\Introduction $introduction + * + * @return bool + * + * @throws IntroductionPersistenceException in case the underlying storage cannot delete the Introduction + */ + public function delete(Entity\Introduction $introduction): bool + { + if (!$introduction->id) { + return false; + } + + try { + return $this->db->delete(self::$table_name, ['id' => $introduction->id]); + } catch (\Exception $e) { + throw new IntroductionPersistenceException(sprintf('Cannot delete Introduction with id %d', $introduction->id), $e); + } + } + + /** + * @param Entity\Introduction $introduction + * + * @return Entity\Introduction + * + * @throws IntroductionPersistenceException In case the underlying storage cannot save the Introduction + */ + public function save(Entity\Introduction $introduction): Entity\Introduction + { + try { + $fields = $this->convertToTableRow($introduction); + + if ($introduction->id) { + $this->db->update(self::$table_name, $fields, ['id' => $introduction->id]); + return $this->factory->createFromTableRow($fields); + } else { + $this->db->insert(self::$table_name, $fields); + return $this->selectOneById($this->db->lastInsertId(), $introduction->uid); + } + } catch (\Exception $exception) { + throw new IntroductionPersistenceException(sprintf('Cannot insert/update the Introduction %d for user %d', $introduction->id, $introduction->uid), $exception); + } + } +} diff --git a/src/Core/UserImport.php b/src/Core/UserImport.php index 4ac59d14cb..67960fabda 100644 --- a/src/Core/UserImport.php +++ b/src/Core/UserImport.php @@ -27,7 +27,7 @@ use Friendica\DI; use Friendica\Model\Photo; use Friendica\Model\Profile; use Friendica\Object\Image; -use Friendica\Security\PermissionSet\Depository\PermissionSet; +use Friendica\Security\PermissionSet\Repository\PermissionSet; use Friendica\Util\Strings; use Friendica\Worker\Delivery; diff --git a/src/DI.php b/src/DI.php index eea219bd22..7e5f309b33 100644 --- a/src/DI.php +++ b/src/DI.php @@ -427,11 +427,11 @@ abstract class DI // /** - * @return Contact\FriendSuggest\Depository\FriendSuggest; + * @return Contact\FriendSuggest\Repository\FriendSuggest; */ public static function fsuggest() { - return self::$dice->create(Contact\FriendSuggest\Depository\FriendSuggest::class); + return self::$dice->create(Contact\FriendSuggest\Repository\FriendSuggest::class); } /** @@ -443,11 +443,11 @@ abstract class DI } /** - * @return Contact\Introduction\Depository\Introduction + * @return Contact\Introduction\Repository\Introduction */ public static function intro() { - return self::$dice->create(Contact\Introduction\Depository\Introduction::class); + return self::$dice->create(Contact\Introduction\Repository\Introduction::class); } /** @@ -458,9 +458,9 @@ abstract class DI return self::$dice->create(Contact\Introduction\Factory\Introduction::class); } - public static function permissionSet(): Security\PermissionSet\Depository\PermissionSet + public static function permissionSet(): Security\PermissionSet\Repository\PermissionSet { - return self::$dice->create(Security\PermissionSet\Depository\PermissionSet::class); + return self::$dice->create(Security\PermissionSet\Repository\PermissionSet::class); } public static function permissionSetFactory(): Security\PermissionSet\Factory\PermissionSet @@ -468,9 +468,9 @@ abstract class DI return self::$dice->create(Security\PermissionSet\Factory\PermissionSet::class); } - public static function profileField(): Profile\ProfileField\Depository\ProfileField + public static function profileField(): Profile\ProfileField\Repository\ProfileField { - return self::$dice->create(Profile\ProfileField\Depository\ProfileField::class); + return self::$dice->create(Profile\ProfileField\Repository\ProfileField::class); } public static function profileFieldFactory(): Profile\ProfileField\Factory\ProfileField @@ -478,9 +478,9 @@ abstract class DI return self::$dice->create(Profile\ProfileField\Factory\ProfileField::class); } - public static function notification(): Navigation\Notifications\Depository\Notification + public static function notification(): Navigation\Notifications\Repository\Notification { - return self::$dice->create(Navigation\Notifications\Depository\Notification::class); + return self::$dice->create(Navigation\Notifications\Repository\Notification::class); } public static function notificationFactory(): Navigation\Notifications\Factory\Notification @@ -488,9 +488,9 @@ abstract class DI return self::$dice->create(Navigation\Notifications\Factory\Notification::class); } - public static function notify(): Navigation\Notifications\Depository\Notify + public static function notify(): Navigation\Notifications\Repository\Notify { - return self::$dice->create(Navigation\Notifications\Depository\Notify::class); + return self::$dice->create(Navigation\Notifications\Repository\Notify::class); } public static function notifyFactory(): Navigation\Notifications\Factory\Notify diff --git a/src/Factory/Api/Mastodon/Account.php b/src/Factory/Api/Mastodon/Account.php index a987e6874c..7b7eba0ed5 100644 --- a/src/Factory/Api/Mastodon/Account.php +++ b/src/Factory/Api/Mastodon/Account.php @@ -27,7 +27,7 @@ use Friendica\Collection\Api\Mastodon\Fields; use Friendica\Model\APContact; use Friendica\Model\Contact; use Friendica\Network\HTTPException; -use Friendica\Profile\ProfileField\Depository\ProfileField as ProfileFieldDepository; +use Friendica\Profile\ProfileField\Repository\ProfileField as ProfileFieldRepository; use ImagickException; use Psr\Log\LoggerInterface; @@ -35,17 +35,17 @@ class Account extends BaseFactory { /** @var BaseURL */ private $baseUrl; - /** @var ProfileFieldDepository */ - private $profileFieldDepo; + /** @var ProfileFieldRepository */ + private $profileFieldRepo; /** @var Field */ private $mstdnFieldFactory; - public function __construct(LoggerInterface $logger, BaseURL $baseURL, ProfileFieldDepository $profileFieldDepo, Field $mstdnFieldFactory) + public function __construct(LoggerInterface $logger, BaseURL $baseURL, ProfileFieldRepository $profileFieldRepo, Field $mstdnFieldFactory) { parent::__construct($logger); $this->baseUrl = $baseURL; - $this->profileFieldDepo = $profileFieldDepo; + $this->profileFieldRepo = $profileFieldRepo; $this->mstdnFieldFactory = $mstdnFieldFactory; } @@ -76,7 +76,7 @@ class Account extends BaseFactory $self_contact = Contact::selectFirst(['uid'], ['nurl' => $publicContact['nurl'], 'self' => true]); if (!empty($self_contact['uid'])) { - $profileFields = $this->profileFieldDepo->selectPublicFieldsByUserId($self_contact['uid']); + $profileFields = $this->profileFieldRepo->selectPublicFieldsByUserId($self_contact['uid']); $fields = $this->mstdnFieldFactory->createFromProfileFields($profileFields); } else { $fields = new Fields(); @@ -94,7 +94,7 @@ class Account extends BaseFactory { $publicContact = Contact::selectFirst([], ['uid' => $userId, 'self' => true]); - $profileFields = $this->profileFieldDepo->selectPublicFieldsByUserId($userId); + $profileFields = $this->profileFieldRepo->selectPublicFieldsByUserId($userId); $fields = $this->mstdnFieldFactory->createFromProfileFields($profileFields); $apContact = APContact::getByURL($publicContact['url'], false); diff --git a/src/Module/Api/Friendica/Profile/Show.php b/src/Module/Api/Friendica/Profile/Show.php index 8102ac4bcc..4167e6c61f 100644 --- a/src/Module/Api/Friendica/Profile/Show.php +++ b/src/Module/Api/Friendica/Profile/Show.php @@ -28,7 +28,7 @@ use Friendica\Model\Contact; use Friendica\Model\Profile; use Friendica\Module\BaseApi; use Friendica\Network\HTTPException; -use Friendica\Security\PermissionSet\Depository\PermissionSet; +use Friendica\Security\PermissionSet\Repository\PermissionSet; /** * API endpoint: /api/friendica/profile/show diff --git a/src/Navigation/Notifications/Depository/Notification.php b/src/Navigation/Notifications/Depository/Notification.php deleted file mode 100644 index a93bce6657..0000000000 --- a/src/Navigation/Notifications/Depository/Notification.php +++ /dev/null @@ -1,141 +0,0 @@ -getArrayCopy()); - } - - public function countForUser($uid, array $condition, array $params = []): int - { - $condition = DBA::mergeConditions($condition, ['uid' => $uid]); - - return $this->count($condition, $params); - } - - public function existsForUser($uid, array $condition): bool - { - $condition = DBA::mergeConditions($condition, ['uid' => $uid]); - - return $this->exists($condition); - } - - /** - * @param int $id - * @return Entity\Notification - * @throws NotFoundException - */ - public function selectOneById(int $id): Entity\Notification - { - return $this->selectOne(['id' => $id]); - } - - public function selectOneForUser(int $uid, array $condition, array $params = []): Entity\Notification - { - $condition = DBA::mergeConditions($condition, ['uid' => $uid]); - - return $this->selectOne($condition, $params); - } - - public function selectForUser(int $uid, array $condition = [], array $params = []): Collection\Notifications - { - $condition = DBA::mergeConditions($condition, ['uid' => $uid]); - - return $this->select($condition, $params); - } - - public function selectAllForUser(int $uid): Collection\Notifications - { - return $this->selectForUser($uid); - } - - /** - * @param array $condition - * @param array $params - * @param int|null $min_id Retrieve models with an id no fewer than this, as close to it as possible - * @param int|null $max_id Retrieve models with an id no greater than this, as close to it as possible - * @param int $limit - * @return BaseCollection - * @throws Exception - * @see _selectByBoundaries - */ - public function selectByBoundaries(array $condition = [], array $params = [], int $min_id = null, int $max_id = null, int $limit = self::LIMIT) - { - $BaseCollection = parent::_selectByBoundaries($condition, $params, $min_id, $max_id, $limit); - - return new Collection\Notifications($BaseCollection->getArrayCopy(), $BaseCollection->getTotalCount()); - } - - public function setAllSeenForUser(int $uid, array $condition = []): bool - { - $condition = DBA::mergeConditions($condition, ['uid' => $uid]); - - return $this->db->update(self::$table_name, ['seen' => true], $condition); - } - - /** - * @param Entity\Notification $Notification - * @return Entity\Notification - * @throws Exception - */ - public function save(Entity\Notification $Notification): Entity\Notification - { - $fields = [ - 'uid' => $Notification->uid, - 'vid' => Verb::getID($Notification->verb), - 'type' => $Notification->type, - 'actor-id' => $Notification->actorId, - 'target-uri-id' => $Notification->targetUriId, - 'parent-uri-id' => $Notification->parentUriId, - 'seen' => $Notification->seen, - ]; - - if ($Notification->id) { - $this->db->update(self::$table_name, $fields, ['id' => $Notification->id]); - } else { - $fields['created'] = DateTimeFormat::utcNow(); - $this->db->insert(self::$table_name, $fields); - - $Notification = $this->selectOneById($this->db->lastInsertId()); - } - - return $Notification; - } -} diff --git a/src/Navigation/Notifications/Depository/Notify.php b/src/Navigation/Notifications/Depository/Notify.php deleted file mode 100644 index a380166c61..0000000000 --- a/src/Navigation/Notifications/Depository/Notify.php +++ /dev/null @@ -1,734 +0,0 @@ -l10n = $l10n; - $this->baseUrl = $baseUrl; - $this->config = $config; - $this->emailer = $emailer; - $this->notification = $notification; - - parent::__construct($database, $logger, $factory ?? new Factory\Notify($logger)); - } - - /** - * @param array $condition - * @param array $params - * @return Entity\Notify - * @throws HTTPException\NotFoundException - */ - private function selectOne(array $condition, array $params = []): Entity\Notify - { - return parent::_selectOne($condition, $params); - } - - private function select(array $condition, array $params = []): Collection\Notifies - { - return new Collection\Notifies(parent::_select($condition, $params)->getArrayCopy()); - } - - public function countForUser($uid, array $condition, array $params = []): int - { - $condition = DBA::mergeConditions($condition, ['uid' => $uid]); - - return $this->count($condition, $params); - } - - public function existsForUser($uid, array $condition): bool - { - $condition = DBA::mergeConditions($condition, ['uid' => $uid]); - - return $this->exists($condition); - } - - /** - * @param int $id - * @return Entity\Notify - * @throws HTTPException\NotFoundException - */ - public function selectOneById(int $id): Entity\Notify - { - return $this->selectOne(['id' => $id]); - } - - public function selectForUser(int $uid, array $condition, array $params): Collection\Notifies - { - $condition = DBA::mergeConditions($condition, ['uid' => $uid]); - - return $this->select($condition, $params); - } - - /** - * Returns notifications for the user, unread first, ordered in descending chronological order. - * - * @param int $uid - * @param int $limit - * @return Collection\Notifies - */ - public function selectAllForUser(int $uid, int $limit): Collection\Notifies - { - return $this->selectForUser($uid, [], ['order' => ['seen' => 'ASC', 'date' => 'DESC'], 'limit' => $limit]); - } - - public function setAllSeenForUser(int $uid, array $condition = []): bool - { - $condition = DBA::mergeConditions($condition, ['uid' => $uid]); - - return $this->db->update(self::$table_name, ['seen' => true], $condition); - } - - /** - * @param Entity\Notify $Notify - * @return Entity\Notify - * @throws HTTPException\NotFoundException - * @throws HTTPException\InternalServerErrorException - * @throws Exception\NotificationCreationInterceptedException - */ - public function save(Entity\Notify $Notify): Entity\Notify - { - $fields = [ - 'type' => $Notify->type, - 'name' => $Notify->name, - 'url' => $Notify->url, - 'photo' => $Notify->photo, - 'msg' => $Notify->msg, - 'uid' => $Notify->uid, - 'link' => $Notify->link, - 'iid' => $Notify->itemId, - 'parent' => $Notify->parent, - 'seen' => $Notify->seen, - 'verb' => $Notify->verb, - 'otype' => $Notify->otype, - 'name_cache' => $Notify->name_cache, - 'msg_cache' => $Notify->msg_cache, - 'uri-id' => $Notify->uriId, - 'parent-uri-id' => $Notify->parentUriId, - ]; - - if ($Notify->id) { - $this->db->update(self::$table_name, $fields, ['id' => $Notify->id]); - } else { - $fields['date'] = DateTimeFormat::utcNow(); - Hook::callAll('enotify_store', $fields); - - $this->db->insert(self::$table_name, $fields); - - $Notify = $this->selectOneById($this->db->lastInsertId()); - } - - return $Notify; - } - - public function setAllSeenForRelatedNotify(Entity\Notify $Notify): bool - { - $condition = [ - '(`link` = ? OR (`parent` != 0 AND `parent` = ? AND `otype` = ?)) AND `uid` = ?', - $Notify->link, - $Notify->parent, - $Notify->otype, - $Notify->uid - ]; - return $this->db->update(self::$table_name, ['seen' => true], $condition); - } - - /** - * Creates a notification entry and possibly sends a mail - * - * @param array $params Array with the elements: - * type, event, otype, activity, verb, uid, cid, item, link, - * source_name, source_mail, source_nick, source_link, source_photo, - * show_in_notification_page - * - * @return bool - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - function createFromArray($params) - { - /** @var string the common prefix of a notification subject */ - $subjectPrefix = $this->l10n->t('[Friendica:Notify]'); - - // Temporary logging for finding the origin - if (!isset($params['uid'])) { - $this->logger->notice('Missing parameters "uid".', ['params' => $params, 'callstack' => System::callstack()]); - } - - // Ensure that the important fields are set at any time - $fields = ['nickname', 'page-flags', 'notify-flags', 'language', 'username', 'email']; - $user = DBA::selectFirst('user', $fields, ['uid' => $params['uid']]); - - if (!DBA::isResult($user)) { - $this->logger->error('Unknown user', ['uid' => $params['uid']]); - return false; - } - - // There is no need to create notifications for forum accounts - if (in_array($user['page-flags'], [Model\User::PAGE_FLAGS_COMMUNITY, Model\User::PAGE_FLAGS_PRVGROUP])) { - return false; - } - - $params['notify_flags'] = $user['notify-flags']; - $params['language'] = $user['language']; - $params['to_name'] = $user['username']; - $params['to_email'] = $user['email']; - - // from here on everything is in the recipients language - $l10n = $this->l10n->withLang($params['language']); - - if (!empty($params['cid'])) { - $contact = Model\Contact::getById($params['cid'], ['url', 'name', 'photo']); - if (DBA::isResult($contact)) { - $params['source_link'] = $contact['url']; - $params['source_name'] = $contact['name']; - $params['source_photo'] = $contact['photo']; - } - } - - $siteurl = $this->baseUrl->get(true); - $sitename = $this->config->get('config', 'sitename'); - - // with $params['show_in_notification_page'] == false, the notification isn't inserted into - // the database, and an email is sent if applicable. - // default, if not specified: true - $show_in_notification_page = isset($params['show_in_notification_page']) ? $params['show_in_notification_page'] : true; - - $title = $params['item']['title'] ?? ''; - $body = $params['item']['body'] ?? ''; - - $parent_id = $params['item']['parent'] ?? 0; - $parent_uri_id = $params['item']['parent-uri-id'] ?? 0; - - $epreamble = ''; - $preamble = ''; - $subject = ''; - $sitelink = ''; - $tsitelink = ''; - $hsitelink = ''; - $itemlink = ''; - - switch ($params['type']) { - case Model\Notification\Type::MAIL: - $itemlink = $params['link']; - - $subject = $l10n->t('%s New mail received at %s', $subjectPrefix, $sitename); - - $preamble = $l10n->t('%1$s sent you a new private message at %2$s.', $params['source_name'], $sitename); - $epreamble = $l10n->t('%1$s sent you %2$s.', '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', '[url=' . $itemlink . ']' . $l10n->t('a private message').'[/url]'); - - $sitelink = $l10n->t('Please visit %s to view and/or reply to your private messages.'); - $tsitelink = sprintf($sitelink, $itemlink); - $hsitelink = sprintf($sitelink, '' . $sitename . ''); - - // Mail notifications aren't using the "notify" table entry - $show_in_notification_page = false; - break; - - case Model\Notification\Type::COMMENT: - if (Model\Post\ThreadUser::getIgnored($parent_uri_id, $params['uid'])) { - $this->logger->info('Thread is ignored', ['parent' => $parent_id, 'parent-uri-id' => $parent_uri_id]); - return false; - } - - $item = Model\Post::selectFirstForUser($params['uid'], Model\Item::ITEM_FIELDLIST, ['id' => $parent_id, 'deleted' => false]); - if (empty($item)) { - return false; - } - - $item_post_type = Model\Item::postType($item, $l10n); - - $content = Plaintext::getPost($item, 70); - if (!empty($content['text'])) { - $title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"'; - } else { - $title = ''; - } - - // First go for the general message - - // "George Bull's post" - $message = $l10n->t('%1$s commented on %2$s\'s %3$s %4$s'); - $dest_str = sprintf($message, $params['source_name'], $item['author-name'], $item_post_type, $title); - - // "your post" - if ($item['wall']) { - $message = $l10n->t('%1$s commented on your %2$s %3$s'); - $dest_str = sprintf($message, $params['source_name'], $item_post_type, $title); - // "their post" - } elseif ($item['author-link'] == $params['source_link']) { - $message = $l10n->t('%1$s commented on their %2$s %3$s'); - $dest_str = sprintf($message, $params['source_name'], $item_post_type, $title); - } - - $subject = $l10n->t('%1$s Comment to conversation #%2$d by %3$s', $subjectPrefix, $parent_id, $params['source_name']); - - $preamble = $l10n->t('%s commented on an item/conversation you have been following.', $params['source_name']); - - $epreamble = $dest_str; - - $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf($sitelink, $siteurl); - $hsitelink = sprintf($sitelink, '' . $sitename . ''); - $itemlink = $params['link']; - break; - - case Model\Notification\Type::WALL: - $subject = $l10n->t('%s %s posted to your profile wall', $subjectPrefix, $params['source_name']); - - $preamble = $l10n->t('%1$s posted to your profile wall at %2$s', $params['source_name'], $sitename); - $epreamble = $l10n->t('%1$s posted to [url=%2$s]your wall[/url]', - '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', - $params['link'] - ); - - $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf($sitelink, $siteurl); - $hsitelink = sprintf($sitelink, ''.$sitename.''); - $itemlink = $params['link']; - break; - - case Model\Notification\Type::POKE: - $subject = $l10n->t('%1$s %2$s poked you', $subjectPrefix, $params['source_name']); - - $preamble = $l10n->t('%1$s poked you at %2$s', $params['source_name'], $sitename); - $epreamble = $l10n->t('%1$s [url=%2$s]poked you[/url].', - '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', - $params['link'] - ); - - $subject = str_replace('poked', $l10n->t($params['activity']), $subject); - $preamble = str_replace('poked', $l10n->t($params['activity']), $preamble); - $epreamble = str_replace('poked', $l10n->t($params['activity']), $epreamble); - - $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf($sitelink, $siteurl); - $hsitelink = sprintf($sitelink, ''.$sitename.''); - $itemlink = $params['link']; - break; - - case Model\Notification\Type::INTRO: - $itemlink = $params['link']; - $subject = $l10n->t('%s Introduction received', $subjectPrefix); - - $preamble = $l10n->t('You\'ve received an introduction from \'%1$s\' at %2$s', $params['source_name'], $sitename); - $epreamble = $l10n->t('You\'ve received [url=%1$s]an introduction[/url] from %2$s.', - $itemlink, - '[url='.$params['source_link'].']'.$params['source_name'].'[/url]' - ); - - $body = $l10n->t('You may visit their profile at %s', $params['source_link']); - - $sitelink = $l10n->t('Please visit %s to approve or reject the introduction.'); - $tsitelink = sprintf($sitelink, $siteurl); - $hsitelink = sprintf($sitelink, ''.$sitename.''); - - switch ($params['verb']) { - case Activity::FRIEND: - // someone started to share with user (mostly OStatus) - $subject = $l10n->t('%s A new person is sharing with you', $subjectPrefix); - - $preamble = $l10n->t('%1$s is sharing with you at %2$s', $params['source_name'], $sitename); - $epreamble = $l10n->t('%1$s is sharing with you at %2$s', - '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', - $sitename - ); - break; - case Activity::FOLLOW: - // someone started to follow the user (mostly OStatus) - $subject = $l10n->t('%s You have a new follower', $subjectPrefix); - - $preamble = $l10n->t('You have a new follower at %2$s : %1$s', $params['source_name'], $sitename); - $epreamble = $l10n->t('You have a new follower at %2$s : %1$s', - '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', - $sitename - ); - break; - default: - // ACTIVITY_REQ_FRIEND is default activity for notifications - break; - } - break; - - case Model\Notification\Type::SUGGEST: - $itemlink = $params['link']; - $subject = $l10n->t('%s Friend suggestion received', $subjectPrefix); - - $preamble = $l10n->t('You\'ve received a friend suggestion from \'%1$s\' at %2$s', $params['source_name'], $sitename); - $epreamble = $l10n->t('You\'ve received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s.', - $itemlink, - '[url='.$params['item']['url'].']'.$params['item']['name'].'[/url]', - '[url='.$params['source_link'].']'.$params['source_name'].'[/url]' - ); - - $body = $l10n->t('Name:').' '.$params['item']['name']."\n"; - $body .= $l10n->t('Photo:').' '.$params['item']['photo']."\n"; - $body .= $l10n->t('You may visit their profile at %s', $params['item']['url']); - - $sitelink = $l10n->t('Please visit %s to approve or reject the suggestion.'); - $tsitelink = sprintf($sitelink, $siteurl); - $hsitelink = sprintf($sitelink, ''.$sitename.''); - break; - - case Model\Notification\Type::CONFIRM: - if ($params['verb'] == Activity::FRIEND) { // mutual connection - $itemlink = $params['link']; - $subject = $l10n->t('%s Connection accepted', $subjectPrefix); - - $preamble = $l10n->t('\'%1$s\' has accepted your connection request at %2$s', $params['source_name'], $sitename); - $epreamble = $l10n->t('%2$s has accepted your [url=%1$s]connection request[/url].', - $itemlink, - '[url='.$params['source_link'].']'.$params['source_name'].'[/url]' - ); - - $body = $l10n->t('You are now mutual friends and may exchange status updates, photos, and email without restriction.'); - - $sitelink = $l10n->t('Please visit %s if you wish to make any changes to this relationship.'); - $tsitelink = sprintf($sitelink, $siteurl); - $hsitelink = sprintf($sitelink, ''.$sitename.''); - } else { // ACTIVITY_FOLLOW - $itemlink = $params['link']; - $subject = $l10n->t('%s Connection accepted', $subjectPrefix); - - $preamble = $l10n->t('\'%1$s\' has accepted your connection request at %2$s', $params['source_name'], $sitename); - $epreamble = $l10n->t('%2$s has accepted your [url=%1$s]connection request[/url].', - $itemlink, - '[url='.$params['source_link'].']'.$params['source_name'].'[/url]' - ); - - $body = $l10n->t('\'%1$s\' has chosen to accept you a fan, which restricts some forms of communication - such as private messaging and some profile interactions. If this is a celebrity or community page, these settings were applied automatically.', $params['source_name']); - $body .= "\n\n"; - $body .= $l10n->t('\'%1$s\' may choose to extend this into a two-way or more permissive relationship in the future.', $params['source_name']); - - $sitelink = $l10n->t('Please visit %s if you wish to make any changes to this relationship.'); - $tsitelink = sprintf($sitelink, $siteurl); - $hsitelink = sprintf($sitelink, ''.$sitename.''); - } - break; - - case Model\Notification\Type::SYSTEM: - switch($params['event']) { - case "SYSTEM_REGISTER_REQUEST": - $itemlink = $params['link']; - $subject = $l10n->t('[Friendica System Notify]') . ' ' . $l10n->t('registration request'); - - $preamble = $l10n->t('You\'ve received a registration request from \'%1$s\' at %2$s', $params['source_name'], $sitename); - $epreamble = $l10n->t('You\'ve received a [url=%1$s]registration request[/url] from %2$s.', - $itemlink, - '[url='.$params['source_link'].']'.$params['source_name'].'[/url]' - ); - - $body = $l10n->t("Full Name: %s\nSite Location: %s\nLogin Name: %s (%s)", - $params['source_name'], - $siteurl, $params['source_mail'], - $params['source_nick'] - ); - - $sitelink = $l10n->t('Please visit %s to approve or reject the request.'); - $tsitelink = sprintf($sitelink, $params['link']); - $hsitelink = sprintf($sitelink, ''.$sitename.'

'); - break; - case "SYSTEM_DB_UPDATE_FAIL": - break; - } - break; - - default: - $this->logger->notice('Unhandled type', ['type' => $params['type']]); - return false; - } - - return $this->storeAndSend($params, $sitelink, $tsitelink, $hsitelink, $title, $subject, $preamble, $epreamble, $body, $itemlink, $show_in_notification_page); - } - - private function storeAndSend($params, $sitelink, $tsitelink, $hsitelink, $title, $subject, $preamble, $epreamble, $body, $itemlink, $show_in_notification_page) - { - $item_id = $params['item']['id'] ?? 0; - $uri_id = $params['item']['uri-id'] ?? null; - $parent_id = $params['item']['parent'] ?? 0; - $parent_uri_id = $params['item']['parent-uri-id'] ?? null; - - // Ensure that the important fields are set at any time - $fields = ['nickname']; - $user = Model\User::getById($params['uid'], $fields); - - $sitename = $this->config->get('config', 'sitename'); - - $nickname = $user['nickname']; - - $hostname = $this->baseUrl->getHostname(); - if (strpos($hostname, ':')) { - $hostname = substr($hostname, 0, strpos($hostname, ':')); - } - - // Creates a new email builder for the notification email - $emailBuilder = $this->emailer->newNotifyMail(); - - $emailBuilder->setHeader('X-Friendica-Account', '<' . $nickname . '@' . $hostname . '>'); - - $subject .= " (".$nickname."@".$hostname.")"; - - $h = [ - 'params' => $params, - 'subject' => $subject, - 'preamble' => $preamble, - 'epreamble' => $epreamble, - 'body' => $body, - 'sitelink' => $sitelink, - 'tsitelink' => $tsitelink, - 'hsitelink' => $hsitelink, - 'itemlink' => $itemlink - ]; - - Hook::callAll('enotify', $h); - - $subject = $h['subject']; - - $preamble = $h['preamble']; - $epreamble = $h['epreamble']; - - $body = $h['body']; - - $tsitelink = $h['tsitelink']; - $hsitelink = $h['hsitelink']; - $itemlink = $h['itemlink']; - - $notify_id = 0; - - if ($show_in_notification_page) { - $Notify = $this->factory->createFromParams($params, $itemlink, $item_id, $uri_id, $parent_id, $parent_uri_id); - try { - $Notify = $this->save($Notify); - } catch (Exception\NotificationCreationInterceptedException $e) { - // Notification insertion can be intercepted by an addon registering the 'enotify_store' hook - return false; - } - - $Notify->updateMsgFromPreamble($epreamble); - $Notify = $this->save($Notify); - - $itemlink = $this->baseUrl->get() . '/notification/' . $Notify->id; - $notify_id = $Notify->id; - } - - // send email notification if notification preferences permit - if ((intval($params['notify_flags']) & intval($params['type'])) - || $params['type'] == Model\Notification\Type::SYSTEM) { - - $this->logger->notice('sending notification email'); - - if (isset($params['parent']) && (intval($params['parent']) != 0)) { - $parent = Model\Post::selectFirst(['guid'], ['id' => $params['parent']]); - $message_id = "<" . $parent['guid'] . "@" . gethostname() . ">"; - - // Is this the first email notification for this parent item and user? - if (!DBA::exists('notify-threads', ['master-parent-uri-id' => $parent_uri_id, 'receiver-uid' => $params['uid']])) { - $this->logger->info("notify_id:" . intval($notify_id) . ", parent: " . intval($params['parent']) . "uid: " . intval($params['uid'])); - - $fields = ['notify-id' => $notify_id, 'master-parent-uri-id' => $parent_uri_id, - 'receiver-uid' => $params['uid'], 'parent-item' => 0]; - DBA::insert('notify-threads', $fields); - - $emailBuilder->setHeader('Message-ID', $message_id); - $log_msg = "include/enotify: No previous notification found for this parent:\n" . - " parent: ${params['parent']}\n" . " uid : ${params['uid']}\n"; - $this->logger->info($log_msg); - } else { - // If not, just "follow" the thread. - $emailBuilder->setHeader('References', $message_id); - $emailBuilder->setHeader('In-Reply-To', $message_id); - $this->logger->info("There's already a notification for this parent."); - } - } - - $datarray = [ - 'preamble' => $preamble, - 'type' => $params['type'], - 'parent' => $parent_id, - 'source_name' => $params['source_name'] ?? null, - 'source_link' => $params['source_link'] ?? null, - 'source_photo' => $params['source_photo'] ?? null, - 'uid' => $params['uid'], - 'hsitelink' => $hsitelink, - 'tsitelink' => $tsitelink, - 'itemlink' => $itemlink, - 'title' => $title, - 'body' => $body, - 'subject' => $subject, - 'headers' => $emailBuilder->getHeaders(), - ]; - - Hook::callAll('enotify_mail', $datarray); - - $emailBuilder - ->withHeaders($datarray['headers']) - ->withRecipient($params['to_email']) - ->forUser([ - 'uid' => $datarray['uid'], - 'language' => $params['language'], - ]) - ->withNotification($datarray['subject'], $datarray['preamble'], $datarray['title'], $datarray['body']) - ->withSiteLink($datarray['tsitelink'], $datarray['hsitelink']) - ->withItemLink($datarray['itemlink']); - - // If a photo is present, add it to the email - if (!empty($datarray['source_photo'])) { - $emailBuilder->withPhoto( - $datarray['source_photo'], - $datarray['source_link'] ?? $sitelink, - $datarray['source_name'] ?? $sitename); - } - - $email = $emailBuilder->build(); - - // use the Emailer class to send the message - return $this->emailer->send($email); - } - - return false; - } - - public function createFromNotification(Entity\Notification $Notification) - { - $this->logger->info('Start', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); - - if ($Notification->type === Model\Post\UserNotification::TYPE_NONE) { - $this->logger->info('Not an item based notification, quitting', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); - return false; - } - - $params = []; - $params['verb'] = $Notification->verb; - $params['uid'] = $Notification->uid; - $params['otype'] = Model\Notification\ObjectType::ITEM; - - $user = Model\User::getById($Notification->uid); - - $params['notify_flags'] = $user['notify-flags']; - $params['language'] = $user['language']; - $params['to_name'] = $user['username']; - $params['to_email'] = $user['email']; - - // from here on everything is in the recipients language - $l10n = $this->l10n->withLang($user['language']); - - $contact = Model\Contact::getById($Notification->actorId, ['url', 'name', 'photo']); - if (DBA::isResult($contact)) { - $params['source_link'] = $contact['url']; - $params['source_name'] = $contact['name']; - $params['source_photo'] = $contact['photo']; - } - - $item = Model\Post::selectFirstForUser($Notification->uid, Model\Item::ITEM_FIELDLIST, - ['uid' => [0, $Notification->uid], 'uri-id' => $Notification->targetUriId, 'deleted' => false], - ['order' => ['uid' => true]]); - if (empty($item)) { - $this->logger->info('Item not found', ['uri-id' => $Notification->targetUriId, 'type' => $Notification->type]); - return false; - } - - $params['item'] = $item; - $params['parent'] = $item['parent']; - $params['link'] = $this->baseUrl->get() . '/display/' . urlencode($item['guid']); - - $subjectPrefix = $l10n->t('[Friendica:Notify]'); - - if (Model\Post\ThreadUser::getIgnored($Notification->parentUriId, $Notification->uid)) { - $this->logger->info('Thread is ignored', ['parent-uri-id' => $Notification->parentUriId, 'type' => $Notification->type]); - return false; - } - - // Check to see if there was already a tag notify or comment notify for this post. - // If so don't create a second notification - $condition = ['type' => [Model\Notification\Type::TAG_SELF, Model\Notification\Type::COMMENT, Model\Notification\Type::SHARE], - 'link' => $params['link'], 'verb' => Activity::POST]; - if ($this->existsForUser($Notification->uid, $condition)) { - $this->logger->info('Duplicate found, quitting', $condition + ['uid' => $Notification->uid]); - return false; - } - - $content = Plaintext::getPost($item, 70); - if (!empty($content['text'])) { - $title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"'; - } else { - $title = $item['title']; - } - - // Some mail software relies on subject field for threading. - // So, we cannot have different subjects for notifications of the same thread. - // Before this we have the name of the replier on the subject rendering - // different subjects for messages on the same thread. - if ($Notification->type === Model\Post\UserNotification::TYPE_EXPLICIT_TAGGED) { - $params['type'] = Model\Notification\Type::TAG_SELF; - $subject = $l10n->t('%s %s tagged you', $subjectPrefix, $contact['name']); - } elseif ($Notification->type === Model\Post\UserNotification::TYPE_SHARED) { - $params['type'] = Model\Notification\Type::SHARE; - $subject = $l10n->t('%s %s shared a new post', $subjectPrefix, $contact['name']); - } else { - $params['type'] = Model\Notification\Type::COMMENT; - $subject = $l10n->t('%1$s Comment to conversation #%2$d by %3$s', $subjectPrefix, $item['parent'], $contact['name']); - } - - $msg = $this->notification->getMessageFromNotification($Notification, $this->baseUrl, $l10n); - if (empty($msg)) { - $this->logger->info('No notification message, quitting', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); - return false; - } - - $preamble = $msg['plain']; - $epreamble = $msg['rich']; - - $sitename = $this->config->get('config', 'sitename'); - $siteurl = $this->baseUrl->get(true); - - $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.'); - $tsitelink = sprintf($sitelink, $siteurl); - $hsitelink = sprintf($sitelink, '' . $sitename . ''); - $itemlink = $params['link']; - - $this->logger->info('Perform notification', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); - - return $this->storeAndSend($params, $sitelink, $tsitelink, $hsitelink, $title, $subject, $preamble, $epreamble, $item['body'], $itemlink, true); - } -} diff --git a/src/Navigation/Notifications/Factory/FormattedNotification.php b/src/Navigation/Notifications/Factory/FormattedNotification.php index 4e8c1b8462..df0a779155 100644 --- a/src/Navigation/Notifications/Factory/FormattedNotification.php +++ b/src/Navigation/Notifications/Factory/FormattedNotification.php @@ -32,7 +32,7 @@ use Friendica\Model\Contact; use Friendica\Model\Post; use Friendica\Module\BaseNotifications; use Friendica\Navigation\Notifications\Collection\FormattedNotifications; -use Friendica\Navigation\Notifications\Depository; +use Friendica\Navigation\Notifications\Repository; use Friendica\Navigation\Notifications\ValueObject; use Friendica\Network\HTTPException\InternalServerErrorException; use Friendica\Protocol\Activity; @@ -54,14 +54,14 @@ class FormattedNotification extends BaseFactory { /** @var Database */ private $dba; - /** @var Depository\Notify */ + /** @var Repository\Notify */ private $notify; /** @var BaseURL */ private $baseUrl; /** @var L10n */ private $l10n; - public function __construct(LoggerInterface $logger, Database $dba, Depository\Notify $notify, BaseURL $baseUrl, L10n $l10n) + public function __construct(LoggerInterface $logger, Database $dba, Repository\Notify $notify, BaseURL $baseUrl, L10n $l10n) { parent::__construct($logger); diff --git a/src/Navigation/Notifications/Repository/Notification.php b/src/Navigation/Notifications/Repository/Notification.php new file mode 100644 index 0000000000..154461e236 --- /dev/null +++ b/src/Navigation/Notifications/Repository/Notification.php @@ -0,0 +1,141 @@ +getArrayCopy()); + } + + public function countForUser($uid, array $condition, array $params = []): int + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->count($condition, $params); + } + + public function existsForUser($uid, array $condition): bool + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->exists($condition); + } + + /** + * @param int $id + * @return Entity\Notification + * @throws NotFoundException + */ + public function selectOneById(int $id): Entity\Notification + { + return $this->selectOne(['id' => $id]); + } + + public function selectOneForUser(int $uid, array $condition, array $params = []): Entity\Notification + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->selectOne($condition, $params); + } + + public function selectForUser(int $uid, array $condition = [], array $params = []): Collection\Notifications + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->select($condition, $params); + } + + public function selectAllForUser(int $uid): Collection\Notifications + { + return $this->selectForUser($uid); + } + + /** + * @param array $condition + * @param array $params + * @param int|null $min_id Retrieve models with an id no fewer than this, as close to it as possible + * @param int|null $max_id Retrieve models with an id no greater than this, as close to it as possible + * @param int $limit + * @return BaseCollection + * @throws Exception + * @see _selectByBoundaries + */ + public function selectByBoundaries(array $condition = [], array $params = [], int $min_id = null, int $max_id = null, int $limit = self::LIMIT) + { + $BaseCollection = parent::_selectByBoundaries($condition, $params, $min_id, $max_id, $limit); + + return new Collection\Notifications($BaseCollection->getArrayCopy(), $BaseCollection->getTotalCount()); + } + + public function setAllSeenForUser(int $uid, array $condition = []): bool + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->db->update(self::$table_name, ['seen' => true], $condition); + } + + /** + * @param Entity\Notification $Notification + * @return Entity\Notification + * @throws Exception + */ + public function save(Entity\Notification $Notification): Entity\Notification + { + $fields = [ + 'uid' => $Notification->uid, + 'vid' => Verb::getID($Notification->verb), + 'type' => $Notification->type, + 'actor-id' => $Notification->actorId, + 'target-uri-id' => $Notification->targetUriId, + 'parent-uri-id' => $Notification->parentUriId, + 'seen' => $Notification->seen, + ]; + + if ($Notification->id) { + $this->db->update(self::$table_name, $fields, ['id' => $Notification->id]); + } else { + $fields['created'] = DateTimeFormat::utcNow(); + $this->db->insert(self::$table_name, $fields); + + $Notification = $this->selectOneById($this->db->lastInsertId()); + } + + return $Notification; + } +} diff --git a/src/Navigation/Notifications/Repository/Notify.php b/src/Navigation/Notifications/Repository/Notify.php new file mode 100644 index 0000000000..3a6a4133c0 --- /dev/null +++ b/src/Navigation/Notifications/Repository/Notify.php @@ -0,0 +1,734 @@ +l10n = $l10n; + $this->baseUrl = $baseUrl; + $this->config = $config; + $this->emailer = $emailer; + $this->notification = $notification; + + parent::__construct($database, $logger, $factory ?? new Factory\Notify($logger)); + } + + /** + * @param array $condition + * @param array $params + * @return Entity\Notify + * @throws HTTPException\NotFoundException + */ + private function selectOne(array $condition, array $params = []): Entity\Notify + { + return parent::_selectOne($condition, $params); + } + + private function select(array $condition, array $params = []): Collection\Notifies + { + return new Collection\Notifies(parent::_select($condition, $params)->getArrayCopy()); + } + + public function countForUser($uid, array $condition, array $params = []): int + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->count($condition, $params); + } + + public function existsForUser($uid, array $condition): bool + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->exists($condition); + } + + /** + * @param int $id + * @return Entity\Notify + * @throws HTTPException\NotFoundException + */ + public function selectOneById(int $id): Entity\Notify + { + return $this->selectOne(['id' => $id]); + } + + public function selectForUser(int $uid, array $condition, array $params): Collection\Notifies + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->select($condition, $params); + } + + /** + * Returns notifications for the user, unread first, ordered in descending chronological order. + * + * @param int $uid + * @param int $limit + * @return Collection\Notifies + */ + public function selectAllForUser(int $uid, int $limit): Collection\Notifies + { + return $this->selectForUser($uid, [], ['order' => ['seen' => 'ASC', 'date' => 'DESC'], 'limit' => $limit]); + } + + public function setAllSeenForUser(int $uid, array $condition = []): bool + { + $condition = DBA::mergeConditions($condition, ['uid' => $uid]); + + return $this->db->update(self::$table_name, ['seen' => true], $condition); + } + + /** + * @param Entity\Notify $Notify + * @return Entity\Notify + * @throws HTTPException\NotFoundException + * @throws HTTPException\InternalServerErrorException + * @throws Exception\NotificationCreationInterceptedException + */ + public function save(Entity\Notify $Notify): Entity\Notify + { + $fields = [ + 'type' => $Notify->type, + 'name' => $Notify->name, + 'url' => $Notify->url, + 'photo' => $Notify->photo, + 'msg' => $Notify->msg, + 'uid' => $Notify->uid, + 'link' => $Notify->link, + 'iid' => $Notify->itemId, + 'parent' => $Notify->parent, + 'seen' => $Notify->seen, + 'verb' => $Notify->verb, + 'otype' => $Notify->otype, + 'name_cache' => $Notify->name_cache, + 'msg_cache' => $Notify->msg_cache, + 'uri-id' => $Notify->uriId, + 'parent-uri-id' => $Notify->parentUriId, + ]; + + if ($Notify->id) { + $this->db->update(self::$table_name, $fields, ['id' => $Notify->id]); + } else { + $fields['date'] = DateTimeFormat::utcNow(); + Hook::callAll('enotify_store', $fields); + + $this->db->insert(self::$table_name, $fields); + + $Notify = $this->selectOneById($this->db->lastInsertId()); + } + + return $Notify; + } + + public function setAllSeenForRelatedNotify(Entity\Notify $Notify): bool + { + $condition = [ + '(`link` = ? OR (`parent` != 0 AND `parent` = ? AND `otype` = ?)) AND `uid` = ?', + $Notify->link, + $Notify->parent, + $Notify->otype, + $Notify->uid + ]; + return $this->db->update(self::$table_name, ['seen' => true], $condition); + } + + /** + * Creates a notification entry and possibly sends a mail + * + * @param array $params Array with the elements: + * type, event, otype, activity, verb, uid, cid, item, link, + * source_name, source_mail, source_nick, source_link, source_photo, + * show_in_notification_page + * + * @return bool + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + function createFromArray($params) + { + /** @var string the common prefix of a notification subject */ + $subjectPrefix = $this->l10n->t('[Friendica:Notify]'); + + // Temporary logging for finding the origin + if (!isset($params['uid'])) { + $this->logger->notice('Missing parameters "uid".', ['params' => $params, 'callstack' => System::callstack()]); + } + + // Ensure that the important fields are set at any time + $fields = ['nickname', 'page-flags', 'notify-flags', 'language', 'username', 'email']; + $user = DBA::selectFirst('user', $fields, ['uid' => $params['uid']]); + + if (!DBA::isResult($user)) { + $this->logger->error('Unknown user', ['uid' => $params['uid']]); + return false; + } + + // There is no need to create notifications for forum accounts + if (in_array($user['page-flags'], [Model\User::PAGE_FLAGS_COMMUNITY, Model\User::PAGE_FLAGS_PRVGROUP])) { + return false; + } + + $params['notify_flags'] = $user['notify-flags']; + $params['language'] = $user['language']; + $params['to_name'] = $user['username']; + $params['to_email'] = $user['email']; + + // from here on everything is in the recipients language + $l10n = $this->l10n->withLang($params['language']); + + if (!empty($params['cid'])) { + $contact = Model\Contact::getById($params['cid'], ['url', 'name', 'photo']); + if (DBA::isResult($contact)) { + $params['source_link'] = $contact['url']; + $params['source_name'] = $contact['name']; + $params['source_photo'] = $contact['photo']; + } + } + + $siteurl = $this->baseUrl->get(true); + $sitename = $this->config->get('config', 'sitename'); + + // with $params['show_in_notification_page'] == false, the notification isn't inserted into + // the database, and an email is sent if applicable. + // default, if not specified: true + $show_in_notification_page = isset($params['show_in_notification_page']) ? $params['show_in_notification_page'] : true; + + $title = $params['item']['title'] ?? ''; + $body = $params['item']['body'] ?? ''; + + $parent_id = $params['item']['parent'] ?? 0; + $parent_uri_id = $params['item']['parent-uri-id'] ?? 0; + + $epreamble = ''; + $preamble = ''; + $subject = ''; + $sitelink = ''; + $tsitelink = ''; + $hsitelink = ''; + $itemlink = ''; + + switch ($params['type']) { + case Model\Notification\Type::MAIL: + $itemlink = $params['link']; + + $subject = $l10n->t('%s New mail received at %s', $subjectPrefix, $sitename); + + $preamble = $l10n->t('%1$s sent you a new private message at %2$s.', $params['source_name'], $sitename); + $epreamble = $l10n->t('%1$s sent you %2$s.', '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', '[url=' . $itemlink . ']' . $l10n->t('a private message').'[/url]'); + + $sitelink = $l10n->t('Please visit %s to view and/or reply to your private messages.'); + $tsitelink = sprintf($sitelink, $itemlink); + $hsitelink = sprintf($sitelink, '' . $sitename . ''); + + // Mail notifications aren't using the "notify" table entry + $show_in_notification_page = false; + break; + + case Model\Notification\Type::COMMENT: + if (Model\Post\ThreadUser::getIgnored($parent_uri_id, $params['uid'])) { + $this->logger->info('Thread is ignored', ['parent' => $parent_id, 'parent-uri-id' => $parent_uri_id]); + return false; + } + + $item = Model\Post::selectFirstForUser($params['uid'], Model\Item::ITEM_FIELDLIST, ['id' => $parent_id, 'deleted' => false]); + if (empty($item)) { + return false; + } + + $item_post_type = Model\Item::postType($item, $l10n); + + $content = Plaintext::getPost($item, 70); + if (!empty($content['text'])) { + $title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"'; + } else { + $title = ''; + } + + // First go for the general message + + // "George Bull's post" + $message = $l10n->t('%1$s commented on %2$s\'s %3$s %4$s'); + $dest_str = sprintf($message, $params['source_name'], $item['author-name'], $item_post_type, $title); + + // "your post" + if ($item['wall']) { + $message = $l10n->t('%1$s commented on your %2$s %3$s'); + $dest_str = sprintf($message, $params['source_name'], $item_post_type, $title); + // "their post" + } elseif ($item['author-link'] == $params['source_link']) { + $message = $l10n->t('%1$s commented on their %2$s %3$s'); + $dest_str = sprintf($message, $params['source_name'], $item_post_type, $title); + } + + $subject = $l10n->t('%1$s Comment to conversation #%2$d by %3$s', $subjectPrefix, $parent_id, $params['source_name']); + + $preamble = $l10n->t('%s commented on an item/conversation you have been following.', $params['source_name']); + + $epreamble = $dest_str; + + $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, '' . $sitename . ''); + $itemlink = $params['link']; + break; + + case Model\Notification\Type::WALL: + $subject = $l10n->t('%s %s posted to your profile wall', $subjectPrefix, $params['source_name']); + + $preamble = $l10n->t('%1$s posted to your profile wall at %2$s', $params['source_name'], $sitename); + $epreamble = $l10n->t('%1$s posted to [url=%2$s]your wall[/url]', + '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', + $params['link'] + ); + + $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, ''.$sitename.''); + $itemlink = $params['link']; + break; + + case Model\Notification\Type::POKE: + $subject = $l10n->t('%1$s %2$s poked you', $subjectPrefix, $params['source_name']); + + $preamble = $l10n->t('%1$s poked you at %2$s', $params['source_name'], $sitename); + $epreamble = $l10n->t('%1$s [url=%2$s]poked you[/url].', + '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', + $params['link'] + ); + + $subject = str_replace('poked', $l10n->t($params['activity']), $subject); + $preamble = str_replace('poked', $l10n->t($params['activity']), $preamble); + $epreamble = str_replace('poked', $l10n->t($params['activity']), $epreamble); + + $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, ''.$sitename.''); + $itemlink = $params['link']; + break; + + case Model\Notification\Type::INTRO: + $itemlink = $params['link']; + $subject = $l10n->t('%s Introduction received', $subjectPrefix); + + $preamble = $l10n->t('You\'ve received an introduction from \'%1$s\' at %2$s', $params['source_name'], $sitename); + $epreamble = $l10n->t('You\'ve received [url=%1$s]an introduction[/url] from %2$s.', + $itemlink, + '[url='.$params['source_link'].']'.$params['source_name'].'[/url]' + ); + + $body = $l10n->t('You may visit their profile at %s', $params['source_link']); + + $sitelink = $l10n->t('Please visit %s to approve or reject the introduction.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, ''.$sitename.''); + + switch ($params['verb']) { + case Activity::FRIEND: + // someone started to share with user (mostly OStatus) + $subject = $l10n->t('%s A new person is sharing with you', $subjectPrefix); + + $preamble = $l10n->t('%1$s is sharing with you at %2$s', $params['source_name'], $sitename); + $epreamble = $l10n->t('%1$s is sharing with you at %2$s', + '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', + $sitename + ); + break; + case Activity::FOLLOW: + // someone started to follow the user (mostly OStatus) + $subject = $l10n->t('%s You have a new follower', $subjectPrefix); + + $preamble = $l10n->t('You have a new follower at %2$s : %1$s', $params['source_name'], $sitename); + $epreamble = $l10n->t('You have a new follower at %2$s : %1$s', + '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', + $sitename + ); + break; + default: + // ACTIVITY_REQ_FRIEND is default activity for notifications + break; + } + break; + + case Model\Notification\Type::SUGGEST: + $itemlink = $params['link']; + $subject = $l10n->t('%s Friend suggestion received', $subjectPrefix); + + $preamble = $l10n->t('You\'ve received a friend suggestion from \'%1$s\' at %2$s', $params['source_name'], $sitename); + $epreamble = $l10n->t('You\'ve received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s.', + $itemlink, + '[url='.$params['item']['url'].']'.$params['item']['name'].'[/url]', + '[url='.$params['source_link'].']'.$params['source_name'].'[/url]' + ); + + $body = $l10n->t('Name:').' '.$params['item']['name']."\n"; + $body .= $l10n->t('Photo:').' '.$params['item']['photo']."\n"; + $body .= $l10n->t('You may visit their profile at %s', $params['item']['url']); + + $sitelink = $l10n->t('Please visit %s to approve or reject the suggestion.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, ''.$sitename.''); + break; + + case Model\Notification\Type::CONFIRM: + if ($params['verb'] == Activity::FRIEND) { // mutual connection + $itemlink = $params['link']; + $subject = $l10n->t('%s Connection accepted', $subjectPrefix); + + $preamble = $l10n->t('\'%1$s\' has accepted your connection request at %2$s', $params['source_name'], $sitename); + $epreamble = $l10n->t('%2$s has accepted your [url=%1$s]connection request[/url].', + $itemlink, + '[url='.$params['source_link'].']'.$params['source_name'].'[/url]' + ); + + $body = $l10n->t('You are now mutual friends and may exchange status updates, photos, and email without restriction.'); + + $sitelink = $l10n->t('Please visit %s if you wish to make any changes to this relationship.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, ''.$sitename.''); + } else { // ACTIVITY_FOLLOW + $itemlink = $params['link']; + $subject = $l10n->t('%s Connection accepted', $subjectPrefix); + + $preamble = $l10n->t('\'%1$s\' has accepted your connection request at %2$s', $params['source_name'], $sitename); + $epreamble = $l10n->t('%2$s has accepted your [url=%1$s]connection request[/url].', + $itemlink, + '[url='.$params['source_link'].']'.$params['source_name'].'[/url]' + ); + + $body = $l10n->t('\'%1$s\' has chosen to accept you a fan, which restricts some forms of communication - such as private messaging and some profile interactions. If this is a celebrity or community page, these settings were applied automatically.', $params['source_name']); + $body .= "\n\n"; + $body .= $l10n->t('\'%1$s\' may choose to extend this into a two-way or more permissive relationship in the future.', $params['source_name']); + + $sitelink = $l10n->t('Please visit %s if you wish to make any changes to this relationship.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, ''.$sitename.''); + } + break; + + case Model\Notification\Type::SYSTEM: + switch($params['event']) { + case "SYSTEM_REGISTER_REQUEST": + $itemlink = $params['link']; + $subject = $l10n->t('[Friendica System Notify]') . ' ' . $l10n->t('registration request'); + + $preamble = $l10n->t('You\'ve received a registration request from \'%1$s\' at %2$s', $params['source_name'], $sitename); + $epreamble = $l10n->t('You\'ve received a [url=%1$s]registration request[/url] from %2$s.', + $itemlink, + '[url='.$params['source_link'].']'.$params['source_name'].'[/url]' + ); + + $body = $l10n->t("Full Name: %s\nSite Location: %s\nLogin Name: %s (%s)", + $params['source_name'], + $siteurl, $params['source_mail'], + $params['source_nick'] + ); + + $sitelink = $l10n->t('Please visit %s to approve or reject the request.'); + $tsitelink = sprintf($sitelink, $params['link']); + $hsitelink = sprintf($sitelink, ''.$sitename.'

'); + break; + case "SYSTEM_DB_UPDATE_FAIL": + break; + } + break; + + default: + $this->logger->notice('Unhandled type', ['type' => $params['type']]); + return false; + } + + return $this->storeAndSend($params, $sitelink, $tsitelink, $hsitelink, $title, $subject, $preamble, $epreamble, $body, $itemlink, $show_in_notification_page); + } + + private function storeAndSend($params, $sitelink, $tsitelink, $hsitelink, $title, $subject, $preamble, $epreamble, $body, $itemlink, $show_in_notification_page) + { + $item_id = $params['item']['id'] ?? 0; + $uri_id = $params['item']['uri-id'] ?? null; + $parent_id = $params['item']['parent'] ?? 0; + $parent_uri_id = $params['item']['parent-uri-id'] ?? null; + + // Ensure that the important fields are set at any time + $fields = ['nickname']; + $user = Model\User::getById($params['uid'], $fields); + + $sitename = $this->config->get('config', 'sitename'); + + $nickname = $user['nickname']; + + $hostname = $this->baseUrl->getHostname(); + if (strpos($hostname, ':')) { + $hostname = substr($hostname, 0, strpos($hostname, ':')); + } + + // Creates a new email builder for the notification email + $emailBuilder = $this->emailer->newNotifyMail(); + + $emailBuilder->setHeader('X-Friendica-Account', '<' . $nickname . '@' . $hostname . '>'); + + $subject .= " (".$nickname."@".$hostname.")"; + + $h = [ + 'params' => $params, + 'subject' => $subject, + 'preamble' => $preamble, + 'epreamble' => $epreamble, + 'body' => $body, + 'sitelink' => $sitelink, + 'tsitelink' => $tsitelink, + 'hsitelink' => $hsitelink, + 'itemlink' => $itemlink + ]; + + Hook::callAll('enotify', $h); + + $subject = $h['subject']; + + $preamble = $h['preamble']; + $epreamble = $h['epreamble']; + + $body = $h['body']; + + $tsitelink = $h['tsitelink']; + $hsitelink = $h['hsitelink']; + $itemlink = $h['itemlink']; + + $notify_id = 0; + + if ($show_in_notification_page) { + $Notify = $this->factory->createFromParams($params, $itemlink, $item_id, $uri_id, $parent_id, $parent_uri_id); + try { + $Notify = $this->save($Notify); + } catch (Exception\NotificationCreationInterceptedException $e) { + // Notification insertion can be intercepted by an addon registering the 'enotify_store' hook + return false; + } + + $Notify->updateMsgFromPreamble($epreamble); + $Notify = $this->save($Notify); + + $itemlink = $this->baseUrl->get() . '/notification/' . $Notify->id; + $notify_id = $Notify->id; + } + + // send email notification if notification preferences permit + if ((intval($params['notify_flags']) & intval($params['type'])) + || $params['type'] == Model\Notification\Type::SYSTEM) { + + $this->logger->notice('sending notification email'); + + if (isset($params['parent']) && (intval($params['parent']) != 0)) { + $parent = Model\Post::selectFirst(['guid'], ['id' => $params['parent']]); + $message_id = "<" . $parent['guid'] . "@" . gethostname() . ">"; + + // Is this the first email notification for this parent item and user? + if (!DBA::exists('notify-threads', ['master-parent-uri-id' => $parent_uri_id, 'receiver-uid' => $params['uid']])) { + $this->logger->info("notify_id:" . intval($notify_id) . ", parent: " . intval($params['parent']) . "uid: " . intval($params['uid'])); + + $fields = ['notify-id' => $notify_id, 'master-parent-uri-id' => $parent_uri_id, + 'receiver-uid' => $params['uid'], 'parent-item' => 0]; + DBA::insert('notify-threads', $fields); + + $emailBuilder->setHeader('Message-ID', $message_id); + $log_msg = "include/enotify: No previous notification found for this parent:\n" . + " parent: ${params['parent']}\n" . " uid : ${params['uid']}\n"; + $this->logger->info($log_msg); + } else { + // If not, just "follow" the thread. + $emailBuilder->setHeader('References', $message_id); + $emailBuilder->setHeader('In-Reply-To', $message_id); + $this->logger->info("There's already a notification for this parent."); + } + } + + $datarray = [ + 'preamble' => $preamble, + 'type' => $params['type'], + 'parent' => $parent_id, + 'source_name' => $params['source_name'] ?? null, + 'source_link' => $params['source_link'] ?? null, + 'source_photo' => $params['source_photo'] ?? null, + 'uid' => $params['uid'], + 'hsitelink' => $hsitelink, + 'tsitelink' => $tsitelink, + 'itemlink' => $itemlink, + 'title' => $title, + 'body' => $body, + 'subject' => $subject, + 'headers' => $emailBuilder->getHeaders(), + ]; + + Hook::callAll('enotify_mail', $datarray); + + $emailBuilder + ->withHeaders($datarray['headers']) + ->withRecipient($params['to_email']) + ->forUser([ + 'uid' => $datarray['uid'], + 'language' => $params['language'], + ]) + ->withNotification($datarray['subject'], $datarray['preamble'], $datarray['title'], $datarray['body']) + ->withSiteLink($datarray['tsitelink'], $datarray['hsitelink']) + ->withItemLink($datarray['itemlink']); + + // If a photo is present, add it to the email + if (!empty($datarray['source_photo'])) { + $emailBuilder->withPhoto( + $datarray['source_photo'], + $datarray['source_link'] ?? $sitelink, + $datarray['source_name'] ?? $sitename); + } + + $email = $emailBuilder->build(); + + // use the Emailer class to send the message + return $this->emailer->send($email); + } + + return false; + } + + public function createFromNotification(Entity\Notification $Notification) + { + $this->logger->info('Start', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); + + if ($Notification->type === Model\Post\UserNotification::TYPE_NONE) { + $this->logger->info('Not an item based notification, quitting', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); + return false; + } + + $params = []; + $params['verb'] = $Notification->verb; + $params['uid'] = $Notification->uid; + $params['otype'] = Model\Notification\ObjectType::ITEM; + + $user = Model\User::getById($Notification->uid); + + $params['notify_flags'] = $user['notify-flags']; + $params['language'] = $user['language']; + $params['to_name'] = $user['username']; + $params['to_email'] = $user['email']; + + // from here on everything is in the recipients language + $l10n = $this->l10n->withLang($user['language']); + + $contact = Model\Contact::getById($Notification->actorId, ['url', 'name', 'photo']); + if (DBA::isResult($contact)) { + $params['source_link'] = $contact['url']; + $params['source_name'] = $contact['name']; + $params['source_photo'] = $contact['photo']; + } + + $item = Model\Post::selectFirstForUser($Notification->uid, Model\Item::ITEM_FIELDLIST, + ['uid' => [0, $Notification->uid], 'uri-id' => $Notification->targetUriId, 'deleted' => false], + ['order' => ['uid' => true]]); + if (empty($item)) { + $this->logger->info('Item not found', ['uri-id' => $Notification->targetUriId, 'type' => $Notification->type]); + return false; + } + + $params['item'] = $item; + $params['parent'] = $item['parent']; + $params['link'] = $this->baseUrl->get() . '/display/' . urlencode($item['guid']); + + $subjectPrefix = $l10n->t('[Friendica:Notify]'); + + if (Model\Post\ThreadUser::getIgnored($Notification->parentUriId, $Notification->uid)) { + $this->logger->info('Thread is ignored', ['parent-uri-id' => $Notification->parentUriId, 'type' => $Notification->type]); + return false; + } + + // Check to see if there was already a tag notify or comment notify for this post. + // If so don't create a second notification + $condition = ['type' => [Model\Notification\Type::TAG_SELF, Model\Notification\Type::COMMENT, Model\Notification\Type::SHARE], + 'link' => $params['link'], 'verb' => Activity::POST]; + if ($this->existsForUser($Notification->uid, $condition)) { + $this->logger->info('Duplicate found, quitting', $condition + ['uid' => $Notification->uid]); + return false; + } + + $content = Plaintext::getPost($item, 70); + if (!empty($content['text'])) { + $title = '"' . trim(str_replace("\n", " ", $content['text'])) . '"'; + } else { + $title = $item['title']; + } + + // Some mail software relies on subject field for threading. + // So, we cannot have different subjects for notifications of the same thread. + // Before this we have the name of the replier on the subject rendering + // different subjects for messages on the same thread. + if ($Notification->type === Model\Post\UserNotification::TYPE_EXPLICIT_TAGGED) { + $params['type'] = Model\Notification\Type::TAG_SELF; + $subject = $l10n->t('%s %s tagged you', $subjectPrefix, $contact['name']); + } elseif ($Notification->type === Model\Post\UserNotification::TYPE_SHARED) { + $params['type'] = Model\Notification\Type::SHARE; + $subject = $l10n->t('%s %s shared a new post', $subjectPrefix, $contact['name']); + } else { + $params['type'] = Model\Notification\Type::COMMENT; + $subject = $l10n->t('%1$s Comment to conversation #%2$d by %3$s', $subjectPrefix, $item['parent'], $contact['name']); + } + + $msg = $this->notification->getMessageFromNotification($Notification, $this->baseUrl, $l10n); + if (empty($msg)) { + $this->logger->info('No notification message, quitting', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); + return false; + } + + $preamble = $msg['plain']; + $epreamble = $msg['rich']; + + $sitename = $this->config->get('config', 'sitename'); + $siteurl = $this->baseUrl->get(true); + + $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.'); + $tsitelink = sprintf($sitelink, $siteurl); + $hsitelink = sprintf($sitelink, '' . $sitename . ''); + $itemlink = $params['link']; + + $this->logger->info('Perform notification', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]); + + return $this->storeAndSend($params, $sitelink, $tsitelink, $hsitelink, $title, $subject, $preamble, $epreamble, $item['body'], $itemlink, true); + } +} diff --git a/src/Profile/ProfileField/Depository/ProfileField.php b/src/Profile/ProfileField/Depository/ProfileField.php deleted file mode 100644 index a6e441f3b6..0000000000 --- a/src/Profile/ProfileField/Depository/ProfileField.php +++ /dev/null @@ -1,280 +0,0 @@ -. - * - */ - -namespace Friendica\Profile\ProfileField\Depository; - -use Friendica\BaseDepository; -use Friendica\Database\Database; -use Friendica\Profile\ProfileField\Exception\ProfileFieldNotFoundException; -use Friendica\Profile\ProfileField\Exception\ProfileFieldPersistenceException; -use Friendica\Profile\ProfileField\Exception\UnexpectedPermissionSetException; -use Friendica\Profile\ProfileField\Factory; -use Friendica\Profile\ProfileField\Entity; -use Friendica\Profile\ProfileField\Collection; -use Friendica\Security\PermissionSet\Depository\PermissionSet as PermissionSetDepository; -use Friendica\Util\DateTimeFormat; -use Psr\Log\LoggerInterface; - -class ProfileField extends BaseDepository -{ - /** @var Factory\ProfileField */ - protected $factory; - - protected static $table_name = 'profile_field'; - - protected static $view_name = 'profile_field-view'; - - /** @var PermissionSetDepository */ - protected $permissionSetDepository; - - public function __construct(Database $database, LoggerInterface $logger, Factory\ProfileField $factory, PermissionSetDepository $permissionSetDepository) - { - parent::__construct($database, $logger, $factory); - - $this->permissionSetDepository = $permissionSetDepository; - } - - /** - * @param array $condition - * @param array $params - * - * @return Entity\ProfileField - * - * @throws ProfileFieldNotFoundException - * @throws UnexpectedPermissionSetException - */ - private function selectOne(array $condition, array $params = []): Entity\ProfileField - { - $fields = $this->db->selectFirst(static::$view_name, [], $condition, $params); - if (!$this->db->isResult($fields)) { - throw new ProfileFieldNotFoundException(); - } - - return $this->factory->createFromTableRow($fields); - } - - /** - * @param array $condition - * @param array $params - * - * @return Collection\ProfileFields - * - * @throws ProfileFieldPersistenceException In case of underlying persistence exceptions - * @throws UnexpectedPermissionSetException - */ - private function select(array $condition, array $params = []): Collection\ProfileFields - { - $rows = $this->db->selectToArray(static::$view_name, [], $condition, $params); - - $Entities = new Collection\ProfileFields(); - foreach ($rows as $fields) { - $Entities[] = $this->factory->createFromTableRow($fields); - } - - return $Entities; - } - - /** - * Converts a given ProfileField into a DB compatible row array - * - * @param Entity\ProfileField $profileField - * - * @return array - */ - protected function convertToTableRow(Entity\ProfileField $profileField): array - { - return [ - 'uid' => $profileField->uid, - 'label' => $profileField->label, - 'value' => $profileField->value, - 'order' => $profileField->order, - 'created' => $profileField->created->format(DateTimeFormat::MYSQL), - 'edited' => $profileField->edited->format(DateTimeFormat::MYSQL), - 'psid' => $profileField->permissionSet->id - ]; - } - - /** - * Returns all public available ProfileFields for a specific user - * - * @param int $uid the user id - * - * @return Collection\ProfileFields - * - * @throws ProfileFieldPersistenceException In case of underlying persistence exceptions - */ - public function selectPublicFieldsByUserId(int $uid): Collection\ProfileFields - { - try { - $publicPermissionSet = $this->permissionSetDepository->selectPublicForUser($uid); - - return $this->select([ - 'uid' => $uid, - 'psid' => $publicPermissionSet->id - ]); - } catch (\Exception $exception) { - throw new ProfileFieldPersistenceException(sprintf('Cannot select public ProfileField for user "%d"', $uid), $exception); - } - } - - /** - * @param int $uid Field owner user Id - * - * @throws ProfileFieldPersistenceException In case of underlying persistence exceptions - */ - public function selectByUserId(int $uid): Collection\ProfileFields - { - try { - return $this->select( - ['uid' => $uid], - ['order' => ['order']] - ); - } catch (\Exception $exception) { - throw new ProfileFieldPersistenceException(sprintf('Cannot select ProfileField for user "%d"', $uid), $exception); - } - } - - /** - * Retrieve all custom profile field a given contact is able to access to, including public profile fields. - * - * @param int $cid Private contact id, must be owned by $uid - * @param int $uid Field owner user id - * - * @throws \Exception - */ - public function selectByContactId(int $cid, int $uid): Collection\ProfileFields - { - $permissionSets = $this->permissionSetDepository->selectByContactId($cid, $uid); - - $permissionSetIds = $permissionSets->column('id'); - - // Includes public custom fields - $permissionSetIds[] = $this->permissionSetDepository->selectPublicForUser($uid)->id; - - return $this->select( - ['uid' => $uid, 'psid' => $permissionSetIds], - ['order' => ['order']] - ); - } - - /** - * @param int $id - * - * @return Entity\ProfileField - * - * @ProfileFieldNotFoundException In case there is no ProfileField found - */ - public function selectOneById(int $id): Entity\ProfileField - { - try { - return $this->selectOne(['id' => $id]); - } catch (\Exception $exception) { - throw new ProfileFieldNotFoundException(sprintf('Cannot find Profile "%s"', $id), $exception); - } - } - - /** - * Delets a whole collection of ProfileFields - * - * @param Collection\ProfileFields $profileFields - * - * @return bool - * @throws ProfileFieldPersistenceException in case the persistence layer cannot delete the ProfileFields - */ - public function deleteCollection(Collection\ProfileFields $profileFields): bool - { - try { - return $this->db->delete(self::$table_name, ['id' => $profileFields->column('id')]); - } catch (\Exception $exception) { - throw new ProfileFieldPersistenceException('Cannot delete ProfileFields', $exception); - } - } - - /** - * @param Entity\ProfileField $profileField - * - * @return Entity\ProfileField - * @throws ProfileFieldPersistenceException in case the persistence layer cannot save the ProfileField - */ - public function save(Entity\ProfileField $profileField): Entity\ProfileField - { - if ($profileField->permissionSet->id === null) { - throw new ProfileFieldPersistenceException('PermissionSet needs to be saved first.'); - } - - $fields = $this->convertToTableRow($profileField); - - try { - if ($profileField->id) { - $this->db->update(self::$table_name, $fields, ['id' => $profileField->id]); - } else { - $this->db->insert(self::$table_name, $fields); - - $profileField = $this->selectOneById($this->db->lastInsertId()); - } - } catch (\Exception $exception) { - throw new ProfileFieldPersistenceException(sprintf('Cannot save ProfileField with id "%d" and label "%s"', $profileField->id, $profileField->label), $exception); - } - - return $profileField; - } - - public function saveCollectionForUser(int $uid, Collection\ProfileFields $profileFields): Collection\ProfileFields - { - $savedProfileFields = new Collection\ProfileFields(); - - $profileFieldsOld = $this->selectByUserId($uid); - - // Prunes profile field whose label has been emptied - $labels = $profileFields->column('label'); - $prunedProfileFieldsOld = $profileFieldsOld->filter(function (Entity\ProfileField $profileFieldOld) use ($labels) { - return array_search($profileFieldOld->label, $labels) === false; - }); - $this->deleteCollection($prunedProfileFieldsOld); - - // Update the order based on the new Profile Field Collection - $order = 0; - $labelProfileFieldsOld = array_flip($profileFieldsOld->column('label')); - - foreach ($profileFields as $profileField) { - // Update existing field (preserve - if (array_key_exists($profileField->label, $labelProfileFieldsOld)) { - $profileFieldOldId = $labelProfileFieldsOld[$profileField->label]; - /** @var Entity\ProfileField $foundProfileFieldOld */ - $foundProfileFieldOld = $profileFieldsOld[$profileFieldOldId]; - $foundProfileFieldOld->update( - $profileField->value, - $order, - $profileField->permissionSet - ); - - $savedProfileFields->append($this->save($foundProfileFieldOld)); - } else { - $profileField->setOrder($order); - $savedProfileFields->append($this->save($profileField)); - } - - $order++; - } - - return $savedProfileFields; - } -} diff --git a/src/Profile/ProfileField/Repository/ProfileField.php b/src/Profile/ProfileField/Repository/ProfileField.php new file mode 100644 index 0000000000..09959e430b --- /dev/null +++ b/src/Profile/ProfileField/Repository/ProfileField.php @@ -0,0 +1,280 @@ +. + * + */ + +namespace Friendica\Profile\ProfileField\Repository; + +use Friendica\BaseRepository; +use Friendica\Database\Database; +use Friendica\Profile\ProfileField\Exception\ProfileFieldNotFoundException; +use Friendica\Profile\ProfileField\Exception\ProfileFieldPersistenceException; +use Friendica\Profile\ProfileField\Exception\UnexpectedPermissionSetException; +use Friendica\Profile\ProfileField\Factory; +use Friendica\Profile\ProfileField\Entity; +use Friendica\Profile\ProfileField\Collection; +use Friendica\Security\PermissionSet\Repository\PermissionSet as PermissionSetRepository; +use Friendica\Util\DateTimeFormat; +use Psr\Log\LoggerInterface; + +class ProfileField extends BaseRepository +{ + /** @var Factory\ProfileField */ + protected $factory; + + protected static $table_name = 'profile_field'; + + protected static $view_name = 'profile_field-view'; + + /** @var PermissionSetRepository */ + protected $permissionSetRepository; + + public function __construct(Database $database, LoggerInterface $logger, Factory\ProfileField $factory, PermissionSetRepository $permissionSetRepository) + { + parent::__construct($database, $logger, $factory); + + $this->permissionSetRepository = $permissionSetRepository; + } + + /** + * @param array $condition + * @param array $params + * + * @return Entity\ProfileField + * + * @throws ProfileFieldNotFoundException + * @throws UnexpectedPermissionSetException + */ + private function selectOne(array $condition, array $params = []): Entity\ProfileField + { + $fields = $this->db->selectFirst(static::$view_name, [], $condition, $params); + if (!$this->db->isResult($fields)) { + throw new ProfileFieldNotFoundException(); + } + + return $this->factory->createFromTableRow($fields); + } + + /** + * @param array $condition + * @param array $params + * + * @return Collection\ProfileFields + * + * @throws ProfileFieldPersistenceException In case of underlying persistence exceptions + * @throws UnexpectedPermissionSetException + */ + private function select(array $condition, array $params = []): Collection\ProfileFields + { + $rows = $this->db->selectToArray(static::$view_name, [], $condition, $params); + + $Entities = new Collection\ProfileFields(); + foreach ($rows as $fields) { + $Entities[] = $this->factory->createFromTableRow($fields); + } + + return $Entities; + } + + /** + * Converts a given ProfileField into a DB compatible row array + * + * @param Entity\ProfileField $profileField + * + * @return array + */ + protected function convertToTableRow(Entity\ProfileField $profileField): array + { + return [ + 'uid' => $profileField->uid, + 'label' => $profileField->label, + 'value' => $profileField->value, + 'order' => $profileField->order, + 'created' => $profileField->created->format(DateTimeFormat::MYSQL), + 'edited' => $profileField->edited->format(DateTimeFormat::MYSQL), + 'psid' => $profileField->permissionSet->id + ]; + } + + /** + * Returns all public available ProfileFields for a specific user + * + * @param int $uid the user id + * + * @return Collection\ProfileFields + * + * @throws ProfileFieldPersistenceException In case of underlying persistence exceptions + */ + public function selectPublicFieldsByUserId(int $uid): Collection\ProfileFields + { + try { + $publicPermissionSet = $this->permissionSetRepository->selectPublicForUser($uid); + + return $this->select([ + 'uid' => $uid, + 'psid' => $publicPermissionSet->id + ]); + } catch (\Exception $exception) { + throw new ProfileFieldPersistenceException(sprintf('Cannot select public ProfileField for user "%d"', $uid), $exception); + } + } + + /** + * @param int $uid Field owner user Id + * + * @throws ProfileFieldPersistenceException In case of underlying persistence exceptions + */ + public function selectByUserId(int $uid): Collection\ProfileFields + { + try { + return $this->select( + ['uid' => $uid], + ['order' => ['order']] + ); + } catch (\Exception $exception) { + throw new ProfileFieldPersistenceException(sprintf('Cannot select ProfileField for user "%d"', $uid), $exception); + } + } + + /** + * Retrieve all custom profile field a given contact is able to access to, including public profile fields. + * + * @param int $cid Private contact id, must be owned by $uid + * @param int $uid Field owner user id + * + * @throws \Exception + */ + public function selectByContactId(int $cid, int $uid): Collection\ProfileFields + { + $permissionSets = $this->permissionSetRepository->selectByContactId($cid, $uid); + + $permissionSetIds = $permissionSets->column('id'); + + // Includes public custom fields + $permissionSetIds[] = $this->permissionSetRepository->selectPublicForUser($uid)->id; + + return $this->select( + ['uid' => $uid, 'psid' => $permissionSetIds], + ['order' => ['order']] + ); + } + + /** + * @param int $id + * + * @return Entity\ProfileField + * + * @ProfileFieldNotFoundException In case there is no ProfileField found + */ + public function selectOneById(int $id): Entity\ProfileField + { + try { + return $this->selectOne(['id' => $id]); + } catch (\Exception $exception) { + throw new ProfileFieldNotFoundException(sprintf('Cannot find Profile "%s"', $id), $exception); + } + } + + /** + * Delets a whole collection of ProfileFields + * + * @param Collection\ProfileFields $profileFields + * + * @return bool + * @throws ProfileFieldPersistenceException in case the persistence layer cannot delete the ProfileFields + */ + public function deleteCollection(Collection\ProfileFields $profileFields): bool + { + try { + return $this->db->delete(self::$table_name, ['id' => $profileFields->column('id')]); + } catch (\Exception $exception) { + throw new ProfileFieldPersistenceException('Cannot delete ProfileFields', $exception); + } + } + + /** + * @param Entity\ProfileField $profileField + * + * @return Entity\ProfileField + * @throws ProfileFieldPersistenceException in case the persistence layer cannot save the ProfileField + */ + public function save(Entity\ProfileField $profileField): Entity\ProfileField + { + if ($profileField->permissionSet->id === null) { + throw new ProfileFieldPersistenceException('PermissionSet needs to be saved first.'); + } + + $fields = $this->convertToTableRow($profileField); + + try { + if ($profileField->id) { + $this->db->update(self::$table_name, $fields, ['id' => $profileField->id]); + } else { + $this->db->insert(self::$table_name, $fields); + + $profileField = $this->selectOneById($this->db->lastInsertId()); + } + } catch (\Exception $exception) { + throw new ProfileFieldPersistenceException(sprintf('Cannot save ProfileField with id "%d" and label "%s"', $profileField->id, $profileField->label), $exception); + } + + return $profileField; + } + + public function saveCollectionForUser(int $uid, Collection\ProfileFields $profileFields): Collection\ProfileFields + { + $savedProfileFields = new Collection\ProfileFields(); + + $profileFieldsOld = $this->selectByUserId($uid); + + // Prunes profile field whose label has been emptied + $labels = $profileFields->column('label'); + $prunedProfileFieldsOld = $profileFieldsOld->filter(function (Entity\ProfileField $profileFieldOld) use ($labels) { + return array_search($profileFieldOld->label, $labels) === false; + }); + $this->deleteCollection($prunedProfileFieldsOld); + + // Update the order based on the new Profile Field Collection + $order = 0; + $labelProfileFieldsOld = array_flip($profileFieldsOld->column('label')); + + foreach ($profileFields as $profileField) { + // Update existing field (preserve + if (array_key_exists($profileField->label, $labelProfileFieldsOld)) { + $profileFieldOldId = $labelProfileFieldsOld[$profileField->label]; + /** @var Entity\ProfileField $foundProfileFieldOld */ + $foundProfileFieldOld = $profileFieldsOld[$profileFieldOldId]; + $foundProfileFieldOld->update( + $profileField->value, + $order, + $profileField->permissionSet + ); + + $savedProfileFields->append($this->save($foundProfileFieldOld)); + } else { + $profileField->setOrder($order); + $savedProfileFields->append($this->save($profileField)); + } + + $order++; + } + + return $savedProfileFields; + } +} diff --git a/src/Security/PermissionSet/Depository/PermissionSet.php b/src/Security/PermissionSet/Depository/PermissionSet.php deleted file mode 100644 index b91f452180..0000000000 --- a/src/Security/PermissionSet/Depository/PermissionSet.php +++ /dev/null @@ -1,250 +0,0 @@ -. - * - */ - -namespace Friendica\Security\PermissionSet\Depository; - -use Exception; -use Friendica\BaseDepository; -use Friendica\Database\Database; -use Friendica\Model\Contact; -use Friendica\Model\Group; -use Friendica\Network\HTTPException\NotFoundException; -use Friendica\Security\PermissionSet\Factory; -use Friendica\Security\PermissionSet\Collection; -use Friendica\Security\PermissionSet\Entity; -use Friendica\Util\ACLFormatter; -use Psr\Log\LoggerInterface; - -class PermissionSet extends BaseDepository -{ - /** @var int Virtual permission set id for public permission */ - const PUBLIC = 0; - - /** @var Factory\PermissionSet */ - protected $factory; - - protected static $table_name = 'permissionset'; - - /** @var ACLFormatter */ - private $aclFormatter; - - public function __construct(Database $database, LoggerInterface $logger, Factory\PermissionSet $factory, ACLFormatter $aclFormatter) - { - parent::__construct($database, $logger, $factory); - - $this->aclFormatter = $aclFormatter; - } - - /** - * replaces the PUBLIC id for the public permissionSet - * (no need to create the default permission set over and over again) - * - * @param $condition - */ - private function checkPublicSelect(&$condition) - { - if (empty($condition['allow_cid']) && - empty($condition['allow_gid']) && - empty($condition['deny_cid']) && - empty($condition['deny_gid'])) { - $condition['uid'] = self::PUBLIC; - } - } - - /** - * @param array $condition - * @param array $params - * - * @return Entity\PermissionSet - * @throws NotFoundException - */ - private function selectOne(array $condition, array $params = []): Entity\PermissionSet - { - return parent::_selectOne($condition, $params); - } - - private function select(array $condition, array $params = []): Collection\PermissionSets - { - return new Collection\PermissionSets(parent::_select($condition, $params)->getArrayCopy()); - } - - /** - * Converts a given PermissionSet into a DB compatible row array - * - * @param Entity\PermissionSet $permissionSet - * - * @return array - */ - protected function convertToTableRow(Entity\PermissionSet $permissionSet): array - { - return [ - 'uid' => $permissionSet->uid, - 'allow_cid' => $this->aclFormatter->toString($permissionSet->allow_cid), - 'allow_gid' => $this->aclFormatter->toString($permissionSet->allow_gid), - 'deny_cid' => $this->aclFormatter->toString($permissionSet->deny_cid), - 'deny_gid' => $this->aclFormatter->toString($permissionSet->deny_gid), - ]; - } - - /** - * @param int $id A PermissionSet table row id or self::PUBLIC - * @param int $uid The owner of the PermissionSet - * @return Entity\PermissionSet - * @throws NotFoundException - */ - public function selectOneById(int $id, int $uid): Entity\PermissionSet - { - if ($id === self::PUBLIC) { - return $this->factory->createFromString($uid); - } - - return $this->selectOne(['id' => $id, 'uid' => $uid]); - } - - /** - * Returns a permission set collection for a given contact - * - * @param int $cid Contact id of the visitor - * @param int $uid User id whom the items belong, used for ownership check. - * - * @return Collection\PermissionSets - */ - public function selectByContactId(int $cid, int $uid): Collection\PermissionSets - { - $cdata = Contact::getPublicAndUserContactID($cid, $uid); - if (!empty($cdata)) { - $public_contact_str = $this->aclFormatter->toString($cdata['public']); - $user_contact_str = $this->aclFormatter->toString($cdata['user']); - $cid = $cdata['user']; - } else { - $public_contact_str = $this->aclFormatter->toString($cid); - $user_contact_str = ''; - } - - $groups = []; - if (!empty($user_contact_str) && $this->db->exists('contact', [ - 'id' => $cid, - 'uid' => $uid, - 'blocked' => false - ])) { - $groups = Group::getIdsByContactId($cid); - } - - $group_str = '<<>>'; // should be impossible to match - foreach ($groups as $group_id) { - $group_str .= '|<' . preg_quote($group_id) . '>'; - } - - if (!empty($user_contact_str)) { - $condition = ["`uid` = ? AND (NOT (`deny_cid` REGEXP ? OR `deny_cid` REGEXP ? OR deny_gid REGEXP ?) - AND (allow_cid REGEXP ? OR allow_cid REGEXP ? OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))", - $uid, $user_contact_str, $public_contact_str, $group_str, - $user_contact_str, $public_contact_str, $group_str]; - } else { - $condition = ["`uid` = ? AND (NOT (`deny_cid` REGEXP ? OR deny_gid REGEXP ?) - AND (allow_cid REGEXP ? OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))", - $uid, $public_contact_str, $group_str, $public_contact_str, $group_str]; - } - - return $this->select($condition); - } - - /** - * Fetch the default PermissionSet for a given user, create it if it doesn't exist - * - * @param int $uid - * - * @return Entity\PermissionSet - * @throws Exception - */ - public function selectDefaultForUser(int $uid): Entity\PermissionSet - { - $self_contact = Contact::selectFirst(['id'], ['uid' => $uid, 'self' => true]); - - return $this->selectOrCreate($this->factory->createFromString( - $uid, - $this->aclFormatter->toString($self_contact['id']) - )); - } - - /** - * Fetch the public PermissionSet - * - * @param int $uid - * - * @return Entity\PermissionSet - */ - public function selectPublicForUser(int $uid): Entity\PermissionSet - { - return $this->factory->createFromString($uid, '', '', '', '', self::PUBLIC); - } - - /** - * Selects or creates a PermissionSet based on it's fields - * - * @param Entity\PermissionSet $permissionSet - * - * @return Entity\PermissionSet - */ - public function selectOrCreate(Entity\PermissionSet $permissionSet): Entity\PermissionSet - { - if ($permissionSet->id) { - return $permissionSet; - } - - // Don't select/update Public permission sets - if ($permissionSet->isPublic()) { - return $this->selectPublicForUser($permissionSet->uid); - } - - try { - return $this->selectOne($this->convertToTableRow($permissionSet)); - } catch (NotFoundException $exception) { - return $this->save($permissionSet); - } - } - - /** - * @param Entity\PermissionSet $permissionSet - * - * @return Entity\PermissionSet - * @throws NotFoundException - */ - public function save(Entity\PermissionSet $permissionSet): Entity\PermissionSet - { - // Don't save/update the common public PermissionSet - if ($permissionSet->isPublic()) { - return $this->selectPublicForUser($permissionSet->uid); - } - - $fields = $this->convertToTableRow($permissionSet); - - if ($permissionSet->id) { - $this->db->update(self::$table_name, $fields, ['id' => $permissionSet->id]); - } else { - $this->db->insert(self::$table_name, $fields); - - $permissionSet = $this->selectOneById($this->db->lastInsertId(), $permissionSet->uid); - } - - return $permissionSet; - } -} diff --git a/src/Security/PermissionSet/Entity/PermissionSet.php b/src/Security/PermissionSet/Entity/PermissionSet.php index 6a04093315..830c631a4c 100644 --- a/src/Security/PermissionSet/Entity/PermissionSet.php +++ b/src/Security/PermissionSet/Entity/PermissionSet.php @@ -3,7 +3,7 @@ namespace Friendica\Security\PermissionSet\Entity; use Friendica\BaseEntity; -use Friendica\Security\PermissionSet\Depository\PermissionSet as PermissionSetDepository; +use Friendica\Security\PermissionSet\Repository\PermissionSet as PermissionSetRepository; /** * @property-read int|null $id @@ -55,7 +55,7 @@ class PermissionSet extends BaseEntity */ public function isPublic(): bool { - return (($this->id === PermissionSetDepository::PUBLIC) || + return (($this->id === PermissionSetRepository::PUBLIC) || (is_null($this->id) && empty($this->allow_cid) && empty($this->allow_gid) && diff --git a/src/Security/PermissionSet/Repository/PermissionSet.php b/src/Security/PermissionSet/Repository/PermissionSet.php new file mode 100644 index 0000000000..af02646463 --- /dev/null +++ b/src/Security/PermissionSet/Repository/PermissionSet.php @@ -0,0 +1,250 @@ +. + * + */ + +namespace Friendica\Security\PermissionSet\Repository; + +use Exception; +use Friendica\BaseRepository; +use Friendica\Database\Database; +use Friendica\Model\Contact; +use Friendica\Model\Group; +use Friendica\Network\HTTPException\NotFoundException; +use Friendica\Security\PermissionSet\Factory; +use Friendica\Security\PermissionSet\Collection; +use Friendica\Security\PermissionSet\Entity; +use Friendica\Util\ACLFormatter; +use Psr\Log\LoggerInterface; + +class PermissionSet extends BaseRepository +{ + /** @var int Virtual permission set id for public permission */ + const PUBLIC = 0; + + /** @var Factory\PermissionSet */ + protected $factory; + + protected static $table_name = 'permissionset'; + + /** @var ACLFormatter */ + private $aclFormatter; + + public function __construct(Database $database, LoggerInterface $logger, Factory\PermissionSet $factory, ACLFormatter $aclFormatter) + { + parent::__construct($database, $logger, $factory); + + $this->aclFormatter = $aclFormatter; + } + + /** + * replaces the PUBLIC id for the public permissionSet + * (no need to create the default permission set over and over again) + * + * @param $condition + */ + private function checkPublicSelect(&$condition) + { + if (empty($condition['allow_cid']) && + empty($condition['allow_gid']) && + empty($condition['deny_cid']) && + empty($condition['deny_gid'])) { + $condition['uid'] = self::PUBLIC; + } + } + + /** + * @param array $condition + * @param array $params + * + * @return Entity\PermissionSet + * @throws NotFoundException + */ + private function selectOne(array $condition, array $params = []): Entity\PermissionSet + { + return parent::_selectOne($condition, $params); + } + + private function select(array $condition, array $params = []): Collection\PermissionSets + { + return new Collection\PermissionSets(parent::_select($condition, $params)->getArrayCopy()); + } + + /** + * Converts a given PermissionSet into a DB compatible row array + * + * @param Entity\PermissionSet $permissionSet + * + * @return array + */ + protected function convertToTableRow(Entity\PermissionSet $permissionSet): array + { + return [ + 'uid' => $permissionSet->uid, + 'allow_cid' => $this->aclFormatter->toString($permissionSet->allow_cid), + 'allow_gid' => $this->aclFormatter->toString($permissionSet->allow_gid), + 'deny_cid' => $this->aclFormatter->toString($permissionSet->deny_cid), + 'deny_gid' => $this->aclFormatter->toString($permissionSet->deny_gid), + ]; + } + + /** + * @param int $id A PermissionSet table row id or self::PUBLIC + * @param int $uid The owner of the PermissionSet + * @return Entity\PermissionSet + * @throws NotFoundException + */ + public function selectOneById(int $id, int $uid): Entity\PermissionSet + { + if ($id === self::PUBLIC) { + return $this->factory->createFromString($uid); + } + + return $this->selectOne(['id' => $id, 'uid' => $uid]); + } + + /** + * Returns a permission set collection for a given contact + * + * @param int $cid Contact id of the visitor + * @param int $uid User id whom the items belong, used for ownership check. + * + * @return Collection\PermissionSets + */ + public function selectByContactId(int $cid, int $uid): Collection\PermissionSets + { + $cdata = Contact::getPublicAndUserContactID($cid, $uid); + if (!empty($cdata)) { + $public_contact_str = $this->aclFormatter->toString($cdata['public']); + $user_contact_str = $this->aclFormatter->toString($cdata['user']); + $cid = $cdata['user']; + } else { + $public_contact_str = $this->aclFormatter->toString($cid); + $user_contact_str = ''; + } + + $groups = []; + if (!empty($user_contact_str) && $this->db->exists('contact', [ + 'id' => $cid, + 'uid' => $uid, + 'blocked' => false + ])) { + $groups = Group::getIdsByContactId($cid); + } + + $group_str = '<<>>'; // should be impossible to match + foreach ($groups as $group_id) { + $group_str .= '|<' . preg_quote($group_id) . '>'; + } + + if (!empty($user_contact_str)) { + $condition = ["`uid` = ? AND (NOT (`deny_cid` REGEXP ? OR `deny_cid` REGEXP ? OR deny_gid REGEXP ?) + AND (allow_cid REGEXP ? OR allow_cid REGEXP ? OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))", + $uid, $user_contact_str, $public_contact_str, $group_str, + $user_contact_str, $public_contact_str, $group_str]; + } else { + $condition = ["`uid` = ? AND (NOT (`deny_cid` REGEXP ? OR deny_gid REGEXP ?) + AND (allow_cid REGEXP ? OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))", + $uid, $public_contact_str, $group_str, $public_contact_str, $group_str]; + } + + return $this->select($condition); + } + + /** + * Fetch the default PermissionSet for a given user, create it if it doesn't exist + * + * @param int $uid + * + * @return Entity\PermissionSet + * @throws Exception + */ + public function selectDefaultForUser(int $uid): Entity\PermissionSet + { + $self_contact = Contact::selectFirst(['id'], ['uid' => $uid, 'self' => true]); + + return $this->selectOrCreate($this->factory->createFromString( + $uid, + $this->aclFormatter->toString($self_contact['id']) + )); + } + + /** + * Fetch the public PermissionSet + * + * @param int $uid + * + * @return Entity\PermissionSet + */ + public function selectPublicForUser(int $uid): Entity\PermissionSet + { + return $this->factory->createFromString($uid, '', '', '', '', self::PUBLIC); + } + + /** + * Selects or creates a PermissionSet based on it's fields + * + * @param Entity\PermissionSet $permissionSet + * + * @return Entity\PermissionSet + */ + public function selectOrCreate(Entity\PermissionSet $permissionSet): Entity\PermissionSet + { + if ($permissionSet->id) { + return $permissionSet; + } + + // Don't select/update Public permission sets + if ($permissionSet->isPublic()) { + return $this->selectPublicForUser($permissionSet->uid); + } + + try { + return $this->selectOne($this->convertToTableRow($permissionSet)); + } catch (NotFoundException $exception) { + return $this->save($permissionSet); + } + } + + /** + * @param Entity\PermissionSet $permissionSet + * + * @return Entity\PermissionSet + * @throws NotFoundException + */ + public function save(Entity\PermissionSet $permissionSet): Entity\PermissionSet + { + // Don't save/update the common public PermissionSet + if ($permissionSet->isPublic()) { + return $this->selectPublicForUser($permissionSet->uid); + } + + $fields = $this->convertToTableRow($permissionSet); + + if ($permissionSet->id) { + $this->db->update(self::$table_name, $fields, ['id' => $permissionSet->id]); + } else { + $this->db->insert(self::$table_name, $fields); + + $permissionSet = $this->selectOneById($this->db->lastInsertId(), $permissionSet->uid); + } + + return $permissionSet; + } +} diff --git a/tests/src/Profile/ProfileField/Depository/ProfileFieldTest.php b/tests/src/Profile/ProfileField/Depository/ProfileFieldTest.php deleted file mode 100644 index 87ea6ca9c5..0000000000 --- a/tests/src/Profile/ProfileField/Depository/ProfileFieldTest.php +++ /dev/null @@ -1,136 +0,0 @@ -depository = DI::profileField(); - $this->factory = DI::profileFieldFactory(); - $this->permissionSetFactory = DI::permissionSetFactory(); - $this->permissionSetDepository = DI::permissionSet(); - } - - /** - * Test create ProfileField without a valid PermissionSet - */ - public function testSavingWithoutPermissionSet() - { - self::expectExceptionMessage('PermissionSet needs to be saved first.'); - self::expectException(ProfileFieldPersistenceException::class); - - $profileField = $this->factory->createFromValues(42, 0, 'public', 'value', $this->permissionSetFactory->createFromString(42, '', '<~>')); - - self::assertEquals($profileField->uid, $profileField->permissionSet->uid); - - $this->depository->save($profileField); - } - - /** - * Test saving a new entity - */ - public function testSaveNew() - { - $profileField = $this->factory->createFromValues(42, 0, 'public', 'value', $this->permissionSetDepository->save($this->permissionSetFactory->createFromString(42, '', '<~>'))); - - self::assertEquals($profileField->uid, $profileField->permissionSet->uid); - - $savedProfileField = $this->depository->save($profileField); - - self::assertNotNull($savedProfileField->id); - self::assertNull($profileField->id); - - $selectedProfileField = $this->depository->selectOneById($savedProfileField->id); - - self::assertEquals($savedProfileField, $selectedProfileField); - - $profileFields = new ProfileFields([$selectedProfileField]); - $this->depository->deleteCollection($profileFields); - } - - /** - * Test updating the order of a ProfileField - */ - public function testUpdateOrder() - { - $profileField = $this->factory->createFromValues(42, 0, 'public', 'value', $this->permissionSetDepository->save($this->permissionSetFactory->createFromString(42, '', '<~>'))); - - self::assertEquals($profileField->uid, $profileField->permissionSet->uid); - - $savedProfileField = $this->depository->save($profileField); - - self::assertNotNull($savedProfileField->id); - self::assertNull($profileField->id); - - $selectedProfileField = $this->depository->selectOneById($savedProfileField->id); - - self::assertEquals($savedProfileField, $selectedProfileField); - - $selectedProfileField->setOrder(66); - - $updatedOrderProfileField = $this->depository->save($selectedProfileField); - - self::assertEquals($selectedProfileField->id, $updatedOrderProfileField->id); - self::assertEquals(66, $updatedOrderProfileField->order); - - // Even using the ID of the old, saved ProfileField returns the right instance - $updatedFromOldProfileField = $this->depository->selectOneById($savedProfileField->id); - self::assertEquals(66, $updatedFromOldProfileField->order); - - $profileFields = new ProfileFields([$updatedFromOldProfileField]); - $this->depository->deleteCollection($profileFields); - } - - /** - * Test updating a whole entity - */ - public function testUpdate() - { - $profileField = $this->factory->createFromValues(42, 0, 'public', 'value', $this->permissionSetDepository->save($this->permissionSetFactory->createFromString(42, '', '<~>'))); - - self::assertEquals($profileField->uid, $profileField->permissionSet->uid); - - $savedProfileField = $this->depository->save($profileField); - - self::assertNotNull($savedProfileField->id); - self::assertNull($profileField->id); - - $selectedProfileField = $this->depository->selectOneById($savedProfileField->id); - - self::assertEquals($savedProfileField, $selectedProfileField); - - $savedProfileField->update('another', 5, $this->permissionSetDepository->selectPublicForUser(42)); - self::assertEquals(PermissionSet::PUBLIC, $savedProfileField->permissionSet->id); - - $publicProfileField = $this->depository->save($savedProfileField); - - self::assertEquals($this->permissionSetDepository->selectPublicForUser(42), $publicProfileField->permissionSet); - self::assertEquals('another', $publicProfileField->value); - self::assertEquals(5, $publicProfileField->order); - - $profileFields = new ProfileFields([$publicProfileField]); - $this->depository->deleteCollection($profileFields); - } -} diff --git a/tests/src/Profile/ProfileField/Entity/ProfileFieldTest.php b/tests/src/Profile/ProfileField/Entity/ProfileFieldTest.php index 8acd1d94ca..275a1d5972 100644 --- a/tests/src/Profile/ProfileField/Entity/ProfileFieldTest.php +++ b/tests/src/Profile/ProfileField/Entity/ProfileFieldTest.php @@ -6,7 +6,7 @@ use Friendica\Profile\ProfileField\Entity\ProfileField; use Friendica\Profile\ProfileField\Exception\ProfileFieldNotFoundException; use Friendica\Profile\ProfileField\Exception\UnexpectedPermissionSetException; use Friendica\Profile\ProfileField\Factory\ProfileField as ProfileFieldFactory; -use Friendica\Security\PermissionSet\Depository\PermissionSet as PermissionSetDepository; +use Friendica\Security\PermissionSet\Repository\PermissionSet as PermissionSetRepository; use Friendica\Security\PermissionSet\Factory\PermissionSet as PermissionSetFactory; use Friendica\Test\MockedTest; use Friendica\Util\ACLFormatter; @@ -16,8 +16,8 @@ use Mockery\MockInterface; class ProfileFieldTest extends MockedTest { - /** @var MockInterface|PermissionSetDepository */ - protected $permissionSetDepository; + /** @var MockInterface|PermissionSetRepository */ + protected $permissionSetRepository; /** @var ProfileFieldFactory */ protected $profileFieldFactory; /** @var MockInterface|PermissionSetFactory */ @@ -27,7 +27,7 @@ class ProfileFieldTest extends MockedTest { parent::setUp(); - $this->permissionSetDepository = \Mockery::mock(PermissionSetDepository::class); + $this->permissionSetRepository = \Mockery::mock(PermissionSetRepository::class); $this->permissionSetFactory = new PermissionSetFactory(new VoidLogger(), new ACLFormatter()); $this->profileFieldFactory = new ProfileFieldFactory(new VoidLogger(), $this->permissionSetFactory); } @@ -180,7 +180,7 @@ class ProfileFieldTest extends MockedTest $permissionSet = $this->permissionSetFactory->createFromTableRow(['uid' => $uid, 'id' => $psid]); - $this->permissionSetDepository->shouldReceive('selectOneById')->with($psid, $uid)->andReturns($permissionSet); + $this->permissionSetRepository->shouldReceive('selectOneById')->with($psid, $uid)->andReturns($permissionSet); self::assertEquals($psid, $entity->permissionSet->id); } diff --git a/tests/src/Profile/ProfileField/Repository/ProfileFieldTest.php b/tests/src/Profile/ProfileField/Repository/ProfileFieldTest.php new file mode 100644 index 0000000000..0da41f51c2 --- /dev/null +++ b/tests/src/Profile/ProfileField/Repository/ProfileFieldTest.php @@ -0,0 +1,136 @@ +depository = DI::profileField(); + $this->factory = DI::profileFieldFactory(); + $this->permissionSetFactory = DI::permissionSetFactory(); + $this->permissionSetRepository = DI::permissionSet(); + } + + /** + * Test create ProfileField without a valid PermissionSet + */ + public function testSavingWithoutPermissionSet() + { + self::expectExceptionMessage('PermissionSet needs to be saved first.'); + self::expectException(ProfileFieldPersistenceException::class); + + $profileField = $this->factory->createFromValues(42, 0, 'public', 'value', $this->permissionSetFactory->createFromString(42, '', '<~>')); + + self::assertEquals($profileField->uid, $profileField->permissionSet->uid); + + $this->depository->save($profileField); + } + + /** + * Test saving a new entity + */ + public function testSaveNew() + { + $profileField = $this->factory->createFromValues(42, 0, 'public', 'value', $this->permissionSetRepository->save($this->permissionSetFactory->createFromString(42, '', '<~>'))); + + self::assertEquals($profileField->uid, $profileField->permissionSet->uid); + + $savedProfileField = $this->depository->save($profileField); + + self::assertNotNull($savedProfileField->id); + self::assertNull($profileField->id); + + $selectedProfileField = $this->depository->selectOneById($savedProfileField->id); + + self::assertEquals($savedProfileField, $selectedProfileField); + + $profileFields = new ProfileFields([$selectedProfileField]); + $this->depository->deleteCollection($profileFields); + } + + /** + * Test updating the order of a ProfileField + */ + public function testUpdateOrder() + { + $profileField = $this->factory->createFromValues(42, 0, 'public', 'value', $this->permissionSetRepository->save($this->permissionSetFactory->createFromString(42, '', '<~>'))); + + self::assertEquals($profileField->uid, $profileField->permissionSet->uid); + + $savedProfileField = $this->depository->save($profileField); + + self::assertNotNull($savedProfileField->id); + self::assertNull($profileField->id); + + $selectedProfileField = $this->depository->selectOneById($savedProfileField->id); + + self::assertEquals($savedProfileField, $selectedProfileField); + + $selectedProfileField->setOrder(66); + + $updatedOrderProfileField = $this->depository->save($selectedProfileField); + + self::assertEquals($selectedProfileField->id, $updatedOrderProfileField->id); + self::assertEquals(66, $updatedOrderProfileField->order); + + // Even using the ID of the old, saved ProfileField returns the right instance + $updatedFromOldProfileField = $this->depository->selectOneById($savedProfileField->id); + self::assertEquals(66, $updatedFromOldProfileField->order); + + $profileFields = new ProfileFields([$updatedFromOldProfileField]); + $this->depository->deleteCollection($profileFields); + } + + /** + * Test updating a whole entity + */ + public function testUpdate() + { + $profileField = $this->factory->createFromValues(42, 0, 'public', 'value', $this->permissionSetRepository->save($this->permissionSetFactory->createFromString(42, '', '<~>'))); + + self::assertEquals($profileField->uid, $profileField->permissionSet->uid); + + $savedProfileField = $this->depository->save($profileField); + + self::assertNotNull($savedProfileField->id); + self::assertNull($profileField->id); + + $selectedProfileField = $this->depository->selectOneById($savedProfileField->id); + + self::assertEquals($savedProfileField, $selectedProfileField); + + $savedProfileField->update('another', 5, $this->permissionSetRepository->selectPublicForUser(42)); + self::assertEquals(PermissionSet::PUBLIC, $savedProfileField->permissionSet->id); + + $publicProfileField = $this->depository->save($savedProfileField); + + self::assertEquals($this->permissionSetRepository->selectPublicForUser(42), $publicProfileField->permissionSet); + self::assertEquals('another', $publicProfileField->value); + self::assertEquals(5, $publicProfileField->order); + + $profileFields = new ProfileFields([$publicProfileField]); + $this->depository->deleteCollection($profileFields); + } +} diff --git a/tests/src/Security/PermissionSet/Depository/PermissionSetTest.php b/tests/src/Security/PermissionSet/Depository/PermissionSetTest.php deleted file mode 100644 index 596b1e6c15..0000000000 --- a/tests/src/Security/PermissionSet/Depository/PermissionSetTest.php +++ /dev/null @@ -1,64 +0,0 @@ -depository = DI::permissionSet(); - $this->factory = DI::permissionSetFactory(); - } - - public function testSelectOneByIdPublic() - { - $permissionSet = $this->depository->selectPublicForUser(1); - - $this->assertInstanceOf(PermissionSet::class, $permissionSet); - self::assertEmpty($permissionSet->allow_cid); - self::assertEmpty($permissionSet->allow_gid); - self::assertEmpty($permissionSet->deny_cid); - self::assertEmpty($permissionSet->deny_gid); - self::assertEmpty(PermissionSetDepository::PUBLIC, $permissionSet->id); - self::assertEquals(1, $permissionSet->uid); - } - - /** - * Test create/update PermissionSets - */ - public function testSaving() - { - $permissionSet = $this->factory->createFromString(42, '', '<~>'); - - $permissionSet = $this->depository->selectOrCreate($permissionSet); - - self::assertNotNull($permissionSet->id); - - $permissionSetSelected = $this->depository->selectOneById($permissionSet->id, 42); - - self::assertEquals($permissionSet, $permissionSetSelected); - - $newPermissionSet = $permissionSet->withAllowedContacts(['1', '2']); - $savedPermissionSet = $this->depository->save($newPermissionSet); - - self::assertNotNull($savedPermissionSet->id); - self::assertNull($newPermissionSet->id); - - $permissionSetSavedSelected = $this->depository->selectOneById($savedPermissionSet->id, 42); - - self::assertEquals($savedPermissionSet, $permissionSetSavedSelected); - } -} diff --git a/tests/src/Security/PermissionSet/Repository/PermissionSetTest.php b/tests/src/Security/PermissionSet/Repository/PermissionSetTest.php new file mode 100644 index 0000000000..697c5885ff --- /dev/null +++ b/tests/src/Security/PermissionSet/Repository/PermissionSetTest.php @@ -0,0 +1,64 @@ +repository = DI::permissionSet(); + $this->factory = DI::permissionSetFactory(); + } + + public function testSelectOneByIdPublic() + { + $permissionSet = $this->repository->selectPublicForUser(1); + + $this->assertInstanceOf(PermissionSet::class, $permissionSet); + self::assertEmpty($permissionSet->allow_cid); + self::assertEmpty($permissionSet->allow_gid); + self::assertEmpty($permissionSet->deny_cid); + self::assertEmpty($permissionSet->deny_gid); + self::assertEmpty(PermissionSetRepository::PUBLIC, $permissionSet->id); + self::assertEquals(1, $permissionSet->uid); + } + + /** + * Test create/update PermissionSets + */ + public function testSaving() + { + $permissionSet = $this->factory->createFromString(42, '', '<~>'); + + $permissionSet = $this->repository->selectOrCreate($permissionSet); + + self::assertNotNull($permissionSet->id); + + $permissionSetSelected = $this->repository->selectOneById($permissionSet->id, 42); + + self::assertEquals($permissionSet, $permissionSetSelected); + + $newPermissionSet = $permissionSet->withAllowedContacts(['1', '2']); + $savedPermissionSet = $this->repository->save($newPermissionSet); + + self::assertNotNull($savedPermissionSet->id); + self::assertNull($newPermissionSet->id); + + $permissionSetSavedSelected = $this->repository->selectOneById($savedPermissionSet->id, 42); + + self::assertEquals($savedPermissionSet, $permissionSetSavedSelected); + } +} diff --git a/update.php b/update.php index 85f407d36c..a2999ed5a5 100644 --- a/update.php +++ b/update.php @@ -55,7 +55,7 @@ use Friendica\Model\Photo; use Friendica\Model\Post; use Friendica\Model\Profile; use Friendica\Model\Storage; -use Friendica\Security\PermissionSet\Depository\PermissionSet; +use Friendica\Security\PermissionSet\Repository\PermissionSet; use Friendica\Worker\Delivery; // Post-update script of PR 5751