3 namespace Friendica\Repository;
5 use Friendica\BaseModel;
6 use Friendica\BaseRepository;
7 use Friendica\Collection;
8 use Friendica\Core\L10n;
9 use Friendica\Database\Database;
10 use Friendica\Database\DBA;
12 use Friendica\Util\ACLFormatter;
13 use Friendica\Util\DateTimeFormat;
14 use Psr\Log\LoggerInterface;
16 class ProfileField extends BaseRepository
18 protected static $table_name = 'profile_field';
20 protected static $model_class = Model\ProfileField::class;
22 protected static $collection_class = Collection\ProfileFields::class;
24 /** @var PermissionSet */
25 private $permissionSet;
26 /** @var ACLFormatter */
27 private $aclFormatter;
31 public function __construct(Database $dba, LoggerInterface $logger, PermissionSet $permissionSet, ACLFormatter $aclFormatter, L10n $l10n)
33 parent::__construct($dba, $logger);
35 $this->permissionSet = $permissionSet;
36 $this->aclFormatter = $aclFormatter;
42 * @return Model\ProfileField
44 protected function create(array $data)
46 return new Model\ProfileField($this->dba, $this->logger, $this->permissionSet, $data);
50 * @param array $condition
51 * @return Model\ProfileField
52 * @throws \Friendica\Network\HTTPException\NotFoundException
54 public function selectFirst(array $condition)
56 return parent::selectFirst($condition);
60 * @param array $condition
61 * @param array $params
62 * @return Collection\ProfileFields
65 public function select(array $condition = [], array $params = [])
67 return parent::select($condition, $params);
71 * @param array $condition
72 * @param array $params
73 * @param int|null $max_id
74 * @param int|null $since_id
76 * @return Collection\ProfileFields
79 public function selectByBoundaries(array $condition = [], array $params = [], int $max_id = null, int $since_id = null, int $limit = self::LIMIT)
81 return parent::selectByBoundaries($condition, $params, $max_id, $since_id, $limit);
85 * @param int $uid Field owner user Id
86 * @return Collection\ProfileFields
89 public function selectByUserId(int $uid)
93 ['order' => ['order']]
98 * Retrieve all custom profile field a given contact is able to access to, including public profile fields.
100 * @param int $cid Private contact id, must be owned by $uid
101 * @param int $uid Field owner user id
102 * @return Collection\ProfileFields
105 public function selectByContactId(int $cid, int $uid)
107 $permissionSets = $this->permissionSet->selectByContactId($cid, $uid);
109 $psids = $permissionSets->column('id');
111 // Includes public custom fields
114 return $this->select(
115 ['uid' => $uid, 'psid' => $psids],
116 ['order' => ['order']]
121 * @param array $fields
122 * @return Model\ProfileField|bool
125 public function insert(array $fields)
127 $fields['created'] = DateTimeFormat::utcNow();
128 $fields['edited'] = DateTimeFormat::utcNow();
130 return parent::insert($fields);
134 * @param Model\ProfileField $model
138 public function update(BaseModel $model)
140 $model->edited = DateTimeFormat::utcNow();
142 return parent::update($model);
146 * @param int $uid User Id
147 * @param Collection\ProfileFields $profileFields Collection of existing profile fields
148 * @param array $profileFieldInputs Array of profile field form inputs indexed by profile field id
149 * @param array $profileFieldOrder List of profile field id in order
150 * @return Collection\ProfileFields
153 public function updateCollectionFromForm(int $uid, Collection\ProfileFields $profileFields, array $profileFieldInputs, array $profileFieldOrder)
155 // Returns an associative array of id => order values
156 $profileFieldOrder = array_flip($profileFieldOrder);
158 // Creation of the new field
159 if (!empty($profileFieldInputs['new']['label'])) {
160 $psid = $this->permissionSet->getIdFromACL(
162 $this->aclFormatter->toString($profileFieldInputs['new']['contact_allow'] ?? ''),
163 $this->aclFormatter->toString($profileFieldInputs['new']['group_allow'] ?? ''),
164 $this->aclFormatter->toString($profileFieldInputs['new']['contact_deny'] ?? ''),
165 $this->aclFormatter->toString($profileFieldInputs['new']['group_deny'] ?? '')
168 $newProfileField = $this->insert([
170 'label' => $profileFieldInputs['new']['label'],
171 'value' => $profileFieldInputs['new']['value'],
173 'order' => $profileFieldOrder['new'],
176 $profileFieldInputs[$newProfileField->id] = $profileFieldInputs['new'];
177 $profileFieldOrder[$newProfileField->id] = $profileFieldOrder['new'];
179 $profileFields[] = $newProfileField;
182 unset($profileFieldInputs['new']);
183 unset($profileFieldOrder['new']);
185 // Prunes profile field whose label has been emptied
186 $profileFields = $profileFields->filter(function (Model\ProfileField $profileField) use (&$profileFieldInputs, &$profileFieldOrder) {
187 $keepModel = !isset($profileFieldInputs[$profileField->id]) || !empty($profileFieldInputs[$profileField->id]['label']);
190 unset($profileFieldInputs[$profileField->id]);
191 unset($profileFieldOrder[$profileField->id]);
192 $this->delete($profileField);
198 // Regenerates the order values if items were deleted
199 $profileFieldOrder = array_flip(array_keys($profileFieldOrder));
201 // Update existing profile fields from form values
202 $profileFields = $profileFields->map(function (Model\ProfileField $profileField) use ($uid, &$profileFieldInputs, &$profileFieldOrder) {
203 if (isset($profileFieldInputs[$profileField->id]) && isset($profileFieldOrder[$profileField->id])) {
204 $psid = $this->permissionSet->getIdFromACL(
206 $this->aclFormatter->toString($profileFieldInputs[$profileField->id]['contact_allow'] ?? ''),
207 $this->aclFormatter->toString($profileFieldInputs[$profileField->id]['group_allow'] ?? ''),
208 $this->aclFormatter->toString($profileFieldInputs[$profileField->id]['contact_deny'] ?? ''),
209 $this->aclFormatter->toString($profileFieldInputs[$profileField->id]['group_deny'] ?? '')
212 $profileField->psid = $psid;
213 $profileField->label = $profileFieldInputs[$profileField->id]['label'];
214 $profileField->value = $profileFieldInputs[$profileField->id]['value'];
215 $profileField->order = $profileFieldOrder[$profileField->id];
217 unset($profileFieldInputs[$profileField->id]);
218 unset($profileFieldOrder[$profileField->id]);
221 return $profileField;
224 return $profileFields;
228 * Migrates a legacy profile to the new slimmer profile with extra custom fields.
229 * Multi profiles are converted to ACl-protected custom fields and deleted.
231 * @param array $profile Profile table row
234 public function migrateFromLegacyProfile(array $profile)
236 // Already processed, aborting
237 if ($profile['is-default'] === null) {
241 if (!$profile['is-default']) {
242 $contacts = Model\Contact::selectToArray(['id'], ['uid' => $profile['uid'], 'profile-id' => $profile['id']]);
243 if (!count($contacts)) {
244 // No contact visibility selected defaults to user-only permission
245 $contacts = Model\Contact::selectToArray(['id'], ['uid' => $profile['uid'], 'self' => true]);
248 $allow_cid = $this->aclFormatter->toString(array_column($contacts, 'id'));
251 $psid = $this->permissionSet->getIdFromACL($profile['uid'], $allow_cid ?? '');
256 'hometown' => $this->l10n->t('Hometown:'),
257 'gender' => $this->l10n->t('Gender:'),
258 'marital' => $this->l10n->t('Marital Status:'),
259 'with' => $this->l10n->t('With:'),
260 'howlong' => $this->l10n->t('Since:'),
261 'sexual' => $this->l10n->t('Sexual Preference:'),
262 'politic' => $this->l10n->t('Political Views:'),
263 'religion' => $this->l10n->t('Religious Views:'),
264 'likes' => $this->l10n->t('Likes:'),
265 'dislikes' => $this->l10n->t('Dislikes:'),
266 'about' => $this->l10n->t('About:'),
267 'summary' => $this->l10n->t('Summary'),
268 'music' => $this->l10n->t('Musical interests'),
269 'book' => $this->l10n->t('Books, literature'),
270 'tv' => $this->l10n->t('Television'),
271 'film' => $this->l10n->t('Film/dance/culture/entertainment'),
272 'interest' => $this->l10n->t('Hobbies/Interests'),
273 'romance' => $this->l10n->t('Love/romance'),
274 'work' => $this->l10n->t('Work/employment'),
275 'education' => $this->l10n->t('School/education'),
276 'contact' => $this->l10n->t('Contact information and Social Networks'),
279 foreach ($custom_fields as $field => $label) {
280 if (!empty($profile[$field]) && $profile[$field] > DBA::NULL_DATE && $profile[$field] > DBA::NULL_DATETIME) {
282 'uid' => $profile['uid'],
285 'label' => trim($label, ':'),
286 'value' => $profile[$field],
290 $profile[$field] = null;
293 if ($profile['is-default']) {
294 $profile['profile-name'] = null;
295 $profile['is-default'] = null;
296 $this->dba->update('profile', $profile, ['id' => $profile['id']]);
297 } elseif (!empty($profile['id'])) {
298 $this->dba->delete('profile', ['id' => $profile['id']]);