/** * convert contact from xml to Addressbook_Model_Contact * * @param SimpleXMLElement $_data * @return Addressbook_Model_Contact */ public function toTineModel(SimpleXMLElement $_data, $_entry = null) { if ($_entry instanceof Addressbook_Model_Contact) { $contact = $_entry; } else { $contact = new Addressbook_Model_Contact(null, true); } unset($contact->jpegphoto); $xmlData = $_data->children('uri:Contacts'); $airSyncBase = $_data->children('uri:AirSyncBase'); foreach ($this->_mapping as $fieldName => $value) { switch ($value) { case 'jpegphoto': // do not change if not set if (isset($xmlData->{$fieldName})) { if (!empty($xmlData->{$fieldName})) { $devicePhoto = base64_decode((string) $xmlData->{$fieldName}); try { $currentPhoto = Tinebase_Controller::getInstance()->getImage('Addressbook', $contact->getId())->getBlob('image/jpeg', 36000); } catch (Exception $e) { } if (isset($currentPhoto) && $currentPhoto == $devicePhoto) { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->INFO(__METHOD__ . '::' . __LINE__ . " photo did not change on device -> preserving server photo"); } } else { if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) { Tinebase_Core::getLogger()->INFO(__METHOD__ . '::' . __LINE__ . " using new contact photo from device (" . strlen($devicePhoto) . "KB)"); } $contact->jpegphoto = $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': if (isset($xmlData->{$fieldName})) { $isoDate = (string) $xmlData->{$fieldName}; $contact->bday = new Tinebase_DateTime($isoDate); if ($this->_device->devicetype == Syncope_Model_Device::TYPE_IPHONE && $this->_device->getMajorVersion() < 800 || preg_match("/^\\d{4}-\\d{2}-\\d{2}\$/", $isoDate)) { // iOS < 4 & webow < 2.1 send birthdays to the entered date, but the time the birthday got entered on the device // acutally iOS < 4 somtimes 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::get(Tinebase_Core::USERTIMEZONE); $contact->bday = new Tinebase_DateTime($contact->bday->setTime(0, 0, 0)->format(Tinebase_Record_Abstract::ISO8601LONG), $userTimezone); $contact->bday->setTimezone('UTC'); } } else { $contact->bday = null; } break; case 'adr_one_countryname': case 'adr_two_countryname': $contact->{$value} = Tinebase_Translation::getRegionCodeByCountryName((string) $xmlData->{$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 { // default handling for all other devices if (isset($xmlData->{$fieldName})) { $contact->{$value} = (string) $xmlData->{$fieldName}; } else { $contact->{$value} = null; } } break; case 'email': case 'email_home': // android send email address as // Lars Kneschke <*****@*****.**> if (preg_match('/(.*)<(.+@[^@]+)>/', (string) $xmlData->{$fieldName}, $matches)) { $contact->{$value} = trim($matches[2]); } else { $contact->{$value} = (string) $xmlData->{$fieldName}; } break; default: if (isset($xmlData->{$fieldName})) { $contact->{$value} = (string) $xmlData->{$fieldName}; } else { $contact->{$value} = null; } break; } } // get body if (version_compare($this->_device->acsversion, '12.0', '>=') === true) { $contact->note = isset($airSyncBase->Body) ? (string) $airSyncBase->Body->Data : null; } else { $contact->note = isset($xmlData->Body) ? (string) $xmlData->Body : null; } // 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)); // 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; }
/** * save unknown contacts * * @param integer $_user_id * @param array $_recipients * @return integer */ protected function _saveUnknownContacts($_user_id, $_recipients) { $result = 0; $_allRecipients = array(); foreach ($_recipients as $_recipts) { array_push($_allRecipients, strtolower($_recipts)); } try { $decodedFilter = array(array('field' => 'email_query', 'operator' => 'in', 'value' => $_allRecipients), array('field' => 'container_id', 'operator' => 'equals', 'value' => array('path' => '/personal/' . $_user_id))); $filter = new Addressbook_Model_ContactFilter($decodedFilter); $foundContacts = $this->_searchContacts($filter, null); $knownContacts = array(); foreach ($foundContacts as $contact) { array_push($knownContacts, $contact['email']); } $unknownContacts = array_diff($_allRecipients, $knownContacts); $unknownContactsFolder = $this->_getUnknownContactsFolder($_user_id); foreach ($unknownContacts as $recipient) { //gets everything before the @ $fullname = preg_replace('/@.*/', '', $recipient); $fullname = ucwords(preg_replace('/[\\-\\_\\.]/', ' ', $fullname)); //cuts the email in the @, removes everythig before the first . and upercases it $org_name = strtoupper(preg_replace('/.*?@/', '', $recipient)); $org_name = strtoupper(preg_replace('/\\..*/', '', $org_name)); $n_given = $fullname; $n_family = ""; $pieces = explode(' ', $fullname); if (count($pieces) > 1) { $n_given = preg_replace('/(\\s.*)/', '', $fullname); $n_family = preg_replace('/.*?\\s/', '', $fullname); } $contactData = array('email' => $recipient, 'n_family' => $n_family, 'n_given' => $n_given, 'org_name' => $org_name, 'container_id' => $unknownContactsFolder->id); if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " adding new contact " . print_r($contactData, true)); } $contact = new Addressbook_Model_Contact(NULL, FALSE); $contact->setFromArray($contactData); Addressbook_Controller_Contact::getInstance()->create($contact, FALSE); $result += 1; } } catch (Zend_Mail_Protocol_Exception $zmpe) { Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Could not save message note: ' . $zmpe->getMessage()); throw $zmpe; $result = -1; } return $result; }
/** * 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; }
/** * 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; }
/** * converts vcard to Addressbook_Model_Contact * * @param Sabre_VObject_Component|stream|string $_blob the vcard to parse * @param Tinebase_Record_Abstract $_record update existing contact * @return Addressbook_Model_Contact */ public function toTine20Model($_blob, Tinebase_Record_Abstract $_record = null) { if ($_blob instanceof Sabre_VObject_Component) { $vcard = $_blob; } else { if (is_resource($_blob)) { $_blob = stream_get_contents($_blob); } if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' vcard data: ' . $_blob); } $vcard = Sabre_VObject_Reader::read($_blob); } if ($_record instanceof Addressbook_Model_Contact) { $contact = $_record; } else { $contact = new Addressbook_Model_Contact(null, false); } $data = $this->_emptyArray; 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) { if (strtolower($typeProperty) == 'home' || strtolower($typeProperty) == 'work') { $type = strtolower($typeProperty); break; } } if ($type == 'home') { // home address $data['adr_two_street2'] = $property->value[1]; $data['adr_two_street'] = $property->value[2]; $data['adr_two_locality'] = $property->value[3]; $data['adr_two_region'] = $property->value[4]; $data['adr_two_postalcode'] = $property->value[5]; $data['adr_two_countryname'] = $property->value[6]; } elseif ($type == 'work') { // work address $data['adr_one_street2'] = $property->value[1]; $data['adr_one_street'] = $property->value[2]; $data['adr_one_locality'] = $property->value[3]; $data['adr_one_region'] = $property->value[4]; $data['adr_one_postalcode'] = $property->value[5]; $data['adr_one_countryname'] = $property->value[6]; } break; case 'CATEGORIES': $tags = $property->value; if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' cardData ' . print_r($tags, true)); } break; case 'EMAIL': $this->_toTine20ModelParseEmail($data, $property); break; case 'FN': $data['n_fn'] = $property->value; break; case 'N': #$components = Sabre_VObject_Property::splitCompoundValues($property->value); $data['n_family'] = $property->value[0]; $data['n_given'] = $property->value[1]; $data['n_middle'] = isset($property->value[2]) ? $property->value[2] : null; $data['n_prefix'] = isset($property->value[3]) ? $property->value[3] : null; $data['n_suffix'] = isset($property->value[4]) ? $property->value[4] : null; break; case 'NOTE': $data['note'] = $property->value; break; case 'ORG': #$components = Sabre_VObject_Property::splitCompoundValues($property->value); $data['org_name'] = $property->value[0]; $data['org_unit'] = isset($property->value[1]) ? $property->value[1] : null; break; case 'PHOTO': $data['jpegphoto'] = base64_decode($property->value); break; case 'TEL': $this->_toTine20ModelParseTel($data, $property); break; case 'URL': switch (strtoupper($property['TYPE'])) { case 'HOME': $data['url_home'] = $property->value; break; case 'WORK': default: $data['url'] = $property->value; break; } break; case 'TITLE': $data['title'] = $property->value; 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)); } if (empty($data['n_family'])) { $parts = explode(' ', $data['n_fn']); $data['n_family'] = $parts[count($parts) - 1]; $data['n_given'] = count($parts) > 1 ? $parts[0] : null; } $contact->setFromArray($data); if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) { Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' data ' . print_r($contact->toArray(), true)); } return $contact; }