merge of '785de8feba6c3e190c55defa1a5e18e85c9b82ba'

and '9fc56cd3ca7051c4b29662926289d2cb181bc57a'

Monotone-Parent: 785de8feba6c3e190c55defa1a5e18e85c9b82ba
Monotone-Parent: 9fc56cd3ca7051c4b29662926289d2cb181bc57a
Monotone-Revision: 33545be02b741723c3eb86718dc21f534032fc81

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2012-04-18T20:09:02
Monotone-Branch: ca.inverse.sogo
This commit is contained in:
Wolfgang Sourdeau
2012-04-18 20:09:02 +00:00
30 changed files with 618 additions and 166 deletions
+50
View File
@@ -1,3 +1,53 @@
2012-04-18 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/WebServerResources/generic.js (showAuthenticationDialog): new
dialog providing an interface for requesting a username and a
password to acallback passed as parameter.
(accessToSubscribedFolder): fixed method to return the proper url
for folders owned by the active user.
* UI/WebServerResources/SchedulerUI.js (reloadWebCalendars): now
invokes "reloadAction" on every web calendar foldar, in a chain of
simultaneous requests.
(reloadWebCalendar): new function invoking "reloadAction" on a
single cal folder.
(reloadWebCalendarCallback): callback for the above invocation
that takes care of refreshing the views at the end of a refresh
chain, if present, and when useful.
* UI/Scheduler/UIxCalFolderActions.m (reloadAction): new web
method that applies only to SOGoWebAppointmentFolder instances and
which returns a JSON representation of the result returned by
-[SOGoWebAppointmentFolder loadWebCalendar]. This method replaces
-[UIxCalMainActions -reloadWebCalendars].
(setCredentialsAction): new web method acting as a proxy for
-[SOGoWebAppointmentFolder setUsername:andPassword:].
* UI/WebServerResources/generic.js (clickEventWrapper): new
function that returns a wrapper function for click callbacks which
invokes "preventDefault" on the "event" parameter before it is
passed to the real callback.
* UI/Scheduler/UIxCalMainActions.m
(-reloadWebCalendarsAndRedirectAction): removed obsolete method.
(-reloadWebCalendars): removed obsolete method.
* SoObjects/Appointments/SOGoAppointmentFolders.m
(-newWebCalendarWithName:atURL:): perform sanity checks on the url
parameter.
* SoObjects/Appointments/SOGoWebAppointmentFolder.m
(-setUsername:andPassword:): new method that associate an
encrypted login pair for authenticating to remove calendars.
(-loadWebCalendar): rewrote method to make use of curl, thereby
permitting HTTP authentication. A NSDictionary containing
different informations about the connection is not returned,
including the HTTP status and an error tag, when required.
* SoObjects/Appointments/SOGoAppointmentFolder.m
(-importCalendar:): journals and freebusys are not
supported.
2012-04-16 Jean Raby <jraby@inverse.ca>
* SOPE/NGCards/iCalMonthlyRecurrenceCalculator.m
@@ -2746,8 +2746,8 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
components = [[calendar events] mutableCopy];
[components autorelease];
[components addObjectsFromArray: [calendar todos]];
[components addObjectsFromArray: [calendar journals]];
[components addObjectsFromArray: [calendar freeBusys]];
// [components addObjectsFromArray: [calendar journals]];
// [components addObjectsFromArray: [calendar freeBusys]];
count = [components count];
for (i = 0; i < count; i++)
{
+25 -13
View File
@@ -25,6 +25,7 @@
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h>
#import <Foundation/NSURL.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/WORequest+So.h>
@@ -133,25 +134,35 @@ static SoSecurityManager *sm = nil;
- (SOGoWebAppointmentFolder *)
newWebCalendarWithName: (NSString *) folderDisplayName
atURL: (NSString *) url
atURL: (NSString *) urlString
{
NSException *error;
SOGoAppointmentFolder *aptFolder;
SOGoWebAppointmentFolder *webCalendar;
NSString *name;
NSURL *url;
if ([self newFolderWithName: folderDisplayName
nameInContainer: &name])
webCalendar = nil;
else
webCalendar = nil;
if ([folderDisplayName length] > 0 && [urlString length] > 0)
{
aptFolder = [subFolders objectForKey: name];
[aptFolder setFolderPropertyValue: url
inCategory: @"WebCalendars"];
webCalendar = [SOGoWebAppointmentFolder objectWithName: name
inContainer: self];
[webCalendar setOCSPath: [aptFolder ocsPath]];
[subFolders setObject: webCalendar forKey: name];
url = [NSURL URLWithString: urlString];
if ([[url scheme] hasPrefix: @"http"])
{
error = [self newFolderWithName: folderDisplayName
nameInContainer: &name];
if (!error)
{
aptFolder = [subFolders objectForKey: name];
[aptFolder setFolderPropertyValue: urlString
inCategory: @"WebCalendars"];
webCalendar = [SOGoWebAppointmentFolder objectWithName: name
inContainer: self];
[webCalendar setOCSPath: [aptFolder ocsPath]];
[subFolders setObject: webCalendar forKey: name];
}
}
}
return webCalendar;
@@ -542,6 +553,7 @@ static SoSecurityManager *sm = nil;
calSettings = [us objectForKey: @"Calendar"];
refs = [[calSettings objectForKey: @"WebCalendars"] allKeys];
max = [refs count];
for (count = 0; count < max; count++)
{
ref = [refs objectAtIndex: count];
@@ -27,7 +27,10 @@
@interface SOGoWebAppointmentFolder : SOGoAppointmentFolder
- (int) loadWebCalendar;
- (void) setUsername: (NSString *) username
andPassword: (NSString *) password;
- (NSDictionary *) loadWebCalendar;
- (void) deleteAllContent;
- (void) setReloadOnLogin: (BOOL) newReloadOnLogin;
+159 -33
View File
@@ -21,12 +21,26 @@
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/Foundation.h>
#import <curl/curl.h>
#import <Foundation/NSData.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSURL.h>
#import <Foundation/NSValue.h>
#import <NGObjWeb/WOHTTPConnection.h>
#import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGHttp/NGHttpResponse.h>
#import <NGCards/iCalCalendar.h>
#import <GDLContentStore/GCSFolder.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <SoObjects/SOGo/SOGoUser.h>
#import <SoObjects/SOGo/SOGoUserSettings.h>
#import <SOGo/SOGoAuthenticator.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import "SOGoWebAppointmentFolder.h"
@@ -39,45 +53,154 @@
[[self ocsFolder] deleteAllContent];
}
- (int) loadWebCalendar
- (NSDictionary *) _loadAuthData
{
NSString *location, *contents;
WOHTTPURLHandle *handle;
iCalCalendar *calendar;
NSData *data;
NSDictionary *authData;
NSString *authValue, *userPassword;
NSArray *parts, *keys;
userPassword = [[self authenticatorInContext: context]
passwordInContext: context];
if ([userPassword length] == 0)
{
authData = nil;
}
else
{
authValue
= [[self folderPropertyValueInCategory: @"WebCalendarsAuthentication"]
decryptWithKey: userPassword];
parts = [authValue componentsSeparatedByString: @":"];
if ([parts count] == 2)
{
keys = [NSArray arrayWithObjects: @"username", @"password", nil];
authData = [NSDictionary dictionaryWithObjects: parts
forKeys: keys];
}
else
authData = nil;
}
return authData;
}
- (void) setUsername: (NSString *) username
andPassword: (NSString *) password
{
NSString *authValue, *userPassword;
userPassword = [[self authenticatorInContext: context]
passwordInContext: context];
if ([userPassword length] > 0)
{
if (!username)
username = @"";
if (!password)
password = @"";
authValue = [NSString stringWithFormat: @"%@:%@", username, password];
[self setFolderPropertyValue: [authValue encryptWithKey: userPassword]
inCategory: @"WebCalendarsAuthentication"];
}
}
- (NSDictionary *) loadWebCalendar
{
NSString *location, *httpauth;
NSDictionary *authInfos;
NSMutableData *bodyData;
NSURL *url;
CURL *curl;
CURLcode rc;
char error[CURL_ERROR_SIZE];
NSMutableDictionary *result;
NSString *content, *newDisplayName;
iCalCalendar *calendar;
NSUInteger imported, status;
int imported = 0;
result = [NSMutableDictionary dictionary];
// Prepare HTTPS post using libcurl
location = [self folderPropertyValueInCategory: @"WebCalendars"];
[result setObject: location forKey: @"url"];
url = [NSURL URLWithString: location];
if (url)
{
handle = AUTORELEASE([[WOHTTPURLHandle alloc] initWithURL: url cached: NO]);
data = [handle resourceData];
if (!data && [[location lowercaseString] hasPrefix: @"https"])
{
NSLog(@"WARNING: Your GNUstep/SOPE installation might not have SSL support.");
return -1;
}
contents = [[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding];
[contents autorelease];
calendar = [iCalCalendar parseSingleFromSource: contents];
if (calendar)
curl_global_init (CURL_GLOBAL_SSL);
curl = curl_easy_init ();
if (curl)
{
[self deleteAllContent];
imported = [self importCalendar: calendar];
}
else
{
imported = -1;
curl_easy_setopt (curl, CURLOPT_URL, [location UTF8String]);
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L);
authInfos = [self _loadAuthData];
if (authInfos)
{
httpauth = [authInfos keysWithFormat: @"%{username}:%{password}"];
curl_easy_setopt (curl, CURLOPT_USERPWD, [httpauth UTF8String]);
curl_easy_setopt (curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
}
bodyData = [NSMutableData data];
size_t curlBodyFunction (void *ptr, size_t size, size_t nmemb, void *inSelf)
{
size_t total;
total = size * nmemb;
[bodyData appendBytes: ptr length: total];
return total;
}
curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, curlBodyFunction);
error[0] = 0;
curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, &error);
// Perform SOAP request
rc = curl_easy_perform (curl);
if (rc == 0)
{
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &status);
[result setObject: [NSNumber numberWithUnsignedInt: status]
forKey: @"status"];
if (status == 200)
{
content = [[NSString alloc] initWithData: bodyData
encoding: NSUTF8StringEncoding];
if (!content)
content = [[NSString alloc] initWithData: bodyData
encoding: NSISOLatin1StringEncoding];
[content autorelease];
calendar = [iCalCalendar parseSingleFromSource: content];
if (calendar)
{
newDisplayName = [[calendar
firstChildWithTag: @"x-wr-calname"]
flattenedValuesForKey: @""];
if ([newDisplayName length] > 0)
[self setDisplayName: newDisplayName];
[self deleteAllContent];
imported = [self importCalendar: calendar];
[result setObject: [NSNumber numberWithInt: imported]
forKey: @"imported"];
}
else
[result setObject: @"invalid-calendar-content" forKey: @"error"];
}
else
[result setObject: @"http-error" forKey: @"error"];
}
else
[result setObject: @"bad-url" forKey: @"error"];
curl_easy_cleanup (curl);
}
}
return imported;
else
[result setObject: @"invalid-url" forKey: @"error"];
return result;
}
- (void) setReloadOnLogin: (BOOL) newReloadOnLogin
@@ -98,7 +221,10 @@
error = [super delete];
if (!error)
[self setFolderPropertyValue: nil inCategory: @"WebCalendars"];
{
[self setFolderPropertyValue: nil inCategory: @"WebCalendars"];
[self setFolderPropertyValue: nil inCategory: @"WebCalendarsAuthentication"];
}
return error;
}
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Recarregar Calendários Remotos";
"Properties" = "Propriedades";
"Done" = "Feito";
"An error occured while importing calendar." = "Um erro ocorreu na importação do calendário.";
"An error occurred while importing calendar." = "Um erro ocorreu na importação do calendário.";
"No event was imported." = "Nenhum evento importado.";
"A total of %{0} events were imported in the calendar." = "Um total de %{0} eventos foram importados no calendário.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Actualitzar calendaris remots";
"Properties" = "Propietats";
"Done" = "Fet";
"An error occured while importing calendar." = "Hi ha hagut un error en importar el calendari.";
"An error occurred while importing calendar." = "Hi ha hagut un error en importar el calendari.";
"No event was imported." = "No s'ha importat cap esdeveniment.";
"A total of %{0} events were imported in the calendar." = "Han estat importats %{0} esdeveniments al calendari.";
+1 -1
View File
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Aktualizovat vzdálené kalendáře";
"Properties" = "Vlastnosti";
"Done" = "Hotovo";
"An error occured while importing calendar." = "Při importu událostí došlo k chybě.";
"An error occurred while importing calendar." = "Při importu událostí došlo k chybě.";
"No event was imported." = "Nebyla importována žádná událost.";
"A total of %{0} events were imported in the calendar." = "Do kalendáře bylo importováno %{0} událostí.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Opdatér kalendere";
"Properties" = "Egenskaber";
"Done" = "Udført";
"An error occured while importing calendar." = "Der opstod en fejl under importering af kalender.";
"An error occurred while importing calendar." = "Der opstod en fejl under importering af kalender.";
"No event was imported." = "Ingen begivenhed blev importeret.";
"A total of %{0} events were imported in the calendar." = "I alt %{0} begivenheder blev importeret i kalenderen.";
+1 -1
View File
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Externe agenda herladen";
"Properties" = "Instellingen";
"Done" = "Klaar";
"An error occured while importing calendar." = "Er is een fout opgetreden bij het importeren van de kalender.";
"An error occurred while importing calendar." = "Er is een fout opgetreden bij het importeren van de kalender.";
"No event was imported." = "Geen enkele gebeurtenis werd geïmporteerd.";
"A total of %{0} events were imported in the calendar." = "Een totaal van %{0} gebeurtenissen werd geïmporteerd in de kalender.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Reload Remote Calendars";
"Properties" = "Properties";
"Done" = "Done";
"An error occured while importing calendar." = "An error occured while importing calendar.";
"An error occurred while importing calendar." = "An error occurred while importing calendar.";
"No event was imported." = "No event was imported.";
"A total of %{0} events were imported in the calendar." = "A total of %{0} events were imported in the calendar.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Recharger les agendas distants";
"Properties" = "Propriétés";
"Done" = "Terminer";
"An error occured while importing calendar." = "Une erreur s'est produite lors de l'importation.";
"An error occurred while importing calendar." = "Une erreur s'est produite lors de l'importation.";
"No event was imported." = "Aucun événement n'a été importé.";
"A total of %{0} events were imported in the calendar." = "Un total de %{0} événements ont été importés dans le calendrier.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Externe Kalender neu laden";
"Properties" = "Einstellungen";
"Done" = "Fertig";
"An error occured while importing calendar." = "Fehler während des Importierens von Terminen.";
"An error occurred while importing calendar." = "Fehler während des Importierens von Terminen.";
"No event was imported." = "Es wurden keine Termine importiert.";
"A total of %{0} events were imported in the calendar." = "%{0} Termine wurden in den Kalender importiert.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Távoli naptárak frissítése";
"Properties" = "Tulajdonságok";
"Done" = "Kész";
"An error occured while importing calendar." = "Hiba történt a naptár importálásakor.";
"An error occurred while importing calendar." = "Hiba történt a naptár importálásakor.";
"No event was imported." = "Nem volt importált esemény.";
"A total of %{0} events were imported in the calendar." = "Összesen %{0} esemény lett importálva a naptárba.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Reload Remote Calendars";
"Properties" = "Eiginleikar";
"Done" = "Ljúka";
"An error occured while importing calendar." = "Villa kom upp þegar dagatalið var flutt inn.";
"An error occurred while importing calendar." = "Villa kom upp þegar dagatalið var flutt inn.";
"No event was imported." = "Enginn viðburður var fluttur inn.";
"A total of %{0} events were imported in the calendar." = "Alls voru %{0} viðburðir fluttir inn í dagatalið.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Aggiorna calendari remoti";
"Properties" = "Proprietà";
"Done" = "Fatto";
"An error occured while importing calendar." = "Si è verificato un errore durante l'importazione del calendario.";
"An error occurred while importing calendar." = "Si è verificato un errore durante l'importazione del calendario.";
"No event was imported." = "Nessun evento è stato importato.";
"A total of %{0} events were imported in the calendar." = "Un totale di %{0} sono stati importati nel calendario.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Last på nytt fjernkalendre";
"Properties" = "Egenskaper";
"Done" = "Klar";
"An error occured while importing calendar." = "En feil har oppstått under kalenderimporten.";
"An error occurred while importing calendar." = "En feil har oppstått under kalenderimporten.";
"No event was imported." = "Ingen hendelser ble importert.";
"A total of %{0} events were imported in the calendar." = "Totalt %{0} hendelser ble importert til kalenderen.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Last på nytt fjernkalendre";
"Properties" = "Egenskaper";
"Done" = "Klar";
"An error occured while importing calendar." = "En feil har oppstått under kalenderimporten.";
"An error occurred while importing calendar." = "En feil har oppstått under kalenderimporten.";
"No event was imported." = "Ingen hendelser ble importert.";
"A total of %{0} events were imported in the calendar." = "Totalt %{0} hendelser ble importert til kalenderen.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Przeładuj zdalne kalendarze";
"Properties" = "Właściwości";
"Done" = "Zrobione";
"An error occured while importing calendar." = "Błąd w trakcie importu kalendarza.";
"An error occurred while importing calendar." = "Błąd w trakcie importu kalendarza.";
"No event was imported." = "Nie zaimportowano żadnego wydarzenia.";
"A total of %{0} events were imported in the calendar." = "Do kalendarza zaimportowano razem %{0} wydarzeń(nia).";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Обновить удаленные календари";
"Properties" = "Свойства";
"Done" = "Готово";
"An error occured while importing calendar." = "Ошибка при импорте календаря.";
"An error occurred while importing calendar." = "Ошибка при импорте календаря.";
"No event was imported." = "События не были импортированы.";
"A total of %{0} events were imported in the calendar." = " Всего %{0} событий было импортировано в календарь.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Recargar calendarios remotos";
"Properties" = "Propiedades";
"Done" = "Hecho";
"An error occured while importing calendar." = "Ha ocurrido un error mientras importaba el calendario.";
"An error occurred while importing calendar." = "Ha ocurrido un error mientras importaba el calendario.";
"No event was imported." = "El evento no fue importado.";
"A total of %{0} events were imported in the calendar." = "Un total de %{0} eventos fueron importados al calendario.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Recargar calendarios remotos";
"Properties" = "Propiedades";
"Done" = "Hecho";
"An error occured while importing calendar." = "Ha ocurido un error mientras importaba el calendario.";
"An error occurred while importing calendar." = "Ha ocurido un error mientras importaba el calendario.";
"No event was imported." = "El evento no fue importado.";
"A total of %{0} events were imported in the calendar." = "Un total de %{0} eventos fueron importados al calendario.";
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Ladda om fjärrkalendrar";
"Properties" = "Egenskaper";
"Done" = "Klar";
"An error occured while importing calendar." = "Ett fel har inträffat under kalenderimporten.";
"An error occurred while importing calendar." = "Ett fel har inträffat under kalenderimporten.";
"No event was imported." = "Inga händeler importerades.";
"A total of %{0} events were imported in the calendar." = "Totalt %{0} händelser importerades till kalendern.";
+43 -3
View File
@@ -20,12 +20,13 @@
*/
#import <Foundation/NSValue.h>
#import <SoObjects/SOGo/NSDictionary+Utilities.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/WORequest.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <Appointments/SOGoAppointmentFolder.h>
#import <Appointments/SOGoWebAppointmentFolder.h>
#import <Appointments/SOGoAppointmentFolderICS.h>
#import "UIxCalFolderActions.h"
@@ -95,4 +96,43 @@
return response;
}
/* These methods are only available on instance of SOGoWebAppointmentFolder. */
- (WOResponse *) reloadAction
{
WOResponse *response;
NSDictionary *results;
response = [self responseWithStatus: 200];
[response setHeader: @"application/json" forKey: @"content-type"];
results = [[self clientObject] loadWebCalendar];
[response appendContentString: [results jsonRepresentation]];
return response;
}
- (WOResponse *) setCredentialsAction
{
WORequest *request;
WOResponse *response;
NSString *username, *password;
request = [context request];
username = [[request formValueForKey: @"username"] stringByTrimmingSpaces];
password = [[request formValueForKey: @"password"] stringByTrimmingSpaces];
if ([username length] > 0 && [password length] > 0)
{
[[self clientObject] setUsername: username
andPassword: password];
response = [self responseWith204];
}
else
response
= (WOResponse *) [NSException exceptionWithHTTPStatus: 400
reason: @"missing 'username' and/or"
@" 'password' parameters"];
return response;
}
@end /* UIxCalFolderActions */
+21 -46
View File
@@ -22,6 +22,7 @@
#import <Foundation/Foundation.h>
#import <NGObjWeb/NSException+HTTP.h>
#import <NGObjWeb/WOContext.h>
#import <NGObjWeb/WOContext+SoObjects.h>
#import <NGObjWeb/WORequest.h>
@@ -29,6 +30,7 @@
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <Appointments/SOGoWebAppointmentFolder.h>
#import <Appointments/SOGoAppointmentFolders.h>
@@ -60,68 +62,41 @@
WORequest *r;
WOResponse *response;
SOGoWebAppointmentFolder *folder;
NSURL *url;
NSString *urlString, *displayName;
NSMutableDictionary *rc;
SOGoAppointmentFolders *folders;
int imported = 0;
r = [context request];
rc = [NSMutableDictionary dictionary];
// Just a check
urlString = [r formValueForKey: @"url"];
url = [NSURL URLWithString: urlString];
if (url)
urlString = [[r formValueForKey: @"url"] stringByTrimmingSpaces];
if ([urlString length] > 0)
{
folders = [self clientObject];
displayName = [self displayNameForUrl: [r formValueForKey: @"url"]];
displayName = [self displayNameForUrl: urlString];
folder = [folders newWebCalendarWithName: displayName
atURL: urlString];
if (folder)
{
imported = [folder loadWebCalendar];
if (imported >= 0)
{
[rc setObject: displayName forKey: @"displayname"];
[rc setObject: [folder nameInContainer] forKey: @"name"];
}
else
{
[folder delete];
}
[rc setObject: [NSNumber numberWithInt: imported]
forKey: @"imported"];
response = [self responseWithStatus: 200];
[response setHeader: @"application/json" forKey: @"content-type"];
rc = [NSMutableDictionary dictionary];
[rc setObject: [folder displayName] forKey: @"name"];
[rc setObject: [folder folderReference] forKey: @"folderID"];
[response appendContentString: [rc jsonRepresentation]];
}
else
response = (WOResponse *)
[NSException exceptionWithHTTPStatus: 400
reason: @"folder was not created"];
}
else
response = (WOResponse *)
[NSException exceptionWithHTTPStatus: 400
reason: @"missing 'url' parameter"];
response = [self responseWithStatus: 200];
[response appendContentString: [rc jsonRepresentation]];
return response;
}
- (WOResponse *) reloadWebCalendarsAction
{
[[self clientObject] reloadWebCalendars: YES];
return [self responseWith204];
}
//
// The method below is different than the -reloadWebCalendarsAction as it
// won't force the reload of all calendars and automatically redirect the
// user to the /SOGo/so page upon completion.
//
// This is particularly useful for WebAuth users and they won't have a
// precise "entry point" in SOGo - so calendars reload upon login
// isn't possible for them.
//
- (WOResponse *) reloadWebCalendarsAndRedirectAction
{
[[self clientObject] reloadWebCalendars: NO];
return [self redirectToLocation: @"/SOGo/so"];
}
@end
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Перевантажити віддалені календарі";
"Properties" = "Властивості";
"Done" = "Зроблено";
"An error occured while importing calendar." = "Виникла помилка під час імпорту календаря.";
"An error occurred while importing calendar." = "Виникла помилка під час імпорту календаря.";
"No event was imported." = "Жодну подію не було імпортовано.";
"A total of %{0} events were imported in the calendar." = "Разом %{0} подій було імпортовано в цьому календарі.";
+1 -1
View File
@@ -114,7 +114,7 @@
"Reload Remote Calendars" = "Ail-lwytho Calendrau Anghysbell";
"Properties" = "Dewisiadau";
"Done" = "Done";
"An error occured while importing calendar." = "An error occured while importing calendar.";
"An error occurred while importing calendar." = "An error occurred while importing calendar.";
"No event was imported." = "No event was imported.";
"A total of %{0} events were imported in the calendar." = "A total of %{0} events were imported in the calendar.";
+15 -10
View File
@@ -45,16 +45,6 @@
actionClass = "UIxCalMainActions";
actionName = "addWebCalendar";
};
reloadWebCalendars = {
protectedBy = "View";
actionClass = "UIxCalMainActions";
actionName = "reloadWebCalendars";
};
reloadWebCalendarsAndRedirect = {
protectedBy = "View";
actionClass = "UIxCalMainActions";
actionName = "reloadWebCalendarsAndRedirect";
};
saveDragHandleState = {
protectedBy = "View";
pageName = "UIxCalMainView";
@@ -181,6 +171,21 @@
};
};
SOGoWebAppointmentFolder = {
methods = {
reload = {
protectedBy = "View";
actionClass = "UIxCalFolderActions";
actionName = "reload";
};
"set-credentials" = {
protectedBy = "View";
actionClass = "UIxCalFolderActions";
actionName = "setCredentials";
};
};
};
SOGoAppointmentFolderICS = {
methods = {
export = {
+211 -34
View File
@@ -1203,25 +1203,180 @@ function onMonthOverview() {
return _ensureView("monthview");
}
function onCalendarReload() {
function refreshEventsAndTasks() {
refreshEvents();
refreshTasks();
reloadWebCalendars();
}
function onCalendarReload() {
if (!reloadWebCalendars()) {
refreshEventsAndTasks();
}
return false;
}
function reloadWebCalendars() {
var url = ApplicationBaseURL + "reloadWebCalendars";
if (document.reloadWebCalAjaxRequest) {
document.reloadWebCalAjaxRequest.aborted = true;
document.reloadWebCalAjaxRequest.abort();
log("* reloadWebCalendars");
var hasWebCalendars = false;
var remaining = [];
var refreshOperations = { "remaining": remaining };
if (UserSettings['Calendar']
&& UserSettings['Calendar']['WebCalendars']) {
var webCalendars = UserSettings['Calendar']['WebCalendars'];
var folders = $("calendarList");
var calendarNodes = folders.childNodesWithTag("li");
for (var i = 0; i < calendarNodes.length; i++) {
var current = calendarNodes[i];
var calendarID = current.getAttribute("id");
var owner = current.getAttribute("owner");
var realID = owner + ":Calendar/" + calendarID.substr(1);
if (webCalendars[realID]) { /* is web calendar ? */
remaining.push(realID);
reloadWebCalendar(realID, refreshOperations);
}
}
}
document.reloadWebCalAjaxRequest
= triggerAjaxRequest(url, reloadWebCalendarsCallback);
return (remaining.length > 0);
}
function reloadWebCalendarsCallback (http) {
changeCalendarDisplay(null, currentView);
var calendarReloadErrors = { "invalid-calendar-content":
_("the returned content was not valid calendar"
+ " data"),
"http-error": _("an unknown http error occurred"
+ " during the load operation"),
"bad-url": _("the url in use is invalid or the"
+ " host is currently unreachable"),
"invalid-url": _("the url being used is invalid"
+ " or not handled") };
function reloadWebCalendar(folderID, refreshOperations) {
var url = URLForFolderID(folderID) + "/reload";
var cbData = { "folderID": folderID };
if (refreshOperations) {
cbData["refreshOperations"] = refreshOperations;
}
triggerAjaxRequest(url, reloadWebCalendarCallback, cbData);
}
function reloadWebCalendarCallback(http) {
var cbData = http.callbackData;
if (http.status == 200) {
var result = http.responseText.evalJSON(true);
var requireAuth = false;
var success = false;
if (result.status) {
if (result.status == 401) {
requireAuth = true;
}
else {
if (result.status == 200) {
success = true;
}
else {
var errorMessage = _("An error occurred while importing calendar.");
if (result["error"]) {
var message = calendarReloadErrors[result["error"]];
errorMessage = (_("An error occurred while loading remote"
+ " calendar: %{0}.").formatted(message));
}
showAlertDialog (errorMessage);
}
}
}
else {
var errorMessage = _("An error occurred while importing calendar.");
if (result["error"]) {
var message = calendarReloadErrors[result["error"]];
errorMessage = (_("An error occurred while loading remote"
+ " calendar: %{0}.").formatted(message));
}
showAlertDialog (errorMessage);
}
if (requireAuth) {
reauthenticateWebCalendar(cbData["folderID"], cbData);
}
else {
var refreshOperations = cbData["refreshOperations"];
if (refreshOperations) {
var remaining = refreshOperations["remaining"];
var calIdx = remaining.indexOf(cbData["folderID"]);
remaining.splice(calIdx, 1);
if (remaining.length == 0) {
refreshEventsAndTasks();
changeCalendarDisplay(null, currentView);
}
else {
var newFolderID = remaining[0];
reloadWebCalendar(newFolderID, refreshOperations);
}
}
else {
if (success) {
refreshEventsAndTasks();
changeCalendarDisplay(null, currentView);
}
}
}
}
else {
showAlertDialog(_("An error occurred while importing calendar."));
var refreshOperations = cbData["refreshOperations"];
if (refreshOperations) {
var remaining = refreshOperations["remaining"];
var calIdx = remaining.indexOf(cbData["folderID"]);
remaining.splice(calIdx, 1);
if (remaining.length > 0) {
var newFolderID = remaining[0];
reloadWebCalendar(newFolderID, refreshOperations);
}
}
}
}
function reauthenticateWebCalendar(folderID, refreshCBData) {
var remoteURL = null;
if (UserSettings['Calendar'] && UserSettings['Calendar']['WebCalendars']) {
var webCalendars = UserSettings['Calendar']['WebCalendars'];
remoteURL = webCalendars[folderID];
}
var parts = remoteURL.split("/");
var hostname = parts[2];
function authenticate(username, password) {
disposeDialog();
var url = URLForFolderID(folderID) + "/set-credentials";
var parameters = ("username=" + encodeURIComponent(username)
+ "&password=" + encodeURIComponent(password));
triggerAjaxRequest(url, authenticateWebCalendarCallback, refreshCBData, parameters,
{ "Content-type": "application/x-www-form-urlencoded" });
}
showAuthenticationDialog(_("Please identify yourself to \"%{0}\"...")
.formatted(hostname),
authenticate);
}
function authenticateWebCalendarCallback(http) {
var cbData = http.callbackData;
var folderID = cbData["folderID"];
var refreshOperations = cbData["refreshOperations"];
if (isHttpStatus204(http.status)) {
reloadWebCalendar(folderID, refreshOperations);
}
else {
if (refreshOperations) {
var remaining = refreshOperations["remaining"];
var calIdx = remaining.indexOf(folderID);
remaining.splice(calIdx, 1);
if (remaining.length > 0) {
var newFolderID = remaining[0];
reloadWebCalendar(newFolderID, refreshOperations);
}
}
}
}
function scrollDayView(scrollEvent) {
@@ -2420,7 +2575,7 @@ function onMenuSharing(event) {
var folders = $("calendarList");
var selected = folders.getSelectedNodes()[0];
/* FIXME: activation of the context menu should preferably select the entry
above which the event has occured */
above which the event has occurred */
if (selected) {
var folderID = selected.getAttribute("id");
var urlstr = URLForFolderID(folderID) + "/acls";
@@ -2473,14 +2628,14 @@ function initCalendarSelector() {
var items = list.childNodesWithTag("li");
for (var i = 0; i < items.length; i++) {
var input = items[i].childNodesWithTag("input")[0];
$(input).observe("click", updateCalendarStatus);
$(input).observe("click", clickEventWrapper(updateCalendarStatus));
}
var links = $("calendarSelectorButtons").childNodesWithTag("a");
$(links[0]).observe("click", onCalendarNew);
$(links[1]).observe("click", onCalendarWebAdd);
$(links[2]).observe("click", onCalendarAdd);
$(links[3]).observe("click", onCalendarRemove);
$(links[0]).observe("click", clickEventWrapper(onCalendarNew));
$(links[1]).observe("click", clickEventWrapper(onCalendarWebAdd));
$(links[2]).observe("click", clickEventWrapper(onCalendarAdd));
$(links[3]).observe("click", clickEventWrapper(onCalendarRemove));
}
function onCalendarSelectionChange(event) {
@@ -2567,29 +2722,51 @@ function onCalendarWebAdd(event) {
}
function onCalendarWebAddConfirm() {
disposeDialog();
var calendarUrl = this.value;
if (calendarUrl) {
if (document.addWebCalendarRequest) {
document.addWebCalendarRequest.aborted = true;
document.addWebCalendarRequest.abort ();
}
var url = ApplicationBaseURL + "/addWebCalendar?url=" + escape (calendarUrl);
document.addWebCalendarRequest =
triggerAjaxRequest (url, addWebCalendarCallback);
var url = ApplicationBaseURL + "/addWebCalendar"
var parameters = "url=" + calendarUrl;
triggerAjaxRequest(url, addWebCalendarCallback, calendarUrl, parameters,
{ "Content-type": "application/x-www-form-urlencoded" });
}
disposeDialog();
}
function addWebCalendarCallback (http) {
var data = http.responseText.evalJSON(true);
if (data.imported >= 0) {
appendCalendar(data.displayname, "/" + data.name);
refreshEvents();
refreshTasks();
changeCalendarDisplay();
function addWebCalendarCallback(http) {
if (http.status == 200) {
var data = http.responseText.evalJSON(true);
if (!data || data["error"] || !data["name"] || !data["folderID"]) {
showAlertDialog (_("An error occurred while importing calendar."));
}
else {
if (UserSettings['Calendar']) {
var webCalendars = UserSettings['Calendar']['WebCalendars'];
if (!webCalendars) {
webCalendars = {};
UserSettings['Calendar']['WebCalendars'] = webCalendars;
}
webCalendars[data["folderID"]] = http.callbackData;
}
appendCalendar(data["name"], data["folderID"]);
reloadWebCalendar(data["folderID"]);
}
}
else {
showAlertDialog (_("An error occured while importing calendar."));
showAlertDialog (_("An error occurred while importing calendar."));
}
// if (data.imported) {
// appendCalendar(data.displayname, "/" + data.name);
// refreshEvents();
// refreshTasks();
// changeCalendarDisplay();
// }
// else if (data.status && data.status == 401) {
// reauthenticateWebCalendar(data.name, data.url);
// }
// else {
// }
}
function onCalendarExport(event) {
@@ -2637,7 +2814,7 @@ function uploadCompleted(response) {
var div = $("uploadResults");
if (data.imported < 0)
$("uploadResultsContent").update(_("An error occured while importing calendar."));
$("uploadResultsContent").update(_("An error occurred while importing calendar."));
else if (data.imported == 0)
$("uploadResultsContent").update(_("No event was imported."));
else {
@@ -2699,7 +2876,7 @@ function appendCalendar(folderName, folderPath) {
li.getElementsByTagName("input")[0].checked = true;
// Register event on checkbox
$(checkBox).on("click", updateCalendarStatus);
$(checkBox).on("click", clickEventWrapper(updateCalendarStatus));
var url = URLForFolderID(folderPath) + "/canAccessContent";
triggerAjaxRequest(url, calendarEntryCallback, folderPath);
+68 -4
View File
@@ -47,6 +47,15 @@ var removeFolderRequestCount = 0;
// Email validation regexp
var emailRE = /^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i;
function clickEventWrapper(functionRef) {
function button_clickEventWrapper(event) {
preventDefault(event);
return functionRef(event);
}
return button_clickEventWrapper;
}
function createElement(tagName, id, classes,
attributes, htmlAttributes,
@@ -1257,11 +1266,18 @@ function accessToSubscribedFolder(serverFolder) {
var parts = serverFolder.split(":");
if (parts.length > 1) {
var username = parts[0];
var paths = parts[1].split("/");
folder = "/" + parts[0].asCSSIdentifier() + "_" + paths[2];
if (username == UserLogin) {
folder = paths[1];
}
else {
folder = "/" + username.asCSSIdentifier() + "_" + paths[1];
}
}
else
else {
folder = serverFolder;
}
return folder;
}
@@ -1927,8 +1943,9 @@ function createButton(id, caption, action) {
var span = createElement("span", null, null, null, null, newButton);
span.appendChild(document.createTextNode(caption));
}
if (action)
newButton.on("click", action);
if (action) {
newButton.on("click", clickEventWrapper(action));
}
return newButton;
}
@@ -2086,6 +2103,53 @@ function _showSelectDialog(title, label, options, button, callbackFcn, callbackA
dialog.appear({ duration: 0.2 });
}
function showAuthenticationDialog(label, callback) {
log("* showAuthenticationDialog");
log(backtrace());
var div = $("bgDialogDiv");
if (div && div.visible() && div.getOpacity() > 0) {
log("push");
dialogsStack.push(_showAuthenticationDialog.bind(this, label, callback));
}
else {
log("show");
_showAuthenticationDialog(label, callback);
}
}
function _showAuthenticationDialog(label, callback) {
var dialog = dialogs[label];
if (dialog) {
$("bgDialogDiv").show();
var inputs = dialog.getElementsByTagName("input");
for (var i = 0; i < inputs.length; i++) {
inputs[i].value = "";
}
}
else {
var fields = createElement("p", null, ["prompt"]);
fields.appendChild(document.createTextNode(_("Username:")));
var un_input = createElement("input", null, "textField",
{ type: "text", "value": "" });
fields.appendChild(un_input);
fields.appendChild(document.createTextNode(_("Password:")));
var pw_input = createElement("input", null, "textField",
{ type: "password", "value": "" });
fields.appendChild(pw_input);
function callbackWrapper() {
callback(un_input.value, pw_input.value);
}
fields.appendChild(createButton(null, _("OK"), callbackWrapper));
fields.appendChild(createButton(null, _("Cancel"), disposeDialog));
dialog = createDialog(null, label, null, fields, "none");
document.body.appendChild(dialog);
dialogs[label] = dialog;
}
dialog.appear({ duration: 0.2,
afterFinish: function () { dialog.down("input").focus(); } });
}
function disposeDialog() {
$$("DIV.dialog").each(function(div) {
if (div.visible() && div.getOpacity() == 1)