Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Contact often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Contact, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
33 | class Contact extends VObject\VCard implements IPIMObject { |
||
34 | |||
35 | /** |
||
36 | * The name of the object type in this case VCARD. |
||
37 | * |
||
38 | * This is used when serializing the object. |
||
39 | * |
||
40 | * @var string |
||
41 | */ |
||
42 | public $name = 'VCARD'; |
||
43 | |||
44 | /** |
||
45 | * @brief language object |
||
46 | * |
||
47 | * @var \OCP\IL10N |
||
48 | */ |
||
49 | public static $l10n; |
||
50 | |||
51 | protected $props = array(); |
||
52 | |||
53 | /** |
||
54 | * Create a new Contact object |
||
55 | * |
||
56 | * @param AddressBook $parent |
||
57 | * @param Backend\AbstractBackend $backend |
||
58 | * @param mixed $data |
||
59 | */ |
||
60 | 19 | public function __construct($parent, $backend, $data = null) { |
|
61 | 19 | parent::__construct('VCARD'); |
|
62 | |||
63 | 19 | self::$l10n = $parent::$l10n; |
|
|
|||
64 | //\OCP\Util::writeLog('contacts', __METHOD__ . ' , data: ' . print_r($data, true), \OCP\Util::DEBUG); |
||
65 | 19 | $this->props['parent'] = $parent; |
|
66 | 19 | $this->props['backend'] = $backend; |
|
67 | 19 | $this->props['retrieved'] = false; |
|
68 | 19 | $this->props['saved'] = false; |
|
69 | |||
70 | 19 | if (!is_null($data)) { |
|
71 | 19 | if ($data instanceof VObject\VCard) { |
|
72 | 3 | foreach ($data->children as $child) { |
|
73 | 3 | $this->add($child); |
|
74 | 3 | } |
|
75 | 3 | $this->setRetrieved(true); |
|
76 | 19 | } elseif (is_array($data)) { |
|
77 | 18 | foreach ($data as $key => $value) { |
|
78 | switch ($key) { |
||
79 | 18 | case 'id': |
|
80 | 18 | $this->props['id'] = $value; |
|
81 | 18 | break; |
|
82 | 18 | case 'permissions': |
|
83 | 18 | $this->props['permissions'] = $value; |
|
84 | 18 | break; |
|
85 | 18 | case 'lastmodified': |
|
86 | 2 | $this->props['lastmodified'] = $value; |
|
87 | 2 | break; |
|
88 | 18 | case 'uri': |
|
89 | 2 | $this->props['uri'] = $value; |
|
90 | 2 | break; |
|
91 | 18 | case 'carddata': |
|
92 | 18 | $this->props['carddata'] = $value; |
|
93 | 18 | $this->retrieve(); |
|
94 | 18 | break; |
|
95 | 18 | case 'vcard': |
|
96 | $this->props['vcard'] = $value; |
||
97 | $this->retrieve(); |
||
98 | break; |
||
99 | 18 | case 'displayname': |
|
100 | 18 | case 'fullname': |
|
101 | 18 | if(is_string($value)) { |
|
102 | 18 | $this->props['displayname'] = $value; |
|
103 | 18 | $this->FN = $value; |
|
104 | // Set it to saved again as we're not actually changing anything |
||
105 | 18 | $this->setSaved(); |
|
106 | 18 | } |
|
107 | 18 | break; |
|
108 | } |
||
109 | 18 | } |
|
110 | 18 | } |
|
111 | 19 | } |
|
112 | 19 | } |
|
113 | |||
114 | /** |
||
115 | * @return array|null |
||
116 | */ |
||
117 | 5 | public function getMetaData() { |
|
118 | 5 | if (!$this->hasPermission(\OCP\Constants::PERMISSION_READ)) { |
|
119 | throw new Exception(self::$l10n->t('You do not have permissions to see this contact'), 403); |
||
120 | } |
||
121 | 5 | if (!isset($this->props['displayname'])) { |
|
122 | if (!$this->retrieve()) { |
||
123 | \OCP\Util::writeLog('contacts', __METHOD__.' error reading: '.print_r($this->props, true), \OCP\Util::ERROR); |
||
124 | return null; |
||
125 | } |
||
126 | } |
||
127 | return array( |
||
128 | 5 | 'id' => $this->getId(), |
|
129 | 5 | 'displayname' => $this->getDisplayName(), |
|
130 | 5 | 'permissions' => $this->getPermissions(), |
|
131 | 5 | 'lastmodified' => $this->lastModified(), |
|
132 | 5 | 'owner' => $this->getOwner(), |
|
133 | 5 | 'parent' => $this->getParent()->getId(), |
|
134 | 5 | 'backend' => $this->getBackend()->name, |
|
135 | 5 | ); |
|
136 | } |
||
137 | |||
138 | /** |
||
139 | * Get a unique key combined of backend name, address book id and contact id. |
||
140 | * |
||
141 | * @return string |
||
142 | */ |
||
143 | public function combinedKey() { |
||
146 | |||
147 | /** |
||
148 | * @return string|null |
||
149 | */ |
||
150 | 5 | public function getOwner() { |
|
151 | 5 | return isset($this->props['owner']) |
|
152 | 5 | ? $this->props['owner'] |
|
153 | 5 | : $this->getParent()->getOwner(); |
|
154 | } |
||
155 | |||
156 | /** |
||
157 | * @return string|null |
||
158 | */ |
||
159 | 20 | public function getId() { |
|
162 | |||
163 | /** |
||
164 | * @return string|null |
||
165 | */ |
||
166 | 9 | public function getDisplayName() { |
|
174 | |||
175 | /** |
||
176 | * @return string|null |
||
177 | */ |
||
178 | public function getURI() { |
||
181 | |||
182 | /** |
||
183 | * @return string |
||
184 | * TODO: Cache result. |
||
185 | */ |
||
186 | public function getETag() { |
||
190 | |||
191 | /** |
||
192 | * If this object is part of a collection return a reference |
||
193 | * to the parent object, otherwise return null. |
||
194 | * @return IPIMObject|null |
||
195 | */ |
||
196 | 19 | public function getParent() { |
|
199 | |||
200 | 8 | public function getBackend() { |
|
203 | |||
204 | /** CRUDS permissions (Create, Read, Update, Delete, Share) |
||
205 | * |
||
206 | * @return integer |
||
207 | */ |
||
208 | 13 | public function getPermissions() { |
|
209 | 13 | return isset($this->props['permissions']) |
|
210 | 13 | ? $this->props['permissions'] |
|
211 | 13 | : $this->getParent()->getPermissions(); |
|
212 | } |
||
213 | |||
214 | /** |
||
215 | * @param integer $permission |
||
216 | * @return integer |
||
217 | */ |
||
218 | 12 | public function hasPermission($permission) { |
|
221 | |||
222 | /** |
||
223 | * Save the address book data to backend |
||
224 | * FIXME |
||
225 | * |
||
226 | * @param array $data |
||
227 | * @return bool |
||
228 | */ |
||
229 | /* public function update(array $data) { |
||
230 | |||
231 | foreach($data as $key => $value) { |
||
232 | switch($key) { |
||
233 | case 'displayname': |
||
234 | $this->addressBookInfo['displayname'] = $value; |
||
235 | break; |
||
236 | case 'description': |
||
237 | $this->addressBookInfo['description'] = $value; |
||
238 | break; |
||
239 | } |
||
240 | } |
||
241 | return $this->props['backend']->updateContact( |
||
242 | $this->getParent()->getId(), |
||
243 | $this->getId(), |
||
244 | $this |
||
245 | ); |
||
246 | } |
||
247 | */ |
||
248 | /** |
||
249 | * Delete the data from backend |
||
250 | * |
||
251 | * FIXME: Should be removed as it could leave the parent with a dataless object. |
||
252 | * |
||
253 | * @return bool |
||
254 | */ |
||
255 | 1 | public function delete() { |
|
256 | 1 | if (!$this->hasPermission(\OCP\Constants::PERMISSION_DELETE)) { |
|
257 | throw new Exception(self::$l10n->t('You do not have permissions to delete this contact'), 403); |
||
258 | } |
||
259 | 1 | return $this->props['backend']->deleteContact( |
|
260 | 1 | $this->getParent()->getId(), |
|
261 | 1 | $this->getId() |
|
262 | 1 | ); |
|
263 | } |
||
264 | |||
265 | /** |
||
266 | * Save the contact data to backend |
||
267 | * |
||
268 | * @return bool |
||
269 | */ |
||
270 | 4 | public function save($force = false) { |
|
271 | 4 | if (!$this->hasPermission(\OCP\Constants::PERMISSION_UPDATE)) { |
|
272 | throw new Exception(self::$l10n->t('You do not have permissions to update this contact'), 403); |
||
273 | } |
||
274 | 4 | if ($this->isSaved() && !$force) { |
|
275 | \OCP\Util::writeLog('contacts', __METHOD__.' Already saved: ' . print_r($this->props, true), \OCP\Util::DEBUG); |
||
276 | return true; |
||
277 | } |
||
278 | |||
279 | 4 | if (isset($this->FN)) { |
|
280 | 4 | $this->props['displayname'] = (string)$this->FN; |
|
281 | 4 | } |
|
282 | |||
283 | 4 | if ($this->getId()) { |
|
284 | 1 | if (!$this->getBackend()->hasContactMethodFor(\OCP\Constants::PERMISSION_UPDATE)) { |
|
285 | throw new Exception(self::$l10n->t('The backend for this contact does not support updating it'), 501); |
||
286 | } |
||
287 | 1 | if ($this->getBackend() |
|
288 | 1 | ->updateContact( |
|
289 | 1 | $this->getParent()->getId(), |
|
290 | 1 | $this->getId(), |
|
291 | $this |
||
292 | 1 | ) |
|
293 | 1 | ) { |
|
294 | 1 | $this->props['lastmodified'] = time(); |
|
295 | 1 | $this->setSaved(true); |
|
296 | 1 | return true; |
|
297 | } else { |
||
298 | return false; |
||
299 | } |
||
300 | } else { |
||
301 | 3 | if (!$this->getBackend()->hasContactMethodFor(\OCP\Constants::PERMISSION_CREATE)) { |
|
302 | throw new Exception(self::$l10n->t('This backend does not support adding contacts'), 501); |
||
303 | } |
||
304 | 3 | $this->props['id'] = $this->getBackend()->createContact( |
|
305 | 3 | $this->getParent()->getId(), $this |
|
306 | 3 | ); |
|
307 | 3 | $this->setSaved(true); |
|
308 | 3 | return $this->getId() !== false; |
|
309 | } |
||
310 | } |
||
311 | |||
312 | /** |
||
313 | * Get the data from the backend |
||
314 | * FIXME: Clean this up and make sure the logic is OK. |
||
315 | * |
||
316 | * @return bool |
||
317 | */ |
||
318 | 19 | public function retrieve() { |
|
319 | 18 | if ($this->isRetrieved()) { |
|
320 | //\OCP\Util::writeLog('contacts', __METHOD__. ' children', \OCP\Util::DEBUG); |
||
321 | 18 | return true; |
|
322 | } else { |
||
323 | 18 | $data = null; |
|
324 | 18 | if(isset($this->props['vcard']) |
|
325 | 18 | && $this->props['vcard'] instanceof VObject\VCard) { |
|
326 | foreach($this->props['vcard']->children() as $child) { |
||
327 | $this->add($child); |
||
328 | if($child->name === 'FN') { |
||
329 | $this->props['displayname'] |
||
330 | = strtr($child->getValue(), array('\,' => ',', '\;' => ';', '\\\\' => '\\')); |
||
331 | } |
||
332 | } |
||
333 | $this->setRetrieved(true); |
||
334 | $this->setSaved(true); |
||
335 | //$this->children = $this->props['vcard']->children(); |
||
336 | unset($this->props['vcard']); |
||
337 | return true; |
||
338 | 18 | } elseif (!isset($this->props['carddata'])) { |
|
339 | 16 | $result = $this->props['backend']->getContact( |
|
340 | 16 | $this->getParent()->getId(), |
|
341 | 16 | $this->getId() |
|
342 | 16 | ); |
|
343 | 16 | if ($result) { |
|
344 | 16 | if (isset($result['vcard']) |
|
345 | 16 | && $result['vcard'] instanceof VObject\VCard) { |
|
346 | foreach ($result['vcard']->children() as $child) { |
||
347 | $this->add($child); |
||
348 | } |
||
349 | $this->setRetrieved(true); |
||
350 | return true; |
||
351 | 16 | } elseif (isset($result['carddata'])) { |
|
352 | // Save internal values |
||
353 | 16 | $data = $result['carddata']; |
|
354 | 16 | $this->props['carddata'] = $result['carddata']; |
|
355 | 16 | $this->props['lastmodified'] = isset($result['lastmodified']) |
|
356 | 16 | ? $result['lastmodified'] |
|
357 | 16 | : null; |
|
358 | 16 | $this->props['displayname'] = $result['displayname']; |
|
359 | 16 | $this->props['permissions'] = $result['permissions']; |
|
360 | 16 | } else { |
|
361 | \OCP\Util::writeLog('contacts', __METHOD__ |
||
362 | . ' Could not get vcard or carddata: ' |
||
363 | . $this->getId() |
||
364 | . print_r($result, true), \OCP\Util::DEBUG); |
||
365 | return false; |
||
366 | } |
||
367 | 16 | } else { |
|
368 | \OCP\Util::writeLog('contacts', __METHOD__.' Error getting contact: ' . $this->getId(), \OCP\Util::DEBUG); |
||
369 | } |
||
370 | 18 | } elseif (isset($this->props['carddata'])) { |
|
371 | 2 | $data = $this->props['carddata']; |
|
372 | 2 | } |
|
373 | try { |
||
374 | 18 | $obj = \Sabre\VObject\Reader::read( |
|
375 | 18 | $data, |
|
376 | \Sabre\VObject\Reader::OPTION_IGNORE_INVALID_LINES |
||
377 | 18 | ); |
|
378 | 18 | if ($obj) { |
|
379 | 18 | foreach ($obj->children as $child) { |
|
380 | 18 | if($child->name === 'VERSION' || $child->name === 'PRODID') { |
|
381 | 18 | parent::__set($child->name, $child); |
|
382 | 19 | } else { |
|
383 | 18 | $this->add($child); |
|
384 | } |
||
385 | 18 | } |
|
386 | 18 | $this->setRetrieved(true); |
|
387 | 18 | $this->setSaved(true); |
|
388 | 18 | } else { |
|
389 | \OCP\Util::writeLog('contacts', __METHOD__.' Error reading: ' . print_r($data, true), \OCP\Util::DEBUG); |
||
390 | return false; |
||
391 | } |
||
392 | 18 | } catch (Exception $e) { |
|
393 | \OCP\Util::writeLog('contacts', __METHOD__ . |
||
394 | ' Error parsing carddata for: ' . $this->getId() . ' ' . $e->getMessage(), |
||
395 | \OCP\Util::ERROR); |
||
396 | return false; |
||
397 | } |
||
398 | } |
||
399 | 18 | return true; |
|
400 | } |
||
401 | |||
402 | /** |
||
403 | * Get the PHOTO or LOGO |
||
404 | * |
||
405 | * @return \OCP\Image|null |
||
406 | */ |
||
407 | public function getPhoto() { |
||
408 | $image = new \OCP\Image(); |
||
409 | |||
410 | if (isset($this->PHOTO)) { |
||
411 | $photo = $this->PHOTO; |
||
412 | } elseif (isset($this->LOGO)) { |
||
413 | $photo = $this->LOGO; |
||
414 | } else { |
||
415 | return null; |
||
416 | } |
||
417 | |||
418 | $photovalue = $photo->getValue(); |
||
419 | |||
420 | if ( $photo instanceof \Sabre\VObject\Property\Uri && substr($photovalue, 0, 5) === 'data:' ) { |
||
421 | $mimeType = substr($photovalue, 5, strpos($photovalue, ',')-5); |
||
422 | if (strpos($mimeType, ';')) { |
||
423 | $mimeType = substr($mimeType,0,strpos($mimeType, ';')); |
||
424 | } |
||
425 | |||
426 | $photovalue = substr($photovalue, strpos($photovalue,',')+1); |
||
427 | |||
428 | if ($image->loadFromBase64($photovalue)) { |
||
429 | return $image; |
||
430 | } |
||
431 | } elseif ($image->loadFromData($photovalue)) { |
||
432 | return $image; |
||
433 | } |
||
434 | |||
435 | return null; |
||
436 | } |
||
437 | |||
438 | /** |
||
439 | * Set the contact photo. |
||
440 | * |
||
441 | * @param \OCP\Image $photo |
||
442 | */ |
||
443 | public function setPhoto(\OCP\Image $photo) { |
||
444 | // For vCard 3.0 the type must be e.g. JPEG or PNG |
||
445 | // For version 4.0 the full mimetype should be used. |
||
446 | // https://tools.ietf.org/html/rfc2426#section-3.1.4 |
||
447 | View Code Duplication | if (strval($this->VERSION) === '4.0') { |
|
448 | $type = $photo->mimeType(); |
||
449 | } else { |
||
450 | 1 | $type = explode('/', $photo->mimeType()); |
|
451 | 1 | $type = strtoupper(array_pop($type)); |
|
452 | 1 | } |
|
453 | 1 | if (isset($this->PHOTO)) { |
|
454 | 1 | $this->remove('PHOTO'); |
|
455 | 1 | } |
|
456 | $this->add('PHOTO', $photo->data(), ['ENCODING' => 'b', 'TYPE' => $type]); |
||
457 | 1 | $this->setSaved(false); |
|
458 | 1 | ||
459 | return true; |
||
460 | } |
||
461 | |||
462 | /** |
||
463 | * Get a property index in the contact by the checksum of its serialized value |
||
464 | * |
||
465 | * @param string $checksum An 8 char m5d checksum. |
||
466 | * @return integer Property by reference |
||
467 | * @throws Exception with error code 404 if the property is not found. |
||
468 | */ |
||
469 | 2 | public function getPropertyIndexByChecksum($checksum) { |
|
470 | 2 | $this->retrieve(); |
|
471 | 2 | $idx = 0; |
|
472 | 2 | View Code Duplication | foreach ($this->children as $i => &$property) { |
473 | 1 | if (substr(md5($property->serialize()), 0, 8) == $checksum ) { |
|
474 | return $idx; |
||
475 | 2 | } |
|
476 | 1 | $idx += 1; |
|
477 | } |
||
478 | throw new Exception(self::$l10n->t('Property not found'), 404); |
||
479 | } |
||
480 | |||
481 | /** |
||
482 | * Get a property by the checksum of its serialized value |
||
483 | * |
||
484 | * @param string $checksum An 8 char m5d checksum. |
||
485 | * @return \Sabre\VObject\Property Property by reference |
||
486 | 1 | * @throws Exception with error code 404 if the property is not found. |
|
487 | 1 | */ |
|
488 | 1 | public function getPropertyByChecksum($checksum) { |
|
489 | 1 | $this->retrieve(); |
|
490 | 1 | View Code Duplication | foreach ($this->children as $i => &$property) { |
491 | if (substr(md5($property->serialize()), 0, 8) == $checksum ) { |
||
492 | return $property; |
||
493 | } |
||
494 | } |
||
495 | throw new Exception(self::$l10n->t('Property not found'), 404); |
||
496 | } |
||
497 | |||
498 | /** |
||
499 | * Delete a property by the checksum of its serialized value |
||
500 | * It is up to the caller to call ->save() |
||
501 | * |
||
502 | * @param string $checksum An 8 char m5d checksum. |
||
503 | 2 | * @throws @see getPropertyByChecksum |
|
504 | 2 | */ |
|
505 | public function unsetPropertyByChecksum($checksum) { |
||
506 | $idx = $this->getPropertyIndexByChecksum($checksum); |
||
507 | unset($this->children[$idx]); |
||
508 | 2 | $this->setSaved(false); |
|
509 | } |
||
510 | |||
511 | 1 | /** |
|
512 | 1 | * Set a property by the checksum of its serialized value |
|
513 | 1 | * It is up to the caller to call ->save() |
|
514 | 1 | * |
|
515 | 1 | * @param string $checksum An 8 char m5d checksum. |
|
516 | * @param string $name Property name |
||
517 | * @param mixed $value |
||
518 | * @param array $parameters |
||
519 | * @throws @see getPropertyByChecksum |
||
520 | * @return string new checksum |
||
521 | */ |
||
522 | public function setPropertyByChecksum($checksum, $name, $value, $parameters=array()) { |
||
523 | if ($checksum === 'new') { |
||
524 | $property = $this->createProperty($name); |
||
525 | $this->add($property); |
||
526 | } else { |
||
527 | $property = $this->getPropertyByChecksum($checksum); |
||
528 | } |
||
529 | switch ($name) { |
||
530 | case 'EMAIL': |
||
531 | case 'CLOUD': |
||
532 | $value = strtolower($value); |
||
533 | $property->setValue($value); |
||
534 | break; |
||
535 | case 'ADR': |
||
536 | if(is_array($value)) { |
||
537 | $property->setParts($value); |
||
538 | } else { |
||
539 | $property->setValue($value); |
||
540 | } |
||
541 | break; |
||
542 | case 'IMPP': |
||
543 | 1 | if (is_null($parameters) || !isset($parameters['X-SERVICE-TYPE'])) { |
|
544 | 1 | throw new \InvalidArgumentException(self::$l10n->t(' Missing IM parameter for: ') . $name. ' ' . $value, 412); |
|
545 | 1 | } |
|
546 | $serviceType = $parameters['X-SERVICE-TYPE']; |
||
547 | if (is_array($serviceType)) { |
||
548 | $serviceType = $serviceType[0]; |
||
549 | } |
||
550 | $impp = Utils\Properties::getIMOptions($serviceType); |
||
551 | if (is_null($impp)) { |
||
552 | throw new \UnexpectedValueException(self::$l10n->t('Unknown IM: ') . $serviceType, 415); |
||
553 | } |
||
554 | $value = $impp['protocol'] . ':' . $value; |
||
555 | $property->setValue($value); |
||
556 | break; |
||
557 | 3 | View Code Duplication | default: |
558 | \OCP\Util::writeLog('contacts', __METHOD__.' adding: '.$name. ' ' . $value, \OCP\Util::DEBUG); |
||
559 | $property->setValue($value); |
||
560 | 1 | break; |
|
561 | } |
||
562 | $this->setParameters($property, $parameters, true); |
||
563 | $this->setSaved(false); |
||
564 | return substr(md5($property->serialize()), 0, 8); |
||
565 | } |
||
566 | |||
567 | /** |
||
568 | * Set a property by the property name. |
||
569 | * It is up to the caller to call ->save() |
||
570 | * |
||
571 | * @param string $name Property name |
||
572 | * @param mixed $value |
||
573 | * @param array $parameters |
||
574 | * @return bool |
||
575 | 1 | */ |
|
576 | 1 | public function setPropertyByName($name, $value, $parameters=array()) { |
|
577 | 1 | // TODO: parameters are ignored for now. |
|
578 | switch ($name) { |
||
579 | case 'BDAY': |
||
580 | try { |
||
581 | $date = New \DateTime($value); |
||
582 | } catch(Exception $e) { |
||
583 | \OCP\Util::writeLog('contacts', |
||
584 | __METHOD__.' DateTime exception: ' . $e->getMessage(), |
||
585 | \OCP\Util::ERROR |
||
586 | ); |
||
587 | return false; |
||
588 | } |
||
589 | $value = $date->format('Y-m-d'); |
||
590 | $this->BDAY = $value; |
||
591 | $this->BDAY->add('VALUE', 'DATE'); |
||
592 | //\OCP\Util::writeLog('contacts', __METHOD__.' BDAY: '.$this->BDAY->serialize(), \OCP\Util::DEBUG); |
||
593 | break; |
||
594 | case 'CATEGORIES': |
||
595 | case 'N': |
||
596 | case 'ORG': |
||
597 | $property = $this->select($name); |
||
598 | if (count($property) === 0) { |
||
599 | $property = $this->createProperty($name); |
||
600 | $this->add($property); |
||
601 | 1 | View Code Duplication | } elseif (count($property) > 1) { |
602 | 1 | \OCP\Util::writeLog('contacts', |
|
603 | 1 | __METHOD__.' more than one property for ' . $name, |
|
604 | 3 | \OCP\Util::ERROR |
|
605 | 1 | ); |
|
606 | 3 | return false; |
|
607 | 1 | } else { |
|
608 | // select returns an array... |
||
609 | $property = array_shift($property); |
||
610 | 3 | } |
|
611 | 3 | if (($name === 'N') && !is_array($value)) { |
|
612 | 1 | $value = explode(';', (string)$value); |
|
613 | } |
||
614 | if (is_array($value)) { |
||
615 | $property->setParts($value); |
||
616 | } else { |
||
617 | $property->setValue($value); |
||
618 | } |
||
619 | break; |
||
620 | View Code Duplication | default: |
|
621 | \OCP\Util::writeLog('contacts', __METHOD__.' adding: '.$name. ' ' . $value, \OCP\Util::DEBUG); |
||
622 | $this->{$name} = $value; |
||
623 | break; |
||
624 | } |
||
625 | $this->setSaved(false); |
||
626 | return true; |
||
627 | } |
||
628 | |||
629 | protected function setParameters($property, $parameters, $reset = false) { |
||
630 | if (!$parameters) { |
||
631 | return; |
||
632 | } |
||
633 | |||
634 | if ($reset) { |
||
635 | $property->parameters = array(); |
||
636 | } |
||
637 | //debug('Setting parameters: ' . print_r($parameters, true)); |
||
638 | foreach ($parameters as $key => $parameter) { |
||
639 | //debug('Adding parameter: ' . $key); |
||
640 | if (is_array($parameter)) { |
||
641 | foreach ($parameter as $val) { |
||
642 | if (is_array($val)) { |
||
643 | foreach ($val as $val2) { |
||
644 | if (trim($key) && trim($val2)) { |
||
645 | //debug('Adding parameter: '.$key.'=>'.print_r($val2, true)); |
||
646 | 5 | $property->add($key, strip_tags($val2)); |
|
647 | 5 | } |
|
648 | } |
||
649 | } else { |
||
650 | 5 | if (trim($key) && trim($val)) { |
|
651 | 5 | //debug('Adding parameter: '.$key.'=>'.print_r($val, true)); |
|
652 | 5 | $property->add($key, strip_tags($val)); |
|
653 | } |
||
654 | } |
||
655 | } |
||
656 | } else { |
||
657 | if (trim($key) && trim($parameter)) { |
||
658 | //debug('Adding parameter: '.$key.'=>'.print_r($parameter, true)); |
||
659 | $property->add($key, strip_tags($parameter)); |
||
660 | } |
||
661 | } |
||
662 | } |
||
663 | } |
||
664 | |||
665 | public function lastModified() { |
||
666 | if (!isset($this->props['lastmodified']) && !$this->isRetrieved()) { |
||
667 | $this->retrieve(); |
||
668 | } |
||
669 | return isset($this->props['lastmodified']) |
||
670 | ? $this->props['lastmodified'] |
||
671 | : null; |
||
672 | } |
||
673 | |||
674 | /** |
||
675 | * Merge in data from a multi-dimentional array |
||
676 | * |
||
677 | * NOTE: The data has actually already been merged client side! |
||
678 | * NOTE: The only properties coming from the web client are the ones |
||
679 | * defined in \OCA\Contacts\Utils\Properties::$indexProperties and |
||
680 | * UID is skipped for obvious reasons, and PHOTO is currently not updated. |
||
681 | * The data array has this structure: |
||
682 | * |
||
683 | * array( |
||
684 | * 'EMAIL' => array(array('value' => '[email protected]', 'parameters' = array('TYPE' => array('HOME','VOICE')))) |
||
685 | * ); |
||
686 | * @param array $data |
||
687 | * @return bool |
||
688 | */ |
||
689 | public function mergeFromArray(array $data) { |
||
690 | foreach ($data as $name => $properties) { |
||
691 | if (in_array($name, array('PHOTO', 'UID'))) { |
||
692 | continue; |
||
693 | } |
||
694 | View Code Duplication | if (!is_array($properties)) { |
|
695 | \OCP\Util::writeLog('contacts', __METHOD__.' not an array?: ' .$name. ' '.print_r($properties, true), \OCP\Util::DEBUG); |
||
696 | } |
||
697 | if (in_array($name, Utils\Properties::$multiProperties)) { |
||
698 | 8 | unset($this->{$name}); |
|
699 | 8 | } |
|
700 | foreach ($properties as $parray) { |
||
701 | \OCP\Util::writeLog('contacts', __METHOD__.' adding: ' .$name. ' '.print_r($parray['value'], true) . ' ' . print_r($parray['parameters'], true), \OCP\Util::DEBUG); |
||
702 | if (in_array($name, Utils\Properties::$multiProperties)) { |
||
703 | 8 | // TODO: wrap in try/catch, check return value |
|
704 | $this->setPropertyByChecksum('new', $name, $parray['value'], $parray['parameters']); |
||
705 | } else { |
||
706 | 7 | // TODO: Check return value |
|
707 | 7 | if (!isset($this->{$name})) { |
|
708 | $this->setPropertyByName($name, $parray['value'], $parray['parameters']); |
||
709 | } |
||
710 | } |
||
711 | 7 | } |
|
712 | } |
||
713 | $this->setSaved(false); |
||
714 | 18 | return true; |
|
715 | 18 | } |
|
716 | 16 | ||
717 | 16 | public function __get($key) { |
|
718 | 18 | if (!$this->isRetrieved()) { |
|
719 | 18 | $this->retrieve(); |
|
720 | 18 | } |
|
721 | 18 | ||
722 | 18 | return parent::__get($key); |
|
723 | 18 | } |
|
724 | |||
725 | public function __isset($key) { |
||
726 | if (!$this->isRetrieved()) { |
||
727 | $this->retrieve(); |
||
728 | } |
||
729 | |||
730 | return parent::__isset($key); |
||
731 | } |
||
732 | |||
733 | public function __set($key, $value) { |
||
734 | if (!$this->isRetrieved()) { |
||
735 | $this->retrieve(); |
||
736 | } |
||
737 | parent::__set($key, $value); |
||
738 | if ($key === 'FN') { |
||
739 | $this->props['displayname'] = $value; |
||
740 | } |
||
741 | $this->setSaved(false); |
||
742 | } |
||
743 | |||
744 | public function __unset($key) { |
||
745 | if (!$this->isRetrieved()) { |
||
746 | 19 | $this->retrieve(); |
|
747 | 19 | } |
|
748 | 19 | parent::__unset($key); |
|
749 | if ($key === 'PHOTO') { |
||
750 | 19 | Properties::cacheThumbnail( |
|
751 | 19 | $this->getBackend()->name, |
|
752 | $this->getParent()->getId(), |
||
753 | $this->getId(), |
||
754 | 19 | null, |
|
755 | 19 | $this, |
|
756 | 19 | array('remove' => true) |
|
757 | ); |
||
758 | 5 | } |
|
759 | 5 | $this->setSaved(false); |
|
760 | } |
||
761 | |||
762 | /** |
||
763 | * @param boolean $state |
||
764 | */ |
||
765 | public function setRetrieved($state) { |
||
766 | $this->props['retrieved'] = $state; |
||
767 | } |
||
768 | |||
769 | public function isRetrieved() { |
||
770 | return $this->props['retrieved']; |
||
771 | } |
||
772 | |||
773 | public function setSaved($state = true) { |
||
776 | |||
777 | public function isSaved() { |
||
780 | |||
781 | /** |
||
782 | * Generate an event to show in the calendar |
||
783 | * |
||
784 | * @return \Sabre\VObject\Component\VCalendar|null |
||
785 | */ |
||
786 | public function getBirthdayEvent() { |
||
787 | if (!isset($this->BDAY)) { |
||
788 | return null; |
||
789 | } |
||
790 | $birthday = $this->BDAY; |
||
791 | if ((string)$birthday) { |
||
792 | $title = str_replace('{name}', |
||
793 | strtr((string)$this->FN, array('\,' => ',', '\;' => ';')), |
||
794 | App::$l10n->t('{name}\'s Birthday') |
||
795 | ); |
||
796 | try { |
||
797 | $date = new \DateTime($birthday); |
||
798 | } catch(Exception $e) { |
||
799 | return null; |
||
800 | } |
||
801 | $vCal = new \Sabre\VObject\Component\VCalendar(); |
||
802 | $vCal->VERSION = '2.0'; |
||
803 | $vEvent = $vCal->createComponent('VEVENT'); |
||
831 | |||
832 | } |
||
833 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..