From 494938032c9b3e94f343bc41956db5763a8c5999 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:13:27 -0300 Subject: [PATCH 01/68] feat(enum): introduce FieldEditPolicy enum Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Enum/FieldEditPolicy.php | 33 +++++++++++++++++++++++ lib/Enum/FieldExposurePolicy.php | 45 ++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 lib/Enum/FieldEditPolicy.php create mode 100644 lib/Enum/FieldExposurePolicy.php diff --git a/lib/Enum/FieldEditPolicy.php b/lib/Enum/FieldEditPolicy.php new file mode 100644 index 0000000..fd22432 --- /dev/null +++ b/lib/Enum/FieldEditPolicy.php @@ -0,0 +1,33 @@ + + */ + public static function values(): array { + return [ + self::ADMINS->value, + self::USERS->value, + ]; + } + + public static function isValid(string $value): bool { + return self::tryFrom($value) !== null; + } + + public function userCanEdit(): bool { + return $this === self::USERS; + } +} diff --git a/lib/Enum/FieldExposurePolicy.php b/lib/Enum/FieldExposurePolicy.php new file mode 100644 index 0000000..7167494 --- /dev/null +++ b/lib/Enum/FieldExposurePolicy.php @@ -0,0 +1,45 @@ + + */ + public static function values(): array { + return [ + self::HIDDEN->value, + self::PRIVATE->value, + self::USERS->value, + self::PUBLIC->value, + ]; + } + + public static function isValid(string $value): bool { + return self::tryFrom($value) !== null; + } + + public function isUserVisible(): bool { + return $this !== self::HIDDEN; + } + + public function initialVisibility(): FieldVisibility { + return match ($this) { + self::HIDDEN, self::PRIVATE => FieldVisibility::PRIVATE, + self::USERS => FieldVisibility::USERS, + self::PUBLIC => FieldVisibility::PUBLIC, + }; + } +} From b49552bc6a67868341cf9faabe1787e0e73b0d29 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:21 -0300 Subject: [PATCH 02/68] refactor(db): replace legacy flags with FieldEditPolicy and FieldExposurePolicy Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Db/FieldDefinition.php | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/lib/Db/FieldDefinition.php b/lib/Db/FieldDefinition.php index 69f57d4..ec24b93 100644 --- a/lib/Db/FieldDefinition.php +++ b/lib/Db/FieldDefinition.php @@ -20,14 +20,10 @@ * @method void setLabel(string $value) * @method string getType() * @method void setType(string $value) - * @method bool getAdminOnly() - * @method void setAdminOnly(bool $value) - * @method bool getUserEditable() - * @method void setUserEditable(bool $value) - * @method bool getUserVisible() - * @method void setUserVisible(bool $value) - * @method string getInitialVisibility() - * @method void setInitialVisibility(string $value) + * @method string getEditPolicy() + * @method void setEditPolicy(string $value) + * @method string getExposurePolicy() + * @method void setExposurePolicy(string $value) * @method int getSortOrder() * @method void setSortOrder(int $value) * @method bool getActive() @@ -43,10 +39,8 @@ class FieldDefinition extends Entity { protected $fieldKey; protected $label; protected $type; - protected $adminOnly; - protected $userEditable; - protected $userVisible; - protected $initialVisibility; + protected $editPolicy; + protected $exposurePolicy; protected $sortOrder; protected $active; protected $options; @@ -55,9 +49,6 @@ class FieldDefinition extends Entity { public function __construct() { $this->addType('id', 'integer'); - $this->addType('adminOnly', 'boolean'); - $this->addType('userEditable', 'boolean'); - $this->addType('userVisible', 'boolean'); $this->addType('sortOrder', 'integer'); $this->addType('active', 'boolean'); $this->addType('createdAt', 'datetime'); @@ -70,10 +61,8 @@ public function __construct() { * field_key: string, * label: string, * type: string, - * admin_only: bool, - * user_editable: bool, - * user_visible: bool, - * initial_visibility: string, + * edit_policy: string, + * exposure_policy: string, * sort_order: int, * active: bool, * options: list|null, @@ -89,10 +78,8 @@ public function jsonSerialize(): array { 'field_key' => $this->getFieldKey(), 'label' => $this->getLabel(), 'type' => $this->getType(), - 'admin_only' => $this->getAdminOnly(), - 'user_editable' => $this->getUserEditable(), - 'user_visible' => $this->getUserVisible(), - 'initial_visibility' => $this->getInitialVisibility(), + 'edit_policy' => $this->getEditPolicy(), + 'exposure_policy' => $this->getExposurePolicy(), 'sort_order' => $this->getSortOrder(), 'active' => $this->getActive(), 'options' => $rawOptions !== null ? (json_decode($rawOptions, true) ?? null) : null, From 19cf8a9353b5d07ee5e6367ba10fe737ea8fb87e Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:21 -0300 Subject: [PATCH 03/68] refactor(db): update mapper queries to filter by policy enums Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Db/FieldDefinitionMapper.php | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/lib/Db/FieldDefinitionMapper.php b/lib/Db/FieldDefinitionMapper.php index 99bad4f..54d4317 100644 --- a/lib/Db/FieldDefinitionMapper.php +++ b/lib/Db/FieldDefinitionMapper.php @@ -27,10 +27,8 @@ public function findByFieldKey(string $fieldKey): ?FieldDefinition { 'field_key', 'label', 'type', - 'admin_only', - 'user_editable', - 'user_visible', - 'initial_visibility', + 'edit_policy', + 'exposure_policy', 'sort_order', 'active', 'options', @@ -54,10 +52,8 @@ public function findById(int $id): ?FieldDefinition { 'field_key', 'label', 'type', - 'admin_only', - 'user_editable', - 'user_visible', - 'initial_visibility', + 'edit_policy', + 'exposure_policy', 'sort_order', 'active', 'options', @@ -84,10 +80,8 @@ public function findAllOrdered(): array { 'field_key', 'label', 'type', - 'admin_only', - 'user_editable', - 'user_visible', - 'initial_visibility', + 'edit_policy', + 'exposure_policy', 'sort_order', 'active', 'options', @@ -111,10 +105,8 @@ public function findActiveOrdered(): array { 'field_key', 'label', 'type', - 'admin_only', - 'user_editable', - 'user_visible', - 'initial_visibility', + 'edit_policy', + 'exposure_policy', 'sort_order', 'active', 'options', From 2e5b0f4e18fc2219e93b5604037c080f1e3a9a76 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:21 -0300 Subject: [PATCH 04/68] feat(migration): add edit_policy and exposure_policy columns Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Migration/Version1000Date20260309120000.php | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/Migration/Version1000Date20260309120000.php b/lib/Migration/Version1000Date20260309120000.php index 04c7882..c01ee42 100644 --- a/lib/Migration/Version1000Date20260309120000.php +++ b/lib/Migration/Version1000Date20260309120000.php @@ -41,16 +41,11 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addColumn('type', Types::STRING, [ 'length' => 32, ]); - $table->addColumn('admin_only', Types::BOOLEAN, [ - 'default' => false, - ]); - $table->addColumn('user_editable', Types::BOOLEAN, [ - 'default' => false, - ]); - $table->addColumn('user_visible', Types::BOOLEAN, [ - 'default' => true, + $table->addColumn('edit_policy', Types::STRING, [ + 'length' => 32, + 'default' => 'users', ]); - $table->addColumn('initial_visibility', Types::STRING, [ + $table->addColumn('exposure_policy', Types::STRING, [ 'length' => 32, 'default' => 'private', ]); From aabd13e9813ce59bf525b6e8841e37e15b482f7f Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:21 -0300 Subject: [PATCH 05/68] refactor(api): declare policy enum types in response definitions Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/ResponseDefinitions.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php index e40b6e0..28fec42 100644 --- a/lib/ResponseDefinitions.php +++ b/lib/ResponseDefinitions.php @@ -12,14 +12,14 @@ /** * @psalm-type ProfileFieldsType = 'text'|'number'|'select' * @psalm-type ProfileFieldsVisibility = 'private'|'users'|'public' + * @psalm-type ProfileFieldsEditPolicy = 'admins'|'users' + * @psalm-type ProfileFieldsExposurePolicy = 'hidden'|'private'|'users'|'public' * @psalm-type ProfileFieldsDefinitionInput = array{ * field_key?: string, * label?: string, * type?: string, - * admin_only?: bool, - * user_editable?: bool, - * user_visible?: bool, - * initial_visibility?: string, + * edit_policy?: ProfileFieldsEditPolicy, + * exposure_policy?: ProfileFieldsExposurePolicy, * sort_order?: int, * active?: bool, * options?: list, @@ -29,10 +29,8 @@ * field_key: non-empty-string, * label: non-empty-string, * type: ProfileFieldsType, - * admin_only: bool, - * user_editable: bool, - * user_visible: bool, - * initial_visibility: ProfileFieldsVisibility, + * edit_policy: ProfileFieldsEditPolicy, + * exposure_policy: ProfileFieldsExposurePolicy, * sort_order: int, * active: bool, * options: list|null, From e504e1a1fda6d60fa02c4d1897b264a0c524bef5 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:21 -0300 Subject: [PATCH 06/68] refactor(service): evaluate field access via FieldEditPolicy and FieldExposurePolicy Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Service/FieldAccessService.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/Service/FieldAccessService.php b/lib/Service/FieldAccessService.php index 25e4579..a702061 100644 --- a/lib/Service/FieldAccessService.php +++ b/lib/Service/FieldAccessService.php @@ -10,6 +10,8 @@ namespace OCA\ProfileFields\Service; use OCA\ProfileFields\Db\FieldDefinition; +use OCA\ProfileFields\Enum\FieldEditPolicy; +use OCA\ProfileFields\Enum\FieldExposurePolicy; use OCA\ProfileFields\Enum\FieldVisibility; class FieldAccessService { @@ -38,15 +40,11 @@ public function canEditValue(?string $actorUid, string $ownerUid, FieldDefinitio return false; } - if (!$definition->getUserVisible()) { + if (!FieldExposurePolicy::from($definition->getExposurePolicy())->isUserVisible()) { return false; } - if ($definition->getAdminOnly()) { - return false; - } - - return $definition->getUserEditable(); + return FieldEditPolicy::from($definition->getEditPolicy())->userCanEdit(); } public function canChangeVisibility(?string $actorUid, string $ownerUid, bool $actorIsAdmin): bool { From d664d6ae98ab514e6912729b8292f8b228fd4c83 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:22 -0300 Subject: [PATCH 07/68] refactor(service): validate FieldEditPolicy and FieldExposurePolicy on definitions Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Service/FieldDefinitionValidator.php | 44 +++++++++--------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/lib/Service/FieldDefinitionValidator.php b/lib/Service/FieldDefinitionValidator.php index eccf9fb..c0e81cf 100644 --- a/lib/Service/FieldDefinitionValidator.php +++ b/lib/Service/FieldDefinitionValidator.php @@ -10,8 +10,9 @@ namespace OCA\ProfileFields\Service; use InvalidArgumentException; +use OCA\ProfileFields\Enum\FieldEditPolicy; +use OCA\ProfileFields\Enum\FieldExposurePolicy; use OCA\ProfileFields\Enum\FieldType; -use OCA\ProfileFields\Enum\FieldVisibility; class FieldDefinitionValidator { /** @@ -19,10 +20,8 @@ class FieldDefinitionValidator { * field_key?: string, * label?: string, * type?: string, - * admin_only?: bool, - * user_editable?: bool, - * user_visible?: bool, - * initial_visibility?: string, + * edit_policy?: string, + * exposure_policy?: string, * sort_order?: int, * active?: bool, * options?: list, @@ -31,10 +30,8 @@ class FieldDefinitionValidator { * field_key: non-empty-string, * label: non-empty-string, * type: 'text'|'number'|'select', - * admin_only: bool, - * user_editable: bool, - * user_visible: bool, - * initial_visibility: 'private'|'users'|'public', + * edit_policy: 'admins'|'users', + * exposure_policy: 'hidden'|'private'|'users'|'public', * sort_order: int, * active: bool, * options: list|null, @@ -52,19 +49,14 @@ public function validate(array $definition): array { throw new InvalidArgumentException('type is not supported'); } - $visibility = (string)($definition['initial_visibility'] ?? FieldVisibility::PRIVATE->value); - if (!FieldVisibility::isValid($visibility)) { - throw new InvalidArgumentException('initial_visibility is not supported'); + $editPolicy = (string)($definition['edit_policy'] ?? FieldEditPolicy::USERS->value); + if (!FieldEditPolicy::isValid($editPolicy)) { + throw new InvalidArgumentException('edit_policy is not supported'); } - $adminOnly = (bool)($definition['admin_only'] ?? false); - $userEditable = (bool)($definition['user_editable'] ?? false); - $userVisible = (bool)($definition['user_visible'] ?? true); - if ($adminOnly && $userEditable) { - throw new InvalidArgumentException('admin_only and user_editable cannot both be enabled'); - } - if (!$userVisible && $userEditable) { - throw new InvalidArgumentException('user_editable cannot be enabled when the field is hidden from users'); + $exposurePolicy = (string)($definition['exposure_policy'] ?? FieldExposurePolicy::PRIVATE->value); + if (!FieldExposurePolicy::isValid($exposurePolicy)) { + throw new InvalidArgumentException('exposure_policy is not supported'); } $options = $this->validateOptions($type, $definition['options'] ?? null); @@ -73,10 +65,8 @@ public function validate(array $definition): array { 'field_key' => $fieldKey, 'label' => $label, 'type' => $type, - 'admin_only' => $adminOnly, - 'user_editable' => $userEditable, - 'user_visible' => $userVisible, - 'initial_visibility' => $visibility, + 'edit_policy' => $editPolicy, + 'exposure_policy' => $exposurePolicy, 'sort_order' => (int)($definition['sort_order'] ?? 0), 'active' => (bool)($definition['active'] ?? true), 'options' => $options, @@ -117,10 +107,8 @@ private function validateOptions(string $type, mixed $options): ?array { * field_key?: string, * label?: string, * type?: string, - * admin_only?: bool, - * user_editable?: bool, - * user_visible?: bool, - * initial_visibility?: string, + * edit_policy?: string, + * exposure_policy?: string, * sort_order?: int, * active?: bool, * options?: list, From b6766164c236480250bd51959aea597afa0ca047 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:22 -0300 Subject: [PATCH 08/68] refactor(service): apply policy enums in field definition service Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Service/FieldDefinitionService.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/Service/FieldDefinitionService.php b/lib/Service/FieldDefinitionService.php index 5a64b79..fcf758b 100644 --- a/lib/Service/FieldDefinitionService.php +++ b/lib/Service/FieldDefinitionService.php @@ -39,10 +39,8 @@ public function create(array $definition): FieldDefinition { $entity->setFieldKey($validated['field_key']); $entity->setLabel($validated['label']); $entity->setType($validated['type']); - $entity->setAdminOnly($validated['admin_only']); - $entity->setUserEditable($validated['user_editable']); - $entity->setUserVisible($validated['user_visible']); - $entity->setInitialVisibility($validated['initial_visibility']); + $entity->setEditPolicy($validated['edit_policy']); + $entity->setExposurePolicy($validated['exposure_policy']); $entity->setSortOrder($validated['sort_order']); $entity->setActive($validated['active']); try { @@ -71,10 +69,8 @@ public function update(FieldDefinition $existing, array $definition): FieldDefin $existing->setLabel($validated['label']); $existing->setType($validated['type']); - $existing->setAdminOnly($validated['admin_only']); - $existing->setUserEditable($validated['user_editable']); - $existing->setUserVisible($validated['user_visible']); - $existing->setInitialVisibility($validated['initial_visibility']); + $existing->setEditPolicy($validated['edit_policy']); + $existing->setExposurePolicy($validated['exposure_policy']); $existing->setSortOrder($validated['sort_order']); $existing->setActive($validated['active']); try { From 7daec5377e7b1c7bffb91ae15fe7d67241a483e4 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:22 -0300 Subject: [PATCH 09/68] refactor(service): enforce policy enums in field value service Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Service/FieldValueService.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Service/FieldValueService.php b/lib/Service/FieldValueService.php index c34b2d4..f527f4b 100644 --- a/lib/Service/FieldValueService.php +++ b/lib/Service/FieldValueService.php @@ -16,6 +16,7 @@ use OCA\ProfileFields\Db\FieldDefinition; use OCA\ProfileFields\Db\FieldValue; use OCA\ProfileFields\Db\FieldValueMapper; +use OCA\ProfileFields\Enum\FieldExposurePolicy; use OCA\ProfileFields\Enum\FieldType; use OCA\ProfileFields\Enum\FieldVisibility; use OCA\ProfileFields\Workflow\Event\ProfileFieldValueCreatedEvent; @@ -48,7 +49,7 @@ public function upsert( ): FieldValue { $normalizedValue = $this->normalizeValue($definition, $rawValue); $valueJson = $this->encodeValue($normalizedValue); - $visibility = $currentVisibility ?? $definition->getInitialVisibility(); + $visibility = $currentVisibility ?? FieldExposurePolicy::from($definition->getExposurePolicy())->initialVisibility()->value; if (!FieldVisibility::isValid($visibility)) { throw new InvalidArgumentException('current_visibility is not supported'); } From a407a42a75e4e2d13398bac36cfb51bb9f141b62 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:22 -0300 Subject: [PATCH 10/68] refactor(service): use policy enums when importing field data Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Service/DataImportService.php | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/Service/DataImportService.php b/lib/Service/DataImportService.php index 9971469..1c3cd42 100644 --- a/lib/Service/DataImportService.php +++ b/lib/Service/DataImportService.php @@ -70,10 +70,8 @@ public function import(array $payload, bool $dryRun = false): array { * field_key: non-empty-string, * label: non-empty-string, * type: 'text'|'number', - * admin_only: bool, - * user_editable: bool, - * user_visible: bool, - * initial_visibility: 'private'|'users'|'public', + * edit_policy: 'admins'|'users', + * exposure_policy: 'hidden'|'private'|'users'|'public', * sort_order: int, * active: bool, * created_at?: non-empty-string, @@ -151,10 +149,8 @@ private function collectValueSummary(array $values, array &$summary): void { * field_key: non-empty-string, * label: non-empty-string, * type: 'text'|'number', - * admin_only: bool, - * user_editable: bool, - * user_visible: bool, - * initial_visibility: 'private'|'users'|'public', + * edit_policy: 'admins'|'users', + * exposure_policy: 'hidden'|'private'|'users'|'public', * sort_order: int, * active: bool, * created_at?: non-empty-string, @@ -253,10 +249,8 @@ private function persistValues(array $values, array $definitionsByFieldKey, arra * field_key: non-empty-string, * label: non-empty-string, * type: 'text'|'number', - * admin_only: bool, - * user_editable: bool, - * user_visible: bool, - * initial_visibility: 'private'|'users'|'public', + * edit_policy: 'admins'|'users', + * exposure_policy: 'hidden'|'private'|'users'|'public', * sort_order: int, * active: bool, * created_at?: non-empty-string, From b96174d39f24d5a9e59b6936780c303cc80b1738 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:23 -0300 Subject: [PATCH 11/68] refactor(service): validate policy enum values in import payload Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Service/ImportPayloadValidator.php | 30 +++++++++----------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/lib/Service/ImportPayloadValidator.php b/lib/Service/ImportPayloadValidator.php index 323c6e6..b164cb8 100644 --- a/lib/Service/ImportPayloadValidator.php +++ b/lib/Service/ImportPayloadValidator.php @@ -33,10 +33,8 @@ public function __construct( * field_key: non-empty-string, * label: non-empty-string, * type: 'text'|'number', - * admin_only: bool, - * user_editable: bool, - * user_visible: bool, - * initial_visibility: 'private'|'users'|'public', + * edit_policy: 'admins'|'users', + * exposure_policy: 'hidden'|'private'|'users'|'public', * sort_order: int, * active: bool, * created_at?: non-empty-string, @@ -74,10 +72,8 @@ public function validate(array $payload): array { * field_key: non-empty-string, * label: non-empty-string, * type: 'text'|'number', - * admin_only: bool, - * user_editable: bool, - * user_visible: bool, - * initial_visibility: 'private'|'users'|'public', + * edit_policy: 'admins'|'users', + * exposure_policy: 'hidden'|'private'|'users'|'public', * sort_order: int, * active: bool, * created_at?: non-empty-string, @@ -124,10 +120,8 @@ private function validateDefinitions(array $definitions): array { * field_key: non-empty-string, * label: non-empty-string, * type: 'text'|'number', - * admin_only: bool, - * user_editable: bool, - * user_visible: bool, - * initial_visibility: 'private'|'users'|'public', + * edit_policy: 'admins'|'users', + * exposure_policy: 'hidden'|'private'|'users'|'public', * sort_order: int, * active: bool, * created_at?: non-empty-string, @@ -262,19 +256,15 @@ private function normalizeOptionalDate(array $payload, string $key, string $mess * field_key: non-empty-string, * label: non-empty-string, * type: 'text'|'number', - * admin_only: bool, - * user_editable: bool, - * user_visible: bool, - * initial_visibility: 'private'|'users'|'public', + * edit_policy: 'admins'|'users', + * exposure_policy: 'hidden'|'private'|'users'|'public', * sort_order: int, * active: bool, * } $definition */ private function isCompatibleDefinition(FieldDefinition $existingDefinition, array $definition): bool { return $existingDefinition->getType() === $definition['type'] - && $existingDefinition->getAdminOnly() === $definition['admin_only'] - && $existingDefinition->getUserEditable() === $definition['user_editable'] - && $existingDefinition->getUserVisible() === $definition['user_visible'] - && $existingDefinition->getInitialVisibility() === $definition['initial_visibility']; + && $existingDefinition->getEditPolicy() === $definition['edit_policy'] + && $existingDefinition->getExposurePolicy() === $definition['exposure_policy']; } } From af047f2f8e64b3d9caa37426869196467ffa1cc9 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:23 -0300 Subject: [PATCH 12/68] refactor(search): filter directory results by FieldExposurePolicy Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Search/ProfileFieldDirectorySearchService.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/Search/ProfileFieldDirectorySearchService.php b/lib/Search/ProfileFieldDirectorySearchService.php index cc2fac1..cf63b5a 100644 --- a/lib/Search/ProfileFieldDirectorySearchService.php +++ b/lib/Search/ProfileFieldDirectorySearchService.php @@ -12,6 +12,7 @@ use InvalidArgumentException; use OCA\ProfileFields\Db\FieldValue; use OCA\ProfileFields\Db\FieldValueMapper; +use OCA\ProfileFields\Enum\FieldExposurePolicy; use OCA\ProfileFields\Enum\FieldVisibility; use OCA\ProfileFields\Service\FieldDefinitionService; use OCP\IGroupManager; @@ -72,7 +73,7 @@ public function search(?IUser $actor, string $term, int $limit, int $offset): ar continue; } - if (!$this->isSearchableForActor($definition->getUserVisible(), $value->getCurrentVisibility(), $actorIsAdmin, $actorUid !== null)) { + if (!$this->isSearchableForActor(FieldExposurePolicy::from($definition->getExposurePolicy()), $value->getCurrentVisibility(), $actorIsAdmin, $actorUid !== null)) { continue; } @@ -123,12 +124,12 @@ private function extractScalarValue(FieldValue $value): ?string { return trim((string)$scalar); } - private function isSearchableForActor(bool $fieldIsUserVisible, string $currentVisibility, bool $actorIsAdmin, bool $actorIsAuthenticated): bool { + private function isSearchableForActor(FieldExposurePolicy $exposurePolicy, string $currentVisibility, bool $actorIsAdmin, bool $actorIsAuthenticated): bool { if ($actorIsAdmin) { return true; } - if (!$fieldIsUserVisible) { + if (!$exposurePolicy->isUserVisible()) { return false; } From bb4c41abcc64aabaa852eced131f586f36243943 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:23 -0300 Subject: [PATCH 13/68] refactor(controller): map policy enums in field definition API Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- .../FieldDefinitionApiController.php | 36 +++++++------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/lib/Controller/FieldDefinitionApiController.php b/lib/Controller/FieldDefinitionApiController.php index 9810cee..f8585ae 100644 --- a/lib/Controller/FieldDefinitionApiController.php +++ b/lib/Controller/FieldDefinitionApiController.php @@ -55,10 +55,8 @@ public function index(): DataResponse { * @param string $fieldKey Immutable unique key of the field * @param string $label Human-readable label shown in the UI * @param string $type Value type accepted by the field - * @param bool $adminOnly Whether only admins can edit values for this field - * @param bool $userEditable Whether the owner can edit the field value - * @param bool $userVisible Whether the owner can see the field in personal settings - * @param string $initialVisibility Initial visibility applied to new values + * @param string $editPolicy Whether values are managed by admins only or by users too + * @param string $exposurePolicy Whether the field is hidden or which default visibility new values receive * @param int $sortOrder Display order used in admin and profile forms * @param bool $active Whether the definition is currently active * @param list $options Allowed values for select fields (ignored for other types) @@ -72,10 +70,8 @@ public function create( string $fieldKey, string $label, string $type, - bool $adminOnly = false, - bool $userEditable = false, - bool $userVisible = true, - string $initialVisibility = 'private', + string $editPolicy = 'users', + string $exposurePolicy = 'private', int $sortOrder = 0, bool $active = true, array $options = [], @@ -85,10 +81,8 @@ public function create( 'field_key' => $fieldKey, 'label' => $label, 'type' => $type, - 'admin_only' => $adminOnly, - 'user_editable' => $userEditable, - 'user_visible' => $userVisible, - 'initial_visibility' => $initialVisibility, + 'edit_policy' => $editPolicy, + 'exposure_policy' => $exposurePolicy, 'sort_order' => $sortOrder, 'active' => $active, ]; @@ -111,10 +105,8 @@ public function create( * @param int $id Identifier of the field definition * @param string $label Human-readable label shown in the UI * @param string $type Value type accepted by the field - * @param bool $adminOnly Whether only admins can edit values for this field - * @param bool $userEditable Whether the owner can edit the field value - * @param bool $userVisible Whether the owner can see the field in personal settings - * @param string $initialVisibility Initial visibility applied to new values + * @param string $editPolicy Whether values are managed by admins only or by users too + * @param string $exposurePolicy Whether the field is hidden or which default visibility new values receive * @param int $sortOrder Display order used in admin and profile forms * @param bool $active Whether the definition is currently active * @param list $options Allowed values for select fields (ignored for other types) @@ -129,10 +121,8 @@ public function update( int $id, string $label, string $type, - bool $adminOnly = false, - bool $userEditable = false, - bool $userVisible = true, - string $initialVisibility = 'private', + string $editPolicy = 'users', + string $exposurePolicy = 'private', int $sortOrder = 0, bool $active = true, array $options = [], @@ -146,10 +136,8 @@ public function update( $payload = [ 'label' => $label, 'type' => $type, - 'admin_only' => $adminOnly, - 'user_editable' => $userEditable, - 'user_visible' => $userVisible, - 'initial_visibility' => $initialVisibility, + 'edit_policy' => $editPolicy, + 'exposure_policy' => $exposurePolicy, 'sort_order' => $sortOrder, 'active' => $active, ]; From 9d844425d1e65c1255d0c03005d8711a1840fc74 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:23 -0300 Subject: [PATCH 14/68] refactor(controller): map policy enums in field value API Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- lib/Controller/FieldValueApiController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Controller/FieldValueApiController.php b/lib/Controller/FieldValueApiController.php index 6de3417..afd4efc 100644 --- a/lib/Controller/FieldValueApiController.php +++ b/lib/Controller/FieldValueApiController.php @@ -11,6 +11,7 @@ use InvalidArgumentException; use OCA\ProfileFields\AppInfo\Application; +use OCA\ProfileFields\Enum\FieldExposurePolicy; use OCA\ProfileFields\Service\FieldAccessService; use OCA\ProfileFields\Service\FieldDefinitionService; use OCA\ProfileFields\Service\FieldValueService; @@ -58,7 +59,7 @@ public function index(): DataResponse { $editableFields = []; foreach ($definitions as $definition) { - if (!$definition->getUserVisible()) { + if (!FieldExposurePolicy::from($definition->getExposurePolicy())->isUserVisible()) { continue; } From 372faf7cb435716081119526eff68bb7d7c876d3 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:53 -0300 Subject: [PATCH 15/68] feat(openapi): expose FieldEditPolicy and FieldExposurePolicy in spec Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- openapi.json | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/openapi.json b/openapi.json index c66314f..fa66e1d 100644 --- a/openapi.json +++ b/openapi.json @@ -27,10 +27,8 @@ "field_key", "label", "type", - "admin_only", - "user_editable", - "user_visible", - "initial_visibility", + "edit_policy", + "exposure_policy", "sort_order", "active", "options", @@ -53,17 +51,11 @@ "type": { "$ref": "#/components/schemas/Type" }, - "admin_only": { - "type": "boolean" + "edit_policy": { + "$ref": "#/components/schemas/EditPolicy" }, - "user_editable": { - "type": "boolean" - }, - "user_visible": { - "type": "boolean" - }, - "initial_visibility": { - "$ref": "#/components/schemas/Visibility" + "exposure_policy": { + "$ref": "#/components/schemas/ExposurePolicy" }, "sort_order": { "type": "integer", @@ -87,6 +79,13 @@ } } }, + "EditPolicy": { + "type": "string", + "enum": [ + "admins", + "users" + ] + }, "EditableField": { "type": "object", "required": [ @@ -107,6 +106,15 @@ } } }, + "ExposurePolicy": { + "type": "string", + "enum": [ + "hidden", + "private", + "users", + "public" + ] + }, "OCSMeta": { "type": "object", "required": [ From b01cf5a0bfbee2c8ebd3ba7a3acb8b1c7bb6f663 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:53 -0300 Subject: [PATCH 16/68] feat(openapi): expose policy enums in administration spec Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- openapi-administration.json | 80 ++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 46 deletions(-) diff --git a/openapi-administration.json b/openapi-administration.json index ac4e54e..256cd6c 100644 --- a/openapi-administration.json +++ b/openapi-administration.json @@ -27,10 +27,8 @@ "field_key", "label", "type", - "admin_only", - "user_editable", - "user_visible", - "initial_visibility", + "edit_policy", + "exposure_policy", "sort_order", "active", "options", @@ -53,17 +51,11 @@ "type": { "$ref": "#/components/schemas/Type" }, - "admin_only": { - "type": "boolean" - }, - "user_editable": { - "type": "boolean" - }, - "user_visible": { - "type": "boolean" + "edit_policy": { + "$ref": "#/components/schemas/EditPolicy" }, - "initial_visibility": { - "$ref": "#/components/schemas/Visibility" + "exposure_policy": { + "$ref": "#/components/schemas/ExposurePolicy" }, "sort_order": { "type": "integer", @@ -87,6 +79,22 @@ } } }, + "EditPolicy": { + "type": "string", + "enum": [ + "admins", + "users" + ] + }, + "ExposurePolicy": { + "type": "string", + "enum": [ + "hidden", + "private", + "users", + "public" + ] + }, "LookupField": { "type": "object", "required": [ @@ -355,25 +363,15 @@ "type": "string", "description": "Value type accepted by the field" }, - "adminOnly": { - "type": "boolean", - "default": false, - "description": "Whether only admins can edit values for this field" - }, - "userEditable": { - "type": "boolean", - "default": false, - "description": "Whether the owner can edit the field value" - }, - "userVisible": { - "type": "boolean", - "default": true, - "description": "Whether the owner can see the field in personal settings" + "editPolicy": { + "type": "string", + "default": "users", + "description": "Whether values are managed by admins only or by users too" }, - "initialVisibility": { + "exposurePolicy": { "type": "string", "default": "private", - "description": "Initial visibility applied to new values" + "description": "Whether the field is hidden or which default visibility new values receive" }, "sortOrder": { "type": "integer", @@ -518,25 +516,15 @@ "type": "string", "description": "Value type accepted by the field" }, - "adminOnly": { - "type": "boolean", - "default": false, - "description": "Whether only admins can edit values for this field" - }, - "userEditable": { - "type": "boolean", - "default": false, - "description": "Whether the owner can edit the field value" - }, - "userVisible": { - "type": "boolean", - "default": true, - "description": "Whether the owner can see the field in personal settings" + "editPolicy": { + "type": "string", + "default": "users", + "description": "Whether values are managed by admins only or by users too" }, - "initialVisibility": { + "exposurePolicy": { "type": "string", "default": "private", - "description": "Initial visibility applied to new values" + "description": "Whether the field is hidden or which default visibility new values receive" }, "sortOrder": { "type": "integer", From a365a2ea6e2c2b42cb441cb86e007e46ff9dc4b2 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:53 -0300 Subject: [PATCH 17/68] feat(openapi): expose policy enums in full spec Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- openapi-full.json | 94 +++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 56 deletions(-) diff --git a/openapi-full.json b/openapi-full.json index 728d488..9160a38 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -27,10 +27,8 @@ "field_key", "label", "type", - "admin_only", - "user_editable", - "user_visible", - "initial_visibility", + "edit_policy", + "exposure_policy", "sort_order", "active", "options", @@ -53,17 +51,11 @@ "type": { "$ref": "#/components/schemas/Type" }, - "admin_only": { - "type": "boolean" - }, - "user_editable": { - "type": "boolean" - }, - "user_visible": { - "type": "boolean" + "edit_policy": { + "$ref": "#/components/schemas/EditPolicy" }, - "initial_visibility": { - "$ref": "#/components/schemas/Visibility" + "exposure_policy": { + "$ref": "#/components/schemas/ExposurePolicy" }, "sort_order": { "type": "integer", @@ -99,17 +91,11 @@ "type": { "type": "string" }, - "admin_only": { - "type": "boolean" + "edit_policy": { + "$ref": "#/components/schemas/EditPolicy" }, - "user_editable": { - "type": "boolean" - }, - "user_visible": { - "type": "boolean" - }, - "initial_visibility": { - "type": "string" + "exposure_policy": { + "$ref": "#/components/schemas/ExposurePolicy" }, "sort_order": { "type": "integer", @@ -126,6 +112,13 @@ } } }, + "EditPolicy": { + "type": "string", + "enum": [ + "admins", + "users" + ] + }, "EditableField": { "type": "object", "required": [ @@ -146,6 +139,15 @@ } } }, + "ExposurePolicy": { + "type": "string", + "enum": [ + "hidden", + "private", + "users", + "public" + ] + }, "LookupField": { "type": "object", "required": [ @@ -436,25 +438,15 @@ "type": "string", "description": "Value type accepted by the field" }, - "adminOnly": { - "type": "boolean", - "default": false, - "description": "Whether only admins can edit values for this field" - }, - "userEditable": { - "type": "boolean", - "default": false, - "description": "Whether the owner can edit the field value" - }, - "userVisible": { - "type": "boolean", - "default": true, - "description": "Whether the owner can see the field in personal settings" + "editPolicy": { + "type": "string", + "default": "users", + "description": "Whether values are managed by admins only or by users too" }, - "initialVisibility": { + "exposurePolicy": { "type": "string", "default": "private", - "description": "Initial visibility applied to new values" + "description": "Whether the field is hidden or which default visibility new values receive" }, "sortOrder": { "type": "integer", @@ -599,25 +591,15 @@ "type": "string", "description": "Value type accepted by the field" }, - "adminOnly": { - "type": "boolean", - "default": false, - "description": "Whether only admins can edit values for this field" - }, - "userEditable": { - "type": "boolean", - "default": false, - "description": "Whether the owner can edit the field value" - }, - "userVisible": { - "type": "boolean", - "default": true, - "description": "Whether the owner can see the field in personal settings" + "editPolicy": { + "type": "string", + "default": "users", + "description": "Whether values are managed by admins only or by users too" }, - "initialVisibility": { + "exposurePolicy": { "type": "string", "default": "private", - "description": "Initial visibility applied to new values" + "description": "Whether the field is hidden or which default visibility new values receive" }, "sortOrder": { "type": "integer", From d0a9971a9d0c4636af93b0311ed926a83fe8b069 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:14:54 -0300 Subject: [PATCH 18/68] chore(build): add redocly config for openapi-typescript generation Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- redocly.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 redocly.yaml diff --git a/redocly.yaml b/redocly.yaml new file mode 100644 index 0000000..c43685d --- /dev/null +++ b/redocly.yaml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 LibreCode coop and contributors +# SPDX-License-Identifier: AGPL-3.0-or-later +apis: + openapi@v1: + root: ./openapi.json + x-openapi-ts: + output: ./src/types/openapi/openapi.ts + openapi-administration@v1: + root: ./openapi-administration.json + x-openapi-ts: + output: ./src/types/openapi/openapi-administration.ts + openapi-full@v1: + root: ./openapi-full.json + x-openapi-ts: + output: ./src/types/openapi/openapi-full.ts From 455c12f7bd6bce9fa4f82fd5bfb3658a14fb6264 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:15:23 -0300 Subject: [PATCH 19/68] feat(types): regenerate openapi.ts with policy enum types Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- src/types/openapi/openapi.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/types/openapi/openapi.ts b/src/types/openapi/openapi.ts index 1ab2e3d..aa0db2a 100644 --- a/src/types/openapi/openapi.ts +++ b/src/types/openapi/openapi.ts @@ -3,7 +3,7 @@ * Do not make direct changes to the file. */ -export interface paths { +export type paths = { "/ocs/v2.php/apps/profile_fields/api/v1/me/values": { parameters: { query?: never; @@ -64,9 +64,9 @@ export interface paths { patch?: never; trace?: never; }; -} +}; export type webhooks = Record; -export interface components { +export type components = { schemas: { Definition: { /** Format: int64 */ @@ -74,10 +74,8 @@ export interface components { field_key: string; label: string; type: components["schemas"]["Type"]; - admin_only: boolean; - user_editable: boolean; - user_visible: boolean; - initial_visibility: components["schemas"]["Visibility"]; + edit_policy: components["schemas"]["EditPolicy"]; + exposure_policy: components["schemas"]["ExposurePolicy"]; /** Format: int64 */ sort_order: number; active: boolean; @@ -85,11 +83,15 @@ export interface components { created_at: string; updated_at: string; }; + /** @enum {string} */ + EditPolicy: "admins" | "users"; EditableField: { definition: components["schemas"]["Definition"]; value: components["schemas"]["ValueRecord"]; can_edit: boolean; }; + /** @enum {string} */ + ExposurePolicy: "hidden" | "private" | "users" | "public"; OCSMeta: { status: string; statuscode: number; @@ -121,7 +123,7 @@ export interface components { requestBodies: never; headers: never; pathItems: never; -} +}; export type $defs = Record; export interface operations { "field_value_api-index": { From a6a87dc7776283bc70c413dddeb322bc67ba0a3a Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:15:23 -0300 Subject: [PATCH 20/68] feat(types): regenerate openapi-administration.ts with policy enum types Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- src/types/openapi/openapi-administration.ts | 58 +++++++-------------- 1 file changed, 20 insertions(+), 38 deletions(-) diff --git a/src/types/openapi/openapi-administration.ts b/src/types/openapi/openapi-administration.ts index 53aeb9a..d050176 100644 --- a/src/types/openapi/openapi-administration.ts +++ b/src/types/openapi/openapi-administration.ts @@ -3,7 +3,7 @@ * Do not make direct changes to the file. */ -export interface paths { +export type paths = { "/ocs/v2.php/apps/profile_fields/api/v1/definitions": { parameters: { query?: never; @@ -140,9 +140,9 @@ export interface paths { patch?: never; trace?: never; }; -} +}; export type webhooks = Record; -export interface components { +export type components = { schemas: { Definition: { /** Format: int64 */ @@ -150,10 +150,8 @@ export interface components { field_key: string; label: string; type: components["schemas"]["Type"]; - admin_only: boolean; - user_editable: boolean; - user_visible: boolean; - initial_visibility: components["schemas"]["Visibility"]; + edit_policy: components["schemas"]["EditPolicy"]; + exposure_policy: components["schemas"]["ExposurePolicy"]; /** Format: int64 */ sort_order: number; active: boolean; @@ -161,6 +159,10 @@ export interface components { created_at: string; updated_at: string; }; + /** @enum {string} */ + EditPolicy: "admins" | "users"; + /** @enum {string} */ + ExposurePolicy: "hidden" | "private" | "users" | "public"; LookupField: { definition: components["schemas"]["Definition"]; value: components["schemas"]["ValueRecord"]; @@ -214,7 +216,7 @@ export interface components { requestBodies: never; headers: never; pathItems: never; -} +}; export type $defs = Record; export interface operations { "field_definition_api-index": { @@ -265,25 +267,15 @@ export interface operations { /** @description Value type accepted by the field */ type: string; /** - * @description Whether only admins can edit values for this field - * @default false - */ - adminOnly?: boolean; - /** - * @description Whether the owner can edit the field value - * @default false - */ - userEditable?: boolean; - /** - * @description Whether the owner can see the field in personal settings - * @default true + * @description Whether values are managed by admins only or by users too + * @default users */ - userVisible?: boolean; + editPolicy?: string; /** - * @description Initial visibility applied to new values + * @description Whether the field is hidden or which default visibility new values receive * @default private */ - initialVisibility?: string; + exposurePolicy?: string; /** * Format: int64 * @description Display order used in admin and profile forms @@ -357,25 +349,15 @@ export interface operations { /** @description Value type accepted by the field */ type: string; /** - * @description Whether only admins can edit values for this field - * @default false - */ - adminOnly?: boolean; - /** - * @description Whether the owner can edit the field value - * @default false - */ - userEditable?: boolean; - /** - * @description Whether the owner can see the field in personal settings - * @default true + * @description Whether values are managed by admins only or by users too + * @default users */ - userVisible?: boolean; + editPolicy?: string; /** - * @description Initial visibility applied to new values + * @description Whether the field is hidden or which default visibility new values receive * @default private */ - initialVisibility?: string; + exposurePolicy?: string; /** * Format: int64 * @description Display order used in admin and profile forms From 6e75da16b98d750fc843d2dc5e9ed5a1fd55dda0 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:15:23 -0300 Subject: [PATCH 21/68] feat(types): regenerate openapi-full.ts with policy enum types Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- src/types/openapi/openapi-full.ts | 64 +++++++++++-------------------- 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/src/types/openapi/openapi-full.ts b/src/types/openapi/openapi-full.ts index a11fa2e..1603a5e 100644 --- a/src/types/openapi/openapi-full.ts +++ b/src/types/openapi/openapi-full.ts @@ -3,7 +3,7 @@ * Do not make direct changes to the file. */ -export interface paths { +export type paths = { "/ocs/v2.php/apps/profile_fields/api/v1/definitions": { parameters: { query?: never; @@ -200,9 +200,9 @@ export interface paths { patch?: never; trace?: never; }; -} +}; export type webhooks = Record; -export interface components { +export type components = { schemas: { Definition: { /** Format: int64 */ @@ -210,10 +210,8 @@ export interface components { field_key: string; label: string; type: components["schemas"]["Type"]; - admin_only: boolean; - user_editable: boolean; - user_visible: boolean; - initial_visibility: components["schemas"]["Visibility"]; + edit_policy: components["schemas"]["EditPolicy"]; + exposure_policy: components["schemas"]["ExposurePolicy"]; /** Format: int64 */ sort_order: number; active: boolean; @@ -225,20 +223,22 @@ export interface components { field_key?: string; label?: string; type?: string; - admin_only?: boolean; - user_editable?: boolean; - user_visible?: boolean; - initial_visibility?: string; + edit_policy?: components["schemas"]["EditPolicy"]; + exposure_policy?: components["schemas"]["ExposurePolicy"]; /** Format: int64 */ sort_order?: number; active?: boolean; options?: string[]; }; + /** @enum {string} */ + EditPolicy: "admins" | "users"; EditableField: { definition: components["schemas"]["Definition"]; value: components["schemas"]["ValueRecord"]; can_edit: boolean; }; + /** @enum {string} */ + ExposurePolicy: "hidden" | "private" | "users" | "public"; LookupField: { definition: components["schemas"]["Definition"]; value: components["schemas"]["ValueRecord"]; @@ -299,7 +299,7 @@ export interface components { requestBodies: never; headers: never; pathItems: never; -} +}; export type $defs = Record; export interface operations { "field_definition_api-index": { @@ -350,25 +350,15 @@ export interface operations { /** @description Value type accepted by the field */ type: string; /** - * @description Whether only admins can edit values for this field - * @default false - */ - adminOnly?: boolean; - /** - * @description Whether the owner can edit the field value - * @default false - */ - userEditable?: boolean; - /** - * @description Whether the owner can see the field in personal settings - * @default true + * @description Whether values are managed by admins only or by users too + * @default users */ - userVisible?: boolean; + editPolicy?: string; /** - * @description Initial visibility applied to new values + * @description Whether the field is hidden or which default visibility new values receive * @default private */ - initialVisibility?: string; + exposurePolicy?: string; /** * Format: int64 * @description Display order used in admin and profile forms @@ -442,25 +432,15 @@ export interface operations { /** @description Value type accepted by the field */ type: string; /** - * @description Whether only admins can edit values for this field - * @default false - */ - adminOnly?: boolean; - /** - * @description Whether the owner can edit the field value - * @default false - */ - userEditable?: boolean; - /** - * @description Whether the owner can see the field in personal settings - * @default true + * @description Whether values are managed by admins only or by users too + * @default users */ - userVisible?: boolean; + editPolicy?: string; /** - * @description Initial visibility applied to new values + * @description Whether the field is hidden or which default visibility new values receive * @default private */ - initialVisibility?: string; + exposurePolicy?: string; /** * Format: int64 * @description Display order used in admin and profile forms From 6bec60bcd3422fd5a8c8b69849e8659f72ed8a4e Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:15:24 -0300 Subject: [PATCH 22/68] refactor(types): expose FieldEditPolicy and FieldExposurePolicy in index Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- src/types/index.ts | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/types/index.ts b/src/types/index.ts index 7b2a4ba..7ee701e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -21,7 +21,15 @@ type ApiRequestJsonBody = ApiJsonBody & { + edit_policy: FieldEditPolicy + exposure_policy: FieldExposurePolicy +} // openapi-typescript collapses the loose `value: mixed` schema to Record. // Keep the surrounding contract generated and widen only this payload leaf for frontend use. @@ -43,8 +51,25 @@ export type LookupResult = Omit } -export type CreateDefinitionPayload = ApiRequestJsonBody -export type UpdateDefinitionPayload = ApiRequestJsonBody +export type CreateDefinitionPayload = { + fieldKey: string + label: string + type: FieldType + editPolicy?: FieldEditPolicy + exposurePolicy?: FieldExposurePolicy + sortOrder?: number + active?: boolean + options?: string[] +} +export type UpdateDefinitionPayload = { + label: string + type: FieldType + editPolicy?: FieldEditPolicy + exposurePolicy?: FieldExposurePolicy + sortOrder?: number + active?: boolean + options?: string[] +} export type UpsertOwnValuePayload = ApiRequestJsonBody export type UpdateOwnVisibilityPayload = ApiRequestJsonBody export type UpsertAdminUserValuePayload = ApiRequestJsonBody @@ -57,3 +82,14 @@ export type AdminEditableField = { export type ApiError = { message: string } + +export const definitionDefaultVisibility = (definition: Pick): FieldVisibility => { + switch (definition.exposure_policy) { + case 'public': + return 'public' + case 'users': + return 'users' + default: + return 'private' + } +} From abd7f048d1b6de20eef886086b9b779184ab4f2f Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:15:24 -0300 Subject: [PATCH 23/68] fix(types): extend generateOcsUrl and generateUrl signatures Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- env.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/env.d.ts b/env.d.ts index c14d27e..d92e54c 100644 --- a/env.d.ts +++ b/env.d.ts @@ -4,8 +4,8 @@ /// declare module '@nextcloud/router' { - export function generateOcsUrl(path: string): string - export function generateUrl(path: string): string + export function generateOcsUrl(path: string, params?: object, options?: object): string + export function generateUrl(path: string, params?: object, options?: object): string } interface SettingsUserListRow { From 01d55af399287e4e99c2b9ad61286d5784ec8de3 Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:15:24 -0300 Subject: [PATCH 24/68] chore(deps): fix typescript:generate script to use openapi-typescript -t Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4834997..ef7fbff 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build": "NODE_ENV=production vite --mode production build", "dev": "NODE_ENV=development vite --mode development build", "screenshots:refresh": "node playwright/generate-screenshots.mjs", - "typescript:generate": "mkdir -p src/types/openapi && npx openapi-typescript openapi.json -o src/types/openapi/openapi.ts && npx openapi-typescript openapi-administration.json -o src/types/openapi/openapi-administration.ts && npx openapi-typescript openapi-full.json -o src/types/openapi/openapi-full.ts", + "typescript:generate": "npx openapi-typescript -t", "watch": "NODE_ENV=development vite --mode development build --watch", "test": "vitest run", "test:watch": "vitest", From ef4007a171fb9e5a1d6787c7249e3494adb1f91c Mon Sep 17 00:00:00 2001 From: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> Date: Thu, 19 Mar 2026 00:15:24 -0300 Subject: [PATCH 25/68] feat(admin-ui): replace status text with NcChip and add NcActions menu Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com> --- src/views/AdminSettings.vue | 875 +++++++++++++++++++++++++----------- 1 file changed, 607 insertions(+), 268 deletions(-) diff --git a/src/views/AdminSettings.vue b/src/views/AdminSettings.vue index 6a965d7..dbc9861 100644 --- a/src/views/AdminSettings.vue +++ b/src/views/AdminSettings.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later

Field catalog administration

- Create the global field catalog, control who can edit each field and tune the default visibility used when a value is first stored. + Create the global field catalog, choose who can edit each field, and define how exposed new values are by default.

@@ -22,9 +22,6 @@ SPDX-License-Identifier: AGPL-3.0-or-later {{ errorMessage }} - - {{ successMessage }} -
@@ -35,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later

Defined fields

-

Pick an existing definition to edit its rules or start a fresh field.

+

Pick a field to edit it or create a new one.

New field @@ -44,48 +41,104 @@ SPDX-License-Identifier: AGPL-3.0-or-later -
    -
  • - -
  • -
+ + + -
+ +
-
- - - New field - +
+
+

{{ editorEmptyState.title }}

+

{{ editorEmptyState.description }}

+
+ + New field + +
+
+ +