1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC;
use OCP\Constants;
use OCP\Contacts\IManager;
use OCP\IAddressBook;
class ContactsManager implements IManager {
/**
* This function is used to search and find contacts within the users address books.
* In case $pattern is empty all contacts will be returned.
*
* @param string $pattern which should match within the $searchProperties
* @param array $searchProperties defines the properties within the query pattern should match
* @param array $options = array() to define the search behavior
* - 'types' boolean (since 15.0.0) If set to true, fields that come with a TYPE property will be an array
* example: ['id' => 5, 'FN' => 'Thomas Tanghus', 'EMAIL' => ['type => 'HOME', 'value' => 'g@h.i']]
* - 'escape_like_param' - If set to false wildcards _ and % are not escaped
* - 'limit' - Set a numeric limit for the search results
* - 'offset' - Set the offset for the limited search results
* - 'enumeration' - (since 23.0.0) Whether user enumeration on system address book is allowed
* - 'fullmatch' - (since 23.0.0) Whether matching on full detail in system address book is allowed
* - 'strict_search' - (since 23.0.0) Whether the search pattern is full string or partial search
* @psalm-param array{types?: bool, escape_like_param?: bool, limit?: int, offset?: int, enumeration?: bool, fullmatch?: bool, strict_search?: bool} $options
* @return array an array of contacts which are arrays of key-value-pairs
*/
public function search($pattern, $searchProperties = [], $options = []) {
$this->loadAddressBooks();
$result = [];
foreach ($this->addressBooks as $addressBook) {
$searchOptions = $options;
$strictSearch = array_key_exists('strict_search', $options) && $options['strict_search'] === true;
if ($addressBook->isSystemAddressBook()) {
$enumeration = !\array_key_exists('enumeration', $options) || $options['enumeration'] !== false;
$fullMatch = !\array_key_exists('fullmatch', $options) || $options['fullmatch'] !== false;
if (!$enumeration && !$fullMatch) {
// No access to system address book AND no full match allowed
continue;
}
if ($strictSearch) {
$searchOptions['wildcard'] = false;
} else {
$searchOptions['wildcard'] = $enumeration;
}
} else {
$searchOptions['wildcard'] = !$strictSearch;
}
$r = $addressBook->search($pattern, $searchProperties, $searchOptions);
$contacts = [];
foreach ($r as $c) {
$c['addressbook-key'] = $addressBook->getKey();
$contacts[] = $c;
}
$result = array_merge($result, $contacts);
}
return $result;
}
/**
* This function can be used to delete the contact identified by the given id
*
* @param int $id the unique identifier to a contact
* @param string $addressBookKey identifier of the address book in which the contact shall be deleted
* @return bool successful or not
*/
public function delete($id, $addressBookKey) {
$addressBook = $this->getAddressBook($addressBookKey);
if (!$addressBook) {
return false;
}
if ($addressBook->getPermissions() & Constants::PERMISSION_DELETE) {
return $addressBook->delete($id);
}
return false;
}
/**
* This function is used to create a new contact if 'id' is not given or not present.
* Otherwise the contact will be updated by replacing the entire data set.
*
* @param array $properties this array if key-value-pairs defines a contact
* @param string $addressBookKey identifier of the address book in which the contact shall be created or updated
* @return ?array representing the contact just created or updated
*/
public function createOrUpdate($properties, $addressBookKey) {
$addressBook = $this->getAddressBook($addressBookKey);
if (!$addressBook) {
return null;
}
if ($addressBook->getPermissions() & Constants::PERMISSION_CREATE) {
return $addressBook->createOrUpdate($properties);
}
return null;
}
/**
* Check if contacts are available (e.g. contacts app enabled)
*
* @return bool true if enabled, false if not
*/
public function isEnabled(): bool {
return !empty($this->addressBooks) || !empty($this->addressBookLoaders);
}
/**
* @param IAddressBook $addressBook
*/
public function registerAddressBook(IAddressBook $addressBook) {
$this->addressBooks[$addressBook->getKey()] = $addressBook;
}
/**
* @param IAddressBook $addressBook
*/
public function unregisterAddressBook(IAddressBook $addressBook) {
unset($this->addressBooks[$addressBook->getKey()]);
}
/**
* Return a list of the user's addressbooks
*
* @return IAddressBook[]
* @since 16.0.0
*/
public function getUserAddressBooks(): array {
$this->loadAddressBooks();
return $this->addressBooks;
}
/**
* removes all registered address book instances
*/
public function clear() {
$this->addressBooks = [];
$this->addressBookLoaders = [];
}
/**
* @var IAddressBook[] which holds all registered address books
*/
private $addressBooks = [];
/**
* @var \Closure[] to call to load/register address books
*/
private $addressBookLoaders = [];
/**
* In order to improve lazy loading a closure can be registered which will be called in case
* address books are actually requested
*
* @param \Closure $callable
*/
public function register(\Closure $callable) {
$this->addressBookLoaders[] = $callable;
}
/**
* Get (and load when needed) the address book for $key
*/
protected function getAddressBook(string $addressBookKey): ?IAddressBook {
$this->loadAddressBooks();
if (!array_key_exists($addressBookKey, $this->addressBooks)) {
return null;
}
return $this->addressBooks[$addressBookKey];
}
/**
* Load all address books registered with 'register'
*/
protected function loadAddressBooks() {
foreach ($this->addressBookLoaders as $callable) {
$callable($this);
}
$this->addressBookLoaders = [];
}
}
|