PageRenderTime 27ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/tine20/Addressbook/Frontend/ActiveSync.php

https://gitlab.com/rsilveira1987/Expresso
PHP | 362 lines | 194 code | 56 blank | 112 comment | 40 complexity | 4374a45e68649281865d89088f4dc830 MD5 | raw file
  1. <?php
  2. /**
  3. * Tine 2.0
  4. *
  5. * @package Addressbook
  6. * @subpackage Frontend
  7. * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3
  8. * @copyright Copyright (c) 2008-2014 Metaways Infosystems GmbH (http://www.metaways.de)
  9. * @author Lars Kneschke <l.kneschke@metaways.de>
  10. */
  11. /**
  12. * class documentation
  13. *
  14. * @package Addressbook
  15. * @subpackage Frontend
  16. */
  17. class Addressbook_Frontend_ActiveSync extends ActiveSync_Frontend_Abstract implements Syncroton_Data_IDataSearch
  18. {
  19. protected $_mapping = array(
  20. #'Anniversary' => 'anniversary',
  21. #'AssistantName' => 'assistantname',
  22. 'assistantPhoneNumber' => 'tel_assistent',
  23. 'birthday' => 'bday',
  24. 'body' => 'note',
  25. #'Business2PhoneNumber' => 'business2phonenumber',
  26. 'businessAddressCity' => 'adr_one_locality',
  27. 'businessAddressCountry' => 'adr_one_countryname',
  28. 'businessAddressPostalCode' => 'adr_one_postalcode',
  29. 'businessAddressState' => 'adr_one_region',
  30. 'businessAddressStreet' => 'adr_one_street',
  31. 'businessFaxNumber' => 'tel_fax',
  32. 'businessPhoneNumber' => 'tel_work',
  33. #'CarPhoneNumber' => 'carphonenumber',
  34. #'Categories' => 'categories',
  35. #'Category' => 'category',
  36. #'Children' => 'children',
  37. #'Child' => 'child',
  38. 'companyName' => 'org_name',
  39. 'department' => 'org_unit',
  40. 'email1Address' => 'email',
  41. 'email2Address' => 'email_home',
  42. #'Email3Address' => 'email3address',
  43. 'fileAs' => 'n_fileas',
  44. 'firstName' => 'n_given',
  45. 'home2PhoneNumber' => 'tel_cell_private',
  46. 'homeAddressCity' => 'adr_two_locality',
  47. 'homeAddressCountry' => 'adr_two_countryname',
  48. 'homeAddressPostalCode' => 'adr_two_postalcode',
  49. 'homeAddressState' => 'adr_two_region',
  50. 'homeAddressStreet' => 'adr_two_street',
  51. 'homeFaxNumber' => 'tel_fax_home',
  52. 'homePhoneNumber' => 'tel_home',
  53. 'jobTitle' => 'title',
  54. 'lastName' => 'n_family',
  55. 'middleName' => 'n_middle',
  56. 'mobilePhoneNumber' => 'tel_cell',
  57. 'officeLocation' => 'room',
  58. #'OtherCity' => 'adr_one_locality',
  59. #'OtherCountry' => 'adr_one_countryname',
  60. #'OtherPostalCode' => 'adr_one_postalcode',
  61. #'OtherState' => 'adr_one_region',
  62. #'OtherStreet' => 'adr_one_street',
  63. 'pagerNumber' => 'tel_pager',
  64. #'RadioPhoneNumber' => 'radiophonenumber',
  65. #'Spouse' => 'spouse',
  66. 'suffix' => 'n_prefix',
  67. #'Title' => '', //salutation
  68. 'webPage' => 'url',
  69. #'YomiCompanyName' => 'yomicompanyname',
  70. #'YomiFirstName' => 'yomifirstname',
  71. #'YomiLastName' => 'yomilastname',
  72. #'Rtf' => 'rtf',
  73. 'picture' => 'jpegphoto'
  74. );
  75. /**
  76. * name of Tine 2.0 backend application
  77. *
  78. * @var string
  79. */
  80. protected $_applicationName = 'Addressbook';
  81. /**
  82. * name of Tine 2.0 model to use
  83. *
  84. * @var string
  85. */
  86. protected $_modelName = 'Contact';
  87. /**
  88. * type of the default folder
  89. *
  90. * @var int
  91. */
  92. protected $_defaultFolderType = Syncroton_Command_FolderSync::FOLDERTYPE_CONTACT;
  93. /**
  94. * default container for new entries
  95. *
  96. * @var string
  97. */
  98. protected $_defaultFolder = ActiveSync_Preference::DEFAULTADDRESSBOOK;
  99. /**
  100. * type of user created folders
  101. *
  102. * @var int
  103. */
  104. protected $_folderType = Syncroton_Command_FolderSync::FOLDERTYPE_CONTACT_USER_CREATED;
  105. /**
  106. * name of property which defines the filterid for different content classes
  107. *
  108. * @var string
  109. */
  110. protected $_filterProperty = 'contactsfilterId';
  111. /**
  112. * field to sort search results by
  113. *
  114. * @var string
  115. */
  116. protected $_sortField = 'n_fileas';
  117. protected $_defaultContainerPreferenceName = Addressbook_Preference::DEFAULTADDRESSBOOK;
  118. /**
  119. * Search command handler
  120. *
  121. * the search command is only a stub to make the AS Search command happy
  122. * Tine 2.0 sync's the GAL entries as normal adddressbooks
  123. *
  124. * @param Syncroton_Model_StoreRequest $store Search query parameters
  125. * @return Syncroton_Model_StoreResponse
  126. */
  127. public function search(Syncroton_Model_StoreRequest $store)
  128. {
  129. $storeResponse = new Syncroton_Model_StoreResponse();
  130. $storeResponse->total = 0;
  131. return $storeResponse;
  132. }
  133. /**
  134. * (non-PHPdoc)
  135. * @see ActiveSync_Frontend_Abstract::toSyncrotonModel()
  136. */
  137. public function toSyncrotonModel($entry, array $options = array())
  138. {
  139. if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(
  140. __METHOD__ . '::' . __LINE__ . " contact data " . print_r($entry->toArray(), TRUE));
  141. $syncrotonContact = new Syncroton_Model_Contact();
  142. foreach ($this->_mapping as $syncrotonProperty => $tine20Property) {
  143. // skip empty values
  144. if (empty($entry->$tine20Property) && $entry->$tine20Property != '0' || count($entry->$tine20Property) === 0) {
  145. continue;
  146. }
  147. switch($tine20Property) {
  148. case 'adr_one_countryname':
  149. case 'adr_two_countryname':
  150. $syncrotonContact->$syncrotonProperty = Tinebase_Translation::getCountryNameByRegionCode($entry->$tine20Property);
  151. break;
  152. case 'bday':
  153. $syncrotonContact->$syncrotonProperty = $entry->$tine20Property;
  154. if ($this->_device->devicetype == Syncroton_Model_Device::TYPE_BLACKBERRY && version_compare($this->_device->getMajorVersion(), '10', '>=')) {
  155. // BB 10+ expects birthday to be at noon
  156. $syncrotonContact->$syncrotonProperty->addHour(12);
  157. }
  158. break;
  159. case 'note':
  160. $syncrotonContact->$syncrotonProperty = new Syncroton_Model_EmailBody(array(
  161. 'type' => Syncroton_Model_EmailBody::TYPE_PLAINTEXT,
  162. 'data' => $entry->$tine20Property
  163. ));
  164. break;
  165. case 'jpegphoto':
  166. try {
  167. $image = Tinebase_Controller::getInstance()->getImage('Addressbook', $entry->getId());
  168. $syncrotonContact->$syncrotonProperty = $image->getBlob('image/jpeg', 36000);
  169. } catch (Exception $e) {
  170. Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . " Image for contact {$entry->getId()} not found or invalid");
  171. }
  172. break;
  173. // @todo validate tags are working
  174. case 'tags':
  175. $syncrotonContact->$syncrotonProperty = $entry->$tine20Property->name;
  176. break;
  177. default:
  178. $syncrotonContact->$syncrotonProperty = $entry->$tine20Property;
  179. break;
  180. }
  181. }
  182. return $syncrotonContact;
  183. }
  184. /**
  185. * convert contact from xml to Addressbook_Model_Contact
  186. *
  187. * @param SimpleXMLElement $_data
  188. * @return Addressbook_Model_Contact
  189. */
  190. public function toTineModel(Syncroton_Model_IEntry $data, $entry = null)
  191. {
  192. if ($entry instanceof Addressbook_Model_Contact) {
  193. $contact = $entry;
  194. } else {
  195. $contact = new Addressbook_Model_Contact(null, true);
  196. }
  197. unset($contact->jpegphoto);
  198. foreach($this->_mapping as $fieldName => $value) {
  199. if (!isset($data->$fieldName)) {
  200. $contact->$value = null;
  201. continue;
  202. }
  203. switch ($value) {
  204. case 'jpegphoto':
  205. if(!empty($data->$fieldName)) {
  206. $devicePhoto = $data->$fieldName;
  207. try {
  208. $currentPhoto = Tinebase_Controller::getInstance()->getImage('Addressbook', $contact->getId())->getBlob('image/jpeg', 36000);
  209. } catch (Exception $e) {}
  210. if (isset($currentPhoto) && $currentPhoto == $devicePhoto) {
  211. if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->INFO(__METHOD__ . '::' . __LINE__ . " photo did not change on device -> preserving server photo");
  212. } else {
  213. if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->INFO(__METHOD__ . '::' . __LINE__ . " using new contact photo from device (" . strlen($devicePhoto) . "KB)");
  214. $contact->jpegphoto = $devicePhoto;
  215. }
  216. } else if ($entry && ! empty($entry->jpegphoto)) {
  217. $contact->jpegphoto = '';
  218. if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->INFO(__METHOD__ . '::' . __LINE__
  219. . ' Deleting contact photo on device request (contact id: ' . $contact->getId() . ')');
  220. }
  221. break;
  222. case 'bday':
  223. $contact->$value = new Tinebase_DateTime($data->$fieldName);
  224. if ($this->_device->devicetype == Syncroton_Model_Device::TYPE_IPHONE && $this->_device->getMajorVersion() < 800) {
  225. // iOS < 4 & webow < 2.1 send birthdays to the entered date, but the time the birthday got entered on the device
  226. // acutally iOS < 4 somtimes sends the bday at noon but the timezone is not clear
  227. // -> we don't trust the time part and set the birthdays timezone to the timezone the user has set in tine
  228. $userTimezone = Tinebase_Core::getUserTimezone();
  229. $contact->$value = new Tinebase_DateTime($contact->bday->setTime(0,0,0)->format(Tinebase_Record_Abstract::ISO8601LONG), $userTimezone);
  230. $contact->$value->setTimezone('UTC');
  231. } elseif ($this->_device->devicetype == Syncroton_Model_Device::TYPE_BLACKBERRY && version_compare($this->_device->getMajorVersion(), '10', '>=')) {
  232. // BB 10+ expects birthday to be at noon
  233. $contact->$value->subHour(12);
  234. }
  235. break;
  236. case 'adr_one_countryname':
  237. case 'adr_two_countryname':
  238. $contact->$value = Tinebase_Translation::getRegionCodeByCountryName($data->$fieldName);
  239. break;
  240. case 'adr_one_street':
  241. if (strtolower($this->_device->devicetype) == 'palm') {
  242. // palm pre sends the whole address in the <Contacts:BusinessStreet> tag
  243. unset($contact->adr_one_street);
  244. } else {
  245. $contact->$value = $data->$fieldName;
  246. }
  247. break;
  248. case 'email':
  249. case 'email_home':
  250. // android sends email address as
  251. // Lars Kneschke <l.kneschke@metaways.de>
  252. if (preg_match('/(.*)<(.+@[^@]+)>/', $data->$fieldName, $matches)) {
  253. $contact->$value = trim($matches[2]);
  254. } else {
  255. $contact->$value = $data->$fieldName;
  256. }
  257. break;
  258. case 'note':
  259. // @todo check $data->$fieldName->Type and convert to/from HTML if needed
  260. if ($data->$fieldName instanceof Syncroton_Model_EmailBody) {
  261. $contact->$value = $data->$fieldName->data;
  262. } else {
  263. $contact->$value = null;
  264. }
  265. break;
  266. case 'url':
  267. // remove facebook urls
  268. if (! preg_match('/^fb:\/\//', $data->$fieldName)) {
  269. $contact->$value = $data->$fieldName;
  270. }
  271. break;
  272. default:
  273. $contact->$value = $data->$fieldName;
  274. break;
  275. }
  276. }
  277. // force update of n_fileas and n_fn
  278. $contact->setFromArray(array(
  279. 'n_given' => $contact->n_given,
  280. 'n_family' => $contact->n_family,
  281. 'org_name' => $contact->org_name
  282. ));
  283. // either "org_name" or "n_family" must be given!
  284. if (empty($contact->org_name) && empty($contact->n_family)) {
  285. $contact->n_family = 'imported';
  286. }
  287. // contact should be valid now
  288. $contact->isValid();
  289. if (Tinebase_Core::isLogLevel(Zend_Log::TRACE))
  290. Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . " contactData " . print_r($contact->toArray(), true));
  291. return $contact;
  292. }
  293. /**
  294. * get devices with multiple folders
  295. *
  296. * @return array
  297. */
  298. protected function _getDevicesWithMultipleFolders()
  299. {
  300. // outlook currently (Microsoft.Outlook.15) does not support mutliple addressbooks
  301. // @see 0009184: Only Admin Contact Data is synced (Outlook 2013)
  302. $doesNotSupportMultipleFolders = array('windowsoutlook15');
  303. $result = array_diff(parent::_getDevicesWithMultipleFolders(), $doesNotSupportMultipleFolders);
  304. return $result;
  305. }
  306. }