Completed
Push — master ( d67396...31cfba )
by
unknown
58:26 queued 13:56
created
lib/private/Profile/ProfileManager.php 1 patch
Indentation   +387 added lines, -387 removed lines patch added patch discarded remove patch
@@ -38,391 +38,391 @@
 block discarded – undo
38 38
  * @psalm-import-type CoreProfileFields from ResponseDefinitions
39 39
  */
40 40
 class ProfileManager implements IProfileManager {
41
-	/** @var ILinkAction[] */
42
-	private array $actions = [];
43
-
44
-	/** @var null|ILinkAction[] */
45
-	private ?array $sortedActions = null;
46
-	/** @var CappedMemoryCache<ProfileConfig> */
47
-	private CappedMemoryCache $configCache;
48
-
49
-	private const CORE_APP_ID = 'core';
50
-
51
-	/**
52
-	 * Array of account property actions
53
-	 */
54
-	private const ACCOUNT_PROPERTY_ACTIONS = [
55
-		EmailAction::class,
56
-		PhoneAction::class,
57
-		WebsiteAction::class,
58
-		TwitterAction::class,
59
-		FediverseAction::class,
60
-	];
61
-
62
-	/**
63
-	 * Array of account properties displayed on the profile
64
-	 */
65
-	private const PROFILE_PROPERTIES = [
66
-		IAccountManager::PROPERTY_ADDRESS,
67
-		IAccountManager::PROPERTY_AVATAR,
68
-		IAccountManager::PROPERTY_BIOGRAPHY,
69
-		IAccountManager::PROPERTY_DISPLAYNAME,
70
-		IAccountManager::PROPERTY_HEADLINE,
71
-		IAccountManager::PROPERTY_ORGANISATION,
72
-		IAccountManager::PROPERTY_ROLE,
73
-		IAccountManager::PROPERTY_PRONOUNS,
74
-	];
75
-
76
-	public function __construct(
77
-		private IAccountManager $accountManager,
78
-		private IAppManager $appManager,
79
-		private IConfig $config,
80
-		private ProfileConfigMapper $configMapper,
81
-		private ContainerInterface $container,
82
-		private KnownUserService $knownUserService,
83
-		private IFactory $l10nFactory,
84
-		private LoggerInterface $logger,
85
-		private Coordinator $coordinator,
86
-	) {
87
-		$this->configCache = new CappedMemoryCache();
88
-	}
89
-
90
-	/**
91
-	 * If no user is passed as an argument return whether profile is enabled globally in `config.php`
92
-	 */
93
-	public function isProfileEnabled(?IUser $user = null): bool {
94
-		$profileEnabledGlobally = $this->config->getSystemValueBool('profile.enabled', true);
95
-
96
-		if (empty($user) || !$profileEnabledGlobally) {
97
-			return $profileEnabledGlobally;
98
-		}
99
-
100
-		$account = $this->accountManager->getAccount($user);
101
-		return (bool)filter_var(
102
-			$account->getProperty(IAccountManager::PROPERTY_PROFILE_ENABLED)->getValue(),
103
-			FILTER_VALIDATE_BOOLEAN,
104
-			FILTER_NULL_ON_FAILURE,
105
-		);
106
-	}
107
-
108
-	/**
109
-	 * Register an action for the user
110
-	 */
111
-	private function registerAction(ILinkAction $action, IUser $targetUser, ?IUser $visitingUser): void {
112
-		$action->preload($targetUser);
113
-
114
-		if ($action->getTarget() === null) {
115
-			// Actions without a target are not registered
116
-			return;
117
-		}
118
-
119
-		if ($action->getAppId() !== self::CORE_APP_ID) {
120
-			if (!$this->appManager->isEnabledForUser($action->getAppId(), $targetUser)) {
121
-				$this->logger->notice('App: ' . $action->getAppId() . ' cannot register actions as it is not enabled for the target user: ' . $targetUser->getUID());
122
-				return;
123
-			}
124
-			if (!$this->appManager->isEnabledForUser($action->getAppId(), $visitingUser)) {
125
-				$this->logger->notice('App: ' . $action->getAppId() . ' cannot register actions as it is not enabled for the visiting user: ' . ($visitingUser ? $visitingUser->getUID() : '(user not connected)'));
126
-				return;
127
-			}
128
-		}
129
-
130
-		if (in_array($action->getId(), self::PROFILE_PROPERTIES, true)) {
131
-			$this->logger->error('Cannot register action with ID: ' . $action->getId() . ', as it is used by a core account property.');
132
-			return;
133
-		}
134
-
135
-		if (isset($this->actions[$action->getId()])) {
136
-			$this->logger->error('Cannot register duplicate action: ' . $action->getId());
137
-			return;
138
-		}
139
-
140
-		// Add action to associative array of actions
141
-		$this->actions[$action->getId()] = $action;
142
-	}
143
-
144
-	/**
145
-	 * Return an array of registered profile actions for the user
146
-	 *
147
-	 * @return ILinkAction[]
148
-	 */
149
-	private function getActions(IUser $targetUser, ?IUser $visitingUser): array {
150
-		// If actions are already registered and sorted, return them
151
-		if ($this->sortedActions !== null) {
152
-			return $this->sortedActions;
153
-		}
154
-
155
-		foreach (self::ACCOUNT_PROPERTY_ACTIONS as $actionClass) {
156
-			/** @var ILinkAction $action */
157
-			$action = $this->container->get($actionClass);
158
-			$this->registerAction($action, $targetUser, $visitingUser);
159
-		}
160
-
161
-		$context = $this->coordinator->getRegistrationContext();
162
-
163
-		if ($context !== null) {
164
-			foreach ($context->getProfileLinkActions() as $registration) {
165
-				/** @var ILinkAction $action */
166
-				$action = $this->container->get($registration->getService());
167
-				$this->registerAction($action, $targetUser, $visitingUser);
168
-			}
169
-		}
170
-
171
-		$actionsClone = $this->actions;
172
-		// Sort associative array into indexed array in ascending order of priority
173
-		usort($actionsClone, function (ILinkAction $a, ILinkAction $b) {
174
-			return $a->getPriority() === $b->getPriority() ? 0 : ($a->getPriority() < $b->getPriority() ? -1 : 1);
175
-		});
176
-
177
-		$this->sortedActions = $actionsClone;
178
-		return $this->sortedActions;
179
-	}
180
-
181
-	/**
182
-	 * Return whether the profile parameter of the target user
183
-	 * is visible to the visiting user
184
-	 */
185
-	public function isProfileFieldVisible(string $profileField, IUser $targetUser, ?IUser $visitingUser): bool {
186
-		try {
187
-			$account = $this->accountManager->getAccount($targetUser);
188
-			$scope = $account->getProperty($profileField)->getScope();
189
-		} catch (PropertyDoesNotExistException $e) {
190
-			// Allow the exception as not all profile parameters are account properties
191
-		}
192
-
193
-		$visibility = $this->getProfileConfig($targetUser, $visitingUser)[$profileField]['visibility'];
194
-		// Handle profile visibility and account property scope
195
-
196
-		if ($visibility === self::VISIBILITY_SHOW_USERS_ONLY) {
197
-			if (empty($scope)) {
198
-				return $visitingUser !== null;
199
-			}
200
-
201
-			return match ($scope) {
202
-				IAccountManager::SCOPE_PRIVATE => $visitingUser !== null && $this->knownUserService->isKnownToUser($targetUser->getUID(), $visitingUser->getUID()),
203
-				IAccountManager::SCOPE_LOCAL,
204
-				IAccountManager::SCOPE_FEDERATED,
205
-				IAccountManager::SCOPE_PUBLISHED => $visitingUser !== null,
206
-				default => false,
207
-			};
208
-		}
209
-
210
-		if ($visibility === self::VISIBILITY_SHOW) {
211
-			if (empty($scope)) {
212
-				return true;
213
-			}
214
-
215
-			return match ($scope) {
216
-				IAccountManager::SCOPE_PRIVATE => $visitingUser !== null && $this->knownUserService->isKnownToUser($targetUser->getUID(), $visitingUser->getUID()),
217
-				IAccountManager::SCOPE_LOCAL,
218
-				IAccountManager::SCOPE_FEDERATED,
219
-				IAccountManager::SCOPE_PUBLISHED => true,
220
-				default => false,
221
-			};
222
-		}
223
-
224
-		return false;
225
-	}
226
-
227
-	/**
228
-	 * Return the profile parameters of the target user that are visible to the visiting user
229
-	 * in an associative array
230
-	 * @psalm-return CoreProfileFields
231
-	 */
232
-	public function getProfileFields(IUser $targetUser, ?IUser $visitingUser): array {
233
-		$account = $this->accountManager->getAccount($targetUser);
234
-
235
-		// Initialize associative array of profile parameters
236
-		$profileParameters = [
237
-			'userId' => $account->getUser()->getUID(),
238
-		];
239
-
240
-		// Add account properties
241
-		foreach (self::PROFILE_PROPERTIES as $property) {
242
-			switch ($property) {
243
-				case IAccountManager::PROPERTY_ADDRESS:
244
-				case IAccountManager::PROPERTY_BIOGRAPHY:
245
-				case IAccountManager::PROPERTY_DISPLAYNAME:
246
-				case IAccountManager::PROPERTY_HEADLINE:
247
-				case IAccountManager::PROPERTY_ORGANISATION:
248
-				case IAccountManager::PROPERTY_ROLE:
249
-				case IAccountManager::PROPERTY_PRONOUNS:
250
-					$profileParameters[$property]
251
-						= $this->isProfileFieldVisible($property, $targetUser, $visitingUser)
252
-						// Explicitly set to null when value is empty string
253
-						? ($account->getProperty($property)->getValue() ?: null)
254
-						: null;
255
-					break;
256
-				case IAccountManager::PROPERTY_AVATAR:
257
-					// Add avatar visibility
258
-					$profileParameters['isUserAvatarVisible'] = $this->isProfileFieldVisible($property, $targetUser, $visitingUser);
259
-					break;
260
-			}
261
-		}
262
-
263
-		// Add actions
264
-		$profileParameters['actions'] = array_map(
265
-			function (ILinkAction $action) {
266
-				return [
267
-					'id' => $action->getId(),
268
-					'icon' => $action->getIcon(),
269
-					'title' => $action->getTitle(),
270
-					'target' => $action->getTarget(),
271
-				];
272
-			},
273
-			// This is needed to reindex the array after filtering
274
-			array_values(
275
-				array_filter(
276
-					$this->getActions($targetUser, $visitingUser),
277
-					function (ILinkAction $action) use ($targetUser, $visitingUser) {
278
-						return $this->isProfileFieldVisible($action->getId(), $targetUser, $visitingUser);
279
-					}
280
-				),
281
-			)
282
-		);
283
-
284
-		return $profileParameters;
285
-	}
286
-
287
-	/**
288
-	 * Return the filtered profile config containing only
289
-	 * the properties to be stored on the database
290
-	 */
291
-	private function filterNotStoredProfileConfig(array $profileConfig): array {
292
-		$dbParamConfigProperties = [
293
-			'visibility',
294
-		];
295
-
296
-		foreach ($profileConfig as $paramId => $paramConfig) {
297
-			$profileConfig[$paramId] = array_intersect_key($paramConfig, array_flip($dbParamConfigProperties));
298
-		}
299
-
300
-		return $profileConfig;
301
-	}
302
-
303
-	/**
304
-	 * Return the default profile config
305
-	 */
306
-	private function getDefaultProfileConfig(IUser $targetUser, ?IUser $visitingUser): array {
307
-		// Construct the default config for actions
308
-		$actionsConfig = [];
309
-		foreach ($this->getActions($targetUser, $visitingUser) as $action) {
310
-			$actionsConfig[$action->getId()] = ['visibility' => self::DEFAULT_VISIBILITY];
311
-		}
312
-
313
-		// Construct the default config for account properties
314
-		$propertiesConfig = [];
315
-		foreach (self::DEFAULT_PROPERTY_VISIBILITY as $property => $visibility) {
316
-			$propertiesConfig[$property] = ['visibility' => $visibility];
317
-		}
318
-
319
-		return array_merge($actionsConfig, $propertiesConfig);
320
-	}
321
-
322
-	/**
323
-	 * Return the profile config of the target user,
324
-	 * if a config does not already exist a default config is created and returned
325
-	 */
326
-	public function getProfileConfig(IUser $targetUser, ?IUser $visitingUser): array {
327
-		$defaultProfileConfig = $this->getDefaultProfileConfig($targetUser, $visitingUser);
328
-		try {
329
-			if (($config = $this->configCache[$targetUser->getUID()]) === null) {
330
-				$config = $this->configMapper->get($targetUser->getUID());
331
-				$this->configCache[$targetUser->getUID()] = $config;
332
-			}
333
-			// Merge defaults with the existing config in case the defaults are missing
334
-			$config->setConfigArray(array_merge(
335
-				$defaultProfileConfig,
336
-				$this->filterNotStoredProfileConfig($config->getConfigArray()),
337
-			));
338
-			$this->configMapper->update($config);
339
-			$configArray = $config->getConfigArray();
340
-		} catch (DoesNotExistException $e) {
341
-			// Create a new default config if it does not exist
342
-			$config = new ProfileConfig();
343
-			$config->setUserId($targetUser->getUID());
344
-			$config->setConfigArray($defaultProfileConfig);
345
-			$this->configMapper->insert($config);
346
-			$configArray = $config->getConfigArray();
347
-		}
348
-
349
-		return $configArray;
350
-	}
351
-
352
-	/**
353
-	 * Return the profile config of the target user with additional metadata,
354
-	 * if a config does not already exist a default config is created and returned
355
-	 */
356
-	public function getProfileConfigWithMetadata(IUser $targetUser, ?IUser $visitingUser): array {
357
-		$configArray = $this->getProfileConfig($targetUser, $visitingUser);
358
-
359
-		$actionsMetadata = [];
360
-		foreach ($this->getActions($targetUser, $visitingUser) as $action) {
361
-			$actionsMetadata[$action->getId()] = [
362
-				'appId' => $action->getAppId(),
363
-				'displayId' => $action->getDisplayId(),
364
-			];
365
-		}
366
-
367
-		// Add metadata for account property actions which are always configurable
368
-		foreach (self::ACCOUNT_PROPERTY_ACTIONS as $actionClass) {
369
-			/** @var ILinkAction $action */
370
-			$action = $this->container->get($actionClass);
371
-			if (!isset($actionsMetadata[$action->getId()])) {
372
-				$actionsMetadata[$action->getId()] = [
373
-					'appId' => $action->getAppId(),
374
-					'displayId' => $action->getDisplayId(),
375
-				];
376
-			}
377
-		}
378
-
379
-		$propertiesMetadata = [
380
-			IAccountManager::PROPERTY_ADDRESS => [
381
-				'appId' => self::CORE_APP_ID,
382
-				'displayId' => $this->l10nFactory->get('lib')->t('Address'),
383
-			],
384
-			IAccountManager::PROPERTY_AVATAR => [
385
-				'appId' => self::CORE_APP_ID,
386
-				'displayId' => $this->l10nFactory->get('lib')->t('Profile picture'),
387
-			],
388
-			IAccountManager::PROPERTY_BIOGRAPHY => [
389
-				'appId' => self::CORE_APP_ID,
390
-				'displayId' => $this->l10nFactory->get('lib')->t('About'),
391
-			],
392
-			IAccountManager::PROPERTY_DISPLAYNAME => [
393
-				'appId' => self::CORE_APP_ID,
394
-				'displayId' => $this->l10nFactory->get('lib')->t('Display name'),
395
-			],
396
-			IAccountManager::PROPERTY_HEADLINE => [
397
-				'appId' => self::CORE_APP_ID,
398
-				'displayId' => $this->l10nFactory->get('lib')->t('Headline'),
399
-			],
400
-			IAccountManager::PROPERTY_ORGANISATION => [
401
-				'appId' => self::CORE_APP_ID,
402
-				'displayId' => $this->l10nFactory->get('lib')->t('Organization'),
403
-			],
404
-			IAccountManager::PROPERTY_ROLE => [
405
-				'appId' => self::CORE_APP_ID,
406
-				'displayId' => $this->l10nFactory->get('lib')->t('Role'),
407
-			],
408
-			IAccountManager::PROPERTY_PRONOUNS => [
409
-				'appId' => self::CORE_APP_ID,
410
-				'displayId' => $this->l10nFactory->get('lib')->t('Pronouns'),
411
-			],
412
-		];
413
-
414
-		$paramMetadata = array_merge($actionsMetadata, $propertiesMetadata);
415
-		$configArray = array_intersect_key($configArray, $paramMetadata);
416
-
417
-		foreach ($configArray as $paramId => $paramConfig) {
418
-			if (isset($paramMetadata[$paramId])) {
419
-				$configArray[$paramId] = array_merge(
420
-					$paramConfig,
421
-					$paramMetadata[$paramId],
422
-				);
423
-			}
424
-		}
425
-
426
-		return $configArray;
427
-	}
41
+    /** @var ILinkAction[] */
42
+    private array $actions = [];
43
+
44
+    /** @var null|ILinkAction[] */
45
+    private ?array $sortedActions = null;
46
+    /** @var CappedMemoryCache<ProfileConfig> */
47
+    private CappedMemoryCache $configCache;
48
+
49
+    private const CORE_APP_ID = 'core';
50
+
51
+    /**
52
+     * Array of account property actions
53
+     */
54
+    private const ACCOUNT_PROPERTY_ACTIONS = [
55
+        EmailAction::class,
56
+        PhoneAction::class,
57
+        WebsiteAction::class,
58
+        TwitterAction::class,
59
+        FediverseAction::class,
60
+    ];
61
+
62
+    /**
63
+     * Array of account properties displayed on the profile
64
+     */
65
+    private const PROFILE_PROPERTIES = [
66
+        IAccountManager::PROPERTY_ADDRESS,
67
+        IAccountManager::PROPERTY_AVATAR,
68
+        IAccountManager::PROPERTY_BIOGRAPHY,
69
+        IAccountManager::PROPERTY_DISPLAYNAME,
70
+        IAccountManager::PROPERTY_HEADLINE,
71
+        IAccountManager::PROPERTY_ORGANISATION,
72
+        IAccountManager::PROPERTY_ROLE,
73
+        IAccountManager::PROPERTY_PRONOUNS,
74
+    ];
75
+
76
+    public function __construct(
77
+        private IAccountManager $accountManager,
78
+        private IAppManager $appManager,
79
+        private IConfig $config,
80
+        private ProfileConfigMapper $configMapper,
81
+        private ContainerInterface $container,
82
+        private KnownUserService $knownUserService,
83
+        private IFactory $l10nFactory,
84
+        private LoggerInterface $logger,
85
+        private Coordinator $coordinator,
86
+    ) {
87
+        $this->configCache = new CappedMemoryCache();
88
+    }
89
+
90
+    /**
91
+     * If no user is passed as an argument return whether profile is enabled globally in `config.php`
92
+     */
93
+    public function isProfileEnabled(?IUser $user = null): bool {
94
+        $profileEnabledGlobally = $this->config->getSystemValueBool('profile.enabled', true);
95
+
96
+        if (empty($user) || !$profileEnabledGlobally) {
97
+            return $profileEnabledGlobally;
98
+        }
99
+
100
+        $account = $this->accountManager->getAccount($user);
101
+        return (bool)filter_var(
102
+            $account->getProperty(IAccountManager::PROPERTY_PROFILE_ENABLED)->getValue(),
103
+            FILTER_VALIDATE_BOOLEAN,
104
+            FILTER_NULL_ON_FAILURE,
105
+        );
106
+    }
107
+
108
+    /**
109
+     * Register an action for the user
110
+     */
111
+    private function registerAction(ILinkAction $action, IUser $targetUser, ?IUser $visitingUser): void {
112
+        $action->preload($targetUser);
113
+
114
+        if ($action->getTarget() === null) {
115
+            // Actions without a target are not registered
116
+            return;
117
+        }
118
+
119
+        if ($action->getAppId() !== self::CORE_APP_ID) {
120
+            if (!$this->appManager->isEnabledForUser($action->getAppId(), $targetUser)) {
121
+                $this->logger->notice('App: ' . $action->getAppId() . ' cannot register actions as it is not enabled for the target user: ' . $targetUser->getUID());
122
+                return;
123
+            }
124
+            if (!$this->appManager->isEnabledForUser($action->getAppId(), $visitingUser)) {
125
+                $this->logger->notice('App: ' . $action->getAppId() . ' cannot register actions as it is not enabled for the visiting user: ' . ($visitingUser ? $visitingUser->getUID() : '(user not connected)'));
126
+                return;
127
+            }
128
+        }
129
+
130
+        if (in_array($action->getId(), self::PROFILE_PROPERTIES, true)) {
131
+            $this->logger->error('Cannot register action with ID: ' . $action->getId() . ', as it is used by a core account property.');
132
+            return;
133
+        }
134
+
135
+        if (isset($this->actions[$action->getId()])) {
136
+            $this->logger->error('Cannot register duplicate action: ' . $action->getId());
137
+            return;
138
+        }
139
+
140
+        // Add action to associative array of actions
141
+        $this->actions[$action->getId()] = $action;
142
+    }
143
+
144
+    /**
145
+     * Return an array of registered profile actions for the user
146
+     *
147
+     * @return ILinkAction[]
148
+     */
149
+    private function getActions(IUser $targetUser, ?IUser $visitingUser): array {
150
+        // If actions are already registered and sorted, return them
151
+        if ($this->sortedActions !== null) {
152
+            return $this->sortedActions;
153
+        }
154
+
155
+        foreach (self::ACCOUNT_PROPERTY_ACTIONS as $actionClass) {
156
+            /** @var ILinkAction $action */
157
+            $action = $this->container->get($actionClass);
158
+            $this->registerAction($action, $targetUser, $visitingUser);
159
+        }
160
+
161
+        $context = $this->coordinator->getRegistrationContext();
162
+
163
+        if ($context !== null) {
164
+            foreach ($context->getProfileLinkActions() as $registration) {
165
+                /** @var ILinkAction $action */
166
+                $action = $this->container->get($registration->getService());
167
+                $this->registerAction($action, $targetUser, $visitingUser);
168
+            }
169
+        }
170
+
171
+        $actionsClone = $this->actions;
172
+        // Sort associative array into indexed array in ascending order of priority
173
+        usort($actionsClone, function (ILinkAction $a, ILinkAction $b) {
174
+            return $a->getPriority() === $b->getPriority() ? 0 : ($a->getPriority() < $b->getPriority() ? -1 : 1);
175
+        });
176
+
177
+        $this->sortedActions = $actionsClone;
178
+        return $this->sortedActions;
179
+    }
180
+
181
+    /**
182
+     * Return whether the profile parameter of the target user
183
+     * is visible to the visiting user
184
+     */
185
+    public function isProfileFieldVisible(string $profileField, IUser $targetUser, ?IUser $visitingUser): bool {
186
+        try {
187
+            $account = $this->accountManager->getAccount($targetUser);
188
+            $scope = $account->getProperty($profileField)->getScope();
189
+        } catch (PropertyDoesNotExistException $e) {
190
+            // Allow the exception as not all profile parameters are account properties
191
+        }
192
+
193
+        $visibility = $this->getProfileConfig($targetUser, $visitingUser)[$profileField]['visibility'];
194
+        // Handle profile visibility and account property scope
195
+
196
+        if ($visibility === self::VISIBILITY_SHOW_USERS_ONLY) {
197
+            if (empty($scope)) {
198
+                return $visitingUser !== null;
199
+            }
200
+
201
+            return match ($scope) {
202
+                IAccountManager::SCOPE_PRIVATE => $visitingUser !== null && $this->knownUserService->isKnownToUser($targetUser->getUID(), $visitingUser->getUID()),
203
+                IAccountManager::SCOPE_LOCAL,
204
+                IAccountManager::SCOPE_FEDERATED,
205
+                IAccountManager::SCOPE_PUBLISHED => $visitingUser !== null,
206
+                default => false,
207
+            };
208
+        }
209
+
210
+        if ($visibility === self::VISIBILITY_SHOW) {
211
+            if (empty($scope)) {
212
+                return true;
213
+            }
214
+
215
+            return match ($scope) {
216
+                IAccountManager::SCOPE_PRIVATE => $visitingUser !== null && $this->knownUserService->isKnownToUser($targetUser->getUID(), $visitingUser->getUID()),
217
+                IAccountManager::SCOPE_LOCAL,
218
+                IAccountManager::SCOPE_FEDERATED,
219
+                IAccountManager::SCOPE_PUBLISHED => true,
220
+                default => false,
221
+            };
222
+        }
223
+
224
+        return false;
225
+    }
226
+
227
+    /**
228
+     * Return the profile parameters of the target user that are visible to the visiting user
229
+     * in an associative array
230
+     * @psalm-return CoreProfileFields
231
+     */
232
+    public function getProfileFields(IUser $targetUser, ?IUser $visitingUser): array {
233
+        $account = $this->accountManager->getAccount($targetUser);
234
+
235
+        // Initialize associative array of profile parameters
236
+        $profileParameters = [
237
+            'userId' => $account->getUser()->getUID(),
238
+        ];
239
+
240
+        // Add account properties
241
+        foreach (self::PROFILE_PROPERTIES as $property) {
242
+            switch ($property) {
243
+                case IAccountManager::PROPERTY_ADDRESS:
244
+                case IAccountManager::PROPERTY_BIOGRAPHY:
245
+                case IAccountManager::PROPERTY_DISPLAYNAME:
246
+                case IAccountManager::PROPERTY_HEADLINE:
247
+                case IAccountManager::PROPERTY_ORGANISATION:
248
+                case IAccountManager::PROPERTY_ROLE:
249
+                case IAccountManager::PROPERTY_PRONOUNS:
250
+                    $profileParameters[$property]
251
+                        = $this->isProfileFieldVisible($property, $targetUser, $visitingUser)
252
+                        // Explicitly set to null when value is empty string
253
+                        ? ($account->getProperty($property)->getValue() ?: null)
254
+                        : null;
255
+                    break;
256
+                case IAccountManager::PROPERTY_AVATAR:
257
+                    // Add avatar visibility
258
+                    $profileParameters['isUserAvatarVisible'] = $this->isProfileFieldVisible($property, $targetUser, $visitingUser);
259
+                    break;
260
+            }
261
+        }
262
+
263
+        // Add actions
264
+        $profileParameters['actions'] = array_map(
265
+            function (ILinkAction $action) {
266
+                return [
267
+                    'id' => $action->getId(),
268
+                    'icon' => $action->getIcon(),
269
+                    'title' => $action->getTitle(),
270
+                    'target' => $action->getTarget(),
271
+                ];
272
+            },
273
+            // This is needed to reindex the array after filtering
274
+            array_values(
275
+                array_filter(
276
+                    $this->getActions($targetUser, $visitingUser),
277
+                    function (ILinkAction $action) use ($targetUser, $visitingUser) {
278
+                        return $this->isProfileFieldVisible($action->getId(), $targetUser, $visitingUser);
279
+                    }
280
+                ),
281
+            )
282
+        );
283
+
284
+        return $profileParameters;
285
+    }
286
+
287
+    /**
288
+     * Return the filtered profile config containing only
289
+     * the properties to be stored on the database
290
+     */
291
+    private function filterNotStoredProfileConfig(array $profileConfig): array {
292
+        $dbParamConfigProperties = [
293
+            'visibility',
294
+        ];
295
+
296
+        foreach ($profileConfig as $paramId => $paramConfig) {
297
+            $profileConfig[$paramId] = array_intersect_key($paramConfig, array_flip($dbParamConfigProperties));
298
+        }
299
+
300
+        return $profileConfig;
301
+    }
302
+
303
+    /**
304
+     * Return the default profile config
305
+     */
306
+    private function getDefaultProfileConfig(IUser $targetUser, ?IUser $visitingUser): array {
307
+        // Construct the default config for actions
308
+        $actionsConfig = [];
309
+        foreach ($this->getActions($targetUser, $visitingUser) as $action) {
310
+            $actionsConfig[$action->getId()] = ['visibility' => self::DEFAULT_VISIBILITY];
311
+        }
312
+
313
+        // Construct the default config for account properties
314
+        $propertiesConfig = [];
315
+        foreach (self::DEFAULT_PROPERTY_VISIBILITY as $property => $visibility) {
316
+            $propertiesConfig[$property] = ['visibility' => $visibility];
317
+        }
318
+
319
+        return array_merge($actionsConfig, $propertiesConfig);
320
+    }
321
+
322
+    /**
323
+     * Return the profile config of the target user,
324
+     * if a config does not already exist a default config is created and returned
325
+     */
326
+    public function getProfileConfig(IUser $targetUser, ?IUser $visitingUser): array {
327
+        $defaultProfileConfig = $this->getDefaultProfileConfig($targetUser, $visitingUser);
328
+        try {
329
+            if (($config = $this->configCache[$targetUser->getUID()]) === null) {
330
+                $config = $this->configMapper->get($targetUser->getUID());
331
+                $this->configCache[$targetUser->getUID()] = $config;
332
+            }
333
+            // Merge defaults with the existing config in case the defaults are missing
334
+            $config->setConfigArray(array_merge(
335
+                $defaultProfileConfig,
336
+                $this->filterNotStoredProfileConfig($config->getConfigArray()),
337
+            ));
338
+            $this->configMapper->update($config);
339
+            $configArray = $config->getConfigArray();
340
+        } catch (DoesNotExistException $e) {
341
+            // Create a new default config if it does not exist
342
+            $config = new ProfileConfig();
343
+            $config->setUserId($targetUser->getUID());
344
+            $config->setConfigArray($defaultProfileConfig);
345
+            $this->configMapper->insert($config);
346
+            $configArray = $config->getConfigArray();
347
+        }
348
+
349
+        return $configArray;
350
+    }
351
+
352
+    /**
353
+     * Return the profile config of the target user with additional metadata,
354
+     * if a config does not already exist a default config is created and returned
355
+     */
356
+    public function getProfileConfigWithMetadata(IUser $targetUser, ?IUser $visitingUser): array {
357
+        $configArray = $this->getProfileConfig($targetUser, $visitingUser);
358
+
359
+        $actionsMetadata = [];
360
+        foreach ($this->getActions($targetUser, $visitingUser) as $action) {
361
+            $actionsMetadata[$action->getId()] = [
362
+                'appId' => $action->getAppId(),
363
+                'displayId' => $action->getDisplayId(),
364
+            ];
365
+        }
366
+
367
+        // Add metadata for account property actions which are always configurable
368
+        foreach (self::ACCOUNT_PROPERTY_ACTIONS as $actionClass) {
369
+            /** @var ILinkAction $action */
370
+            $action = $this->container->get($actionClass);
371
+            if (!isset($actionsMetadata[$action->getId()])) {
372
+                $actionsMetadata[$action->getId()] = [
373
+                    'appId' => $action->getAppId(),
374
+                    'displayId' => $action->getDisplayId(),
375
+                ];
376
+            }
377
+        }
378
+
379
+        $propertiesMetadata = [
380
+            IAccountManager::PROPERTY_ADDRESS => [
381
+                'appId' => self::CORE_APP_ID,
382
+                'displayId' => $this->l10nFactory->get('lib')->t('Address'),
383
+            ],
384
+            IAccountManager::PROPERTY_AVATAR => [
385
+                'appId' => self::CORE_APP_ID,
386
+                'displayId' => $this->l10nFactory->get('lib')->t('Profile picture'),
387
+            ],
388
+            IAccountManager::PROPERTY_BIOGRAPHY => [
389
+                'appId' => self::CORE_APP_ID,
390
+                'displayId' => $this->l10nFactory->get('lib')->t('About'),
391
+            ],
392
+            IAccountManager::PROPERTY_DISPLAYNAME => [
393
+                'appId' => self::CORE_APP_ID,
394
+                'displayId' => $this->l10nFactory->get('lib')->t('Display name'),
395
+            ],
396
+            IAccountManager::PROPERTY_HEADLINE => [
397
+                'appId' => self::CORE_APP_ID,
398
+                'displayId' => $this->l10nFactory->get('lib')->t('Headline'),
399
+            ],
400
+            IAccountManager::PROPERTY_ORGANISATION => [
401
+                'appId' => self::CORE_APP_ID,
402
+                'displayId' => $this->l10nFactory->get('lib')->t('Organization'),
403
+            ],
404
+            IAccountManager::PROPERTY_ROLE => [
405
+                'appId' => self::CORE_APP_ID,
406
+                'displayId' => $this->l10nFactory->get('lib')->t('Role'),
407
+            ],
408
+            IAccountManager::PROPERTY_PRONOUNS => [
409
+                'appId' => self::CORE_APP_ID,
410
+                'displayId' => $this->l10nFactory->get('lib')->t('Pronouns'),
411
+            ],
412
+        ];
413
+
414
+        $paramMetadata = array_merge($actionsMetadata, $propertiesMetadata);
415
+        $configArray = array_intersect_key($configArray, $paramMetadata);
416
+
417
+        foreach ($configArray as $paramId => $paramConfig) {
418
+            if (isset($paramMetadata[$paramId])) {
419
+                $configArray[$paramId] = array_merge(
420
+                    $paramConfig,
421
+                    $paramMetadata[$paramId],
422
+                );
423
+            }
424
+        }
425
+
426
+        return $configArray;
427
+    }
428 428
 }
Please login to merge, or discard this patch.