Total Complexity | 84 |
Total Lines | 469 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like BirthdayService 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.
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 BirthdayService, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
55 | class BirthdayService { |
||
56 | public const BIRTHDAY_CALENDAR_URI = 'contact_birthdays'; |
||
57 | public const EXCLUDE_FROM_BIRTHDAY_CALENDAR_PROPERTY_NAME = 'X-NC-EXCLUDE-FROM-BIRTHDAY-CALENDAR'; |
||
58 | |||
59 | private GroupPrincipalBackend $principalBackend; |
||
60 | private CalDavBackend $calDavBackEnd; |
||
61 | private CardDavBackend $cardDavBackEnd; |
||
62 | private IConfig $config; |
||
63 | private IDBConnection $dbConnection; |
||
64 | private IL10N $l10n; |
||
65 | |||
66 | /** |
||
67 | * BirthdayService constructor. |
||
68 | */ |
||
69 | public function __construct(CalDavBackend $calDavBackEnd, |
||
70 | CardDavBackend $cardDavBackEnd, |
||
71 | GroupPrincipalBackend $principalBackend, |
||
72 | IConfig $config, |
||
73 | IDBConnection $dbConnection, |
||
74 | IL10N $l10n) { |
||
75 | $this->calDavBackEnd = $calDavBackEnd; |
||
76 | $this->cardDavBackEnd = $cardDavBackEnd; |
||
77 | $this->principalBackend = $principalBackend; |
||
78 | $this->config = $config; |
||
79 | $this->dbConnection = $dbConnection; |
||
80 | $this->l10n = $l10n; |
||
81 | } |
||
82 | |||
83 | public function onCardChanged(int $addressBookId, |
||
115 | } |
||
116 | } |
||
117 | } |
||
118 | |||
119 | public function onCardDeleted(int $addressBookId, |
||
137 | } |
||
138 | } |
||
139 | } |
||
140 | |||
141 | /** |
||
142 | * @throws \Sabre\DAV\Exception\BadRequest |
||
143 | */ |
||
144 | public function ensureCalendarExists(string $principal): ?array { |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * @param $cardData |
||
160 | * @param $dateField |
||
161 | * @param $postfix |
||
162 | * @param $reminderOffset |
||
163 | * @return VCalendar|null |
||
164 | * @throws InvalidDataException |
||
165 | */ |
||
166 | public function buildDateFromContact(string $cardData, |
||
285 | } |
||
286 | |||
287 | /** |
||
288 | * @param string $user |
||
289 | */ |
||
290 | public function resetForUser(string $user):void { |
||
291 | $principal = 'principals/users/'.$user; |
||
292 | $calendar = $this->calDavBackEnd->getCalendarByUri($principal, self::BIRTHDAY_CALENDAR_URI); |
||
293 | if (!$calendar) { |
||
294 | return; // The user's birthday calendar doesn't exist, no need to purge it |
||
295 | } |
||
296 | $calendarObjects = $this->calDavBackEnd->getCalendarObjects($calendar['id'], CalDavBackend::CALENDAR_TYPE_CALENDAR); |
||
297 | |||
298 | foreach ($calendarObjects as $calendarObject) { |
||
299 | $this->calDavBackEnd->deleteCalendarObject($calendar['id'], $calendarObject['uri'], CalDavBackend::CALENDAR_TYPE_CALENDAR, true); |
||
300 | } |
||
301 | } |
||
302 | |||
303 | /** |
||
304 | * @param string $user |
||
305 | * @throws \Sabre\DAV\Exception\BadRequest |
||
306 | */ |
||
307 | public function syncUser(string $user):void { |
||
308 | $principal = 'principals/users/'.$user; |
||
309 | $this->ensureCalendarExists($principal); |
||
310 | $books = $this->cardDavBackEnd->getAddressBooksForUser($principal); |
||
311 | foreach ($books as $book) { |
||
312 | $cards = $this->cardDavBackEnd->getCards($book['id']); |
||
313 | foreach ($cards as $card) { |
||
314 | $this->onCardChanged((int) $book['id'], $card['uri'], $card['carddata']); |
||
315 | } |
||
316 | } |
||
317 | } |
||
318 | |||
319 | /** |
||
320 | * @param string $existingCalendarData |
||
321 | * @param VCalendar $newCalendarData |
||
322 | * @return bool |
||
323 | */ |
||
324 | public function birthdayEvenChanged(string $existingCalendarData, |
||
325 | VCalendar $newCalendarData):bool { |
||
326 | try { |
||
327 | $existingBirthday = Reader::read($existingCalendarData); |
||
328 | } catch (Exception $ex) { |
||
329 | return true; |
||
330 | } |
||
331 | |||
332 | return ( |
||
333 | $newCalendarData->VEVENT->DTSTART->getValue() !== $existingBirthday->VEVENT->DTSTART->getValue() || |
||
334 | $newCalendarData->VEVENT->SUMMARY->getValue() !== $existingBirthday->VEVENT->SUMMARY->getValue() |
||
335 | ); |
||
336 | } |
||
337 | |||
338 | /** |
||
339 | * @param integer $addressBookId |
||
340 | * @return mixed |
||
341 | */ |
||
342 | protected function getAllAffectedPrincipals(int $addressBookId) { |
||
343 | $targetPrincipals = []; |
||
344 | $shares = $this->cardDavBackEnd->getShares($addressBookId); |
||
345 | foreach ($shares as $share) { |
||
346 | if ($share['{http://owncloud.org/ns}group-share']) { |
||
347 | $users = $this->principalBackend->getGroupMemberSet($share['{http://owncloud.org/ns}principal']); |
||
348 | foreach ($users as $user) { |
||
349 | $targetPrincipals[] = $user['uri']; |
||
350 | } |
||
351 | } else { |
||
352 | $targetPrincipals[] = $share['{http://owncloud.org/ns}principal']; |
||
353 | } |
||
354 | } |
||
355 | return array_values(array_unique($targetPrincipals, SORT_STRING)); |
||
356 | } |
||
357 | |||
358 | /** |
||
359 | * @param string $cardUri |
||
360 | * @param string $cardData |
||
361 | * @param array $book |
||
362 | * @param int $calendarId |
||
363 | * @param array $type |
||
364 | * @param string $reminderOffset |
||
365 | * @throws InvalidDataException |
||
366 | * @throws \Sabre\DAV\Exception\BadRequest |
||
367 | */ |
||
368 | private function updateCalendar(string $cardUri, |
||
369 | string $cardData, |
||
370 | array $book, |
||
371 | int $calendarId, |
||
372 | array $type, |
||
373 | ?string $reminderOffset):void { |
||
374 | $objectUri = $book['uri'] . '-' . $cardUri . $type['postfix'] . '.ics'; |
||
375 | $calendarData = $this->buildDateFromContact($cardData, $type['field'], $type['postfix'], $reminderOffset); |
||
376 | $existing = $this->calDavBackEnd->getCalendarObject($calendarId, $objectUri); |
||
377 | if ($calendarData === null) { |
||
378 | if ($existing !== null) { |
||
379 | $this->calDavBackEnd->deleteCalendarObject($calendarId, $objectUri, CalDavBackend::CALENDAR_TYPE_CALENDAR, true); |
||
380 | } |
||
381 | } else { |
||
382 | if ($existing === null) { |
||
383 | // not found by URI, but maybe by UID |
||
384 | // happens when a contact with birthday is moved to a different address book |
||
385 | $calendarInfo = $this->calDavBackEnd->getCalendarById($calendarId); |
||
386 | $extraData = $this->calDavBackEnd->getDenormalizedData($calendarData->serialize()); |
||
387 | |||
388 | if ($calendarInfo && array_key_exists('principaluri', $calendarInfo)) { |
||
389 | $existing2path = $this->calDavBackEnd->getCalendarObjectByUID($calendarInfo['principaluri'], $extraData['uid']); |
||
390 | if ($existing2path !== null && array_key_exists('uri', $calendarInfo)) { |
||
391 | // delete the old birthday entry first so that we do not get duplicate UIDs |
||
392 | $existing2objectUri = substr($existing2path, strlen($calendarInfo['uri']) + 1); |
||
393 | $this->calDavBackEnd->deleteCalendarObject($calendarId, $existing2objectUri, CalDavBackend::CALENDAR_TYPE_CALENDAR, true); |
||
394 | } |
||
395 | } |
||
396 | |||
397 | $this->calDavBackEnd->createCalendarObject($calendarId, $objectUri, $calendarData->serialize()); |
||
398 | } else { |
||
399 | if ($this->birthdayEvenChanged($existing['calendardata'], $calendarData)) { |
||
400 | $this->calDavBackEnd->updateCalendarObject($calendarId, $objectUri, $calendarData->serialize()); |
||
401 | } |
||
402 | } |
||
403 | } |
||
404 | } |
||
405 | |||
406 | /** |
||
407 | * checks if the admin opted-out of birthday calendars |
||
408 | * |
||
409 | * @return bool |
||
410 | */ |
||
411 | private function isGloballyEnabled():bool { |
||
412 | return $this->config->getAppValue('dav', 'generateBirthdayCalendar', 'yes') === 'yes'; |
||
413 | } |
||
414 | |||
415 | /** |
||
416 | * Extracts the userId part of a principal |
||
417 | * |
||
418 | * @param string $userPrincipal |
||
419 | * @return string|null |
||
420 | */ |
||
421 | private function principalToUserId(string $userPrincipal):?string { |
||
422 | if (substr($userPrincipal, 0, 17) === 'principals/users/') { |
||
423 | return substr($userPrincipal, 17); |
||
424 | } |
||
425 | return null; |
||
426 | } |
||
427 | |||
428 | /** |
||
429 | * Checks if the user opted-out of birthday calendars |
||
430 | * |
||
431 | * @param string $userPrincipal The user principal to check for |
||
432 | * @return bool |
||
433 | */ |
||
434 | private function isUserEnabled(string $userPrincipal):bool { |
||
443 | } |
||
444 | |||
445 | /** |
||
446 | * Get the reminder offset value for a user. This is a duration string (e.g. |
||
447 | * PT9H) or null if no reminder is wanted. |
||
448 | * |
||
449 | * @param string $userPrincipal |
||
450 | * @return string|null |
||
451 | */ |
||
452 | private function getReminderOffsetForUser(string $userPrincipal):?string { |
||
460 | } |
||
461 | |||
462 | /** |
||
463 | * Formats title of Birthday event |
||
464 | * |
||
465 | * @param string $field Field name like BDAY, ANNIVERSARY, ... |
||
466 | * @param string $name Name of contact |
||
467 | * @param int|null $year Year of birth, anniversary, ... |
||
468 | * @param bool $supports4Byte Whether or not the database supports 4 byte chars |
||
469 | * @return string The formatted title |
||
470 | */ |
||
471 | private function formatTitle(string $field, |
||
524 | } |
||
525 | } |
||
526 | } |
||
528 |
This check looks for function or method calls that always return null and whose return value is assigned to a variable.
The method
getObject()
can return nothing but null, so it makes no sense to assign that value to a variable.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.