1: <?php
2: 3: 4: 5: 6: 7: 8:
9: class Turba
10: {
11: 12: 13:
14: const VFS_PATH = '.horde/turba/documents';
15:
16: 17: 18: 19: 20: 21:
22: static public function availableSources()
23: {
24: $cfgSources = Horde::loadConfiguration('backends.php', 'cfgSources', 'turba');
25: $sources = array();
26: foreach ($cfgSources as $key => $source) {
27: if (empty($source['disabled'])) {
28: $sources[$key] = $source;
29: }
30: }
31: return $sources;
32: }
33:
34: 35: 36: 37: 38: 39: 40: 41: 42:
43: static public function getAddressBooks($permission = Horde_Perms::READ,
44: array $options = array())
45: {
46: $addressbooks = array();
47: foreach (array_keys(self::getAddressBookOrder()) as $addressbook) {
48: $addressbooks[$addressbook] = $GLOBALS['cfgSources'][$addressbook];
49: }
50:
51: if (!$addressbooks) {
52: $addressbooks = $GLOBALS['cfgSources'];
53: }
54:
55: return self::permissionsFilter($addressbooks, $permission, $options);
56: }
57:
58: 59: 60: 61: 62: 63:
64: static public function getAddressBookOrder()
65: {
66: $lines = json_decode($GLOBALS['prefs']->getValue('addressbooks'));
67: $addressbooks = array();
68: if (!empty($lines)) {
69: $i = 0;
70: foreach ($lines as $line) {
71: $line = trim($line);
72: if ($line && isset($GLOBALS['cfgSources'][$line])) {
73: $addressbooks[$line] = $i++;
74: }
75: }
76: }
77: return $addressbooks;
78: }
79:
80: 81: 82: 83: 84:
85: static public function getDefaultAddressbook()
86: {
87: $lines = json_decode($GLOBALS['prefs']->getValue('addressbooks'));
88: if (!empty($lines)) {
89: foreach ($lines as $line) {
90: $line = trim($line);
91: if ($line && isset($GLOBALS['cfgSources'][$line])) {
92: return $line;
93: }
94: }
95: }
96:
97:
98: if (!empty($_SESSION['turba']['has_share'])) {
99: try {
100: $owned_shares = self::listShares(true);
101: if (count($owned_shares) > 0) {
102: return key($owned_shares);
103: }
104: } catch (Exception $e) {}
105: }
106:
107: reset($GLOBALS['cfgSources']);
108: return key($GLOBALS['cfgSources']);
109: }
110:
111: 112: 113:
114: static public function getPreferredSortOrder()
115: {
116: return @unserialize($GLOBALS['prefs']->getValue('sortorder'));
117: }
118:
119: 120: 121: 122: 123: 124:
125: static public function setPreferredSortOrder(Horde_Variables $vars,
126: $source)
127: {
128: if (!strlen($sortby = $vars->get('sortby'))) {
129: return;
130: }
131:
132: $sources = self::getColumns();
133: $columns = isset($sources[$source])
134: ? $sources[$source]
135: : array();
136: $column_name = self::getColumnName($sortby, $columns);
137:
138: $append = true;
139: $ascending = ($vars->get('sortdir') == 0);
140:
141: if ($vars->get('sortadd')) {
142: $sortorder = self::getPreferredSortOrder();
143: foreach ($sortorder as $i => $elt) {
144: if ($elt['field'] == $column_name) {
145: $sortorder[$i]['ascending'] = $ascending;
146: $append = false;
147: }
148: }
149: } else {
150: $sortorder = array();
151: }
152:
153: if ($append) {
154: $sortorder[] = array(
155: 'ascending' => $ascending,
156: 'field' => $column_name
157: );
158: }
159:
160: $GLOBALS['prefs']->setValue('sortorder', serialize($sortorder));
161: }
162:
163: 164: 165: 166: 167: 168: 169: 170:
171: static public function getColumnName($i, $columns)
172: {
173: return (($i == 0) || !isset($columns[$i - 1]))
174: ? 'name'
175: : $columns[$i - 1];
176: }
177:
178: 179:
180: static public function getColumns()
181: {
182: $columns = array();
183: $lines = explode("\n", $GLOBALS['prefs']->getValue('columns'));
184: foreach ($lines as $line) {
185: $line = trim($line);
186: if ($line) {
187: $cols = explode("\t", $line);
188: if (count($cols) > 1) {
189: $source = array_splice($cols, 0, 1);
190: $columns[$source[0]] = $cols;
191: }
192: }
193: }
194:
195: return $columns;
196: }
197:
198: 199: 200: 201: 202: 203: 204: 205:
206: static public function formatCompositeField($format, $fields)
207: {
208: return preg_replace('/ +/', ' ', trim(vsprintf($format, $fields), " \t\n\r\0\x0B,"));
209: }
210:
211: 212: 213: 214: 215: 216: 217:
218: static public function guessLastname($name)
219: {
220: $name = trim(preg_replace('|\s|', ' ', $name));
221: if (!empty($name)) {
222:
223: if (is_int(strpos($name, ','))) {
224: $name = Horde_String::substr($name, 0, strpos($name, ','));
225: }
226:
227:
228: $name = trim(preg_replace('|\(.*\)|', '', $name));
229:
230: $namelist = explode(' ', $name);
231: $name = $namelist[($nameindex = (count($namelist) - 1))];
232:
233: while (!empty($name) && Horde_String::length($name) < 5 &&
234: strspn($name[(Horde_String::length($name) - 1)], '.:-') &&
235: !empty($namelist[($nameindex - 1)])) {
236: $nameindex--;
237: $name = $namelist[$nameindex];
238: }
239: }
240: return strlen($name) ? $name : null;
241: }
242:
243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258:
259: static public function formatName(Turba_Object $ob, $name_format = null)
260: {
261: static $default_format;
262:
263: if (!$name_format) {
264: if (!isset($default_format)) {
265: $default_format = $GLOBALS['prefs']->getValue('name_format');
266: }
267: $name_format = $default_format;
268: }
269:
270:
271: if ($name_format != 'first_last' && $name_format != 'last_first') {
272: return $ob->getValue('name');
273: }
274:
275:
276: if ($ob->hasValue('firstname') && $ob->hasValue('lastname')) {
277: if ($name_format == 'last_first') {
278: return $ob->getValue('lastname') . ', ' . $ob->getValue('firstname');
279: } else {
280: return $ob->getValue('firstname') . ' ' . $ob->getValue('lastname');
281: }
282: } else {
283:
284: $name = $ob->getValue('name');
285: $lastname = self::guessLastname($name);
286: if ($name_format == 'last_first' &&
287: !is_int(strpos($name, ',')) &&
288: Horde_String::length($name) > Horde_String::length($lastname)) {
289: $name = preg_replace('/\s+' . preg_quote($lastname, '/') . '/', '', $name);
290: $name = $lastname . ', ' . $name;
291: }
292: if ($name_format == 'first_last' &&
293: is_int(strpos($name, ',')) &&
294: Horde_String::length($name) > Horde_String::length($lastname)) {
295: $name = preg_replace('/' . preg_quote($lastname, '/') . ',\s*/', '', $name);
296: $name = $name . ' ' . $lastname;
297: }
298:
299: return $name;
300: }
301: }
302:
303: 304: 305: 306: 307: 308: 309: 310: 311: 312:
313: static public function formatEmailAddresses($data, $name)
314: {
315: global $registry;
316: static $useRegistry;
317:
318: if (!isset($useRegistry)) {
319: $useRegistry = $registry->hasMethod('mail/batchCompose');
320: }
321:
322: $array = is_array($data);
323: if (!$array) {
324: $data = array($data);
325: }
326:
327: $addresses = array();
328: foreach ($data as $i => $email_vals) {
329: $email_vals = explode(',', $email_vals);
330: foreach ($email_vals as $j => $email_val) {
331: $email_val = trim($email_val);
332:
333:
334: $mailbox_host = explode('@', $email_val);
335: if (!isset($mailbox_host[1])) {
336: $mailbox_host[1] = '';
337: }
338: $address = Horde_Mime_Address::writeAddress($mailbox_host[0], $mailbox_host[1], $name);
339:
340:
341:
342: $addresses[$i . ':' . $j] = array('to' => addslashes(str_replace('@>', '>', $address)));
343: }
344: }
345:
346: if ($useRegistry) {
347: try {
348: $addresses = $GLOBALS['registry']->call('mail/batchCompose', array($addresses));
349: } catch (Horde_Exception $e) {
350: $useRegistry = false;
351: $addresses = array();
352: }
353: } else {
354: $addresses = array();
355: }
356:
357: foreach ($data as $i => $email_vals) {
358: $email_vals = explode(',', $email_vals);
359: $email_values = false;
360: foreach ($email_vals as $j => $email_val) {
361: if (isset($addresses[$i . ':' . $j])) {
362: $mail_link = $addresses[$i . ':' . $j];
363: } else {
364: $mail_link = 'mailto:' . urlencode($email_val);
365: }
366:
367: $email_value = Horde::link($mail_link) . htmlspecialchars($email_val) . '</a>';
368: if ($email_values) {
369: $email_values .= ', ' . $email_value;
370: } else {
371: $email_values = $email_value;
372: }
373: }
374: }
375:
376: if ($array) {
377: return $email_values[0];
378: } else {
379: return $email_values;
380: }
381: }
382:
383: 384: 385: 386: 387: 388: 389:
390: static public function getUserName($uid)
391: {
392: static $names = array();
393:
394: if (!isset($names[$uid])) {
395: $ident = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($uid);
396: $ident->setDefault($ident->getDefault());
397: $names[$uid] = $ident->getValue('fullname');
398: if (empty($names[$uid])) {
399: $names[$uid] = $uid;
400: }
401: }
402:
403: return $names[$uid];
404: }
405:
406: 407: 408: 409: 410: 411: 412: 413: 414: 415:
416: static public function getExtendedPermission(Turba_Driver $addressBook, $permission)
417: {
418:
419:
420: $key = $addressBook->getName() . ':' . $permission;
421:
422: $perms = $GLOBALS['injector']->getInstance('Horde_Perms');
423: if (!$perms->exists('turba:sources:' . $key)) {
424: return true;
425: }
426:
427: $allowed = $perms->getPermissions('turba:sources:' . $key, $GLOBALS['registry']->getAuth());
428: if (is_array($allowed)) {
429: switch ($permission) {
430: case 'max_contacts':
431: $allowed = max($allowed);
432: break;
433: }
434: }
435: return $allowed;
436: }
437:
438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448:
449: static public function permissionsFilter(array $in, $permission = Horde_Perms::READ, array $options = array())
450: {
451: $out = array();
452:
453: foreach ($in as $sourceId => $source) {
454: try {
455: $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver')->create($sourceId);
456: } catch (Turba_Exception $e) {
457: Horde::logMessage($e, 'ERR');
458: continue;
459: }
460:
461: if ($driver->hasPermission($permission)) {
462: if (!empty($options['require_add']) && !$driver->canAdd()) {
463: continue;
464: }
465: $out[$sourceId] = $source;
466: }
467: }
468:
469: return $out;
470: }
471:
472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483:
484: static public function getConfigFromShares(array $sources)
485: {
486: try {
487: $shares = self::listShares();
488: } catch (Horde_Share_Exception $e) {
489:
490:
491: $GLOBALS['notification']->push($e, 'horde.error');
492: return $sources;
493: }
494:
495: 496:
497: $all_shares = null;
498: foreach ($sources as $key => $cfg) {
499: if (!empty($cfg['all_shares'])) {
500:
501: $all_shares = $key;
502: }
503: }
504:
505: $sortedSources = $defaults = $vbooks = array();
506: $personal = false;
507: foreach ($shares as $name => &$share) {
508: if (isset($sources[$name])) {
509: continue;
510: }
511:
512: $personal |= $share->get('owner') == $GLOBALS['registry']->getAuth();
513:
514: $params = @unserialize($share->get('params'));
515: if (empty($params['source']) && !empty($all_shares)) {
516: $params['source'] = $all_shares;
517: }
518: if (isset($params['type']) && $params['type'] == 'vbook') {
519:
520: $params['share'] = $share;
521: $vbooks[$name] = $params;
522: } elseif (!empty($params['source']) &&
523: !empty($sources[$params['source']]['use_shares'])) {
524: if (empty($params['name'])) {
525: $params['name'] = $name;
526: $share->set('params', serialize($params));
527: try {
528: $share->save();
529: } catch (Horde_Share_Exception $e) {
530: Horde::logMessage($e, 'ERR');
531: }
532: }
533:
534: $info = $sources[$params['source']];
535: $info['params']['config'] = $sources[$params['source']];
536: $info['params']['config']['params']['share'] = $share;
537: $info['params']['config']['params']['name'] = $params['name'];
538: $info['title'] = $share->get('name');
539: $info['type'] = 'share';
540: $info['use_shares'] = false;
541: $sortedSources[$params['source']][$name] = $info;
542: }
543: }
544:
545:
546: $newSources = array();
547: foreach (array_keys($sources) as $source) {
548: if (empty($sources[$source]['use_shares'])) {
549: $newSources[$source] = $sources[$source];
550: continue;
551: }
552: if (isset($sortedSources[$source])) {
553: $newSources = array_merge($newSources, $sortedSources[$source]);
554: }
555: if (!empty($GLOBALS['conf']['share']['auto_create']) &&
556: $GLOBALS['registry']->getAuth() && !$personal) {
557:
558:
559: try {
560: $driver = $GLOBALS['injector']->getInstance('Turba_Factory_Driver')->create($source);
561: } catch (Turba_Exception $e) {
562: $GLOBALS['notification']->push($e->getMessage(), 'horde.error');
563: continue;
564: }
565:
566: $sourceKey = strval(new Horde_Support_Randomid());
567: try {
568: $share = $driver->createShare(
569: $sourceKey,
570: array(
571: 'params' => array(
572: 'source' => $source,
573: 'default' => true,
574: 'name' => $GLOBALS['registry']->getAuth()
575: )
576: )
577: );
578: } catch (Horde_Share_Exception $e) {
579: Horde::logMessage($e, 'ERR');
580: continue;
581: }
582:
583: $source_config = $sources[$source];
584: $source_config['params']['share'] = $share;
585: $newSources[$sourceKey] = $source_config;
586: $personal = true;
587: }
588: }
589:
590:
591: foreach ($vbooks as $name => $params) {
592: if (!isset($newSources[$params['source']])) {
593: continue;
594: }
595: $newSources[$name] = array(
596: 'title' => $shares[$name]->get('name'),
597: 'type' => 'vbook',
598: 'params' => $params,
599: 'export' => true,
600: 'browse' => true,
601: 'map' => $newSources[$params['source']]['map'],
602: 'search' => $newSources[$params['source']]['search'],
603: 'strict' => $newSources[$params['source']]['strict'],
604: 'use_shares' => false,
605: );
606: }
607:
608: return $newSources;
609: }
610:
611: 612: 613: 614: 615: 616: 617:
618: static public function getSourceFromShare(Horde_Share $share)
619: {
620:
621: $cfgSources = Turba::availableSources();
622:
623: $params = @unserialize($share->get('params'));
624: $newConfig = $cfgSources[$params['source']];
625: $newConfig['params']['config'] = $cfgSources[$params['source']];
626: $newConfig['params']['config']['params']['share'] = $share;
627: $newConfig['params']['config']['params']['name'] = $params['name'];
628: $newConfig['title'] = $share->get('name');
629: $newConfig['type'] = 'share';
630: $newConfig['use_shares'] = false;
631:
632: return $newConfig;
633: }
634:
635: 636: 637: 638: 639: 640: 641: 642: 643:
644: static public function listShares($owneronly = false, $permission = Horde_Perms::READ)
645: {
646: if (!$GLOBALS['session']->get('turba', 'has_share')) {
647:
648: return array();
649: }
650: if ($owneronly && !$GLOBALS['registry']->getAuth()) {
651: return array();
652: }
653:
654: try {
655: $sources = $GLOBALS['turba_shares']->listShares(
656: $GLOBALS['registry']->getAuth(),
657: array('perm' => $permission,
658: 'attributes' => $owneronly ? $GLOBALS['registry']->getAuth() : null));
659: } catch (Horde_Share_Exception $e) {
660: Horde::logMessage($e, 'ERR');
661: return array();
662: }
663:
664: return $sources;
665: }
666:
667: 668: 669: 670: 671: 672: 673: 674: 675:
676: static public function createShare($share_name, $params)
677: {
678: if (!isset($params['name'])) {
679:
680: $identity = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create();
681: $name = sprintf(_("Address book of %s"), $identity->getName());
682: } else {
683: $name = $params['name'];
684: unset($params['name']);
685: }
686:
687:
688: try {
689: $share = $GLOBALS['turba_shares']->newShare($GLOBALS['registry']->getAuth(), $share_name, $name);
690:
691:
692: foreach ($params as $key => $value) {
693: if (!is_scalar($value)) {
694: $value = serialize($value);
695: }
696: $share->set($key, $value);
697: }
698: $GLOBALS['turba_shares']->addShare($share);
699: $result = $share->save();
700: } catch (Horde_Share_Exception $e) {
701: Horde::logMessage($e, 'ERR');
702: throw new Turba_Exception($e);
703: }
704:
705: 706:
707: $share_name = $share->getName();
708:
709: 710:
711: $prefs = json_decode($GLOBALS['prefs']->getValue('addressbooks'), true);
712: if (!is_array($prefs) || array_search($share_name, $prefs) === false) {
713: $prefs[] = $share_name;
714: $GLOBALS['prefs']->setValue('addressbooks', json_encode($prefs));
715: }
716:
717: return $share;
718: }
719:
720: 721: 722:
723: static public function addBrowseJs()
724: {
725: Horde::addScriptFile('browse.js', 'turba');
726: Horde::addInlineJsVars(array(
727: 'TurbaBrowse.confirmdelete' => _("Are you sure that you want to delete %s?"),
728: 'TurbaBrowse.contact1' => _("You must select at least one contact first."),
729: 'TurbaBrowse.contact2' => _("You must select a target contact list."),
730: 'TurbaBrowse.contact3' => _("Please name the new contact list:"),
731: 'TurbaBrowse.copymove' => _("You must select a target address book."),
732: 'TurbaBrowse.submit' => _("Are you sure that you want to delete the selected contacts?")
733: ));
734: }
735:
736: }
737: