Passed
Push — master ( b3cfa1...662ab9 )
by Blizzz
14:36 queued 11s
created
lib/public/Accounts/IAccountManager.php 1 patch
Indentation   +97 added lines, -97 removed lines patch added patch discarded remove patch
@@ -38,101 +38,101 @@
 block discarded – undo
38 38
  */
39 39
 interface IAccountManager {
40 40
 
41
-	/**
42
-	 * Contact details visible locally only
43
-	 *
44
-	 * @since 21.0.1
45
-	 */
46
-	public const SCOPE_PRIVATE = 'v2-private';
47
-
48
-	/**
49
-	 * Contact details visible locally and through public link access on local instance
50
-	 *
51
-	 * @since 21.0.1
52
-	 */
53
-	public const SCOPE_LOCAL = 'v2-local';
54
-
55
-	/**
56
-	 * Contact details visible locally, through public link access and on trusted federated servers.
57
-	 *
58
-	 * @since 21.0.1
59
-	 */
60
-	public const SCOPE_FEDERATED = 'v2-federated';
61
-
62
-	/**
63
-	 * Contact details visible locally, through public link access, on trusted federated servers
64
-	 * and published to the public lookup server.
65
-	 *
66
-	 * @since 21.0.1
67
-	 */
68
-	public const SCOPE_PUBLISHED = 'v2-published';
69
-
70
-	/**
71
-	 * Contact details only visible locally
72
-	 *
73
-	 * @deprecated 21.0.1
74
-	 */
75
-	public const VISIBILITY_PRIVATE = 'private';
76
-
77
-	/**
78
-	 * Contact details visible on trusted federated servers.
79
-	 *
80
-	 * @deprecated 21.0.1
81
-	 */
82
-	public const VISIBILITY_CONTACTS_ONLY = 'contacts';
83
-
84
-	/**
85
-	 * Contact details visible on trusted federated servers and in the public lookup server.
86
-	 *
87
-	 * @deprecated 21.0.1
88
-	 */
89
-	public const VISIBILITY_PUBLIC = 'public';
90
-
91
-	public const PROPERTY_AVATAR = 'avatar';
92
-	public const PROPERTY_DISPLAYNAME = 'displayname';
93
-	public const PROPERTY_PHONE = 'phone';
94
-	public const PROPERTY_EMAIL = 'email';
95
-	public const PROPERTY_WEBSITE = 'website';
96
-	public const PROPERTY_ADDRESS = 'address';
97
-	public const PROPERTY_TWITTER = 'twitter';
98
-
99
-	public const COLLECTION_EMAIL = 'additional_mail';
100
-
101
-	public const NOT_VERIFIED = '0';
102
-	public const VERIFICATION_IN_PROGRESS = '1';
103
-	public const VERIFIED = '2';
104
-
105
-	/**
106
-	 * Get the account data for a given user
107
-	 *
108
-	 * @since 15.0.0
109
-	 *
110
-	 * @param IUser $user
111
-	 * @return IAccount
112
-	 */
113
-	public function getAccount(IUser $user): IAccount;
114
-
115
-	/**
116
-	 * Update the account data with for the user
117
-	 *
118
-	 * @since 21.0.1
119
-	 *
120
-	 * @param IAccount $account
121
-	 * @throws \InvalidArgumentException Message is the property that was invalid
122
-	 */
123
-	public function updateAccount(IAccount $account): void;
124
-
125
-	/**
126
-	 * Search for users based on account data
127
-	 *
128
-	 * @param string $property - property or property collection name – since
129
-	 * NC 22 the implementation MAY add a fitting property collection into the
130
-	 * search even if a property name was given e.g. email property and email
131
-	 * collection)
132
-	 * @param string[] $values
133
-	 * @return array
134
-	 *
135
-	 * @since 21.0.0
136
-	 */
137
-	public function searchUsers(string $property, array $values): array;
41
+    /**
42
+     * Contact details visible locally only
43
+     *
44
+     * @since 21.0.1
45
+     */
46
+    public const SCOPE_PRIVATE = 'v2-private';
47
+
48
+    /**
49
+     * Contact details visible locally and through public link access on local instance
50
+     *
51
+     * @since 21.0.1
52
+     */
53
+    public const SCOPE_LOCAL = 'v2-local';
54
+
55
+    /**
56
+     * Contact details visible locally, through public link access and on trusted federated servers.
57
+     *
58
+     * @since 21.0.1
59
+     */
60
+    public const SCOPE_FEDERATED = 'v2-federated';
61
+
62
+    /**
63
+     * Contact details visible locally, through public link access, on trusted federated servers
64
+     * and published to the public lookup server.
65
+     *
66
+     * @since 21.0.1
67
+     */
68
+    public const SCOPE_PUBLISHED = 'v2-published';
69
+
70
+    /**
71
+     * Contact details only visible locally
72
+     *
73
+     * @deprecated 21.0.1
74
+     */
75
+    public const VISIBILITY_PRIVATE = 'private';
76
+
77
+    /**
78
+     * Contact details visible on trusted federated servers.
79
+     *
80
+     * @deprecated 21.0.1
81
+     */
82
+    public const VISIBILITY_CONTACTS_ONLY = 'contacts';
83
+
84
+    /**
85
+     * Contact details visible on trusted federated servers and in the public lookup server.
86
+     *
87
+     * @deprecated 21.0.1
88
+     */
89
+    public const VISIBILITY_PUBLIC = 'public';
90
+
91
+    public const PROPERTY_AVATAR = 'avatar';
92
+    public const PROPERTY_DISPLAYNAME = 'displayname';
93
+    public const PROPERTY_PHONE = 'phone';
94
+    public const PROPERTY_EMAIL = 'email';
95
+    public const PROPERTY_WEBSITE = 'website';
96
+    public const PROPERTY_ADDRESS = 'address';
97
+    public const PROPERTY_TWITTER = 'twitter';
98
+
99
+    public const COLLECTION_EMAIL = 'additional_mail';
100
+
101
+    public const NOT_VERIFIED = '0';
102
+    public const VERIFICATION_IN_PROGRESS = '1';
103
+    public const VERIFIED = '2';
104
+
105
+    /**
106
+     * Get the account data for a given user
107
+     *
108
+     * @since 15.0.0
109
+     *
110
+     * @param IUser $user
111
+     * @return IAccount
112
+     */
113
+    public function getAccount(IUser $user): IAccount;
114
+
115
+    /**
116
+     * Update the account data with for the user
117
+     *
118
+     * @since 21.0.1
119
+     *
120
+     * @param IAccount $account
121
+     * @throws \InvalidArgumentException Message is the property that was invalid
122
+     */
123
+    public function updateAccount(IAccount $account): void;
124
+
125
+    /**
126
+     * Search for users based on account data
127
+     *
128
+     * @param string $property - property or property collection name – since
129
+     * NC 22 the implementation MAY add a fitting property collection into the
130
+     * search even if a property name was given e.g. email property and email
131
+     * collection)
132
+     * @param string[] $values
133
+     * @return array
134
+     *
135
+     * @since 21.0.0
136
+     */
137
+    public function searchUsers(string $property, array $values): array;
138 138
 }
Please login to merge, or discard this patch.
lib/public/Accounts/IAccount.php 1 patch
Indentation   +78 added lines, -78 removed lines patch added patch discarded remove patch
@@ -36,89 +36,89 @@
 block discarded – undo
36 36
  */
37 37
 interface IAccount extends \JsonSerializable {
38 38
 
39
-	/**
40
-	 * Set a property with data
41
-	 *
42
-	 * @since 15.0.0
43
-	 *
44
-	 * @param string $property  Must be one of the PROPERTY_ prefixed constants of \OCP\Accounts\IAccountManager
45
-	 * @param string $value
46
-	 * @param string $scope Must be one of the VISIBILITY_ prefixed constants of \OCP\Accounts\IAccountManager
47
-	 * @param string $verified \OCP\Accounts\IAccountManager::NOT_VERIFIED | \OCP\Accounts\IAccountManager::VERIFICATION_IN_PROGRESS | \OCP\Accounts\IAccountManager::VERIFIED
48
-	 * @param string $verificationData Optional, defaults to empty string. Since @22.0.0.
49
-	 * @return IAccount
50
-	 */
51
-	public function setProperty(string $property, string $value, string $scope, string $verified, string $verificationData = ''): IAccount;
39
+    /**
40
+     * Set a property with data
41
+     *
42
+     * @since 15.0.0
43
+     *
44
+     * @param string $property  Must be one of the PROPERTY_ prefixed constants of \OCP\Accounts\IAccountManager
45
+     * @param string $value
46
+     * @param string $scope Must be one of the VISIBILITY_ prefixed constants of \OCP\Accounts\IAccountManager
47
+     * @param string $verified \OCP\Accounts\IAccountManager::NOT_VERIFIED | \OCP\Accounts\IAccountManager::VERIFICATION_IN_PROGRESS | \OCP\Accounts\IAccountManager::VERIFIED
48
+     * @param string $verificationData Optional, defaults to empty string. Since @22.0.0.
49
+     * @return IAccount
50
+     */
51
+    public function setProperty(string $property, string $value, string $scope, string $verified, string $verificationData = ''): IAccount;
52 52
 
53
-	/**
54
-	 * Get a property by its key
55
-	 *
56
-	 * @since 15.0.0
57
-	 *
58
-	 * @param string $property Must be one of the PROPERTY_ prefixed constants of \OCP\Accounts\IAccountManager
59
-	 * @return IAccountProperty
60
-	 * @throws PropertyDoesNotExistException
61
-	 */
62
-	public function getProperty(string $property): IAccountProperty;
53
+    /**
54
+     * Get a property by its key
55
+     *
56
+     * @since 15.0.0
57
+     *
58
+     * @param string $property Must be one of the PROPERTY_ prefixed constants of \OCP\Accounts\IAccountManager
59
+     * @return IAccountProperty
60
+     * @throws PropertyDoesNotExistException
61
+     */
62
+    public function getProperty(string $property): IAccountProperty;
63 63
 
64
-	/**
65
-	 * Get all properties of an account. Array indices are property names.
66
-	 * Values from IAccountPropertyCollections are not included in the return
67
-	 * array.
68
-	 *
69
-	 * @since 15.0.0
70
-	 * @deprecated 22.0.0 use getAllProperties()
71
-	 */
72
-	public function getProperties(): array;
64
+    /**
65
+     * Get all properties of an account. Array indices are property names.
66
+     * Values from IAccountPropertyCollections are not included in the return
67
+     * array.
68
+     *
69
+     * @since 15.0.0
70
+     * @deprecated 22.0.0 use getAllProperties()
71
+     */
72
+    public function getProperties(): array;
73 73
 
74
-	/**
75
-	 * Get all properties of an account. Array indices are numeric. To get
76
-	 * the property name, call getName() against the value.
77
-	 *
78
-	 * IAccountPropertyCollections are being flattened into an IAccountProperty
79
-	 * for each value.
80
-	 *
81
-	 * @since 22.0.0
82
-	 *
83
-	 * @return Generator<int, IAccountProperty>
84
-	 */
85
-	public function getAllProperties(): Generator;
74
+    /**
75
+     * Get all properties of an account. Array indices are numeric. To get
76
+     * the property name, call getName() against the value.
77
+     *
78
+     * IAccountPropertyCollections are being flattened into an IAccountProperty
79
+     * for each value.
80
+     *
81
+     * @since 22.0.0
82
+     *
83
+     * @return Generator<int, IAccountProperty>
84
+     */
85
+    public function getAllProperties(): Generator;
86 86
 
87
-	/**
88
-	 * Set a property collection (multi-value properties)
89
-	 *
90
-	 * @since 22.0.0
91
-	 */
92
-	public function setPropertyCollection(IAccountPropertyCollection $propertyCollection): IAccount;
87
+    /**
88
+     * Set a property collection (multi-value properties)
89
+     *
90
+     * @since 22.0.0
91
+     */
92
+    public function setPropertyCollection(IAccountPropertyCollection $propertyCollection): IAccount;
93 93
 
94
-	/**
95
-	 * Returns the requestes propery collection (multi-value properties)
96
-	 *
97
-	 * @since 22.0.0
98
-	 */
99
-	public function getPropertyCollection(string $propertyCollection): IAccountPropertyCollection;
94
+    /**
95
+     * Returns the requestes propery collection (multi-value properties)
96
+     *
97
+     * @since 22.0.0
98
+     */
99
+    public function getPropertyCollection(string $propertyCollection): IAccountPropertyCollection;
100 100
 
101
-	/**
102
-	 * Get all properties that match the provided filters for scope and verification status
103
-	 *
104
-	 * Since 22.0.0 values from IAccountPropertyCollection are included, but also
105
-	 * as IAccountProperty instances. They for properties of IAccountPropertyCollection are
106
-	 * suffixed incrementally, i.e. #0, #1 ... #n – the numbers have no further meaning.
107
-	 *
108
-	 * @since 15.0.0
109
-	 *
110
-	 * @param string $scope Must be one of the VISIBILITY_ prefixed constants of \OCP\Accounts\IAccountManager
111
-	 * @param string $verified \OCP\Accounts\IAccountManager::NOT_VERIFIED | \OCP\Accounts\IAccountManager::VERIFICATION_IN_PROGRESS | \OCP\Accounts\IAccountManager::VERIFIED
112
-	 * @return IAccountProperty[]
113
-	 */
114
-	public function getFilteredProperties(string $scope = null, string $verified = null): array;
101
+    /**
102
+     * Get all properties that match the provided filters for scope and verification status
103
+     *
104
+     * Since 22.0.0 values from IAccountPropertyCollection are included, but also
105
+     * as IAccountProperty instances. They for properties of IAccountPropertyCollection are
106
+     * suffixed incrementally, i.e. #0, #1 ... #n – the numbers have no further meaning.
107
+     *
108
+     * @since 15.0.0
109
+     *
110
+     * @param string $scope Must be one of the VISIBILITY_ prefixed constants of \OCP\Accounts\IAccountManager
111
+     * @param string $verified \OCP\Accounts\IAccountManager::NOT_VERIFIED | \OCP\Accounts\IAccountManager::VERIFICATION_IN_PROGRESS | \OCP\Accounts\IAccountManager::VERIFIED
112
+     * @return IAccountProperty[]
113
+     */
114
+    public function getFilteredProperties(string $scope = null, string $verified = null): array;
115 115
 
116
-	/**
117
-	 * Get the related user for the account data
118
-	 *
119
-	 * @since 15.0.0
120
-	 *
121
-	 * @return IUser
122
-	 */
123
-	public function getUser(): IUser;
116
+    /**
117
+     * Get the related user for the account data
118
+     *
119
+     * @since 15.0.0
120
+     *
121
+     * @return IUser
122
+     */
123
+    public function getUser(): IUser;
124 124
 }
Please login to merge, or discard this patch.
lib/public/Accounts/IAccountPropertyCollection.php 1 patch
Indentation   +38 added lines, -38 removed lines patch added patch discarded remove patch
@@ -38,47 +38,47 @@
 block discarded – undo
38 38
  */
39 39
 interface IAccountPropertyCollection extends JsonSerializable {
40 40
 
41
-	/**
42
-	 * retuns the collection name
43
-	 *
44
-	 * @since 22.0.0
45
-	 */
46
-	public function getName(): string;
41
+    /**
42
+     * retuns the collection name
43
+     *
44
+     * @since 22.0.0
45
+     */
46
+    public function getName(): string;
47 47
 
48
-	/**
49
-	 * set properties of this collection
50
-	 *
51
-	 * @param IAccountProperty[] $properties
52
-	 * @throws InvalidArgumentException
53
-	 * @since 22.0.0
54
-	 */
55
-	public function setProperties(array $properties): IAccountPropertyCollection;
48
+    /**
49
+     * set properties of this collection
50
+     *
51
+     * @param IAccountProperty[] $properties
52
+     * @throws InvalidArgumentException
53
+     * @since 22.0.0
54
+     */
55
+    public function setProperties(array $properties): IAccountPropertyCollection;
56 56
 
57
-	/**
58
-	 * @return IAccountProperty[]
59
-	 * @since 22.0.0
60
-	 */
61
-	public function getProperties(): array;
57
+    /**
58
+     * @return IAccountProperty[]
59
+     * @since 22.0.0
60
+     */
61
+    public function getProperties(): array;
62 62
 
63
-	/**
64
-	 * adds a property to this collection
65
-	 *
66
-	 * @throws InvalidArgumentException
67
-	 * @since 22.0.0
68
-	 */
69
-	public function addProperty(IAccountProperty $property): IAccountPropertyCollection;
63
+    /**
64
+     * adds a property to this collection
65
+     *
66
+     * @throws InvalidArgumentException
67
+     * @since 22.0.0
68
+     */
69
+    public function addProperty(IAccountProperty $property): IAccountPropertyCollection;
70 70
 
71
-	/**
72
-	 * removes a property of this collection
73
-	 *
74
-	 * @since 22.0.0
75
-	 */
76
-	public function removeProperty(IAccountProperty $property): IAccountPropertyCollection;
71
+    /**
72
+     * removes a property of this collection
73
+     *
74
+     * @since 22.0.0
75
+     */
76
+    public function removeProperty(IAccountProperty $property): IAccountPropertyCollection;
77 77
 
78
-	/**
79
-	 * removes a property identified by its value
80
-	 *
81
-	 * @since 22.0.0
82
-	 */
83
-	public function removePropertyByValue(string $value): IAccountPropertyCollection;
78
+    /**
79
+     * removes a property identified by its value
80
+     *
81
+     * @since 22.0.0
82
+     */
83
+    public function removePropertyByValue(string $value): IAccountPropertyCollection;
84 84
 }
Please login to merge, or discard this patch.
lib/private/Accounts/AccountPropertyCollection.php 1 patch
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -32,59 +32,59 @@
 block discarded – undo
32 32
 
33 33
 class AccountPropertyCollection implements IAccountPropertyCollection {
34 34
 
35
-	/** @var string */
36
-	protected $collectionName = '';
35
+    /** @var string */
36
+    protected $collectionName = '';
37 37
 
38
-	/** @var IAccountProperty[] */
39
-	protected $properties = [];
38
+    /** @var IAccountProperty[] */
39
+    protected $properties = [];
40 40
 
41
-	public function __construct(string $collectionName) {
42
-		$this->collectionName = $collectionName;
43
-	}
41
+    public function __construct(string $collectionName) {
42
+        $this->collectionName = $collectionName;
43
+    }
44 44
 
45
-	public function setProperties(array $properties): IAccountPropertyCollection {
46
-		/** @var IAccountProperty $property */
47
-		$this->properties = [];
48
-		foreach ($properties as $property) {
49
-			$this->addProperty($property);
50
-		}
51
-		return $this;
52
-	}
45
+    public function setProperties(array $properties): IAccountPropertyCollection {
46
+        /** @var IAccountProperty $property */
47
+        $this->properties = [];
48
+        foreach ($properties as $property) {
49
+            $this->addProperty($property);
50
+        }
51
+        return $this;
52
+    }
53 53
 
54
-	public function getProperties(): array {
55
-		return $this->properties;
56
-	}
54
+    public function getProperties(): array {
55
+        return $this->properties;
56
+    }
57 57
 
58
-	public function addProperty(IAccountProperty $property): IAccountPropertyCollection {
59
-		if ($property->getName() !== $this->collectionName) {
60
-			throw new InvalidArgumentException('Provided property does not match collection name');
61
-		}
62
-		$this->properties[] = $property;
63
-		return $this;
64
-	}
58
+    public function addProperty(IAccountProperty $property): IAccountPropertyCollection {
59
+        if ($property->getName() !== $this->collectionName) {
60
+            throw new InvalidArgumentException('Provided property does not match collection name');
61
+        }
62
+        $this->properties[] = $property;
63
+        return $this;
64
+    }
65 65
 
66
-	public function removeProperty(IAccountProperty $property): IAccountPropertyCollection {
67
-		$ref = array_search($property, $this->properties, true);
68
-		if ($ref !== false) {
69
-			unset($this->properties[$ref]);
70
-		}
71
-		return $this;
72
-	}
66
+    public function removeProperty(IAccountProperty $property): IAccountPropertyCollection {
67
+        $ref = array_search($property, $this->properties, true);
68
+        if ($ref !== false) {
69
+            unset($this->properties[$ref]);
70
+        }
71
+        return $this;
72
+    }
73 73
 
74
-	public function removePropertyByValue(string $value): IAccountPropertyCollection {
75
-		foreach ($this->properties as $i => $property) {
76
-			if ($property->getValue() === $value) {
77
-				unset($this->properties[$i]);
78
-			}
79
-		}
80
-		return $this;
81
-	}
74
+    public function removePropertyByValue(string $value): IAccountPropertyCollection {
75
+        foreach ($this->properties as $i => $property) {
76
+            if ($property->getValue() === $value) {
77
+                unset($this->properties[$i]);
78
+            }
79
+        }
80
+        return $this;
81
+    }
82 82
 
83
-	public function jsonSerialize() {
84
-		return [$this->collectionName => $this->properties];
85
-	}
83
+    public function jsonSerialize() {
84
+        return [$this->collectionName => $this->properties];
85
+    }
86 86
 
87
-	public function getName(): string {
88
-		return $this->collectionName;
89
-	}
87
+    public function getName(): string {
88
+        return $this->collectionName;
89
+    }
90 90
 }
Please login to merge, or discard this patch.
lib/private/Accounts/Account.php 1 patch
Indentation   +90 added lines, -90 removed lines patch added patch discarded remove patch
@@ -35,94 +35,94 @@
 block discarded – undo
35 35
 use OCP\IUser;
36 36
 
37 37
 class Account implements IAccount {
38
-	use TAccountsHelper;
39
-
40
-	/** @var IAccountPropertyCollection[]|IAccountProperty[] */
41
-	private $properties = [];
42
-
43
-	/** @var IUser */
44
-	private $user;
45
-
46
-	public function __construct(IUser $user) {
47
-		$this->user = $user;
48
-	}
49
-
50
-	public function setProperty(string $property, string $value, string $scope, string $verified, string $verificationData = ''): IAccount {
51
-		if ($this->isCollection($property)) {
52
-			throw new \InvalidArgumentException('setProperty cannot set an IAccountsPropertyCollection');
53
-		}
54
-		$this->properties[$property] = new AccountProperty($property, $value, $scope, $verified, $verificationData);
55
-		return $this;
56
-	}
57
-
58
-	public function getProperty(string $property): IAccountProperty {
59
-		if ($this->isCollection($property)) {
60
-			throw new \InvalidArgumentException('getProperty cannot retrieve an IAccountsPropertyCollection');
61
-		}
62
-		if (!array_key_exists($property, $this->properties) || !$this->properties[$property] instanceof IAccountProperty) {
63
-			throw new PropertyDoesNotExistException($property);
64
-		}
65
-		return $this->properties[$property];
66
-	}
67
-
68
-	public function getProperties(): array {
69
-		return array_filter($this->properties, function ($obj) {
70
-			return $obj instanceof IAccountProperty;
71
-		});
72
-	}
73
-
74
-	public function getAllProperties(): Generator {
75
-		foreach ($this->properties as $propertyObject) {
76
-			if ($propertyObject instanceof IAccountProperty) {
77
-				yield $propertyObject;
78
-			} elseif ($propertyObject instanceof IAccountPropertyCollection) {
79
-				foreach ($propertyObject->getProperties() as $property) {
80
-					yield $property;
81
-				}
82
-			}
83
-		}
84
-	}
85
-
86
-	public function getFilteredProperties(string $scope = null, string $verified = null): array {
87
-		$result = $incrementals = [];
88
-		/** @var IAccountProperty $obj */
89
-		foreach ($this->getAllProperties() as $obj) {
90
-			if ($scope !== null && $scope !== $obj->getScope()) {
91
-				continue;
92
-			}
93
-			if ($verified !== null && $verified !== $obj->getVerified()) {
94
-				continue;
95
-			}
96
-			$index = $obj->getName();
97
-			if ($this->isCollection($index)) {
98
-				$incrementals[$index] = ($incrementals[$index] ?? -1) + 1;
99
-				$index .= '#' . $incrementals[$index];
100
-			}
101
-			$result[$index] = $obj;
102
-		}
103
-		return $result;
104
-	}
105
-
106
-	public function jsonSerialize() {
107
-		return $this->properties;
108
-	}
109
-
110
-	public function getUser(): IUser {
111
-		return $this->user;
112
-	}
113
-
114
-	public function setPropertyCollection(IAccountPropertyCollection $propertyCollection): IAccount {
115
-		$this->properties[$propertyCollection->getName()] = $propertyCollection;
116
-		return $this;
117
-	}
118
-
119
-	public function getPropertyCollection(string $propertyCollection): IAccountPropertyCollection {
120
-		if (!array_key_exists($propertyCollection, $this->properties)) {
121
-			throw new PropertyDoesNotExistException($propertyCollection);
122
-		}
123
-		if (!$this->properties[$propertyCollection] instanceof IAccountPropertyCollection) {
124
-			throw new \RuntimeException('Requested collection is not an IAccountPropertyCollection');
125
-		}
126
-		return $this->properties[$propertyCollection];
127
-	}
38
+    use TAccountsHelper;
39
+
40
+    /** @var IAccountPropertyCollection[]|IAccountProperty[] */
41
+    private $properties = [];
42
+
43
+    /** @var IUser */
44
+    private $user;
45
+
46
+    public function __construct(IUser $user) {
47
+        $this->user = $user;
48
+    }
49
+
50
+    public function setProperty(string $property, string $value, string $scope, string $verified, string $verificationData = ''): IAccount {
51
+        if ($this->isCollection($property)) {
52
+            throw new \InvalidArgumentException('setProperty cannot set an IAccountsPropertyCollection');
53
+        }
54
+        $this->properties[$property] = new AccountProperty($property, $value, $scope, $verified, $verificationData);
55
+        return $this;
56
+    }
57
+
58
+    public function getProperty(string $property): IAccountProperty {
59
+        if ($this->isCollection($property)) {
60
+            throw new \InvalidArgumentException('getProperty cannot retrieve an IAccountsPropertyCollection');
61
+        }
62
+        if (!array_key_exists($property, $this->properties) || !$this->properties[$property] instanceof IAccountProperty) {
63
+            throw new PropertyDoesNotExistException($property);
64
+        }
65
+        return $this->properties[$property];
66
+    }
67
+
68
+    public function getProperties(): array {
69
+        return array_filter($this->properties, function ($obj) {
70
+            return $obj instanceof IAccountProperty;
71
+        });
72
+    }
73
+
74
+    public function getAllProperties(): Generator {
75
+        foreach ($this->properties as $propertyObject) {
76
+            if ($propertyObject instanceof IAccountProperty) {
77
+                yield $propertyObject;
78
+            } elseif ($propertyObject instanceof IAccountPropertyCollection) {
79
+                foreach ($propertyObject->getProperties() as $property) {
80
+                    yield $property;
81
+                }
82
+            }
83
+        }
84
+    }
85
+
86
+    public function getFilteredProperties(string $scope = null, string $verified = null): array {
87
+        $result = $incrementals = [];
88
+        /** @var IAccountProperty $obj */
89
+        foreach ($this->getAllProperties() as $obj) {
90
+            if ($scope !== null && $scope !== $obj->getScope()) {
91
+                continue;
92
+            }
93
+            if ($verified !== null && $verified !== $obj->getVerified()) {
94
+                continue;
95
+            }
96
+            $index = $obj->getName();
97
+            if ($this->isCollection($index)) {
98
+                $incrementals[$index] = ($incrementals[$index] ?? -1) + 1;
99
+                $index .= '#' . $incrementals[$index];
100
+            }
101
+            $result[$index] = $obj;
102
+        }
103
+        return $result;
104
+    }
105
+
106
+    public function jsonSerialize() {
107
+        return $this->properties;
108
+    }
109
+
110
+    public function getUser(): IUser {
111
+        return $this->user;
112
+    }
113
+
114
+    public function setPropertyCollection(IAccountPropertyCollection $propertyCollection): IAccount {
115
+        $this->properties[$propertyCollection->getName()] = $propertyCollection;
116
+        return $this;
117
+    }
118
+
119
+    public function getPropertyCollection(string $propertyCollection): IAccountPropertyCollection {
120
+        if (!array_key_exists($propertyCollection, $this->properties)) {
121
+            throw new PropertyDoesNotExistException($propertyCollection);
122
+        }
123
+        if (!$this->properties[$propertyCollection] instanceof IAccountPropertyCollection) {
124
+            throw new \RuntimeException('Requested collection is not an IAccountPropertyCollection');
125
+        }
126
+        return $this->properties[$propertyCollection];
127
+    }
128 128
 }
Please login to merge, or discard this patch.
lib/private/Accounts/TAccountsHelper.php 1 patch
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -29,12 +29,12 @@
 block discarded – undo
29 29
 use OCP\Accounts\IAccountManager;
30 30
 
31 31
 trait TAccountsHelper {
32
-	protected function isCollection(string $propertyName): bool {
33
-		return in_array($propertyName,
34
-			[
35
-				IAccountManager::COLLECTION_EMAIL,
36
-			],
37
-			true
38
-		);
39
-	}
32
+    protected function isCollection(string $propertyName): bool {
33
+        return in_array($propertyName,
34
+            [
35
+                IAccountManager::COLLECTION_EMAIL,
36
+            ],
37
+            true
38
+        );
39
+    }
40 40
 }
Please login to merge, or discard this patch.
lib/private/Accounts/AccountManager.php 1 patch
Indentation   +550 added lines, -550 removed lines patch added patch discarded remove patch
@@ -60,554 +60,554 @@
 block discarded – undo
60 60
  * @package OC\Accounts
61 61
  */
62 62
 class AccountManager implements IAccountManager {
63
-	use TAccountsHelper;
64
-
65
-	/** @var  IDBConnection database connection */
66
-	private $connection;
67
-
68
-	/** @var IConfig */
69
-	private $config;
70
-
71
-	/** @var string table name */
72
-	private $table = 'accounts';
73
-
74
-	/** @var string table name */
75
-	private $dataTable = 'accounts_data';
76
-
77
-	/** @var EventDispatcherInterface */
78
-	private $eventDispatcher;
79
-
80
-	/** @var IJobList */
81
-	private $jobList;
82
-
83
-	/** @var LoggerInterface */
84
-	private $logger;
85
-
86
-	public function __construct(IDBConnection $connection,
87
-								IConfig $config,
88
-								EventDispatcherInterface $eventDispatcher,
89
-								IJobList $jobList,
90
-								LoggerInterface $logger) {
91
-		$this->connection = $connection;
92
-		$this->config = $config;
93
-		$this->eventDispatcher = $eventDispatcher;
94
-		$this->jobList = $jobList;
95
-		$this->logger = $logger;
96
-	}
97
-
98
-	/**
99
-	 * @param string $input
100
-	 * @return string Provided phone number in E.164 format when it was a valid number
101
-	 * @throws \InvalidArgumentException When the phone number was invalid or no default region is set and the number doesn't start with a country code
102
-	 */
103
-	protected function parsePhoneNumber(string $input): string {
104
-		$defaultRegion = $this->config->getSystemValueString('default_phone_region', '');
105
-
106
-		if ($defaultRegion === '') {
107
-			// When no default region is set, only +49… numbers are valid
108
-			if (strpos($input, '+') !== 0) {
109
-				throw new \InvalidArgumentException(self::PROPERTY_PHONE);
110
-			}
111
-
112
-			$defaultRegion = 'EN';
113
-		}
114
-
115
-		$phoneUtil = PhoneNumberUtil::getInstance();
116
-		try {
117
-			$phoneNumber = $phoneUtil->parse($input, $defaultRegion);
118
-			if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
119
-				return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
120
-			}
121
-		} catch (NumberParseException $e) {
122
-		}
123
-
124
-		throw new \InvalidArgumentException(self::PROPERTY_PHONE);
125
-	}
126
-
127
-	/**
128
-	 *
129
-	 * @param string $input
130
-	 * @return string
131
-	 * @throws \InvalidArgumentException When the website did not have http(s) as protocol or the host name was empty
132
-	 */
133
-	protected function parseWebsite(string $input): string {
134
-		$parts = parse_url($input);
135
-		if (!isset($parts['scheme']) || ($parts['scheme'] !== 'https' && $parts['scheme'] !== 'http')) {
136
-			throw new \InvalidArgumentException(self::PROPERTY_WEBSITE);
137
-		}
138
-
139
-		if (!isset($parts['host']) || $parts['host'] === '') {
140
-			throw new \InvalidArgumentException(self::PROPERTY_WEBSITE);
141
-		}
142
-
143
-		return $input;
144
-	}
145
-
146
-	protected function sanitizeLength(array &$propertyData, bool $throwOnData = false): void {
147
-		if (isset($propertyData['value']) && strlen($propertyData['value']) > 2048) {
148
-			if ($throwOnData) {
149
-				throw new \InvalidArgumentException();
150
-			} else {
151
-				$propertyData['value'] = '';
152
-			}
153
-		}
154
-	}
155
-
156
-	protected function testValueLengths(array &$data, bool $throwOnData = false): void {
157
-		try {
158
-			foreach ($data as $propertyName => &$propertyData) {
159
-				if ($this->isCollection($propertyName)) {
160
-					$this->testValueLengths($propertyData, $throwOnData);
161
-				} else {
162
-					$this->sanitizeLength($propertyData, $throwOnData);
163
-				}
164
-			}
165
-		} catch (\InvalidArgumentException $e) {
166
-			throw new \InvalidArgumentException($propertyName);
167
-		}
168
-	}
169
-
170
-	protected function testPropertyScopes(array &$data, array $allowedScopes, bool $throwOnData = false, string $parentPropertyName = null): void {
171
-		foreach ($data as $propertyNameOrIndex => &$propertyData) {
172
-			if ($this->isCollection($propertyNameOrIndex)) {
173
-				$this->testPropertyScopes($propertyData, $allowedScopes, $throwOnData);
174
-			} elseif (isset($propertyData['scope'])) {
175
-				$effectivePropertyName = $parentPropertyName ?? $propertyNameOrIndex;
176
-
177
-				if ($throwOnData && !in_array($propertyData['scope'], $allowedScopes, true)) {
178
-					throw new \InvalidArgumentException('scope');
179
-				}
180
-
181
-				if (
182
-					$propertyData['scope'] === self::SCOPE_PRIVATE
183
-					&& ($effectivePropertyName === self::PROPERTY_DISPLAYNAME || $effectivePropertyName === self::PROPERTY_EMAIL)
184
-				) {
185
-					if ($throwOnData) {
186
-						// v2-private is not available for these fields
187
-						throw new \InvalidArgumentException('scope');
188
-					} else {
189
-						// default to local
190
-						$data[$propertyNameOrIndex]['scope'] = self::SCOPE_LOCAL;
191
-					}
192
-				} else {
193
-					// migrate scope values to the new format
194
-					// invalid scopes are mapped to a default value
195
-					$data[$propertyNameOrIndex]['scope'] = AccountProperty::mapScopeToV2($propertyData['scope']);
196
-				}
197
-			}
198
-		}
199
-	}
200
-
201
-	/**
202
-	 * update user record
203
-	 *
204
-	 * @param IUser $user
205
-	 * @param array $data
206
-	 * @param bool $throwOnData Set to true if you can inform the user about invalid data
207
-	 * @return array The potentially modified data (e.g. phone numbers are converted to E.164 format)
208
-	 * @throws \InvalidArgumentException Message is the property that was invalid
209
-	 */
210
-	public function updateUser(IUser $user, array $data, bool $throwOnData = false): array {
211
-		$userData = $this->getUser($user);
212
-		$updated = true;
213
-
214
-		if (isset($data[self::PROPERTY_PHONE]) && $data[self::PROPERTY_PHONE]['value'] !== '') {
215
-			// Sanitize null value.
216
-			$data[self::PROPERTY_PHONE]['value'] = $data[self::PROPERTY_PHONE]['value'] ?? '';
217
-
218
-			try {
219
-				$data[self::PROPERTY_PHONE]['value'] = $this->parsePhoneNumber($data[self::PROPERTY_PHONE]['value']);
220
-			} catch (\InvalidArgumentException $e) {
221
-				if ($throwOnData) {
222
-					throw $e;
223
-				}
224
-				$data[self::PROPERTY_PHONE]['value'] = '';
225
-			}
226
-		}
227
-
228
-		$this->testValueLengths($data);
229
-
230
-		if (isset($data[self::PROPERTY_WEBSITE]) && $data[self::PROPERTY_WEBSITE]['value'] !== '') {
231
-			try {
232
-				$data[self::PROPERTY_WEBSITE]['value'] = $this->parseWebsite($data[self::PROPERTY_WEBSITE]['value']);
233
-			} catch (\InvalidArgumentException $e) {
234
-				if ($throwOnData) {
235
-					throw $e;
236
-				}
237
-				$data[self::PROPERTY_WEBSITE]['value'] = '';
238
-			}
239
-		}
240
-
241
-		$allowedScopes = [
242
-			self::SCOPE_PRIVATE,
243
-			self::SCOPE_LOCAL,
244
-			self::SCOPE_FEDERATED,
245
-			self::SCOPE_PUBLISHED,
246
-			self::VISIBILITY_PRIVATE,
247
-			self::VISIBILITY_CONTACTS_ONLY,
248
-			self::VISIBILITY_PUBLIC,
249
-		];
250
-
251
-		$this->testPropertyScopes($data, $allowedScopes, $throwOnData);
252
-
253
-		if (empty($userData)) {
254
-			$this->insertNewUser($user, $data);
255
-		} elseif ($userData !== $data) {
256
-			$data = $this->checkEmailVerification($userData, $data, $user);
257
-			$data = $this->updateVerifyStatus($userData, $data);
258
-			$this->updateExistingUser($user, $data);
259
-		} else {
260
-			// nothing needs to be done if new and old data set are the same
261
-			$updated = false;
262
-		}
263
-
264
-		if ($updated) {
265
-			$this->eventDispatcher->dispatch(
266
-				'OC\AccountManager::userUpdated',
267
-				new GenericEvent($user, $data)
268
-			);
269
-		}
270
-
271
-		return $data;
272
-	}
273
-
274
-	/**
275
-	 * delete user from accounts table
276
-	 *
277
-	 * @param IUser $user
278
-	 */
279
-	public function deleteUser(IUser $user) {
280
-		$uid = $user->getUID();
281
-		$query = $this->connection->getQueryBuilder();
282
-		$query->delete($this->table)
283
-			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
284
-			->execute();
285
-
286
-		$this->deleteUserData($user);
287
-	}
288
-
289
-	/**
290
-	 * delete user from accounts table
291
-	 *
292
-	 * @param IUser $user
293
-	 */
294
-	public function deleteUserData(IUser $user): void {
295
-		$uid = $user->getUID();
296
-		$query = $this->connection->getQueryBuilder();
297
-		$query->delete($this->dataTable)
298
-			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
299
-			->execute();
300
-	}
301
-
302
-	/**
303
-	 * get stored data from a given user
304
-	 *
305
-	 * @deprecated use getAccount instead to make sure migrated properties work correctly
306
-	 */
307
-	public function getUser(IUser $user, bool $insertIfNotExists = true): array {
308
-		$uid = $user->getUID();
309
-		$query = $this->connection->getQueryBuilder();
310
-		$query->select('data')
311
-			->from($this->table)
312
-			->where($query->expr()->eq('uid', $query->createParameter('uid')))
313
-			->setParameter('uid', $uid);
314
-		$result = $query->execute();
315
-		$accountData = $result->fetchAll();
316
-		$result->closeCursor();
317
-
318
-		if (empty($accountData)) {
319
-			$userData = $this->buildDefaultUserRecord($user);
320
-			if ($insertIfNotExists) {
321
-				$this->insertNewUser($user, $userData);
322
-			}
323
-			return $userData;
324
-		}
325
-
326
-		$userDataArray = json_decode($accountData[0]['data'], true);
327
-		$jsonError = json_last_error();
328
-		if ($userDataArray === null || $userDataArray === [] || $jsonError !== JSON_ERROR_NONE) {
329
-			$this->logger->critical("User data of $uid contained invalid JSON (error $jsonError), hence falling back to a default user record");
330
-			return $this->buildDefaultUserRecord($user);
331
-		}
332
-
333
-		return $this->addMissingDefaultValues($userDataArray);
334
-	}
335
-
336
-	public function searchUsers(string $property, array $values): array {
337
-		$chunks = array_chunk($values, 500);
338
-		$query = $this->connection->getQueryBuilder();
339
-		$query->select('*')
340
-			->from($this->dataTable)
341
-			->where($query->expr()->eq('name', $query->createNamedParameter($property)))
342
-			->andWhere($query->expr()->in('value', $query->createParameter('values')));
343
-
344
-		$matches = [];
345
-		foreach ($chunks as $chunk) {
346
-			$query->setParameter('values', $chunk, IQueryBuilder::PARAM_STR_ARRAY);
347
-			$result = $query->execute();
348
-
349
-			while ($row = $result->fetch()) {
350
-				$matches[$row['uid']] = $row['value'];
351
-			}
352
-			$result->closeCursor();
353
-		}
354
-
355
-		$result = array_merge($matches, $this->searchUsersForRelatedCollection($property, $values));
356
-
357
-		return array_flip($result);
358
-	}
359
-
360
-	protected function searchUsersForRelatedCollection(string $property, array $values): array {
361
-		switch ($property) {
362
-			case IAccountManager::PROPERTY_EMAIL:
363
-				return array_flip($this->searchUsers(IAccountManager::COLLECTION_EMAIL, $values));
364
-			default:
365
-				return [];
366
-		}
367
-	}
368
-
369
-	/**
370
-	 * check if we need to ask the server for email verification, if yes we create a cronjob
371
-	 *
372
-	 * @param $oldData
373
-	 * @param $newData
374
-	 * @param IUser $user
375
-	 * @return array
376
-	 */
377
-	protected function checkEmailVerification($oldData, $newData, IUser $user): array {
378
-		if ($oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']) {
379
-			$this->jobList->add(VerifyUserData::class,
380
-				[
381
-					'verificationCode' => '',
382
-					'data' => $newData[self::PROPERTY_EMAIL]['value'],
383
-					'type' => self::PROPERTY_EMAIL,
384
-					'uid' => $user->getUID(),
385
-					'try' => 0,
386
-					'lastRun' => time()
387
-				]
388
-			);
389
-			$newData[self::PROPERTY_EMAIL]['verified'] = self::VERIFICATION_IN_PROGRESS;
390
-		}
391
-
392
-		return $newData;
393
-	}
394
-
395
-	/**
396
-	 * make sure that all expected data are set
397
-	 *
398
-	 * @param array $userData
399
-	 * @return array
400
-	 */
401
-	protected function addMissingDefaultValues(array $userData) {
402
-		foreach ($userData as $key => $value) {
403
-			if (!isset($userData[$key]['verified'])) {
404
-				$userData[$key]['verified'] = self::NOT_VERIFIED;
405
-			}
406
-		}
407
-
408
-		return $userData;
409
-	}
410
-
411
-	/**
412
-	 * reset verification status if personal data changed
413
-	 *
414
-	 * @param array $oldData
415
-	 * @param array $newData
416
-	 * @return array
417
-	 */
418
-	protected function updateVerifyStatus(array $oldData, array $newData): array {
419
-
420
-		// which account was already verified successfully?
421
-		$twitterVerified = isset($oldData[self::PROPERTY_TWITTER]['verified']) && $oldData[self::PROPERTY_TWITTER]['verified'] === self::VERIFIED;
422
-		$websiteVerified = isset($oldData[self::PROPERTY_WEBSITE]['verified']) && $oldData[self::PROPERTY_WEBSITE]['verified'] === self::VERIFIED;
423
-		$emailVerified = isset($oldData[self::PROPERTY_EMAIL]['verified']) && $oldData[self::PROPERTY_EMAIL]['verified'] === self::VERIFIED;
424
-
425
-		// keep old verification status if we don't have a new one
426
-		if (!isset($newData[self::PROPERTY_TWITTER]['verified'])) {
427
-			// keep old verification status if value didn't changed and an old value exists
428
-			$keepOldStatus = $newData[self::PROPERTY_TWITTER]['value'] === $oldData[self::PROPERTY_TWITTER]['value'] && isset($oldData[self::PROPERTY_TWITTER]['verified']);
429
-			$newData[self::PROPERTY_TWITTER]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_TWITTER]['verified'] : self::NOT_VERIFIED;
430
-		}
431
-
432
-		if (!isset($newData[self::PROPERTY_WEBSITE]['verified'])) {
433
-			// keep old verification status if value didn't changed and an old value exists
434
-			$keepOldStatus = $newData[self::PROPERTY_WEBSITE]['value'] === $oldData[self::PROPERTY_WEBSITE]['value'] && isset($oldData[self::PROPERTY_WEBSITE]['verified']);
435
-			$newData[self::PROPERTY_WEBSITE]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_WEBSITE]['verified'] : self::NOT_VERIFIED;
436
-		}
437
-
438
-		if (!isset($newData[self::PROPERTY_EMAIL]['verified'])) {
439
-			// keep old verification status if value didn't changed and an old value exists
440
-			$keepOldStatus = $newData[self::PROPERTY_EMAIL]['value'] === $oldData[self::PROPERTY_EMAIL]['value'] && isset($oldData[self::PROPERTY_EMAIL]['verified']);
441
-			$newData[self::PROPERTY_EMAIL]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_EMAIL]['verified'] : self::VERIFICATION_IN_PROGRESS;
442
-		}
443
-
444
-		// reset verification status if a value from a previously verified data was changed
445
-		if ($twitterVerified &&
446
-			$oldData[self::PROPERTY_TWITTER]['value'] !== $newData[self::PROPERTY_TWITTER]['value']
447
-		) {
448
-			$newData[self::PROPERTY_TWITTER]['verified'] = self::NOT_VERIFIED;
449
-		}
450
-
451
-		if ($websiteVerified &&
452
-			$oldData[self::PROPERTY_WEBSITE]['value'] !== $newData[self::PROPERTY_WEBSITE]['value']
453
-		) {
454
-			$newData[self::PROPERTY_WEBSITE]['verified'] = self::NOT_VERIFIED;
455
-		}
456
-
457
-		if ($emailVerified &&
458
-			$oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']
459
-		) {
460
-			$newData[self::PROPERTY_EMAIL]['verified'] = self::NOT_VERIFIED;
461
-		}
462
-
463
-		return $newData;
464
-	}
465
-
466
-	/**
467
-	 * add new user to accounts table
468
-	 *
469
-	 * @param IUser $user
470
-	 * @param array $data
471
-	 */
472
-	protected function insertNewUser(IUser $user, array $data): void {
473
-		$uid = $user->getUID();
474
-		$jsonEncodedData = json_encode($data);
475
-		$query = $this->connection->getQueryBuilder();
476
-		$query->insert($this->table)
477
-			->values(
478
-				[
479
-					'uid' => $query->createNamedParameter($uid),
480
-					'data' => $query->createNamedParameter($jsonEncodedData),
481
-				]
482
-			)
483
-			->execute();
484
-
485
-		$this->deleteUserData($user);
486
-		$this->writeUserData($user, $data);
487
-	}
488
-
489
-	/**
490
-	 * update existing user in accounts table
491
-	 *
492
-	 * @param IUser $user
493
-	 * @param array $data
494
-	 */
495
-	protected function updateExistingUser(IUser $user, array $data): void {
496
-		$uid = $user->getUID();
497
-		$jsonEncodedData = json_encode($data);
498
-		$query = $this->connection->getQueryBuilder();
499
-		$query->update($this->table)
500
-			->set('data', $query->createNamedParameter($jsonEncodedData))
501
-			->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
502
-			->execute();
503
-
504
-		$this->deleteUserData($user);
505
-		$this->writeUserData($user, $data);
506
-	}
507
-
508
-	protected function writeUserData(IUser $user, array $data): void {
509
-		$query = $this->connection->getQueryBuilder();
510
-		$query->insert($this->dataTable)
511
-			->values(
512
-				[
513
-					'uid' => $query->createNamedParameter($user->getUID()),
514
-					'name' => $query->createParameter('name'),
515
-					'value' => $query->createParameter('value'),
516
-				]
517
-			);
518
-		$this->writeUserDataProperties($query, $data);
519
-	}
520
-
521
-	protected function writeUserDataProperties(IQueryBuilder $query, array $data, string $parentPropertyName = null): void {
522
-		foreach ($data as $propertyName => $property) {
523
-			if ($this->isCollection($propertyName)) {
524
-				$this->writeUserDataProperties($query, $property, $propertyName);
525
-				continue;
526
-			}
527
-			if (($parentPropertyName ?? $propertyName) === self::PROPERTY_AVATAR) {
528
-				continue;
529
-			}
530
-
531
-			$query->setParameter('name', $parentPropertyName ?? $propertyName)
532
-				->setParameter('value', $property['value'] ?? '');
533
-			$query->execute();
534
-		}
535
-	}
536
-
537
-	/**
538
-	 * build default user record in case not data set exists yet
539
-	 *
540
-	 * @param IUser $user
541
-	 * @return array
542
-	 */
543
-	protected function buildDefaultUserRecord(IUser $user) {
544
-		return [
545
-			self::PROPERTY_DISPLAYNAME =>
546
-				[
547
-					'value' => $user->getDisplayName(),
548
-					'scope' => self::SCOPE_FEDERATED,
549
-					'verified' => self::NOT_VERIFIED,
550
-				],
551
-			self::PROPERTY_ADDRESS =>
552
-				[
553
-					'value' => '',
554
-					'scope' => self::SCOPE_LOCAL,
555
-					'verified' => self::NOT_VERIFIED,
556
-				],
557
-			self::PROPERTY_WEBSITE =>
558
-				[
559
-					'value' => '',
560
-					'scope' => self::SCOPE_LOCAL,
561
-					'verified' => self::NOT_VERIFIED,
562
-				],
563
-			self::PROPERTY_EMAIL =>
564
-				[
565
-					'value' => $user->getEMailAddress(),
566
-					'scope' => self::SCOPE_FEDERATED,
567
-					'verified' => self::NOT_VERIFIED,
568
-				],
569
-			self::PROPERTY_AVATAR =>
570
-				[
571
-					'scope' => self::SCOPE_FEDERATED
572
-				],
573
-			self::PROPERTY_PHONE =>
574
-				[
575
-					'value' => '',
576
-					'scope' => self::SCOPE_LOCAL,
577
-					'verified' => self::NOT_VERIFIED,
578
-				],
579
-			self::PROPERTY_TWITTER =>
580
-				[
581
-					'value' => '',
582
-					'scope' => self::SCOPE_LOCAL,
583
-					'verified' => self::NOT_VERIFIED,
584
-				],
585
-		];
586
-	}
587
-
588
-	private function parseAccountData(IUser $user, $data): Account {
589
-		$account = new Account($user);
590
-		foreach ($data as $property => $accountData) {
591
-			$account->setProperty($property, $accountData['value'] ?? '', $accountData['scope'] ?? self::SCOPE_LOCAL, $accountData['verified'] ?? self::NOT_VERIFIED);
592
-		}
593
-		return $account;
594
-	}
595
-
596
-	public function getAccount(IUser $user): IAccount {
597
-		return $this->parseAccountData($user, $this->getUser($user));
598
-	}
599
-
600
-	public function updateAccount(IAccount $account): void {
601
-		$data = [];
602
-
603
-		foreach ($account->getProperties() as $property) {
604
-			$data[$property->getName()] = [
605
-				'value' => $property->getValue(),
606
-				'scope' => $property->getScope(),
607
-				'verified' => $property->getVerified(),
608
-			];
609
-		}
610
-
611
-		$this->updateUser($account->getUser(), $data, true);
612
-	}
63
+    use TAccountsHelper;
64
+
65
+    /** @var  IDBConnection database connection */
66
+    private $connection;
67
+
68
+    /** @var IConfig */
69
+    private $config;
70
+
71
+    /** @var string table name */
72
+    private $table = 'accounts';
73
+
74
+    /** @var string table name */
75
+    private $dataTable = 'accounts_data';
76
+
77
+    /** @var EventDispatcherInterface */
78
+    private $eventDispatcher;
79
+
80
+    /** @var IJobList */
81
+    private $jobList;
82
+
83
+    /** @var LoggerInterface */
84
+    private $logger;
85
+
86
+    public function __construct(IDBConnection $connection,
87
+                                IConfig $config,
88
+                                EventDispatcherInterface $eventDispatcher,
89
+                                IJobList $jobList,
90
+                                LoggerInterface $logger) {
91
+        $this->connection = $connection;
92
+        $this->config = $config;
93
+        $this->eventDispatcher = $eventDispatcher;
94
+        $this->jobList = $jobList;
95
+        $this->logger = $logger;
96
+    }
97
+
98
+    /**
99
+     * @param string $input
100
+     * @return string Provided phone number in E.164 format when it was a valid number
101
+     * @throws \InvalidArgumentException When the phone number was invalid or no default region is set and the number doesn't start with a country code
102
+     */
103
+    protected function parsePhoneNumber(string $input): string {
104
+        $defaultRegion = $this->config->getSystemValueString('default_phone_region', '');
105
+
106
+        if ($defaultRegion === '') {
107
+            // When no default region is set, only +49… numbers are valid
108
+            if (strpos($input, '+') !== 0) {
109
+                throw new \InvalidArgumentException(self::PROPERTY_PHONE);
110
+            }
111
+
112
+            $defaultRegion = 'EN';
113
+        }
114
+
115
+        $phoneUtil = PhoneNumberUtil::getInstance();
116
+        try {
117
+            $phoneNumber = $phoneUtil->parse($input, $defaultRegion);
118
+            if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) {
119
+                return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164);
120
+            }
121
+        } catch (NumberParseException $e) {
122
+        }
123
+
124
+        throw new \InvalidArgumentException(self::PROPERTY_PHONE);
125
+    }
126
+
127
+    /**
128
+     *
129
+     * @param string $input
130
+     * @return string
131
+     * @throws \InvalidArgumentException When the website did not have http(s) as protocol or the host name was empty
132
+     */
133
+    protected function parseWebsite(string $input): string {
134
+        $parts = parse_url($input);
135
+        if (!isset($parts['scheme']) || ($parts['scheme'] !== 'https' && $parts['scheme'] !== 'http')) {
136
+            throw new \InvalidArgumentException(self::PROPERTY_WEBSITE);
137
+        }
138
+
139
+        if (!isset($parts['host']) || $parts['host'] === '') {
140
+            throw new \InvalidArgumentException(self::PROPERTY_WEBSITE);
141
+        }
142
+
143
+        return $input;
144
+    }
145
+
146
+    protected function sanitizeLength(array &$propertyData, bool $throwOnData = false): void {
147
+        if (isset($propertyData['value']) && strlen($propertyData['value']) > 2048) {
148
+            if ($throwOnData) {
149
+                throw new \InvalidArgumentException();
150
+            } else {
151
+                $propertyData['value'] = '';
152
+            }
153
+        }
154
+    }
155
+
156
+    protected function testValueLengths(array &$data, bool $throwOnData = false): void {
157
+        try {
158
+            foreach ($data as $propertyName => &$propertyData) {
159
+                if ($this->isCollection($propertyName)) {
160
+                    $this->testValueLengths($propertyData, $throwOnData);
161
+                } else {
162
+                    $this->sanitizeLength($propertyData, $throwOnData);
163
+                }
164
+            }
165
+        } catch (\InvalidArgumentException $e) {
166
+            throw new \InvalidArgumentException($propertyName);
167
+        }
168
+    }
169
+
170
+    protected function testPropertyScopes(array &$data, array $allowedScopes, bool $throwOnData = false, string $parentPropertyName = null): void {
171
+        foreach ($data as $propertyNameOrIndex => &$propertyData) {
172
+            if ($this->isCollection($propertyNameOrIndex)) {
173
+                $this->testPropertyScopes($propertyData, $allowedScopes, $throwOnData);
174
+            } elseif (isset($propertyData['scope'])) {
175
+                $effectivePropertyName = $parentPropertyName ?? $propertyNameOrIndex;
176
+
177
+                if ($throwOnData && !in_array($propertyData['scope'], $allowedScopes, true)) {
178
+                    throw new \InvalidArgumentException('scope');
179
+                }
180
+
181
+                if (
182
+                    $propertyData['scope'] === self::SCOPE_PRIVATE
183
+                    && ($effectivePropertyName === self::PROPERTY_DISPLAYNAME || $effectivePropertyName === self::PROPERTY_EMAIL)
184
+                ) {
185
+                    if ($throwOnData) {
186
+                        // v2-private is not available for these fields
187
+                        throw new \InvalidArgumentException('scope');
188
+                    } else {
189
+                        // default to local
190
+                        $data[$propertyNameOrIndex]['scope'] = self::SCOPE_LOCAL;
191
+                    }
192
+                } else {
193
+                    // migrate scope values to the new format
194
+                    // invalid scopes are mapped to a default value
195
+                    $data[$propertyNameOrIndex]['scope'] = AccountProperty::mapScopeToV2($propertyData['scope']);
196
+                }
197
+            }
198
+        }
199
+    }
200
+
201
+    /**
202
+     * update user record
203
+     *
204
+     * @param IUser $user
205
+     * @param array $data
206
+     * @param bool $throwOnData Set to true if you can inform the user about invalid data
207
+     * @return array The potentially modified data (e.g. phone numbers are converted to E.164 format)
208
+     * @throws \InvalidArgumentException Message is the property that was invalid
209
+     */
210
+    public function updateUser(IUser $user, array $data, bool $throwOnData = false): array {
211
+        $userData = $this->getUser($user);
212
+        $updated = true;
213
+
214
+        if (isset($data[self::PROPERTY_PHONE]) && $data[self::PROPERTY_PHONE]['value'] !== '') {
215
+            // Sanitize null value.
216
+            $data[self::PROPERTY_PHONE]['value'] = $data[self::PROPERTY_PHONE]['value'] ?? '';
217
+
218
+            try {
219
+                $data[self::PROPERTY_PHONE]['value'] = $this->parsePhoneNumber($data[self::PROPERTY_PHONE]['value']);
220
+            } catch (\InvalidArgumentException $e) {
221
+                if ($throwOnData) {
222
+                    throw $e;
223
+                }
224
+                $data[self::PROPERTY_PHONE]['value'] = '';
225
+            }
226
+        }
227
+
228
+        $this->testValueLengths($data);
229
+
230
+        if (isset($data[self::PROPERTY_WEBSITE]) && $data[self::PROPERTY_WEBSITE]['value'] !== '') {
231
+            try {
232
+                $data[self::PROPERTY_WEBSITE]['value'] = $this->parseWebsite($data[self::PROPERTY_WEBSITE]['value']);
233
+            } catch (\InvalidArgumentException $e) {
234
+                if ($throwOnData) {
235
+                    throw $e;
236
+                }
237
+                $data[self::PROPERTY_WEBSITE]['value'] = '';
238
+            }
239
+        }
240
+
241
+        $allowedScopes = [
242
+            self::SCOPE_PRIVATE,
243
+            self::SCOPE_LOCAL,
244
+            self::SCOPE_FEDERATED,
245
+            self::SCOPE_PUBLISHED,
246
+            self::VISIBILITY_PRIVATE,
247
+            self::VISIBILITY_CONTACTS_ONLY,
248
+            self::VISIBILITY_PUBLIC,
249
+        ];
250
+
251
+        $this->testPropertyScopes($data, $allowedScopes, $throwOnData);
252
+
253
+        if (empty($userData)) {
254
+            $this->insertNewUser($user, $data);
255
+        } elseif ($userData !== $data) {
256
+            $data = $this->checkEmailVerification($userData, $data, $user);
257
+            $data = $this->updateVerifyStatus($userData, $data);
258
+            $this->updateExistingUser($user, $data);
259
+        } else {
260
+            // nothing needs to be done if new and old data set are the same
261
+            $updated = false;
262
+        }
263
+
264
+        if ($updated) {
265
+            $this->eventDispatcher->dispatch(
266
+                'OC\AccountManager::userUpdated',
267
+                new GenericEvent($user, $data)
268
+            );
269
+        }
270
+
271
+        return $data;
272
+    }
273
+
274
+    /**
275
+     * delete user from accounts table
276
+     *
277
+     * @param IUser $user
278
+     */
279
+    public function deleteUser(IUser $user) {
280
+        $uid = $user->getUID();
281
+        $query = $this->connection->getQueryBuilder();
282
+        $query->delete($this->table)
283
+            ->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
284
+            ->execute();
285
+
286
+        $this->deleteUserData($user);
287
+    }
288
+
289
+    /**
290
+     * delete user from accounts table
291
+     *
292
+     * @param IUser $user
293
+     */
294
+    public function deleteUserData(IUser $user): void {
295
+        $uid = $user->getUID();
296
+        $query = $this->connection->getQueryBuilder();
297
+        $query->delete($this->dataTable)
298
+            ->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
299
+            ->execute();
300
+    }
301
+
302
+    /**
303
+     * get stored data from a given user
304
+     *
305
+     * @deprecated use getAccount instead to make sure migrated properties work correctly
306
+     */
307
+    public function getUser(IUser $user, bool $insertIfNotExists = true): array {
308
+        $uid = $user->getUID();
309
+        $query = $this->connection->getQueryBuilder();
310
+        $query->select('data')
311
+            ->from($this->table)
312
+            ->where($query->expr()->eq('uid', $query->createParameter('uid')))
313
+            ->setParameter('uid', $uid);
314
+        $result = $query->execute();
315
+        $accountData = $result->fetchAll();
316
+        $result->closeCursor();
317
+
318
+        if (empty($accountData)) {
319
+            $userData = $this->buildDefaultUserRecord($user);
320
+            if ($insertIfNotExists) {
321
+                $this->insertNewUser($user, $userData);
322
+            }
323
+            return $userData;
324
+        }
325
+
326
+        $userDataArray = json_decode($accountData[0]['data'], true);
327
+        $jsonError = json_last_error();
328
+        if ($userDataArray === null || $userDataArray === [] || $jsonError !== JSON_ERROR_NONE) {
329
+            $this->logger->critical("User data of $uid contained invalid JSON (error $jsonError), hence falling back to a default user record");
330
+            return $this->buildDefaultUserRecord($user);
331
+        }
332
+
333
+        return $this->addMissingDefaultValues($userDataArray);
334
+    }
335
+
336
+    public function searchUsers(string $property, array $values): array {
337
+        $chunks = array_chunk($values, 500);
338
+        $query = $this->connection->getQueryBuilder();
339
+        $query->select('*')
340
+            ->from($this->dataTable)
341
+            ->where($query->expr()->eq('name', $query->createNamedParameter($property)))
342
+            ->andWhere($query->expr()->in('value', $query->createParameter('values')));
343
+
344
+        $matches = [];
345
+        foreach ($chunks as $chunk) {
346
+            $query->setParameter('values', $chunk, IQueryBuilder::PARAM_STR_ARRAY);
347
+            $result = $query->execute();
348
+
349
+            while ($row = $result->fetch()) {
350
+                $matches[$row['uid']] = $row['value'];
351
+            }
352
+            $result->closeCursor();
353
+        }
354
+
355
+        $result = array_merge($matches, $this->searchUsersForRelatedCollection($property, $values));
356
+
357
+        return array_flip($result);
358
+    }
359
+
360
+    protected function searchUsersForRelatedCollection(string $property, array $values): array {
361
+        switch ($property) {
362
+            case IAccountManager::PROPERTY_EMAIL:
363
+                return array_flip($this->searchUsers(IAccountManager::COLLECTION_EMAIL, $values));
364
+            default:
365
+                return [];
366
+        }
367
+    }
368
+
369
+    /**
370
+     * check if we need to ask the server for email verification, if yes we create a cronjob
371
+     *
372
+     * @param $oldData
373
+     * @param $newData
374
+     * @param IUser $user
375
+     * @return array
376
+     */
377
+    protected function checkEmailVerification($oldData, $newData, IUser $user): array {
378
+        if ($oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']) {
379
+            $this->jobList->add(VerifyUserData::class,
380
+                [
381
+                    'verificationCode' => '',
382
+                    'data' => $newData[self::PROPERTY_EMAIL]['value'],
383
+                    'type' => self::PROPERTY_EMAIL,
384
+                    'uid' => $user->getUID(),
385
+                    'try' => 0,
386
+                    'lastRun' => time()
387
+                ]
388
+            );
389
+            $newData[self::PROPERTY_EMAIL]['verified'] = self::VERIFICATION_IN_PROGRESS;
390
+        }
391
+
392
+        return $newData;
393
+    }
394
+
395
+    /**
396
+     * make sure that all expected data are set
397
+     *
398
+     * @param array $userData
399
+     * @return array
400
+     */
401
+    protected function addMissingDefaultValues(array $userData) {
402
+        foreach ($userData as $key => $value) {
403
+            if (!isset($userData[$key]['verified'])) {
404
+                $userData[$key]['verified'] = self::NOT_VERIFIED;
405
+            }
406
+        }
407
+
408
+        return $userData;
409
+    }
410
+
411
+    /**
412
+     * reset verification status if personal data changed
413
+     *
414
+     * @param array $oldData
415
+     * @param array $newData
416
+     * @return array
417
+     */
418
+    protected function updateVerifyStatus(array $oldData, array $newData): array {
419
+
420
+        // which account was already verified successfully?
421
+        $twitterVerified = isset($oldData[self::PROPERTY_TWITTER]['verified']) && $oldData[self::PROPERTY_TWITTER]['verified'] === self::VERIFIED;
422
+        $websiteVerified = isset($oldData[self::PROPERTY_WEBSITE]['verified']) && $oldData[self::PROPERTY_WEBSITE]['verified'] === self::VERIFIED;
423
+        $emailVerified = isset($oldData[self::PROPERTY_EMAIL]['verified']) && $oldData[self::PROPERTY_EMAIL]['verified'] === self::VERIFIED;
424
+
425
+        // keep old verification status if we don't have a new one
426
+        if (!isset($newData[self::PROPERTY_TWITTER]['verified'])) {
427
+            // keep old verification status if value didn't changed and an old value exists
428
+            $keepOldStatus = $newData[self::PROPERTY_TWITTER]['value'] === $oldData[self::PROPERTY_TWITTER]['value'] && isset($oldData[self::PROPERTY_TWITTER]['verified']);
429
+            $newData[self::PROPERTY_TWITTER]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_TWITTER]['verified'] : self::NOT_VERIFIED;
430
+        }
431
+
432
+        if (!isset($newData[self::PROPERTY_WEBSITE]['verified'])) {
433
+            // keep old verification status if value didn't changed and an old value exists
434
+            $keepOldStatus = $newData[self::PROPERTY_WEBSITE]['value'] === $oldData[self::PROPERTY_WEBSITE]['value'] && isset($oldData[self::PROPERTY_WEBSITE]['verified']);
435
+            $newData[self::PROPERTY_WEBSITE]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_WEBSITE]['verified'] : self::NOT_VERIFIED;
436
+        }
437
+
438
+        if (!isset($newData[self::PROPERTY_EMAIL]['verified'])) {
439
+            // keep old verification status if value didn't changed and an old value exists
440
+            $keepOldStatus = $newData[self::PROPERTY_EMAIL]['value'] === $oldData[self::PROPERTY_EMAIL]['value'] && isset($oldData[self::PROPERTY_EMAIL]['verified']);
441
+            $newData[self::PROPERTY_EMAIL]['verified'] = $keepOldStatus ? $oldData[self::PROPERTY_EMAIL]['verified'] : self::VERIFICATION_IN_PROGRESS;
442
+        }
443
+
444
+        // reset verification status if a value from a previously verified data was changed
445
+        if ($twitterVerified &&
446
+            $oldData[self::PROPERTY_TWITTER]['value'] !== $newData[self::PROPERTY_TWITTER]['value']
447
+        ) {
448
+            $newData[self::PROPERTY_TWITTER]['verified'] = self::NOT_VERIFIED;
449
+        }
450
+
451
+        if ($websiteVerified &&
452
+            $oldData[self::PROPERTY_WEBSITE]['value'] !== $newData[self::PROPERTY_WEBSITE]['value']
453
+        ) {
454
+            $newData[self::PROPERTY_WEBSITE]['verified'] = self::NOT_VERIFIED;
455
+        }
456
+
457
+        if ($emailVerified &&
458
+            $oldData[self::PROPERTY_EMAIL]['value'] !== $newData[self::PROPERTY_EMAIL]['value']
459
+        ) {
460
+            $newData[self::PROPERTY_EMAIL]['verified'] = self::NOT_VERIFIED;
461
+        }
462
+
463
+        return $newData;
464
+    }
465
+
466
+    /**
467
+     * add new user to accounts table
468
+     *
469
+     * @param IUser $user
470
+     * @param array $data
471
+     */
472
+    protected function insertNewUser(IUser $user, array $data): void {
473
+        $uid = $user->getUID();
474
+        $jsonEncodedData = json_encode($data);
475
+        $query = $this->connection->getQueryBuilder();
476
+        $query->insert($this->table)
477
+            ->values(
478
+                [
479
+                    'uid' => $query->createNamedParameter($uid),
480
+                    'data' => $query->createNamedParameter($jsonEncodedData),
481
+                ]
482
+            )
483
+            ->execute();
484
+
485
+        $this->deleteUserData($user);
486
+        $this->writeUserData($user, $data);
487
+    }
488
+
489
+    /**
490
+     * update existing user in accounts table
491
+     *
492
+     * @param IUser $user
493
+     * @param array $data
494
+     */
495
+    protected function updateExistingUser(IUser $user, array $data): void {
496
+        $uid = $user->getUID();
497
+        $jsonEncodedData = json_encode($data);
498
+        $query = $this->connection->getQueryBuilder();
499
+        $query->update($this->table)
500
+            ->set('data', $query->createNamedParameter($jsonEncodedData))
501
+            ->where($query->expr()->eq('uid', $query->createNamedParameter($uid)))
502
+            ->execute();
503
+
504
+        $this->deleteUserData($user);
505
+        $this->writeUserData($user, $data);
506
+    }
507
+
508
+    protected function writeUserData(IUser $user, array $data): void {
509
+        $query = $this->connection->getQueryBuilder();
510
+        $query->insert($this->dataTable)
511
+            ->values(
512
+                [
513
+                    'uid' => $query->createNamedParameter($user->getUID()),
514
+                    'name' => $query->createParameter('name'),
515
+                    'value' => $query->createParameter('value'),
516
+                ]
517
+            );
518
+        $this->writeUserDataProperties($query, $data);
519
+    }
520
+
521
+    protected function writeUserDataProperties(IQueryBuilder $query, array $data, string $parentPropertyName = null): void {
522
+        foreach ($data as $propertyName => $property) {
523
+            if ($this->isCollection($propertyName)) {
524
+                $this->writeUserDataProperties($query, $property, $propertyName);
525
+                continue;
526
+            }
527
+            if (($parentPropertyName ?? $propertyName) === self::PROPERTY_AVATAR) {
528
+                continue;
529
+            }
530
+
531
+            $query->setParameter('name', $parentPropertyName ?? $propertyName)
532
+                ->setParameter('value', $property['value'] ?? '');
533
+            $query->execute();
534
+        }
535
+    }
536
+
537
+    /**
538
+     * build default user record in case not data set exists yet
539
+     *
540
+     * @param IUser $user
541
+     * @return array
542
+     */
543
+    protected function buildDefaultUserRecord(IUser $user) {
544
+        return [
545
+            self::PROPERTY_DISPLAYNAME =>
546
+                [
547
+                    'value' => $user->getDisplayName(),
548
+                    'scope' => self::SCOPE_FEDERATED,
549
+                    'verified' => self::NOT_VERIFIED,
550
+                ],
551
+            self::PROPERTY_ADDRESS =>
552
+                [
553
+                    'value' => '',
554
+                    'scope' => self::SCOPE_LOCAL,
555
+                    'verified' => self::NOT_VERIFIED,
556
+                ],
557
+            self::PROPERTY_WEBSITE =>
558
+                [
559
+                    'value' => '',
560
+                    'scope' => self::SCOPE_LOCAL,
561
+                    'verified' => self::NOT_VERIFIED,
562
+                ],
563
+            self::PROPERTY_EMAIL =>
564
+                [
565
+                    'value' => $user->getEMailAddress(),
566
+                    'scope' => self::SCOPE_FEDERATED,
567
+                    'verified' => self::NOT_VERIFIED,
568
+                ],
569
+            self::PROPERTY_AVATAR =>
570
+                [
571
+                    'scope' => self::SCOPE_FEDERATED
572
+                ],
573
+            self::PROPERTY_PHONE =>
574
+                [
575
+                    'value' => '',
576
+                    'scope' => self::SCOPE_LOCAL,
577
+                    'verified' => self::NOT_VERIFIED,
578
+                ],
579
+            self::PROPERTY_TWITTER =>
580
+                [
581
+                    'value' => '',
582
+                    'scope' => self::SCOPE_LOCAL,
583
+                    'verified' => self::NOT_VERIFIED,
584
+                ],
585
+        ];
586
+    }
587
+
588
+    private function parseAccountData(IUser $user, $data): Account {
589
+        $account = new Account($user);
590
+        foreach ($data as $property => $accountData) {
591
+            $account->setProperty($property, $accountData['value'] ?? '', $accountData['scope'] ?? self::SCOPE_LOCAL, $accountData['verified'] ?? self::NOT_VERIFIED);
592
+        }
593
+        return $account;
594
+    }
595
+
596
+    public function getAccount(IUser $user): IAccount {
597
+        return $this->parseAccountData($user, $this->getUser($user));
598
+    }
599
+
600
+    public function updateAccount(IAccount $account): void {
601
+        $data = [];
602
+
603
+        foreach ($account->getProperties() as $property) {
604
+            $data[$property->getName()] = [
605
+                'value' => $property->getValue(),
606
+                'scope' => $property->getScope(),
607
+                'verified' => $property->getVerified(),
608
+            ];
609
+        }
610
+
611
+        $this->updateUser($account->getUser(), $data, true);
612
+    }
613 613
 }
Please login to merge, or discard this patch.