1<?php 2 3namespace Sabre\VObject; 4 5use Sabre\VObject\Component\VCalendar; 6 7/** 8 * This class generates birthday calendars. 9 * 10 * @copyright Copyright (C) fruux GmbH (https://fruux.com/) 11 * @author Dominik Tobschall (http://tobschall.de/) 12 * @license http://sabre.io/license/ Modified BSD License 13 */ 14class BirthdayCalendarGenerator 15{ 16 /** 17 * Input objects. 18 * 19 * @var array 20 */ 21 protected $objects = []; 22 23 /** 24 * Default year. 25 * Used for dates without a year. 26 */ 27 const DEFAULT_YEAR = 2000; 28 29 /** 30 * Output format for the SUMMARY. 31 * 32 * @var string 33 */ 34 protected $format = '%1$s\'s Birthday'; 35 36 /** 37 * Creates the generator. 38 * 39 * Check the setTimeRange and setObjects methods for details about the 40 * arguments. 41 * 42 * @param mixed $objects 43 */ 44 public function __construct($objects = null) 45 { 46 if ($objects) { 47 $this->setObjects($objects); 48 } 49 } 50 51 /** 52 * Sets the input objects. 53 * 54 * You must either supply a vCard as a string or as a Component/VCard object. 55 * It's also possible to supply an array of strings or objects. 56 * 57 * @param mixed $objects 58 */ 59 public function setObjects($objects) 60 { 61 if (!is_array($objects)) { 62 $objects = [$objects]; 63 } 64 65 $this->objects = []; 66 foreach ($objects as $object) { 67 if (is_string($object)) { 68 $vObj = Reader::read($object); 69 if (!$vObj instanceof Component\VCard) { 70 throw new \InvalidArgumentException('String could not be parsed as \\Sabre\\VObject\\Component\\VCard by setObjects'); 71 } 72 73 $this->objects[] = $vObj; 74 } elseif ($object instanceof Component\VCard) { 75 $this->objects[] = $object; 76 } else { 77 throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component\\VCard arguments to setObjects'); 78 } 79 } 80 } 81 82 /** 83 * Sets the output format for the SUMMARY. 84 * 85 * @param string $format 86 */ 87 public function setFormat($format) 88 { 89 $this->format = $format; 90 } 91 92 /** 93 * Parses the input data and returns a VCALENDAR. 94 * 95 * @return Component/VCalendar 96 */ 97 public function getResult() 98 { 99 $calendar = new VCalendar(); 100 101 foreach ($this->objects as $object) { 102 // Skip if there is no BDAY property. 103 if (!$object->select('BDAY')) { 104 continue; 105 } 106 107 // We've seen clients (ez-vcard) putting "BDAY:" properties 108 // without a value into vCards. If we come across those, we'll 109 // skip them. 110 if (empty($object->BDAY->getValue())) { 111 continue; 112 } 113 114 // We're always converting to vCard 4.0 so we can rely on the 115 // VCardConverter handling the X-APPLE-OMIT-YEAR property for us. 116 $object = $object->convert(Document::VCARD40); 117 118 // Skip if the card has no FN property. 119 if (!isset($object->FN)) { 120 continue; 121 } 122 123 // Skip if the BDAY property is not of the right type. 124 if (!$object->BDAY instanceof Property\VCard\DateAndOrTime) { 125 continue; 126 } 127 128 // Skip if we can't parse the BDAY value. 129 try { 130 $dateParts = DateTimeParser::parseVCardDateTime($object->BDAY->getValue()); 131 } catch (InvalidDataException $e) { 132 continue; 133 } 134 135 // Set a year if it's not set. 136 $unknownYear = false; 137 138 if (!$dateParts['year']) { 139 $object->BDAY = self::DEFAULT_YEAR.'-'.$dateParts['month'].'-'.$dateParts['date']; 140 141 $unknownYear = true; 142 } 143 144 // Create event. 145 $event = $calendar->add('VEVENT', [ 146 'SUMMARY' => sprintf($this->format, $object->FN->getValue()), 147 'DTSTART' => new \DateTime($object->BDAY->getValue()), 148 'RRULE' => 'FREQ=YEARLY', 149 'TRANSP' => 'TRANSPARENT', 150 ]); 151 152 // add VALUE=date 153 $event->DTSTART['VALUE'] = 'DATE'; 154 155 // Add X-SABRE-BDAY property. 156 if ($unknownYear) { 157 $event->add('X-SABRE-BDAY', 'BDAY', [ 158 'X-SABRE-VCARD-UID' => $object->UID->getValue(), 159 'X-SABRE-VCARD-FN' => $object->FN->getValue(), 160 'X-SABRE-OMIT-YEAR' => self::DEFAULT_YEAR, 161 ]); 162 } else { 163 $event->add('X-SABRE-BDAY', 'BDAY', [ 164 'X-SABRE-VCARD-UID' => $object->UID->getValue(), 165 'X-SABRE-VCARD-FN' => $object->FN->getValue(), 166 ]); 167 } 168 } 169 170 return $calendar; 171 } 172} 173