/** * (non-PHPdoc) * @see Addressbook_Convert_Contact_VCard_Abstract::_toTine20ModelParseTel() */ protected function _toTine20ModelParseTel(&$data, \Sabre\VObject\Property $property) { if (!isset($property['TYPE'])) { // CardDAVSync sends OTHER just as TEL:12345678 without any TYPE $data['tel_other'] = $property->getValue(); } parent::_toTine20ModelParseTel($data, $property); }
/** * (non-PHPdoc) * @see Addressbook_Convert_Contact_VCard_Abstract::_toTine20ModelParseEmail() */ protected function _toTine20ModelParseEmail(&$data, \Sabre\VObject\Property $property, \Sabre\VObject\Component\VCard $vcard) { if ($vcard->{'X-OUTLOOK-EMAIL-2'} && $vcard->{'X-OUTLOOK-EMAIL-2'}->getValue() == $property->getValue()) { $data['email_home'] = $property->getValue(); } elseif ($vcard->{'X-OUTLOOK-EMAIL-3'} && $vcard->{'X-OUTLOOK-EMAIL-3'}->getValue() == $property->getValue()) { // we don't map email3 } else { $data['email'] = $property->getValue(); } }
public function testGroupProperty() { $arr = array('Home', 'work', 'Friends, Family'); $property = \Sabre\VObject\Property::create('CATEGORIES'); $property->setParts($arr); // Test parsing and serializing $this->assertEquals('Home,work,Friends\\, Family', $property->value); $this->assertEquals('CATEGORIES:Home,work,Friends\\, Family' . "\r\n", $property->serialize()); $this->assertEquals(3, count($property->getParts())); // Test add $property->addGroup('Coworkers'); $this->assertTrue($property->hasGroup('coworkers')); $this->assertEquals(4, count($property->getParts())); $this->assertEquals('Home,work,Friends\\, Family,Coworkers', $property->value); // Test remove $this->assertTrue($property->hasGroup('Friends, fAmIlY')); $property->removeGroup('Friends, fAmIlY'); $this->assertEquals(3, count($property->getParts())); $parts = $property->getParts(); $this->assertEquals('Coworkers', $parts[2]); // Test rename $property->renameGroup('work', 'Work'); $parts = $property->getParts(); $this->assertEquals('Work', $parts[1]); //$this->assertEquals(true, false); }
/** * Hydrate data from a XML subtree, as it would appear in a xCard or xCal * object. * * @param array $value * * @return void */ function setXmlValue(array $value) { $value = array_map(function ($value) { return 'true' === $value; }, $value); parent::setXmlValue($value); }
/** * Sets the json value, as it would appear in a jCard or jCal object. * * The value must always be an array. * * @param array $value * @return void */ public function setJsonValue(array $value) { $value = array_map(function ($item) { return strtr(implode('/', $item), array(':' => '', '-' => '')); }, $value); parent::setJsonValue($value); }
/** * (non-PHPdoc) * @see Addressbook_Convert_Contact_VCard_Abstract::_toTine20ModelParseTel() */ protected function _toTine20ModelParseTel(&$data, \Sabre\VObject\Property $property) { $telField = null; if (isset($property['TYPE'])) { // CELL if ($property['TYPE']->has('cell') && $property['TYPE']->has('voice') && !$property['TYPE']->has('iphone')) { $telField = 'tel_cell'; } elseif ($property['TYPE']->has('cell') && $property['TYPE']->has('iphone')) { $telField = 'tel_cell_private'; } } if (!empty($telField)) { $data[$telField] = $property->getValue(); } else { parent::_toTine20ModelParseTel($data, $property); } }
/** * (non-PHPdoc) * @see Addressbook_Convert_Contact_VCard_Abstract::_toTine20ModelParseEmail() */ protected function _toTine20ModelParseEmail(&$data, \Sabre\VObject\Property $property, \Sabre\VObject\Component\VCard $vcard) { $type = null; if ($property['TYPE']) { if ($property['TYPE']->has('pref')) { $type = 'work'; } } switch ($type) { case 'work': $data['email'] = $property->getValue(); break; default: $data['email_home'] = $property->getValue(); break; } }
/** * Create online calendar for user * * @Route("/{username}.ics") * * @param string $username User to create the calendar for * @return Symfony\Component\HttpFoundation\Response */ public function calendarAction($username) { $user = $this->get('user_provider')->loadUserByUsername($username); $om = $this->getObjectManager('VIB\\FliesBundle\\Entity\\Vial'); $calendar = VObject\Component::create('VCALENDAR'); $calendar->VERSION = '2.0'; $field = 'X-WR-CALNAME'; $calendar->{$field} = $user->getShortName() . '\'s flywork'; $stockDates = $om->getRepository('VIB\\FliesBundle\\Entity\\StockVial')->getFlipDates($user); foreach ($stockDates as $stockDate) { $event = VObject\Component::create('VEVENT'); $calendar->add($event); $event->SUMMARY = 'Transfer stocks'; $dtstart = VObject\Property::create('DTSTART'); $dtstart->setDateTime($stockDate, VObject\Property\DateTime::DATE); $event->DTSTART = $dtstart; $alarm = VObject\Component::create('VALARM'); $event->add($alarm); $alarm->TRIGGER = 'PT8H'; $alarm->ACTION = 'DISPLAY'; } $crossDates = $om->getRepository('VIB\\FliesBundle\\Entity\\CrossVial')->getFlipDates($user); foreach ($crossDates as $crossDate) { $crossDates[] = $crossDate; $event = VObject\Component::create('VEVENT'); $calendar->add($event); $event->SUMMARY = 'Check crosses'; $dtstart = VObject\Property::create('DTSTART'); $dtstart->setDateTime($crossDate, VObject\Property\DateTime::DATE); $event->DTSTART = $dtstart; $alarm = VObject\Component::create('VALARM'); $event->add($alarm); $alarm->TRIGGER = 'PT8H'; $alarm->ACTION = 'DISPLAY'; } return new Response($calendar->serialize(), 200, array('Content-Type' => 'text/calendar; charset=utf-8', 'Content-Disposition' => 'inline; filename="calendar.ics"')); }
/** * Validates the node for correctness. * * The following options are supported: * - Node::REPAIR - If something is broken, and automatic repair may * be attempted. * * An array is returned with warnings. * * Every item in the array has the following properties: * * level - (number between 1 and 3 with severity information) * * message - (human readable message) * * node - (reference to the offending node) * * @param int $options * @return array */ public function validate($options = 0) { $warnings = parent::validate($options); if (isset($this->minimumPropertyValues[$this->name])) { $minimum = $this->minimumPropertyValues[$this->name]; $parts = $this->getParts(); if (count($parts) < $minimum) { $warnings[] = array('level' => 1, 'message' => 'This property must have at least ' . $minimum . ' components. It only has ' . count($parts), 'node' => $this); if ($options & self::REPAIR) { $parts = array_pad($parts, $minimum, ''); $this->setParts($parts); } } } return $warnings; }
/** * Sets the json value, as it would appear in a jCard or jCal object. * * The value must always be an array. * * @param array $value * @return void */ public function setJsonValue(array $value) { $value = array_map('base64_decode', $value); parent::setJsonValue($value); }
/** * Validates the node for correctness. * * The following options are supported: * Node::REPAIR - May attempt to automatically repair the problem. * * This method returns an array with detected problems. * Every element has the following properties: * * * level - problem level. * * message - A human-readable string describing the issue. * * node - A reference to the problematic node. * * The level means: * 1 - The issue was repaired (only happens if REPAIR was turned on) * 2 - An inconsequential issue * 3 - A severe issue. * * @param int $options * @return array */ public function validate($options = 0) { $messages = parent::validate($options); $value = $this->getValue(); try { DateTimeParser::parseVCardDateTime($value); } catch (\InvalidArgumentException $e) { $messages[] = array('level' => 3, 'message' => 'The supplied value (' . $value . ') is not a correct DATE-AND-OR-TIME property', 'node' => $this); } return $messages; }
/** * get datetime from sabredav datetime property (user TZ is fallback) * * @param Sabre\VObject\Property $dateTimeProperty * @param boolean $_useUserTZ * @return Tinebase_DateTime * * @todo try to guess some common timezones */ protected function _convertToTinebaseDateTime(\Sabre\VObject\Property $dateTimeProperty, $_useUserTZ = FALSE) { $defaultTimezone = date_default_timezone_get(); date_default_timezone_set((string) Tinebase_Core::getUserTimezone()); if ($dateTimeProperty instanceof Sabre\VObject\Property\ICalendar\DateTime) { $dateTime = $dateTimeProperty->getDateTime(); $tz = $_useUserTZ || isset($dateTimeProperty['VALUE']) && strtoupper($dateTimeProperty['VALUE']) == 'DATE' ? (string) Tinebase_Core::getUserTimezone() : $dateTime->getTimezone(); $result = new Tinebase_DateTime($dateTime->format(Tinebase_Record_Abstract::ISO8601LONG), $tz); } else { $result = new Tinebase_DateTime($dateTimeProperty->getValue()); } date_default_timezone_set($defaultTimezone); return $result; }
/** * Build a valid iCal format block from the given event * * @param array Hash array with event/task properties from libkolab * @param object VCalendar object to append event to or false for directly sending data to stdout * @param callable Callback function to fetch attachment contents, false if no attachment export * @param object RECURRENCE-ID property when serializing a recurrence exception */ private function _to_ical($event, $vcal, $get_attachment, $recurrence_id = null) { $type = $event['_type'] ?: 'event'; $ve = VObject\Component::create($this->type_component_map[$type]); $ve->add('UID', $event['uid']); // set DTSTAMP according to RFC 5545, 3.8.7.2. $dtstamp = !empty($event['changed']) && !empty($this->method) ? $event['changed'] : new DateTime(); $ve->add($this->datetime_prop('DTSTAMP', $dtstamp, true)); // all-day events end the next day if ($event['allday'] && !empty($event['end'])) { $event['end'] = clone $event['end']; $event['end']->add(new \DateInterval('P1D')); $event['end']->_dateonly = true; } if (!empty($event['created'])) { $ve->add($this->datetime_prop('CREATED', $event['created'], true)); } if (!empty($event['changed'])) { $ve->add($this->datetime_prop('LAST-MODIFIED', $event['changed'], true)); } if (!empty($event['start'])) { $ve->add($this->datetime_prop('DTSTART', $event['start'], false, (bool) $event['allday'])); } if (!empty($event['end'])) { $ve->add($this->datetime_prop('DTEND', $event['end'], false, (bool) $event['allday'])); } if (!empty($event['due'])) { $ve->add($this->datetime_prop('DUE', $event['due'], false)); } // we're exporting a recurrence instance only if (!$recurrence_id && $event['recurrence_date'] && $event['recurrence_date'] instanceof DateTime) { $recurrence_id = $this->datetime_prop('RECURRENCE-ID', $event['recurrence_date'], false, (bool) $event['allday']); if ($event['thisandfuture']) { $recurrence_id->add('RANGE', 'THISANDFUTURE'); } } if ($recurrence_id) { $ve->add($recurrence_id); } $ve->add('SUMMARY', $event['title']); if ($event['location']) { $ve->add($this->is_apple() ? new vobject_location_property('LOCATION', $event['location']) : new VObject\Property('LOCATION', $event['location'])); } if ($event['description']) { $ve->add('DESCRIPTION', strtr($event['description'], array("\r\n" => "\n", "\r" => "\n"))); } // normalize line endings if (isset($event['sequence'])) { $ve->add('SEQUENCE', $event['sequence']); } if ($event['recurrence'] && !$recurrence_id) { $exdates = $rdates = null; if (isset($event['recurrence']['EXDATE'])) { $exdates = $event['recurrence']['EXDATE']; unset($event['recurrence']['EXDATE']); // don't serialize EXDATEs into RRULE value } if (isset($event['recurrence']['RDATE'])) { $rdates = $event['recurrence']['RDATE']; unset($event['recurrence']['RDATE']); // don't serialize RDATEs into RRULE value } if ($event['recurrence']['FREQ']) { $ve->add('RRULE', libcalendaring::to_rrule($event['recurrence'], (bool) $event['allday'])); } // add EXDATEs each one per line (for Thunderbird Lightning) if (is_array($exdates)) { foreach ($exdates as $ex) { if ($ex instanceof \DateTime) { $exd = clone $event['start']; $exd->setDate($ex->format('Y'), $ex->format('n'), $ex->format('j')); $exd->setTimeZone(new \DateTimeZone('UTC')); $ve->add(new VObject\Property('EXDATE', $exd->format('Ymd\\THis\\Z'))); } } } // add RDATEs if (is_array($rdates) && !empty($rdates)) { $sample = $this->datetime_prop('RDATE', $rdates[0]); $rdprop = new VObject\Property\MultiDateTime('RDATE', null); $rdprop->setDateTimes($rdates, $sample->getDateType()); $ve->add($rdprop); } } if ($event['categories']) { $cat = VObject\Property::create('CATEGORIES'); $cat->setParts((array) $event['categories']); $ve->add($cat); } if (!empty($event['free_busy'])) { $ve->add('TRANSP', $event['free_busy'] == 'free' ? 'TRANSPARENT' : 'OPAQUE'); // for Outlook clients we provide the X-MICROSOFT-CDO-BUSYSTATUS property if (stripos($this->agent, 'outlook') !== false) { $ve->add('X-MICROSOFT-CDO-BUSYSTATUS', $event['free_busy'] == 'outofoffice' ? 'OOF' : strtoupper($event['free_busy'])); } } if ($event['priority']) { $ve->add('PRIORITY', $event['priority']); } if ($event['cancelled']) { $ve->add('STATUS', 'CANCELLED'); } else { if ($event['free_busy'] == 'tentative') { $ve->add('STATUS', 'TENTATIVE'); } else { if ($event['complete'] == 100) { $ve->add('STATUS', 'COMPLETED'); } else { if (!empty($event['status'])) { $ve->add('STATUS', $event['status']); } } } } if (!empty($event['sensitivity'])) { $ve->add('CLASS', strtoupper($event['sensitivity'])); } if (!empty($event['complete'])) { $ve->add('PERCENT-COMPLETE', intval($event['complete'])); } // Apple iCal and BusyCal required the COMPLETED date to be set in order to consider a task complete if ($event['status'] == 'COMPLETED' || $event['complete'] == 100) { $ve->add($this->datetime_prop('COMPLETED', $event['changed'] ?: new DateTime('now - 1 hour'), true)); } if ($event['valarms']) { foreach ($event['valarms'] as $alarm) { $va = VObject\Component::create('VALARM'); $va->action = $alarm['action']; if ($alarm['trigger'] instanceof DateTime) { $va->add($this->datetime_prop('TRIGGER', $alarm['trigger'], true)); } else { $alarm_props = array(); if (strtoupper($alarm['related']) == 'END') { $alarm_props['RELATED'] = 'END'; } $va->add('TRIGGER', $alarm['trigger'], $alarm_props); } if ($alarm['action'] == 'EMAIL') { foreach ((array) $alarm['attendees'] as $attendee) { $va->add('ATTENDEE', 'mailto:' . $attendee); } } if ($alarm['description']) { $va->add('DESCRIPTION', $alarm['description'] ?: $event['title']); } if ($alarm['summary']) { $va->add('SUMMARY', $alarm['summary']); } if ($alarm['duration']) { $va->add('DURATION', $alarm['duration']); $va->add('REPEAT', intval($alarm['repeat'])); } if ($alarm['uri']) { $va->add('ATTACH', $alarm['uri'], array('VALUE' => 'URI')); } $ve->add($va); } } else { if ($event['alarms']) { $va = VObject\Component::create('VALARM'); list($trigger, $va->action) = explode(':', $event['alarms']); $val = libcalendaring::parse_alarm_value($trigger); if ($val[3]) { $va->add('TRIGGER', $val[3]); } else { if ($val[0] instanceof DateTime) { $va->add($this->datetime_prop('TRIGGER', $val[0])); } } $ve->add($va); } } foreach ((array) $event['attendees'] as $attendee) { if ($attendee['role'] == 'ORGANIZER') { if (empty($event['organizer'])) { $event['organizer'] = $attendee; } } else { if (!empty($attendee['email'])) { if (isset($attendee['rsvp'])) { $attendee['rsvp'] = $attendee['rsvp'] ? 'TRUE' : null; } $ve->add('ATTENDEE', 'mailto:' . $attendee['email'], array_filter(self::map_keys($attendee, $this->attendee_keymap))); } } } if ($event['organizer']) { $ve->add('ORGANIZER', 'mailto:' . $event['organizer']['email'], self::map_keys($event['organizer'], array('name' => 'CN'))); } foreach ((array) $event['url'] as $url) { if (!empty($url)) { $ve->add('URL', $url); } } if (!empty($event['parent_id'])) { $ve->add('RELATED-TO', $event['parent_id'], array('RELTYPE' => 'PARENT')); } if ($event['comment']) { $ve->add('COMMENT', $event['comment']); } $memory_limit = parse_bytes(ini_get('memory_limit')); // export attachments if (!empty($event['attachments'])) { foreach ((array) $event['attachments'] as $attach) { // check available memory and skip attachment export if we can't buffer it // @todo: use rcube_utils::mem_check() if (is_callable($get_attachment) && $memory_limit > 0 && ($memory_used = function_exists('memory_get_usage') ? memory_get_usage() : 16 * 1024 * 1024) && $attach['size'] && $memory_used + $attach['size'] * 3 > $memory_limit) { continue; } // embed attachments using the given callback function if (is_callable($get_attachment) && ($data = call_user_func($get_attachment, $attach['id'], $event))) { // embed attachments for iCal $ve->add('ATTACH', base64_encode($data), array_filter(array('VALUE' => 'BINARY', 'ENCODING' => 'BASE64', 'FMTTYPE' => $attach['mimetype'], 'X-LABEL' => $attach['name']))); unset($data); // attempt to free memory } else { if (!empty($this->attach_uri)) { $ve->add('ATTACH', strtr($this->attach_uri, array('{{id}}' => urlencode($attach['id']), '{{name}}' => urlencode($attach['name']), '{{mimetype}}' => urlencode($attach['mimetype']))), array('FMTTYPE' => $attach['mimetype'], 'VALUE' => 'URI')); } } } } foreach ((array) $event['links'] as $uri) { $ve->add('ATTACH', $uri); } // add custom properties foreach ((array) $event['x-custom'] as $prop) { $ve->add($prop[0], $prop[1]); } // append to vcalendar container if ($vcal) { $vcal->add($ve); } else { // serialize and send to stdout echo $ve->serialize(); } // append recurrence exceptions if (is_array($event['recurrence']) && $event['recurrence']['EXCEPTIONS']) { foreach ($event['recurrence']['EXCEPTIONS'] as $ex) { $exdate = $ex['recurrence_date'] ?: $ex['start']; $recurrence_id = $this->datetime_prop('RECURRENCE-ID', $exdate, false, (bool) $event['allday']); if ($ex['thisandfuture']) { $recurrence_id->add('RANGE', 'THISANDFUTURE'); } $this->_to_ical($ex, $vcal, $get_attachment, $recurrence_id); } } }
/** * @brief Data structure of properties * @param object $property * @return associative array * * returns an associative array with * ['name'] name of property * ['value'] htmlspecialchars escaped value of property * ['parameters'] associative array name=>value * ['checksum'] checksum of whole property * NOTE: $value is not escaped anymore. It shouldn't make any difference * but we should look out for any problems. */ public static function structureProperty(\Sabre\VObject\Property $property) { if (!in_array($property->name, App::$index_properties)) { return; } $value = $property->getValue(); if ($property->name == 'ADR' || $property->name == 'N' || $property->name == 'ORG' || $property->name == 'CATEGORIES') { $value = $property->getParts(); if ($property->name == 'CATEGORIES') { $value = str_replace(';', ',', $value); } if ($property->name == 'N') { //$value = stripslashes($value); // \OCP\Util::writeLog('contactsplus','NAME VAL: '.$value, \OCP\Util::DEBUG); } $value = array_map('trim', $value); } elseif ($property->name == 'BDAY') { if (strlen($value) >= 8 && is_int(substr($value, 0, 4)) && is_int(substr($value, 4, 2)) && is_int(substr($value, 6, 2))) { $value = substr($value, 0, 4) . '-' . substr($value, 4, 2) . '-' . substr($value, 6, 2); } else { if ($value[5] !== '-' || $value[7] !== '-') { try { // Skype exports as e.g. Jan 14, 1996 $date = new \DateTime($value); $value = $date->format('Y-m-d'); } catch (\Exception $e) { \OCP\Util::writeLog('contactsplus', __METHOD__ . ' Error parsing date: ' . $value, \OCP\Util::DEBUG); return; } } } } elseif ($property->name == 'PHOTO') { $value = true; } elseif ($property->name == 'IMPP') { if (strpos($value, ':') !== false) { $value = explode(':', $value); $protocol = array_shift($value); if (!isset($property['X-SERVICE-TYPE'])) { $property['X-SERVICE-TYPE'] = strtoupper($protocol); } $value = implode('', $value); } } if (is_string($value)) { $value = strtr($value, array('\\,' => ',', '\\;' => ';')); } $temp = array('value' => $value, 'parameters' => array()); // This cuts around a 3rd off of the json response size. if (in_array($property->name, App::$multi_properties)) { $temp['checksum'] = substr(md5($property->serialize()), 0, 8); } foreach ($property->parameters as $parameter) { // Faulty entries by kaddressbook // Actually TYPE=PREF is correct according to RFC 2426 // but this way is more handy in the UI. Tanghus. if ($parameter->name == 'TYPE' && strtoupper($parameter->getValue()) == 'PREF') { $parameter->name = 'PREF'; $parameter->setValue('1'); } // NOTE: Apparently Sabre_VObject_Reader can't always deal with value list parameters // like TYPE=HOME,CELL,VOICE. Tanghus. // TODO: Check if parameter is has commas and split + merge if so. if ($parameter->name == 'TYPE') { $pvalue = $parameter->getValue(); if (is_string($pvalue) && strpos($pvalue, ',') !== false) { $pvalue = array_map('trim', explode(',', $pvalue)); } $pvalue = is_array($pvalue) ? $pvalue : array($pvalue); if (isset($temp['parameters'][$parameter->name])) { $temp['parameters'][$parameter->name][] = \OCP\Util::sanitizeHTML($pvalue); } else { $temp['parameters'][$parameter->name] = \OCP\Util::sanitizeHTML($pvalue); } } else { //$value = strtr($value, array('\,' => ',', '\;' => ';')); $temp['parameters'][$parameter->name] = \OCP\Util::sanitizeHTML($parameter->getValue()); } } return $temp; }
/** * @brief returns the vcard property corresponding to the parameter * creates the property if it doesn't exists yet * @param $vcard the vcard to get or create the properties with * @param $importEntry the parameter to find * @return the property|false */ protected function getOrCreateVCardProperty(&$vcard, $importEntry) { if (isset($vcard) && isset($importEntry)) { // looking for a property with the same name $properties = $vcard->select($importEntry['property']); foreach ($properties as $property) { if ($importEntry['type'] == null && !isset($importEntry->additional_property)) { return $property; } foreach ($property->parameters as $parameter) { // Filtering types if ($parameter->name == 'TYPE' && !strcmp($parameter->value, $importEntry['type'])) { $found = 0; if (isset($importEntry->additional_property)) { // Filtering additional properties if necessary (I know, there are a lot of inner loops, sorry) foreach ($importEntry->additional_property as $additional_property) { if ((string) $parameter->name == $additional_property['name']) { $found++; } } if ($found == count($importEntry->additional_property)) { return $property; } } return $property; } } if (isset($importEntry['group']) && $property->group == $importEntry['group']) { return $property; } } // Property not found, creating one $property = \Sabre\VObject\Property::create($importEntry['property']); $vcard->add($property); if ($importEntry['type'] != null) { $property->parameters[] = new \Sabre\VObject\Parameter('TYPE', '' . StringUtil::convertToUTF8($importEntry['type'])); switch ($importEntry['property']) { case "ADR": $property->setValue(";;;;;;"); break; case "FN": $property->setValue(";;;;"); break; } } if ($importEntry['group'] != null) { $property->group = $importEntry['group']; } return $property; } else { return false; } }
/** * Validates the node for correctness. * * The following options are supported: * Node::REPAIR - May attempt to automatically repair the problem. * * This method returns an array with detected problems. * Every element has the following properties: * * * level - problem level. * * message - A human-readable string describing the issue. * * node - A reference to the problematic node. * * The level means: * 1 - The issue was repaired (only happens if REPAIR was turned on) * 2 - An inconsequential issue * 3 - A severe issue. * * @param int $options * @return array */ public function validate($options = 0) { $messages = parent::validate($options); $valueType = $this->getValueType(); $value = $this->getValue(); try { switch ($valueType) { case 'DATE': $foo = DateTimeParser::parseDate($value); break; case 'DATE-TIME': $foo = DateTimeParser::parseDateTime($value); break; } } catch (\LogicException $e) { $messages[] = array('level' => 3, 'message' => 'The supplied value (' . $value . ') is not a correct ' . $valueType, 'node' => $this); } return $messages; }
$vcard->add($property); $checksum = substr(md5($property->serialize()), 0, 8); try { VCard::edit($id, $vcard); } catch (Exception $e) { bailOut($e->getMessage()); } \OCP\JSON::success(array('data' => array('checksum' => $checksum, 'oldchecksum' => $_POST['checksum']))); exit; } } else { $element = $name; $property = $vcard->select($name); debug('propertylist: ' . get_class($property)); if (count($property) === 0) { $property = VObject\Property::create($name); $vcard->add($property); } else { $property = array_shift($property); } } /* preprocessing value */ switch ($element) { case 'BDAY': $date = new \DateTime($value); $value = $date->format('Y-m-d'); break; case 'FN': if (!$value) { // create a method thats returns an alternative for FN. //$value = getOtherValue();
/** * This method serializes only the value of a property. This is used to * create xCard or xCal documents. * * @param Xml\Writer $writer XML writer. * * @return void */ protected function xmlSerializeValue(Xml\Writer $writer) { // Special-casing the GEO property. // // See: // http://tools.ietf.org/html/rfc6321#section-3.4.1.2 if ($this->name === 'GEO') { $value = array_map('floatval', $this->getParts()); $writer->writeElement('latitude', $value[0]); $writer->writeElement('longitude', $value[1]); } else { parent::xmlSerializeValue($writer); } }
/** * Work around issue in older VObject sersions * https://github.com/fruux/sabre-vobject/issues/24 * * @param \Sabre\VObject\Property $property Reference to a \Sabre\VObject\Property. */ public function fixPropertyParameters(&$property) { // Work around issue in older VObject sersions // https://github.com/fruux/sabre-vobject/issues/24 foreach ($property->parameters as $key => $parameter) { if (strpos($parameter->getValue(), ',') === false) { continue; } $values = explode(',', $parameter->getValue()); $values = array_map('trim', $values); $parameter->setValue(array_shift($values)); foreach ($values as $value) { $property->add($parameter->name, $value); } } }
/** * @param $properties * @return mixed */ public function createOrUpdate($properties) { $id = null; /** * @var \OCA\Contacts\VObject\VCard */ $vcard = null; if (array_key_exists('id', $properties)) { // TODO: test if $id belongs to this addressbook $id = $properties['id']; // TODO: Test $vcard $vcard = $this->addressBook->getChild($properties['id']); foreach (array_keys($properties) as $name) { if (isset($vcard->{$name})) { unset($vcard->{$name}); } } } else { $vcard = \Sabre\VObject\Component::create('VCARD'); $uid = substr(md5(rand() . time()), 0, 10); $vcard->add('UID', $uid); try { $id = $this->addressBook->addChild($vcard); } catch (\Exception $e) { \OCP\Util::writeLog('contacts', __METHOD__ . ' ' . $e->getMessage(), \OCP\Util::ERROR); return false; } } foreach ($properties as $name => $value) { switch ($name) { case 'ADR': case 'N': if (is_array($value)) { $property = \Sabre\VObject\Property::create($name); $property->setParts($value); $vcard->add($property); } else { $vcard->{$name} = $value; } break; case 'BDAY': // TODO: try/catch $date = new \DateTime($value); $vcard->BDAY = $date->format('Y-m-d'); $vcard->BDAY->VALUE = 'DATE'; break; case 'EMAIL': case 'TEL': case 'IMPP': // NOTE: We don't know if it's GTalk, Jabber etc. only the protocol // NOTE: We don't know if it's GTalk, Jabber etc. only the protocol case 'URL': if (is_array($value)) { foreach ($value as $val) { $vcard->add($name, strip_tags($val)); } } else { $vcard->add($name, strip_tags($value)); } default: $vcard->{$name} = $value; break; } } try { VCard::edit($id, $vcard); } catch (\Exception $e) { \OCP\Util::writeLog('contacts', __METHOD__ . ' ' . $e->getMessage(), \OCP\Util::ERROR); return false; } $asarray = VCard::structureContact($vcard); $asarray['id'] = $id; return $asarray; }
/** * @brief converts a VCard into a owncloud VCard * @param $element the VCard element to convert * @return VCard|false */ public function convertElementToVCard($element) { try { $source = VObject\Reader::read($element, VObject\Reader::OPTION_FORGIVING); } catch (VObject\ParseException $error) { return false; } $dest = \Sabre\VObject\Component::create('VCARD'); foreach ($source->children() as $sourceProperty) { $importEntry = $this->getImportEntry($sourceProperty, $source); if ($importEntry) { $value = $sourceProperty->value; if (isset($importEntry['remove'])) { $value = str_replace($importEntry['remove'], '', $sourceProperty->value); } $values = array($value); if (isset($importEntry['separator'])) { $values = explode($importEntry['separator'], $value); } foreach ($values as $oneValue) { if (isset($importEntry->vcard_favourites)) { foreach ($importEntry->vcard_favourites as $vcardFavourite) { if (strcasecmp((string)$vcardFavourite, trim($oneValue)) == 0) { $property = \Sabre\VObject\Property::create("X-FAVOURITES", 'yes'); $dest->add($property); } else { $property = $this->getOrCreateVCardProperty($dest, $importEntry->vcard_entry); $this->updateProperty($property, $importEntry, trim($oneValue)); } } } else { $property = $this->getOrCreateVCardProperty($dest, $importEntry->vcard_entry); $this->updateProperty($property, $importEntry, $sourceProperty->value); } } } else { $property = clone $sourceProperty; $dest->add($property); } } $dest->validate(\Sabre\VObject\Component\VCard::REPAIR); return $dest; }
/** * parse birthday * * @param array $data * @param Sabre\VObject\Property $property */ protected function _toTine20ModelParseBday(&$_data, \Sabre\VObject\Property $_property) { $tzone = new DateTimeZone(Tinebase_Core::getUserTimezone()); $_data['bday'] = new Tinebase_DateTime($_property->getValue(), $tzone); $_data['bday']->setTimezone(new DateTimeZone('UTC')); }
/** * (non-PHPdoc) * @see Addressbook_Convert_Contact_VCard_Abstract::_toTine20ModelParseEmail() */ protected function _toTine20ModelParseEmail(&$data, \Sabre\VObject\Property $property, \Sabre\VObject\Component\VCard $vcard) { /* * eM Client supports three types of mail */ if ($property['TYPE']->has('home')) { $data['email_home'] = $property->getValue(); } elseif ($property['TYPE']->has('work') && $data['email'] == '') { $data['email'] = $property->getValue(); } elseif ($property['TYPE']->has('pref') || $data['email'] == '') { $data['email'] = $property->getValue(); } }
/** * @brief converts a unique element into a owncloud VCard * @param $element the element to convert * @return VCard, all unconverted elements are stored in X-Unknown-Element parameters */ public function convertElementToVCard($element, $title = null) { $vcard = \Sabre\VObject\Component::create('VCARD'); $nbElt = count($element); for ($i = 0; $i < $nbElt; $i++) { if ($element[$i] != '') { //$importEntry = false; // Look for the right import_entry if (isset($this->configContent->import_core->base_parsing)) { if (strcasecmp((string) $this->configContent->import_core->base_parsing, 'position') == 0) { $importEntry = $this->getImportEntryFromPosition((string) $i); } else { if (strcasecmp((string) $this->configContent->import_core->base_parsing, 'name') == 0 && isset($title[$i])) { $importEntry = $this->getImportEntryFromName($title[$i]); } } } if ($importEntry) { // Create a new property and attach it to the vcard $value = $element[$i]; if (isset($importEntry['remove'])) { $value = str_replace($importEntry['remove'], '', $element[$i]); } $values = array($value); if (isset($importEntry['separator'])) { $values = explode($importEntry['separator'], $value); } foreach ($values as $oneValue) { if (isset($importEntry->vcard_favourites)) { foreach ($importEntry->vcard_favourites as $vcardFavourite) { if (strcasecmp((string) $vcardFavourite, trim($oneValue)) == 0) { $property = \Sabre\VObject\Property::create("X-FAVOURITES", 'yes'); $vcard->add($property); } else { $property = $this->getOrCreateVCardProperty($vcard, $importEntry->vcard_entry); $this->updateProperty($property, $importEntry, trim($oneValue)); } } } else { $property = $this->getOrCreateVCardProperty($vcard, $importEntry->vcard_entry); $this->updateProperty($property, $importEntry, trim($oneValue)); } } } else { if (isset($element[$i]) && isset($title[$i])) { $property = \Sabre\VObject\Property::create("X-Unknown-Element", StringUtil::convertToUTF8($element[$i])); $property->parameters[] = new \Sabre\VObject\Parameter('TYPE', '' . StringUtil::convertToUTF8($title[$i])); $vcard->add($property); } } } } $vcard->validate(\Sabre\VObject\Component\VCard::REPAIR); return $vcard; }
/** * Colorizes a property. * * @param Property $property * @return void */ protected function serializeProperty(Property $property) { if ($property->group) { $this->cWrite('default', $property->group); $this->cWrite('red', '.'); } $str = ''; $this->cWrite('yellow', $property->name); foreach ($property->parameters as $param) { $this->cWrite('red', ';'); $this->cWrite('blue', $param->serialize()); } $this->cWrite('red', ':'); if ($property instanceof Property\Binary) { $this->cWrite('default', 'embedded binary stripped. (' . strlen($property->getValue()) . ' bytes)'); } else { $parts = $property->getParts(); $first1 = true; // Looping through property values foreach ($parts as $part) { if ($first1) { $first1 = false; } else { $this->cWrite('red', $property->delimiter); } $first2 = true; // Looping through property sub-values foreach ((array) $part as $subPart) { if ($first2) { $first2 = false; } else { // The sub-value delimiter is always comma $this->cWrite('red', ','); } $subPart = strtr($subPart, ['\\' => $this->colorize('purple', '\\\\', 'green'), ';' => $this->colorize('purple', '\\;', 'green'), ',' => $this->colorize('purple', '\\,', 'green'), "\n" => $this->colorize('purple', "\\n\n\t", 'green'), "\r" => ""]); $this->cWrite('green', $subPart); } } } $this->cWrite("default", "\n"); }
/** * Validates the node for correctness. * * The following options are supported: * Node::REPAIR - May attempt to automatically repair the problem. * * This method returns an array with detected problems. * Every element has the following properties: * * * level - problem level. * * message - A human-readable string describing the issue. * * node - A reference to the problematic node. * * The level means: * 1 - The issue was repaired (only happens if REPAIR was turned on) * 2 - An inconsequential issue * 3 - A severe issue. * * @param int $options * * @return array */ function validate($options = 0) { $messages = parent::validate($options); $valueType = $this->getValueType(); $values = $this->getParts(); try { foreach ($values as $value) { switch ($valueType) { case 'DATE': DateTimeParser::parseDate($value); break; case 'DATE-TIME': DateTimeParser::parseDateTime($value); break; } } } catch (InvalidDataException $e) { $messages[] = ['level' => 3, 'message' => 'The supplied value (' . $value . ') is not a correct ' . $valueType, 'node' => $this]; } return $messages; }
/** * @brief Get data structure of property. * @param \Sabre\VObject\Property $property * @return associative array * * returns an associative array with * ['name'] name of property * ['value'] htmlspecialchars escaped value of property * ['parameters'] associative array name=>value * ['checksum'] checksum of whole property * NOTE: $value is not escaped anymore. It shouldn't make any difference * but we should look out for any problems. */ public static function serializeProperty(\Sabre\VObject\Property $property) { if (!in_array($property->name, Properties::$index_properties)) { return; } $value = $property->value; if ($property->name == 'ADR' || $property->name == 'N' || $property->name == 'ORG' || $property->name == 'CATEGORIES') { $value = $property->getParts(); $value = array_map('trim', $value); } elseif ($property->name == 'BDAY') { if (strpos($value, '-') === false) { if (strlen($value) >= 8) { $value = substr($value, 0, 4) . '-' . substr($value, 4, 2) . '-' . substr($value, 6, 2); } else { return null; // Badly malformed :-( } } } elseif ($property->name == 'PHOTO') { $value = true; } elseif ($property->name == 'IMPP') { if (strpos($value, ':') !== false) { $value = explode(':', $value); $protocol = array_shift($value); if (!isset($property['X-SERVICE-TYPE'])) { $property['X-SERVICE-TYPE'] = strtoupper($protocol); } $value = implode('', $value); } } if (is_string($value)) { $value = strtr($value, array('\\,' => ',', '\\;' => ';')); } $temp = array('value' => $value, 'parameters' => array()); // This cuts around a 3rd off of the json response size. if (in_array($property->name, Properties::$multi_properties)) { $temp['checksum'] = substr(md5($property->serialize()), 0, 8); } foreach ($property->parameters as $parameter) { // Faulty entries by kaddressbook // Actually TYPE=PREF is correct according to RFC 2426 // but this way is more handy in the UI. Tanghus. if ($parameter->name == 'TYPE' && strtoupper($parameter->value) == 'PREF') { $parameter->name = 'PREF'; $parameter->value = '1'; } // NOTE: Apparently Sabre_VObject_Reader can't always deal with value list parameters // like TYPE=HOME,CELL,VOICE. Tanghus. // TODO: Check if parameter is has commas and split + merge if so. if ($parameter->name == 'TYPE') { $pvalue = $parameter->value; if (is_string($pvalue) && strpos($pvalue, ',') !== false) { $pvalue = array_map('trim', explode(',', $pvalue)); } $pvalue = is_array($pvalue) ? $pvalue : array($pvalue); if (isset($temp['parameters'][$parameter->name])) { $temp['parameters'][$parameter->name][] = \OCP\Util::sanitizeHTML($pvalue); } else { $temp['parameters'][$parameter->name] = \OCP\Util::sanitizeHTML($pvalue); } } else { $temp['parameters'][$parameter->name] = \OCP\Util::sanitizeHTML($parameter->value); } } return $temp; }
/** * Hydrate data from a XML subtree, as it would appear in a xCard or xCal * object. * * @param array $value * * @return void */ function setXmlValue(array $value) { $value = array_map('intval', $value); parent::setXmlValue($value); }
/** * Set a property by the property name. * It is up to the caller to call ->save() * * @param string $name Property name * @param mixed $value * @param array $parameters * @return bool */ public function setPropertyByName($name, $value, $parameters = array()) { // TODO: parameters are ignored for now. switch ($name) { case 'BDAY': try { $date = new \DateTime($value); } catch (\Exception $e) { \OCP\Util::writeLog('contacts', __METHOD__ . ' DateTime exception: ' . $e->getMessage(), \OCP\Util::ERROR); return false; } $value = $date->format('Y-m-d'); $this->BDAY = $value; $this->BDAY->add('VALUE', 'DATE'); //\OCP\Util::writeLog('contacts', __METHOD__.' BDAY: '.$this->BDAY->serialize(), \OCP\Util::DEBUG); break; case 'CATEGORIES': case 'N': case 'ORG': $property = $this->select($name); if (count($property) === 0) { $property = \Sabre\VObject\Property::create($name); $this->add($property); } else { // Actually no idea why this works $property = array_shift($property); } if (is_array($value)) { $property->setParts($value); } else { $this->{$name} = $value; } break; default: \OCP\Util::writeLog('contacts', __METHOD__ . ' adding: ' . $name . ' ' . $value, \OCP\Util::DEBUG); $this->{$name} = $value; break; } $this->setSaved(false); return true; }
/** * @brief converts an LDIF element into a VCard property * and updates the VCard * @param $value the LDIF value * @param $importEntry the VCard entry to modify * @param $dest the VCard to modify (for adding a X-FAVOURITE property) */ private function convertElementToProperty($value, $importEntry, &$dest) { if (isset($importEntry->vcard_favourites)) { foreach ($importEntry->vcard_favourites as $vcardFavourite) { if (strcasecmp((string)$vcardFavourite, trim($value)) == 0) { $property = \Sabre\VObject\Property::create("X-FAVOURITES", 'yes'); $dest->add($property); } else { $property = $this->getOrCreateVCardProperty($dest, $importEntry->vcard_entry); if (isset($importEntry['image']) && $importEntry['image'] == "true") { $this->updateImageProperty($property, $value); } else { $this->updateProperty($property, $importEntry, $value); } } } } else { $property = $this->getOrCreateVCardProperty($dest, $importEntry->vcard_entry); if (isset($importEntry['image']) && $importEntry['image'] == "true") { $this->updateImageProperty($property, $value); } else { $this->updateProperty($property, $importEntry, $value); } } }