3 * @copyright Copyright (C) 2010-2023, the Friendica project
5 * @license GNU AGPL version 3 or any later version
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 namespace Friendica\Profile\ProfileField\Repository;
24 use Friendica\BaseRepository;
25 use Friendica\Database\Database;
26 use Friendica\Profile\ProfileField\Exception\ProfileFieldNotFoundException;
27 use Friendica\Profile\ProfileField\Exception\ProfileFieldPersistenceException;
28 use Friendica\Profile\ProfileField\Exception\UnexpectedPermissionSetException;
29 use Friendica\Profile\ProfileField\Factory;
30 use Friendica\Profile\ProfileField\Entity;
31 use Friendica\Profile\ProfileField\Collection;
32 use Friendica\Security\PermissionSet\Repository\PermissionSet as PermissionSetRepository;
33 use Friendica\Util\DateTimeFormat;
34 use Psr\Log\LoggerInterface;
36 class ProfileField extends BaseRepository
38 /** @var Factory\ProfileField */
41 protected static $table_name = 'profile_field';
43 protected static $view_name = 'profile_field-view';
45 /** @var PermissionSetRepository */
46 protected $permissionSetRepository;
48 public function __construct(Database $database, LoggerInterface $logger, Factory\ProfileField $factory, PermissionSetRepository $permissionSetRepository)
50 parent::__construct($database, $logger, $factory);
52 $this->permissionSetRepository = $permissionSetRepository;
56 * @param array $condition
57 * @param array $params
59 * @return Entity\ProfileField
61 * @throws ProfileFieldNotFoundException
62 * @throws UnexpectedPermissionSetException
64 private function selectOne(array $condition, array $params = []): Entity\ProfileField
66 $fields = $this->db->selectFirst(static::$view_name, [], $condition, $params);
67 if (!$this->db->isResult($fields)) {
68 throw new ProfileFieldNotFoundException();
71 return $this->factory->createFromTableRow($fields);
75 * @param array $condition
76 * @param array $params
78 * @return Collection\ProfileFields
80 * @throws ProfileFieldPersistenceException In case of underlying persistence exceptions
81 * @throws UnexpectedPermissionSetException
83 private function select(array $condition, array $params = []): Collection\ProfileFields
85 $rows = $this->db->selectToArray(static::$view_name, [], $condition, $params);
87 $Entities = new Collection\ProfileFields();
88 foreach ($rows as $fields) {
89 $Entities[] = $this->factory->createFromTableRow($fields);
96 * Converts a given ProfileField into a DB compatible row array
98 * @param Entity\ProfileField $profileField
102 protected function convertToTableRow(Entity\ProfileField $profileField): array
105 'uid' => $profileField->uid,
106 'label' => $profileField->label,
107 'value' => $profileField->value,
108 'order' => $profileField->order,
109 'created' => $profileField->created->format(DateTimeFormat::MYSQL),
110 'edited' => $profileField->edited->format(DateTimeFormat::MYSQL),
111 'psid' => $profileField->permissionSet->id
116 * Returns all public available ProfileFields for a specific user
118 * @param int $uid the user id
120 * @return Collection\ProfileFields
122 * @throws ProfileFieldPersistenceException In case of underlying persistence exceptions
124 public function selectPublicFieldsByUserId(int $uid): Collection\ProfileFields
127 $publicPermissionSet = $this->permissionSetRepository->selectPublicForUser($uid);
129 return $this->select([
131 'psid' => $publicPermissionSet->id
133 } catch (\Exception $exception) {
134 throw new ProfileFieldPersistenceException(sprintf('Cannot select public ProfileField for user "%d"', $uid), $exception);
139 * @param int $uid Field owner user Id
141 * @throws ProfileFieldPersistenceException In case of underlying persistence exceptions
143 public function selectByUserId(int $uid): Collection\ProfileFields
146 return $this->select(
148 ['order' => ['order']]
150 } catch (\Exception $exception) {
151 throw new ProfileFieldPersistenceException(sprintf('Cannot select ProfileField for user "%d"', $uid), $exception);
156 * Retrieve all custom profile field a given contact is able to access to, including public profile fields.
158 * @param int $cid Private contact id, must be owned by $uid
159 * @param int $uid Field owner user id
163 public function selectByContactId(int $cid, int $uid): Collection\ProfileFields
165 $permissionSets = $this->permissionSetRepository->selectByContactId($cid, $uid);
167 $permissionSetIds = $permissionSets->column('id');
169 // Includes public custom fields
170 $permissionSetIds[] = $this->permissionSetRepository->selectPublicForUser($uid)->id;
172 return $this->select(
173 ['uid' => $uid, 'psid' => $permissionSetIds],
174 ['order' => ['order']]
181 * @return Entity\ProfileField
183 * @ProfileFieldNotFoundException In case there is no ProfileField found
185 public function selectOneById(int $id): Entity\ProfileField
188 return $this->selectOne(['id' => $id]);
189 } catch (\Exception $exception) {
190 throw new ProfileFieldNotFoundException(sprintf('Cannot find Profile "%s"', $id), $exception);
195 * Delets a whole collection of ProfileFields
197 * @param Collection\ProfileFields $profileFields
200 * @throws ProfileFieldPersistenceException in case the persistence layer cannot delete the ProfileFields
202 public function deleteCollection(Collection\ProfileFields $profileFields): bool
205 return $this->db->delete(self::$table_name, ['id' => $profileFields->column('id')]);
206 } catch (\Exception $exception) {
207 throw new ProfileFieldPersistenceException('Cannot delete ProfileFields', $exception);
212 * @param Entity\ProfileField $profileField
214 * @return Entity\ProfileField
215 * @throws ProfileFieldPersistenceException in case the persistence layer cannot save the ProfileField
217 public function save(Entity\ProfileField $profileField): Entity\ProfileField
219 if ($profileField->permissionSet->id === null) {
220 throw new ProfileFieldPersistenceException('PermissionSet needs to be saved first.');
223 $fields = $this->convertToTableRow($profileField);
226 if ($profileField->id) {
227 $this->db->update(self::$table_name, $fields, ['id' => $profileField->id]);
229 $this->db->insert(self::$table_name, $fields);
231 $profileField = $this->selectOneById($this->db->lastInsertId());
233 } catch (\Exception $exception) {
234 throw new ProfileFieldPersistenceException(sprintf('Cannot save ProfileField with id "%d" and label "%s"', $profileField->id, $profileField->label), $exception);
237 return $profileField;
240 public function saveCollectionForUser(int $uid, Collection\ProfileFields $profileFields): Collection\ProfileFields
242 $savedProfileFields = new Collection\ProfileFields();
244 $profileFieldsOld = $this->selectByUserId($uid);
246 // Prunes profile field whose label has been emptied
247 $labels = $profileFields->column('label');
248 $prunedProfileFieldsOld = $profileFieldsOld->filter(function (Entity\ProfileField $profileFieldOld) use ($labels) {
249 return array_search($profileFieldOld->label, $labels) === false;
251 $this->deleteCollection($prunedProfileFieldsOld);
253 // Update the order based on the new Profile Field Collection
255 $labelProfileFieldsOld = array_flip($profileFieldsOld->column('label'));
257 foreach ($profileFields as $profileField) {
258 // Update existing field (preserve
259 if (array_key_exists($profileField->label, $labelProfileFieldsOld)) {
260 $profileFieldOldId = $labelProfileFieldsOld[$profileField->label];
261 /** @var Entity\ProfileField $foundProfileFieldOld */
262 $foundProfileFieldOld = $profileFieldsOld[$profileFieldOldId];
263 $foundProfileFieldOld->update(
264 $profileField->value,
266 $profileField->permissionSet
269 $savedProfileFields->append($this->save($foundProfileFieldOld));
271 $profileField->setOrder($order);
272 $savedProfileFields->append($this->save($profileField));
278 return $savedProfileFields;