1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15:
16:
17: if (!defined('KRONOLITH_BASE')) {
18: define('KRONOLITH_BASE', dirname(__FILE__) . '/..');
19: }
20:
21: if (!defined('HORDE_BASE')) {
22: 23:
24: if (file_exists(KRONOLITH_BASE . '/config/horde.local.php')) {
25: include KRONOLITH_BASE . '/config/horde.local.php';
26: } else {
27: define('HORDE_BASE', KRONOLITH_BASE . '/..');
28: }
29: }
30:
31: 32:
33: require_once HORDE_BASE . '/lib/core.php';
34:
35: class Kronolith_Application extends Horde_Registry_Application
36: {
37: 38:
39: public $ajaxView = true;
40:
41: 42:
43: public $mobileView = true;
44:
45: 46:
47: public $version = 'H4 (3.0.19-git)';
48:
49: 50: 51: 52: 53:
54: protected function _init()
55: {
56: 57: 58:
59: $GLOBALS['injector']->getInstance('Horde_Autoloader')->addClassPathMapper(new Horde_Autoloader_ClassPathMapper_Prefix('/^Content_/', $GLOBALS['registry']->get('fileroot', 'content') . '/lib/'));
60: if (!class_exists('Content_Tagger')) {
61: throw new Horde_Exception('The Content_Tagger class could not be found. Make sure the Content application is installed.');
62: }
63:
64: $GLOBALS['injector']->bindFactory('Kronolith_Geo', 'Kronolith_Factory_Geo', 'create');
65:
66:
67: $GLOBALS['registry']->setTimeZone();
68:
69:
70: $GLOBALS['kronolith_shares'] = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Share')->create();
71:
72: Kronolith::initialize();
73:
74: $GLOBALS['linkTags'] = array();
75: foreach ($GLOBALS['display_calendars'] as $calendar) {
76: $GLOBALS['linkTags'][] = '<link href="' . Kronolith::feedUrl($calendar) . '" rel="alternate" type="application/atom+xml" />';
77: }
78: }
79:
80: 81:
82: public function perms()
83: {
84: return array(
85: 'max_events' => array(
86: 'title' => _("Maximum Number of Events"),
87: 'type' => 'int'
88: )
89: );
90: }
91:
92: 93:
94: public function ($menu)
95: {
96: global $browser, $conf, $injector, $notification, $prefs, $registry;
97:
98: 99:
100: if (!$registry->getAuth() && !count(Kronolith::listCalendars())) {
101: $notification->push(_("No calendars are available to guests."));
102: }
103:
104: $menu->add(Horde::url($prefs->getValue('defaultview') . '.php'), _("_Today"), 'today.png', null, null, null, '__noselection');
105: if (Kronolith::getDefaultCalendar(Horde_Perms::EDIT) &&
106: ($injector->getInstance('Horde_Core_Perms')->hasAppPermission('max_events') === true ||
107: $injector->getInstance('Horde_Core_Perms')->hasAppPermission('max_events') > Kronolith::countEvents())) {
108: $menu->add(Horde::url('new.php')->add('url', Horde::selfUrl(true, false, true)), _("_New Event"), 'new.png');
109: }
110:
111: if ($browser->hasFeature('dom')) {
112: Horde_Core_Ui_JsCalendar::init(array(
113: 'click_month' => true,
114: 'click_week' => true,
115: 'click_year' => true,
116: 'full_weekdays' => true
117: ));
118: Horde::addScriptFile('goto.js', 'kronolith');
119: Horde::addInlineJsVars(array(
120: 'KronolithGoto.dayurl' => strval(Horde::url('day.php')),
121: 'KronolithGoto.monthurl' => strval(Horde::url('month.php')),
122: 'KronolithGoto.weekurl' => strval(Horde::url('week.php')),
123: 'KronolithGoto.yearurl' => strval(Horde::url('year.php'))
124: ));
125: $menu->add(new Horde_Url(''), _("_Goto"), 'goto.png', null, '', null, 'kgotomenu');
126: }
127: $menu->add(Horde::url('search.php'), _("_Search"), 'search.png');
128:
129:
130: if ($conf['menu']['import_export'] &&
131: !Kronolith::showAjaxView()) {
132: $menu->add(Horde::url('data.php'), _("_Import/Export"), 'data.png');
133: }
134: }
135:
136: 137:
138: public function hasPermission($permission, $allowed, $opts = array())
139: {
140: if (is_array($allowed)) {
141: switch ($permission) {
142: case 'max_events':
143: $allowed = max($allowed);
144: break;
145: }
146: }
147: return $allowed;
148: }
149:
150: 151:
152: public function prefsInit($ui)
153: {
154: global $prefs, $registry;
155:
156:
157: if (!$registry->hasMethod('contacts/sources')) {
158: $ui->suppressGroups[] = 'addressbooks';
159: }
160:
161: if ($prefs->isLocked('default_alarm')) {
162: $ui->suppressGroups[] = 'event_options';
163: }
164: }
165:
166: 167:
168: public function prefsGroup($ui)
169: {
170: global $conf, $prefs;
171:
172: foreach ($ui->getChangeablePrefs() as $val) {
173: switch ($val) {
174: case 'day_hour_end':
175: case 'day_hour_start':
176: $hour = array();
177: for ($i = 0; $i <= 48; ++$i) {
178: $hour[$i] = date(($prefs->getValue('twentyFour')) ? 'G:i' : 'g:ia', mktime(0, $i * 30, 0));
179: }
180: $ui->override[$val] = $hour;
181: break;
182:
183: case 'default_share':
184: foreach (Kronolith::listInternalCalendars(false, Horde_Perms::EDIT) as $id => $calendar) {
185: $ui->override['default_share'][$id] = $calendar->get('name');
186: }
187: break;
188: case 'sync_calendars':
189: $sync = @unserialize($prefs->getValue('sync_calendars'));
190: if (empty($sync)) {
191: $prefs->setValue('sync_calendars', serialize(array(Kronolith::getDefaultCalendar())));
192: }
193: $out = array();
194: foreach (Kronolith::listInternalCalendars(true, Horde_Perms::EDIT) as $key => $cal) {
195: if ($cal->getName() != Kronolith::getDefaultCalendar(Horde_Perms::EDIT)) {
196: $out[$key] = $cal->get('name');
197: }
198: }
199: $ui->override['sync_calendars'] = $out;
200: break;
201: case 'event_alarms_select':
202: if (empty($conf['alarms']['driver']) ||
203: $prefs->isLocked('event_alarms_select')) {
204: $ui->suppress[] = 'event_alarms';
205: } else {
206: Horde_Core_Prefs_Ui_Widgets::alarmInit();
207: }
208: break;
209:
210: case 'fb_cals':
211: $fb_list = array();
212: foreach (Kronolith::listCalendars() as $fb_cal => $cal) {
213: if ($cal->display()) {
214: $fb_list[htmlspecialchars($fb_cal)] = htmlspecialchars($cal->name());
215: }
216: }
217: $ui->override['fb_cals'] = $fb_list;
218: break;
219:
220: case 'sourceselect':
221: Horde_Core_Prefs_Ui_Widgets::addressbooksInit();
222: break;
223: }
224: }
225: }
226:
227: 228:
229: public function prefsSpecial($ui, $item)
230: {
231: switch ($item) {
232: case 'default_alarm_management':
233: return $this->_defaultAlarmManagement($ui);
234:
235: case 'event_alarms_select':
236: return Horde_Core_Prefs_Ui_Widgets::alarm(array(
237: 'label' => _("Choose how you want to receive reminders for events with alarms:"),
238: 'pref' => 'event_alarms'
239: ));
240:
241: case 'sourceselect':
242: $search = Kronolith::getAddressbookSearchParams();
243: return Horde_Core_Prefs_Ui_Widgets::addressbooks(array(
244: 'fields' => $search['fields'],
245: 'sources' => $search['sources']
246: ));
247: }
248:
249: return '';
250: }
251:
252: 253:
254: public function prefsSpecialUpdate($ui, $item)
255: {
256: switch ($item) {
257: case 'default_alarm_management':
258: $GLOBALS['prefs']->setValue('default_alarm', (int)$ui->vars->alarm_value * (int)$ui->vars->alarm_unit);
259: return true;
260:
261: case 'event_alarms_select':
262: $data = Horde_Core_Prefs_Ui_Widgets::alarmUpdate($ui, array('pref' => 'event_alarms'));
263: if (!is_null($data)) {
264: $GLOBALS['prefs']->setValue('event_alarms', serialize($data));
265: return true;
266: }
267: break;
268:
269: case 'remote_cal_management':
270: return $this->_prefsRemoteCalManagement($ui);
271:
272: case 'sourceselect':
273: return $this->_prefsSourceselect($ui);
274: }
275:
276: return false;
277: }
278:
279: 280:
281: public function prefsCallback($ui)
282: {
283: if ($GLOBALS['prefs']->isDirty('event_alarms')) {
284: try {
285: $alarms = $GLOBALS['registry']->callAppMethod('kronolith', 'listAlarms', array('args' => array($_SERVER['REQUEST_TIME'])));
286: if (!empty($alarms)) {
287: $horde_alarm = $GLOBALS['injector']->getInstance('Horde_Alarm');
288: foreach ($alarms as $alarm) {
289: $alarm['start'] = new Horde_Date($alarm['start']);
290: $alarm['end'] = new Horde_Date($alarm['end']);
291: $horde_alarm->set($alarm);
292: }
293: }
294: } catch (Exception $e) {}
295: }
296:
297:
298: if ($GLOBALS['prefs']->isDirty('sync_calendars') || $GLOBALS['prefs']->isDirty('default_share')) {
299: $sync = @unserialize($GLOBALS['prefs']->getValue('sync_calendars'));
300: $haveDefault = false;
301: $default = Kronolith::getDefaultCalendar(Horde_Perms::EDIT);
302: foreach ($sync as $cid) {
303: if ($cid == $default) {
304: $haveDefault = true;
305: break;
306: }
307: }
308: if (!$haveDefault) {
309: $sync[] = $default;
310: $GLOBALS['prefs']->setValue('sync_calendars', serialize($sync));
311: }
312: }
313:
314: if ($GLOBALS['conf']['activesync']['enabled'] && $GLOBALS['prefs']->isDirty('sync_calendars')) {
315: try {
316: $stateMachine = $GLOBALS['injector']->getInstance('Horde_ActiveSyncState');
317: $stateMachine->setLogger($GLOBALS['injector']->getInstance('Horde_Log_Logger'));
318: $devices = $stateMachine->listDevices($GLOBALS['registry']->getAuth());
319: foreach ($devices as $device) {
320: $stateMachine->removeState(null, $device['device_id'], $GLOBALS['registry']->getAuth());
321: }
322: $GLOBALS['notification']->push(_("All state removed for your ActiveSync devices. They will resynchronize next time they connect to the server."));
323: } catch (Horde_ActiveSync_Exception $e) {
324: $GLOBALS['notification']->push(_("There was an error communicating with the ActiveSync server: %s"), $e->getMessage(), 'horde.err');
325: }
326: }
327: }
328:
329: 330: 331: 332: 333: 334: 335:
336: protected function _defaultAlarmManagement($ui)
337: {
338: $t = $GLOBALS['injector']->createInstance('Horde_Template');
339: $t->setOption('gettext', true);
340:
341: if ($alarm_value = $GLOBALS['prefs']->getValue('default_alarm')) {
342: if ($alarm_value % 10080 == 0) {
343: $alarm_value /= 10080;
344: $t->set('week', true);
345: } elseif ($alarm_value % 1440 == 0) {
346: $alarm_value /= 1440;
347: $t->set('day', true);
348: } elseif ($alarm_value % 60 == 0) {
349: $alarm_value /= 60;
350: $t->set('hour', true);
351: } else {
352: $t->set('minute', true);
353: }
354: } else {
355: $t->set('minute', true);
356: }
357:
358: $t->set('alarm_value', intval($alarm_value));
359:
360: return $t->fetch(KRONOLITH_TEMPLATES . '/prefs/defaultalarm.html');
361: }
362:
363: 364: 365: 366: 367: 368: 369:
370: protected function _prefsRemoteCalManagement($ui)
371: {
372: $calName = $ui->vars->remote_name;
373: $calUrl = trim($ui->vars->remote_url);
374: $calUser = trim($ui->vars->remote_user);
375: $calPasswd = trim($ui->vars->remote_password);
376:
377: $key = $GLOBALS['registry']->getAuthCredential('password');
378: if ($key) {
379: $secret = $injector->getInstance('Horde_Secret');
380: $calUser = base64_encode($secret->write($key, $calUser));
381: $calPasswd = base64_encode($secret->write($key, $calPasswd));
382: }
383:
384: $calActionID = isset($ui->vars->remote_action)
385: ? $ui->vars->remote_action
386: : 'add';
387:
388: if ($calActionID == 'add') {
389: if (!empty($calName) && !empty($calUrl)) {
390: $cals = unserialize($GLOBALS['prefs']->getValue('remote_cals'));
391: $cals[] = array('name' => $calName,
392: 'url' => $calUrl,
393: 'user' => $calUser,
394: 'password' => $calPasswd);
395: $GLOBALS['prefs']->setValue('remote_cals', serialize($cals));
396: }
397: } elseif ($calActionID == 'delete') {
398: $cals = unserialize($GLOBALS['prefs']->getValue('remote_cals'));
399: foreach ($cals as $key => $cal) {
400: if ($cal['url'] == $calUrl) {
401: unset($cals[$key]);
402: break;
403: }
404: }
405: $GLOBALS['prefs']->setValue('remote_cals', serialize($cals));
406: } elseif ($calActionID == 'edit') {
407: $cals = unserialize($GLOBALS['prefs']->getValue('remote_cals'));
408: foreach ($cals as $key => $cal) {
409: if ($cal['url'] == $calUrl) {
410: $cals[$key]['name'] = $calName;
411: $cals[$key]['url'] = $calUrl;
412: $cals[$key]['user'] = $calUser;
413: $cals[$key]['password'] = $calPasswd;
414: break;
415: }
416: }
417: $GLOBALS['prefs']->setValue('remote_cals', serialize($cals));
418: }
419: }
420:
421: 422: 423: 424: 425: 426: 427:
428: protected function _prefsSourceselect($ui)
429: {
430: global $prefs;
431:
432: $data = Horde_Core_Prefs_Ui_Widgets::addressbooksUpdate($ui);
433: $updated = false;
434:
435: if (isset($data['sources'])) {
436: $prefs->setValue('search_sources', $data['sources']);
437: $updated = true;
438: }
439:
440: if (isset($data['fields'])) {
441: $prefs->setValue('search_fields', $data['fields']);
442: $updated = true;
443: }
444:
445: return $updated;
446: }
447:
448: 449:
450: public function removeUserData($user)
451: {
452: $error = false;
453:
454:
455: Kronolith::removeUserEvents($user);
456:
457:
458: try {
459: $shares = $GLOBALS['kronolith_shares']->listShares(
460: $user,
461: array('attributes' => $user));
462: foreach ($shares as $share) {
463: $GLOBALS['kronolith_shares']->removeShare($share);
464: }
465: } catch (Exception $e) {
466: Horde::logMessage($e, 'NOTICE');
467: $error = true;
468: }
469:
470: 471:
472: try {
473: $shares = $GLOBALS['kronolith_shares']->listShares($user);
474: foreach ($shares as $share) {
475: $share->removeUser($user);
476: }
477: } catch (Horde_Share_Exception $e) {
478: Horde::logMessage($e, 'NOTICE');
479: $error = true;
480: }
481:
482: if ($error) {
483: throw new Kronolith_Exception(sprintf(_("There was an error removing calendars for %s. Details have been logged."), $user));
484: }
485: }
486:
487:
488:
489: 490:
491: public function (Horde_Tree_Base $tree, $parent = null,
492: array $params = array())
493: {
494: switch ($params['id']) {
495: case 'alarms':
496: try {
497: $alarms = Kronolith::listAlarms(new Horde_Date($_SERVER['REQUEST_TIME']), $GLOBALS['display_calendars'], true);
498: } catch (Kronolith_Exception $e) {
499: return;
500: }
501:
502: $alarmCount = 0;
503: $alarmImg = Horde_Themes::img('alarm.png');
504: $horde_alarm = $GLOBALS['injector']->getInstance('Horde_Alarm');
505:
506: foreach ($alarms as $calId => $calAlarms) {
507: foreach ($calAlarms as $event) {
508: if ($horde_alarm->isSnoozed($event->uid, $GLOBALS['registry']->getAuth())) {
509: continue;
510: }
511: ++$alarmCount;
512: $tree->addNode(
513: $parent . $calId . $event->id,
514: $parent,
515: htmlspecialchars($event->getTitle()),
516: 1,
517: false,
518: array(
519: 'icon' => $alarmImg,
520: 'url' => $event->getViewUrl(array(), false, false)
521: )
522: );
523: }
524: }
525:
526: if ($GLOBALS['registry']->get('url', $parent)) {
527: $purl = $GLOBALS['registry']->get('url', $parent);
528: } elseif ($GLOBALS['registry']->get('status', $parent) == 'heading' ||
529: !$GLOBALS['registry']->get('webroot')) {
530: $purl = null;
531: } else {
532: $purl = Horde::url($GLOBALS['registry']->getInitialPage($parent));
533: }
534:
535: $pnode_name = $GLOBALS['registry']->get('name', $parent);
536: if ($alarmCount) {
537: $pnode_name = '<strong>' . $pnode_name . '</strong>';
538: }
539:
540: $tree->addNode(
541: $parent,
542: $GLOBALS['registry']->get('menu_parent', $parent),
543: $pnode_name,
544: 0,
545: false,
546: array(
547: 'icon' => $GLOBALS['registry']->get('icon', $parent),
548: 'url' => $purl,
549: )
550: );
551: break;
552:
553: case 'menu':
554: $menus = array(
555: array('new', _("New Event"), 'new.png', Horde::url('new.php')),
556: array('day', _("Day"), 'dayview.png', Horde::url('day.php')),
557: array('work', _("Work Week"), 'workweekview.png', Horde::url('workweek.php')),
558: array('week', _("Week"), 'weekview.png', Horde::url('week.php')),
559: array('month', _("Month"), 'monthview.png', Horde::url('month.php')),
560: array('year', _("Year"), 'yearview.png', Horde::url('year.php')),
561: array('search', _("Search"), 'search.png', Horde::url('search.php'))
562: );
563:
564: foreach ($menus as $menu) {
565: $tree->addNode(
566: $parent . $menu[0],
567: $parent,
568: $menu[1],
569: 1,
570: false,
571: array(
572: 'icon' => Horde_Themes::img($menu[2]),
573: 'url' => $menu[3]
574: )
575: );
576: }
577: break;
578: }
579: }
580:
581: 582: 583: 584:
585: public function mobileInitCallback()
586: {
587: $datejs = str_replace('_', '-', $GLOBALS['language']) . '.js';
588: if (!file_exists($GLOBALS['registry']->get('jsfs', 'horde') . '/date/' . $datejs)) {
589: $datejs = 'en-US.js';
590: }
591:
592: Horde::addScriptFile('date/' . $datejs, 'horde');
593: Horde::addScriptFile('date/date.js', 'horde');
594: Horde::addScriptFile('mobile.js');
595: require KRONOLITH_TEMPLATES . '/mobile/javascript_defs.php';
596:
597:
598: Horde::addInlineScript(
599: '$(window.document).bind("mobileinit", function() {
600: $.mobile.page.prototype.options.addBackBtn = true;
601: $.mobile.page.prototype.options.backBtnText = "' . _("Back") .'";
602: $.mobile.loadingMessage = "' . _("loading") . '";
603:
604: // Setup event bindings to populate views on pagebeforeshow
605: KronolithMobile.date = new Date();
606: $("#dayview").live("pagebeforeshow", function() {
607: KronolithMobile.view = "day";
608: $(".kronolithDayDate").html(KronolithMobile.date.toString("ddd") + " " + KronolithMobile.date.toString("d"));
609: KronolithMobile.loadEvents(KronolithMobile.date, KronolithMobile.date, "day");
610: });
611:
612: $("#monthview").live("pagebeforeshow", function(event, ui) {
613: KronolithMobile.view = "month";
614: // (re)build the minical only if we need to
615: if (!$(".kronolithMinicalDate").data("date") ||
616: ($(".kronolithMinicalDate").data("date").toString("M") != KronolithMobile.date.toString("M"))) {
617: KronolithMobile.moveToMonth(KronolithMobile.date);
618: }
619: });
620:
621: $("#eventview").live("pageshow", function(event, ui) {
622: KronolithMobile.view = "event";
623: });
624:
625: // Set up overview
626: $("#overview").live("pageshow", function(event, ui) {
627: KronolithMobile.view = "overview";
628: if (!KronolithMobile.haveOverview) {
629: KronolithMobile.loadEvents(KronolithMobile.date, KronolithMobile.date.clone().addDays(7), "overview");
630: KronolithMobile.haveOverview = true;
631: }
632: });
633:
634: });'
635: );
636: }
637:
638:
639:
640: 641:
642: public function listAlarms($time, $user = null)
643: {
644: $current_user = $GLOBALS['registry']->getAuth();
645: if ((empty($user) || $user != $current_user) && !$GLOBALS['registry']->isAdmin()) {
646: throw new Horde_Exception_PermissionDenied();
647: }
648:
649: $group = $GLOBALS['injector']->getInstance('Horde_Group');
650: $alarm_list = array();
651: $time = new Horde_Date($time);
652: $calendars = is_null($user) ? array_keys($GLOBALS['kronolith_shares']->listAllShares()) : $GLOBALS['display_calendars'];
653: $alarms = Kronolith::listAlarms($time, $calendars, true);
654: foreach ($alarms as $calendar => $cal_alarms) {
655: if (!$cal_alarms) {
656: continue;
657: }
658: try {
659: $share = $GLOBALS['kronolith_shares']->getShare($calendar);
660: } catch (Exception $e) {
661: continue;
662: }
663: if (empty($user)) {
664: $users = $share->listUsers(Horde_Perms::READ);
665: $groups = $share->listGroups(Horde_Perms::READ);
666: foreach ($groups as $gid) {
667: try {
668: $users = array_merge($users, $group->listUsers($gid));
669: } catch (Horde_Group_Exception $e) {}
670: }
671: $users = array_unique($users);
672: } else {
673: $users = array($user);
674: }
675: $owner = $share->get('owner');
676: foreach ($cal_alarms as $event) {
677: foreach ($users as $alarm_user) {
678: if ($alarm_user == $current_user) {
679: $prefs = $GLOBALS['prefs'];
680: } else {
681: $prefs = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Prefs')->create('kronolith', array(
682: 'cache' => false,
683: 'user' => $alarm_user
684: ));
685: }
686: $shown_calendars = unserialize($prefs->getValue('display_cals'));
687: $reminder = $prefs->getValue('event_reminder');
688: if (($reminder == 'owner' && $alarm_user == $owner) ||
689: ($reminder == 'show' && in_array($calendar, $shown_calendars)) ||
690: $reminder == 'read') {
691: $GLOBALS['registry']->setLanguageEnvironment($prefs->getValue('language'));
692: $alarm = $event->toAlarm($time, $alarm_user, $prefs);
693: if ($alarm) {
694: $alarm_list[] = $alarm;
695: }
696: }
697: }
698: }
699: }
700:
701: return $alarm_list;
702: }
703:
704: }
705: