From c3c4684bae0cc4b1406d1bf12e4c95570a7c1e7d Mon Sep 17 00:00:00 2001 From: "Aleksander Machniak (Kolab Systems)" Date: Tue, 28 Jun 2011 10:32:52 +0200 Subject: [PATCH] Improved calendars list: use unified kolab calendars naming, Added calendar icons with namespace/readonly state indicator (as in addressbook) --- .../calendar/drivers/kolab/kolab_calendar.php | 55 +++++++-- .../calendar/drivers/kolab/kolab_driver.php | 105 ++++++++++-------- plugins/calendar/skins/default/calendar.css | 25 ++++- .../skins/default/images/calendars.png | Bin 0 -> 2261 bytes 4 files changed, 130 insertions(+), 55 deletions(-) create mode 100644 plugins/calendar/skins/default/images/calendars.png diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php index 816bf699..cf55d999 100644 --- a/plugins/calendar/drivers/kolab/kolab_calendar.php +++ b/plugins/calendar/drivers/kolab/kolab_calendar.php @@ -27,6 +27,7 @@ class kolab_calendar private $events; private $id2uid; private $imap_folder = 'INBOX/Calendar'; + private $namespace; private $sensitivity_map = array('public', 'private', 'confidential'); private $priority_map = array('low', 'normal', 'high'); @@ -61,6 +62,19 @@ class kolab_calendar $this->storage = rcube_kolab::get_storage($this->imap_folder); $this->ready = !PEAR::isError($this->storage); + + // Set readonly and editable flags according to folder permissions + if ($this->ready) { + if ($this->get_owner() == $_SESSION['username']) { + $this->readonly = false; + } + else { + $acl = $this->storage->_folder->getACL(); + $acl = $acl[$_SESSION['username']]; + if (strpos($acl, 'i') !== false) + $this->readonly = false; + } + } } @@ -72,9 +86,8 @@ class kolab_calendar */ public function get_name() { - // @TODO: get namespace prefixes from IMAP - $dispname = preg_replace(array('!INBOX/Calendar/!', '!^INBOX/!', '!^shared/!', '!^user/([^/]+)/!'), array('','','','(\\1) '), $this->imap_folder); - return rcube_charset_convert(strlen($dispname) ? $dispname : $this->imap_folder, "UTF7-IMAP"); + $folder = rcube_kolab::object_name($this->imap_folder, $this->namespace); + return $folder; } @@ -89,6 +102,31 @@ class kolab_calendar } + /** + * Getter for the IMAP folder owner + * + * @return string Name of the folder owner + */ + public function get_owner() + { + return $this->storage->_folder->getOwner(); + } + + + /** + * Getter for the name of the namespace to which the IMAP folder belongs + * + * @return string Name of the namespace (personal, other, shared) + */ + public function get_namespace() + { + if ($this->namespace === null) { + $this->namespace = rcube_kolab::folder_namespace($this->imap_folder); + } + return $this->namespace; + } + + /** * Getter for the top-end calendar folder name (not the entire path) * @@ -195,18 +233,15 @@ class kolab_calendar { if (!is_array($event)) return false; - - - + //generate new event from RC input $object = $this->_from_rcube_event($event); - + //generate new UID $object['uid'] = $this->storage->generateUID(); - - + $saved = $this->storage->save($object); - + return $saved; } diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index b6114c58..c58ef345 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -29,7 +29,6 @@ class kolab_driver extends calendar_driver private $rc; private $cal; private $calendars; - private $folders; /** * Default constructor @@ -49,49 +48,37 @@ class kolab_driver extends calendar_driver { // already read sources if (isset($this->calendars)) - return $this->calendars; + return $this->calendars; // get all folders that have "event" type $folders = rcube_kolab::get_folders('event'); - $this->folders = $this->calendars = array(); + $this->calendars = array(); if (PEAR::isError($folders)) { - raise_error(array( - 'code' => 600, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => "Failed to list calendar folders from Kolab server:" . $folders->getMessage()), - true, false); + raise_error(array( + 'code' => 600, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Failed to list calendar folders from Kolab server:" . $folders->getMessage()), + true, false); } else { - foreach ($folders as $c_folder) { - $calendar = new kolab_calendar($c_folder->name, $this->cal); - $this->folders[$calendar->id] = $calendar; - if ($calendar->ready) { - $this->calendars[$calendar->id] = array( - 'id' => $calendar->id, - 'name' => $calendar->get_name(), - 'editname' => $calendar->get_foldername(), - 'color' => $calendar->get_color(), - 'readonly' => $c_folder->_owner != $_SESSION['username'], - ); - } - } + // convert to UTF8 and sort + $names = array(); + foreach ($folders as $folder) + $names[$folder->name] = rcube_charset_convert($folder->name, 'UTF7-IMAP'); + + asort($names, SORT_LOCALE_STRING); + + foreach ($names as $utf7name => $name) { + $calendar = new kolab_calendar($utf7name, $this->cal); + $this->calendars[$calendar->id] = $calendar; + } } return $this->calendars; } - private function _get_storage($cid, $readonly = false) - { - if ($readonly) - return $this->folders[$cid]; - else if (!$this->calendars[$cid]['readonly']) - return $this->folders[$cid]; - return false; - } - - /** * Get a list of available calendars from this source */ @@ -99,11 +86,41 @@ class kolab_driver extends calendar_driver { // attempt to create a default calendar for this user if (empty($this->calendars)) { - if ($this->create_calendar(array('name' => 'Default', 'color' => 'cc0000'))) + if ($this->create_calendar(array('name' => 'Calendar', 'color' => 'cc0000'))) $this->_read_calendars(); } - return $this->calendars; + $calendars = $names = array(); + + foreach ($this->calendars as $id => $cal) { + if ($cal->ready) { + $name = $origname = $cal->get_name(); + + // find folder prefix to truncate (the same code as in kolab_addressbook plugin) + for ($i = count($names)-1; $i >= 0; $i--) { + if (strpos($name, $names[$i].' » ') === 0) { + $length = strlen($names[$i].' » '); + $prefix = substr($name, 0, $length); + $count = count(explode(' » ', $prefix)); + $name = str_repeat('  ', $count-1) . '» ' . substr($name, $length); + break; + } + } + + $names[] = $origname; + + $calendars[$cal->id] = array( + 'id' => $cal->id, + 'name' => $name, + 'editname' => $cal->get_foldername(), + 'color' => $cal->get_color(), + 'readonly' => $cal->readonly, + 'class_name' => $cal->get_namespace(), + ); + } + } + + return $calendars; } @@ -148,7 +165,7 @@ class kolab_driver extends calendar_driver */ public function edit_calendar($prop) { - if ($prop['id'] && ($cal = $this->folders[$prop['id']])) { + if ($prop['id'] && ($cal = $this->calendars[$prop['id']])) { $newfolder = rcube_charset_convert($prop['name'], RCMAIL_CHARSET, 'UTF7-IMAP'); $oldfolder = $cal->get_realname(); // add namespace prefix (when needed) @@ -185,7 +202,7 @@ class kolab_driver extends calendar_driver */ public function remove_calendar($prop) { - if ($prop['id'] && ($cal = $this->folders[$prop['id']])) { + if ($prop['id'] && ($cal = $this->calendars[$prop['id']])) { $folder = $cal->get_realname(); if (rcube_kolab::folder_delete($folder)) { // remove color in user prefs (temp. solution) @@ -209,7 +226,7 @@ class kolab_driver extends calendar_driver public function new_event($event) { $cid = $event['calendar'] ? $event['calendar'] : reset(array_keys($this->calendars)); - if ($storage = $this->_get_storage($cid)) + if ($storage = $this->calendars($cid)) return $storage->insert_event($event); return false; @@ -223,7 +240,7 @@ class kolab_driver extends calendar_driver */ public function edit_event($event) { - if ($storage = $this->_get_storage($event['calendar'])) + if ($storage = $this->calendars[$event['calendar']]) return $storage->update_event($event); return false; @@ -237,7 +254,7 @@ class kolab_driver extends calendar_driver */ public function move_event($event) { - if (($storage = $this->_get_storage($event['calendar'])) && ($ev = $storage->get_event($event['id']))) + if (($storage = $this->calendars[$event['calendar']]) && ($ev = $storage->get_event($event['id']))) return $storage->update_event($event + $ev); return false; @@ -266,9 +283,9 @@ class kolab_driver extends calendar_driver */ public function remove_event($event) { - if (($storage = $this->_get_storage($event['calendar'])) && ($ev = $storage->get_event($event['id']))) + if (($storage = $this->calendars[$event['calendar']]) && ($ev = $storage->get_event($event['id']))) return $storage->delete_event($event); - + return false; } @@ -285,15 +302,15 @@ class kolab_driver extends calendar_driver { if ($calendars && is_string($calendars)) $calendars = explode(',', $calendars); - + $events = array(); foreach ($this->calendars as $cid => $calendar) { if ($calendars && !in_array($cid, $calendars)) continue; - - $events = array_merge($this->folders[$cid]->list_events($start, $end, $search)); + + $events = array_merge($this->calendars[$cid]->list_events($start, $end, $search)); } - + return $events; } diff --git a/plugins/calendar/skins/default/calendar.css b/plugins/calendar/skins/default/calendar.css index dfa94948..304bebfc 100644 --- a/plugins/calendar/skins/default/calendar.css +++ b/plugins/calendar/skins/default/calendar.css @@ -92,10 +92,11 @@ pre { #calendarslist li { margin: 0; - padding: 2px; + padding: 1px; display: block; background: #fff; border-bottom: 1px solid #EBEBEB; + white-space: nowrap; } #calendarslist li label { @@ -104,6 +105,8 @@ pre { #calendarslist li span { cursor: default; + background: url(images/calendars.png) 0 -3px no-repeat; + padding-left: 18px; } #calendarslist li input { @@ -119,6 +122,26 @@ pre { font-weight: bold; } +#calendarslist li.readonly span { + background-position: 0 -21px; +} + +#calendarslist li.other span { + background-position: 0 -39px; +} + +#calendarslist li.other.readonly span { + background-position: 0 -57px; +} + +#calendarslist li.shared span { + background-position: 0 -75px; +} + +#calendarslist li.shared.readonly span { + background-position: 0 -93px; +} + #agendalist { width: 100%; margin: 0 auto; diff --git a/plugins/calendar/skins/default/images/calendars.png b/plugins/calendar/skins/default/images/calendars.png new file mode 100644 index 0000000000000000000000000000000000000000..305daaa0e62e85aa757804da599e4e9a3c3c1bc7 GIT binary patch literal 2261 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