Passed
Push — master ( 812bc8...7ac6ee )
by Roeland
17:22 queued 11s
created
lib/private/Accounts/AccountManager.php 1 patch
Indentation   +414 added lines, -414 removed lines patch added patch discarded remove patch
@@ -58,418 +58,418 @@
 block discarded – undo
58 58
  */
59 59
 class AccountManager implements IAccountManager {
60 60
 
61
-	/** @var  IDBConnection database connection */
62
-	private $connection;
63
-
64
-	/** @var IConfig */
65
-	private $config;
66
-
67
-	/** @var string table name */
68
-	private $table = 'accounts';
69
-
70
-	/** @var string table name */
71
-	private $dataTable = 'accounts_data';
72
-
73
-	/** @var EventDispatcherInterface */
74
-	private $eventDispatcher;
75
-
76
-	/** @var IJobList */
77
-	private $jobList;
78
-
79
-	/** @var LoggerInterface */
80
-	private $logger;
81
-
82
-	public function __construct(IDBConnection $connection,
83
-								IConfig $config,
84
-								EventDispatcherInterface $eventDispatcher,
85
-								IJobList $jobList,
86
-								LoggerInterface $logger) {
87
-		$this->connection = $connection;
88
-		$this->config = $config;
89
-		$this->eventDispatcher = $eventDispatcher;
90
-		$this->jobList = $jobList;
91
-		$this->logger = $logger;
92
-	}
93
-
94
-	/**
95
-	 * @param string $input
96
-	 * @return string Provided phone number in E.164 format when it was a valid number
97
-	 * @throws \InvalidArgumentException When the phone number was invalid or no default region is set and the number doesn't start with a country code
98
-	 */
99
-	protected function parsePhoneNumber(string $input): string {
100
-		$defaultRegion = $this->config->getSystemValueString('default_phone_region', '');
101
-
102
-		if ($defaultRegion === '') {
103
-			// When no default region is set, only +49… numbers are valid
104
-			if (strpos($input, '+') !== 0) {
105
-				throw new \InvalidArgumentException(self::PROPERTY_PHONE);
106
-			}
107
-
108
-			$defaultRegion = 'EN';
109
-		}
110
-
111
-		$phoneUtil = PhoneNumberUtil::getInstance();
112
-		try {
113
-			$phoneNumber = $phoneUtil->parse($input, $defaultRegion);
114
-			if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
115
-				return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
116
-			}
117
-		} catch (NumberParseException $e) {
118
-		}
119
-
120
-		throw new \InvalidArgumentException(self::PROPERTY_PHONE);
121
-	}
122
-
123
-	/**
124
-	 * update user record
125
-	 *
126
-	 * @param IUser $user
127
-	 * @param array $data
128
-	 * @param bool $throwOnData Set to true if you can inform the user about invalid data
129
-	 * @return array The potentially modified data (e.g. phone numbers are converted to E.164 format)
130
-	 * @throws \InvalidArgumentException Message is the property that was invalid
131
-	 */
132
-	public function updateUser(IUser $user, array $data, bool $throwOnData = false): array {
133
-		$userData = $this->getUser($user);
134
-		$updated = true;
135
-
136
-		if (isset($data[self::PROPERTY_PHONE]) && $data[self::PROPERTY_PHONE]['value'] !== '') {
137
-			try {
138
-				$data[self::PROPERTY_PHONE]['value'] = $this->parsePhoneNumber($data[self::PROPERTY_PHONE]['value']);
139
-			} catch (\InvalidArgumentException $e) {
140
-				if ($throwOnData) {
141
-					throw $e;
142
-				}
143
-				$data[self::PROPERTY_PHONE]['value'] = '';
144
-			}
145
-		}
146
-
147
-		if (empty($userData)) {
148
-			$this->insertNewUser($user, $data);
149
-		} elseif ($userData !== $data) {
150
-			$data = $this->checkEmailVerification($userData, $data, $user);
151
-			$data = $this->updateVerifyStatus($userData, $data);
152
-			$this->updateExistingUser($user, $data);
153
-		} else {
154
-			// nothing needs to be done if new and old data set are the same
155
-			$updated = false;
156
-		}
157
-
158
-		if ($updated) {
159
-			$this->eventDispatcher->dispatch(
160
-				'OC\AccountManager::userUpdated',
161
-				new GenericEvent($user, $data)
162
-			);
163
-		}
164
-
165
-		return $data;
166
-	}
167
-
168
-	/**
169
-	 * delete user from accounts table
170
-	 *
171
-	 * @param IUser $user
172
-	 */
173
-	public function deleteUser(IUser $user) {
174
-		$uid = $user->getUID();
175
-		$query = $this->connection->getQueryBuilder();
176
-		$query->delete($this->table)
177
-			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
178
-			->execute();
179
-
180
-		$this->deleteUserData($user);
181
-	}
182
-
183
-	/**
184
-	 * delete user from accounts table
185
-	 *
186
-	 * @param IUser $user
187
-	 */
188
-	public function deleteUserData(IUser $user): void {
189
-		$uid = $user->getUID();
190
-		$query = $this->connection->getQueryBuilder();
191
-		$query->delete($this->dataTable)
192
-			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
193
-			->execute();
194
-	}
195
-
196
-	/**
197
-	 * get stored data from a given user
198
-	 *
199
-	 * @param IUser $user
200
-	 * @return array
201
-	 */
202
-	public function getUser(IUser $user) {
203
-		$uid = $user->getUID();
204
-		$query = $this->connection->getQueryBuilder();
205
-		$query->select('data')
206
-			->from($this->table)
207
-			->where($query->expr()->eq('uid', $query->createParameter('uid')))
208
-			->setParameter('uid', $uid);
209
-		$result = $query->execute();
210
-		$accountData = $result->fetchAll();
211
-		$result->closeCursor();
212
-
213
-		if (empty($accountData)) {
214
-			$userData = $this->buildDefaultUserRecord($user);
215
-			$this->insertNewUser($user, $userData);
216
-			return $userData;
217
-		}
218
-
219
-		$userDataArray = json_decode($accountData[0]['data'], true);
220
-		$jsonError = json_last_error();
221
-		if ($userDataArray === null || $userDataArray === [] || $jsonError !== JSON_ERROR_NONE) {
222
-			$this->logger->critical("User data of $uid contained invalid JSON (error $jsonError), hence falling back to a default user record");
223
-			return $this->buildDefaultUserRecord($user);
224
-		}
225
-
226
-		$userDataArray = $this->addMissingDefaultValues($userDataArray);
227
-
228
-		return $userDataArray;
229
-	}
230
-
231
-	public function searchUsers(string $property, array $values): array {
232
-		$chunks = array_chunk($values, 500);
233
-		$query = $this->connection->getQueryBuilder();
234
-		$query->select('*')
235
-			->from($this->dataTable)
236
-			->where($query->expr()->eq('name', $query->createNamedParameter($property)))
237
-			->andWhere($query->expr()->in('value', $query->createParameter('values')));
238
-
239
-		$matches = [];
240
-		foreach ($chunks as $chunk) {
241
-			$query->setParameter('values', $chunk, IQueryBuilder::PARAM_STR_ARRAY);
242
-			$result = $query->execute();
243
-
244
-			while ($row = $result->fetch()) {
245
-				$matches[$row['value']] = $row['uid'];
246
-			}
247
-			$result->closeCursor();
248
-		}
249
-
250
-		return $matches;
251
-	}
252
-
253
-	/**
254
-	 * check if we need to ask the server for email verification, if yes we create a cronjob
255
-	 *
256
-	 * @param $oldData
257
-	 * @param $newData
258
-	 * @param IUser $user
259
-	 * @return array
260
-	 */
261
-	protected function checkEmailVerification($oldData, $newData, IUser $user) {
262
-		if ($oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']) {
263
-			$this->jobList->add(VerifyUserData::class,
264
-				[
265
-					'verificationCode' => '',
266
-					'data' => $newData[self::PROPERTY_EMAIL]['value'],
267
-					'type' => self::PROPERTY_EMAIL,
268
-					'uid' => $user->getUID(),
269
-					'try' => 0,
270
-					'lastRun' => time()
271
-				]
272
-			);
273
-			$newData[self::PROPERTY_EMAIL]['verified'] = self::VERIFICATION_IN_PROGRESS;
274
-		}
275
-
276
-		return $newData;
277
-	}
278
-
279
-	/**
280
-	 * make sure that all expected data are set
281
-	 *
282
-	 * @param array $userData
283
-	 * @return array
284
-	 */
285
-	protected function addMissingDefaultValues(array $userData) {
286
-		foreach ($userData as $key => $value) {
287
-			if (!isset($userData[$key]['verified'])) {
288
-				$userData[$key]['verified'] = self::NOT_VERIFIED;
289
-			}
290
-		}
291
-
292
-		return $userData;
293
-	}
294
-
295
-	/**
296
-	 * reset verification status if personal data changed
297
-	 *
298
-	 * @param array $oldData
299
-	 * @param array $newData
300
-	 * @return array
301
-	 */
302
-	protected function updateVerifyStatus($oldData, $newData) {
303
-
304
-		// which account was already verified successfully?
305
-		$twitterVerified = isset($oldData[self::PROPERTY_TWITTER]['verified']) && $oldData[self::PROPERTY_TWITTER]['verified'] === self::VERIFIED;
306
-		$websiteVerified = isset($oldData[self::PROPERTY_WEBSITE]['verified']) && $oldData[self::PROPERTY_WEBSITE]['verified'] === self::VERIFIED;
307
-		$emailVerified = isset($oldData[self::PROPERTY_EMAIL]['verified']) && $oldData[self::PROPERTY_EMAIL]['verified'] === self::VERIFIED;
308
-
309
-		// keep old verification status if we don't have a new one
310
-		if (!isset($newData[self::PROPERTY_TWITTER]['verified'])) {
311
-			// keep old verification status if value didn't changed and an old value exists
312
-			$keepOldStatus = $newData[self::PROPERTY_TWITTER]['value'] === $oldData[self::PROPERTY_TWITTER]['value'] && isset($oldData[self::PROPERTY_TWITTER]['verified']);
313
-			$newData[self::PROPERTY_TWITTER]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_TWITTER]['verified'] : self::NOT_VERIFIED;
314
-		}
315
-
316
-		if (!isset($newData[self::PROPERTY_WEBSITE]['verified'])) {
317
-			// keep old verification status if value didn't changed and an old value exists
318
-			$keepOldStatus = $newData[self::PROPERTY_WEBSITE]['value'] === $oldData[self::PROPERTY_WEBSITE]['value'] && isset($oldData[self::PROPERTY_WEBSITE]['verified']);
319
-			$newData[self::PROPERTY_WEBSITE]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_WEBSITE]['verified'] : self::NOT_VERIFIED;
320
-		}
321
-
322
-		if (!isset($newData[self::PROPERTY_EMAIL]['verified'])) {
323
-			// keep old verification status if value didn't changed and an old value exists
324
-			$keepOldStatus = $newData[self::PROPERTY_EMAIL]['value'] === $oldData[self::PROPERTY_EMAIL]['value'] && isset($oldData[self::PROPERTY_EMAIL]['verified']);
325
-			$newData[self::PROPERTY_EMAIL]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_EMAIL]['verified'] : self::VERIFICATION_IN_PROGRESS;
326
-		}
327
-
328
-		// reset verification status if a value from a previously verified data was changed
329
-		if ($twitterVerified &&
330
-			$oldData[self::PROPERTY_TWITTER]['value'] !== $newData[self::PROPERTY_TWITTER]['value']
331
-		) {
332
-			$newData[self::PROPERTY_TWITTER]['verified'] = self::NOT_VERIFIED;
333
-		}
334
-
335
-		if ($websiteVerified &&
336
-			$oldData[self::PROPERTY_WEBSITE]['value'] !== $newData[self::PROPERTY_WEBSITE]['value']
337
-		) {
338
-			$newData[self::PROPERTY_WEBSITE]['verified'] = self::NOT_VERIFIED;
339
-		}
340
-
341
-		if ($emailVerified &&
342
-			$oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']
343
-		) {
344
-			$newData[self::PROPERTY_EMAIL]['verified'] = self::NOT_VERIFIED;
345
-		}
346
-
347
-		return $newData;
348
-	}
349
-
350
-	/**
351
-	 * add new user to accounts table
352
-	 *
353
-	 * @param IUser $user
354
-	 * @param array $data
355
-	 */
356
-	protected function insertNewUser(IUser $user, array $data): void {
357
-		$uid = $user->getUID();
358
-		$jsonEncodedData = json_encode($data);
359
-		$query = $this->connection->getQueryBuilder();
360
-		$query->insert($this->table)
361
-			->values(
362
-				[
363
-					'uid' => $query->createNamedParameter($uid),
364
-					'data' => $query->createNamedParameter($jsonEncodedData),
365
-				]
366
-			)
367
-			->execute();
368
-
369
-		$this->deleteUserData($user);
370
-		$this->writeUserData($user, $data);
371
-	}
372
-
373
-	/**
374
-	 * update existing user in accounts table
375
-	 *
376
-	 * @param IUser $user
377
-	 * @param array $data
378
-	 */
379
-	protected function updateExistingUser(IUser $user, array $data): void {
380
-		$uid = $user->getUID();
381
-		$jsonEncodedData = json_encode($data);
382
-		$query = $this->connection->getQueryBuilder();
383
-		$query->update($this->table)
384
-			->set('data', $query->createNamedParameter($jsonEncodedData))
385
-			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
386
-			->execute();
387
-
388
-		$this->deleteUserData($user);
389
-		$this->writeUserData($user, $data);
390
-	}
391
-
392
-	protected function writeUserData(IUser $user, array $data): void {
393
-		$query = $this->connection->getQueryBuilder();
394
-		$query->insert($this->dataTable)
395
-			->values(
396
-				[
397
-					'uid' => $query->createNamedParameter($user->getUID()),
398
-					'name' => $query->createParameter('name'),
399
-					'value' => $query->createParameter('value'),
400
-				]
401
-			);
402
-		foreach ($data as $propertyName => $property) {
403
-			if ($propertyName === self::PROPERTY_AVATAR) {
404
-				continue;
405
-			}
406
-
407
-			$query->setParameter('name', $propertyName)
408
-				->setParameter('value', $property['value']);
409
-			$query->execute();
410
-		}
411
-	}
412
-
413
-	/**
414
-	 * build default user record in case not data set exists yet
415
-	 *
416
-	 * @param IUser $user
417
-	 * @return array
418
-	 */
419
-	protected function buildDefaultUserRecord(IUser $user) {
420
-		return [
421
-			self::PROPERTY_DISPLAYNAME =>
422
-				[
423
-					'value' => $user->getDisplayName(),
424
-					'scope' => self::VISIBILITY_CONTACTS_ONLY,
425
-					'verified' => self::NOT_VERIFIED,
426
-				],
427
-			self::PROPERTY_ADDRESS =>
428
-				[
429
-					'value' => '',
430
-					'scope' => self::VISIBILITY_PRIVATE,
431
-					'verified' => self::NOT_VERIFIED,
432
-				],
433
-			self::PROPERTY_WEBSITE =>
434
-				[
435
-					'value' => '',
436
-					'scope' => self::VISIBILITY_PRIVATE,
437
-					'verified' => self::NOT_VERIFIED,
438
-				],
439
-			self::PROPERTY_EMAIL =>
440
-				[
441
-					'value' => $user->getEMailAddress(),
442
-					'scope' => self::VISIBILITY_CONTACTS_ONLY,
443
-					'verified' => self::NOT_VERIFIED,
444
-				],
445
-			self::PROPERTY_AVATAR =>
446
-				[
447
-					'scope' => self::VISIBILITY_CONTACTS_ONLY
448
-				],
449
-			self::PROPERTY_PHONE =>
450
-				[
451
-					'value' => '',
452
-					'scope' => self::VISIBILITY_PRIVATE,
453
-					'verified' => self::NOT_VERIFIED,
454
-				],
455
-			self::PROPERTY_TWITTER =>
456
-				[
457
-					'value' => '',
458
-					'scope' => self::VISIBILITY_PRIVATE,
459
-					'verified' => self::NOT_VERIFIED,
460
-				],
461
-		];
462
-	}
463
-
464
-	private function parseAccountData(IUser $user, $data): Account {
465
-		$account = new Account($user);
466
-		foreach ($data as $property => $accountData) {
467
-			$account->setProperty($property, $accountData['value'] ?? '', $accountData['scope'] ?? self::VISIBILITY_PRIVATE, $accountData['verified'] ?? self::NOT_VERIFIED);
468
-		}
469
-		return $account;
470
-	}
471
-
472
-	public function getAccount(IUser $user): IAccount {
473
-		return $this->parseAccountData($user, $this->getUser($user));
474
-	}
61
+    /** @var  IDBConnection database connection */
62
+    private $connection;
63
+
64
+    /** @var IConfig */
65
+    private $config;
66
+
67
+    /** @var string table name */
68
+    private $table = 'accounts';
69
+
70
+    /** @var string table name */
71
+    private $dataTable = 'accounts_data';
72
+
73
+    /** @var EventDispatcherInterface */
74
+    private $eventDispatcher;
75
+
76
+    /** @var IJobList */
77
+    private $jobList;
78
+
79
+    /** @var LoggerInterface */
80
+    private $logger;
81
+
82
+    public function __construct(IDBConnection $connection,
83
+                                IConfig $config,
84
+                                EventDispatcherInterface $eventDispatcher,
85
+                                IJobList $jobList,
86
+                                LoggerInterface $logger) {
87
+        $this->connection = $connection;
88
+        $this->config = $config;
89
+        $this->eventDispatcher = $eventDispatcher;
90
+        $this->jobList = $jobList;
91
+        $this->logger = $logger;
92
+    }
93
+
94
+    /**
95
+     * @param string $input
96
+     * @return string Provided phone number in E.164 format when it was a valid number
97
+     * @throws \InvalidArgumentException When the phone number was invalid or no default region is set and the number doesn't start with a country code
98
+     */
99
+    protected function parsePhoneNumber(string $input): string {
100
+        $defaultRegion = $this->config->getSystemValueString('default_phone_region', '');
101
+
102
+        if ($defaultRegion === '') {
103
+            // When no default region is set, only +49… numbers are valid
104
+            if (strpos($input, '+') !== 0) {
105
+                throw new \InvalidArgumentException(self::PROPERTY_PHONE);
106
+            }
107
+
108
+            $defaultRegion = 'EN';
109
+        }
110
+
111
+        $phoneUtil = PhoneNumberUtil::getInstance();
112
+        try {
113
+            $phoneNumber = $phoneUtil->parse($input, $defaultRegion);
114
+            if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
115
+                return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
116
+            }
117
+        } catch (NumberParseException $e) {
118
+        }
119
+
120
+        throw new \InvalidArgumentException(self::PROPERTY_PHONE);
121
+    }
122
+
123
+    /**
124
+     * update user record
125
+     *
126
+     * @param IUser $user
127
+     * @param array $data
128
+     * @param bool $throwOnData Set to true if you can inform the user about invalid data
129
+     * @return array The potentially modified data (e.g. phone numbers are converted to E.164 format)
130
+     * @throws \InvalidArgumentException Message is the property that was invalid
131
+     */
132
+    public function updateUser(IUser $user, array $data, bool $throwOnData = false): array {
133
+        $userData = $this->getUser($user);
134
+        $updated = true;
135
+
136
+        if (isset($data[self::PROPERTY_PHONE]) && $data[self::PROPERTY_PHONE]['value'] !== '') {
137
+            try {
138
+                $data[self::PROPERTY_PHONE]['value'] = $this->parsePhoneNumber($data[self::PROPERTY_PHONE]['value']);
139
+            } catch (\InvalidArgumentException $e) {
140
+                if ($throwOnData) {
141
+                    throw $e;
142
+                }
143
+                $data[self::PROPERTY_PHONE]['value'] = '';
144
+            }
145
+        }
146
+
147
+        if (empty($userData)) {
148
+            $this->insertNewUser($user, $data);
149
+        } elseif ($userData !== $data) {
150
+            $data = $this->checkEmailVerification($userData, $data, $user);
151
+            $data = $this->updateVerifyStatus($userData, $data);
152
+            $this->updateExistingUser($user, $data);
153
+        } else {
154
+            // nothing needs to be done if new and old data set are the same
155
+            $updated = false;
156
+        }
157
+
158
+        if ($updated) {
159
+            $this->eventDispatcher->dispatch(
160
+                'OC\AccountManager::userUpdated',
161
+                new GenericEvent($user, $data)
162
+            );
163
+        }
164
+
165
+        return $data;
166
+    }
167
+
168
+    /**
169
+     * delete user from accounts table
170
+     *
171
+     * @param IUser $user
172
+     */
173
+    public function deleteUser(IUser $user) {
174
+        $uid = $user->getUID();
175
+        $query = $this->connection->getQueryBuilder();
176
+        $query->delete($this->table)
177
+            ->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
178
+            ->execute();
179
+
180
+        $this->deleteUserData($user);
181
+    }
182
+
183
+    /**
184
+     * delete user from accounts table
185
+     *
186
+     * @param IUser $user
187
+     */
188
+    public function deleteUserData(IUser $user): void {
189
+        $uid = $user->getUID();
190
+        $query = $this->connection->getQueryBuilder();
191
+        $query->delete($this->dataTable)
192
+            ->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
193
+            ->execute();
194
+    }
195
+
196
+    /**
197
+     * get stored data from a given user
198
+     *
199
+     * @param IUser $user
200
+     * @return array
201
+     */
202
+    public function getUser(IUser $user) {
203
+        $uid = $user->getUID();
204
+        $query = $this->connection->getQueryBuilder();
205
+        $query->select('data')
206
+            ->from($this->table)
207
+            ->where($query->expr()->eq('uid', $query->createParameter('uid')))
208
+            ->setParameter('uid', $uid);
209
+        $result = $query->execute();
210
+        $accountData = $result->fetchAll();
211
+        $result->closeCursor();
212
+
213
+        if (empty($accountData)) {
214
+            $userData = $this->buildDefaultUserRecord($user);
215
+            $this->insertNewUser($user, $userData);
216
+            return $userData;
217
+        }
218
+
219
+        $userDataArray = json_decode($accountData[0]['data'], true);
220
+        $jsonError = json_last_error();
221
+        if ($userDataArray === null || $userDataArray === [] || $jsonError !== JSON_ERROR_NONE) {
222
+            $this->logger->critical("User data of $uid contained invalid JSON (error $jsonError), hence falling back to a default user record");
223
+            return $this->buildDefaultUserRecord($user);
224
+        }
225
+
226
+        $userDataArray = $this->addMissingDefaultValues($userDataArray);
227
+
228
+        return $userDataArray;
229
+    }
230
+
231
+    public function searchUsers(string $property, array $values): array {
232
+        $chunks = array_chunk($values, 500);
233
+        $query = $this->connection->getQueryBuilder();
234
+        $query->select('*')
235
+            ->from($this->dataTable)
236
+            ->where($query->expr()->eq('name', $query->createNamedParameter($property)))
237
+            ->andWhere($query->expr()->in('value', $query->createParameter('values')));
238
+
239
+        $matches = [];
240
+        foreach ($chunks as $chunk) {
241
+            $query->setParameter('values', $chunk, IQueryBuilder::PARAM_STR_ARRAY);
242
+            $result = $query->execute();
243
+
244
+            while ($row = $result->fetch()) {
245
+                $matches[$row['value']] = $row['uid'];
246
+            }
247
+            $result->closeCursor();
248
+        }
249
+
250
+        return $matches;
251
+    }
252
+
253
+    /**
254
+     * check if we need to ask the server for email verification, if yes we create a cronjob
255
+     *
256
+     * @param $oldData
257
+     * @param $newData
258
+     * @param IUser $user
259
+     * @return array
260
+     */
261
+    protected function checkEmailVerification($oldData, $newData, IUser $user) {
262
+        if ($oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']) {
263
+            $this->jobList->add(VerifyUserData::class,
264
+                [
265
+                    'verificationCode' => '',
266
+                    'data' => $newData[self::PROPERTY_EMAIL]['value'],
267
+                    'type' => self::PROPERTY_EMAIL,
268
+                    'uid' => $user->getUID(),
269
+                    'try' => 0,
270
+                    'lastRun' => time()
271
+                ]
272
+            );
273
+            $newData[self::PROPERTY_EMAIL]['verified'] = self::VERIFICATION_IN_PROGRESS;
274
+        }
275
+
276
+        return $newData;
277
+    }
278
+
279
+    /**
280
+     * make sure that all expected data are set
281
+     *
282
+     * @param array $userData
283
+     * @return array
284
+     */
285
+    protected function addMissingDefaultValues(array $userData) {
286
+        foreach ($userData as $key => $value) {
287
+            if (!isset($userData[$key]['verified'])) {
288
+                $userData[$key]['verified'] = self::NOT_VERIFIED;
289
+            }
290
+        }
291
+
292
+        return $userData;
293
+    }
294
+
295
+    /**
296
+     * reset verification status if personal data changed
297
+     *
298
+     * @param array $oldData
299
+     * @param array $newData
300
+     * @return array
301
+     */
302
+    protected function updateVerifyStatus($oldData, $newData) {
303
+
304
+        // which account was already verified successfully?
305
+        $twitterVerified = isset($oldData[self::PROPERTY_TWITTER]['verified']) && $oldData[self::PROPERTY_TWITTER]['verified'] === self::VERIFIED;
306
+        $websiteVerified = isset($oldData[self::PROPERTY_WEBSITE]['verified']) && $oldData[self::PROPERTY_WEBSITE]['verified'] === self::VERIFIED;
307
+        $emailVerified = isset($oldData[self::PROPERTY_EMAIL]['verified']) && $oldData[self::PROPERTY_EMAIL]['verified'] === self::VERIFIED;
308
+
309
+        // keep old verification status if we don't have a new one
310
+        if (!isset($newData[self::PROPERTY_TWITTER]['verified'])) {
311
+            // keep old verification status if value didn't changed and an old value exists
312
+            $keepOldStatus = $newData[self::PROPERTY_TWITTER]['value'] === $oldData[self::PROPERTY_TWITTER]['value'] && isset($oldData[self::PROPERTY_TWITTER]['verified']);
313
+            $newData[self::PROPERTY_TWITTER]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_TWITTER]['verified'] : self::NOT_VERIFIED;
314
+        }
315
+
316
+        if (!isset($newData[self::PROPERTY_WEBSITE]['verified'])) {
317
+            // keep old verification status if value didn't changed and an old value exists
318
+            $keepOldStatus = $newData[self::PROPERTY_WEBSITE]['value'] === $oldData[self::PROPERTY_WEBSITE]['value'] && isset($oldData[self::PROPERTY_WEBSITE]['verified']);
319
+            $newData[self::PROPERTY_WEBSITE]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_WEBSITE]['verified'] : self::NOT_VERIFIED;
320
+        }
321
+
322
+        if (!isset($newData[self::PROPERTY_EMAIL]['verified'])) {
323
+            // keep old verification status if value didn't changed and an old value exists
324
+            $keepOldStatus = $newData[self::PROPERTY_EMAIL]['value'] === $oldData[self::PROPERTY_EMAIL]['value'] && isset($oldData[self::PROPERTY_EMAIL]['verified']);
325
+            $newData[self::PROPERTY_EMAIL]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_EMAIL]['verified'] : self::VERIFICATION_IN_PROGRESS;
326
+        }
327
+
328
+        // reset verification status if a value from a previously verified data was changed
329
+        if ($twitterVerified &&
330
+            $oldData[self::PROPERTY_TWITTER]['value'] !== $newData[self::PROPERTY_TWITTER]['value']
331
+        ) {
332
+            $newData[self::PROPERTY_TWITTER]['verified'] = self::NOT_VERIFIED;
333
+        }
334
+
335
+        if ($websiteVerified &&
336
+            $oldData[self::PROPERTY_WEBSITE]['value'] !== $newData[self::PROPERTY_WEBSITE]['value']
337
+        ) {
338
+            $newData[self::PROPERTY_WEBSITE]['verified'] = self::NOT_VERIFIED;
339
+        }
340
+
341
+        if ($emailVerified &&
342
+            $oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']
343
+        ) {
344
+            $newData[self::PROPERTY_EMAIL]['verified'] = self::NOT_VERIFIED;
345
+        }
346
+
347
+        return $newData;
348
+    }
349
+
350
+    /**
351
+     * add new user to accounts table
352
+     *
353
+     * @param IUser $user
354
+     * @param array $data
355
+     */
356
+    protected function insertNewUser(IUser $user, array $data): void {
357
+        $uid = $user->getUID();
358
+        $jsonEncodedData = json_encode($data);
359
+        $query = $this->connection->getQueryBuilder();
360
+        $query->insert($this->table)
361
+            ->values(
362
+                [
363
+                    'uid' => $query->createNamedParameter($uid),
364
+                    'data' => $query->createNamedParameter($jsonEncodedData),
365
+                ]
366
+            )
367
+            ->execute();
368
+
369
+        $this->deleteUserData($user);
370
+        $this->writeUserData($user, $data);
371
+    }
372
+
373
+    /**
374
+     * update existing user in accounts table
375
+     *
376
+     * @param IUser $user
377
+     * @param array $data
378
+     */
379
+    protected function updateExistingUser(IUser $user, array $data): void {
380
+        $uid = $user->getUID();
381
+        $jsonEncodedData = json_encode($data);
382
+        $query = $this->connection->getQueryBuilder();
383
+        $query->update($this->table)
384
+            ->set('data', $query->createNamedParameter($jsonEncodedData))
385
+            ->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
386
+            ->execute();
387
+
388
+        $this->deleteUserData($user);
389
+        $this->writeUserData($user, $data);
390
+    }
391
+
392
+    protected function writeUserData(IUser $user, array $data): void {
393
+        $query = $this->connection->getQueryBuilder();
394
+        $query->insert($this->dataTable)
395
+            ->values(
396
+                [
397
+                    'uid' => $query->createNamedParameter($user->getUID()),
398
+                    'name' => $query->createParameter('name'),
399
+                    'value' => $query->createParameter('value'),
400
+                ]
401
+            );
402
+        foreach ($data as $propertyName => $property) {
403
+            if ($propertyName === self::PROPERTY_AVATAR) {
404
+                continue;
405
+            }
406
+
407
+            $query->setParameter('name', $propertyName)
408
+                ->setParameter('value', $property['value']);
409
+            $query->execute();
410
+        }
411
+    }
412
+
413
+    /**
414
+     * build default user record in case not data set exists yet
415
+     *
416
+     * @param IUser $user
417
+     * @return array
418
+     */
419
+    protected function buildDefaultUserRecord(IUser $user) {
420
+        return [
421
+            self::PROPERTY_DISPLAYNAME =>
422
+                [
423
+                    'value' => $user->getDisplayName(),
424
+                    'scope' => self::VISIBILITY_CONTACTS_ONLY,
425
+                    'verified' => self::NOT_VERIFIED,
426
+                ],
427
+            self::PROPERTY_ADDRESS =>
428
+                [
429
+                    'value' => '',
430
+                    'scope' => self::VISIBILITY_PRIVATE,
431
+                    'verified' => self::NOT_VERIFIED,
432
+                ],
433
+            self::PROPERTY_WEBSITE =>
434
+                [
435
+                    'value' => '',
436
+                    'scope' => self::VISIBILITY_PRIVATE,
437
+                    'verified' => self::NOT_VERIFIED,
438
+                ],
439
+            self::PROPERTY_EMAIL =>
440
+                [
441
+                    'value' => $user->getEMailAddress(),
442
+                    'scope' => self::VISIBILITY_CONTACTS_ONLY,
443
+                    'verified' => self::NOT_VERIFIED,
444
+                ],
445
+            self::PROPERTY_AVATAR =>
446
+                [
447
+                    'scope' => self::VISIBILITY_CONTACTS_ONLY
448
+                ],
449
+            self::PROPERTY_PHONE =>
450
+                [
451
+                    'value' => '',
452
+                    'scope' => self::VISIBILITY_PRIVATE,
453
+                    'verified' => self::NOT_VERIFIED,
454
+                ],
455
+            self::PROPERTY_TWITTER =>
456
+                [
457
+                    'value' => '',
458
+                    'scope' => self::VISIBILITY_PRIVATE,
459
+                    'verified' => self::NOT_VERIFIED,
460
+                ],
461
+        ];
462
+    }
463
+
464
+    private function parseAccountData(IUser $user, $data): Account {
465
+        $account = new Account($user);
466
+        foreach ($data as $property => $accountData) {
467
+            $account->setProperty($property, $accountData['value'] ?? '', $accountData['scope'] ?? self::VISIBILITY_PRIVATE, $accountData['verified'] ?? self::NOT_VERIFIED);
468
+        }
469
+        return $account;
470
+    }
471
+
472
+    public function getAccount(IUser $user): IAccount {
473
+        return $this->parseAccountData($user, $this->getUser($user));
474
+    }
475 475
 }
Please login to merge, or discard this patch.