plugins/calendar

Thomas Brüderli bruederli at kolabsys.com
Tue Oct 14 20:23:57 CEST 2014


 plugins/calendar/calendar.php                                |   34 ++++++++
 plugins/calendar/calendar_ui.js                              |   43 ++++++++++
 plugins/calendar/drivers/calendar_driver.php                 |   14 ++-
 plugins/calendar/drivers/database/database_driver.php        |   14 +++
 plugins/calendar/drivers/kolab/kolab_calendar.php            |   45 ++++++++++
 plugins/calendar/drivers/kolab/kolab_driver.php              |   30 +++++++
 plugins/calendar/drivers/kolab/kolab_invitation_calendar.php |   46 +++++++----
 plugins/calendar/drivers/kolab/kolab_user_calendar.php       |   12 ++
 plugins/calendar/skins/larry/calendar.css                    |   29 ++++++
 9 files changed, 248 insertions(+), 19 deletions(-)

New commits:
commit 0ab0b797b8744b59718ef382ea11e934230b995e
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Tue Oct 14 20:23:52 2014 +0200

    Display count 'bubble' for open pending invitations (#3268)

diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 5794e01..d109302 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -132,6 +132,7 @@ class calendar extends rcube_plugin
       $this->register_action('index', array($this, 'calendar_view'));
       $this->register_action('event', array($this, 'event_action'));
       $this->register_action('calendar', array($this, 'calendar_action'));
+      $this->register_action('count', array($this, 'count_events'));
       $this->register_action('load_events', array($this, 'load_events'));
       $this->register_action('export_events', array($this, 'export_events'));
       $this->register_action('import_events', array($this, 'import_events'));
@@ -1139,6 +1140,27 @@ class calendar extends rcube_plugin
   }
 
   /**
+   * Handler for requests fetching event counts for calendars
+   */
+  public function count_events()
+  {
+    // don't update session on these requests (avoiding race conditions)
+    $this->rc->session->nowrite = true;
+
+    $start = rcube_utils::get_input_value('start', rcube_utils::INPUT_GET);
+    if (!$start)
+      $start = (new DateTime('today 00:00:00', $this->timezone))->format('U');
+
+    $counts = $this->driver->count_events(
+      rcube_utils::get_input_value('source', rcube_utils::INPUT_GET),
+      $start,
+      rcube_utils::get_input_value('end', rcube_utils::INPUT_GET)
+    );
+
+    $this->rc->output->command('plugin.update_counts', array('counts' => $counts));
+  }
+
+  /**
    * Load event data from an iTip message attachment
    */
   public function itip_events($msgref)
@@ -1187,6 +1209,8 @@ class calendar extends rcube_plugin
         return;
     }
 
+    $counts = array();
+
     foreach ($this->driver->list_calendars(true) as $cal) {
       $events = $this->driver->load_events(
         rcube_utils::get_input_value('start', rcube_utils::INPUT_GPC),
@@ -1201,6 +1225,16 @@ class calendar extends rcube_plugin
         $this->rc->output->command('plugin.refresh_calendar',
           array('source' => $cal['id'], 'update' => $this->_client_event($event)));
       }
+
+      // refresh count for this calendar
+      if ($cal['counts']) {
+        $today = new DateTime('today 00:00:00', $this->timezone);
+        $counts += $this->driver->count_events($cal['id'], $today->format('U'));
+      }
+    }
+
+    if (!empty($counts)) {
+      $this->rc->output->command('plugin.update_counts', array('counts' => $counts));
     }
   }
 
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index fe11420..f2ece35 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -67,6 +67,7 @@ function rcube_calendar_ui(settings)
     var freebusy_ui = { workinhoursonly:false, needsupdate:false };
     var freebusy_data = {};
     var current_view = null;
+    var count_sources = [];
     var exec_deferred = bw.ie6 ? 5 : 1;
     var sensitivitylabels = { 'public':rcmail.gettext('public','calendar'), 'private':rcmail.gettext('private','calendar'), 'confidential':rcmail.gettext('confidential','calendar') };
     var ui_loading = rcmail.set_busy(true, 'loading');
@@ -3123,6 +3124,8 @@ function rcube_calendar_ui(settings)
         }
         else
           fc.fullCalendar('refetchEvents', source);
+
+        fetch_counts();
       }
       // add/update single event object
       else if (source && p.update) {
@@ -3145,6 +3148,7 @@ function rcube_calendar_ui(settings)
       // refetch all calendars
       else if (p.refetch) {
         fc.fullCalendar('refetchEvents');
+        fetch_counts();
       }
 
       // remove temp events
@@ -3165,6 +3169,28 @@ function rcube_calendar_ui(settings)
       return query;
     };
 
+    // callback from server providing event counts
+    this.update_counts = function(p)
+    {
+      $.each(p.counts, function(cal, count) {
+        var li = calendars_list.get_item(cal),
+          bubble = $(li).children('.calendar').find('span.count');
+
+        if (!bubble.length && count > 0) {
+          bubble = $('<span>')
+            .addClass('count')
+            .appendTo($(li).children('.calendar').first())
+        }
+
+        if (count > 0) {
+          bubble.text(count).show();
+        }
+        else {
+          bubble.text('').hide();
+        }
+      });
+    };
+
     // callback after an iTip message event was imported
     this.itip_message_processed = function(data)
     {
@@ -3415,6 +3441,16 @@ function rcube_calendar_ui(settings)
       }
     }
 
+    // fetch counts for some calendars from the server
+    var fetch_counts = function()
+    {
+      if (count_sources.length) {
+        setTimeout(function() {
+          rcmail.http_request('calendar/count', { source:count_sources });
+        }, 500);
+      }
+    };
+
 
     /***  startup code  ***/
 
@@ -3431,6 +3467,9 @@ function rcube_calendar_ui(settings)
       if (active) {
         event_sources.push(this.calendars[id]);
       }
+      if (cal.counts) {
+        count_sources.push(id);
+      }
 
       if (!cal.readonly && !this.selected_calendar) {
         this.selected_calendar = id;
@@ -4005,6 +4044,9 @@ function rcube_calendar_ui(settings)
     // initialize more UI elements (deferred)
     window.setTimeout(init_calendar_ui, exec_deferred);
 
+    // fetch counts for some calendars
+    fetch_counts();
+
     // add proprietary css styles if not IE
     if (!bw.ie)
       $('div.fc-content').addClass('rcube-fc-content');
@@ -4055,6 +4097,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
   rcmail.addEventListener('plugin.refresh_calendar', function(p){ cal.refresh(p); });
   rcmail.addEventListener('plugin.import_success', function(p){ cal.import_success(p); });
   rcmail.addEventListener('plugin.import_error', function(p){ cal.import_error(p); });
+  rcmail.addEventListener('plugin.update_counts', function(p){ cal.update_counts(p); });
   rcmail.addEventListener('plugin.reload_view', function(p){ cal.reload_view(p); });
   rcmail.addEventListener('plugin.resource_data', function(p){ cal.resource_data_load(p); });
   rcmail.addEventListener('plugin.resource_owner', function(p){ cal.resource_owner_load(p); });
diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php
index 702dd22..b6624ce 100644
--- a/plugins/calendar/drivers/calendar_driver.php
+++ b/plugins/calendar/drivers/calendar_driver.php
@@ -268,8 +268,8 @@ abstract class calendar_driver
   /**
    * Get events from source.
    *
-   * @param  integer Event's new start (unix timestamp)
-   * @param  integer Event's new end (unix timestamp)
+   * @param  integer Date range start (unix timestamp)
+   * @param  integer Date range end (unix timestamp)
    * @param  string  Search query (optional)
    * @param  mixed   List of calendar IDs to load events from (either as array or comma-separated string)
    * @param  boolean Include virtual/recurring events (optional)
@@ -279,6 +279,16 @@ abstract class calendar_driver
   abstract function load_events($start, $end, $query = null, $calendars = null, $virtual = 1, $modifiedsince = null);
 
   /**
+   * Get number of events in the given calendar
+   *
+   * @param  mixed   List of calendar IDs to count events (either as array or comma-separated string)
+   * @param  integer Date range start (unix timestamp)
+   * @param  integer Date range end (unix timestamp)
+   * @return array   Hash array with counts grouped by calendar ID
+   */
+  abstract function count_events($calendars, $start, $end = null);
+
+  /**
    * Get a list of pending alarms to be displayed to the user
    *
    * @param  integer Current time (unix timestamp)
diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php
index 781f71e..c1e37da 100644
--- a/plugins/calendar/drivers/database/database_driver.php
+++ b/plugins/calendar/drivers/database/database_driver.php
@@ -844,6 +844,20 @@ class database_driver extends calendar_driver
   }
 
   /**
+   * Get number of events in the given calendar
+   *
+   * @param  mixed   List of calendar IDs to count events (either as array or comma-separated string)
+   * @param  integer Date range start (unix timestamp)
+   * @param  integer Date range end (unix timestamp)
+   * @return array   Hash array with counts grouped by calendar ID
+   */
+  public function count_events($calendars, $start, $end = null)
+  {
+      // not implemented
+      return array();
+  }
+
+  /**
    * Convert sql record into a rcube style event object
    */
   private function _read_postprocess($event)
diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index c99ca61..85a3b63 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -334,6 +334,51 @@ class kolab_calendar extends kolab_storage_folder_api
     return $events;
   }
 
+  /**
+   *
+   * @param  integer Date range start (unix timestamp)
+   * @param  integer Date range end (unix timestamp)
+   * @param  array   Additional query to filter events
+   * @return integer Count
+   */
+  public function count_events($start, $end = null, $filter_query = null)
+  {
+    // convert to DateTime for comparisons
+    try {
+      $start = new DateTime('@'.$start);
+    }
+    catch (Exception $e) {
+      $start = new DateTime('@0');
+    }
+    if ($end) {
+      try {
+        $end = new DateTime('@'.$end);
+      }
+      catch (Exception $e) {
+        $end = null;
+      }
+    }
+
+    // query Kolab storage
+    $query[] = array('dtend',   '>=', $start);
+    
+    if ($end)
+      $query[] = array('dtstart', '<=', $end);
+
+    // add query to exclude pending/declined invitations
+    if (empty($filter_query)) {
+      foreach ($this->cal->get_user_emails() as $email) {
+        $query[] = array('tags', '!=', 'x-partstat:' . $email . ':needs-action');
+        $query[] = array('tags', '!=', 'x-partstat:' . $email . ':declined');
+      }
+    }
+    else if (is_array($filter_query)) {
+      $query = array_merge($query, $filter_query);
+    }
+
+    // we rely the Kolab storage query (no post-filtering)
+    return $this->storage->count($query);
+  }
 
   /**
    * Create a new event record
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index fb2a1de..f33267a 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -243,6 +243,10 @@ class kolab_driver extends calendar_driver
             'children' => false,
           );
 
+          if ($id == self::INVITATIONS_CALENDAR_PENDING) {
+            $calendars[$id]['counts'] = true;
+          }
+
           if (is_object($tree)) {
             $tree->children[] = $cal;
           }
@@ -1030,6 +1034,32 @@ class kolab_driver extends calendar_driver
   }
 
   /**
+   * Get number of events in the given calendar
+   *
+   * @param  mixed   List of calendar IDs to count events (either as array or comma-separated string)
+   * @param  integer Date range start (unix timestamp)
+   * @param  integer Date range end (unix timestamp)
+   * @return array   Hash array with counts grouped by calendar ID
+   */
+  public function count_events($calendars, $start, $end = null)
+  {
+      $counts = array();
+
+      if ($calendars && is_string($calendars))
+        $calendars = explode(',', $calendars);
+      else if (!$calendars)
+        $calendars = array_keys($this->calendars);
+
+      foreach ($calendars as $cid) {
+        if ($storage = $this->get_calendar($cid)) {
+            $counts[$cid] = $storage->count_events($start, $end);
+        }
+      }
+
+      return $counts;
+  }
+
+  /**
    * Get a list of pending alarms to be displayed to the user
    *
    * @see calendar_driver::pending_alarms()
diff --git a/plugins/calendar/drivers/kolab/kolab_invitation_calendar.php b/plugins/calendar/drivers/kolab/kolab_invitation_calendar.php
index 24fad59..a2e9cf2 100644
--- a/plugins/calendar/drivers/kolab/kolab_invitation_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_invitation_calendar.php
@@ -199,20 +199,6 @@ class kolab_invitation_calendar
    */
   public function list_events($start, $end, $search = null, $virtual = 1, $query = array())
   {
-    // convert to DateTime for comparisons
-    try {
-      $start_dt = new DateTime('@'.$start);
-    }
-    catch (Exception $e) {
-      $start_dt = new DateTime('@0');
-    }
-    try {
-      $end_dt = new DateTime('@'.$end);
-    }
-    catch (Exception $e) {
-      $end_dt = new DateTime('today +10 years');
-    }
-
     // get email addresses of the current user
     $user_emails = $this->cal->get_user_emails();
     $subquery = array();
@@ -232,7 +218,7 @@ class kolab_invitation_calendar
       foreach ($cal->list_events($start, $end, $search, 1, $query, array(array($subquery, 'OR'))) as $event) {
         $match = false;
 
-        // post-filter events to skip pending and declined invitations
+        // post-filter events to match out partstats
         if (is_array($event['attendees'])) {
           foreach ($event['attendees'] as $attendee) {
             if (in_array($attendee['email'], $user_emails) && in_array($attendee['status'], $this->partstats)) {
@@ -255,6 +241,36 @@ class kolab_invitation_calendar
   }
 
   /**
+   *
+   * @param  integer Date range start (unix timestamp)
+   * @param  integer Date range end (unix timestamp)
+   * @return integer Count
+   */
+  public function count_events($start, $end = null)
+  {
+    // get email addresses of the current user
+    $user_emails = $this->cal->get_user_emails();
+    $subquery = array();
+    foreach ($user_emails as $email) {
+      foreach ($this->partstats as $partstat) {
+        $subquery[] = array('tags', '=', 'x-partstat:' . $email . ':' . strtolower($partstat));
+      }
+    }
+
+    // aggregate counts from all calendar folders
+    $count = 0;
+    foreach (kolab_storage::list_folders('', '*', 'event', null) as $foldername) {
+      $cal = new kolab_calendar($foldername, $this->cal);
+      if ($cal->get_namespace() == 'other')
+        continue;
+
+      $count += $cal->count_events($start, $end, array(array($subquery, 'OR')));
+    }
+
+    return $count;
+  }
+
+  /**
    * Helper method to modify some event properties
    */
   private function _mod_event($event)
diff --git a/plugins/calendar/drivers/kolab/kolab_user_calendar.php b/plugins/calendar/drivers/kolab/kolab_user_calendar.php
index 93bcc0b..7687e3e 100644
--- a/plugins/calendar/drivers/kolab/kolab_user_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_user_calendar.php
@@ -240,6 +240,18 @@ class kolab_user_calendar extends kolab_calendar
   }
 
   /**
+   *
+   * @param  integer Date range start (unix timestamp)
+   * @param  integer Date range end (unix timestamp)
+   * @return integer Count
+   */
+  public function count_events($start, $end = null)
+  {
+    // not implemented
+    return 0;
+  }
+
+  /**
    * Helper method to fetch free/busy data for the user and turn it into calendar data
    */
   private function fetch_freebusy($limit_changed = null)
diff --git a/plugins/calendar/skins/larry/calendar.css b/plugins/calendar/skins/larry/calendar.css
index 36b374b..1caff48 100644
--- a/plugins/calendar/skins/larry/calendar.css
+++ b/plugins/calendar/skins/larry/calendar.css
@@ -221,7 +221,7 @@ pre {
 	right: 45px;
 	cursor: default;
 	background: url(images/calendars.png) right 20px no-repeat;
-	overflow: hidden;
+	overflow-x: hidden;
 	text-overflow: ellipsis;
 	white-space: nowrap;
 	color: #004458;
@@ -275,9 +275,10 @@ pre {
 	top: 2px;
 	right: 22px;
 	padding: 5px 20px 0 6px;
-	min-width: 40px;
+/*	min-width: 40px; */
 	height: 19px;
 	text-align: right;
+	z-index: 4;
 }
 
 #calendars .treelist div:hover span.actions {
@@ -425,6 +426,30 @@ pre {
 }
 */
 
+#calendars .treelist .calendar .count {
+	position: absolute;
+	display: inline-block;
+	top: 5px;
+	right: 68px;
+	min-width: 1.3em;
+	padding: 2px 4px;
+	background: #005d76;
+	background: -moz-linear-gradient(top, #005d76 0%, #004558 100%);
+	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#005d76), color-stop(100%,#004558));
+	background: -o-linear-gradient(top, #005d76 0%, #004558 100%);
+	background: -ms-linear-gradient(top, #005d76 0%, #004558 100%);
+	background: linear-gradient(to bottom, #005d76 0%, #004558 100%);
+	-webkit-box-shadow: inset 0 1px 1px 0 #002635;
+	box-shadow: inset 0 1px 1px 0 #002635;
+	border-radius: 10px;
+	color: #fff;
+	text-align: center;
+	font-style: normal;
+	font-weight: bold;
+	text-shadow: none;
+	z-index: 3;
+}
+
 #calendars .searchresults {
 	background: #b0ccd7;
 	margin-top: 8px;




More information about the commits mailing list