Automatically add and remove free/busy information for added/removed attendees.
authorJan Schneider <jan@horde.org>
Mon, 24 May 2010 17:57:23 +0000 (19:57 +0200)
committerJan Schneider <jan@horde.org>
Mon, 24 May 2010 17:57:42 +0000 (19:57 +0200)
framework/Ajax/lib/Horde/Ajax/Application/Base.php
horde/js/prettyautocomplete.js
kronolith/js/kronolith.js
kronolith/lib/Ajax/Application.php
kronolith/lib/Ajax/Imple/ContactAutoCompleter.php

index 89988ad..1ee2e2a 100644 (file)
@@ -48,6 +48,14 @@ abstract class Horde_Ajax_Application_Base
     protected $_readOnly = array();
 
     /**
+     * Default domain.
+     *
+     * @see parseEmailAddress()
+     * @var string
+     */
+    protected $_defaultDomain;
+
+    /**
      * Constructor.
      *
      * @param string $app     The application name.
@@ -143,6 +151,31 @@ abstract class Horde_Ajax_Application_Base
     }
 
     /**
+     * Parses a valid email address out of a complete address string.
+     *
+     * Variables used:
+     * - mbox (string): The name of the new mailbox.
+     * - parent (string): The parent mailbox.
+     *
+     * @return string  The parsed email address.
+     * @throws Horde_Exception
+     * @throws Horde_Mail_Exception
+     */
+    public function parseEmailAddress()
+    {
+        $rfc822 = new Horde_Mail_Rfc822();
+        $params = array();
+        if ($this->_defaultDomain) {
+            $params['default_domain'] = $this->_defaultDomain;
+        }
+        $res = $rfc822->parseAddressList($this->_vars->email, $params);
+        if (!count($res)) {
+            throw new Horde_Exception(_("No valid email address found"));
+        }
+        return (object)array('email' => Horde_Mime_Address::writeAddress($res[0]->mailbox, $res[0]->host));
+    }
+
+    /**
      * Loads a chunk of PHP code (usually an HTML template) from the
      * application's templates directory.
      *
index 069c4bd..a18b166 100644 (file)
@@ -18,7 +18,8 @@ var PrettyAutocompleter = Class.create({
             // This function should *always* return escaped HTML
             displayFilter: function(t) { return t.escapeHTML() },
             filterCallback: this._filterChoices.bind(this),
-            onAdd: Prototype.K
+            onAdd: Prototype.K,
+            onRemove: Prototype.K
         }, params || {});
 
         // Array to hold the currently selected items to ease with removing
@@ -167,6 +168,7 @@ var PrettyAutocompleter = Class.create({
         var value = $F(this.p.trigger).replace(/^,/, '').strip();
         if (value.length) {
             this.addNewItemNode(value);
+            this.p.onAdd(value);
         }
     },
 
@@ -182,6 +184,7 @@ var PrettyAutocompleter = Class.create({
     _updateElement: function(item)
     {
         this.addNewItemNode(item);
+        this.p.onAdd(item);
     },
 
     addNewItemNode: function(value)
@@ -211,7 +214,6 @@ var PrettyAutocompleter = Class.create({
 
         // ...and keep the selectedItems array up to date.
         this.selectedItems.push({ rawValue: value, displayValue: displayValue });
-        this.p.onAdd(value);
     },
 
     removeItemNode: function(item)
@@ -224,6 +226,7 @@ var PrettyAutocompleter = Class.create({
             }
         }
         item.remove();
+        this.p.onRemove(value);
     },
 
     disable: function()
index 9a2893e..55cacfb 100644 (file)
@@ -4775,7 +4775,21 @@ KronolithCore = {
      */
     addAttendee: function(attendee)
     {
-        var tr = new Element('tr'), i;
+        if (typeof attendee == 'string') {
+            if (attendee.include('@')) {
+                this.doAction('parseEmailAddress',
+                              { email: attendee },
+                              function (r) {
+                                  if (r.response.email) {
+                                      this.addAttendee({ e: r.response.email, l: attendee });
+                                  }
+                              }.bind(this));
+                return;
+            } else {
+                attendee = { l: attendee };
+            }
+        }
+
         if (attendee.e) {
             this.fbLoading++;
             this.doAction('getFreeBusy',
@@ -4788,11 +4802,14 @@ KronolithCore = {
                               if (Object.isUndefined(r.response.fb)) {
                                   return;
                               }
-                              this.freeBusy.set(attendee.e, [ tr, r.response.fb ]);
+                              this.freeBusy.get(attendee.l)[1] = r.response.fb;
                               this.insertFreeBusy(attendee.e);
                           }.bind(this));
         }
-        tr.insert(new Element('td').writeAttribute('title', attendee.l).insert(attendee.e ? attendee.e.escapeHTML() : attendee.l));
+
+        var tr = new Element('tr'), i;
+        this.freeBusy.set(attendee.l, [ tr ]);
+        tr.insert(new Element('td').writeAttribute('title', attendee.l).insert(attendee.e ? attendee.e.escapeHTML() : attendee.l.escapeHTML()));
         for (i = 0; i < 24; i++) {
             tr.insert(new Element('td', { className: 'kronolithFBUnknown' }));
         }
@@ -4800,6 +4817,18 @@ KronolithCore = {
     },
 
     /**
+     * Removes an attendee row from the free/busy table.
+     *
+     * @param object attendee  An attendee object with the properties:
+     *                         - e: email address
+     *                         - l: the display name of the attendee
+     */
+    removeAttendee: function(attendee)
+    {
+        this.freeBusy.get(attendee)[0].remove();
+    },
+
+    /**
      * Updates rows with free/busy information in the attendees table.
      *
      * @todo Update when changing dates; only show free time for fb times we
index afec82e..20e342a 100644 (file)
@@ -22,6 +22,18 @@ class Kronolith_Ajax_Application extends Horde_Ajax_Application_Base
     public $notify = true;
 
     /**
+     * Constructor.
+     *
+     * @param string $app     The application name.
+     * @param string $action  The AJAX action to perform.
+     */
+    public function __construct($app, $action = null)
+    {
+        parent::__construct($app, $action);
+        $this->_defaultDomain = empty($GLOBALS['conf']['storage']['default_domain']) ? null : $GLOBALS['conf']['storage']['default_domain'];
+    }
+
+    /**
      * TODO
      */
     public function listEvents()
index cfc6cc6..06582b3 100644 (file)
@@ -26,7 +26,9 @@ class Kronolith_Ajax_Imple_ContactAutoCompleter extends Horde_Ajax_Imple_AutoCom
         $ret = array('params' => $js_params,
                      'raw_params' => array(
                          'onSelect' => 'function (v) { if (!v.endsWith(";")) { v += ","; } return v + " "; }',
-                         'onType' => 'function (e) { return e.include("<") ? "" : e; }'
+                         'onType' => 'function (e) { return e.include("<") ? "" : e; }',
+                         'onAdd' => 'KronolithCore.addAttendee.bind(KronolithCore)',
+                         'onRemove' => 'KronolithCore.removeAttendee.bind(KronolithCore)',
                       ));
         if (empty($this->_params['pretty'])) {
             $ret['ajax'] = 'ContactAutoCompleter';