Merge pull request #10860 from nupplaphil/feat/depository_profilefield

Move ProfileField to Depository Paradigm
This commit is contained in:
Hypolite Petovan 2021-10-18 18:52:33 -04:00 committed by GitHub
commit 235eab0d99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1519 additions and 644 deletions

View File

@ -1,6 +1,6 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 2021.12-dev (Siberian Iris) -- Friendica 2021.12-dev (Siberian Iris)
-- DB_UPDATE_VERSION 1439 -- DB_UPDATE_VERSION 1440
-- ------------------------------------------ -- ------------------------------------------
@ -2587,3 +2587,23 @@ CREATE VIEW `workerqueue-view` AS SELECT
FROM `process` FROM `process`
INNER JOIN `workerqueue` ON `workerqueue`.`pid` = `process`.`pid` INNER JOIN `workerqueue` ON `workerqueue`.`pid` = `process`.`pid`
WHERE NOT `workerqueue`.`done`; WHERE NOT `workerqueue`.`done`;
--
-- VIEW profile_field-view
--
DROP VIEW IF EXISTS `profile_field-view`;
CREATE VIEW `profile_field-view` AS SELECT
`profile_field`.`id` AS `id`,
`profile_field`.`uid` AS `uid`,
`profile_field`.`label` AS `label`,
`profile_field`.`value` AS `value`,
`profile_field`.`order` AS `order`,
`profile_field`.`psid` AS `psid`,
`permissionset`.`allow_cid` AS `allow_cid`,
`permissionset`.`allow_gid` AS `allow_gid`,
`permissionset`.`deny_cid` AS `deny_cid`,
`permissionset`.`deny_gid` AS `deny_gid`,
`profile_field`.`created` AS `created`,
`profile_field`.`edited` AS `edited`
FROM `profile_field`
INNER JOIN `permissionset` ON `permissionset`.`id` = `profile_field`.`psid`;

View File

@ -87,7 +87,7 @@ class BaseCollection extends \ArrayIterator
*/ */
public function column($column, $index_key = null) public function column($column, $index_key = null)
{ {
return array_column($this->getArrayCopy(), $column, $index_key); return array_column($this->getArrayCopy(true), $column, $index_key);
} }
/** /**
@ -124,4 +124,21 @@ class BaseCollection extends \ArrayIterator
{ {
return new static(array_reverse($this->getArrayCopy()), $this->getTotalCount()); return new static(array_reverse($this->getArrayCopy()), $this->getTotalCount());
} }
/**
* @inheritDoc
*
* includes recursion for entity::toArray() function
* @see BaseEntity::toArray()
*/
public function getArrayCopy(bool $recursive = false): array
{
if (!$recursive) {
return parent::getArrayCopy();
}
return array_map(function ($item) {
return is_object($item) ? $item->toArray() : $item;
}, parent::getArrayCopy());
}
} }

View File

@ -53,4 +53,18 @@ abstract class BaseEntity extends BaseDataTransferObject
return $this->$name; return $this->$name;
} }
/**
* @param $name
* @return bool
* @throws HTTPException\InternalServerErrorException
*/
public function __isset($name)
{
if (!property_exists($this, $name)) {
throw new HTTPException\InternalServerErrorException('Unknown property ' . $name . ' in Entity ' . static::class);
}
return !empty($this->$name);
}
} }

View File

@ -25,6 +25,7 @@ use Friendica\Database\DBA;
use Friendica\Database\DBStructure; use Friendica\Database\DBStructure;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Photo; use Friendica\Model\Photo;
use Friendica\Model\Profile;
use Friendica\Object\Image; use Friendica\Object\Image;
use Friendica\Security\PermissionSet\Depository\PermissionSet; use Friendica\Security\PermissionSet\Depository\PermissionSet;
use Friendica\Util\Strings; use Friendica\Util\Strings;
@ -278,7 +279,7 @@ class UserImport
$profile['id'] = DBA::lastInsertId(); $profile['id'] = DBA::lastInsertId();
} }
DI::profileField()->migrateFromLegacyProfile($profile); Profile::migrate($profile);
} }
$permissionSet = DI::permissionSet()->selectDefaultForUser($newuid); $permissionSet = DI::permissionSet()->selectDefaultForUser($newuid);

View File

@ -452,12 +452,14 @@ abstract class DI
return self::$dice->create(Security\PermissionSet\Factory\PermissionSet::class); return self::$dice->create(Security\PermissionSet\Factory\PermissionSet::class);
} }
/** public static function profileField(): Profile\ProfileField\Depository\ProfileField
* @return Repository\ProfileField
*/
public static function profileField()
{ {
return self::$dice->create(Repository\ProfileField::class); return self::$dice->create(Profile\ProfileField\Depository\ProfileField::class);
}
public static function profileFieldFactory(): Profile\ProfileField\Factory\ProfileField
{
return self::$dice->create(Profile\ProfileField\Factory\ProfileField::class);
} }
public static function notification(): Navigation\Notifications\Depository\Notification public static function notification(): Navigation\Notifications\Depository\Notification

View File

@ -27,8 +27,7 @@ use Friendica\Collection\Api\Mastodon\Fields;
use Friendica\Model\APContact; use Friendica\Model\APContact;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Repository\ProfileField; use Friendica\Profile\ProfileField\Depository\ProfileField as ProfileFieldDepository;
use Friendica\Security\PermissionSet\Depository\PermissionSet;
use ImagickException; use ImagickException;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -36,17 +35,17 @@ class Account extends BaseFactory
{ {
/** @var BaseURL */ /** @var BaseURL */
private $baseUrl; private $baseUrl;
/** @var ProfileField */ /** @var ProfileFieldDepository */
private $profileFieldRepo; private $profileFieldDepo;
/** @var Field */ /** @var Field */
private $mstdnFieldFactory; private $mstdnFieldFactory;
public function __construct(LoggerInterface $logger, BaseURL $baseURL, ProfileField $profileFieldRepo, Field $mstdnFieldFactory) public function __construct(LoggerInterface $logger, BaseURL $baseURL, ProfileFieldDepository $profileFieldDepo, Field $mstdnFieldFactory)
{ {
parent::__construct($logger); parent::__construct($logger);
$this->baseUrl = $baseURL; $this->baseUrl = $baseURL;
$this->profileFieldRepo = $profileFieldRepo; $this->profileFieldDepo = $profileFieldDepo;
$this->mstdnFieldFactory = $mstdnFieldFactory; $this->mstdnFieldFactory = $mstdnFieldFactory;
} }
@ -77,7 +76,7 @@ class Account extends BaseFactory
$self_contact = Contact::selectFirst(['uid'], ['nurl' => $publicContact['nurl'], 'self' => true]); $self_contact = Contact::selectFirst(['uid'], ['nurl' => $publicContact['nurl'], 'self' => true]);
if (!empty($self_contact['uid'])) { if (!empty($self_contact['uid'])) {
$profileFields = $this->profileFieldRepo->select(['uid' => $self_contact['uid'], 'psid' => PermissionSet::PUBLIC]); $profileFields = $this->profileFieldDepo->selectPublicFieldsByUserId($self_contact['uid']);
$fields = $this->mstdnFieldFactory->createFromProfileFields($profileFields); $fields = $this->mstdnFieldFactory->createFromProfileFields($profileFields);
} else { } else {
$fields = new Fields(); $fields = new Fields();
@ -95,7 +94,7 @@ class Account extends BaseFactory
{ {
$publicContact = Contact::selectFirst([], ['uid' => $userId, 'self' => true]); $publicContact = Contact::selectFirst([], ['uid' => $userId, 'self' => true]);
$profileFields = $this->profileFieldRepo->select(['uid' => $userId, 'psid' => PermissionSet::PUBLIC]); $profileFields = $this->profileFieldDepo->selectPublicFieldsByUserId($userId);
$fields = $this->mstdnFieldFactory->createFromProfileFields($profileFields); $fields = $this->mstdnFieldFactory->createFromProfileFields($profileFields);
$apContact = APContact::getByURL($publicContact['url'], false); $apContact = APContact::getByURL($publicContact['url'], false);

View File

@ -23,15 +23,16 @@ namespace Friendica\Factory\Api\Mastodon;
use Friendica\BaseFactory; use Friendica\BaseFactory;
use Friendica\Collection\Api\Mastodon\Fields; use Friendica\Collection\Api\Mastodon\Fields;
use Friendica\Collection\ProfileFields; use Friendica\Profile\ProfileField\Collection\ProfileFields;
use Friendica\Content\Text\BBCode; use Friendica\Content\Text\BBCode;
use Friendica\Model\ProfileField; use Friendica\Profile\ProfileField\Entity\ProfileField;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
class Field extends BaseFactory class Field extends BaseFactory
{ {
/** /**
* @param ProfileField $profileField * @param ProfileField $profileField
*
* @return \Friendica\Object\Api\Mastodon\Field * @return \Friendica\Object\Api\Mastodon\Field
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */

View File

@ -38,6 +38,7 @@ use Friendica\DI;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Protocol\Activity; use Friendica\Protocol\Activity;
use Friendica\Protocol\Diaspora; use Friendica\Protocol\Diaspora;
use Friendica\Security\PermissionSet\Entity\PermissionSet;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\HTTPSignature; use Friendica\Util\HTTPSignature;
use Friendica\Util\Network; use Friendica\Util\Network;
@ -936,4 +937,86 @@ class Profile
return ['total' => $total, 'entries' => $profiles]; return ['total' => $total, 'entries' => $profiles];
} }
/**
* Migrates a legacy profile to the new slimmer profile with extra custom fields.
* Multi profiles are converted to ACl-protected custom fields and deleted.
*
* @param array $profile One profile array
* @throws \Exception
*/
public static function migrate(array $profile)
{
// Already processed, aborting
if ($profile['is-default'] === null) {
return;
}
$contacts = [];
if (!$profile['is-default']) {
$contacts = Contact::selectToArray(['id'], [
'uid' => $profile['uid'],
'profile-id' => $profile['id']
]);
if (!count($contacts)) {
// No contact visibility selected defaults to user-only permission
$contacts = Contact::selectToArray(['id'], ['uid' => $profile['uid'], 'self' => true]);
}
}
$permissionSet = DI::permissionSet()->selectOrCreate(
new PermissionSet(
$profile['uid'],
array_column($contacts, 'id') ?? []
)
);
$order = 1;
$custom_fields = [
'hometown' => DI::l10n()->t('Hometown:'),
'marital' => DI::l10n()->t('Marital Status:'),
'with' => DI::l10n()->t('With:'),
'howlong' => DI::l10n()->t('Since:'),
'sexual' => DI::l10n()->t('Sexual Preference:'),
'politic' => DI::l10n()->t('Political Views:'),
'religion' => DI::l10n()->t('Religious Views:'),
'likes' => DI::l10n()->t('Likes:'),
'dislikes' => DI::l10n()->t('Dislikes:'),
'pdesc' => DI::l10n()->t('Title/Description:'),
'summary' => DI::l10n()->t('Summary'),
'music' => DI::l10n()->t('Musical interests'),
'book' => DI::l10n()->t('Books, literature'),
'tv' => DI::l10n()->t('Television'),
'film' => DI::l10n()->t('Film/dance/culture/entertainment'),
'interest' => DI::l10n()->t('Hobbies/Interests'),
'romance' => DI::l10n()->t('Love/romance'),
'work' => DI::l10n()->t('Work/employment'),
'education' => DI::l10n()->t('School/education'),
'contact' => DI::l10n()->t('Contact information and Social Networks'),
];
foreach ($custom_fields as $field => $label) {
if (!empty($profile[$field]) && $profile[$field] > DBA::NULL_DATE && $profile[$field] > DBA::NULL_DATETIME) {
DI::profileField()->save(DI::profileFieldFactory()->createFromValues(
$profile['uid'],
$order,
trim($label, ':'),
$profile[$field],
$permissionSet
));
}
$profile[$field] = null;
}
if ($profile['is-default']) {
$profile['profile-name'] = null;
$profile['is-default'] = null;
DBA::update('profile', $profile, ['id' => $profile['id']]);
} else if (!empty($profile['id'])) {
DBA::delete('profile', ['id' => $profile['id']]);
}
}
} }

View File

@ -1,85 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Model;
use Friendica\BaseModel;
use Friendica\Database\Database;
use Friendica\Network\HTTPException\NotFoundException;
use Friendica\Security\PermissionSet\Depository\PermissionSet as PermissionSetDepository;
use Friendica\Security\PermissionSet\Entity\PermissionSet;
use Psr\Log\LoggerInterface;
/**
* Custom profile field model class.
*
* Custom profile fields are user-created arbitrary profile fields that can be assigned a permission set to restrict its
* display to specific Friendica contacts as it requires magic authentication to work.
*
* @property int uid
* @property int order
* @property int psid
* @property string label
* @property string value
* @property string created
* @property string edited
* @property PermissionSet permissionSet
*/
class ProfileField extends BaseModel
{
/** @var PermissionSet */
private $permissionSet;
/** @var PermissionSetDepository */
private $permissionSetDepository;
public function __construct(Database $dba, LoggerInterface $logger, PermissionSetDepository $permissionSetDepository, array $data = [])
{
parent::__construct($dba, $logger, $data);
$this->permissionSetDepository = $permissionSetDepository;
}
public function __get($name)
{
$this->checkValid();
switch ($name) {
case 'permissionSet':
if (empty($this->permissionSet)) {
$permissionSet = $this->permissionSetDepository->selectOneById($this->psid, $this->uid);
if ($permissionSet->uid !== $this->uid) {
throw new NotFoundException(sprintf('PermissionSet %d (user-id: %d) for ProfileField %d (user-id: %d) is invalid.', $permissionSet->id, $permissionSet->uid, $this->id, $this->uid));
}
$this->permissionSet = $permissionSet;
}
$return = $this->permissionSet;
break;
default:
$return = parent::__get($name);
break;
}
return $return;
}
}

View File

@ -86,7 +86,7 @@ class Objects extends BaseModule
$permissionSets = DI::permissionSet()->selectByContactId($requester_id, $item['uid']); $permissionSets = DI::permissionSet()->selectByContactId($requester_id, $item['uid']);
if (!empty($permissionSets)) { if (!empty($permissionSets)) {
$psid = array_merge($permissionSets->column('id'), $psid = array_merge($permissionSets->column('id'),
[DI::permissionSet()->selectEmptyForUser($item['uid'])]); [DI::permissionSet()->selectPublicForUser($item['uid'])]);
$validated = in_array($item['psid'], $psid); $validated = in_array($item['psid'], $psid);
} }
} }

View File

@ -21,7 +21,7 @@
namespace Friendica\Module\Api\Friendica\Profile; namespace Friendica\Module\Api\Friendica\Profile;
use Friendica\Collection\ProfileFields; use Friendica\Profile\ProfileField\Collection\ProfileFields;
use Friendica\Content\Text\BBCode; use Friendica\Content\Text\BBCode;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
@ -45,7 +45,7 @@ class Show extends BaseApi
$profile = Profile::getByUID($uid); $profile = Profile::getByUID($uid);
$profileFields = DI::profileField()->select(['uid' => $uid, 'psid' => PermissionSet::PUBLIC]); $profileFields = DI::profileField()->selectPublicFieldsByUserId($uid);
$profile = self::formatProfile($profile, $profileFields); $profile = self::formatProfile($profile, $profileFields);

View File

@ -213,7 +213,7 @@ class Status extends BaseProfile
$permissionSets = DI::permissionSet()->selectByContactId($remote_user, $profile['uid']); $permissionSets = DI::permissionSet()->selectByContactId($remote_user, $profile['uid']);
if (!empty($permissionSets)) { if (!empty($permissionSets)) {
$condition = ['psid' => array_merge($permissionSets->column('id'), $condition = ['psid' => array_merge($permissionSets->column('id'),
[DI::permissionSet()->selectEmptyForUser($profile['uid'])->id])]; [DI::permissionSet()->selectPublicForUser($profile['uid'])->id])];
} }
} elseif ($profile['uid'] == local_user()) { } elseif ($profile['uid'] == local_user()) {
$condition = []; $condition = [];

View File

@ -30,7 +30,8 @@ use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\Profile; use Friendica\Model\Profile;
use Friendica\Model\ProfileField; use Friendica\Profile\ProfileField\Collection\ProfileFields;
use Friendica\Profile\ProfileField\Entity\ProfileField;
use Friendica\Model\User; use Friendica\Model\User;
use Friendica\Module\BaseSettings; use Friendica\Module\BaseSettings;
use Friendica\Module\Security\Login; use Friendica\Module\Security\Login;
@ -100,16 +101,13 @@ class Index extends BaseSettings
$homepage = 'http://' . $homepage; $homepage = 'http://' . $homepage;
} }
$profileFields = DI::profileField()->selectByUserId(local_user()); $profileFieldsNew = self::getProfileFieldsFromInput(
$profileFields = DI::profileField()->updateCollectionFromForm(
local_user(), local_user(),
$profileFields,
$_REQUEST['profile_field'], $_REQUEST['profile_field'],
$_REQUEST['profile_field_order'] $_REQUEST['profile_field_order']
); );
DI::profileField()->saveCollection($profileFields); DI::profileField()->saveCollectionForUser(local_user(), $profileFieldsNew);
$result = Profile::update( $result = Profile::update(
[ [
@ -265,6 +263,56 @@ class Index extends BaseSettings
return $o; return $o;
} }
private static function getProfileFieldsFromInput(int $uid, array $profileFieldInputs, array $profileFieldOrder): ProfileFields
{
$profileFields = new ProfileFields();
// Returns an associative array of id => order values
$profileFieldOrder = array_flip($profileFieldOrder);
// Creation of the new field
if (!empty($profileFieldInputs['new']['label'])) {
$permissionSet = DI::permissionSet()->selectOrCreate(DI::permissionSetFactory()->createFromString(
$uid,
DI::aclFormatter()->toString($profileFieldInputs['new']['contact_allow'] ?? ''),
DI::aclFormatter()->toString($profileFieldInputs['new']['group_allow'] ?? ''),
DI::aclFormatter()->toString($profileFieldInputs['new']['contact_deny'] ?? ''),
DI::aclFormatter()->toString($profileFieldInputs['new']['group_deny'] ?? '')
));
$profileFields->append(DI::profileFieldFactory()->createFromValues(
$uid,
$profileFieldOrder['new'],
$profileFieldInputs['new']['label'],
$profileFieldInputs['new']['value'],
$permissionSet
));
}
unset($profileFieldInputs['new']);
unset($profileFieldOrder['new']);
foreach ($profileFieldInputs as $id => $profileFieldInput) {
$permissionSet = DI::permissionSet()->selectOrCreate(DI::permissionSetFactory()->createFromString(
$uid,
DI::aclFormatter()->toString($profileFieldInput['contact_allow'] ?? ''),
DI::aclFormatter()->toString($profileFieldInput['group_allow'] ?? ''),
DI::aclFormatter()->toString($profileFieldInput['contact_deny'] ?? ''),
DI::aclFormatter()->toString($profileFieldInput['group_deny'] ?? '')
));
$profileFields->append(DI::profileFieldFactory()->createFromValues(
$uid,
$profileFieldOrder[$id],
$profileFieldInput['label'],
$profileFieldInput['value'],
$permissionSet
));
}
return $profileFields;
}
private static function cleanKeywords($keywords) private static function cleanKeywords($keywords)
{ {
$keywords = str_replace(',', ' ', $keywords); $keywords = str_replace(',', ' ', $keywords);

View File

@ -19,17 +19,23 @@
* *
*/ */
namespace Friendica\Collection; namespace Friendica\Profile\ProfileField\Collection;
use Friendica\BaseCollection; use Friendica\BaseCollection;
use Friendica\Profile\ProfileField\Entity;
class ProfileFields extends BaseCollection class ProfileFields extends BaseCollection
{ {
public function current(): Entity\ProfileField
{
return parent::current();
}
/** /**
* @param callable $callback * @param callable $callback
* @return ProfileFields * @return ProfileFields
*/ */
public function map(callable $callback) public function map(callable $callback): ProfileFields
{ {
return parent::map($callback); return parent::map($callback);
} }
@ -39,7 +45,7 @@ class ProfileFields extends BaseCollection
* @param int $flag * @param int $flag
* @return ProfileFields * @return ProfileFields
*/ */
public function filter(callable $callback = null, int $flag = 0) public function filter(callable $callback = null, int $flag = 0): ProfileFields
{ {
return parent::filter($callback, $flag); return parent::filter($callback, $flag);
} }

View File

@ -0,0 +1,281 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\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) {
$this->logger->warning('row', ['row' => $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;
}
}

View File

@ -0,0 +1,112 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Profile\ProfileField\Entity;
use Friendica\BaseEntity;
use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Profile\ProfileField\Exception\ProfileFieldNotFoundException;
use Friendica\Security\PermissionSet\Entity\PermissionSet;
/**
* Custom profile field entity class.
*
* Custom profile fields are user-created arbitrary profile fields that can be assigned a permission set to restrict its
* display to specific Friendica contacts as it requires magic authentication to work.
*
* @property-read int|null $id
* @property-read int $uid
* @property-read int $order
* @property-read string $label
* @property-read string $value
* @property-read \DateTime $created
* @property-read \DateTime $edited
* @property PermissionSet $permissionSet
*/
class ProfileField extends BaseEntity
{
/** @var int|null */
protected $id;
/** @var PermissionSet */
protected $permissionSet;
/** @var int */
protected $uid;
/** @var int */
protected $order;
/** @var string */
protected $label;
/** @var string */
protected $value;
/** @var \DateTime */
protected $created;
/** @var \DateTime */
protected $edited;
public function __construct(int $uid, int $order, string $label, string $value, \DateTime $created, \DateTime $edited, PermissionSet $permissionSet, int $id = null)
{
$this->permissionSet = $permissionSet;
$this->uid = $uid;
$this->order = $order;
$this->label = $label;
$this->value = $value;
$this->created = $created;
$this->edited = $edited;
$this->id = $id;
}
/**
* @throws ProfileFieldNotFoundException
*/
public function __get($name)
{
try {
return parent::__get($name);
} catch (InternalServerErrorException $exception) {
throw new ProfileFieldNotFoundException($exception->getMessage());
}
}
/**
* Updates a ProfileField
*
* @param string $value The current or changed value
* @param int $order The current or changed order
* @param PermissionSet $permissionSet The current or changed PermissionSet
*/
public function update(string $value, int $order, PermissionSet $permissionSet)
{
$this->value = $value;
$this->order = $order;
$this->permissionSet = $permissionSet;
$this->edited = new \DateTime('now', new \DateTimeZone('UTC'));
}
/**
* Sets the order of the ProfileField
*
* @param int $order
*/
public function setOrder(int $order)
{
$this->order = $order;
$this->edited = new \DateTime('now', new \DateTimeZone('UTC'));
}
}

View File

@ -0,0 +1,33 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Profile\ProfileField\Exception;
use OutOfBoundsException;
use Throwable;
class ProfileFieldNotFoundException extends OutOfBoundsException
{
public function __construct($message = "", Throwable $previous = null)
{
parent::__construct($message, 404, $previous);
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Profile\ProfileField\Exception;
use Throwable;
class ProfileFieldPersistenceException extends \RuntimeException
{
public function __construct($message = "", Throwable $previous = null)
{
parent::__construct($message, 500, $previous);
}
}

View File

@ -0,0 +1,26 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Profile\ProfileField\Exception;
class UnexpectedPermissionSetException extends \Exception
{
}

View File

@ -0,0 +1,106 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Profile\ProfileField\Factory;
use Friendica\BaseFactory;
use Friendica\Profile\ProfileField\Exception\UnexpectedPermissionSetException;
use Friendica\Security\PermissionSet\Factory\PermissionSet as PermissionSetFactory;
use Friendica\Profile\ProfileField\Entity;
use Friendica\Capabilities\ICanCreateFromTableRow;
use Friendica\Security\PermissionSet\Entity\PermissionSet;
use Psr\Log\LoggerInterface;
class ProfileField extends BaseFactory implements ICanCreateFromTableRow
{
/** @var PermissionSetFactory */
private $permissionSetFactory;
public function __construct(LoggerInterface $logger, PermissionSetFactory $permissionSetFactory)
{
parent::__construct($logger);
$this->permissionSetFactory = $permissionSetFactory;
}
/**
* @inheritDoc
*
* @throws UnexpectedPermissionSetException
*/
public function createFromTableRow(array $row, PermissionSet $permissionSet = null): Entity\ProfileField
{
if (empty($permissionSet) &&
(!array_key_exists('psid', $row) || !array_key_exists('allow_cid', $row) || !array_key_exists('allow_gid', $row) || !array_key_exists('deny_cid', $row) || !array_key_exists('deny_gid', $row))
) {
throw new UnexpectedPermissionSetException('Either set the PermissionSet fields (join) or the PermissionSet itself');
}
return new Entity\ProfileField(
$row['uid'],
$row['order'],
$row['label'],
$row['value'],
new \DateTime($row['created'] ?? 'now', new \DateTimeZone('UTC')),
new \DateTime($row['edited'] ?? 'now', new \DateTimeZone('UTC')),
$permissionSet ?? $this->permissionSetFactory->createFromString(
$row['uid'],
$row['allow_cid'],
$row['allow_gid'],
$row['deny_cid'],
$row['deny_gid'],
$row['psid']
),
$row['id'] ?? null
);
}
/**
* Creates a ProfileField instance based on it's values
*
* @param int $uid
* @param int $order
* @param string $label
* @param string $value
* @param PermissionSet $permissionSet
* @param int|null $id
*
* @return Entity\ProfileField
* @throws UnexpectedPermissionSetException
*/
public function createFromValues(
int $uid,
int $order,
string $label,
string $value,
PermissionSet $permissionSet,
int $id = null
): Entity\ProfileField {
return $this->createFromTableRow([
'uid' => $uid,
'order' => $order,
'psid' => $permissionSet->id,
'label' => $label,
'value' => $value,
'id' => $id,
], $permissionSet);
}
}

View File

@ -229,7 +229,7 @@ class Transmitter
$permissionSets = DI::permissionSet()->selectByContactId($requester_id, $owner['uid']); $permissionSets = DI::permissionSet()->selectByContactId($requester_id, $owner['uid']);
if (!empty($permissionSets)) { if (!empty($permissionSets)) {
$condition = ['psid' => array_merge($permissionSets->column('id'), $condition = ['psid' => array_merge($permissionSets->column('id'),
[DI::permissionSet()->selectEmptyForUser($owner['uid'])])]; [DI::permissionSet()->selectPublicForUser($owner['uid'])])];
} }
} }
} }

View File

@ -1,324 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Repository;
use Friendica\BaseModel;
use Friendica\BaseRepository;
use Friendica\Collection;
use Friendica\Core\L10n;
use Friendica\Database\Database;
use Friendica\Database\DBA;
use Friendica\Model;
use Friendica\Security\PermissionSet\Depository\PermissionSet;
use Friendica\Util\DateTimeFormat;
use Psr\Log\LoggerInterface;
class ProfileField extends BaseRepository
{
protected static $table_name = 'profile_field';
protected static $model_class = Model\ProfileField::class;
protected static $collection_class = Collection\ProfileFields::class;
/** @var PermissionSet */
private $permissionSet;
/** @var \Friendica\Security\PermissionSet\Factory\PermissionSet */
private $permissionSetFactory;
/** @var L10n */
private $l10n;
public function __construct(Database $dba, LoggerInterface $logger, PermissionSet $permissionSet, \Friendica\Security\PermissionSet\Factory\PermissionSet $permissionSetFactory, L10n $l10n)
{
parent::__construct($dba, $logger);
$this->permissionSet = $permissionSet;
$this->permissionSetFactory = $permissionSetFactory;
$this->l10n = $l10n;
}
/**
* @param array $data
* @return Model\ProfileField
*/
protected function create(array $data)
{
return new Model\ProfileField($this->dba, $this->logger, $this->permissionSet, $data);
}
/**
* @param array $condition
* @return Model\ProfileField
* @throws \Friendica\Network\HTTPException\NotFoundException
*/
public function selectFirst(array $condition)
{
return parent::selectFirst($condition);
}
/**
* @param array $condition
* @param array $params
* @return Collection\ProfileFields
* @throws \Exception
*/
public function select(array $condition = [], array $params = [])
{
return parent::select($condition, $params);
}
/**
* @param array $condition
* @param array $params
* @param int|null $min_id
* @param int|null $max_id
* @param int $limit
* @return Collection\ProfileFields
* @throws \Exception
*/
public function selectByBoundaries(array $condition = [], array $params = [], int $min_id = null, int $max_id = null, int $limit = self::LIMIT)
{
return parent::selectByBoundaries($condition, $params, $min_id, $max_id, $limit);
}
/**
* @param int $uid Field owner user Id
* @return Collection\ProfileFields
* @throws \Exception
*/
public function selectByUserId(int $uid)
{
return $this->select(
['uid' => $uid],
['order' => ['order']]
);
}
/**
* 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
* @return Collection\ProfileFields
* @throws \Exception
*/
public function selectByContactId(int $cid, int $uid)
{
$permissionSets = $this->permissionSet->selectByContactId($cid, $uid);
$psids = $permissionSets->column('id');
// Includes public custom fields
$psids[] = 0;
return $this->select(
['uid' => $uid, 'psid' => $psids],
['order' => ['order']]
);
}
/**
* @param array $fields
* @return Model\ProfileField|bool
* @throws \Exception
*/
public function insert(array $fields)
{
$fields['created'] = DateTimeFormat::utcNow();
$fields['edited'] = DateTimeFormat::utcNow();
return parent::insert($fields);
}
/**
* @param Model\ProfileField $model
* @return bool
* @throws \Exception
*/
public function update(BaseModel $model)
{
$model->edited = DateTimeFormat::utcNow();
return parent::update($model);
}
/**
* @param int $uid User Id
* @param Collection\ProfileFields $profileFields Collection of existing profile fields
* @param array $profileFieldInputs Array of profile field form inputs indexed by profile field id
* @param array $profileFieldOrder List of profile field id in order
* @return Collection\ProfileFields
* @throws \Exception
*/
public function updateCollectionFromForm(int $uid, Collection\ProfileFields $profileFields, array $profileFieldInputs, array $profileFieldOrder)
{
// Returns an associative array of id => order values
$profileFieldOrder = array_flip($profileFieldOrder);
// Creation of the new field
if (!empty($profileFieldInputs['new']['label'])) {
$psid = $this->permissionSet->selectOrCreate($this->permissionSetFactory->createFromString(
$uid,
$profileFieldInputs['new']['contact_allow'] ?? '',
$profileFieldInputs['new']['group_allow'] ?? '',
$profileFieldInputs['new']['contact_deny'] ?? '',
$profileFieldInputs['new']['group_deny'] ?? ''
))->id;
$newProfileField = $this->insert([
'uid' => $uid,
'label' => $profileFieldInputs['new']['label'],
'value' => $profileFieldInputs['new']['value'],
'psid' => $psid,
'order' => $profileFieldOrder['new'],
]);
$profileFieldInputs[$newProfileField->id] = $profileFieldInputs['new'];
$profileFieldOrder[$newProfileField->id] = $profileFieldOrder['new'];
$profileFields[] = $newProfileField;
}
unset($profileFieldInputs['new']);
unset($profileFieldOrder['new']);
// Prunes profile field whose label has been emptied
$profileFields = $profileFields->filter(function (Model\ProfileField $profileField) use (&$profileFieldInputs, &$profileFieldOrder) {
$keepModel = !isset($profileFieldInputs[$profileField->id]) || !empty($profileFieldInputs[$profileField->id]['label']);
if (!$keepModel) {
unset($profileFieldInputs[$profileField->id]);
unset($profileFieldOrder[$profileField->id]);
$this->delete($profileField);
}
return $keepModel;
});
// Regenerates the order values if items were deleted
$profileFieldOrder = array_flip(array_keys($profileFieldOrder));
// Update existing profile fields from form values
$profileFields = $profileFields->map(function (Model\ProfileField $profileField) use ($uid, &$profileFieldInputs, &$profileFieldOrder) {
if (isset($profileFieldInputs[$profileField->id]) && isset($profileFieldOrder[$profileField->id])) {
$psid = $this->permissionSet->selectOrCreate($this->permissionSetFactory->createFromString(
$uid,
$profileFieldInputs[$profileField->id]['contact_allow'] ?? '',
$profileFieldInputs[$profileField->id]['group_allow'] ?? '',
$profileFieldInputs[$profileField->id]['contact_deny'] ?? '',
$profileFieldInputs[$profileField->id]['group_deny'] ?? ''
))->id;
$profileField->psid = $psid;
$profileField->label = $profileFieldInputs[$profileField->id]['label'];
$profileField->value = $profileFieldInputs[$profileField->id]['value'];
$profileField->order = $profileFieldOrder[$profileField->id];
unset($profileFieldInputs[$profileField->id]);
unset($profileFieldOrder[$profileField->id]);
}
return $profileField;
});
return $profileFields;
}
/**
* Migrates a legacy profile to the new slimmer profile with extra custom fields.
* Multi profiles are converted to ACl-protected custom fields and deleted.
*
* @param array $profile Profile table row
* @throws \Exception
*/
public function migrateFromLegacyProfile(array $profile)
{
// Already processed, aborting
if ($profile['is-default'] === null) {
return;
}
$contacts = [];
if (!$profile['is-default']) {
$contacts = Model\Contact::selectToArray(['id'], ['uid' => $profile['uid'], 'profile-id' => $profile['id']]);
if (!count($contacts)) {
// No contact visibility selected defaults to user-only permission
$contacts = Model\Contact::selectToArray(['id'], ['uid' => $profile['uid'], 'self' => true]);
}
}
$psid = $this->permissionSet->selectOrCreate(
new \Friendica\Security\PermissionSet\Entity\PermissionSet(
$profile['uid'],
array_column($contacts, 'id') ?? []
)
)->id;
$order = 1;
$custom_fields = [
'hometown' => $this->l10n->t('Hometown:'),
'marital' => $this->l10n->t('Marital Status:'),
'with' => $this->l10n->t('With:'),
'howlong' => $this->l10n->t('Since:'),
'sexual' => $this->l10n->t('Sexual Preference:'),
'politic' => $this->l10n->t('Political Views:'),
'religion' => $this->l10n->t('Religious Views:'),
'likes' => $this->l10n->t('Likes:'),
'dislikes' => $this->l10n->t('Dislikes:'),
'pdesc' => $this->l10n->t('Title/Description:'),
'summary' => $this->l10n->t('Summary'),
'music' => $this->l10n->t('Musical interests'),
'book' => $this->l10n->t('Books, literature'),
'tv' => $this->l10n->t('Television'),
'film' => $this->l10n->t('Film/dance/culture/entertainment'),
'interest' => $this->l10n->t('Hobbies/Interests'),
'romance' => $this->l10n->t('Love/romance'),
'work' => $this->l10n->t('Work/employment'),
'education' => $this->l10n->t('School/education'),
'contact' => $this->l10n->t('Contact information and Social Networks'),
];
foreach ($custom_fields as $field => $label) {
if (!empty($profile[$field]) && $profile[$field] > DBA::NULL_DATE && $profile[$field] > DBA::NULL_DATETIME) {
$this->insert([
'uid' => $profile['uid'],
'psid' => $psid,
'order' => $order++,
'label' => trim($label, ':'),
'value' => $profile[$field],
]);
}
$profile[$field] = null;
}
if ($profile['is-default']) {
$profile['profile-name'] = null;
$profile['is-default'] = null;
$this->dba->update('profile', $profile, ['id' => $profile['id']]);
} elseif (!empty($profile['id'])) {
$this->dba->delete('profile', ['id' => $profile['id']]);
}
}
}

View File

@ -53,6 +53,22 @@ class PermissionSet extends BaseDepository
$this->aclFormatter = $aclFormatter; $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 $condition
* @param array $params * @param array $params
@ -89,22 +105,18 @@ class PermissionSet extends BaseDepository
} }
/** /**
* @param int $id A permissionset table row id or self::PUBLIC * @param int $id A PermissionSet table row id or self::PUBLIC
* @param int|null $uid Should be provided when id can be self::PUBLIC * @param int $uid The owner of the PermissionSet
* @return Entity\PermissionSet * @return Entity\PermissionSet
* @throws NotFoundException * @throws NotFoundException
*/ */
public function selectOneById(int $id, int $uid = null): Entity\PermissionSet public function selectOneById(int $id, int $uid): Entity\PermissionSet
{ {
if ($id === self::PUBLIC) { if ($id === self::PUBLIC) {
if (empty($uid)) {
throw new \InvalidArgumentException('Missing uid for Public permission set instantiation');
}
return $this->factory->createFromString($uid); return $this->factory->createFromString($uid);
} }
return $this->selectOne(['id' => $id]); return $this->selectOne(['id' => $id, 'uid' => $uid]);
} }
/** /**
@ -174,15 +186,15 @@ class PermissionSet extends BaseDepository
} }
/** /**
* Fetch the empty PermissionSet for a given user, create it if it doesn't exist * Fetch the public PermissionSet
* *
* @param int $uid * @param int $uid
* *
* @return Entity\PermissionSet * @return Entity\PermissionSet
*/ */
public function selectEmptyForUser(int $uid): Entity\PermissionSet public function selectPublicForUser(int $uid): Entity\PermissionSet
{ {
return $this->selectOrCreate($this->factory->createFromString($uid)); return $this->factory->createFromString($uid, '', '', '', '', self::PUBLIC);
} }
/** /**
@ -198,6 +210,11 @@ class PermissionSet extends BaseDepository
return $permissionSet; return $permissionSet;
} }
// Don't select/update Public permission sets
if ($permissionSet->isPublic()) {
return $this->selectPublicForUser($permissionSet->uid);
}
try { try {
return $this->selectOne($this->convertToTableRow($permissionSet)); return $this->selectOne($this->convertToTableRow($permissionSet));
} catch (NotFoundException $exception) { } catch (NotFoundException $exception) {
@ -205,8 +222,19 @@ class PermissionSet extends BaseDepository
} }
} }
/**
* @param Entity\PermissionSet $permissionSet
*
* @return Entity\PermissionSet
* @throws NotFoundException
*/
public function save(Entity\PermissionSet $permissionSet): Entity\PermissionSet 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); $fields = $this->convertToTableRow($permissionSet);
if ($permissionSet->id) { if ($permissionSet->id) {
@ -214,7 +242,7 @@ class PermissionSet extends BaseDepository
} else { } else {
$this->db->insert(self::$table_name, $fields); $this->db->insert(self::$table_name, $fields);
$permissionSet = $this->selectOneById($this->db->lastInsertId()); $permissionSet = $this->selectOneById($this->db->lastInsertId(), $permissionSet->uid);
} }
return $permissionSet; return $permissionSet;

View File

@ -3,6 +3,7 @@
namespace Friendica\Security\PermissionSet\Entity; namespace Friendica\Security\PermissionSet\Entity;
use Friendica\BaseEntity; use Friendica\BaseEntity;
use Friendica\Security\PermissionSet\Depository\PermissionSet as PermissionSetDepository;
/** /**
* @property-read int|null $id * @property-read int|null $id
@ -47,6 +48,21 @@ class PermissionSet extends BaseEntity
$this->deny_gid = $deny_gid; $this->deny_gid = $deny_gid;
} }
/**
* Checks, if the current PermissionSet is a/the public PermissionSet
*
* @return bool
*/
public function isPublic(): bool
{
return (($this->id === PermissionSetDepository::PUBLIC) ||
(is_null($this->id) &&
empty($this->allow_cid) &&
empty($this->allow_gid) &&
empty($this->deny_cid) &&
empty($this->deny_gid)));
}
/** /**
* Creates a new Entity with a new allowed_cid list (wipes the id because it isn't the same entity anymore) * Creates a new Entity with a new allowed_cid list (wipes the id because it isn't the same entity anymore)
* *

View File

@ -51,7 +51,8 @@ class PermissionSet extends BaseFactory implements ICanCreateFromTableRow
string $allow_cid = '', string $allow_cid = '',
string $allow_gid = '', string $allow_gid = '',
string $deny_cid = '', string $deny_cid = '',
string $deny_gid = ''): Entity\PermissionSet string $deny_gid = '',
int $id = null): Entity\PermissionSet
{ {
return $this->createFromTableRow([ return $this->createFromTableRow([
'uid' => $uid, 'uid' => $uid,
@ -59,6 +60,7 @@ class PermissionSet extends BaseFactory implements ICanCreateFromTableRow
'allow_gid' => $allow_gid, 'allow_gid' => $allow_gid,
'deny_cid' => $deny_cid, 'deny_cid' => $deny_cid,
'deny_gid' => $deny_gid, 'deny_gid' => $deny_gid,
'id' => $id,
]); ]);
} }
} }

View File

@ -55,7 +55,7 @@
use Friendica\Database\DBA; use Friendica\Database\DBA;
if (!defined('DB_UPDATE_VERSION')) { if (!defined('DB_UPDATE_VERSION')) {
define('DB_UPDATE_VERSION', 1439); define('DB_UPDATE_VERSION', 1440);
} }
return [ return [

View File

@ -1047,5 +1047,23 @@
INNER JOIN `workerqueue` ON `workerqueue`.`pid` = `process`.`pid` INNER JOIN `workerqueue` ON `workerqueue`.`pid` = `process`.`pid`
WHERE NOT `workerqueue`.`done`" WHERE NOT `workerqueue`.`done`"
], ],
"profile_field-view" => [
"fields" => [
"id" => ["profile_field", "id"],
"uid" => ["profile_field", "uid"],
"label" => ["profile_field", "label"],
"value" => ["profile_field", "value"],
"order" => ["profile_field", "order"],
"psid"=> ["profile_field", "psid"],
"allow_cid" => ["permissionset", "allow_cid"],
"allow_gid" => ["permissionset", "allow_gid"],
"deny_cid" => ["permissionset", "deny_cid"],
"deny_gid" => ["permissionset", "deny_gid"],
"created" => ["profile_field", "created"],
"edited" => ["profile_field", "edited"],
],
"query" => "FROM `profile_field`
INNER JOIN `permissionset` ON `permissionset`.`id` = `profile_field`.`psid`"
],
]; ];

View File

@ -0,0 +1,9 @@
<?php
namespace Friendica\Test\Util;
use Friendica\BaseCollection;
class CollectionDouble extends BaseCollection
{
}

View File

@ -0,0 +1,26 @@
<?php
namespace Friendica\Test\Util;
use Friendica\BaseEntity;
/**
* @property-read string $protString
* @property-read int $protInt
* @property-read \DateTime $protDateTime
*/
class EntityDouble extends BaseEntity
{
protected $protString;
protected $protInt;
protected $protDateTime;
private $privString;
public function __construct(string $protString, int $protInt, \DateTime $protDateTime, string $privString)
{
$this->protString = $protString;
$this->protInt = $protInt;
$this->protDateTime = $protDateTime;
$this->privString = $privString;
}
}

View File

@ -26,6 +26,8 @@ use Friendica\Model\Notification;
return [ return [
// Empty these tables // Empty these tables
'profile_field',
'permissionset',
'cache', 'cache',
'conversation', 'conversation',
'pconfig', 'pconfig',

View File

@ -0,0 +1,24 @@
<?php
namespace Friendica\Test\src;
use Friendica\Test\MockedTest;
use Friendica\Test\Util\CollectionDouble;
use Friendica\Test\Util\EntityDouble;
class CollectionTest extends MockedTest
{
/**
* Test if the BaseCollection::column() works as expected
*/
public function testGetArrayCopy()
{
$collection = new CollectionDouble();
$collection->append(new EntityDouble('test', 23, new \DateTime('now', new \DateTimeZone('UTC')), 'privTest'));
$collection->append(new EntityDouble('test2', 25, new \DateTime('now', new \DateTimeZone('UTC')), 'privTest23'));
self::assertEquals(['test', 'test2'], $collection->column('protString'));
self::assertEmpty($collection->column('privString'));
self::assertEquals([23,25], $collection->column('protInt'));
}
}

View File

@ -0,0 +1,136 @@
<?php
namespace Friendica\Test\src\Profile\ProfileField\Depository;
use Friendica\Profile\ProfileField\Collection\ProfileFields;
use Friendica\Profile\ProfileField\Depository\ProfileField as ProfileFieldDepository;
use Friendica\Profile\ProfileField\Exception\ProfileFieldPersistenceException;
use Friendica\Profile\ProfileField\Factory\ProfileField as ProfileFieldFactory;
use Friendica\Security\PermissionSet\Depository\PermissionSet;
use Friendica\Security\PermissionSet\Factory\PermissionSet as PermissionSetFactory;
use Friendica\Security\PermissionSet\Depository\PermissionSet as PermissionSetDepository;
use Friendica\Test\FixtureTest;
use Friendica\DI;
class ProfileFieldTest extends FixtureTest
{
/** @var ProfileFieldDepository */
private $depository;
/** @var ProfileFieldFactory */
private $factory;
/** @var PermissionSetFactory */
private $permissionSetFactory;
/** @var PermissionSetDepository */
private $permissionSetDepository;
public function setUp(): void
{
parent::setUp();
$this->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);
}
}

View File

@ -0,0 +1,208 @@
<?php
namespace Friendica\Test\src\Profile\ProfileField\Entity;
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\Factory\PermissionSet as PermissionSetFactory;
use Friendica\Test\MockedTest;
use Friendica\Util\ACLFormatter;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Logger\VoidLogger;
use Mockery\MockInterface;
class ProfileFieldTest extends MockedTest
{
/** @var MockInterface|PermissionSetDepository */
protected $permissionSetDepository;
/** @var ProfileFieldFactory */
protected $profileFieldFactory;
/** @var MockInterface|PermissionSetFactory */
protected $permissionSetFactory;
protected function setUp(): void
{
parent::setUp();
$this->permissionSetDepository = \Mockery::mock(PermissionSetDepository::class);
$this->permissionSetFactory = new PermissionSetFactory(new VoidLogger(), new ACLFormatter());
$this->profileFieldFactory = new ProfileFieldFactory(new VoidLogger(), $this->permissionSetFactory);
}
public function dataEntity()
{
return [
'default' => [
'uid' => 23,
'order' => 1,
'psid' => 2,
'label' => 'test',
'value' => 'more',
'created' => new \DateTime('2021-10-10T21:12:00.000000+0000', new \DateTimeZone('UTC')),
'edited' => new \DateTime('2021-10-10T21:12:00.000000+0000', new \DateTimeZone('UTC')),
'permissionSet' => [
'uid' => 23,
'allow_cid' => "<1>",
'allow_gid' => "<~>",
'deny_cid' => '<2>',
'deny_gid' => '<3>',
'id' => 2,
]
],
'withId' => [
'uid' => 23,
'order' => 1,
'psid' => 2,
'label' => 'test',
'value' => 'more',
'created' => new \DateTime('2021-10-10T21:12:00.000000+0000', new \DateTimeZone('UTC')),
'edited' => new \DateTime('2021-10-10T21:12:00.000000+0000', new \DateTimeZone('UTC')),
'permissionSet' => [
'uid' => 23,
'allow_cid' => "<1>",
'allow_gid' => "<~>",
'deny_cid' => '<2>',
'deny_gid' => '<3>',
'id' => 2,
],
'id' => 54,
],
];
}
/**
* @dataProvider dataEntity
*/
public function testEntity(int $uid, int $order, int $psid, string $label, string $value, \DateTime $created, \DateTime $edited, array $permissionSet, $id = null)
{
$entity = new ProfileField($uid, $order, $label, $value, $created, $edited, $this->permissionSetFactory->createFromTableRow($permissionSet), $id);
self::assertEquals($uid, $entity->uid);
self::assertEquals($order, $entity->order);
self::assertEquals($psid, $entity->permissionSet->id);
self::assertEquals($label, $entity->label);
self::assertEquals($value, $entity->value);
self::assertEquals($created, $entity->created);
self::assertEquals($edited, $entity->edited);
self::assertEquals($id, $entity->id);
}
/**
* @dataProvider dataEntity
*/
public function testUpdate(int $uid, int $order, int $psid, string $label, string $value, \DateTime $created, \DateTime $edited, array $permissionSet, $id = null)
{
$permissionSet = $this->permissionSetFactory->createFromTableRow(['uid' => 2, 'id' => $psid]);
$entity = $this->profileFieldFactory->createFromTableRow([
'uid' => $uid,
'order' => $order,
'psid' => $psid,
'label' => $label,
'value' => $value,
'created' => $created->format(DateTimeFormat::MYSQL),
'edited' => $edited->format(DateTimeFormat::MYSQL),
'id' => $id,
], $permissionSet);
$permissionSetNew = $this->permissionSetFactory->createFromTableRow([
'uid' => 2,
'allow_cid' => '<1>',
'id' => 23
]);
$entity->update('updatedValue', 2345, $permissionSetNew);
self::assertEquals($uid, $entity->uid);
self::assertEquals(2345, $entity->order);
self::assertEquals(23, $entity->permissionSet->id);
self::assertEquals($label, $entity->label);
self::assertEquals('updatedValue', $entity->value);
self::assertEquals($created, $entity->created);
self::assertGreaterThan($edited, $entity->edited);
self::assertEquals($id, $entity->id);
}
/**
* @dataProvider dataEntity
*/
public function testSetOrder(int $uid, int $order, int $psid, string $label, string $value, \DateTime $created, \DateTime $edited, array $permissionSet, $id = null)
{
$permissionSet = $this->permissionSetFactory->createFromTableRow(['uid' => 2, 'id' => $psid]);
$entity = $this->profileFieldFactory->createFromTableRow([
'uid' => $uid,
'order' => $order,
'psid' => $psid,
'label' => $label,
'value' => $value,
'created' => $created->format(DateTimeFormat::MYSQL),
'edited' => $edited->format(DateTimeFormat::MYSQL),
'id' => $id,
], $permissionSet);
$entity->setOrder(2345);
self::assertEquals($uid, $entity->uid);
self::assertEquals(2345, $entity->order);
self::assertEquals($psid, $entity->permissionSet->id);
self::assertEquals($label, $entity->label);
self::assertEquals($value, $entity->value);
self::assertEquals($created, $entity->created);
self::assertGreaterThan($edited, $entity->edited);
self::assertEquals($id, $entity->id);
}
/**
* Test the exception because of a wrong property
*
* @dataProvider dataEntity
*/
public function testWrongGet(int $uid, int $order, int $psid, string $label, string $value, \DateTime $created, \DateTime $edited, array $permissionSet, $id = null)
{
$entity = new ProfileField($uid, $order, $label, $value, $created, $edited, $this->permissionSetFactory->createFromTableRow($permissionSet), $id);
self::expectException(ProfileFieldNotFoundException::class);
$entity->wrong;
}
/**
* Test gathering the permissionset
*
* @dataProvider dataEntity
*/
public function testPermissionSet(int $uid, int $order, int $psid, string $label, string $value, \DateTime $created, \DateTime $edited, array $permissionSet, $id = null)
{
$entity = new ProfileField($uid, $order, $label, $value, $created, $edited, $this->permissionSetFactory->createFromTableRow($permissionSet), $id);
$permissionSet = $this->permissionSetFactory->createFromTableRow(['uid' => $uid, 'id' => $psid]);
$this->permissionSetDepository->shouldReceive('selectOneById')->with($psid, $uid)->andReturns($permissionSet);
self::assertEquals($psid, $entity->permissionSet->id);
}
/**
* Test the exception because the factory cannot find a permissionSet ID, nor the permissionSet itself
*
* @dataProvider dataEntity
*/
public function testMissingPermissionFactory(int $uid, int $order, int $psid, string $label, string $value, \DateTime $created, \DateTime $edited, array $permissionSet, $id = null)
{
self::expectException(UnexpectedPermissionSetException::class);
self::expectExceptionMessage('Either set the PermissionSet fields (join) or the PermissionSet itself');
$entity = $this->profileFieldFactory->createFromTableRow([
'uid' => $uid,
'order' => $order,
'label' => $label,
'value' => $value,
'created' => $created->format(DateTimeFormat::MYSQL),
'edited' => $edited->format(DateTimeFormat::MYSQL),
'id' => $id,
]);
}
}

View File

@ -2,39 +2,63 @@
namespace Friendica\Test\src\Security\PermissionSet\Depository; namespace Friendica\Test\src\Security\PermissionSet\Depository;
use Dice\Dice; use Friendica\Security\PermissionSet\Depository\PermissionSet as PermissionSetDepository;
use Friendica\Database\Database; use Friendica\Security\PermissionSet\Entity\PermissionSet;
use Friendica\Security\PermissionSet\Factory\PermissionSet as PermissionSetFactory;
use Friendica\Test\FixtureTest;
use Friendica\DI; use Friendica\DI;
use Friendica\Security\PermissionSet\Depository\PermissionSet;
use Friendica\Test\MockedTest;
use Friendica\Test\Util\Database\StaticDatabase;
class PermissionSetTest extends MockedTest class PermissionSetTest extends FixtureTest
{ {
/** @var PermissionSet */ /** @var PermissionSetDepository */
private $depository; private $depository;
/** @var PermissionSetFactory */
private $factory;
public function setUp(): void public function setUp(): void
{ {
$dice = (new Dice()) parent::setUp();
->addRules(include __DIR__ . '/../../../../../static/dependencies.config.php')
->addRule(Database::class, ['instanceOf' => StaticDatabase::class, 'shared' => true]);
DI::init($dice);
$this->depository = DI::permissionSet(); $this->depository = DI::permissionSet();
} $this->factory = DI::permissionSetFactory();
public function testSelectOneByIdPublicMissingUid()
{
$this->expectException(\InvalidArgumentException::class);
$this->depository->selectOneById(PermissionSet::PUBLIC);
} }
public function testSelectOneByIdPublic() public function testSelectOneByIdPublic()
{ {
$permissionSet = $this->depository->selectOneById(PermissionSet::PUBLIC, 1); $permissionSet = $this->depository->selectPublicForUser(1);
$this->assertInstanceOf(\Friendica\Security\PermissionSet\Entity\PermissionSet::class, $permissionSet); $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);
} }
} }

View File

@ -55,6 +55,7 @@ use Friendica\Model\Photo;
use Friendica\Model\Post; use Friendica\Model\Post;
use Friendica\Model\Profile; use Friendica\Model\Profile;
use Friendica\Model\Storage; use Friendica\Model\Storage;
use Friendica\Security\PermissionSet\Depository\PermissionSet;
use Friendica\Worker\Delivery; use Friendica\Worker\Delivery;
// Post-update script of PR 5751 // Post-update script of PR 5751
@ -206,7 +207,7 @@ function update_1332()
$profiles = DBA::select('profile', [], $condition); $profiles = DBA::select('profile', [], $condition);
while ($profile = DBA::fetch($profiles)) { while ($profile = DBA::fetch($profiles)) {
DI::profileField()->migrateFromLegacyProfile($profile); Profile::migrate($profile);
} }
DBA::close($profiles); DBA::close($profiles);
@ -1028,3 +1029,12 @@ function update_1439()
} }
DBA::close($intros); DBA::close($intros);
} }
function update_1440()
{
// Fix wrong public permissionset
DBA::p("UPDATE `profile_field` SET `psid` = ? WHERE psid IN (SELECT `id` FROM `permissionset` WHERE `id` != ? AND `allow_cid` = '' AND `allow_gid` = '' AND `deny_cid` = '' AND `deny_gid` = '')", PermissionSet::PUBLIC, PermissionSet::PUBLIC);
DBA::delete('permissionset', ["`id` != ? AND `allow_cid` = '' AND `allow_gid` = '' AND `deny_cid` = '' AND `deny_gid` = ''", PermissionSet::PUBLIC]);
return Update::SUCCESS;
}

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 2021.12-dev\n" "Project-Id-Version: 2021.12-dev\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-10-16 19:31-0400\n" "POT-Creation-Date: 2021-10-18 00:07+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -331,7 +331,7 @@ msgid "Access denied."
msgstr "" msgstr ""
#: mod/cal.php:61 mod/cal.php:78 mod/photos.php:69 mod/photos.php:143 #: mod/cal.php:61 mod/cal.php:78 mod/photos.php:69 mod/photos.php:143
#: mod/photos.php:815 src/Model/Profile.php:228 src/Module/HCard.php:52 #: mod/photos.php:815 src/Model/Profile.php:229 src/Module/HCard.php:52
#: src/Module/Profile/Common.php:41 src/Module/Profile/Common.php:52 #: src/Module/Profile/Common.php:41 src/Module/Profile/Common.php:52
#: src/Module/Profile/Contacts.php:40 src/Module/Profile/Contacts.php:50 #: src/Module/Profile/Contacts.php:40 src/Module/Profile/Contacts.php:50
#: src/Module/Profile/Media.php:38 src/Module/Profile/Status.php:58 #: src/Module/Profile/Media.php:38 src/Module/Profile/Status.php:58
@ -509,7 +509,7 @@ msgstr ""
msgid "Permission settings" msgid "Permission settings"
msgstr "" msgstr ""
#: mod/editpost.php:116 src/Core/ACL.php:327 #: mod/editpost.php:116 src/Core/ACL.php:325
msgid "CC: email addresses" msgid "CC: email addresses"
msgstr "" msgstr ""
@ -527,7 +527,7 @@ msgstr ""
msgid "Categories (comma-separated list)" msgid "Categories (comma-separated list)"
msgstr "" msgstr ""
#: mod/editpost.php:123 src/Core/ACL.php:328 #: mod/editpost.php:123 src/Core/ACL.php:326
msgid "Example: bob@example.com, mary@example.com" msgid "Example: bob@example.com, mary@example.com"
msgstr "" msgstr ""
@ -545,7 +545,7 @@ msgid "Cancel"
msgstr "" msgstr ""
#: mod/editpost.php:134 src/Content/Conversation.php:380 #: mod/editpost.php:134 src/Content/Conversation.php:380
#: src/Content/Widget/VCard.php:107 src/Model/Profile.php:459 #: src/Content/Widget/VCard.php:107 src/Model/Profile.php:460
#: src/Module/Admin/Logs/View.php:92 #: src/Module/Admin/Logs/View.php:92
msgid "Message" msgid "Message"
msgstr "" msgstr ""
@ -615,13 +615,13 @@ msgid "Event Finishes:"
msgstr "" msgstr ""
#: mod/events.php:506 src/Module/Profile/Profile.php:172 #: mod/events.php:506 src/Module/Profile/Profile.php:172
#: src/Module/Settings/Profile/Index.php:239 #: src/Module/Settings/Profile/Index.php:237
msgid "Description:" msgid "Description:"
msgstr "" msgstr ""
#: mod/events.php:508 src/Content/Widget/VCard.php:98 src/Model/Event.php:80 #: mod/events.php:508 src/Content/Widget/VCard.php:98 src/Model/Event.php:80
#: src/Model/Event.php:107 src/Model/Event.php:466 src/Model/Event.php:915 #: src/Model/Event.php:107 src/Model/Event.php:466 src/Model/Event.php:915
#: src/Model/Profile.php:367 src/Module/Contact.php:565 #: src/Model/Profile.php:368 src/Module/Contact.php:565
#: src/Module/Directory.php:150 src/Module/Notifications/Introductions.php:165 #: src/Module/Directory.php:150 src/Module/Notifications/Introductions.php:165
#: src/Module/Profile/Profile.php:194 #: src/Module/Profile/Profile.php:194
msgid "Location:" msgid "Location:"
@ -647,7 +647,7 @@ msgstr ""
#: src/Module/Install.php:245 src/Module/Install.php:287 #: src/Module/Install.php:245 src/Module/Install.php:287
#: src/Module/Install.php:324 src/Module/Invite.php:177 #: src/Module/Install.php:324 src/Module/Invite.php:177
#: src/Module/Item/Compose.php:150 src/Module/Profile/Profile.php:247 #: src/Module/Item/Compose.php:150 src/Module/Profile/Profile.php:247
#: src/Module/Settings/Profile/Index.php:223 src/Object/Post.php:963 #: src/Module/Settings/Profile/Index.php:221 src/Object/Post.php:963
#: view/theme/duepuntozero/config.php:69 view/theme/frio/config.php:160 #: view/theme/duepuntozero/config.php:69 view/theme/frio/config.php:160
#: view/theme/quattro/config.php:71 view/theme/vier/config.php:119 #: view/theme/quattro/config.php:71 view/theme/vier/config.php:119
msgid "Submit" msgid "Submit"
@ -2374,7 +2374,7 @@ msgstr ""
msgid "All contacts" msgid "All contacts"
msgstr "" msgstr ""
#: src/BaseModule.php:212 src/Content/Widget.php:231 src/Core/ACL.php:195 #: src/BaseModule.php:212 src/Content/Widget.php:231 src/Core/ACL.php:193
#: src/Module/Contact.php:756 src/Module/PermissionTooltip.php:75 #: src/Module/Contact.php:756 src/Module/PermissionTooltip.php:75
#: src/Module/PermissionTooltip.php:97 #: src/Module/PermissionTooltip.php:97
msgid "Followers" msgid "Followers"
@ -2988,7 +2988,7 @@ msgstr ""
#: src/Content/Item.php:444 src/Content/Item.php:466 src/Model/Contact.php:1010 #: src/Content/Item.php:444 src/Content/Item.php:466 src/Model/Contact.php:1010
#: src/Model/Contact.php:1068 src/Model/Contact.php:1077 #: src/Model/Contact.php:1068 src/Model/Contact.php:1077
#: src/Module/Directory.php:160 src/Module/Settings/Profile/Index.php:226 #: src/Module/Directory.php:160 src/Module/Settings/Profile/Index.php:224
msgid "View Profile" msgid "View Profile"
msgstr "" msgstr ""
@ -3357,7 +3357,7 @@ msgid "The end"
msgstr "" msgstr ""
#: src/Content/Text/HTML.php:885 src/Content/Widget/VCard.php:103 #: src/Content/Text/HTML.php:885 src/Content/Widget/VCard.php:103
#: src/Model/Profile.php:453 #: src/Model/Profile.php:454
msgid "Follow" msgid "Follow"
msgstr "" msgstr ""
@ -3533,68 +3533,68 @@ msgstr[1] ""
msgid "More Trending Tags" msgid "More Trending Tags"
msgstr "" msgstr ""
#: src/Content/Widget/VCard.php:96 src/Model/Profile.php:372 #: src/Content/Widget/VCard.php:96 src/Model/Profile.php:373
#: src/Module/Contact.php:567 src/Module/Profile/Profile.php:176 #: src/Module/Contact.php:567 src/Module/Profile/Profile.php:176
msgid "XMPP:" msgid "XMPP:"
msgstr "" msgstr ""
#: src/Content/Widget/VCard.php:97 src/Model/Profile.php:373 #: src/Content/Widget/VCard.php:97 src/Model/Profile.php:374
#: src/Module/Contact.php:569 src/Module/Profile/Profile.php:180 #: src/Module/Contact.php:569 src/Module/Profile/Profile.php:180
msgid "Matrix:" msgid "Matrix:"
msgstr "" msgstr ""
#: src/Content/Widget/VCard.php:101 src/Model/Profile.php:465 #: src/Content/Widget/VCard.php:101 src/Model/Profile.php:466
#: src/Module/Notifications/Introductions.php:179 #: src/Module/Notifications/Introductions.php:179
msgid "Network:" msgid "Network:"
msgstr "" msgstr ""
#: src/Content/Widget/VCard.php:105 src/Model/Profile.php:455 #: src/Content/Widget/VCard.php:105 src/Model/Profile.php:456
msgid "Unfollow" msgid "Unfollow"
msgstr "" msgstr ""
#: src/Core/ACL.php:166 src/Module/Profile/Profile.php:242 #: src/Core/ACL.php:164 src/Module/Profile/Profile.php:242
msgid "Yourself" msgid "Yourself"
msgstr "" msgstr ""
#: src/Core/ACL.php:202 src/Module/PermissionTooltip.php:81 #: src/Core/ACL.php:200 src/Module/PermissionTooltip.php:81
#: src/Module/PermissionTooltip.php:103 #: src/Module/PermissionTooltip.php:103
msgid "Mutuals" msgid "Mutuals"
msgstr "" msgstr ""
#: src/Core/ACL.php:294 #: src/Core/ACL.php:292
msgid "Post to Email" msgid "Post to Email"
msgstr "" msgstr ""
#: src/Core/ACL.php:321 #: src/Core/ACL.php:319
msgid "Public" msgid "Public"
msgstr "" msgstr ""
#: src/Core/ACL.php:322 #: src/Core/ACL.php:320
msgid "" msgid ""
"This content will be shown to all your followers and can be seen in the " "This content will be shown to all your followers and can be seen in the "
"community pages and by anyone with its link." "community pages and by anyone with its link."
msgstr "" msgstr ""
#: src/Core/ACL.php:323 #: src/Core/ACL.php:321
msgid "Limited/Private" msgid "Limited/Private"
msgstr "" msgstr ""
#: src/Core/ACL.php:324 #: src/Core/ACL.php:322
msgid "" msgid ""
"This content will be shown only to the people in the first box, to the " "This content will be shown only to the people in the first box, to the "
"exception of the people mentioned in the second box. It won't appear " "exception of the people mentioned in the second box. It won't appear "
"anywhere public." "anywhere public."
msgstr "" msgstr ""
#: src/Core/ACL.php:325 #: src/Core/ACL.php:323
msgid "Show to:" msgid "Show to:"
msgstr "" msgstr ""
#: src/Core/ACL.php:326 #: src/Core/ACL.php:324
msgid "Except to:" msgid "Except to:"
msgstr "" msgstr ""
#: src/Core/ACL.php:329 #: src/Core/ACL.php:327
msgid "Connectors" msgid "Connectors"
msgstr "" msgstr ""
@ -4178,35 +4178,35 @@ msgid ""
"\t\t\t\t\tThe friendica database was successfully updated from %s to %s." "\t\t\t\t\tThe friendica database was successfully updated from %s to %s."
msgstr "" msgstr ""
#: src/Core/UserImport.php:124 #: src/Core/UserImport.php:125
msgid "Error decoding account file" msgid "Error decoding account file"
msgstr "" msgstr ""
#: src/Core/UserImport.php:130 #: src/Core/UserImport.php:131
msgid "Error! No version data in file! This is not a Friendica account file?" msgid "Error! No version data in file! This is not a Friendica account file?"
msgstr "" msgstr ""
#: src/Core/UserImport.php:138 #: src/Core/UserImport.php:139
#, php-format #, php-format
msgid "User '%s' already exists on this server!" msgid "User '%s' already exists on this server!"
msgstr "" msgstr ""
#: src/Core/UserImport.php:174 #: src/Core/UserImport.php:175
msgid "User creation error" msgid "User creation error"
msgstr "" msgstr ""
#: src/Core/UserImport.php:219 #: src/Core/UserImport.php:220
#, php-format #, php-format
msgid "%d contact not imported" msgid "%d contact not imported"
msgid_plural "%d contacts not imported" msgid_plural "%d contacts not imported"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
#: src/Core/UserImport.php:272 #: src/Core/UserImport.php:273
msgid "User profile creation error" msgid "User profile creation error"
msgstr "" msgstr ""
#: src/Core/UserImport.php:325 #: src/Core/UserImport.php:326
msgid "Done. You can now login with your username and password" msgid "Done. You can now login with your username and password"
msgstr "" msgstr ""
@ -4515,66 +4515,146 @@ msgstr ""
msgid "[no subject]" msgid "[no subject]"
msgstr "" msgstr ""
#: src/Model/Profile.php:355 src/Module/Profile/Profile.php:256 #: src/Model/Profile.php:356 src/Module/Profile/Profile.php:256
#: src/Module/Profile/Profile.php:258 #: src/Module/Profile/Profile.php:258
msgid "Edit profile" msgid "Edit profile"
msgstr "" msgstr ""
#: src/Model/Profile.php:357 #: src/Model/Profile.php:358
msgid "Change profile photo" msgid "Change profile photo"
msgstr "" msgstr ""
#: src/Model/Profile.php:370 src/Module/Directory.php:155 #: src/Model/Profile.php:371 src/Module/Directory.php:155
#: src/Module/Profile/Profile.php:184 #: src/Module/Profile/Profile.php:184
msgid "Homepage:" msgid "Homepage:"
msgstr "" msgstr ""
#: src/Model/Profile.php:371 src/Module/Contact.php:571 #: src/Model/Profile.php:372 src/Module/Contact.php:571
#: src/Module/Notifications/Introductions.php:167 #: src/Module/Notifications/Introductions.php:167
msgid "About:" msgid "About:"
msgstr "" msgstr ""
#: src/Model/Profile.php:457 #: src/Model/Profile.php:458
msgid "Atom feed" msgid "Atom feed"
msgstr "" msgstr ""
#: src/Model/Profile.php:495 src/Model/Profile.php:592 #: src/Model/Profile.php:496 src/Model/Profile.php:593
msgid "g A l F d" msgid "g A l F d"
msgstr "" msgstr ""
#: src/Model/Profile.php:496 #: src/Model/Profile.php:497
msgid "F d" msgid "F d"
msgstr "" msgstr ""
#: src/Model/Profile.php:558 src/Model/Profile.php:643 #: src/Model/Profile.php:559 src/Model/Profile.php:644
msgid "[today]" msgid "[today]"
msgstr "" msgstr ""
#: src/Model/Profile.php:568 #: src/Model/Profile.php:569
msgid "Birthday Reminders" msgid "Birthday Reminders"
msgstr "" msgstr ""
#: src/Model/Profile.php:569 #: src/Model/Profile.php:570
msgid "Birthdays this week:" msgid "Birthdays this week:"
msgstr "" msgstr ""
#: src/Model/Profile.php:630 #: src/Model/Profile.php:631
msgid "[No description]" msgid "[No description]"
msgstr "" msgstr ""
#: src/Model/Profile.php:656 #: src/Model/Profile.php:657
msgid "Event Reminders" msgid "Event Reminders"
msgstr "" msgstr ""
#: src/Model/Profile.php:657 #: src/Model/Profile.php:658
msgid "Upcoming events the next 7 days:" msgid "Upcoming events the next 7 days:"
msgstr "" msgstr ""
#: src/Model/Profile.php:845 #: src/Model/Profile.php:846
#, php-format #, php-format
msgid "OpenWebAuth: %1$s welcomes %2$s" msgid "OpenWebAuth: %1$s welcomes %2$s"
msgstr "" msgstr ""
#: src/Model/Profile.php:978
msgid "Hometown:"
msgstr ""
#: src/Model/Profile.php:979
msgid "Marital Status:"
msgstr ""
#: src/Model/Profile.php:980
msgid "With:"
msgstr ""
#: src/Model/Profile.php:981
msgid "Since:"
msgstr ""
#: src/Model/Profile.php:982
msgid "Sexual Preference:"
msgstr ""
#: src/Model/Profile.php:983
msgid "Political Views:"
msgstr ""
#: src/Model/Profile.php:984
msgid "Religious Views:"
msgstr ""
#: src/Model/Profile.php:985
msgid "Likes:"
msgstr ""
#: src/Model/Profile.php:986
msgid "Dislikes:"
msgstr ""
#: src/Model/Profile.php:987
msgid "Title/Description:"
msgstr ""
#: src/Model/Profile.php:988 src/Module/Admin/Summary.php:234
msgid "Summary"
msgstr ""
#: src/Model/Profile.php:989
msgid "Musical interests"
msgstr ""
#: src/Model/Profile.php:990
msgid "Books, literature"
msgstr ""
#: src/Model/Profile.php:991
msgid "Television"
msgstr ""
#: src/Model/Profile.php:992
msgid "Film/dance/culture/entertainment"
msgstr ""
#: src/Model/Profile.php:993
msgid "Hobbies/Interests"
msgstr ""
#: src/Model/Profile.php:994
msgid "Love/romance"
msgstr ""
#: src/Model/Profile.php:995
msgid "Work/employment"
msgstr ""
#: src/Model/Profile.php:996
msgid "School/education"
msgstr ""
#: src/Model/Profile.php:997
msgid "Contact information and Social Networks"
msgstr ""
#: src/Model/Storage/FilesystemConfig.php:77 #: src/Model/Storage/FilesystemConfig.php:77
msgid "Storage base path" msgid "Storage base path"
msgstr "" msgstr ""
@ -6607,10 +6687,6 @@ msgstr ""
msgid "Server Settings" msgid "Server Settings"
msgstr "" msgstr ""
#: src/Module/Admin/Summary.php:234 src/Repository/ProfileField.php:290
msgid "Summary"
msgstr ""
#: src/Module/Admin/Summary.php:236 #: src/Module/Admin/Summary.php:236
msgid "Registered users" msgid "Registered users"
msgstr "" msgstr ""
@ -7731,7 +7807,7 @@ msgid "Sort by post received date"
msgstr "" msgstr ""
#: src/Module/Conversation/Network.php:250 #: src/Module/Conversation/Network.php:250
#: src/Module/Settings/Profile/Index.php:228 #: src/Module/Settings/Profile/Index.php:226
msgid "Personal" msgid "Personal"
msgstr "" msgstr ""
@ -7955,7 +8031,7 @@ msgid "Twitter Source / Tweet URL (requires API key)"
msgstr "" msgstr ""
#: src/Module/Debug/Feed.php:38 src/Module/Filer/SaveTag.php:40 #: src/Module/Debug/Feed.php:38 src/Module/Filer/SaveTag.php:40
#: src/Module/Settings/Profile/Index.php:142 #: src/Module/Settings/Profile/Index.php:140
msgid "You must be logged in to use this module" msgid "You must be logged in to use this module"
msgstr "" msgstr ""
@ -8718,12 +8794,12 @@ msgstr ""
msgid "Birthday:" msgid "Birthday:"
msgstr "" msgstr ""
#: src/Module/Profile/Profile.php:167 src/Module/Settings/Profile/Index.php:246 #: src/Module/Profile/Profile.php:167 src/Module/Settings/Profile/Index.php:244
#: src/Util/Temporal.php:165 #: src/Util/Temporal.php:165
msgid "Age: " msgid "Age: "
msgstr "" msgstr ""
#: src/Module/Profile/Profile.php:167 src/Module/Settings/Profile/Index.php:246 #: src/Module/Profile/Profile.php:167 src/Module/Settings/Profile/Index.php:244
#: src/Util/Temporal.php:165 #: src/Util/Temporal.php:165
#, php-format #, php-format
msgid "%d year old" msgid "%d year old"
@ -9298,133 +9374,133 @@ msgstr ""
msgid "Beginning of week:" msgid "Beginning of week:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:83 #: src/Module/Settings/Profile/Index.php:84
msgid "Profile Name is required." msgid "Profile Name is required."
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:134 #: src/Module/Settings/Profile/Index.php:132
msgid "Profile couldn't be updated." msgid "Profile couldn't be updated."
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:173 #: src/Module/Settings/Profile/Index.php:171
#: src/Module/Settings/Profile/Index.php:193 #: src/Module/Settings/Profile/Index.php:191
msgid "Label:" msgid "Label:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:174 #: src/Module/Settings/Profile/Index.php:172
#: src/Module/Settings/Profile/Index.php:194 #: src/Module/Settings/Profile/Index.php:192
msgid "Value:" msgid "Value:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:184 #: src/Module/Settings/Profile/Index.php:182
#: src/Module/Settings/Profile/Index.php:204 #: src/Module/Settings/Profile/Index.php:202
msgid "Field Permissions" msgid "Field Permissions"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:185 #: src/Module/Settings/Profile/Index.php:183
#: src/Module/Settings/Profile/Index.php:205 #: src/Module/Settings/Profile/Index.php:203
msgid "(click to open/close)" msgid "(click to open/close)"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:191 #: src/Module/Settings/Profile/Index.php:189
msgid "Add a new profile field" msgid "Add a new profile field"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:221 #: src/Module/Settings/Profile/Index.php:219
msgid "Profile Actions" msgid "Profile Actions"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:222 #: src/Module/Settings/Profile/Index.php:220
msgid "Edit Profile Details" msgid "Edit Profile Details"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:224 #: src/Module/Settings/Profile/Index.php:222
msgid "Change Profile Photo" msgid "Change Profile Photo"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:229 #: src/Module/Settings/Profile/Index.php:227
msgid "Profile picture" msgid "Profile picture"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:230 #: src/Module/Settings/Profile/Index.php:228
msgid "Location" msgid "Location"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:231 src/Util/Temporal.php:93 #: src/Module/Settings/Profile/Index.php:229 src/Util/Temporal.php:93
#: src/Util/Temporal.php:95 #: src/Util/Temporal.php:95
msgid "Miscellaneous" msgid "Miscellaneous"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:232 #: src/Module/Settings/Profile/Index.php:230
msgid "Custom Profile Fields" msgid "Custom Profile Fields"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:234 src/Module/Welcome.php:58 #: src/Module/Settings/Profile/Index.php:232 src/Module/Welcome.php:58
msgid "Upload Profile Photo" msgid "Upload Profile Photo"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:238 #: src/Module/Settings/Profile/Index.php:236
msgid "Display name:" msgid "Display name:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:241 #: src/Module/Settings/Profile/Index.php:239
msgid "Street Address:" msgid "Street Address:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:242 #: src/Module/Settings/Profile/Index.php:240
msgid "Locality/City:" msgid "Locality/City:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:243 #: src/Module/Settings/Profile/Index.php:241
msgid "Region/State:" msgid "Region/State:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:244 #: src/Module/Settings/Profile/Index.php:242
msgid "Postal/Zip Code:" msgid "Postal/Zip Code:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:245 #: src/Module/Settings/Profile/Index.php:243
msgid "Country:" msgid "Country:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:247 #: src/Module/Settings/Profile/Index.php:245
msgid "XMPP (Jabber) address:" msgid "XMPP (Jabber) address:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:247 #: src/Module/Settings/Profile/Index.php:245
msgid "The XMPP address will be published so that people can follow you there." msgid "The XMPP address will be published so that people can follow you there."
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:248 #: src/Module/Settings/Profile/Index.php:246
msgid "Matrix (Element) address:" msgid "Matrix (Element) address:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:248 #: src/Module/Settings/Profile/Index.php:246
msgid "" msgid ""
"The Matrix address will be published so that people can follow you there." "The Matrix address will be published so that people can follow you there."
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:249 #: src/Module/Settings/Profile/Index.php:247
msgid "Homepage URL:" msgid "Homepage URL:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:250 #: src/Module/Settings/Profile/Index.php:248
msgid "Public Keywords:" msgid "Public Keywords:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:250 #: src/Module/Settings/Profile/Index.php:248
msgid "(Used for suggesting potential friends, can be seen by others)" msgid "(Used for suggesting potential friends, can be seen by others)"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:251 #: src/Module/Settings/Profile/Index.php:249
msgid "Private Keywords:" msgid "Private Keywords:"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:251 #: src/Module/Settings/Profile/Index.php:249
msgid "(Used for searching profiles, never shown to others)" msgid "(Used for searching profiles, never shown to others)"
msgstr "" msgstr ""
#: src/Module/Settings/Profile/Index.php:252 #: src/Module/Settings/Profile/Index.php:250
#, php-format #, php-format
msgid "" msgid ""
"<p>Custom fields appear on <a href=\"%s\">your profile page</a>.</p>\n" "<p>Custom fields appear on <a href=\"%s\">your profile page</a>.</p>\n"
@ -10124,15 +10200,15 @@ msgstr ""
msgid "%s created a new post" msgid "%s created a new post"
msgstr "" msgstr ""
#: src/Navigation/Notifications/Factory/Introduction.php:134 #: src/Navigation/Notifications/Factory/Introduction.php:133
msgid "Friend Suggestion" msgid "Friend Suggestion"
msgstr "" msgstr ""
#: src/Navigation/Notifications/Factory/Introduction.php:160 #: src/Navigation/Notifications/Factory/Introduction.php:159
msgid "Friend/Connect Request" msgid "Friend/Connect Request"
msgstr "" msgstr ""
#: src/Navigation/Notifications/Factory/Introduction.php:160 #: src/Navigation/Notifications/Factory/Introduction.php:159
msgid "New Follower" msgid "New Follower"
msgstr "" msgstr ""
@ -10483,82 +10559,6 @@ msgstr ""
msgid "The folder view/smarty3/ must be writable by webserver." msgid "The folder view/smarty3/ must be writable by webserver."
msgstr "" msgstr ""
#: src/Repository/ProfileField.php:280
msgid "Hometown:"
msgstr ""
#: src/Repository/ProfileField.php:281
msgid "Marital Status:"
msgstr ""
#: src/Repository/ProfileField.php:282
msgid "With:"
msgstr ""
#: src/Repository/ProfileField.php:283
msgid "Since:"
msgstr ""
#: src/Repository/ProfileField.php:284
msgid "Sexual Preference:"
msgstr ""
#: src/Repository/ProfileField.php:285
msgid "Political Views:"
msgstr ""
#: src/Repository/ProfileField.php:286
msgid "Religious Views:"
msgstr ""
#: src/Repository/ProfileField.php:287
msgid "Likes:"
msgstr ""
#: src/Repository/ProfileField.php:288
msgid "Dislikes:"
msgstr ""
#: src/Repository/ProfileField.php:289
msgid "Title/Description:"
msgstr ""
#: src/Repository/ProfileField.php:291
msgid "Musical interests"
msgstr ""
#: src/Repository/ProfileField.php:292
msgid "Books, literature"
msgstr ""
#: src/Repository/ProfileField.php:293
msgid "Television"
msgstr ""
#: src/Repository/ProfileField.php:294
msgid "Film/dance/culture/entertainment"
msgstr ""
#: src/Repository/ProfileField.php:295
msgid "Hobbies/Interests"
msgstr ""
#: src/Repository/ProfileField.php:296
msgid "Love/romance"
msgstr ""
#: src/Repository/ProfileField.php:297
msgid "Work/employment"
msgstr ""
#: src/Repository/ProfileField.php:298
msgid "School/education"
msgstr ""
#: src/Repository/ProfileField.php:299
msgid "Contact information and Social Networks"
msgstr ""
#: src/Security/Authentication.php:209 #: src/Security/Authentication.php:209
msgid "Login failed." msgid "Login failed."
msgstr "" msgstr ""