Color task lists instead of categories. Only used in Kronolith so far.
authorJan Schneider <jan@horde.org>
Sat, 28 Nov 2009 17:16:14 +0000 (18:16 +0100)
committerJan Schneider <jan@horde.org>
Sat, 28 Nov 2009 17:16:14 +0000 (18:16 +0100)
Add calendar management. Beside a few small featurettes, the Ajax interface is
feature complete for the first release now.

33 files changed:
kronolith/ajax.php
kronolith/calendars/delete.php
kronolith/calendars/edit.php
kronolith/calendars/remote_edit.php
kronolith/calendars/remote_unsubscribe.php
kronolith/js/kronolith.js
kronolith/lib/Driver/Ical.php
kronolith/lib/Event/Horde.php
kronolith/lib/Forms/CreateCalendar.php
kronolith/lib/Forms/DeleteCalendar.php
kronolith/lib/Forms/EditCalendar.php
kronolith/lib/Forms/SubscribeRemoteCalendar.php
kronolith/lib/Forms/UnsubscribeRemoteCalendar.php
kronolith/lib/Kronolith.php
kronolith/templates/chunks/calendar.php [new file with mode: 0644]
kronolith/templates/index/edit.inc
kronolith/templates/index/index.inc
kronolith/templates/index/task.inc
kronolith/themes/screen.css
nag/docs/CHANGES
nag/lib/Api.php
nag/lib/Forms/CreateTaskList.php
nag/lib/Forms/DeleteTaskList.php
nag/lib/Forms/EditTaskList.php
nag/lib/Nag.php
nag/lib/Task.php
nag/scripts/sql/nag.mssql.sql
nag/scripts/sql/nag.oci8.sql
nag/scripts/sql/nag.sql
nag/scripts/sql/nag.xml
nag/scripts/upgrades/2009-11-28_add_attribute_color.sql [new file with mode: 0644]
nag/tasklists/delete.php
nag/tasklists/edit.php

index c8e7f53..411dae5 100644 (file)
@@ -469,6 +469,195 @@ try {
         $result->events = 'Searched for calendars: ' . Horde_Util::getFormData('title');
         break;
 
+    case 'SaveCalendar':
+        $calendar_id = Horde_Util::getFormData('calendar');
+        $result = new stdClass;
+
+        switch (Horde_Util::getFormData('type')) {
+        case 'internal':
+            $info = array();
+            foreach (array('name', 'color', 'description', 'tags') as $key) {
+                $info[$key] = Horde_Util::getFormData($key);
+            }
+
+            // Create a calendar.
+            if (!$calendar_id) {
+                if (!Horde_Auth::getAuth() || $prefs->isLocked('default_share')) {
+                    break 2;
+                }
+                $calendar = Kronolith::addShare($info);
+                if ($calendar instanceof PEAR_Error) {
+                    $notification->push($calendar, 'horde.error');
+                    break 2;
+                }
+                $notification->push(sprintf(_("The calendar \"%s\" has been created."), $info['name']), 'horde.success');
+                $result->calendar = $calendar->getName();
+                break;
+            }
+
+            // Update a calendar.
+            $calendar = $kronolith_shares->getShare($calendar_id);
+            if ($calendar instanceof PEAR_Error) {
+                $notification->push($calendar, 'horde.error');
+                break 2;
+            }
+            $original_name = $calendar->get('name');
+            $updated = Kronolith::updateShare($calendar, $info);
+            if ($updated instanceof PEAR_Error) {
+                $notification->push($updated, 'horde.error');
+                break 2;
+            }
+            if ($calendar->get('name') != $original_name) {
+                $notification->push(sprintf(_("The calendar \"%s\" has been renamed to \"%s\"."), $original_name, $calendar->get('name')), 'horde.success');
+            } else {
+                $notification->push(sprintf(_("The calendar \"%s\" has been saved."), $original_name), 'horde.success');
+            }
+
+            break;
+
+        case 'tasklists':
+            $calendar = array();
+            foreach (array('name', 'color', 'description') as $key) {
+                $calendar[$key] = Horde_Util::getFormData($key);
+            }
+
+            // Create a task list.
+            if (!$calendar_id) {
+                if (!Horde_Auth::getAuth() || $prefs->isLocked('default_share')) {
+                    break 2;
+                }
+                $tasklist = $registry->tasks->addTasklist($calendar['name'], $calendar['description'], $calendar['color']);
+                if ($tasklist instanceof PEAR_Error) {
+                    $notification->push($tasklist, 'horde.error');
+                    break 2;
+                }
+                $notification->push(sprintf(_("The task list \"%s\" has been created."), $calendar['name']), 'horde.success');
+                $result->calendar = $tasklist;
+                break;
+            }
+
+            // Update a task list.
+            $calendar_id = substr($calendar_id, 6);
+            $tasklists = $registry->tasks->listTasklists(true, Horde_Perms::EDIT);
+            if (!isset($tasklists[$calendar_id])) {
+                $notification->push(_("You are not allowed to change this task list."), 'horde.error');
+                break 2;
+            }
+            $updated = $registry->tasks->updateTasklist($calendar_id, $calendar);
+            if ($updated instanceof PEAR_Error) {
+                $notification->push($updated, 'horde.error');
+                break 2;
+            }
+            if ($tasklists[$calendar_id]->get('name') != $calendar['name']) {
+                $notification->push(sprintf(_("The task list \"%s\" has been renamed to \"%s\"."), $tasklists[$calendar_id]->get('name'), $calendar['name']), 'horde.success');
+            } else {
+                $notification->push(sprintf(_("The task list \"%s\" has been saved."), $tasklists[$calendar_id]->get('name')), 'horde.success');
+            }
+
+            break;
+
+        case 'remote':
+            $calendar = array();
+            foreach (array('name', 'description', 'url', 'color', 'username', 'password') as $key) {
+                $calendar[$key] = Horde_Util::getFormData($key);
+            }
+            $subscribed = Kronolith::subscribeRemoteCalendar($calendar);
+            if ($subscribed instanceof PEAR_Error) {
+                $notification->push($subscribed, 'horde.error');
+                break 2;
+            }
+            if ($calendar_id) {
+                $notification->push(sprintf(_("The calendar \"%s\" has been saved."), $calendar['name']), 'horde.success');
+            } else {
+                $notification->push(sprintf(_("You have been subscribed to \"%s\" (%s)."), $calendar['name'], $calendar['url']), 'horde.success');
+            }
+            break;
+        }
+
+        $result->saved = true;
+        $result->color = Kronolith::foregroundColor($calendar);
+        break;
+
+    case 'DeleteCalendar':
+        $calendar_id = Horde_Util::getFormData('calendar');
+
+        switch (Horde_Util::getFormData('type')) {
+        case 'internal':
+            $calendar = $kronolith_shares->getShare($calendar_id);
+            if ($calendar instanceof PEAR_Error) {
+                $notification->push($calendar, 'horde.error');
+                break 2;
+            }
+            $deleted = Kronolith::deleteShare($calendar);
+            if ($deleted instanceof PEAR_Error) {
+                $notification->push(sprintf(_("Unable to delete \"%s\": %s"), $calendar->get('name'), $deleted->getMessage()), 'horde.error');
+                break 2;
+            }
+            $notification->push(sprintf(_("The calendar \"%s\" has been deleted."), $calendar->get('name')), 'horde.success');
+            break;
+
+        case 'tasklists':
+            $calendar_id = substr($calendar_id, 6);
+            $tasklists = $registry->tasks->listTasklists(true);
+            if (!isset($tasklists[$calendar_id])) {
+                $notification->push(_("You are not allowed to delete this task list."), 'horde.error');
+                break 2;
+            }
+            $deleted = $registry->tasks->deleteTasklist($calendar_id);
+            if ($deleted instanceof PEAR_Error) {
+                $notification->push(sprintf(_("Unable to delete \"%s\": %s"), $tasklists[$calendar_id]->get('name'), $deleted->getMessage()), 'horde.error');
+                break 2;
+            }
+            $notification->push(sprintf(_("The task list \"%s\" has been deleted."), $tasklists[$calendar_id]->get('name')), 'horde.success');
+            break;
+
+        case 'remote':
+            $deleted = Kronolith::unsubscribeRemoteCalendar($calendar_id);
+            if ($deleted instanceof PEAR_Error) {
+                $notification->push($deleted, 'horde.error');
+                break 2;
+            }
+            $notification->push(sprintf(_("You have been unsubscribed from \"%s\" (%s)."), $deleted['name'], $deleted['url']), 'horde.success');
+            break;
+        }
+
+        $result = new stdClass;
+        $result->deleted = true;
+        break;
+
+    case 'GetRemoteInfo':
+        $params = array();
+        if ($user = Horde_Util::getFormData('username')) {
+            $params['user'] = $user;
+            $params['password'] = Horde_Util::getFormData('password');
+        }
+        if (!empty($GLOBALS['conf']['http']['proxy']['proxy_host'])) {
+            $params['proxy'] = $GLOBALS['conf']['http']['proxy'];
+        }
+        $driver = Kronolith_Driver::factory('Ical', $params);
+        $driver->open(Horde_Util::getFormData('url'));
+        $ical = $driver->getRemoteCalendar(false);
+        if ($ical instanceof PEAR_Error) {
+            if ($ical->getCode() == 401) {
+                $result = new stdClass;
+                $result->auth = true;
+                break;
+            }
+            $notification->push($ical, 'horde.error');
+            break;
+        }
+        $result = new stdClass;
+        $result->success = true;
+        $name = $ical->getAttribute('X-WR-CALNAME');
+        if (!($name instanceof PEAR_Error)) {
+            $result->name = $name;
+        }
+        $desc = $ical->getAttribute('X-WR-CALDESC');
+        if (!($desc instanceof PEAR_Error)) {
+            $result->desc = $desc;
+        }
+        break;
+
     case 'SaveCalPref':
         break;
 
index 9dc3ab7..33a5edd 100644 (file)
@@ -18,24 +18,12 @@ if (!Horde_Auth::getAuth()) {
 }
 
 $vars = Horde_Variables::getDefaultVariables();
-$calendar_id = $vars->get('c');
-if ($calendar_id == Horde_Auth::getAuth()) {
-    $notification->push(_("This calendar cannot be deleted."), 'horde.warning');
-    header('Location: ' . Horde::applicationUrl('calendars/', true));
-    exit;
-}
-
-$calendar = $kronolith_shares->getShare($calendar_id);
+$calendar = $kronolith_shares->getShare($vars->get('c'));
 if (is_a($calendar, 'PEAR_Error')) {
     $notification->push($calendar, 'horde.error');
     header('Location: ' . Horde::applicationUrl('calendars/', true));
     exit;
-} elseif ($calendar->get('owner') != Horde_Auth::getAuth()) {
-    $notification->push(_("You are not allowed to delete this calendar."), 'horde.error');
-    header('Location: ' . Horde::applicationUrl('calendars/', true));
-    exit;
 }
-
 $form = new Kronolith_DeleteCalendarForm($vars, $calendar);
 
 // Execute if the form is valid (must pass with POST variables only).
index 1cad577..10faa3e 100644 (file)
@@ -23,10 +23,6 @@ if (is_a($calendar, 'PEAR_Error')) {
     $notification->push($calendar, 'horde.error');
     header('Location: ' . Horde::applicationUrl('calendars/', true));
     exit;
-} elseif ($calendar->get('owner') != Horde_Auth::getAuth()) {
-    $notification->push(_("You are not allowed to change this calendar."), 'horde.error');
-    header('Location: ' . Horde::applicationUrl('calendars/', true));
-    exit;
 }
 $form = new Kronolith_EditCalendarForm($vars, $calendar);
 
index 1903260..90f29bc 100644 (file)
@@ -60,6 +60,8 @@ if ($key) {
 
 $vars->set('name', $calendar['name']);
 $vars->set('url', $calendar['url']);
+$vars->set('decription', $calendar['desc']);
+$vars->set('color', $calendar['color']);
 $vars->set('username', $username);
 $vars->set('password', $password);
 $title = $form->getTitle();
index 5de9693..b46270f 100644 (file)
@@ -19,22 +19,6 @@ if (!Horde_Auth::getAuth() || $prefs->isLocked('remote_cals')) {
 }
 
 $vars = Horde_Variables::getDefaultVariables();
-$url = $vars->get('url');
-
-$remote_calendar = null;
-$remote_calendars = unserialize($GLOBALS['prefs']->getValue('remote_cals'));
-foreach ($remote_calendars as $key => $calendar) {
-    if ($calendar['url'] == $url) {
-        $remote_calendar = $calendar;
-        break;
-    }
-}
-if (is_null($remote_calendar)) {
-    $notification->push(_("The remote calendar was not found."), 'horde.error');
-    header('Location: ' . Horde::applicationUrl('calendars/', true));
-    exit;
-}
-
 $form = new Kronolith_UnsubscribeRemoteCalendarForm($vars, $remote_calendar);
 
 // Execute if the form is valid (must pass with POST variables only).
index 195ba7a..70d69cb 100644 (file)
@@ -1,6 +1,5 @@
 /**
  * kronolith.js - Base application logic.
- * NOTE: ContextSensitive.js must be loaded before this file.
  *
  * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
  *
@@ -405,6 +404,16 @@ KronolithCore = {
             this._addHistory(fullloc);
             break;
 
+        case 'calendar':
+            if (!this.view) {
+                this.go(Kronolith.conf.login_view);
+                this.go.bind(this, fullloc, data).defer();
+                return;
+            }
+            this.editCalendar(locParts.join(':'));
+            this._addHistory(fullloc);
+            break;
+
         case 'options':
             this.closeView('iframe');
             this.iframeContent(loc, Kronolith.conf.prefs_url);
@@ -799,6 +808,30 @@ KronolithCore = {
     },
 
     /**
+     * Inserts a calendar entry in the sidebar menu.
+     *
+     * @param string type  The calendar type.
+     * @param string id    The calendar id.
+     * @param object cal   The calendar object.
+     * @param Element div  Container DIV where to add the entry (optional).
+     */
+    insertCalendarInList: function(type, id, cal, div)
+    {
+        if (!div) {
+            div = this.getCalendarList(type, cal.owner);
+        }
+        if (cal.owner) {
+            div.insert(new Element('SPAN', { 'class': 'kronolithCalEdit' })
+                       .insert('&rsaquo;'));
+        }
+        div.insert(new Element('DIV', { 'class': cal.show ? 'kronolithCalOn' : 'kronolithCalOff' })
+                   .store('calendar', id)
+                   .store('calendarclass', type)
+                   .setStyle({ backgroundColor: cal.bg, color: cal.fg })
+                   .update(cal.name.escapeHTML()));
+    },
+
+    /**
      * Rebuilds the list of calendars.
      */
     updateCalendarList: function()
@@ -809,19 +842,11 @@ KronolithCore = {
         $H(Kronolith.conf.calendars.internal).each(function(cal) {
             if (cal.value.owner) {
                 my++;
-                div = $('kronolithMyCalendars');
-                div.insert(new Element('SPAN', { 'class': 'kronolithCalEdit' })
-                           .insert('&rsaquo;'));
             } else {
                 shared++;
-                div = $('kronolithSharedCalendars');
             }
-            div.insert(new Element('DIV', { 'class': cal.value.show ? 'kronolithCalOn' : 'kronolithCalOff' })
-                       .store('calendar', cal.key)
-                       .store('calendarclass', 'internal')
-                       .setStyle({ backgroundColor: cal.value.bg, color: cal.value.fg })
-                       .update(cal.value.name.escapeHTML()));
-        });
+            this.insertCalendarInList('internal', cal.key, cal.value);
+        }, this);
         if (my) {
             $('kronolithMyCalendars').show();
         } else {
@@ -838,19 +863,11 @@ KronolithCore = {
         $H(Kronolith.conf.calendars.tasklists).each(function(cal) {
             if (cal.value.owner) {
                 my++;
-                div = $('kronolithMyTasklists');
-                div.insert(new Element('SPAN', { 'class': 'kronolithCalEdit' })
-                           .insert('&rsaquo;'));
             } else {
                 shared++;
-                div = $('kronolithSharedTasklists');
             }
-            div.insert(new Element('DIV', { 'class': cal.value.show ? 'kronolithCalOn' : 'kronolithCalOff' })
-                       .store('calendar', cal.key)
-                       .store('calendarclass', 'tasklists')
-                       .setStyle({ backgroundColor: cal.value.bg, color: cal.value.fg })
-                       .update(cal.value.name.escapeHTML()));
-        });
+            this.insertCalendarInList('tasklists', cal.key, cal.value);
+        }, this);
         if (my) {
             $('kronolithMyTasklists').show();
         } else {
@@ -873,29 +890,17 @@ KronolithCore = {
         ext.each(function(api) {
             $('kronolithExternalCalendars')
                 .insert(new Element('H3')
-                        .insert(new Element('A', { 'class': 'kronolithAdd'  })
-                                .update('+'))
                         .insert({ bottom: extNames.get(api.key).escapeHTML() }))
                 .insert(new Element('DIV', { 'id': 'kronolithExternalCalendar' + api.key, 'class': 'kronolithCalendars' }));
             api.value.each(function(cal) {
-                $('kronolithExternalCalendar' + api.key)
-                    .insert(new Element('DIV', { 'class': cal.value.show ? 'kronolithCalOn' : 'kronolithCalOff' })
-                            .store('calendar', api.key + '/' + cal.key)
-                            .store('calendarclass', 'external')
-                            .setStyle({ backgroundColor: cal.value.bg, color: cal.value.fg })
-                            .update(cal.value.name.escapeHTML()));
-            });
-        });
+                this.insertCalendarInList('external', api.key + '/' + cal.key, cal.value, $('kronolithExternalCalendar' + api.key));
+            }, this);
+        }, this);
 
         remote = $H(Kronolith.conf.calendars.remote);
         remote.each(function(cal) {
-            $('kronolithRemoteCalendars')
-                .insert(new Element('DIV', { 'class': cal.value.show ? 'kronolithCalOn' : 'kronolithCalOff' })
-                        .store('calendar', cal.key)
-                        .store('calendarclass', 'remote')
-                        .setStyle({ backgroundColor: cal.value.bg, color: cal.value.fg })
-                        .update(cal.value.name.escapeHTML()));
-        });
+            this.insertCalendarInList('remote', cal.key, cal.value);
+        }, this);
         if (remote.size()) {
             $('kronolithRemoteCalendars').show();
         } else {
@@ -904,13 +909,8 @@ KronolithCore = {
 
         holidays = $H(Kronolith.conf.calendars.holiday);
         holidays.each(function(cal) {
-            $('kronolithHolidayCalendars')
-                .insert(new Element('DIV', { 'class': cal.value.show ? 'kronolithCalOn' : 'kronolithCalOff' })
-                        .store('calendar', cal.key)
-                        .store('calendarclass', 'holiday')
-                        .setStyle({ backgroundColor: cal.value.bg, color: cal.value.fg })
-                        .update(cal.value.name.escapeHTML()));
-        });
+            this.insertCalendarInList('holiday', cal.key, cal.value);
+        }, this);
         if (holidays.size()) {
             $('kronolithHolidayCalendars').show();
         } else {
@@ -919,6 +919,33 @@ KronolithCore = {
     },
 
     /**
+     * Returns the DIV container that holds all calendars of a certain type.
+     *
+     * @param string type  A calendar type
+     *
+     * @return Element  The container of the calendar type.
+     */
+    getCalendarList: function(type, personal)
+    {
+        switch (type) {
+        case 'internal':
+            return personal
+                ? $('kronolithMyCalendars')
+                : $('kronolithSharedCalendars');
+        case 'tasklists':
+            return personal
+                ? $('kronolithMyTasklists')
+                : $('kronolithSharedTasklists');
+        case 'external':
+            return $('kronolithExternalCalendars');
+        case 'remote':
+            return $('kronolithRemoteCalendars');
+        case 'holiday':
+            return $('kronolithHolidayCalendars');
+        }
+    },
+
+    /**
      * Propagates a SELECT drop down list with the editable calendars.
      *
      * @param string id  The id of the SELECT element.
@@ -1980,6 +2007,180 @@ KronolithCore = {
     },
 
     /**
+     * Opens the form for editing a calendar.
+     *
+     * @param string calendar  Calendar type and calendar id, separated by '|'.
+     */
+    editCalendar: function(calendar)
+    {
+        if ($('kronolithCalendarDialog')) {
+            RedBox.showHtml($('kronolithCalendarDialog').show());
+            this._editCalendar(calendar);
+        } else {
+            RedBox.loading();
+            this.doAction('ChunkContent', { 'chunk': 'calendar' }, function(r) {
+                if (r.response.chunk) {
+                    RedBox.showHtml(r.response.chunk);
+                    this._editCalendar(calendar);
+                }
+            }.bind(this));
+        }
+    },
+
+    /**
+     * Callback for editing a calendar. Fills the edit form with the correct
+     * values.
+     *
+     * @param string calendar  Calendar type and calendar id, separated by '|'.
+     */
+    _editCalendar: function(calendar)
+    {
+        calendar = calendar.split('|');
+        var type = calendar[0];
+        calendar = calendar.length == 1 ? null : calendar[1];
+
+        $('kronolithCalendarDialog').select('.kronolithCalendarDiv').invoke('hide');
+        $('kronolithCalendar' + type + '1').show();
+        $('kronolithCalendarForm' + type).select('.kronolithCalendarContinue').invoke('enable');
+
+        /* Reset form to defaults if this is for adding calendars. */
+        if (!calendar) {
+            var fields = [ 'Id', 'Name' ];
+            switch (type) {
+            case 'internal':
+            case 'tasklists':
+                fields.push('Description');
+                break;
+            case 'remote':
+                fields.push('Description', 'Url', 'Username', 'Password');
+                break;
+            }
+            fields.each(function(field) {
+                $('kronolithCalendar' + type + field).clear();
+            });
+            $('kronolithCalendar' + type + 'Color').setValue('#dddddd').setStyle({ 'backgroundColor': '#dddddd', 'color': '#000' });
+            $('kronolithCalendarForm' + type).down('.kronolithCalendarDelete').hide();
+            return;
+        }
+
+        if (Object.isUndefined(Kronolith.conf.calendars[type]) ||
+            Object.isUndefined(Kronolith.conf.calendars[type][calendar])) {
+            this._closeRedBox();
+            window.history.back();
+            return;
+        }
+
+        var info = Kronolith.conf.calendars[type][calendar];
+
+        $('kronolithCalendar' + type + 'Id').setValue(calendar);
+        $('kronolithCalendar' + type + 'Name').setValue(info.name);
+        $('kronolithCalendar' + type + 'Color').setValue(info.bg).setStyle({ 'backgroundColor': info.bg, 'color': info.fg });
+
+        switch (type) {
+        case 'internal':
+        case 'tasklists':
+            $('kronolithCalendarinternalDescription').setValue(info.desc);
+            break;
+        case 'remote':
+            $('kronolithCalendarremoteUrl').setValue(calendar);
+            $('kronolithCalendarremoteDescription').setValue(info.desc);
+            $('kronolithCalendarremoteUsername').setValue(info.user);
+            $('kronolithCalendarremotePassword').setValue(info.password);
+            break;
+        }
+
+        /* Currently we only show the calendar form for own calendars anyway,
+           but be prepared. */
+        var form = $('kronolithCalendarForm' + type);
+        if (info.owner) {
+            form.enable();
+            if (type == 'internal' &&
+                calendar == Kronolith.conf.user) {
+                form.down('.kronolithCalendarDelete').hide();
+            } else {
+                form.down('.kronolithCalendarDelete').show();
+            }
+            form.down('.kronolithCalendarSave').show();
+        } else {
+            form.disable();
+            form.down('.kronolithCalendarDelete').hide();
+            form.down('.kronolithCalendarSave').hide();
+        }
+    },
+
+    /**
+     * Opens the next screen of the calendar management wizard.
+     *
+     * @param string type  The calendar type.
+     */
+    _calendarNext: function(type)
+    {
+        var i = 1;
+        while (!$('kronolithCalendar' + type + i).visible()) {
+            i++;
+        }
+        $('kronolithCalendar' + type + i).hide();
+        $('kronolithCalendar' + type + ++i).show();
+    },
+
+    /**
+     * Submits the calendar form to save the calendar data.
+     *
+     * @param Element  The form node.
+     */
+    saveCalendar: function(form)
+    {
+        var type = form.id.replace(/kronolithCalendarForm/, ''),
+            data = form.serialize({ 'hash': true });
+
+        this.doAction('SaveCalendar',
+                      data,
+                      function(r) {
+                          if (r.response.saved) {
+                              if (data.calendar) {
+                                  var cal = Kronolith.conf.calendars[type][data.calendar],
+                                      color = {
+                                          backgroundColor: data.color,
+                                          color: r.response.color
+                                      };
+                                  cal.bg = data.color;
+                                  cal.fg = r.response.color;
+                                  cal.name = data.name;
+                                  cal.desc = data.description;
+                                  this.getCalendarList(type, cal.owner).select('div').each(function(element) {
+                                      if (element.retrieve('calendar') == data.calendar) {
+                                          element
+                                              .setStyle(color)
+                                              .update(cal.name.escapeHTML());
+                                          throw $break;
+                                      }
+                                  });
+                                  $('kronolithBody').select('div').each(function(el) {
+                                      if (el.retrieve('calendar') == type + '|' + data.calendar) {
+                                          el.setStyle(color);
+                                      }
+                                  });
+                              } else {
+                                  var cal = {
+                                      bg: data.color,
+                                      fg: r.response.color,
+                                      name: data.name,
+                                      desc: data.description,
+                                      edit: true,
+                                      owner: true,
+                                      show: true
+                                  };
+                                  Kronolith.conf.calendars[type][r.response.calendar] = cal;
+                                  this.insertCalendarInList(type, r.response.calendar, cal);
+                              }
+                          }
+                          form.down('.kronolithCalendarSave').enable();
+                          this._closeRedBox();
+                          window.history.back();
+                      }.bind(this));
+    },
+
+    /**
      * Parses a date attribute string into a Date object.
      *
      * For other strings use Date.parse().
@@ -2144,9 +2345,9 @@ KronolithCore = {
     },
 
     /**
-     * Deletes an event from the cache.
+     * Deletes an event or a complete calendar from the cache.
      *
-     * @param string event     An event ID.
+     * @param string event     An event ID or empty if deleting the calendar.
      * @param string calendar  A calendar string or array.
      */
     _deleteCache: function(event, calendar)
@@ -2158,9 +2359,13 @@ KronolithCore = {
             !this.ecache.get(calendar[0]).get(calendar[1])) {
             return;
         }
-        this.ecache.get(calendar[0]).get(calendar[1]).each(function(day) {
-            day.value.unset(event);
-        });
+        if (event) {
+            this.ecache.get(calendar[0]).get(calendar[1]).each(function(day) {
+                day.value.unset(event);
+            });
+        } else {
+            this.ecache.get(calendar[0]).unset(calendar[1]);
+        }
     },
 
     /**
@@ -2271,6 +2476,14 @@ KronolithCore = {
                     this.go('search:' + $F('kronolithSearchTerm'))
                     e.stop();
                     break;
+
+                case 'kronolithCalendarForminternal':
+                case 'kronolithCalendarFormtasklists':
+                case 'kronolithCalendarFormremote':
+                    // Disabled for now, we have to also catch Continue buttons.
+                    //this.saveCalendar(form);
+                    //e.stop();
+                    break;
                 }
                 break;
 
@@ -2355,29 +2568,6 @@ KronolithCore = {
                 this.toggleAllDay();
                 return;
 
-            case 'kronolithEventLinkDescription':
-            case 'kronolithEventLinkReminder':
-            case 'kronolithEventLinkRecur':
-            case 'kronolithEventLinkUrl':
-            case 'kronolithEventLinkAttendees':
-            case 'kronolithEventLinkTags':
-                $('kronolithEventDialog').select('.kronolithTabsOption').invoke('hide');
-                $(id.replace(/Link/, 'Tab')).show();
-                $('kronolithEventDialog').select('.tabset li').invoke('removeClassName', 'activeTab');
-                elt.parentNode.addClassName('activeTab');
-                e.stop();
-                return;
-
-            case 'kronolithTaskLinkDescription':
-            case 'kronolithTaskLinkReminder':
-            case 'kronolithTaskLinkUrl':
-                $('kronolithTaskDialog').select('.kronolithTabsOption').invoke('hide');
-                $(id.replace(/Link/, 'Tab')).show();
-                $('kronolithTaskDialog').select('.tabset li').invoke('removeClassName', 'activeTab');
-                elt.parentNode.addClassName('activeTab');
-                e.stop();
-                return;
-
             case 'kronolithEventLinkNone':
             case 'kronolithEventLinkDaily':
             case 'kronolithEventLinkWeekly':
@@ -2389,11 +2579,13 @@ KronolithCore = {
 
             case 'kronolithEventSave':
                 this.saveEvent();
+                elt.disable();
                 e.stop();
                 return;
 
             case 'kronolithTaskSave':
                 this.saveTask();
+                elt.disable();
                 e.stop();
                 return;
 
@@ -2445,13 +2637,6 @@ KronolithCore = {
                 e.stop();
                 return;
 
-            case 'kronolithEventCancel':
-            case 'kronolithTaskCancel':
-                this._closeRedBox();
-                window.history.back();
-                e.stop();
-                return;
-
             case 'kronolithNavDay':
             case 'kronolithNavWeek':
             case 'kronolithNavMonth':
@@ -2603,6 +2788,26 @@ KronolithCore = {
                 e.stop();
                 return;
 
+            case 'kronolithAdd':
+                this.go('calendar:' + id.replace(/kronolithAdd/, ''));
+                e.stop();
+                return;
+
+            case 'kronolithTabLink':
+                var dialog = elt.up('form');
+                dialog.select('.kronolithTabsOption').invoke('hide');
+                dialog.select('.tabset li').invoke('removeClassName', 'activeTab');
+                $(id.replace(/Link/, 'Tab')).show();
+                elt.parentNode.addClassName('activeTab');
+                e.stop();
+                return;
+
+            case 'kronolithFormCancel':
+                this._closeRedBox();
+                window.history.back();
+                e.stop();
+                return;
+
             case 'kronolithEventTag':
                 $('kronolithEventTags').autocompleter.addNewItemNode(elt.getText());
                 e.stop();
@@ -2616,6 +2821,11 @@ KronolithCore = {
                 }
                 e.stop();
                 return;
+
+            case 'kronolithCalEdit':
+                this.go('calendar:' + elt.next().retrieve('calendarclass') + '|' + elt.next().retrieve('calendar'));
+                e.stop();
+                return;
             }
 
             if (elt.hasClassName('kronolithEvent')) {
@@ -2657,6 +2867,103 @@ KronolithCore = {
                               }.bind(this));
                 e.stop();
                 return;
+            } else if (elt.hasClassName('kronolithCalendarSave')) {
+                elt.disable();
+                this.saveCalendar(elt.up('form'));
+                e.stop();
+                return;
+            } else if (elt.hasClassName('kronolithCalendarContinue')) {
+                var form = elt.up('form'),
+                    type = form.id.replace(/kronolithCalendarForm/, ''),
+                    i = 1;
+                while (!$('kronolithCalendar' + type + i).visible()) {
+                    i++;
+                }
+                if (type == 'remote' && !$F('kronolithCalendarremoteId')) {
+                    elt.disable();
+                    if (i == 1) {
+                        if (!$F('kronolithCalendarremoteUrl')) {
+                            this.showNotifications([ { type: 'horde.warning', message: Kronolith.text.no_url }]);
+                            e.stop();
+                            return;
+                        }
+                        this.doAction('GetRemoteInfo',
+                                      { url: $F('kronolithCalendarremoteUrl') },
+                                      function(r) {
+                                          if (r.response.success) {
+                                              if (r.response.name) {
+                                                  $('kronolithCalendarremoteName').setValue(r.response.name);
+                                              }
+                                              if (r.response.desc) {
+                                                  $('kronolithCalendarremoteDescription').setValue(r.response.desc);
+                                              }
+                                              this._calendarNext(type);
+                                              this._calendarNext(type);
+                                          } else if (r.response.auth) {
+                                              this._calendarNext(type);
+                                          } else {
+                                              elt.enable();
+                                          }
+                                      }.bind(this),
+                                      { asynchronous: false });
+                    }
+                    if (i == 2) {
+                        var params = { url: $F('kronolithCalendarremoteUrl') };
+                        if ($F('kronolithCalendarremoteUsername')) {
+                            params.username = $F('kronolithCalendarremoteUsername');
+                            params.password =  $F('kronolithCalendarremotePassword');
+                        }
+                        this.doAction('GetRemoteInfo',
+                                      params,
+                                      function(r) {
+                                          if (r.response.success) {
+                                              if (r.response.name) {
+                                                  $('kronolithCalendarremoteName').setValue(r.response.name);
+                                              }
+                                              if (r.response.desc) {
+                                                  $('kronolithCalendarremoteDescription').setValue(r.response.desc);
+                                              }
+                                              this._calendarNext(type);
+                                          } else if (r.response.auth) {
+                                              this.showNotifications([{ type: 'horde.warning', message: Kronolith.text.wrong_auth }]);
+                                              elt.enable();
+                                          } else {
+                                              elt.enable();
+                                          }
+                                      }.bind(this));
+                    }
+                    e.stop();
+                    return;
+                }
+                this._calendarNext(type);
+                elt.disable();
+                e.stop();
+                return;
+            } else if (elt.hasClassName('kronolithCalendarDelete')) {
+                var form = elt.up('form'),
+                    type = form.id.replace(/kronolithCalendarForm/, ''),
+                    calendar = $F('kronolithCalendar' + type + 'Id');
+                this.doAction('DeleteCalendar',
+                              { 'type': type, 'calendar': calendar },
+                              function(r) {
+                                  if (r.response.deleted) {
+                                      var div = this.getCalendarList(type, Kronolith.conf.calendars[type][calendar].owner).select('div').find(function(element) {
+                                          return element.retrieve('calendar') == calendar;
+                                      });
+                                      div.previous('span').remove();
+                                      div.remove();
+                                      this._deleteCache(null, calendar);
+                                      $('kronolithBody').select('div').findAll(function(el) {
+                                          return el.retrieve('calendar') == calendar;
+                                      }).invoke('remove');
+                                      delete Kronolith.conf.calendars[type][calendar];
+                                  }
+                                  this._closeRedBox();
+                                  window.history.back();
+                              }.bind(this));
+                elt.disable();
+                e.stop();
+                return;
             }
 
             calClass = elt.retrieve('calendarclass');
@@ -3069,7 +3376,10 @@ KronolithCore = {
 
     _closeRedBox: function()
     {
-        document.body.insert(RedBox.getWindowContents().hide());
+        var content = RedBox.getWindowContents();
+        if (content) {
+            document.body.insert(content.hide());
+        }
         RedBox.close();
     },
 
index 19df1e1..36b24bc 100644 (file)
@@ -55,7 +55,7 @@ class Kronolith_Driver_Ical extends Kronolith_Driver
                                $showRecurrence = false, $hasAlarm = false,
                                $json = false)
     {
-        $iCal = $this->_getRemoteCalendar();
+        $iCal = $this->getRemoteCalendar();
         if (is_a($iCal, 'PEAR_Error')) {
             return $iCal;
         }
@@ -141,7 +141,7 @@ class Kronolith_Driver_Ical extends Kronolith_Driver
             return new Kronolith_Event_Ical($this);
         }
         $eventId = str_replace('ical', '', $eventId);
-        $iCal = $this->_getRemoteCalendar();
+        $iCal = $this->getRemoteCalendar();
         if (is_a($iCal, 'PEAR_Error')) {
             return $iCal;
         }
@@ -164,9 +164,11 @@ class Kronolith_Driver_Ical extends Kronolith_Driver
     /**
      * Fetches a remote calendar into the session and return the data.
      *
+     * @param boolean $cache  Whether to return data from the session cache.
+     *
      * @return Horde_iCalendar  The calendar data, or an error on failure.
      */
-    private function _getRemoteCalendar()
+    public function getRemoteCalendar($cache = true)
     {
         $url = trim($this->_calendar);
 
@@ -175,7 +177,7 @@ class Kronolith_Driver_Ical extends Kronolith_Driver
             $url = str_replace('webcal://', 'http://', $url);
         }
 
-        if (!empty($_SESSION['kronolith']['remote'][$url])) {
+        if ($cache && !empty($_SESSION['kronolith']['remote'][$url])) {
             return $_SESSION['kronolith']['remote'][$url];
         }
 
@@ -206,7 +208,7 @@ class Kronolith_Driver_Ical extends Kronolith_Driver
             Horde::logMessage(sprintf('Failed to retrieve remote calendar: url = "%s", status = %s',
                                       $url, $http->getResponseCode()),
                               __FILE__, __LINE__, PEAR_LOG_INFO);
-            $_SESSION['kronolith']['remote'][$url] = PEAR::raiseError(sprintf(_("Could not open %s."), $url));
+            $_SESSION['kronolith']['remote'][$url] = PEAR::raiseError(sprintf(_("Could not open %s."), $url), $http->getResponseCode());
             return $_SESSION['kronolith']['remote'][$url];
         }
 
index 4e308b0..c3d850d 100644 (file)
@@ -61,6 +61,11 @@ class Kronolith_Event_Horde extends Kronolith_Event
         $this->end = $eventEnd;
         $this->status = Kronolith::STATUS_FREE;
 
+        if (isset($event['color'])) {
+            $this->_backgroundColor = $event['color'];
+            $this->_foregroundColor = Horde_Image::brightness($this->_backgroundColor) < 128 ? '#fff' : '#000';
+        }
+
         if (isset($event['recurrence'])) {
             $recurrence = new Horde_Date_Recurrence($eventStart);
 
index 87c3524..7a71c52 100755 (executable)
@@ -31,23 +31,17 @@ class Kronolith_CreateCalendarForm extends Horde_Form {
         $this->addVariable(_("Color"), 'color', 'colorpicker', false);
         $this->addVariable(_("Description"), 'description', 'longtext', false, false, null, array(4, 60));
         $this->addVariable(_("Tags"), 'tags', 'text', false);
+
         $this->setButtons(array(_("Create")));
     }
 
     function execute()
     {
-        // Create new share.
-        $calendar = $GLOBALS['kronolith_shares']->newShare(hash('md5', microtime()));
-        if (is_a($calendar, 'PEAR_Error')) {
-            return $calendar;
+        $info = array();
+        foreach (array('name', 'color', 'description', 'tags') as $key) {
+            $info[$key] = $this->_vars->get($key);
         }
-        $calendar->set('name', $this->_vars->get('name'));
-        $calendar->set('color', $this->_vars->get('color'));
-        $calendar->set('desc', $this->_vars->get('description'));
-        $tagger = Kronolith::getTagger();
-
-        $tagger->tag($calendar->getName(), $this->_vars->get('tags'), 'calendar');
-        return $GLOBALS['kronolith_shares']->addShare($calendar);
+        return Kronolith::addShare($info);
     }
 
 }
index 4d751d4..0791b02 100644 (file)
@@ -46,42 +46,7 @@ class Kronolith_DeleteCalendarForm extends Horde_Form {
             return false;
         }
 
-        if ($this->_calendar->get('owner') != Horde_Auth::getAuth()) {
-            return PEAR::raiseError(_("Permission denied"));
-        }
-
-        // Delete the calendar.
-        $result = Kronolith::getDriver()->delete($this->_calendar->getName());
-        if (is_a($result, 'PEAR_Error')) {
-            return PEAR::raiseError(sprintf(_("Unable to delete \"%s\": %s"), $this->_calendar->get('name'), $result->getMessage()));
-        } else {
-            // Remove share and all groups/permissions.
-            $result = $GLOBALS['kronolith_shares']->removeShare($this->_calendar);
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
-        }
-
-        // Make sure we still own at least one calendar.
-        if (count(Kronolith::listCalendars(true)) == 0) {
-            // If the default share doesn't exist then create it.
-            if (!$GLOBALS['kronolith_shares']->exists(Horde_Auth::getAuth())) {
-                $identity = Horde_Prefs_Identity::singleton();
-                $name = $identity->getValue('fullname');
-                if (trim($name) == '') {
-                    $name = Horde_Auth::getOriginalAuth();
-                }
-                $calendar = &$GLOBALS['kronolith_shares']->newShare(Horde_Auth::getAuth());
-                if (is_a($calendar, 'PEAR_Error')) {
-                    return;
-                }
-                $calendar->set('name', sprintf(_("%s's Calendar"), $name));
-                $GLOBALS['kronolith_shares']->addShare($calendar);
-                $GLOBALS['all_calendars'][Auth::getAuth()] = &$calendar;
-            }
-        }
-
-        return true;
+        return Kronolith::deleteShare($this->_calendar);
     }
 
 }
index d7cafdc..a4673b2 100644 (file)
@@ -43,26 +43,11 @@ class Kronolith_EditCalendarForm extends Horde_Form {
 
     function execute()
     {
-        $original_name = $this->_calendar->get('name');
-        $new_name = $this->_vars->get('name');
-        $this->_calendar->set('name', $new_name);
-        $this->_calendar->set('color', $this->_vars->get('color'));
-        $this->_calendar->set('desc', $this->_vars->get('description'));
-        if ($original_name != $new_name) {
-            $result = Kronolith::getDriver()->rename($original_name, $new_name);
-            if (is_a($result, 'PEAR_Error')) {
-                return PEAR::raiseError(sprintf(_("Unable to rename \"%s\": %s"), $original_name, $result->getMessage()));
-            }
+        $info = array();
+        foreach (array('name', 'color', 'description', 'tags') as $key) {
+            $info[$key] = $this->_vars->get($key);
         }
-
-        $result = $this->_calendar->save();
-        if (is_a($result, 'PEAR_Error')) {
-            return PEAR::raiseError(sprintf(_("Unable to save calendar \"%s\": %s"), $new_name, $result->getMessage()));
-        }
-
-        $tagger = Kronolith::getTagger();
-        $tagger->replaceTags($this->_calendar->getName(), $this->_vars->get('tags'), 'calendar');
-        return true;
+        return Kronolith::updateShare($this->_calendar, $info);
     }
 
 }
index cb7020e..e04c565 100644 (file)
@@ -28,7 +28,9 @@ class Kronolith_SubscribeRemoteCalendarForm extends Horde_Form {
         parent::Horde_Form($vars, _("Subscribe to a Remote Calendar"));
 
         $this->addVariable(_("Name"), 'name', 'text', true);
+        $this->addVariable(_("Color"), 'color', 'colorpicker', false);
         $this->addVariable(_("URL"), 'url', 'text', true);
+        $this->addVariable(_("Description"), 'description', 'longtext', false, false, null, array(4, 60));
         $this->addVariable(_("Username"), 'username', 'text', false);
         $this->addVariable(_("Password"), 'password', 'password', false);
 
@@ -37,33 +39,11 @@ class Kronolith_SubscribeRemoteCalendarForm extends Horde_Form {
 
     function execute()
     {
-        $name = trim($this->_vars->get('name'));
-        $url = trim($this->_vars->get('url'));
-        $username = trim($this->_vars->get('username'));
-        $password = trim($this->_vars->get('password'));
-
-        if (!(strlen($name) && strlen($url))) {
-            return false;
-        }
-
-        if (strlen($username) || strlen($password)) {
-            $key = Horde_Auth::getCredential('password');
-            if ($key) {
-                $username = base64_encode(Horde_Secret::write($key, $username));
-                $password = base64_encode(Horde_Secret::write($key, $password));
-            }
+        $info = array();
+        foreach (array('name', 'url', 'color', 'username', 'password') as $key) {
+            $info[$key] = $this->_vars->get($key);
         }
-
-        $remote_calendars = unserialize($GLOBALS['prefs']->getValue('remote_cals'));
-        $remote_calendars[] = array(
-            'name' => $name,
-            'url' => $url,
-            'user' => $username,
-            'password' => $password,
-        );
-
-        $GLOBALS['prefs']->setValue('remote_cals', serialize($remote_calendars));
-        return true;
+        return Kronolith::subscribeRemoteCalendar($info);
     }
 
 }
index 7e99042..8dc4878 100644 (file)
@@ -40,21 +40,7 @@ class Kronolith_UnsubscribeRemoteCalendarForm extends Horde_Form {
             return false;
         }
 
-        $url = trim($this->_vars->get('url'));
-        if (!strlen($url)) {
-            return false;
-        }
-
-        $remote_calendars = unserialize($GLOBALS['prefs']->getValue('remote_cals'));
-        foreach ($remote_calendars as $key => $calendar) {
-            if ($calendar['url'] == $url) {
-                unset($remote_calendars[$key]);
-                break;
-            }
-        }
-
-        $GLOBALS['prefs']->setValue('remote_cals', serialize($remote_calendars));
-        return true;
+        return Kronolit::unsubscribeRemoteCalendar($this->_vars->get('url'));
     }
 
 }
index 6c18474..5a35835 100644 (file)
@@ -126,6 +126,7 @@ class Kronolith
             'URI_IMG' => $registry->getImageDir() . '/',
             'URI_SNOOZE' => Horde::url($registry->get('webroot', 'horde') . '/services/snooze.php', true, -1),
             'SESSION_ID' => defined('SID') ? SID : '',
+            'user' => Horde_Auth::getAuth(),
             'prefs_url' => str_replace('&amp;', '&', Horde::getServiceLink('options', 'kronolith')),
             'name' => $registry->get('name'),
             'is_ie6' => ($browser->isBrowser('msie') && ($browser->getMajor() < 7)),
@@ -169,6 +170,7 @@ class Kronolith
                     $code['conf']['calendars']['internal'][$id] = array(
                         'name' => ($owner ? '' : '[' . Horde_Auth::convertUsername($calendar->get('owner'), false) . '] ')
                             . $calendar->get('name'),
+                        'desc' => $calendar->get('desc'),
                         'owner' => $owner,
                         'fg' => self::foregroundColor($calendar),
                         'bg' => self::backgroundColor($calendar),
@@ -187,6 +189,7 @@ class Kronolith
                     $code['conf']['calendars']['tasklists']['tasks/' . $id] = array(
                         'name' => ($owner ? '' : '[' . Horde_Auth::convertUsername($tasklist->get('owner'), false) . '] ')
                             . $tasklist->get('name'),
+                        'desc' => $tasklist->get('desc'),
                         'owner' => $owner,
                         'fg' => self::foregroundColor($tasklist),
                         'bg' => self::backgroundColor($tasklist),
@@ -214,11 +217,15 @@ class Kronolith
 
         // Remote calendars
         foreach ($GLOBALS['all_remote_calendars'] as $calendar) {
-            $code['conf']['calendars']['remote'][$calendar['url']] = array(
-                'name' => $calendar['name'],
-                'fg' => self::foregroundColor($calendar),
-                'bg' => self::backgroundColor($calendar),
-                'show' => in_array($calendar['url'], $GLOBALS['display_remote_calendars']));
+
+            $code['conf']['calendars']['remote'][$calendar['url']] = array_merge(
+                array('name' => $calendar['name'],
+                      'desc' => isset($calendar['desc']) ? $calendar['desc'] : '',
+                      'owner' => true,
+                      'fg' => self::foregroundColor($calendar),
+                      'bg' => self::backgroundColor($calendar),
+                      'show' => in_array($calendar['url'], $GLOBALS['display_remote_calendars'])),
+                self::getRemoteParams($calendar['url']));
         }
 
         // Holidays
@@ -243,6 +250,8 @@ class Kronolith
             'searching' => str_replace('%s', '#{term}', _("Events matching \"%s\"")),
             'allday' => _("All day"),
             'prefs' => _("Options"),
+            'no_url' => _("You must specify a URL."),
+            'wrong_auth' => _("The authentication information you specified wasn't accepted."),
         );
         for ($i = 1; $i <= 12; ++$i) {
             $code['text']['month'][$i - 1] = Horde_Nls::getLangInfo(constant('MON_' . $i));
@@ -1290,6 +1299,158 @@ class Kronolith
     }
 
     /**
+     * Creates a new share.
+     *
+     * @param array $info  Hash with calendar information.
+     *
+     * @return Horde_Share  The new share.
+     */
+    public static function addShare($info)
+    {
+        $calendar = $GLOBALS['kronolith_shares']->newShare(hash('md5', microtime()));
+        if (is_a($calendar, 'PEAR_Error')) {
+            return $calendar;
+        }
+
+        $calendar->set('name', $info['name']);
+        $calendar->set('color', $info['color']);
+        $calendar->set('desc', $info['description']);
+        $tagger = self::getTagger();
+        $tagger->tag($calendar->getName(), $info['tags'], 'calendar');
+
+        $result = $GLOBALS['kronolith_shares']->addShare($calendar);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        $GLOBALS['display_calendars'][] = $calendar->getName();
+        $GLOBALS['prefs']->setValue('display_cals', serialize($GLOBALS['display_calendars']));
+
+        return $calendar;
+    }
+
+    /**
+     * Updates an existing share.
+     *
+     * @param Horde_Share $share  The share to update.
+     * @param array $info         Hash with calendar information.
+     */
+    public static function updateShare(&$calendar, $info)
+    {
+        if ($calendar->get('owner') != Horde_Auth::getAuth()) {
+            return PEAR::raiseError(_("You are not allowed to change this calendar."));
+        }
+
+        $original_name = $calendar->get('name');
+        $calendar->set('name', $info['name']);
+        $calendar->set('color', $info['color']);
+        $calendar->set('desc', $info['description']);
+        if ($original_name != $info['name']) {
+            $result = Kronolith::getDriver()->rename($original_name, $info['name']);
+            if (is_a($result, 'PEAR_Error')) {
+                return PEAR::raiseError(sprintf(_("Unable to rename \"%s\": %s"), $original_name, $result->getMessage()));
+            }
+        }
+
+        $result = $calendar->save();
+        if (is_a($result, 'PEAR_Error')) {
+            return PEAR::raiseError(sprintf(_("Unable to save calendar \"%s\": %s"), $info['name'], $result->getMessage()));
+        }
+
+        $tagger = self::getTagger();
+        $tagger->replaceTags($calendar->getName(), $info['tags'], 'calendar');
+    }
+
+    /**
+     * Deletes a share.
+     *
+     * @param Horde_Share $calendar  The share to delete.
+     */
+    public static function deleteShare($calendar)
+    {
+        if ($calendar->getName() == Horde_Auth::getAuth()) {
+            return PEAR::raiseError(_("This calendar cannot be deleted."));
+        }
+
+        if ($calendar->get('owner') != Horde_Auth::getAuth()) {
+            return PEAR::raiseError(_("You are not allowed to delete this calendar."));
+        }
+
+        // Delete the calendar.
+        $result = Kronolith::getDriver()->delete($calendar->getName());
+        if (is_a($result, 'PEAR_Error')) {
+            return PEAR::raiseError(sprintf(_("Unable to delete \"%s\": %s"), $calendar->get('name'), $result->getMessage()));
+        }
+
+        // Remove share and all groups/permissions.
+        return $GLOBALS['kronolith_shares']->removeShare($calendar);
+    }
+
+    /**
+     * Subscribes to a remote calendar.
+     *
+     * @param array $info  Hash with calendar information.
+     */
+    public static function subscribeRemoteCalendar($info)
+    {
+        if (!(strlen($info['name']) && strlen($info['url']))) {
+            return PEAR::raiseError(_("You must specify a name and a URL."));
+        }
+
+        if (strlen($info['username']) || strlen($info['password'])) {
+            $key = Horde_Auth::getCredential('password');
+            if ($key) {
+                $info['username'] = base64_encode(Horde_Secret::write($key, $info['username']));
+                $info['password'] = base64_encode(Horde_Secret::write($key, $info['password']));
+            }
+        }
+
+        $remote_calendars = unserialize($GLOBALS['prefs']->getValue('remote_cals'));
+        $remote_calendars[] = array(
+            'name' => $info['name'],
+            'desc' => $info['description'],
+            'url' => $info['url'],
+            'color' => $info['color'],
+            'user' => $info['username'],
+            'password' => $info['password'],
+        );
+
+        $GLOBALS['prefs']->setValue('remote_cals', serialize($remote_calendars));
+    }
+
+    /**
+     * Unsubscribes from a remote calendar.
+     *
+     * @param string $url  The calendar URL.
+     *
+     * @return array  Hash with the deleted calendar's information.
+     */
+    public static function unsubscribeRemoteCalendar($url)
+    {
+        $url = trim($url);
+        if (!strlen($url)) {
+            return false;
+        }
+
+        $remote_calendars = unserialize($GLOBALS['prefs']->getValue('remote_cals'));
+        $remote_calendar = null;
+        foreach ($remote_calendars as $key => $calendar) {
+            if ($calendar['url'] == $url) {
+                $remote_calendar = $calendar;
+                unset($remote_calendars[$key]);
+                break;
+            }
+        }
+        if (!$remote_calendar) {
+            return PEAR::raiseError(_("The remote calendar was not found."));
+        }
+
+        $GLOBALS['prefs']->setValue('remote_cals', serialize($remote_calendars));
+
+        return $remote_calendar;
+    }
+
+    /**
      * Returns the feed URL for a calendar.
      *
      * @param string $calendar  A calendar name.
@@ -1918,11 +2079,11 @@ class Kronolith
                 break;
 
             case 'Ical':
+                $params = self::getRemoteParams($calendar);
                 /* Check for HTTP proxy configuration */
                 if (!empty($GLOBALS['conf']['http']['proxy']['proxy_host'])) {
                     $params['proxy'] = $GLOBALS['conf']['http']['proxy'];
                 }
-                $params = self::getRemoteParams($calendar);
                 break;
 
             case 'Horde':
@@ -1969,6 +2130,7 @@ class Kronolith
                 if (!empty($user)) {
                     return array('user' => $user, 'password' => $password);
                 }
+                return array();
             }
         }
 
diff --git a/kronolith/templates/chunks/calendar.php b/kronolith/templates/chunks/calendar.php
new file mode 100644 (file)
index 0000000..df4c291
--- /dev/null
@@ -0,0 +1,161 @@
+<div id="kronolithCalendarDialog" class="kronolithDialog">
+
+<form id="kronolithCalendarForminternal" action="">
+<input id="kronolithCalendarType" type="hidden" name="type" value="internal" />
+<input id="kronolithCalendarinternalId" type="hidden" name="calendar" />
+
+<div class="kronolithCalendarDiv" id="kronolithCalendarinternal1">
+<div>
+  <label><?php echo _("Name") ?>:<br />
+    <input type="text" name="name" id="kronolithCalendarinternalName" class="kronolithLongField" />
+  </label>
+</div>
+
+<div>
+  <label><?php echo _("Color") ?>:<br />
+    <input type="text" name="color" id="kronolithCalendarinternalColor" size="7" />
+  </label>
+</div>
+
+<div class="tabset">
+  <ul>
+    <li class="activeTab"><a href="#" class="kronolithTabLink" id="kronolithCalendarinternalLinkDescription"><?php echo _("Description") ?></a></li>
+    <li><a href="#" class="kronolithTabLink" id="kronolithCalendarinternalLinkTags"><?php echo _("Tags") ?></a></li>
+    <li><a href="#" class="kronolithTabLink" id="kronolithCalendarinternalLinkPerms"><?php echo _("Permissions") ?></a></li>
+  </ul>
+</div>
+<br class="clear" />
+
+<div id="kronolithCalendarinternalTabDescription" class="kronolithTabsOption">
+  <textarea name="description" id="kronolithCalendarinternalDescription" rows="5" cols="40" class="kronolithLongField"></textarea>
+</div>
+
+<div id="kronolithCalendarinternalTabTags" class="kronolithTabsOption kronolithTabTags" style="display:none">
+  <input id="kronolithCalendarinternalTags" name="tags" />
+  <span id="kronolithCalendarinternalTags_loading_img" style="display:none;"><?php echo Horde::img('loading.gif', _("Loading...")) ?></span>
+  <div class="kronolithTopTags" id="kronolithCalendarinternalTopTags"></div>
+</div>
+
+<div id="kronolithCalendarinternalTabPerms" class="kronolithTabsOption" style="display:none">
+tbd
+</div>
+
+<div class="kronolithFormActions">
+  <input type="button" value="<?php echo _("Save") ?>" class="kronolithCalendarSave button ok" />
+  <input type="button" value="<?php echo _("Delete") ?>" class="kronolithCalendarDelete button ko" />
+  <span class="kronolithSep"><?php echo _("or") ?></span> <a class="kronolithFormCancel"><?php echo _("Cancel") ?></a>
+</div>
+</div>
+
+</form>
+
+<form id="kronolithCalendarFormtasklists" action="">
+<input id="kronolithCalendarType" type="hidden" name="type" value="tasklists" />
+<input id="kronolithCalendartasklistsId" type="hidden" name="calendar" />
+
+<div class="kronolithCalendarDiv" id="kronolithCalendartasklists1">
+<div>
+  <label><?php echo _("Name") ?>:<br />
+    <input type="text" name="name" id="kronolithCalendartasklistsName" class="kronolithLongField" />
+  </label>
+</div>
+
+<div>
+  <label><?php echo _("Color") ?>:<br />
+    <input type="text" name="color" id="kronolithCalendartasklistsColor" size="7" />
+  </label>
+</div>
+
+<div class="tabset">
+  <ul>
+    <li class="activeTab"><a href="#" class="kronolithTabLink" id="kronolithCalendartasklistsLinkDescription"><?php echo _("Description") ?></a></li>
+    <li><a href="#" class="kronolithTabLink" id="kronolithCalendartasklistsLinkPerms"><?php echo _("Permissions") ?></a></li>
+  </ul>
+</div>
+<br class="clear" />
+
+<div id="kronolithCalendartasklistsTabDescription" class="kronolithTabsOption">
+  <textarea name="description" id="kronolithCalendartasklistsDescription" rows="5" cols="40" class="kronolithLongField"></textarea>
+</div>
+
+<div id="kronolithCalendartasklistsTabPerms" class="kronolithTabsOption" style="display:none">
+tbd
+</div>
+
+<div class="kronolithFormActions">
+  <input type="button" value="<?php echo _("Save") ?>" class="kronolithCalendarSave button ok" />
+  <input type="button" value="<?php echo _("Delete") ?>" class="kronolithCalendarDelete button ko" />
+  <span class="kronolithSep"><?php echo _("or") ?></span> <a class="kronolithFormCancel"><?php echo _("Cancel") ?></a>
+</div>
+</div>
+
+</form>
+
+<form id="kronolithCalendarFormremote" action="">
+<input id="kronolithCalendarType" type="hidden" name="type" value="remote" />
+<input id="kronolithCalendarremoteId" type="hidden" name="calendar" />
+
+<div class="kronolithCalendarDiv" id="kronolithCalendarremote1">
+<div>
+  <label><?php echo _("URL") ?>:<br />
+    <input type="text" name="url" id="kronolithCalendarremoteUrl" class="kronolithLongField" />
+  </label>
+</div>
+
+<div>
+  <label><?php echo _("Color") ?>:<br />
+    <input type="text" name="color" id="kronolithCalendarremoteColor" size="7" />
+  </label>
+</div>
+
+<div class="kronolithFormActions">
+  <input type="button" value="<?php echo _("Continue") ?>" class="kronolithCalendarContinue button ok" />
+  <input type="button" value="<?php echo _("Delete") ?>" class="kronolithCalendarDelete button ko" />
+  <span class="kronolithSep"><?php echo _("or") ?></span> <a class="kronolithFormCancel"><?php echo _("Cancel") ?></a>
+</div>
+</div>
+
+<div class="kronolithCalendarDiv" id="kronolithCalendarremote2">
+<div><?php echo _("This calendar requires to specify a user name and password.") ?></div>
+
+<div>
+  <label><?php echo _("Username") ?>:<br />
+    <input type="text" name="username" id="kronolithCalendarremoteUsername" class="kronolithLongField" />
+  </label>
+</div>
+
+<div>
+  <label><?php echo _("Password") ?>:<br />
+    <input type="password" name="password" id="kronolithCalendarremotePassword" class="kronolithLongField" />
+  </label>
+</div>
+
+<div class="kronolithFormActions">
+  <input type="button" value="<?php echo _("Continue") ?>" class="kronolithCalendarContinue button ok" />
+  <span class="kronolithSep"><?php echo _("or") ?></span> <a class="kronolithFormCancel"><?php echo _("Cancel") ?></a>
+</div>
+</div>
+
+<div class="kronolithCalendarDiv" id="kronolithCalendarremote3">
+<div>
+  <label><?php echo _("Name") ?>:<br />
+    <input type="text" name="name" id="kronolithCalendarremoteName" class="kronolithLongField" />
+  </label>
+</div>
+
+<div>
+  <label><?php echo _("Description") ?>:<br />
+    <textarea name="description" id="kronolithCalendarremoteDescription" rows="5" cols="40" class="kronolithLongField"></textarea>
+  </label>
+</div>
+
+<div class="kronolithFormActions">
+  <input type="button" value="<?php echo _("Save") ?>" class="kronolithCalendarSave button ok" />
+  <input type="button" value="<?php echo _("Delete") ?>" class="kronolithCalendarDelete button ko" />
+  <span class="kronolithSep"><?php echo _("or") ?></span> <a class="kronolithFormCancel"><?php echo _("Cancel") ?></a>
+</div>
+</div>
+
+</form>
+
+</div>
index c8dd038..9ce11f7 100644 (file)
@@ -1,4 +1,4 @@
-<div id="kronolithEventDialog" style="display:none">
+<div id="kronolithEventDialog" class="kronolithDialog" style="display:none">
 <form id="kronolithEventForm" action="">
 <input id="kronolithEventId" type="hidden" name="id" />
 <input id="kronolithEventCalendar" type="hidden" name="cal" />
 
 <div class="tabset">
   <ul>
-    <li class="activeTab"><a href="#" id="kronolithEventLinkDescription"><?php echo _("Description") ?></a></li>
-    <li><a href="#" id="kronolithEventLinkReminder"><?php echo _("Reminder") ?></a></li>
-    <li><a href="#" id="kronolithEventLinkRecur"><?php echo _("Repeat") ?></a></li>
-    <li><a href="#" id="kronolithEventLinkUrl"><?php echo _("URL") ?></a></li>
-    <li><a href="#" id="kronolithEventLinkAttendees"><?php echo _("Attendees") ?></a></li>
-    <li><a href="#" id="kronolithEventLinkTags"><?php echo _("Tags and Keywords") ?></a></li>
+    <li class="activeTab"><a href="#" class="kronolithTabLink" id="kronolithEventLinkDescription"><?php echo _("Description") ?></a></li>
+    <li><a href="#" class="kronolithTabLink" id="kronolithEventLinkReminder"><?php echo _("Reminder") ?></a></li>
+    <li><a href="#" class="kronolithTabLink" id="kronolithEventLinkRecur"><?php echo _("Repeat") ?></a></li>
+    <li><a href="#" class="kronolithTabLink" id="kronolithEventLinkUrl"><?php echo _("URL") ?></a></li>
+    <li><a href="#" class="kronolithTabLink" id="kronolithEventLinkAttendees"><?php echo _("Attendees") ?></a></li>
+     <li><a href="#" class="kronolithTabLink" id="kronolithEventLinkTags"><?php echo _("Tags") ?></a></li>
   </ul>
 </div>
 <br class="clear" />
   </table>
 </div>
 
-<div id="kronolithEventTabTags" class="kronolithTabsOption" style="display:none">
+<div id="kronolithEventTabTags" class="kronolithTabsOption kronolithTabTags" style="display:none">
   <input id="kronolithEventTags" name="tags" />
   <span id="kronolithEventTags_loading_img" style="display:none;"><?php echo Horde::img('loading.gif', _("Loading...")) ?></span>
-  <div id="kronolithEventTopTags"></div>
+  <div class="kronolithTopTags" id="kronolithEventTopTags"></div>
 </div>
 
-<div id="kronolithEventActions">
+<div class="kronolithFormActions">
   <input id="kronolithEventSave" type="button" value="<?php echo _("Save") ?>" class="button ok" />
   <input id="kronolithEventDelete" type="button" value="<?php echo _("Delete") ?>" class="button ko" />
-  <span class="kronolithSep"><?php echo _("or") ?></span> <a id="kronolithEventCancel" class="kronolithFormCancel"><?php echo _("Cancel") ?></a>
+  <span class="kronolithSep"><?php echo _("or") ?></span> <a class="kronolithFormCancel"><?php echo _("Cancel") ?></a>
 </div>
 
 </form>
index 952a2ad..9489512 100644 (file)
@@ -74,7 +74,9 @@
 
   <div id="kronolithMenuCalendars">
   <h3 id="kronolithCalendarsFirst">
-    <a href="#" class="kronolithAdd">+</a>
+    <?php if (Horde_Auth::getAuth() && !$prefs->isLocked('default_share')): ?>
+    <a href="#" id="kronolithAddinternal" class="kronolithAdd">+</a>
+    <?php endif; ?>
     <span><?php echo _("My Calendars") ?></span>
   </h3>
 
@@ -82,7 +84,9 @@
   </div>
 
   <h3>
-    <a href="#" class="kronolithAdd">+</a>
+    <?php if (Horde_Auth::getAuth() && !$prefs->isLocked('default_share')): ?>
+    <a href="#" id="kronolithAddtasklists" class="kronolithAdd">+</a>
+    <?php endif; ?>
     <span><?php echo _("My Task Lists") ?></span>
   </h3>
 
@@ -90,7 +94,7 @@
   </div>
 
   <h3>
-    <a href="#" class="kronolithAdd">+</a>
+    <!-- to be added when searching for shared calendars is implemented <a href="#" id="kronolithAddinternalshared" class="kronolithAdd">+</a>-->
     <span><?php echo _("Shared Calendars") ?></span>
   </h3>
 
   </div>
 
   <h3>
-    <a href="#" class="kronolithAdd">+</a>
+    <!-- to be added when searching for shared calendars is implemented <a href="#" id="kronolithAddtasklistsshared" class="kronolithAdd">+</a>-->
     <span><?php echo _("Shared Task Lists") ?></span>
   </h3>
 
   <div id="kronolithExternalCalendars"></div>
 
   <h3>
-    <a href="#" class="kronolithAdd">+</a>
+    <a href="#" id="kronolithAddremote" class="kronolithAdd">+</a>
     <span><?php echo _("Remote Calendars") ?></span>
   </h3>
 
index 2a45c22..861a2a1 100644 (file)
@@ -1,4 +1,4 @@
-<div id="kronolithTaskDialog" style="display:none">
+<div id="kronolithTaskDialog" class="kronolithDialog" style="display:none">
 <form id="kronolithTaskForm" action="">
 <input id="kronolithTaskId" type="hidden" name="task_id" />
 <input id="kronolithTaskOldList" type="hidden" name="old_tasklist" />
@@ -12,7 +12,7 @@
   <label><input type="checkbox" name="task[completed]" id="kronolithTaskCompleted" value="1" /> <?php echo _("completed") ?></label>
 </div>
 
-<table cellspacing="" cellpadding="0" border="0"><tbody><tr>
+<table cellspacing="0" cellpadding="0" border="0"><tbody><tr>
   <td>
     <div>
       <label for="kronolithTaskPriority"><?php echo _("Priority") ?>:</label><br />
@@ -42,9 +42,9 @@
 
 <div class="tabset">
   <ul>
-    <li class="activeTab"><a href="#" id="kronolithTaskLinkDescription"><?php echo _("Description") ?></a></li>
-    <li><a href="#" id="kronolithTaskLinkReminder"><?php echo _("Reminder") ?></a></li>
-    <li><a href="#" id="kronolithTaskLinkUrl"><?php echo _("URL") ?></a></li>
+    <li class="activeTab"><a href="#" class="kronolithTabLink" id="kronolithTaskLinkDescription"><?php echo _("Description") ?></a></li>
+    <li><a href="#" class="kronolithTabLink" id="kronolithTaskLinkReminder"><?php echo _("Reminder") ?></a></li>
+    <li><a href="#" class="kronolithTabLink" id="kronolithTaskLinkUrl"><?php echo _("URL") ?></a></li>
   </ul>
 </div>
 <br class="clear" />
   <input type="text" name="task[url]" id="taskUrl" class="kronolithLongField" value="http://" />
 </div>
 
-<div id="kronolithTaskActions">
+<div class="kronolithFormActions">
   <input id="kronolithTaskSave" type="button" value="<?php echo _("Save") ?>" class="button ok" />
   <input id="kronolithTaskDelete" type="button" value="<?php echo _("Delete") ?>" class="button ko" />
-  <span class="kronolithSep"><?php echo _("or") ?></span> <a id="kronolithTaskCancel" class="kronolithFormCancel"><?php echo _("Cancel") ?></a>
+  <span class="kronolithSep"><?php echo _("or") ?></span> <a class="kronolithFormCancel"><?php echo _("Cancel") ?></a>
 </div>
 
 </form>
index 549e583..99be717 100644 (file)
@@ -632,23 +632,23 @@ span#kronolithQuickEvent {
 }
 
 /* Event/task form dialogs */
-#kronolithEventDialog, #kronolithTaskDialog {
+.kronolithDialog {
     width: 700px;
     padding: 10px 20px;
     background: #efefef;
     border: 1px #c0c0c0 solid;
 }
-#kronolithEventDialog div, #kronolithTaskDialog div {
+.kronolithDialog div {
     margin-bottom: 5px;
 }
-#kronolithEventDialog td, #kronolithTaskDialog td {
+.kronolithDialog td {
     padding-right: 10px;
     vertical-align: top;
 }
-#kronolithEventForm label, #kronolithTaskForm label {
+.kronolithDialog label {
     font-weight: bold;
 }
-#kronolithEventForm input.kronolithDatePicker, #kronolithTaskForm input.kronolithDatePicker {
+.kronolithDialog input.kronolithDatePicker {
     padding-right: 20px;
     background-image: url("graphics/picker.png");
     background-position: right center;
@@ -657,15 +657,9 @@ span#kronolithQuickEvent {
 .kronolithLongField {
     width: 100%;
 }
-div#kronolithEventActions, div#kronolithTaskActions {
+div.kronolithFormActions {
     margin: 5px 0;
 }
-div#kronolithEventActions #kronolithEventAdvancedLink {
-    padding-left: 2px;
-}
-div#kronolithEventActions #kronolithEventAdvancedLink a {
-    color: inherit;
-}
 
 #kronolithEventStartTime, #kronolithEventEndTime, #kronolithTaskDueTime, .kronolithEventValue {
     text-align: center;
@@ -673,12 +667,12 @@ div#kronolithEventActions #kronolithEventAdvancedLink a {
 #kronolithEventLocation {
     width: 300px;
 }
-div#kronolithEventTopTags {
+.kronolithTopTags {
     max-height: 75px;
     overflow: auto;
     line-height: 20px;
 }
-div#kronolithEventTopTags span {
+.kronolithTopTags span {
     padding: 2px 4px;
     margin-right: 4px;
     background-color: #fff;
@@ -687,21 +681,21 @@ div#kronolithEventTopTags span {
     -webkit-border-radius: 4px;
     font-size: 90%;
 }
-div#kronolithEventTopTags span:hover {
+.kronolithTopTags span:hover {
     background-color: #ffc;
     cursor: pointer;
 }
-div#kronolithEventTabTags {
-       line-height: 100%;
+.kronolithTabTags {
+    line-height: 100%;
 }
 
-#kronolithEventDialog .tabset, #kronolithTaskDialog .tabset {
+.kronolithDialog .tabset {
     float: none;
     width: auto;
     background: none;
     margin: 15px 0;
 }
-#kronolithEventDialog .tabset ul, #kronolithTaskDialog .tabset ul {
+.kronolithDialog .tabset ul {
     padding-left: 0;
     margin: 0;
 }
index dc5a235..eec230d 100644 (file)
@@ -2,6 +2,7 @@
 v3.0-git
 --------
 
+[jan] Set colors per task list (Request #7480).
 [cjh] Quick Add support: there is a javascript UI element for quickly adding
       tasks, and an API method (tasks/quickAdd) that provides the same
       functionality.
index 19c60af..ce61550 100644 (file)
@@ -92,38 +92,68 @@ class Nag_Api extends Horde_Registry_Api
     }
 
     /**
-     * Add a new task list
+     * Returns a list of task lists.
      *
-     * @param string $name        Task list name
-     * @param string $description Task list description
+     * @param boolean $owneronly   Only return tasklists that this user owns?
+     *                             Defaults to false.
+     * @param integer $permission  The permission to filter tasklists by.
+     *
+     * @return array  The task lists.
+     */
+    public function listTasklists($owneronly, $permission)
+    {
+        require_once dirname(__FILE__) . '/base.php';
+        return Nag::listTasklists($owneronly, $permission);
+    }
+
+    /**
+     * Adds a new task list.
+     *
+     * @param string $name        Task list name.
+     * @param string $description Task list description.
+     * @param string $color       Task list color.
      *
      * @return integer  The new tasklist's id.
      */
-    public function addTasklist($name, $description = '')
+    public function addTasklist($name, $description = '', $color = '')
     {
-        if (!Horde_Auth::getAuth()) {
-            return PEAR::raiseError(_("Permission denied"));
+        require_once dirname(__FILE__) . '/base.php';
+        $tasklist = Nag::addTasklist(array('name' => $name, 'description' => $description, 'color' => $color));
+        if (is_a($tasklist, 'PEAR_Error')) {
+            return $tasklist;
         }
+        return $tasklist->getName();
+    }
 
+    /**
+     * Updates an existing task list.
+     *
+     * @param string $id   A task list id.
+     * @param array $info  Hash with task list information.
+     */
+    public static function updateTasklist($id, $info)
+    {
         require_once dirname(__FILE__) . '/base.php';
-        global $nag_shares;
-
-        $tasklistId = md5(microtime());
-        $tasklist = $nag_shares->newShare($tasklistId);
-
+        $tasklist = $GLOBALS['nag_shares']->getShare($id);
         if (is_a($tasklist, 'PEAR_Error')) {
             return $tasklist;
         }
+        return Nag::updateTasklist($tasklist, $info);
+    }
 
-        $tasklist->set('name', $name, false);
-        $tasklist->set('desc', $description, false);
-        $result = $nag_shares->addShare($tasklist);
-
-        if (is_a($result, 'PEAR_Error')) {
-            return $result;
+    /**
+     * Deletes a task list.
+     *
+     * @param string $id  A task list id.
+     */
+    public function deleteTasklist($id)
+    {
+        require_once dirname(__FILE__) . '/base.php';
+        $tasklist = $GLOBALS['nag_shares']->getShare($id);
+        if (is_a($tasklist, 'PEAR_Error')) {
+            return $tasklist;
         }
-
-        return $tasklistId;
+        return Nag::deleteTasklist($tasklist);
     }
 
     /**
@@ -608,20 +638,6 @@ class Nag_Api extends Horde_Registry_Api
     }
 
     /**
-     * @param boolean $owneronly   Only return tasklists that this user owns?
-     *                             Defaults to false.
-     * @param integer $permission  The permission to filter tasklists by.
-     *
-     * @return array  The task lists.
-     */
-    public function listTasklists($owneronly, $permission)
-    {
-        require_once dirname(__FILE__) . '/base.php';
-
-        return Nag::listTasklists($owneronly, $permission);
-    }
-
-    /**
      * Returns an array of UIDs for all tasks that the current user is authorized
      * to see.
      *
@@ -1321,6 +1337,7 @@ class Nag_Api extends Horde_Registry_Api
                 'start' => $due_date,
                 'end' => $due_date,
                 'category' => $task->category,
+                'color' => $allowed_tasklists[$task->tasklist]->get('color'),
                 'params' => array('task' => $task->id,
                                   'tasklist' => $task->tasklist),
                 'link' => Horde_Util::addParameter(Horde::applicationUrl('view.php', true), array('tasklist' => $task->tasklist, 'task' => $task->id)),
index 7797636..f931199 100644 (file)
@@ -27,22 +27,20 @@ class Nag_CreateTaskListForm extends Horde_Form {
     {
         parent::Horde_Form($vars, _("Create Task List"));
 
-        $this->addVariable(_("Task List Name"), 'name', 'text', true);
-        $this->addVariable(_("Task List Description"), 'description', 'longtext', false, false, null, array(4, 60));
+        $this->addVariable(_("Name"), 'name', 'text', true);
+        $this->addVariable(_("Color"), 'color', 'colorpicker', false);
+        $this->addVariable(_("Description"), 'description', 'longtext', false, false, null, array(4, 60));
 
         $this->setButtons(array(_("Create")));
     }
 
     function execute()
     {
-        // Create new share.
-        $tasklist = $GLOBALS['nag_shares']->newShare(md5(microtime()));
-        if (is_a($tasklist, 'PEAR_Error')) {
-            return $tasklist;
+        $info = array();
+        foreach (array('name', 'color', 'description') as $key) {
+            $info[$key] = $this->_vars->get($key);
         }
-        $tasklist->set('name', $this->_vars->get('name'));
-        $tasklist->set('desc', $this->_vars->get('description'));
-        return $GLOBALS['nag_shares']->addShare($tasklist);
+        return Nag::addTasklist($info);
     }
 
 }
index 5b0352b..ee08674 100644 (file)
@@ -46,42 +46,7 @@ class Nag_DeleteTaskListForm extends Horde_Form {
             return false;
         }
 
-        if ($this->_tasklist->get('owner') != Horde_Auth::getAuth()) {
-            return PEAR::raiseError(_("Permission denied"));
-        }
-
-        // Delete the task list.
-        $storage = &Nag_Driver::singleton($this->_tasklist->getName());
-        $result = $storage->deleteAll();
-        if (is_a($result, 'PEAR_Error')) {
-            return PEAR::raiseError(sprintf(_("Unable to delete \"%s\": %s"), $this->_tasklist->get('name'), $result->getMessage()));
-        } else {
-            // Remove share and all groups/permissions.
-            $result = $GLOBALS['nag_shares']->removeShare($this->_tasklist);
-            if (is_a($result, 'PEAR_Error')) {
-                return $result;
-            }
-        }
-
-        // Make sure we still own at least one task list.
-        if (count(Nag::listTasklists(true)) == 0) {
-            // If the default share doesn't exist then create it.
-            if (!$GLOBALS['nag_shares']->exists(Horde_Auth::getAuth())) {
-                $identity = Horde_Prefs_Identity::singleton();
-                $name = $identity->getValue('fullname');
-                if (trim($name) == '') {
-                    $name = Horde_Auth::getOriginalAuth();
-                }
-                $tasklist = &$GLOBALS['nag_shares']->newShare(Horde_Auth::getAuth());
-                if (is_a($tasklist, 'PEAR_Error')) {
-                    return;
-                }
-                $tasklist->set('name', sprintf(_("%s's Task List"), $name));
-                $GLOBALS['nag_shares']->addShare($tasklist);
-            }
-        }
-
-        return true;
+        return Nag::deleteTasklist($this->_tasklist);
     }
 
 }
index cde7c55..e69e899 100644 (file)
@@ -42,13 +42,11 @@ class Nag_EditTaskListForm extends Horde_Form {
 
     function execute()
     {
-        $this->_tasklist->set('name', $this->_vars->get('name'));
-        $this->_tasklist->set('desc', $this->_vars->get('description'));
-        $result = $this->_tasklist->save();
-        if (is_a($result, 'PEAR_Error')) {
-            return PEAR::raiseError(sprintf(_("Unable to save task list \"%s\": %s"), $id, $result->getMessage()));
+        $info = array();
+        foreach (array('name', 'color', 'description') as $key) {
+            $info[$key] = $this->_vars->get($key);
         }
-        return true;
+        return Nag::updateTasklist($this->_tasklist, $info);
     }
 
 }
index 813c2ac..0c35789 100644 (file)
@@ -424,6 +424,81 @@ class Nag
     }
 
     /**
+     * Creates a new share.
+     *
+     * @param array $info  Hash with calendar information.
+     *
+     * @return Horde_Share  The new share.
+     */
+    function addTasklist($info)
+    {
+        $tasklist = $GLOBALS['nag_shares']->newShare(md5(microtime()));
+        if (is_a($tasklist, 'PEAR_Error')) {
+            return $tasklist;
+        }
+        $tasklist->set('name', $info['name']);
+        $tasklist->set('color', $info['color']);
+        $tasklist->set('desc', $info['description']);
+
+        $result = $GLOBALS['nag_shares']->addShare($tasklist);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        $GLOBALS['display_tasklists'][] = $tasklist->getName();
+        $GLOBALS['prefs']->setValue('display_tasklists', serialize($GLOBALS['display_tasklists']));
+
+        return $tasklist;
+    }
+
+    /**
+     * Updates an existing share.
+     *
+     * @param Horde_Share $share  The share to update.
+     * @param array $info         Hash with task list information.
+     */
+    public static function updateTasklist(&$tasklist, $info)
+    {
+        if ($tasklist->get('owner') != Horde_Auth::getAuth()) {
+            return PEAR::raiseError(_("You are not allowed to change this task list."));
+        }
+
+        $tasklist->set('name', $info['name']);
+        $tasklist->set('color', $info['color']);
+        $tasklist->set('desc', $info['description']);
+        $result = $tasklist->save();
+        if (is_a($result, 'PEAR_Error')) {
+            return PEAR::raiseError(sprintf(_("Unable to save task list \"%s\": %s"), $info['name'], $result->getMessage()));
+        }
+    }
+
+    /**
+     * Deletes a task list.
+     *
+     * @param Horde_Share $tasklist  The task list to delete.
+     */
+    public static function deleteTasklist($tasklist)
+    {
+        if ($tasklist->getName() == Horde_Auth::getAuth()) {
+            return PEAR::raiseError(_("This task list cannot be deleted."));
+        }
+
+        if ($tasklist->get('owner') != Horde_Auth::getAuth()) {
+            return PEAR::raiseError(_("You are not allowed to delete this task list."));
+        }
+
+        // Delete the task list.
+        $storage = &Nag_Driver::singleton($tasklist->getName());
+        $result = $storage->deleteAll();
+        if (is_a($result, 'PEAR_Error')) {
+            return PEAR::raiseError(sprintf(_("Unable to delete \"%s\": %s"), $tasklist->get('name'), $result->getMessage()));
+        }
+
+        // Remove share and all groups/permissions.
+        return $GLOBALS['nag_shares']->removeShare($tasklist);
+    }
+
+    /**
      * Builds the HTML for a priority selection widget.
      *
      * @param string $name       The name of the widget.
index ec78402..65f7dc4 100644 (file)
@@ -746,7 +746,8 @@ class Nag_Task {
             $methods['notify']['show'] = array(
                 '__app' => $GLOBALS['registry']->getApp(),
                 'task' => $this->id,
-                'tasklist' => $this->tasklist);
+                'tasklist' => $this->tasklist,
+                'ajax' => 'task:' . $this->tasklist . ':' . $this->id);
             if (!empty($methods['notify']['sound'])) {
                 if ($methods['notify']['sound'] == 'on') {
                     // Handle boolean sound preferences;
index d3d006b..1168801 100644 (file)
@@ -35,6 +35,7 @@ CREATE TABLE nag_shares (
     perm_guest SMALLINT DEFAULT 0 NOT NULL,
     attribute_name VARCHAR(255) NOT NULL,
     attribute_desc VARCHAR(255),
+    attribute_color VARCHAR(7),
     PRIMARY KEY (share_id)
 );
 
index 96ae114..05c77d4 100644 (file)
@@ -35,6 +35,7 @@ CREATE TABLE nag_shares (
     perm_guest NUMBER(8) DEFAULT 0 NOT NULL,
     attribute_name VARCHAR2(255) NOT NULL,
     attribute_desc VARCHAR2(255),
+    attribute_color VARCHAR2(7),
     PRIMARY KEY (share_id)
 );
 
index cd7520a..1bb7d1a 100644 (file)
@@ -35,6 +35,7 @@ CREATE TABLE nag_shares (
     perm_guest SMALLINT DEFAULT 0 NOT NULL,
     attribute_name VARCHAR(255) NOT NULL,
     attribute_desc VARCHAR(255),
+    attribute_color VARCHAR(7),
     PRIMARY KEY (share_id)
 );
 
index 3a6fe47..99dd3dd 100644 (file)
     <length>255</length>
    </field>
 
+   <field>
+    <name>attribute_color</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>7</length>
+   </field>
+
    <index>
     <name>nag_shares_name</name>
     <field>
diff --git a/nag/scripts/upgrades/2009-11-28_add_attribute_color.sql b/nag/scripts/upgrades/2009-11-28_add_attribute_color.sql
new file mode 100644 (file)
index 0000000..e0eda61
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE nag_shares ADD attribute_color VARCHAR(7);
index 29412f1..9345e1c 100644 (file)
@@ -16,24 +16,12 @@ if (!Horde_Auth::getAuth()) {
 }
 
 $vars = Horde_Variables::getDefaultVariables();
-$tasklist_id = $vars->get('t');
-if ($tasklist_id == Horde_Auth::getAuth()) {
-    $notification->push(_("This task list cannot be deleted."), 'horde.warning');
-    header('Location: ' . Horde::applicationUrl('tasklists/', true));
-    exit;
-}
-
-$tasklist = $nag_shares->getShare($tasklist_id);
+$tasklist = $nag_shares->getShare($vars->get('t'));
 if (is_a($tasklist, 'PEAR_Error')) {
     $notification->push($tasklist, 'horde.error');
     header('Location: ' . Horde::applicationUrl('tasklists/', true));
     exit;
-} elseif ($tasklist->get('owner') != Horde_Auth::getAuth()) {
-    $notification->push(_("You are not allowed to delete this task list."), 'horde.error');
-    header('Location: ' . Horde::applicationUrl('tasklists/', true));
-    exit;
 }
-
 $form = new Nag_DeleteTaskListForm($vars, $tasklist);
 
 // Execute if the form is valid (must pass with POST variables only).
index e2b23f5..72cdc75 100644 (file)
@@ -21,10 +21,6 @@ if (is_a($tasklist, 'PEAR_Error')) {
     $notification->push($tasklist, 'horde.error');
     header('Location: ' . Horde::applicationUrl('tasklists/', true));
     exit;
-} elseif ($tasklist->get('owner') != Horde_Auth::getAuth()) {
-    $notification->push(_("You are not allowed to change this task list."), 'horde.error');
-    header('Location: ' . Horde::applicationUrl('tasklists/', true));
-    exit;
 }
 $form = new Nag_EditTaskListForm($vars, $tasklist);