/** * converts vcard to Addressbook_Model_Contact * * @param \Sabre\VObject\Component|resource|string $blob the vcard to parse * @param Tinebase_Record_Abstract $_record update existing contact * @param array $options array of options * @return Addressbook_Model_Contact */ public function toTine20Model($blob, Tinebase_Record_Abstract $_record = null, $options = array()) { $vcard = self::getVObject($blob); if ($_record instanceof Addressbook_Model_Contact) { $contact = $_record; } else { $contact = new Addressbook_Model_Contact(null, false); } $data = $this->_emptyArray; /** @var \Sabre\VObject\Property $property */ foreach ($vcard->children() as $property) { switch ($property->name) { case 'VERSION': case 'PRODID': case 'UID': // do nothing break; case 'ADR': $type = null; foreach ($property['TYPE'] as $typeProperty) { $typeProperty = strtolower($typeProperty); if (in_array($typeProperty, array('home', 'work'))) { $type = $typeProperty; break; } } $parts = $property->getParts(); if ($type == 'home') { // home address $data['adr_two_street2'] = $parts[1]; $data['adr_two_street'] = $parts[2]; $data['adr_two_locality'] = $parts[3]; $data['adr_two_region'] = $parts[4]; $data['adr_two_postalcode'] = $parts[5]; $data['adr_two_countryname'] = $parts[6]; } elseif ($type == 'work') { // work address $data['adr_one_street2'] = $parts[1]; $data['adr_one_street'] = $parts[2]; $data['adr_one_locality'] = $parts[3]; $data['adr_one_region'] = $parts[4]; $data['adr_one_postalcode'] = $parts[5]; $data['adr_one_countryname'] = $parts[6]; } break; case 'CATEGORIES': $tags = Tinebase_Model_Tag::resolveTagNameToTag($property->getParts(), 'Addressbook'); if (!isset($data['tags'])) { $data['tags'] = $tags; } else { $data['tags']->merge($tags); } break; case 'EMAIL': $this->_toTine20ModelParseEmail($data, $property, $vcard); break; case 'FN': $data['n_fn'] = $property->getValue(); break; case 'N': $parts = $property->getParts(); $data['n_family'] = $parts[0]; $data['n_given'] = isset($parts[1]) ? $parts[1] : null; $data['n_middle'] = isset($parts[2]) ? $parts[2] : null; $data['n_prefix'] = isset($parts[3]) ? $parts[3] : null; $data['n_suffix'] = isset($parts[4]) ? $parts[4] : null; break; case 'NOTE': $data['note'] = $property->getValue(); break; case 'ORG': $parts = $property->getParts(); $data['org_name'] = $parts[0]; $data['org_unit'] = isset($parts[1]) ? $parts[1] : null; break; case 'PHOTO': $jpegphoto = $property->getValue(); break; case 'TEL': $this->_toTine20ModelParseTel($data, $property); break; case 'URL': switch (strtoupper($property['TYPE'])) { case 'HOME': $data['url_home'] = $property->getValue(); break; case 'WORK': default: $data['url'] = $property->getValue(); break; } break; case 'TITLE': $data['title'] = $property->getValue(); break; case 'BDAY': $this->_toTine20ModelParseBday($data, $property); break; default: if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' cardData ' . $property->name); } break; } } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' data ' . print_r($data, true)); } // Some email clients will only set a contact with FN (formatted name) without surname if (empty($data['n_family']) && empty($data['org_name']) && !empty($data['n_fn'])) { if (strpos($data['n_fn'], ",") > 0) { list($lastname, $firstname) = explode(",", $data['n_fn'], 2); $data['n_family'] = trim($lastname); $data['n_given'] = trim($firstname); } elseif (strpos($data['n_fn'], " ") > 0) { list($firstname, $lastname) = explode(" ", $data['n_fn'], 2); $data['n_family'] = trim($lastname); $data['n_given'] = trim($firstname); } else { $data['n_family'] = empty($data['n_fn']) ? 'VCARD (imported)' : $data['n_fn']; } } $contact->setFromArray($data); if (isset($jpegphoto)) { $contact->setSmallContactImage($jpegphoto, $this->_maxPhotoSize); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' data ' . print_r($contact->toArray(), true)); } if (isset($options[self::OPTION_USE_SERVER_MODLOG]) && $options[self::OPTION_USE_SERVER_MODLOG] === true) { $contact->creation_time = $_record->creation_time; $contact->last_modified_time = $_record->last_modified_time; $contact->seq = $_record->seq; } return $contact; }
/** * convert contact from xml to Addressbook_Model_Contact * * @param SimpleXMLElement $_data * @return Addressbook_Model_Contact */ public function toTineModel(Syncroton_Model_IEntry $data, $entry = null) { if ($entry instanceof Addressbook_Model_Contact) { $contact = $entry; } else { $contact = new Addressbook_Model_Contact(null, true); } unset($contact->jpegphoto); foreach ($this->_mapping as $fieldName => $value) { if (!isset($data->{$fieldName})) { $contact->{$value} = null; continue; } switch ($value) { case 'jpegphoto': if (!empty($data->{$fieldName})) { $devicePhoto = $data->{$fieldName}; $contact->setSmallContactImage($devicePhoto); } else { if ($entry && !empty($entry->jpegphoto)) { $contact->jpegphoto = ''; if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->INFO(__METHOD__ . '::' . __LINE__ . ' Deleting contact photo on device request (contact id: ' . $contact->getId() . ')'); } } } break; case 'bday': $contact->{$value} = new Tinebase_DateTime($data->{$fieldName}); if ($this->_device->devicetype == Syncroton_Model_Device::TYPE_IPHONE && $this->_device->getMajorVersion() < 800) { // iOS < 4 & webos < 2.1 send birthdays to the entered date, but the time the birthday got entered on the device // actually iOS < 4 sometimes sends the bday at noon but the timezone is not clear // -> we don't trust the time part and set the birthdays timezone to the timezone the user has set in tine $userTimezone = Tinebase_Core::getUserTimezone(); $contact->{$value} = new Tinebase_DateTime($contact->bday->setTime(0, 0, 0)->format(Tinebase_Record_Abstract::ISO8601LONG), $userTimezone); $contact->{$value}->setTimezone('UTC'); } elseif ($this->_device->devicetype == Syncroton_Model_Device::TYPE_BLACKBERRY && version_compare($this->_device->getMajorVersion(), '10', '>=')) { // BB 10+ expects birthday to be at noon $contact->{$value}->subHour(12); } break; case 'adr_one_countryname': case 'adr_two_countryname': $contact->{$value} = Tinebase_Translation::getRegionCodeByCountryName($data->{$fieldName}); break; case 'adr_one_street': if (strtolower($this->_device->devicetype) == 'palm') { // palm pre sends the whole address in the <Contacts:BusinessStreet> tag unset($contact->adr_one_street); } else { $contact->{$value} = $data->{$fieldName}; } break; case 'email': case 'email_home': // android sends email address as // Lars Kneschke <*****@*****.**> if (preg_match('/(.*)<(.+@[^@]+)>/', $data->{$fieldName}, $matches)) { $contact->{$value} = trim($matches[2]); } else { $contact->{$value} = $data->{$fieldName}; } break; case 'note': // @todo check $data->$fieldName->Type and convert to/from HTML if needed if ($data->{$fieldName} instanceof Syncroton_Model_EmailBody) { $contact->{$value} = $data->{$fieldName}->data; } else { $contact->{$value} = null; } break; case 'url': // remove facebook urls if (!preg_match('/^fb:\\/\\//', $data->{$fieldName})) { $contact->{$value} = $data->{$fieldName}; } break; default: $contact->{$value} = $data->{$fieldName}; break; } } // force update of n_fileas and n_fn $contact->setFromArray(array('n_given' => $contact->n_given, 'n_family' => $contact->n_family, 'org_name' => $contact->org_name)); // either "org_name" or "n_family" must be given! if (empty($contact->org_name) && empty($contact->n_family)) { $contact->n_family = 'imported'; } // contact should be valid now $contact->isValid(); if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) { Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " contactData " . print_r($contact->toArray(), true)); } return $contact; }