From: wolfgang Date: Thu, 22 Mar 2007 19:04:08 +0000 (+0000) Subject: git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1033 d1b88da0-ebda-0310... X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8b0788e36eaf6cefd6d20e1c4970b7942c76ea60;p=scalable-opengroupware.org git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1033 d1b88da0-ebda-0310-925b-ed51d893ca5b --- diff --git a/ChangeLog b/ChangeLog index 56864527..c1dad0c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,65 @@ +2007-03-22 Wolfgang Sourdeau + + * UI/MainUI/SOGoUserHomePage.m ([SOGoUserHomePage + -defaultAction]): remade module to redirect automatically to the + Calendar url. + + * SoObjects/Contacts/NSDictionary+Contact.m: removed module. + +2007-03-21 Wolfgang Sourdeau + + * UI/Contacts/UIxContactsListView.m ([UIxContactsListView + -deleteAction]): new web method to delete personal addressbook + folders. + + * SoObjects/Contacts/SOGoContactGCSFolder.m ([SOGoContactGCSFolder + -delete]): override method to refuse the deletion of the folder if + its nameInContainer is "personal". + + * SoObjects/SOGo/SOGoFolder.m ([SOGoFolder -delete]): new method + that forwards the deletion request to the folder manager. + +2007-03-19 Wolfgang Sourdeau + + * UI/Scheduler/UIxAttendeesEditor.m ([UIxAttendeesEditor + -zoomList]): new method that returns the list of available zoom factors. + + * UI/Scheduler/UIxTaskEditor.m ([UIxTaskEditor + -changeStatusAction]): rewrote method. + + * UI/Scheduler/UIxComponentEditor.m ([UIxComponentEditor + -_handleAttendeesEdition]): make sure "attendeesNames" has a + length > 0 before computing the attendees. Otherwise there will be + an invalid empty entry. + ([UIxComponentEditor -_handleOrganizer]): new method that adds the + organizer when there are attendees and remove it when the + attendees are removed. + +2007-03-18 Wolfgang Sourdeau + + * UI/Scheduler/UIxAttendeesEditor.m): new component. + + * UI/Scheduler/UIxComponentEditor.m: no longer a superclass for + the task and appointment editors. Now a container component + handling the common elements of tasks and events. + + * UI/Scheduler/UIxTaskEditor.m: same as below. + + * UI/Scheduler/UIxAppointmentEditor.m: rewrote template. No longer + a subclass of UIxComponentEditor but a subcomponent of it. + + * UI/Common/UIxToolbar.m ([UIxToolbar -toolbarConfig]): the + toolbar can have the special value "none" to indicate there is + none attached to the window. + + * SoObjects/Appointments/SOGoCalendarComponent.m + ([SOGoCalendarComponent -isOrganizer:emailorOwner:login]): new + method. + ([SOGoCalendarComponent -isParticipant:email]): new method. + ([SOGoCalendarComponent -calendar:create]): takes one parameter + "create" which is a boolean indicating whether we want to create + the calendar if it does not exist. + 2007-03-12 Wolfgang Sourdeau * UI/Scheduler/UIxCalMonthView.m: changed view to use divs instead diff --git a/SOPE/NGCards/NSArray+NGCards.m b/SOPE/NGCards/NSArray+NGCards.m index 9c87ab36..69436217 100644 --- a/SOPE/NGCards/NSArray+NGCards.m +++ b/SOPE/NGCards/NSArray+NGCards.m @@ -33,29 +33,22 @@ { NSString *currentString, *resultString, *cmpString; unsigned int count, max; - BOOL found; - found = NO; + resultString = nil; - cmpString = [aString uppercaseString]; max = [self count]; count = 0; + cmpString = [aString uppercaseString]; - currentString = nil; - while (!found && count < max) + while (!resultString && count < max) { currentString = [self objectAtIndex: count]; if ([[currentString uppercaseString] isEqualToString: cmpString]) - found = YES; + resultString = currentString; else count++; } - if (found) - resultString = currentString; - else - resultString = nil; - return resultString; } diff --git a/SOPE/NGCards/iCalEntityObject.h b/SOPE/NGCards/iCalEntityObject.h index 954c8f66..662aff90 100644 --- a/SOPE/NGCards/iCalEntityObject.h +++ b/SOPE/NGCards/iCalEntityObject.h @@ -75,46 +75,46 @@ typedef enum - (void) setTimeStampAsDate: (NSCalendarDate *)_date; - (NSCalendarDate *) timeStampAsDate; -- (void)setStartDate:(NSCalendarDate *)_date; -- (NSCalendarDate *)startDate; +- (void) setStartDate: (NSCalendarDate *) _date; +- (NSCalendarDate *) startDate; - (BOOL) hasStartDate; -- (void)setLastModified:(NSCalendarDate *)_value; -- (NSCalendarDate *)lastModified; +- (void) setLastModified: (NSCalendarDate *) _value; +- (NSCalendarDate *) lastModified; -- (void)setCreated:(NSCalendarDate *)_value; -- (NSCalendarDate *)created; +- (void) setCreated: (NSCalendarDate *) _value; +- (NSCalendarDate *) created; -- (void)setSequence:(NSNumber *)_value; /* this is an int */ -- (NSNumber *)sequence; -- (void)increaseSequence; +- (void) setSequence: (NSNumber *) _value; /* this is an int */ +- (NSNumber *) sequence; +- (void) increaseSequence; /* url can either be set as NSString or NSURL */ -- (void)setUrl:(id)_value; -- (NSURL *)url; +- (void) setUrl: (id) _value; +- (NSURL *) url; -- (void)setOrganizer:(iCalPerson *)_organizer; -- (iCalPerson *)organizer; +- (void) setOrganizer: (iCalPerson *) _organizer; +- (iCalPerson *) organizer; - (BOOL)isOrganizer:(id)_email; -- (void)setStatus:(NSString *)_value; -- (NSString *)status; +- (void) setStatus: (NSString *) _value; +- (NSString *) status; -- (void)removeAllAttendees; -- (void)addToAttendees:(iCalPerson *)_person; -- (NSArray *)attendees; +- (void) removeAllAttendees; +- (void) addToAttendees: (iCalPerson *) _person; +- (NSArray *) attendees; - (void) setAttendees: (NSArray *) attendees; /* categorize attendees into participants and resources */ -- (NSArray *)participants; -- (NSArray *)resources; -- (BOOL)isParticipant:(id)_email; -- (iCalPerson *)findParticipantWithEmail:(id)_email; +- (NSArray *) participants; +- (NSArray *) resources; +- (BOOL) isParticipant: (id) _email; +- (iCalPerson *) findParticipantWithEmail: (id) _email; -- (void)removeAllAlarms; -- (void)addToAlarms:(id)_alarm; -- (NSArray *)alarms; -- (BOOL)hasAlarms; +- (void) removeAllAlarms; +- (void) addToAlarms: (id) _alarm; +- (NSArray *) alarms; +- (BOOL) hasAlarms; @end diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index dc901a3a..c511e3fc 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -20,12 +20,13 @@ */ #import -#import #import #import #import #import #import +#import +#import // #import #import @@ -107,6 +108,8 @@ static NSNumber *sharedYes = nil; NSString *s; s = [[self container] nameInContainer]; +#warning HH DEBUG + [self logWithFormat:@"CAL UID: %@", s]; return [s isNotNull] ? [NSArray arrayWithObjects:&s count:1] : nil; } @@ -406,6 +409,23 @@ static NSNumber *sharedYes = nil; return @"vevent-collection"; } +- (NSArray *) davResourceType +{ + static NSArray *colType = nil; + NSArray *gdCol, *cdCol; + + if (!colType) + { + cdCol = [NSArray arrayWithObjects: @"calendar", XMLNS_CALDAV, nil]; + gdCol = [NSArray arrayWithObjects: [self groupDavResourceType], + XMLNS_GROUPDAV, nil]; + colType = [NSArray arrayWithObjects: @"collection", cdCol, gdCol, nil]; + [colType retain]; + } + + return colType; +} + /* vevent UID handling */ - (NSString *) resourceNameForEventUID: (NSString *)_u diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index e2ce7232..4fbebbdb 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -208,7 +208,6 @@ - send iMIP mail for all folders not found */ AgenorUserManager *um; - iCalCalendar *newCalendar; iCalEvent *oldApt, *newApt; iCalEventChanges *changes; iCalPerson *organizer; @@ -220,10 +219,9 @@ updateForcesReconsider = NO; - if ([_iCal length] == 0) { - return [NSException exceptionWithHTTPStatus:400 /* Bad Request */ - reason:@"got no iCalendar content to store!"]; - } + if ([_iCal length] == 0) + return [NSException exceptionWithHTTPStatus: 400 /* Bad Request */ + reason: @"got no iCalendar content to store!"]; um = [AgenorUserManager sharedUserManager]; @@ -237,7 +235,7 @@ oldApt = nil; } else - oldApt = (iCalEvent *) [self component]; + oldApt = (iCalEvent *) [self component: NO]; /* compare sequence if requested */ @@ -245,28 +243,23 @@ // TODO } - /* handle new content */ - newCalendar = [iCalCalendar parseSingleFromSource: _iCal]; - newApt = (iCalEvent *) [newCalendar firstChildWithTag: [self componentTag]]; - if (newApt == nil) { - return [NSException exceptionWithHTTPStatus:400 /* Bad Request */ - reason:@"could not parse iCalendar content!"]; - } - + newApt = (iCalEvent *) [self component: NO]; + if (!newApt) + return [NSException exceptionWithHTTPStatus: 400 /* Bad Request */ + reason: @"could not parse iCalendar content!"]; + /* diff */ - changes = [iCalEventChanges changesFromEvent: oldApt - toEvent: newApt]; - - uids = [um getUIDsForICalPersons:[changes deletedAttendees] - applyStrictMapping:NO]; - removedUIDs = [NSMutableArray arrayWithArray:uids]; - - uids = [um getUIDsForICalPersons:[newApt attendees] - applyStrictMapping:NO]; - storeUIDs = [NSMutableArray arrayWithArray:uids]; + changes = [iCalEventChanges changesFromEvent: oldApt toEvent: newApt]; + uids = [um getUIDsForICalPersons: [changes deletedAttendees] + applyStrictMapping: NO]; + removedUIDs = [NSMutableArray arrayWithArray: uids]; + + uids = [um getUIDsForICalPersons: [newApt attendees] + applyStrictMapping: NO]; + storeUIDs = [NSMutableArray arrayWithArray: uids]; props = [changes updatedProperties]; /* detect whether sequence has to be increased */ @@ -276,7 +269,9 @@ /* preserve organizer */ organizer = [newApt organizer]; - uid = [um getUIDForICalPerson:organizer]; + uid = [um getUIDForICalPerson: organizer]; + if (!uid) + uid = [self ownerInContext: nil]; if (uid) { if (![storeUIDs containsObject:uid]) [storeUIDs addObject:uid]; @@ -304,9 +299,9 @@ */ if (oldApt != nil && - ([props containsObject:@"startDate"] || - [props containsObject:@"endDate"] || - [props containsObject:@"duration"])) + ([props containsObject: @"startDate"] || + [props containsObject: @"endDate"] || + [props containsObject: @"duration"])) { NSArray *ps; unsigned i, count; @@ -326,8 +321,8 @@ /* perform storing */ - storeError = [self saveContentString:_iCal inUIDs:storeUIDs]; - delError = [self deleteInUIDs:removedUIDs]; + storeError = [self saveContentString: _iCal inUIDs: storeUIDs]; + delError = [self deleteInUIDs: removedUIDs]; // TODO: make compound if (storeError != nil) return storeError; @@ -393,7 +388,7 @@ /* load existing content */ - apt = (iCalEvent *) [self component]; + apt = (iCalEvent *) [self component: NO]; /* compare sequence if requested */ @@ -445,7 +440,7 @@ ex = nil; // TODO: do we need to use SOGoAppointment? (prefer iCalEvent?) - apt = (iCalEvent *) [self component]; + apt = (iCalEvent *) [self component: NO]; if (apt) { diff --git a/SoObjects/Appointments/SOGoCalendarComponent.h b/SoObjects/Appointments/SOGoCalendarComponent.h index 589b7dbb..80230884 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.h +++ b/SoObjects/Appointments/SOGoCalendarComponent.h @@ -35,11 +35,13 @@ { iCalCalendar *calendar; NSString *calContent; + BOOL isNew; } - (NSString *) componentTag; -- (iCalCalendar *) calendar; -- (iCalRepeatableEntityObject *) component; +- (iCalCalendar *) calendar: (BOOL) create; +- (iCalRepeatableEntityObject *) component: (BOOL) create; +- (BOOL) isNew; - (NSException *) primarySaveContentString: (NSString *) _iCalString; - (NSException *) primaryDelete; @@ -53,6 +55,10 @@ andNewObject: (iCalRepeatableEntityObject *) _newObject toAttendees: (NSArray *) _attendees; +- (BOOL) isOrganizer: (NSString *) email + orOwner: (NSString *) login; +- (BOOL) isParticipant: (NSString *) email; + @end #endif /* SOGOCALENDARCOMPONENT_H */ diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index c79091a7..12ba6c6e 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -69,6 +69,7 @@ static BOOL sendEMailNotifications = NO; { calendar = nil; calContent = nil; + isNew = NO; } return self; @@ -158,30 +159,49 @@ static BOOL sendEMailNotifications = NO; return result; } -- (iCalCalendar *) calendar +- (iCalCalendar *) calendar: (BOOL) create { - NSString *iCalString; + NSString *iCalString, *componentTag; + CardGroup *newComponent; if (!calendar) { iCalString = [self contentAsString]; - if (iCalString) + if ([iCalString length] > 0) + calendar = [iCalCalendar parseSingleFromSource: iCalString]; + else { - calendar = [iCalCalendar parseSingleFromSource: iCalString]; - [calendar retain]; + if (create) + { + calendar = [iCalCalendar groupWithTag: @"vcalendar"]; + [calendar setVersion: @"2.0"]; + [calendar setProdID: @"-//Inverse groupe conseil//SOGo 0.9//EN"]; + componentTag = [[self componentTag] uppercaseString]; + newComponent = [[calendar classForTag: componentTag] + groupWithTag: componentTag]; + [calendar addChild: newComponent]; + isNew = YES; + } } + if (calendar) + [calendar retain]; } return calendar; } -- (iCalRepeatableEntityObject *) component +- (iCalRepeatableEntityObject *) component: (BOOL) create { return (iCalRepeatableEntityObject *) - [[self calendar] + [[self calendar: create] firstChildWithTag: [self componentTag]]; } +- (BOOL) isNew +{ + return isNew; +} + /* raw saving */ - (NSException *) primarySaveContentString: (NSString *) _iCalString @@ -376,7 +396,7 @@ static BOOL sendEMailNotifications = NO; um = [AgenorUserManager sharedUserManager]; email = [um getEmailForUID: login]; - component = [self component]; + component = [self component: NO]; if (component) { if ([component isOrganizer: email]) @@ -397,4 +417,37 @@ static BOOL sendEMailNotifications = NO; return sogoRoles; } +- (BOOL) isOrganizer: (NSString *) email + orOwner: (NSString *) login +{ + BOOL isOrganizerOrOwner; + iCalRepeatableEntityObject *component; + NSString *organizerEmail; + + component = [self component: NO]; + organizerEmail = [[component organizer] rfc822Email]; + if (component && [organizerEmail length] > 0) + isOrganizerOrOwner + = ([organizerEmail caseInsensitiveCompare: email] == NSOrderedSame); + else + isOrganizerOrOwner + = [[container ownerInContext: nil] isEqualToString: login]; + + return isOrganizerOrOwner; +} + +- (BOOL) isParticipant: (NSString *) email +{ + BOOL isParticipant; + iCalRepeatableEntityObject *component; + + component = [self component: NO]; + if (component) + isParticipant = [component isParticipant: email]; + else + isParticipant = NO; + + return isParticipant; +} + @end diff --git a/SoObjects/Appointments/SOGoTaskObject.m b/SoObjects/Appointments/SOGoTaskObject.m index a70cf10e..87dbef17 100644 --- a/SoObjects/Appointments/SOGoTaskObject.m +++ b/SoObjects/Appointments/SOGoTaskObject.m @@ -413,7 +413,7 @@ static NSString *mailTemplateDefaultLanguage = nil; /* load existing content */ - task = (iCalToDo *) [self component]; + task = (iCalToDo *) [self component: NO]; /* compare sequence if requested */ @@ -462,7 +462,7 @@ static NSString *mailTemplateDefaultLanguage = nil; NSString *myEMail; // TODO: do we need to use SOGoTask? (prefer iCalToDo?) - task = (iCalToDo *) [self component]; + task = (iCalToDo *) [self component: NO]; if (task == nil) { return [NSException exceptionWithHTTPStatus:500 /* Server Error */ diff --git a/SoObjects/Contacts/GNUmakefile b/SoObjects/Contacts/GNUmakefile index 99db1eaf..af94111f 100644 --- a/SoObjects/Contacts/GNUmakefile +++ b/SoObjects/Contacts/GNUmakefile @@ -15,7 +15,6 @@ Contacts_OBJC_FILES = \ SOGoContactLDAPEntry.m \ SOGoContactLDAPFolder.m \ \ - NSDictionary+Contact.m \ NGLdapEntry+Contact.m \ Contacts_RESOURCE_FILES += \ diff --git a/SoObjects/Contacts/NSDictionary+Contact.h b/SoObjects/Contacts/NSDictionary+Contact.h deleted file mode 100644 index 3894a44f..00000000 --- a/SoObjects/Contacts/NSDictionary+Contact.h +++ /dev/null @@ -1,36 +0,0 @@ -/* NSDictionary+Contact.h - this file is part of $PROJECT_NAME_HERE$ - * - * Copyright (C) 2006 Inverse groupe conseil - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef NSDICTIONARY_CONTACT_H -#define NSDICTIONARY_CONTACT_H - -#import - -@class NSString; - -@interface NSDictionary (SOGoContact) - -- (NSString *) vcardContentFromSOGoContactRecord; - -@end - -#endif /* NSDICTIONARY_CONTACT_H */ diff --git a/SoObjects/Contacts/NSDictionary+Contact.m b/SoObjects/Contacts/NSDictionary+Contact.m deleted file mode 100644 index 3d67ff62..00000000 --- a/SoObjects/Contacts/NSDictionary+Contact.m +++ /dev/null @@ -1,125 +0,0 @@ -/* NSDictionary+Contact.m - this file is part of SOGo - * - * Copyright (C) 2006 Inverse groupe conseil - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import - -#import "NSDictionary+Contact.h" - -@implementation NSDictionary (SOGoContact) - -- (void) _appendSingleVCardValue: (NSString *) value - withFormat: (NSString *) format - toVCard: (NSMutableString *) vCard -{ - NSString *info; - - info = [self objectForKey: value]; - if (info && [info length] > 0) - [vCard appendFormat: format, info]; -} - -- (void) _appendMultilineVCardValue: (NSString *) value - withFormat: (NSString *) format - toVCard: (NSMutableString *) vCard -{ - NSString *info; - - info = [[self objectForKey: value] - stringByReplacingString: @"\r\n" - withString: @";"]; - if (info && [info length] > 0) - [vCard appendFormat: format, info]; -} - -- (void) _appendArrayedVCardValues: (NSArray *) keys - withFormat: (NSString *) format - toVCard: (NSMutableString *) vCard -{ - NSArray *values; - unsigned int count, max; - - values = [self objectsForKeys: keys notFoundMarker: @""]; - - max = [values count]; - while (count < max - && [[values objectAtIndex: count] isEqualToString: @""]) - count++; - - if (count < max) - [vCard appendFormat: format, [values componentsJoinedByString: @";"]]; -} - -- (NSString *) vcardContentFromSOGoContactRecord -{ - NSMutableString *newVCard; - - newVCard = [NSMutableString new]; - [newVCard autorelease]; - - [newVCard appendString: @"BEGIN:VCARD\r\n" - "VERSION:3.0\r\n" - "PRODID:-//OpenGroupware.org//LSAddress v5.3.85\r\n" - "PROFILE:vCard\r\n"]; - - [self _appendSingleVCardValue: @"cn" - withFormat: @"FN:%@\r\n" - toVCard: newVCard]; - [self _appendArrayedVCardValues: - [NSArray arrayWithObjects: @"sn", @"givenName", nil] - withFormat: @"N:%@;;;\r\n" - toVCard: newVCard]; - [self _appendSingleVCardValue: @"telephoneNumber" - withFormat: @"TEL;TYPE=work,voice,pref:%@\r\n" - toVCard: newVCard]; - [self _appendSingleVCardValue: @"facsimileTelephoneNumber" - withFormat: @"TEL;TYPE=work,fax:%@\r\n" - toVCard: newVCard]; - [self _appendSingleVCardValue: @"homeTelephoneNumber" - withFormat: @"TEL;TYPE=home,voice:%@\r\n" - toVCard: newVCard]; - [self _appendSingleVCardValue: @"mobile" - withFormat: @"TEL;TYPE=cell,voice:%@\r\n" - toVCard: newVCard]; - [self _appendArrayedVCardValues: - [NSArray arrayWithObjects: @"l", @"departmentNumber", nil] - withFormat: @"ORG:%@\r\n" - toVCard: newVCard]; - [self _appendMultilineVCardValue: @"postalAddress" - withFormat: @"ADR:TYPE=work,postal:%@\r\n" - toVCard: newVCard]; - [self _appendMultilineVCardValue: @"homePostalAddress" - withFormat: @"ADR:TYPE=home,postal:%@\r\n" - toVCard: newVCard]; - [self _appendSingleVCardValue: @"mail" - withFormat: @"EMAIL;TYPE=internet,pref:%@\r\n" - toVCard: newVCard]; - [self _appendSingleVCardValue: @"labelledURI" - withFormat: @"URL:%@\r\n" - toVCard: newVCard]; - - [newVCard appendString: @"END:VCARD\r\n"]; - - return newVCard; -} - -@end diff --git a/SoObjects/Contacts/SOGoContactGCSFolder.m b/SoObjects/Contacts/SOGoContactGCSFolder.m index 828be57a..29dbac21 100644 --- a/SoObjects/Contacts/SOGoContactGCSFolder.m +++ b/SoObjects/Contacts/SOGoContactGCSFolder.m @@ -179,6 +179,14 @@ return @"vcard-collection"; } +- (NSException *) delete +{ + return (([nameInContainer isEqualToString: @"personal"]) + ? [NSException exceptionWithHTTPStatus: 403 + reason: @"the 'personal' folder cannot be deleted"] + : [super delete]); +} + // /* GET */ // - (id) GETAction: (id)_ctx diff --git a/SoObjects/SOGo/AgenorUserManager.m b/SoObjects/SOGo/AgenorUserManager.m index 99617edc..b316a11a 100644 --- a/SoObjects/SOGo/AgenorUserManager.m +++ b/SoObjects/SOGo/AgenorUserManager.m @@ -26,6 +26,9 @@ #include #include "SOGoLRUCache.h" +#warning we should rely on the LDAP sources instead... +#define qualifierFormat @"mailNickname = %@" + @interface AgenorUserManager (PrivateAPI) - (NGLdapConnection *)ldapConnection; @@ -322,39 +325,54 @@ static unsigned PoolScanInterval = 5 * 60 /* every five minutes */; return uid; } -- (NSString *)getUIDForEmail:(NSString *)_email { +- (NSString *) getUIDForEmail: (NSString *)_email +{ NSString *uid; - - if ((uid = [self _cachedUIDForEmail:_email]) != nil) - return uid; - - if (useLDAP) { - uid = [self primaryGetAgenorUIDForEmail:_email]; - } - else { - NSRange r; - NSString *domain; - - if(!_email || [_email length] == 0) - return nil; - - r = [_email rangeOfString:@"@"]; - if (r.length == 0) - return nil; + NSRange r; + NSString *domain; + + uid = nil; + if ([_email length] > 0) + { + uid = [self _cachedUIDForEmail:_email]; + if (!uid) + { + if (useLDAP) + uid = [self primaryGetAgenorUIDForEmail:_email]; + + if (!uid) + { + r = [_email rangeOfString:@"@"]; + if (r.length == 0) + return nil; - domain = [_email substringFromIndex:NSMaxRange(r)]; - if (![domain isEqualToString:defaultMailDomain]) - uid = _email; - else - uid = [_email substringToIndex:r.location]; - } + domain = [_email substringFromIndex:NSMaxRange(r)]; + if (![domain isEqualToString:defaultMailDomain]) + uid = _email; + else + uid = [_email substringToIndex:r.location]; + } + if (uid) + [self _cacheUID:uid forEmail:_email]; + } + } - [self _cacheUID:uid forEmail:_email]; return uid; } -- (NSString *)getUIDForICalPerson:(iCalPerson *)_person { - return [self getUIDForEmail:[_person rfc822Email]]; +#warning big ugly hack. LDAP lookup should be fixed +- (NSString *) getUIDForICalPerson: (iCalPerson *) _person +{ + NSString *domainString, *email, *uid; + + domainString = [NSString stringWithFormat: @"@%@", defaultMailDomain]; + email = [_person rfc822Email]; + if ([email hasSuffix: domainString]) + uid = [_person cn]; + else + uid = [self getUIDForEmail: email]; + + return uid; } /* may insert NSNulls into returned array */ @@ -389,7 +407,7 @@ static unsigned PoolScanInterval = 5 * 60 /* every five minutes */; NSString *email; unsigned count; - q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid]; + q = [EOQualifier qualifierWithQualifierFormat:qualifierFormat, _uid]; conn = [self ldapConnection]; resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN @@ -434,28 +452,35 @@ static unsigned PoolScanInterval = 5 * 60 /* every five minutes */; return email; } -- (NSString *)getEmailForUID:(NSString *)_uid { +- (NSString *)getEmailForUID:(NSString *)_uid +{ NSString *email; + NSRange r; - if (![_uid isNotNull] || [_uid length] == 0) - return nil; - if ((email = [self _cachedEmailForUID:_uid]) != nil) - return email; - - if (useLDAP) { - email = [self primaryGetEmailForAgenorUID:_uid]; - } - else { - NSRange r; - - r = [_uid rangeOfString:@"@"]; - email = (r.length > 0) - ? _uid - : [[_uid stringByAppendingString:@"@"] - stringByAppendingString:defaultMailDomain]; - } - - [self _cacheEmail:email forUID:_uid]; + email = nil; + + if ([_uid length] > 0) + { + email = [self _cachedEmailForUID: _uid]; + if (!email) + { + if (useLDAP) + email = [self primaryGetEmailForAgenorUID:_uid]; + + if (!email) + { + r = [_uid rangeOfString:@"@"]; + email = ((r.length > 0) + ? _uid + : [[_uid stringByAppendingString:@"@"] + stringByAppendingString: defaultMailDomain]); + } + + if (email) + [self _cacheEmail: email forUID: _uid]; + } + } + return email; } @@ -474,7 +499,7 @@ static unsigned PoolScanInterval = 5 * 60 /* every five minutes */; if (cnAttrs == nil) cnAttrs = [[NSArray alloc] initWithObjects:@"cn", nil]; - q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid]; + q = [EOQualifier qualifierWithQualifierFormat:qualifierFormat, _uid]; conn = [self ldapConnection]; resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN @@ -564,7 +589,7 @@ static unsigned PoolScanInterval = 5 * 60 /* every five minutes */; NSEnumerator *resultEnum; NGLdapEntry *entry; - q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid]; + q = [EOQualifier qualifierWithQualifierFormat:qualifierFormat, _uid]; conn = [self ldapConnection]; resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN @@ -1031,7 +1056,7 @@ static unsigned PoolScanInterval = 5 * 60 /* every five minutes */; if (attrs == nil) attrs = [[NSArray alloc] initWithObjects:changeInternetAccessAttrName, nil]; - q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid]; + q = [EOQualifier qualifierWithQualifierFormat:qualifierFormat, _uid]; conn = [self ldapConnection]; resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN @@ -1132,7 +1157,7 @@ static unsigned PoolScanInterval = 5 * 60 /* every five minutes */; if (attrs == nil) attrs = [[NSArray alloc] initWithObjects:mailAutoresponderAttrName, nil]; - q = [EOQualifier qualifierWithQualifierFormat:@"uid = %@", _uid]; + q = [EOQualifier qualifierWithQualifierFormat:qualifierFormat, _uid]; conn = [self ldapConnection]; resultEnum = [conn deepSearchAtBaseDN:ldapBaseDN diff --git a/SoObjects/SOGo/SOGoAuthenticator.m b/SoObjects/SOGo/SOGoAuthenticator.m index 2c276e06..be10f0c8 100644 --- a/SoObjects/SOGo/SOGoAuthenticator.m +++ b/SoObjects/SOGo/SOGoAuthenticator.m @@ -99,7 +99,7 @@ static SOGoAuthenticator *auth = nil; - (SOGoUser *) userInContext: (WOContext *)_ctx { static SOGoUser *anonymous = nil, *freebusy; - SoUser *user; + SOGoUser *user; NSArray *traversalPath; NSString *login; diff --git a/SoObjects/SOGo/SOGoFolder.h b/SoObjects/SOGo/SOGoFolder.h index e768cd22..53a75ce9 100644 --- a/SoObjects/SOGo/SOGoFolder.h +++ b/SoObjects/SOGo/SOGoFolder.h @@ -65,6 +65,7 @@ - (NSString *)outlookFolderClass; - (BOOL) create; +- (NSException *) delete; @end diff --git a/SoObjects/SOGo/SOGoFolder.m b/SoObjects/SOGo/SOGoFolder.m index d7fe67b6..c79c0df9 100644 --- a/SoObjects/SOGo/SOGoFolder.m +++ b/SoObjects/SOGo/SOGoFolder.m @@ -90,7 +90,15 @@ } - (GCSFolderManager *)folderManager { - return [GCSFolderManager defaultFolderManager]; + static GCSFolderManager *folderManager = nil; + + if (!folderManager) + { + folderManager = [GCSFolderManager defaultFolderManager]; + [folderManager setFolderNamePrefix: @"SOGo_"]; + } + + return folderManager; } - (GCSFolder *)ocsFolderForPath:(NSString *)_path { @@ -120,14 +128,17 @@ { NSException *result; - [GCSFolderType setFolderNamePrefix: @"SOGo_"]; - result = [[self folderManager] createFolderOfType: [self folderType] atPath: ocsPath]; return (result == nil); } +- (NSException *) delete +{ + return [[self folderManager] deleteFolderAtPath: ocsPath]; +} + - (NSArray *)fetchContentObjectNames { NSArray *fields, *records; diff --git a/SoObjects/SOGo/SOGoUserFolder.m b/SoObjects/SOGo/SOGoUserFolder.m index 5f4cb3a3..d0d6a34e 100644 --- a/SoObjects/SOGo/SOGoUserFolder.m +++ b/SoObjects/SOGo/SOGoUserFolder.m @@ -231,4 +231,27 @@ return YES; } +/* CalDAV support */ +- (NSArray *) davCalendarHomeSet +{ + /* + + http://cal.example.com/home/bernard/calendars/ + + + Note: this is the *container* for calendar collections, not the + collections itself. So for use its the home folder, the + public folder and the groups folder. + */ + WOContext *context; + NSArray *tag; + + context = [[WOApplication application] context]; + tag = [NSArray arrayWithObjects: @"href", @"DAV:", @"D", + [self baseURLInContext: context], nil]; + + return [NSArray arrayWithObject: tag]; +} + @end /* SOGoUserFolder */ diff --git a/UI/Common/UIxPageFrame.h b/UI/Common/UIxPageFrame.h index 7647b169..94fe9816 100644 --- a/UI/Common/UIxPageFrame.h +++ b/UI/Common/UIxPageFrame.h @@ -22,6 +22,8 @@ #ifndef UIXPAGEFRAME_H #define UIXPAGEFRAME_H +#import + @interface WOComponent (PopupExtension) - (BOOL) isPopup; diff --git a/UI/Common/UIxPageFrame.m b/UI/Common/UIxPageFrame.m index dfb04557..e01a23f3 100644 --- a/UI/Common/UIxPageFrame.m +++ b/UI/Common/UIxPageFrame.m @@ -19,12 +19,14 @@ 02111-1307, USA. */ +#import "common.h" +#import +#import + #import #import -#include "common.h" -#include -#include "UIxPageFrame.h" +#import "UIxPageFrame.h" @implementation UIxPageFrame @@ -38,7 +40,8 @@ return self; } -- (void)dealloc { +- (void) dealloc +{ [item release]; [title release]; if (toolbar) @@ -48,27 +51,32 @@ /* accessors */ -- (void)setTitle:(NSString *)_value { +- (void) setTitle: (NSString *) _value +{ ASSIGNCOPY(title, _value); } -- (NSString *)title { +- (NSString *) title +{ if ([self isUIxDebugEnabled]) return title; return [self labelForKey: @"SOGo"]; } -- (void)setItem:(id)_item { +- (void) setItem: (id) _item +{ ASSIGN(item, _item); } -- (id)item { +- (id) item +{ return item; } -- (NSString *)ownerInContext { - return [[self clientObject] ownerInContext:[self context]]; +- (NSString *) ownerInContext +{ + return [[self clientObject] ownerInContext: nil]; } - (NSString *) doctype @@ -81,19 +89,20 @@ /* Help URL/target */ -- (NSString *)helpURL +- (NSString *) helpURL { return [NSString stringWithFormat: @"help/%@.html", title]; } -- (NSString *)helpWindowTarget +- (NSString *) helpWindowTarget { return [NSString stringWithFormat: @"Help_%@", title]; } /* notifications */ -- (void)sleep { +- (void) sleep +{ [item release]; item = nil; [super sleep]; @@ -107,22 +116,22 @@ return [self relativePathToUserFolderSubPath: @""]; } -- (NSString *)relativeCalendarPath +- (NSString *) relativeCalendarPath { return [self relativePathToUserFolderSubPath: @"Calendar/"]; } -- (NSString *)relativeContactsPath +- (NSString *) relativeContactsPath { return [self relativePathToUserFolderSubPath: @"Contacts/"]; } -- (NSString *)relativeMailPath +- (NSString *) relativeMailPath { return [self relativePathToUserFolderSubPath: @"Mail/"]; } -- (NSString *)logoffPath +- (NSString *) logoffPath { return [self relativePathToUserFolderSubPath: @"logoff"]; } @@ -192,8 +201,8 @@ { WOComponent *page; NSString *pageJSFilename; - - page = [[self context] page]; + + page = [[self context] page]; pageJSFilename = [NSString stringWithFormat: @"%@.css", NSStringFromClass([page class])]; diff --git a/UI/Common/UIxToolbar.m b/UI/Common/UIxToolbar.m index c36eaece..e7fd1009 100644 --- a/UI/Common/UIxToolbar.m +++ b/UI/Common/UIxToolbar.m @@ -158,7 +158,8 @@ return tb; } -- (id)toolbarConfig { +- (id) toolbarConfig +{ id tb; if (toolbarConfig != nil) @@ -167,7 +168,7 @@ if (toolbar) tb = toolbar; else - tb = [[self clientObject] lookupName:@"toolbar" inContext:[self context] + tb = [[self clientObject] lookupName: @"toolbar" inContext:[self context] acquire:NO]; if ([tb isKindOfClass:[NSException class]]) { @@ -177,11 +178,17 @@ toolbarConfig = [[NSNull null] retain]; return nil; } - - if ([tb isKindOfClass:[NSString class]]) - tb = [self loadToolbarConfigFromResourceNamed:tb]; + + if ([tb isKindOfClass: [NSString class]]) + { + if ([tb isEqualToString: @"none"]) + tb = [NSNull null]; + else + tb = [self loadToolbarConfigFromResourceNamed:tb]; + } toolbarConfig = [tb retain]; + return toolbarConfig; } @@ -240,6 +247,11 @@ return (amount > 0); } +- (BOOL) hasMenu +{ + return [[[self buttonInfo] valueForKey:@"hasMenu"] boolValue]; +} + - (void) setToolbar: (NSString *) newToolbar { ASSIGN(toolbar, newToolbar); diff --git a/UI/Contacts/English.lproj/Localizable.strings b/UI/Contacts/English.lproj/Localizable.strings index c29237cc..0067b60c 100644 --- a/UI/Contacts/English.lproj/Localizable.strings +++ b/UI/Contacts/English.lproj/Localizable.strings @@ -30,6 +30,10 @@ "invalidemailwarn" = "invalidemailwarn"; "new" = "new"; +"htmlMailFormat_UNKNOWN" = "Unknown"; +"htmlMailFormat_FALSE" = "Plain Text"; +"htmlMailFormat_TRUE" = "HTML"; + "Name or Email" = "Name or Email"; "Personal Addressbook" = "Personal Addressbook"; "Search in Addressbook" = "Search in Addressbook"; @@ -65,8 +69,8 @@ "Company: " = "Company: "; "Street Address: " = "Street Address: "; "City: " = "City: "; -"Province: " = "Province: "; -"Zip or Postal Code: " = "Zip or Postal Code: "; +"State_Province:" = "State/Province:"; +"ZIP_Postal Code:" = "ZIP/Postal Code:"; "Country: " = "Country: "; "Web: " = "Web: "; @@ -91,3 +95,5 @@ "Remove the selected Addressbook" = "Remove the selected Addressbook"; "Name of the Address Book" = "Name of the Address Book"; +"Are you sure you want to delete the selected address book?" += "Are you sure you want to delete the selected address book?"; diff --git a/UI/Contacts/French.lproj/Localizable.strings b/UI/Contacts/French.lproj/Localizable.strings index 7ecdeb34..45053a70 100644 --- a/UI/Contacts/French.lproj/Localizable.strings +++ b/UI/Contacts/French.lproj/Localizable.strings @@ -1,5 +1,15 @@ /* this file is in UTF-8 format! */ +"Contact" = "Contact"; +"Address" = "Adresses"; +"Other" = "Informations complémentaires"; + +"Name" = "Identité"; +"Internet" = "E-mail"; +"Phones" = "Numéros de téléphone"; +"Home" = "Personnelle"; +"Work" = "Professionnelle"; + "Address Books" = "Carnet d'adresses"; "Addresses" = "Adresses"; "Cancel" = "Annuler"; @@ -14,11 +24,9 @@ "Lastname" = "Nom"; "Location" = "Lieux"; "MobilePhone" = "Mobile"; -"Name" = "Nom"; "OfficePhone" = "Bureau"; "Organisation" = "Société"; "Phone" = "Téléphone"; -"Phones" = "Téléphones"; "Postal" = "Professionnelle"; "Save" = "Sauvegarder"; "URL" = "URL"; @@ -28,6 +36,10 @@ "invalidemailwarn" = "Champ de l'email invalide, continuer quand même ?"; "new" = "Nouveau"; +"htmlMailFormat_UNKNOWN" = "Inconnu"; +"htmlMailFormat_FALSE" = "Texte simple (sans HTML)"; +"htmlMailFormat_TRUE" = "HTML"; + "Name or Email" = "Le nom ou l'adresse"; "Personal Addressbook" = "Adresses personnelles"; "Search in Addressbook" = "Carnet d'adresses..."; @@ -44,39 +56,43 @@ "Preferred" = "Préféré"; "Card for %@" = "Fiche pour %@"; -"Display Name: " = "Nom à afficher : "; -"Email Address: " = "Adresse électronique : "; -"Phone Number: " = "Numéro de téléphone : "; +"Display:" = "Nom à afficher :"; +"Display Name:" = "Nom à afficher :"; +"Email:" = "Adresse électronique :"; +"Additional Email:" = "Adresse alternative :"; -"Firstname: " = "Prénom : "; -"Lastname: " = "Nom : "; -"Nickname: " = "Surnom : "; +"Phone Number:" = "Numéro de téléphone :"; +"Prefers to receive messages formatted as:" += "Préfère recevoir les messages au format :"; +"Screen Name:" = "Pseudo :"; + +"First:" = "Prénom :"; +"Last:" = "Nom :"; +"Nickname:" = "Surnom :"; "Telephone" = "Téléphone"; -"Work: " = "Travail : "; -"Home: " = "Domicile : "; -"Fax: " = "Télécopieur : "; -"Mobile: " = "Portable : "; -"Pager: " = "Téléavertisseur : "; - -"Title: " = "Titre/fonction : "; -"Service: " = "Service : "; -"Company: " = "Entreprise : "; -"Street Address: " = "Adresse : "; -"City: " = "Ville : "; -"Province: " = "Province : "; -"Zip or Postal Code: " = "Code postal : "; -"Country: " = "Pays : "; -"Web: " = "Adresse web : "; - -"Home" = "Domicile"; -"Work" = "Travail"; +"Work:" = "Travail :"; +"Home:" = "Domicile :"; +"Fax:" = "Télécopieur :"; +"Mobile:" = "Portable :"; +"Pager:" = "Téléavertisseur :"; + +"Title:" = "Fonction :"; +"Department:" = "Service :"; +"Organization:" = "Société :"; +"Address:" = "Adresse :"; +"City:" = "Ville/Localité :"; +"State_Province:" = "État/Prov. :"; +"ZIP_Postal Code:" = "Code postal :"; +"Country:" = "Pays :"; +"Web Page:" = "Page Web:"; + "Other Infos" = "Informations complémentaires"; -"Note: " = "Commentaires : "; -"Timezone: " = "Fuseau horaire : "; -"Birthday: " = "D. de naissance : "; -"Freebusy URL: " = "Adresse du FreeBusy : "; +"Note:" = "Remarques :"; +"Timezone:" = "Fuseau horaire :"; +"Birthday:" = "D. de naissance :"; +"Freebusy URL:" = "Adresse du FreeBusy :"; "Add as..." = "Ajouter..."; "Recipient" = "Destinataire"; @@ -93,3 +109,5 @@ "Remove the selected Addressbook" = "Enlever le carnet d'adresses sélectionné"; "Name of the Address Book" = "Nom du carnet d'adresses"; +"Are you sure you want to delete the selected address book?" += "Voulez-vous vraiment supprimer le carnet d'adresses sélectionné ?"; diff --git a/UI/Contacts/UIxContactEditor.h b/UI/Contacts/UIxContactEditor.h index 64fc7710..ae9d2373 100644 --- a/UI/Contacts/UIxContactEditor.h +++ b/UI/Contacts/UIxContactEditor.h @@ -31,8 +31,8 @@ @interface UIxContactEditor : UIxComponent { - NSString *errorText; NSString *preferredEmail; + NSString *item; NGVCard *card; NSMutableDictionary *snapshot; /* contains the values for editing */ } diff --git a/UI/Contacts/UIxContactEditor.m b/UI/Contacts/UIxContactEditor.m index ead9c8f3..a6da4280 100644 --- a/UI/Contacts/UIxContactEditor.m +++ b/UI/Contacts/UIxContactEditor.m @@ -46,25 +46,39 @@ - (void) dealloc { [snapshot release]; - [errorText release]; + [preferredEmail release]; [super dealloc]; } /* accessors */ -- (void) setErrorText: (NSString *) _txt +- (NSArray *) htmlMailFormatList { - ASSIGNCOPY(errorText, _txt); + static NSArray *htmlMailFormatItems = nil; + + if (!htmlMailFormatItems) + { + htmlMailFormatItems = [NSArray arrayWithObjects: @"FALSE", @"TRUE", nil]; + [htmlMailFormatItems retain]; + } + + return htmlMailFormatItems; +} + +- (NSString *) itemHtmlMailFormatText +{ + return [self labelForKey: + [NSString stringWithFormat: @"htmlMailFormat_%@", item]]; } -- (NSString *) errorText +- (void) setItem: (NSString *) newItem { - return errorText; + item = newItem; } -- (BOOL) hasErrorText +- (NSString *) item { - return [errorText length] > 0 ? YES : NO; + return item; } /* load/store content format */ @@ -214,6 +228,9 @@ [self _setSnapshotValue: @"workMail" to: workMail]; [self _setSnapshotValue: @"homeMail" to: homeMail]; + + [self _setSnapshotValue: @"mozillaUseHtmlMail" + to: [[card uniqueChildWithTag: @"x-mozilla-html"] value: 0]]; } - (void) _setupOrgFields @@ -291,6 +308,9 @@ [self _setupEmailFields]; + [self _setSnapshotValue: @"screenName" + to: [[card uniqueChildWithTag: @"x-aim"] value: 0]]; + elements = [card childrenWithTag: @"adr" andAttribute: @"type" havingValue: @"work"]; if (elements && [elements count] > 0) @@ -425,6 +445,10 @@ else [card setPreferred: homeMail]; } + + [[card uniqueChildWithTag: @"x-mozilla-html"] + setValue: 0 + to: [snapshot objectForKey: @"mozillaUseHtmlMail"]]; } - (void) _saveSnapshot @@ -463,6 +487,8 @@ [self _savePhoneValues]; [self _saveEmails]; + [[card uniqueChildWithTag: @"x-aim"] + setValue: 0 to: [snapshot objectForKey: @"screenName"]]; } - (id ) saveAction diff --git a/UI/Contacts/UIxContactFoldersView.m b/UI/Contacts/UIxContactFoldersView.m index 6a1b106a..88534001 100644 --- a/UI/Contacts/UIxContactFoldersView.m +++ b/UI/Contacts/UIxContactFoldersView.m @@ -171,7 +171,7 @@ - (WOResponse *) _responseForResults: (NSArray *) results { WOResponse *response; - NSString *email, *responseString; + NSString *email, *responseString, *uid; NSDictionary *result; response = [context response]; @@ -182,9 +182,11 @@ if (!result) result = [results objectAtIndex: 0]; email = [self _emailForResult: result]; + uid = [result objectForKey: @"c_uid"]; + if ([uid length] == 0) + uid = @""; responseString = [NSString stringWithFormat: @"%@:%@", - [result objectForKey: @"c_uid"], - email]; + uid, email]; [response setStatus: 200]; [response setHeader: @"text/plain; charset=iso-8859-1" forKey: @"Content-Type"]; diff --git a/UI/Contacts/UIxContactView.m b/UI/Contacts/UIxContactView.m index 87d4b620..f6919bfe 100644 --- a/UI/Contacts/UIxContactView.m +++ b/UI/Contacts/UIxContactView.m @@ -55,7 +55,7 @@ if (value && [value length] > 0) { if (label) - [cardString appendFormat: @"%@%@
\n", + [cardString appendFormat: @"%@ %@
\n", [self labelForKey: label], value]; else [cardString appendFormat: @"%@
\n", value]; @@ -73,35 +73,74 @@ - (NSString *) displayName { - return [self _cardStringWithLabel: @"Display Name: " + return [self _cardStringWithLabel: @"Display Name:" value: [card fn]]; } - (NSString *) nickName { - return [self _cardStringWithLabel: @"Nickname: " + return [self _cardStringWithLabel: @"Nickname:" value: [card nickname]]; } -- (NSString *) preferredEmail +- (NSString *) primaryEmail { NSString *email, *mailTo; email = [card preferredEMail]; - if (email && [email length] > 0) + if ([email length] > 0) mailTo = [NSString stringWithFormat: @"" @"%@", email, email]; else mailTo = nil; - return [self _cardStringWithLabel: @"Email Address: " + return [self _cardStringWithLabel: @"Email:" value: mailTo]; } +- (NSString *) secondaryEmail +{ + NSString *email, *mailTo; + NSMutableArray *emails; + + emails = [NSMutableArray new]; + [emails addObjectsFromArray: [card childrenWithTag: @"email"]]; + [emails removeObjectsInArray: [card childrenWithTag: @"email" + andAttribute: @"type" + havingValue: @"pref"]]; + + if ([emails count] > 1) + { + email = [[emails objectAtIndex: 0] value: 0]; + mailTo = [NSString stringWithFormat: @"" + @"%@", email, email]; + } + else + mailTo = nil; + + return [self _cardStringWithLabel: @"Additional Email:" + value: mailTo]; +} + +- (NSString *) screenName +{ + NSString *screenName, *goim; + + screenName = [[card uniqueChildWithTag: @"x-aim"] value: 0]; + if ([screenName length] > 0) + goim = [NSString stringWithFormat: @"%@", screenName, screenName]; + else + goim = nil; + + return [self _cardStringWithLabel: @"Screen Name:" value: goim]; +} + - (NSString *) preferredTel { - return [self _cardStringWithLabel: @"Phone Number: " + return [self _cardStringWithLabel: @"Phone Number:" value: [card preferredTel]]; } @@ -137,27 +176,27 @@ - (NSString *) workPhone { - return [self _phoneOfType: @"work" withLabel: @"Work: "]; + return [self _phoneOfType: @"work" withLabel: @"Work:"]; } - (NSString *) homePhone { - return [self _phoneOfType: @"home" withLabel: @"Home: "]; + return [self _phoneOfType: @"home" withLabel: @"Home:"]; } - (NSString *) fax { - return [self _phoneOfType: @"fax" withLabel: @"Fax: "]; + return [self _phoneOfType: @"fax" withLabel: @"Fax:"]; } - (NSString *) mobile { - return [self _phoneOfType: @"cell" withLabel: @"Mobile: "]; + return [self _phoneOfType: @"cell" withLabel: @"Mobile:"]; } - (NSString *) pager { - return [self _phoneOfType: @"pager" withLabel: @"Pager: "]; + return [self _phoneOfType: @"pager" withLabel: @"Pager:"]; } - (BOOL) hasHomeInfos @@ -383,12 +422,12 @@ - (NSString *) bday { - return [self _cardStringWithLabel: @"Birthday: " value: [card bday]]; + return [self _cardStringWithLabel: @"Birthday:" value: [card bday]]; } - (NSString *) tz { - return [self _cardStringWithLabel: @"Timezone: " value: [card tz]]; + return [self _cardStringWithLabel: @"Timezone:" value: [card tz]]; } - (NSString *) note @@ -404,7 +443,7 @@ withString: @"
"]; } - return [self _cardStringWithLabel: @"Note: " value: note]; + return [self _cardStringWithLabel: @"Note:" value: note]; } /* hrefs */ diff --git a/UI/Contacts/UIxContactsListView.m b/UI/Contacts/UIxContactsListView.m index 7a3cdd99..a709758b 100644 --- a/UI/Contacts/UIxContactsListView.m +++ b/UI/Contacts/UIxContactsListView.m @@ -19,6 +19,9 @@ 02111-1307, USA. */ +#import +#import + #import #import #import @@ -104,6 +107,25 @@ return self; } +- (id ) deleteAction +{ + id result; + NSException *ex; + WOResponse *response; + + ex = [[self clientObject] delete]; + if (ex) + result = ex; + else + { + response = [context response]; + [response setStatus: 200]; + result = response; + } + + return result; +} + - (NSString *) defaultSortKey { return @"fn"; diff --git a/UI/Contacts/product.plist b/UI/Contacts/product.plist index a3424d7b..6e4d7ea8 100644 --- a/UI/Contacts/product.plist +++ b/UI/Contacts/product.plist @@ -111,6 +111,11 @@ pageName = "UIxContactsListView"; actionName = "addressBooksContacts"; }; + delete = { + protectedBy = "View"; + pageName = "UIxContactsListView"; + actionName = "delete"; + }; }; }; diff --git a/UI/MailPartViewers/UIxMailPartICalViewer.m b/UI/MailPartViewers/UIxMailPartICalViewer.m index 7af72391..0cac3f1b 100644 --- a/UI/MailPartViewers/UIxMailPartICalViewer.m +++ b/UI/MailPartViewers/UIxMailPartICalViewer.m @@ -212,7 +212,7 @@ } - (iCalEvent *)storedEvent { - return (iCalEvent *) [(SOGoAppointmentObject *)[self storedEventObject] component]; + return (iCalEvent *) [(SOGoAppointmentObject *)[self storedEventObject] component: NO]; } /* organizer tracking */ diff --git a/UI/MainUI/SOGoUserHomePage.m b/UI/MainUI/SOGoUserHomePage.m index 01aa6af4..4cd16e76 100644 --- a/UI/MainUI/SOGoUserHomePage.m +++ b/UI/MainUI/SOGoUserHomePage.m @@ -1,353 +1,42 @@ -/* - Copyright (C) 2004-2005 SKYRIX Software AG - - This file is part of OpenGroupware.org. - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ - -#import -#import -#import -#import - +/* SOGoUserHomePage.m - this file is part of SOGo + * + * Copyright (C) 2007 Inverse groupe conseil + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import #import -#import @interface SOGoUserHomePage : UIxComponent -{ - id item; -} - -- (NSString *)ownPath; -- (NSString *)userFolderPath; -- (NSString *)relativePathToUserFolderSubPath:(NSString *)_sub; -- (NSString *)relativeCalendarPath; -- (NSString *)relativeContactsPath; -- (NSString *)relativeMailPath; - @end -#include -#include -#include -#include "common.h" - @implementation SOGoUserHomePage -static NSArray *internetAccessStates = nil; - -+ (void)initialize { - static BOOL didInit = NO; - - if (didInit) return; - didInit = YES; - - internetAccessStates = [[NSArray alloc] initWithObjects:@"0", @"1", nil]; -} - -- (void)dealloc { - [self->item release]; - [super dealloc]; -} - -/* lookup */ - -- (id)lookupName:(NSString *)_key inContext:(id)_ctx acquire:(BOOL)_flag { - // Note: we do no acquisition - id obj; - - /* first check attributes directly bound to the object */ - if ((obj = [super lookupName:_key inContext:_ctx acquire:NO])) - return obj; - - return nil; -} - -/* accessors */ - -- (void)setItem:(id)_item { - ASSIGN(self->item, _item); -} -- (id)item { - return self->item; -} - -- (NSArray *)internetAccessStates { - return internetAccessStates; -} - -- (NSString *)internetAccessState { - NSUserDefaults *ud; - NSNumber *value; - - ud = [[[self context] activeUser] userDefaults]; - value = [ud objectForKey:@"allowinternet"]; - return [NSString stringWithFormat:@"%d", [value boolValue]]; -} -- (void)setInternetAccessState:(NSString *)_internetAccessState { - NSUserDefaults *ud; - - ud = [[[self context] activeUser] userDefaults]; - [ud setObject:_internetAccessState forKey:@"allowinternet"]; - [ud synchronize]; -} - -- (NSString *)itemInternetAccessStateText { - NSString *key; - - key = [NSString stringWithFormat:@"internetAccessState_%@", self->item]; - return key; -} - -/* paths */ - -- (NSString *)ownPath { - NSString *uri; - NSRange r; - - uri = [[[self context] request] uri]; - - /* first: cut off query parameters */ - - r = [uri rangeOfString:@"?" options:NSBackwardsSearch]; - if (r.length > 0) - uri = [uri substringToIndex:r.location]; - return uri; -} - -- (NSString *)userFolderPath { - WOContext *ctx; - NSArray *traversalObjects; - NSString *url; - - ctx = [self context]; - traversalObjects = [ctx objectTraversalStack]; - url = [[traversalObjects objectAtIndex:1] - baseURLInContext:ctx]; - return [[NSURL URLWithString:url] path]; -} - -- (NSString *)relativePathToUserFolderSubPath:(NSString *)_sub { - NSString *dst, *rel; - - dst = [[self userFolderPath] stringByAppendingPathComponent:_sub]; - rel = [dst urlPathRelativeToPath:[self ownPath]]; - return rel; -} - -- (NSString *)relativeCalendarPath { - return [self relativePathToUserFolderSubPath:@"Calendar/"]; -} - -- (NSString *)relativeContactsPath { - return [self relativePathToUserFolderSubPath:@"Contacts/"]; -} - -- (NSString *)relativeMailPath { - return [NSString stringWithFormat: @"%@%@/view", - [self relativePathToUserFolderSubPath:@"Mail/"], - [self emailForUser]]; -} - -/* objects */ - -- (id)calendarFolder { - return [[self clientObject] lookupName:@"Calendar" - inContext:[self context] - acquire:NO]; -} - -/* checking access */ - -- (BOOL)canAccess { - WOContext *ctx; - NSString *owner; - - ctx = [self context]; - owner = [[self clientObject] ownerInContext:ctx]; - return [owner isEqualToString:[[ctx activeUser] login]]; -} - -- (BOOL)isNotAllowedToChangeInternetAccess { - // TODO: should be a SOGoUser method - AgenorUserManager *um; - WOContext *ctx; - NSString *uid; - - ctx = [self context]; - /* do not allow changes when access is from Internet */ - if (![ctx isAccessFromIntranet]) - return YES; - um = [AgenorUserManager sharedUserManager]; - uid = [[ctx activeUser] login]; - return [um isUserAllowedToChangeSOGoInternetAccess:uid] ? NO : YES; -} - -- (BOOL)isVacationMessageEnabledForInternet { - // TODO: should be a SOGoUser method - AgenorUserManager *um; - NSString *uid; - - um = [AgenorUserManager sharedUserManager]; - uid = [[[self context] activeUser] login]; - return [um isInternetAutoresponderEnabledForUser:uid]; -} - -- (BOOL)isVacationMessageEnabledForIntranet { - // TODO: should be a SOGoUser method - AgenorUserManager *um; - NSString *uid; - - um = [AgenorUserManager sharedUserManager]; - uid = [[[self context] activeUser] login]; - return [um isIntranetAutoresponderEnabledForUser:uid]; -} - -/* actions */ - -#if 0 -- (id)defaultAction { - return [self redirectToLocation:[self relativeCalendarPath]]; -} -#endif - -/* this is triggered by an XMLHTTPRequest */ -- (id)saveInternetAccessStateAction { - NSString *state; - - if ([self isNotAllowedToChangeInternetAccess]) - return [NSException exceptionWithHTTPStatus:403 /* Forbidden */]; - - state = [[[self context] request] formValueForKey:@"allowinternet"]; - [self setInternetAccessState:state]; - return [NSException exceptionWithHTTPStatus:200 /* OK */]; -} - -- (void) _fillFreeBusyItems: (NSMutableArray *) items - withRecords: (NSEnumerator *) records - fromStartDate: (NSCalendarDate *) startDate - toEndDate: (NSCalendarDate *) endDate +- (id ) defaultAction { - NSDictionary *record; - int count, startInterval, endInterval, value; - NSNumber *status; - NSCalendarDate *currentDate; - - record = [records nextObject]; - while (record) - { - status = [record objectForKey: @"status"]; - - value = [[record objectForKey: @"startdate"] intValue]; - currentDate = [NSCalendarDate dateWithTimeIntervalSince1970: value]; - if ([currentDate earlierDate: startDate] == currentDate) - startInterval = 0; - else - startInterval - = ([currentDate timeIntervalSinceDate: startDate] / 900); + NSString *baseURL, *url; - value = [[record objectForKey: @"enddate"] intValue]; - currentDate = [NSCalendarDate dateWithTimeIntervalSince1970: value]; - if ([currentDate earlierDate: endDate] == endDate) - endInterval = [items count] - 1; - else - endInterval = ([currentDate timeIntervalSinceDate: startDate] / 900); + baseURL = [[context request] uri]; + url = [baseURL stringByAppendingString:@"/../Calendar"]; - for (count = startInterval; count < endInterval; count++) - [items replaceObjectAtIndex: count withObject: status]; - - record = [records nextObject]; - } + return [self redirectToLocation: url]; } -- (NSString *) _freeBusyAsTextFromStartDate: (NSCalendarDate *) startDate - toEndDate: (NSCalendarDate *) endDate - forFreeBusy: (SOGoFreeBusyObject *) fb -{ - NSEnumerator *records; - NSMutableArray *freeBusyItems; - NSTimeInterval interval; - int count, intervals; - - interval = [endDate timeIntervalSinceDate: startDate] + 60; - intervals = interval / 900; /* slices of 15 minutes */ - freeBusyItems = [NSMutableArray arrayWithCapacity: intervals]; - for (count = 1; count < intervals; count++) - [freeBusyItems addObject: @"0"]; - - records = [[fb fetchFreeBusyInfosFrom: startDate to: endDate] objectEnumerator]; - [self _fillFreeBusyItems: freeBusyItems withRecords: records - fromStartDate: startDate toEndDate: endDate]; - - return [freeBusyItems componentsJoinedByString: @","]; -} - -- (NSString *) _freeBusyAsText -{ - SOGoFreeBusyObject *co; - NSCalendarDate *startDate, *endDate; - NSString *queryDay, *additionalDays; - NSTimeZone *uTZ; - - co = [self clientObject]; - uTZ = [co userTimeZone]; - - queryDay = [self queryParameterForKey: @"sday"]; - if ([queryDay length]) - startDate = [NSCalendarDate dateFromShortDateString: queryDay - andShortTimeString: @"0000" - inTimeZone: uTZ]; - else - { - startDate = [NSCalendarDate calendarDate]; - [startDate setTimeZone: uTZ]; - startDate = [startDate hour: 0 minute: 0]; - } - - queryDay = [self queryParameterForKey: @"eday"]; - if ([queryDay length]) - endDate = [NSCalendarDate dateFromShortDateString: queryDay - andShortTimeString: @"2359" - inTimeZone: uTZ]; - else - endDate = [startDate hour: 23 minute: 59]; - - additionalDays = [self queryParameterForKey: @"additional"]; - if ([additionalDays length] > 0) - endDate = [endDate dateByAddingYears: 0 months: 0 - days: [additionalDays intValue] - hours: 0 minutes: 0 seconds: 0]; - - return [self _freeBusyAsTextFromStartDate: startDate toEndDate: endDate - forFreeBusy: co]; -} - -- (id ) readFreeBusyAction -{ - WOResponse *response; - - response = [context response]; - [response setStatus: 200]; - [response setHeader: @"text/plain; charset=iso-8859-1" - forKey: @"Content-Type"]; - [response appendContentString: [self _freeBusyAsText]]; - - return response; -} - -@end /* SOGoUserHomePage */ +@end diff --git a/UI/MainUI/product.plist b/UI/MainUI/product.plist index 0a3857ca..2ee0e399 100644 --- a/UI/MainUI/product.plist +++ b/UI/MainUI/product.plist @@ -70,20 +70,9 @@ SOGoUserFolder = { methods = { view = { - protectedBy = "HomePage Access"; - pageName = "SOGoUserHomePage"; - }; - edit = { - protectedBy = "HomePage Access"; + protectedBy = "View"; pageName = "SOGoUserHomePage"; - actionName = "saveInternetAccessState"; }; - /* - GET = { // more or less a hack, see README of dbd - protectedBy = "HomePage Access"; - pageName = "SOGoUserHomePage"; - }; - */ }; }; SOGoGroupsFolder = { diff --git a/UI/SOGoUI/UIxComponent.m b/UI/SOGoUI/UIxComponent.m index 9c179e8b..52f05e55 100644 --- a/UI/SOGoUI/UIxComponent.m +++ b/UI/SOGoUI/UIxComponent.m @@ -269,7 +269,7 @@ static BOOL uixDebugEnabled = NO; uri = [uri substringToIndex:r.location]; /* next: strip trailing slash */ - + if ([uri hasSuffix: @"/"]) uri = [uri substringToIndex: ([uri length] - 1)]; r = [uri rangeOfString:@"/" options: NSBackwardsSearch]; diff --git a/UI/Scheduler/English.lproj/Localizable.strings b/UI/Scheduler/English.lproj/Localizable.strings index b0c3c256..aa881ef1 100644 --- a/UI/Scheduler/English.lproj/Localizable.strings +++ b/UI/Scheduler/English.lproj/Localizable.strings @@ -2,73 +2,72 @@ /* Day */ -"Sunday" = "Sunday"; -"Monday" = "Monday"; -"Tuesday" = "Tuesday"; -"Wednesday" = "Wednesday"; -"Thursday" = "Thursday"; -"Friday" = "Friday"; -"Saturday" = "Saturday"; - -"a2_Sunday" = "Su"; -"a2_Monday" = "Mo"; -"a2_Tuesday" = "Tu"; -"a2_Wednesday" = "We"; -"a2_Thursday" = "Th"; -"a2_Friday" = "Fr"; -"a2_Saturday" = "Sa"; - -"dayLabelFormat" = "%Y-%m-%d"; -"today" = "today"; - +"Sunday" = "Sunday"; +"Monday" = "Monday"; +"Tuesday" = "Tuesday"; +"Wednesday" = "Wednesday"; +"Thursday" = "Thursday"; +"Friday" = "Friday"; +"Saturday" = "Saturday"; + +"a2_Sunday" = "Su"; +"a2_Monday" = "Mo"; +"a2_Tuesday" = "Tu"; +"a2_Wednesday" = "We"; +"a2_Thursday" = "Th"; +"a2_Friday" = "Fr"; +"a2_Saturday" = "Sa"; + +"dayLabelFormat" = "%m/%d/%Y"; +"today" = "Today"; /* Week */ -"Week" = "Week"; -"this week" = "this week"; +"Week" = "Week"; +"this week" = "this week"; +"Week %d" = "Week %d"; /* Month */ -"this month" = "this month"; - -"January" = "January"; -"February" = "February"; -"March" = "March"; -"April" = "April"; -"May" = "May"; -"June" = "June"; -"July" = "July"; -"August" = "August"; -"September" = "September"; -"October" = "October"; -"November" = "November"; -"December" = "December"; - -"a3_January" = "Jan"; -"a3_February" = "Feb"; -"a3_March" = "Mar"; -"a3_April" = "Apr"; -"a3_May" = "May"; -"a3_June" = "Jun"; -"a3_July" = "Jul"; -"a3_August" = "Aug"; -"a3_September" = "Sep"; -"a3_October" = "Oct"; -"a3_November" = "Nov"; -"a3_December" = "Dec"; - +"this month" = "this month"; + +"January" = "January"; +"February" = "February"; +"March" = "March"; +"April" = "April"; +"May" = "May"; +"June" = "June"; +"July" = "July"; +"August" = "August"; +"September" = "September"; +"October" = "October"; +"November" = "November"; +"December" = "December"; + +"a3_January" = "January"; +"a3_February" = "February"; +"a3_March" = "March"; +"a3_April" = "April"; +"a3_May" = "May"; +"a3_June" = "June"; +"a3_July" = "July"; +"a3_August" = "August"; +"a3_September" = "September"; +"a3_October" = "October"; +"a3_November" = "November"; +"a3_December" = "December"; /* Year */ -"this year" = "this year"; +"this year" = "this year"; /* Menu */ -"Calendars" = "Calendars"; -"Calendar" = "Calendar"; -"Contacts" = "Contacts"; +"Calendars" = "Calendars"; +"Calendar" = "Calendar"; +"Contacts" = "Contacts"; "New Calendar..." = "New Calendar..."; "Delete Calendar" = "Delete Calendar"; @@ -80,180 +79,232 @@ /* Misc */ -"OpenGroupware.org" = "OpenGroupware.org"; -"Forbidden" = "Forbidden"; +"OpenGroupware.org" = "OpenGroupware.org"; +"Forbidden" = "Forbidden"; + + +/* Button Titles */ -/* Button titles */ +"new" = "New"; +"printview" = "Print View"; +"edit" = "Edit"; +"delete" = "Delete"; +"proposal" = "Proposal"; +"Save and Close" = "Save and Close"; +"Close" = "Close"; +"Invite Attendees" = "Invite Attendees"; +"Documents" = "Documents"; +"Cancel" = "Cancel"; +"show_rejected_apts" = "Show rejected appointments"; +"hide_rejected_apts" = "Hide rejected appointments"; -"new" = "new"; -"printview" = "printview"; -"edit" = "edit"; -"delete" = "delete"; -"proposal" = "proposal"; -"Save" = "Save"; -"Cancel" = "Cancel"; -"show_rejected_apts" = "Show rejected appointments"; -"hide_rejected_apts" = "Hide rejected appointments"; /* Schedule */ -"Schedule" = "Appointment propositions"; -"No appointments found" = "You don't have any appointments in the near future."; +"Schedule" = "Schedule"; +"No appointments found" = "No appointments found"; "Meetings proposed by you" = "Meetings proposed by you"; "Meetings proposed to you" = "Meetings proposed to you"; -"sched_startDateFormat" = "%d.%m. %H:%M"; -"action" = "Action"; -"accept" = "Accept"; -"decline" = "Decline"; -"more attendees" = "more attendees"; +"sched_startDateFormat" = "%d/%m %H:%M"; +"action" = "Action"; +"accept" = "Accept"; +"decline" = "Decline"; +"more attendees" = "More Attendees"; "Hide already accepted and rejected appointments" = "Hide already accepted and rejected appointments"; "Show already accepted and rejected appointments" = "Show already accepted and rejected appointments"; /* Appointments */ -"Appointment viewer" = "Appointment Viewer"; -"Appointment editor" = "Appointment Editor"; -"Appointment proposal" = "Appointment Proposal"; -"Appointment on" = "Appointment on"; -"From" = "From"; -"To" = "To"; -"Due time" = "Due time"; -"Start date" = "Start date"; -"End date" = "End date"; -"Due date" = "Due date"; -"Earliest start time" = "Earliest start time"; -"Latest end time" = "Latest end time"; -"browse start date" = "browse start date"; -"browse end date" = "browse end date"; -"Title" = "Title"; -"Name" = "Name"; -"Email" = "Email"; -"Status" = "Status"; -"Location" = "Location"; -"Priority" = "Priority"; -"Privacy" = "Privacy"; -"Cycle" = "Cycle"; -"Cycle End" = "End"; -"Categories" = "Categories"; -"Classification" = "Classification"; -"Duration" = "Duration"; -"Attendees" = "Attendees"; -"Resources" = "Resources"; -"Organizer" = "Organizer"; -"Description" = "Description"; -"Start" = "Start"; -"End" = "End"; -"Category" = "Category"; - -"attributes" = "Attributes"; -"attendees" = "Attendees"; +"Appointment viewer" = "Appointment Viewer"; +"Appointment editor" = "Appointment Editor"; +"Appointment proposal" = "Appointment Proposal"; +"Appointment on" = "Appointment on"; +"Start:" = "Start:"; +"Due Date:" = "Due Date:"; +"Title:" = "Title:"; +"Calendar:" = "Calendar:"; +"Name" = "Name"; +"Email" = "Email"; +"Status:" = "Status:"; +"% complete" = "% complete"; +"Location:" = "Location:"; +"Priority:" = "Priority:"; +"Privacy" = "Privacy"; +"Cycle" = "Cycle"; +"Cycle End" = "Cycle End"; +"Categories" = "Categories"; +"Classification" = "Classification"; +"Duration" = "Duration"; +"Attendees:" = "Attendees:"; +"Resources" = "Resources"; +"Organizer:" = "Organizer:"; +"Description:" = "Description:"; +"Document:" = "Document:"; +"Start:" = "Start:"; +"End:" = "End:"; +"Category:" = "Category:"; +"Repeat:" = "Repeat:"; +"Reminder:" = "Reminder:"; + +"Target:" = "Target:"; + +"attributes" = "attributes"; +"attendees" = "attendees"; /* checkbox title */ -"is private" = "is private"; +"is private" = "is private"; /* classification */ -"Public" = "Public"; -"Private" = "Private"; +"Public" = "Public"; +"Private" = "Private"; /* text used in overviews and tooltips */ -"empty title" = "Empty title"; -"private appointment" = "Private appointment"; +"empty title" = "Empty title"; +"private appointment" = "Private appointment"; -"Show Details" = "Show Details"; -"Hide Details" = "Hide Details"; +"Change..." = "Change..."; /* Appointments (participation state) */ -"partStat_NEEDS-ACTION" = "Not decided, yet"; -"partStat_ACCEPTED" = "Accepted"; -"partStat_DECLINED" = "Declined"; -"partStat_TENTATIVE" = "Tentative"; -"partStat_DELEGATED" = "Delegated"; -"partStat_OTHER" = "???"; +"partStat_NEEDS-ACTION" = "Needs action"; +"partStat_ACCEPTED" = "Accepted"; +"partStat_DECLINED" = "Declined"; +"partStat_TENTATIVE" = "Tentative"; +"partStat_DELEGATED" = "Delegated"; +"partStat_OTHER" = "Other"; /* Appointments (error messages) */ -"Conflicts found!" = "One or more conflicts were found."; -"Invalid iCal data!" = "Invalid iCalendar data ..."; -"Could not create iCal data!" = "Could not create iCalendar data ..."; - +"Conflicts found!" = "Conflicts found!"; +"Invalid iCal data!" = "Invalid iCal data!"; +"Could not create iCal data!" = "Could not create iCal data!"; /* Searching */ -"view_all" = "All Events"; -"view_today" = "Today's Events"; -"view_next7" = "Events in the Next 7 Days"; -"view_next14" = "Events in the Next 14 Days"; -"view_next31" = "Events in the Next 31 Days"; -"view_thismonth" = "Events in this Calendar Month"; +"view_all" = "All"; +"view_today" = "Today"; +"view_next7" = "Next 7 days"; +"view_next14" = "Next 14 days"; +"view_next31" = "Next 31 days"; +"view_thismonth" = "This Month"; "view_future" = "All Future Events"; -"view_selectedday" = "Currently Selected Day"; +"view_selectedday" = "Selected Day"; "View:" = "View:"; "Title or Description" = "Title or Description"; -"Search" = "Search"; -"Search attendees" = "Search attendees"; -"Search resources" = "Search resources"; -"Search appointments" = "Search appointments"; -"Search in Anais" = "Search in Anaïs"; +"Search" = "Search"; +"Search attendees" = "Search attendees"; +"Search resources" = "Search resources"; +"Search appointments" = "Search appointments"; +"Search in Anais" = "Search in Anais"; -"All Day" = "All Day"; -"check for conflicts" = "check for conflicts"; +"All day Event" = "All day Event"; +"check for conflicts" = "Check for conflicts"; "Browse URL" = "Browse URL"; /* calendar modes */ -"Overview" = "Overview"; -"Chart" = "Chart"; -"List" = "List"; -"Columns" = "Columns"; - +"Overview" = "Overview"; +"Chart" = "Chart"; +"List" = "List"; +"Columns" = "Columns"; /* Priorities */ -"prio_0" = "Not specified"; -"prio_1" = "High"; -"prio_2" = "High"; -"prio_3" = "High"; -"prio_4" = "High"; -"prio_5" = "Normal"; -"prio_6" = "Low"; -"prio_7" = "Low"; -"prio_8" = "Low"; -"prio_9" = "Low"; +"prio_0" = "Not specified"; +"prio_1" = "High"; +"prio_2" = "High"; +"prio_3" = "High"; +"prio_4" = "High"; +"prio_5" = "Normal"; +"prio_6" = "Low"; +"prio_7" = "Low"; +"prio_8" = "Low"; +"prio_9" = "Low"; /* access classes (privacy) */ "privacy_PUBLIC" = "Public"; -"privacy_CONFIDENTIAL" = "Time and Date Only"; +"privacy_CONFIDENTIAL" = "Confidential"; "privacy_PRIVATE" = "Private"; /* status type */ "status_" = "Not specified"; +"status_NOT-SPECIFIED" = "Not specified"; "status_TENTATIVE" = "Tentative"; "status_CONFIRMED" = "Confirmed"; "status_CANCELLED" = "Cancelled"; +"status_NEEDS-ACTION" = "Needs Action"; +"status_IN-PROCESS" = "In Process"; +"status_COMPLETED" = "Completed on"; /* Cycles */ -"cycle_once" = "once"; -"cycle_daily" = "daily"; -"cycle_weekly" = "weekly"; -"cycle_2weeks" = "all 2 weeks"; -"cycle_4weeks" = "all 4 weeks"; -"cycle_monthly" = "monthly"; -"cycle_weekday" = "weekday"; -"cycle_yearly" = "yearly"; +"cycle_once" = "cycle_once"; +"cycle_daily" = "cycle_daily"; +"cycle_weekly" = "cycle_weekly"; +"cycle_2weeks" = "cycle_2weeks"; +"cycle_4weeks" = "cycle_4weeks"; +"cycle_monthly" = "cycle_monthly"; +"cycle_weekday" = "cycle_weekday"; +"cycle_yearly" = "cycle_yearly"; -"cycle_end_never" = "never"; -"cycle_end_until" = "on date"; +"cycle_end_never" = "cycle_end_never"; +"cycle_end_until" = "cycle_end_until"; /* Appointment categories */ -"APPOINTMENT" = "Appointment"; -"NOT IN OFFICE" = "Outside"; -"MEETING" = "Meeting"; -"HOLIDAY" = "Holiday"; -"PHONE CALL" = "Phone"; +"category_NONE" = "None"; +"category_ANNIVERSARY" = "Anniversary"; +"category_BIRTHDAY" = "Birthday"; +"category_BUSINESS" = "Business"; +"category_CALLS" = "Calls"; +"category_CLIENTS" = "Clients"; +"category_COMPETITION" = "Competition"; +"category_CUSTOMER" = "Customer"; +"category_FAVORITES" = "Favorites"; +"category_FOLLOW UP" = "Follow up"; +"category_GIFTS" = "Gifts"; +"category_HOLIDAYS" = "Holidays"; +"category_IDEAS" = "Ideas"; +"category_ISSUES" = "Issues"; +"category_MISCELLANEOUS" = "Miscellaneous"; +"category_PERSONAL" = "Personal"; +"category_PROJECTS" = "Projects"; +"category_PUBLIC HOLIDAY" = "Public Holiday"; +"category_STATUS" = "Status"; +"category_SUPPLIERS" = "Suppliers"; +"category_TRAVEL" = "Travel"; +"category_VACATION" = "Vacation"; + +"repeat_NEVER" = "Does not repeat"; +"repeat_DAILY" = "Daily"; +"repeat_WEEKLY" = "Weekly"; +"repeat_BI-WEEKLY" = "Bi-weekly"; +"repeat_EVERY WEEKDAY" = "Every Weekday"; +"repeat_MONTHLY" = "Monthly"; +"repeat_YEARLY" = "Yearly"; +"repeat_CUSTOM" = "Custom..."; + +"reminder_NONE" = "No reminder"; +"reminder_5_MINUTES_BEFORE" = "5 minutes before"; +"reminder_10_MINUTES_BEFORE" = "10 minutes before"; +"reminder_15_MINUTES_BEFORE" = "15 minutes before"; +"reminder_30_MINUTES_BEFORE" = "30 minutes before"; +"reminder_45_MINUTES_BEFORE" = "45 minutes before"; +"reminder_1_HOUR_BEFORE" = "1 hour before"; +"reminder_2_HOURS_BEFORE" = "2 hours before"; +"reminder_5_HOURS_BEFORE" = "5 hours before"; +"reminder_15_HOURS_BEFORE" = "15 hours before"; +"reminder_1_DAY_BEFORE" = "1 day before"; +"reminder_2_DAYS_BEFORE" = "2 days before"; +"reminder_1_WEEK_BEFORE" = "1 week before"; +"reminder_CUSTOM" = "Custom..."; + +"zoom_400" = "400%"; +"zoom_200" = "200%"; +"zoom_100" = "100%"; +"zoom_50" = "50%"; +"zoom_25" = "25%"; /* validation errors */ @@ -265,6 +316,11 @@ validate_endbeforestart = "Enddate is before startdate!"; "Tasks" = "Tasks"; "Hide completed tasks" = "Hide completed tasks"; +/* tabs */ +"Task" = "Task"; +"Event" = "Event"; +"Recurrence" = "Recurrence"; + /* toolbar */ "New Event" = "New Event"; "New Task" = "New Task"; @@ -275,11 +331,19 @@ validate_endbeforestart = "Enddate is before startdate!"; "Week View" = "Week View"; "Month View" = "Month View"; -"eventPartStatModificationError" = "Your participation status at the event could not be modified."; +"eventPartStatModificationError" = "Your participation status could not be modified."; + +/* menu */ +"New Event..." = "New Event..."; +"New Task..." = "New Task..."; +"Edit Selected Event..." = "Edit Selected Event..."; +"Delete Selected Event" = "Delete Selected Event"; +"Select All" = "Select All"; +"Workweek days only" = "Workweek days only"; +"Tasks in View" = "Tasks in View"; -/* confirmations */ -"appointmentDeleteConfirmation" = "Erasing this event will not be reversible.\\nDo you want to confirm anyway?"; -"taskDeleteConfirmation" = "Erasing this task will be nonreversible.\\nDo you want to confirm anyway?"; +"appointmentDeleteConfirmation" = "Erasing this event will be permanent.\\nWould you like to continue?"; +"taskDeleteConfirmation" = "Erasing this task will be permanent.\\nWould you like to continue?"; /* Legend */ "Required participant" = "Required participant"; diff --git a/UI/Scheduler/French.lproj/Localizable.strings b/UI/Scheduler/French.lproj/Localizable.strings index 43aef1b2..adb78b5d 100644 --- a/UI/Scheduler/French.lproj/Localizable.strings +++ b/UI/Scheduler/French.lproj/Localizable.strings @@ -2,74 +2,72 @@ /* Day */ -"Sunday" = "Dimanche"; -"Monday" = "Lundi"; -"Tuesday" = "Mardi"; -"Wednesday" = "Mercredi"; -"Thursday" = "Jeudi"; -"Friday" = "Vendredi"; -"Saturday" = "Samedi"; - -"a2_Sunday" = "Di"; -"a2_Monday" = "Lu"; -"a2_Tuesday" = "Ma"; -"a2_Wednesday" = "Me"; -"a2_Thursday" = "Je"; -"a2_Friday" = "Ve"; -"a2_Saturday" = "Sa"; - -"dayLabelFormat" = "%d/%m/%Y"; -"today" = "Aujourd'hui"; - +"Sunday" = "Dimanche"; +"Monday" = "Lundi"; +"Tuesday" = "Mardi"; +"Wednesday" = "Mercredi"; +"Thursday" = "Jeudi"; +"Friday" = "Vendredi"; +"Saturday" = "Samedi"; + +"a2_Sunday" = "Di"; +"a2_Monday" = "Lu"; +"a2_Tuesday" = "Ma"; +"a2_Wednesday" = "Me"; +"a2_Thursday" = "Je"; +"a2_Friday" = "Ve"; +"a2_Saturday" = "Sa"; + +"dayLabelFormat" = "%d/%m/%Y"; +"today" = "Aujourd'hui"; /* Week */ -"Week" = "Semaine"; -"this week" = "cette semaine"; +"Week" = "Semaine"; +"this week" = "cette semaine"; "Week %d" = "Semaine nº %d"; /* Month */ -"this month" = "ce mois"; - -"January" = "Janvier"; -"February" = "Février"; -"March" = "Mars"; -"April" = "Avril"; -"May" = "Mai"; -"June" = "Juin"; -"July" = "Juillet"; -"August" = "Août"; -"September" = "Septembre"; -"October" = "Octobre"; -"November" = "Novembre"; -"December" = "Décembre"; - -"a3_January" = "Jan"; -"a3_February" = "Fev"; -"a3_March" = "Mar"; -"a3_April" = "Avr"; -"a3_May" = "Mai"; -"a3_June" = "Jun"; -"a3_July" = "Jul"; -"a3_August" = "Aou"; -"a3_September" = "Sep"; -"a3_October" = "Oct"; -"a3_November" = "Nov"; -"a3_December" = "Dec"; - +"this month" = "ce mois"; + +"January" = "Janvier"; +"February" = "Février"; +"March" = "Mars"; +"April" = "Avril"; +"May" = "Mai"; +"June" = "Juin"; +"July" = "Juillet"; +"August" = "Août"; +"September" = "Septembre"; +"October" = "Octobre"; +"November" = "Novembre"; +"December" = "Décembre"; + +"a3_January" = "Jan"; +"a3_February" = "Fev"; +"a3_March" = "Mar"; +"a3_April" = "Avr"; +"a3_May" = "Mai"; +"a3_June" = "Jun"; +"a3_July" = "Jul"; +"a3_August" = "Aou"; +"a3_September" = "Sep"; +"a3_October" = "Oct"; +"a3_November" = "Nov"; +"a3_December" = "Dec"; /* Year */ -"this year" = "Cette année"; +"this year" = "Cette année"; /* Menu */ -"Calendars" = "Agendas"; -"Calendar" = "Agenda"; -"Contacts" = "Contacts"; +"Calendars" = "Agendas"; +"Calendar" = "Agenda"; +"Contacts" = "Contacts"; "New Calendar..." = "Nouvel agenda..."; "Delete Calendar" = "Effacer l'agenda"; @@ -81,102 +79,103 @@ /* Misc */ -"OpenGroupware.org" = "OpenGroupware.org"; -"Forbidden" = "Accès non autorisée"; +"OpenGroupware.org" = "OpenGroupware.org"; +"Forbidden" = "Accès non autorisée"; /* Button Titles */ -"new" = "Nouveau"; -"printview" = "Version imprimable"; -"edit" = "Éditer"; -"delete" = "Supprimer"; -"proposal" = "Recherche de plages horaires"; -"Save" = "Enregistrer"; -"Cancel" = "Annuler"; -"show_rejected_apts" = "Afficher les rendez-vous refusés"; -"hide_rejected_apts" = "Cacher les rendez-vous refusés"; +"new" = "Nouveau"; +"printview" = "Version imprimable"; +"edit" = "Éditer"; +"delete" = "Supprimer"; +"proposal" = "Recherche de plages horaires"; +"Save and Close" = "Enregistrer et fermer"; +"Close" = "Fermer"; +"Invite Attendees" = "Participants"; +"Documents" = "Documents"; +"Cancel" = "Annuler"; +"show_rejected_apts" = "Afficher les rendez-vous refusés"; +"hide_rejected_apts" = "Cacher les rendez-vous refusés"; /* Schedule */ -"Schedule" = "Suivi de rendez-vous"; +"Schedule" = "Suivi de rendez-vous"; "No appointments found" = "Aucun rendez-vous"; "Meetings proposed by you" = "Rendez-vous que vous proposez"; "Meetings proposed to you" = "Rendez-vous qui vous sont proposés"; "sched_startDateFormat" = "%d/%m %H:%M"; -"action" = "Action"; -"accept" = "Accepter"; -"decline" = "Refuser"; -"more attendees" = "Autres participants"; -"Hide already accepted and rejected appointments" = "Cacher les invitations refusées"; -"Show already accepted and rejected appointments" = "Montrer aussi les invitations refusées"; +"action" = "Action"; +"accept" = "Accepter"; +"decline" = "Refuser"; +"more attendees" = "Autres participants"; +"Hide already accepted and rejected appointments" = "Cacher les invitations refusées"; +"Show already accepted and rejected appointments" = "Montrer aussi les invitations refusées"; /* Appointments */ -"Appointment viewer" = "Visualisation de rendez-vous"; -"Appointment editor" = "Edition de rendez-vous"; -"Appointment proposal" = "Proposition de rendez-vous"; -"Appointment on" = "Rendez-vous le"; -"From" = "Du"; -"To" = "Au"; -"Due Time" = "Écheance"; -"Start date" = "Date de début"; -"End Date" = "Date de fin"; -"Due Date" = "Écheance"; -"Earliest start time" = "Au plus tôt"; -"Latest end time" = "Au plus tard"; -"browse start date" = "Sélection Date de début"; -"browse end date" = "Sélection Date de fin"; -"Title" ="Titre"; -"Name" = "Nom"; -"Email" = "Email"; -"Status" = "Statut"; -"Location" = "Lieu"; -"Priority" = "Priorité"; -"Privacy" = "Confidentialité"; -"Cycle" = "Récurrence"; -"Cycle End" = "s'arreter"; -"Categories" = "Categories"; -"Classification" = "Classification"; -"Duration" = "Durée"; -"Attendees" = "Participants"; -"Resources" = "Ressources"; -"Organizer" = "Organisateur"; -"Description" = "Description"; -"Start" = "Début"; -"End" = "Fin"; -"Category" = "Catégorie"; - -"attributes" = "Attributs"; -"attendees" = "Participants"; +"Appointment viewer" = "Visualisation de rendez-vous"; +"Appointment editor" = "Edition de rendez-vous"; +"Appointment proposal" = "Proposition de rendez-vous"; +"Appointment on" = "Rendez-vous le"; +"Start:" = "Début :"; +"Due Date:" = "Échéance :"; +"Title:" = "Titre :"; +"Calendar:" = "Agenda :"; +"Name" = "Nom"; +"Email" = "Email"; +"Status:" = "Statut :"; +"% complete" = "% complété"; +"Location:" = "Lieu :"; +"Priority:" = "Priorité"; +"Privacy" = "Confidentialité"; +"Cycle" = "Récurrence"; +"Cycle End" = "s'arreter"; +"Categories" = "Categories"; +"Classification" = "Classification"; +"Duration" = "Durée"; +"Attendees:" = "Participants :"; +"Resources" = "Ressources"; +"Organizer:" = "Organisateur :"; +"Description:" = "Description :"; +"Document:" = "Document :"; +"Start:" = "Début :"; +"End:" = "Fin :"; +"Category:" = "Catégorie :"; +"Repeat:" = "Répéter :"; +"Reminder:" = "Rappel :"; + +"Target:" = "Destination :"; + +"attributes" = "Attributs"; +"attendees" = "Participants"; /* checkbox title */ -"is private" = "Rendez-vous privé"; +"is private" = "Rendez-vous privé"; /* classification */ -"Public" = "Public"; -"Private" = "Privé"; +"Public" = "Public"; +"Private" = "Privé"; /* text used in overviews and tooltips */ -"empty title" = "Titre vide"; -"private appointment" = "Rendez-vous privé"; +"empty title" = "Titre vide"; +"private appointment" = "Rendez-vous privé"; -"Show Details" = "Afficher les détails"; -"Hide Details" = "Cacher les détails"; +"Change..." = "Modifier..."; /* Appointments (participation state) */ -"partStat_NEEDS-ACTION" = "Décision attendue"; -"partStat_ACCEPTED" = "Accepté"; -"partStat_DECLINED" = "Refusé"; -"partStat_TENTATIVE" = "Tentative"; -"partStat_DELEGATED" = "Delegué"; -"partStat_OTHER" = "???"; +"partStat_NEEDS-ACTION" = "Décision attendue"; +"partStat_ACCEPTED" = "Accepté"; +"partStat_DECLINED" = "Refusé"; +"partStat_TENTATIVE" = "Tentative"; +"partStat_DELEGATED" = "Delegué"; +"partStat_OTHER" = "???"; /* Appointments (error messages) */ -"Conflicts found!" = "Un ou plusieurs conflicts ont été détectés"; -"Invalid iCal data!" = "Données iCal non valides ..."; -"Could not create iCal data!" = "Les données iCal n'ont pu être crées ..."; +"Conflicts found!" = "Un ou plusieurs conflicts ont été détectés"; +"Invalid iCal data!" = "Données iCal non valides ..."; +"Could not create iCal data!" = "Les données iCal n'ont pu être crées ..."; /* Searching */ @@ -193,37 +192,36 @@ "View:" = "Voir :"; "Title or Description" = "Le titre ou la description"; -"Search" = "Rechercher"; +"Search" = "Rechercher"; "Search attendees" ="Recherche de participants"; "Search resources" ="Recherche de ressources"; -"Search appointments" = "Recherche de rendez-vous"; -"Search in Anais" = "Recherche par Anaïs"; +"Search appointments" = "Recherche de rendez-vous"; +"Search in Anais" = "Recherche par Anaïs"; -"All Day" = "Toute la journée"; +"All day Event" = "Toute la journée"; "check for conflicts" = "Vérifier les conflits"; "Browse URL" = "Visiter l'URL"; /* calendar modes */ -"Overview" = "Vue synthétique"; -"Chart" = "Vue avec repères horaires"; -"List" = "Vue multi-agenda"; -"Columns" = "Vue avec repères mensuels"; - +"Overview" = "Vue synthétique"; +"Chart" = "Vue avec repères horaires"; +"List" = "Vue multi-agenda"; +"Columns" = "Vue avec repères mensuels"; /* Priorities */ -"prio_0" = "Non-spécifiée"; -"prio_1" = "Haute"; -"prio_2" = "Haute"; -"prio_3" = "Haute"; -"prio_4" = "Haute"; -"prio_5" = "Normale"; -"prio_6" = "Basse"; -"prio_7" = "Basse"; -"prio_8" = "Basse"; -"prio_9" = "Basse"; +"prio_0" = "Non-spécifiée"; +"prio_1" = "Haute"; +"prio_2" = "Haute"; +"prio_3" = "Haute"; +"prio_4" = "Haute"; +"prio_5" = "Normale"; +"prio_6" = "Basse"; +"prio_7" = "Basse"; +"prio_8" = "Basse"; +"prio_9" = "Basse"; /* access classes (privacy) */ "privacy_PUBLIC" = "Public"; @@ -232,31 +230,82 @@ /* status type */ "status_" = "Non-spécifié"; +"status_NOT-SPECIFIED" = "Non spécifié"; "status_TENTATIVE" = "Tentative"; "status_CONFIRMED" = "Confirmé"; "status_CANCELLED" = "Annulé"; +"status_NEEDS-ACTION" = "En attente"; +"status_IN-PROCESS" = "En cours"; +"status_COMPLETED" = "Complété le"; /* Cycles */ -"cycle_once" = "Sans récurrence"; -"cycle_daily" = "Chaque jour"; -"cycle_weekly" = "Chaque semaine"; -"cycle_2weeks" = "Toutes les deux semaines"; -"cycle_4weeks" = "Toutes les quatre semaines "; -"cycle_monthly" = "Tous les mois"; -"cycle_weekday" = "À chaque même jour de la semaine"; -"cycle_yearly" = "Chaque année"; +"cycle_once" = "Sans récurrence"; +"cycle_daily" = "Chaque jour"; +"cycle_weekly" = "Chaque semaine"; +"cycle_2weeks" = "Toutes les deux semaines"; +"cycle_4weeks" = "Toutes les quatre semaines "; +"cycle_monthly" = "Tous les mois"; +"cycle_weekday" = "À chaque même jour de la semaine"; +"cycle_yearly" = "Chaque année"; -"cycle_end_never" = "Jamais"; -"cycle_end_until" = "À la date :"; +"cycle_end_never" = "Jamais"; +"cycle_end_until" = "À la date :"; /* Appointment categories */ -"APPOINTMENT" = "Rendez-vous"; -"NOT IN OFFICE" = "À l'extérieur"; -"MEETING" = "Réunion"; -"HOLIDAY" = "Vacances"; -"PHONE CALL" = "Rendez-vous téléphonique"; +"category_NONE" = "Aucune"; +"category_ANNIVERSARY" = "Anniversaire"; +"category_BIRTHDAY" = "Anniversaire (de nais.)"; +"category_BUSINESS" = "Business"; +"category_CALLS" = "Appels"; +"category_CLIENTS" = "Clients"; +"category_COMPETITION" = "Compétition"; +"category_CUSTOMER" = "Client"; +"category_FAVORITES" = "Favoris"; +"category_FOLLOW UP" = "Suivi"; +"category_GIFTS" = "Cadeaux"; +"category_HOLIDAYS" = "Vacances"; +"category_IDEAS" = "Idées"; +"category_ISSUES" = "Problèmes"; +"category_MISCELLANEOUS" = "Divers"; +"category_PERSONAL" = "Personnel"; +"category_PROJECTS" = "Projets"; +"category_PUBLIC HOLIDAY" = "Public Holiday"; +"category_STATUS" = "Statut"; +"category_SUPPLIERS" = "Fournisseurs"; +"category_TRAVEL" = "Voyage"; +"category_VACATION" = "Absence"; + +"repeat_NEVER" = "Ne se répète pas"; +"repeat_DAILY" = "quotidiennement"; +"repeat_WEEKLY" = "hebdomadairement"; +"repeat_BI-WEEKLY" = "bi-hebdomadairement"; +"repeat_EVERY WEEKDAY" = "les jours ouvrables"; +"repeat_MONTHLY" = "mensuellement"; +"repeat_YEARLY" = "annuellement"; +"repeat_CUSTOM" = "Personnaliser..."; + +"reminder_NONE" = "Pas de rappel"; +"reminder_5_MINUTES_BEFORE" = "5 minutes avant"; +"reminder_10_MINUTES_BEFORE" = "10 minutes avant"; +"reminder_15_MINUTES_BEFORE" = "15 minutes avant"; +"reminder_30_MINUTES_BEFORE" = "30 minutes avant"; +"reminder_45_MINUTES_BEFORE" = "45 minutes avant"; +"reminder_1_HOUR_BEFORE" = "1 heure avant"; +"reminder_2_HOURS_BEFORE" = "2 heures avant"; +"reminder_5_HOURS_BEFORE" = "5 heures avant"; +"reminder_15_HOURS_BEFORE" = "15 heures avant"; +"reminder_1_DAY_BEFORE" = "1 jour avant"; +"reminder_2_DAYS_BEFORE" = "2 jours avant"; +"reminder_1_WEEK_BEFORE" = "1 semaine avant"; +"reminder_CUSTOM" = "Personnaliser..."; + +"zoom_400" = "400%"; +"zoom_200" = "200%"; +"zoom_100" = "100%"; +"zoom_50" = "50%"; +"zoom_25" = "25%"; /* validation errors */ diff --git a/UI/Scheduler/GNUmakefile b/UI/Scheduler/GNUmakefile index 8dfd4df7..b90b510e 100644 --- a/UI/Scheduler/GNUmakefile +++ b/UI/Scheduler/GNUmakefile @@ -30,9 +30,8 @@ SchedulerUI_OBJC_FILES = \ UIxCalMonthViewOld.m \ UIxAptTableView.m \ \ + UIxAttendeesEditor.m \ UIxComponentEditor.m \ - UIxFreeBusyUserSelector.m \ - UIxFreeBusyUserSelectorTable.m \ UIxAppointmentView.m \ UIxAppointmentEditor.m \ UIxAppointmentProposal.m \ @@ -59,7 +58,9 @@ SchedulerUI_RESOURCE_FILES += \ Toolbars/SOGoAppointmentObject.toolbar \ Toolbars/SOGoAppointmentObjectAccept.toolbar \ Toolbars/SOGoAppointmentObjectDecline.toolbar \ - Toolbars/SOGoAppointmentObjectAcceptOrDecline.toolbar + Toolbars/SOGoAppointmentObjectAcceptOrDecline.toolbar \ + Toolbars/SOGoTaskObject.toolbar \ + Toolbars/SOGoComponentClose.toolbar SchedulerUI_LOCALIZED_RESOURCE_FILES += \ Localizable.strings \ diff --git a/UI/Scheduler/Toolbars/SOGoAppointmentObject.toolbar b/UI/Scheduler/Toolbars/SOGoAppointmentObject.toolbar index 17e78b73..b4f11704 100644 --- a/UI/Scheduler/Toolbars/SOGoAppointmentObject.toolbar +++ b/UI/Scheduler/Toolbars/SOGoAppointmentObject.toolbar @@ -1,7 +1,19 @@ ( /* the toolbar groups -*-cperl-*- */ ( { link = "#"; - isSafe = NO; - label = "Save"; - onclick = "return saveEvent(this);"; - image = "tb-compose-save-flat-24x24.png"; } ) + label = "Save and Close"; + onclick = "return saveEvent();"; + image = "tb-compose-save-flat-24x24.png"; }, + { link = "#"; + label = "Invite Attendees"; + onclick = "return onPopupAttendeesWindow();"; + image = "tb-compose-contacts-flat-24x24.png"; }, + { link = "#"; + hasMenu = YES; + label = "Privacy"; + onclick = "return onSelectPrivacy(event);"; + image = "tb-compose-security-flat-24x24.png"; }, + { link = "#"; + label = "Documents"; + onclick = "return onPopupUrlWindow();"; + image = "tb-compose-attach-flat-24x24.png"; } ) ) diff --git a/UI/Scheduler/UIxAppointmentEditor.h b/UI/Scheduler/UIxAppointmentEditor.h index 612de9ea..7254f5d6 100644 --- a/UI/Scheduler/UIxAppointmentEditor.h +++ b/UI/Scheduler/UIxAppointmentEditor.h @@ -1,6 +1,6 @@ /* UIxAppointmentEditor.h - this file is part of SOGo * - * Copyright (C) 2006 Inverse groupe conseil + * Copyright (C) 2007 Inverse groupe conseil * * Author: Wolfgang Sourdeau * @@ -23,50 +23,38 @@ #ifndef UIXAPPOINTMENTEDITOR_H #define UIXAPPOINTMENTEDITOR_H -#import +#import +@class iCalEvent; @class NSString; -@class iCalPerson; -@class iCalRecurrenceRule; -@interface UIxAppointmentEditor : UIxComponentEditor +@interface UIxAppointmentEditor : UIxComponent { - NSCalendarDate *endDate; + iCalEvent *event; + NSCalendarDate *aptStartDate; + NSCalendarDate *aptEndDate; + NSString *item; } +/* template values */ +- (NSString *) saveURL; +- (iCalEvent *) event; + +/* icalendar values */ +- (BOOL) isAllDay; +- (void) setIsAllDay: (BOOL) newIsAllDay; + - (void) setAptStartDate: (NSCalendarDate *) _date; - (NSCalendarDate *) aptStartDate; - (void) setAptEndDate: (NSCalendarDate *) _date; - (NSCalendarDate *) aptEndDate; -/* iCal */ - -- (NSString *) iCalStringTemplate; - -/* new */ - -- (id) newAction; - -/* save */ - -- (void) loadValuesFromAppointment: (iCalEvent *) _apt; -- (void) saveValuesIntoAppointment: (iCalEvent *) _apt; -- (iCalEvent *) appointmentFromString: (NSString *) _iCalString; - -/* conflict management */ - -- (BOOL) containsConflict: (id) _apt; -- (id ) defaultAction; -- (id ) saveAction; -- (id) acceptAction; -- (id) declineAction; - -- (NSString *) saveUrl; - -// TODO: add tentatively +- (NSString *) repeat; +- (void) setRepeat: (NSString *) newRepeat; -- (id) acceptOrDeclineAction: (BOOL) _accept; +- (NSString *) reminder; +- (void) setReminder: (NSString *) newReminder; @end diff --git a/UI/Scheduler/UIxAppointmentEditor.m b/UI/Scheduler/UIxAppointmentEditor.m index da2fda63..17c12eaa 100644 --- a/UI/Scheduler/UIxAppointmentEditor.m +++ b/UI/Scheduler/UIxAppointmentEditor.m @@ -1,458 +1,387 @@ -/* - Copyright (C) 2004-2005 SKYRIX Software AG - - This file is part of OpenGroupware.org. - - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - OGo is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ +/* UIxAppointmentEditor.m - this file is part of SOGo + * + * Copyright (C) 2007 Inverse groupe conseil + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import + +#import +#import + +#import +#import +#import +#import +#import + +#import "UIxComponentEditor.h" +#import "UIxAppointmentEditor.h" -#import -#import +@implementation UIxAppointmentEditor -#import -#import +- (id) init +{ + if ((self = [super init])) + { + aptStartDate = nil; + aptEndDate = nil; + item = nil; + event = nil; + } -#import "common.h" -#import -#import -#import -#import -#import -#import "UIxComponent+Agenor.h" + return self; +} -#import "UIxAppointmentEditor.h" +/* template values */ +- (iCalEvent *) event +{ + return event; +} -/* TODO: CLEAN UP */ +- (NSString *) saveURL +{ + return [NSString stringWithFormat: @"%@/saveAsAppointment", + [[self clientObject] baseURL]]; +} -@implementation UIxAppointmentEditor +- (NSString *) _toolbarForCalObject +{ + SOGoUser *currentUser; + SOGoAppointmentObject *clientObject; + NSString *filename, *email; + iCalPerson *person; + iCalPersonPartStat participationStatus; + + clientObject = [self clientObject]; + currentUser = [[self context] activeUser]; + email = [currentUser email]; + if ([clientObject isOrganizer: email + orOwner: [currentUser login]]) + filename = @"SOGoAppointmentObject.toolbar"; + else + { + if ([clientObject isParticipant: email]) + { + person = [[clientObject component: NO] findParticipantWithEmail: email]; + participationStatus = [person participationStatus]; + if (participationStatus == iCalPersonPartStatAccepted) + filename = @"SOGoAppointmentObjectDecline.toolbar"; + else if (participationStatus == iCalPersonPartStatDeclined) + filename = @"SOGoAppointmentObjectAccept.toolbar"; + else + filename = @"SOGoAppointmentObjectAcceptOrDecline.toolbar"; + } + else + filename = @"SOGoComponentClose.toolbar"; + } -+ (int)version { - return [super version] + 0 /* v2 */; + return filename; } -+ (void)initialize { - NSAssert2([super version] == 2, - @"invalid superclass (%@) version %i !", - NSStringFromClass([self superclass]), [super version]); +- (NSString *) toolbar +{ + return ([self _toolbarForCalObject]); } -- (void) dealloc +/* icalendar values */ +- (BOOL) isAllDay { - [endDate release]; - [super dealloc]; + return NO; } -/* accessors */ +- (void) setIsAllDay: (BOOL) newIsAllDay +{ +} -- (void) setAptStartDate: (NSCalendarDate *)_date +- (void) setAptStartDate: (NSCalendarDate *) newAptStartDate { - [self setStartDate: _date]; + ASSIGN (aptStartDate, newAptStartDate); } - (NSCalendarDate *) aptStartDate { - return [self startDate]; + return aptStartDate; } -- (void) setAptEndDate: (NSCalendarDate *) _date +- (void) setAptEndDate: (NSCalendarDate *) newAptEndDate { - ASSIGN(endDate, _date); + ASSIGN (aptEndDate, newAptEndDate); } - (NSCalendarDate *) aptEndDate { - return endDate; + return aptEndDate; } -/* transparency */ - -- (NSString *) transparency +- (NSArray *) repeatList { - return @"OPAQUE"; + static NSArray *repeatItems = nil; + + if (!repeatItems) + { + repeatItems = [NSArray arrayWithObjects: @"DAILY", + @"WEEKLY", + @"BI-WEEKLY", + @"EVERY WEEKDAY", + @"MONTHLY", + @"YEARLY", + @"-", + @"CUSTOM", + nil]; + [repeatItems retain]; + } + + return repeatItems; } -/* iCal */ - -- (NSString *)iCalStringTemplate { - static NSString *iCalStringTemplate = \ - @"BEGIN:VCALENDAR\r\n" - @"METHOD:REQUEST\r\n" - @"PRODID://Inverse groupe conseil/SOGo 0.9\r\n" - @"VERSION:2.0\r\n" - @"BEGIN:VEVENT\r\n" - @"UID:%@\r\n" - @"CLASS:PUBLIC\r\n" - @"STATUS:CONFIRMED\r\n" /* confirmed by default */ - @"DTSTAMP:%@Z\r\n" - @"DTSTART:%@Z\r\n" - @"DTEND:%@Z\r\n" - @"TRANSP:%@\r\n" - @"SEQUENCE:1\r\n" - @"PRIORITY:5\r\n" - @"%@" /* organizer */ - @"%@" /* participants and resources */ - @"END:VEVENT\r\n" - @"END:VCALENDAR"; - - NSTimeZone *utc; - NSCalendarDate *lStartDate, *lEndDate, *stamp; - NSString *template, *s; - unsigned minutes; - - s = [self queryParameterForKey:@"dur"]; - if ([s length] > 0) - minutes = [s intValue]; +- (NSString *) itemRepeatText +{ + NSString *text; + + if ([item isEqualToString: @"-"]) + text = item; else - minutes = 60; - - utc = [NSTimeZone timeZoneWithName: @"GMT"]; - lStartDate = [self newStartDate]; - [lStartDate setTimeZone: utc]; - lEndDate = [lStartDate dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: minutes seconds: 0]; - - stamp = [NSCalendarDate calendarDate]; - [stamp setTimeZone: utc]; - - s = [self iCalParticipantsAndResourcesStringFromQueryParameters]; - template = [NSString stringWithFormat: iCalStringTemplate, - [[self clientObject] nameInContainer], - [stamp iCalFormattedDateTimeString], - [lStartDate iCalFormattedDateTimeString], - [lEndDate iCalFormattedDateTimeString], - [self transparency], - [self iCalOrganizerString], - s]; - return template; + text = [self labelForKey: [NSString stringWithFormat: @"repeat_%@", item]]; + + return text; } -/* new */ +- (void) setItem: (NSString *) newItem +{ + item = newItem; +} + +- (NSString *) item +{ + return item; +} -- (id) newAction +- (NSArray *) reminderList { - /* - This method creates a unique ID and redirects to the "edit" method on the - new ID. - It is actually a folder method and should be defined on the folder. - - Note: 'clientObject' is the SOGoAppointmentFolder! - Update: remember that there are group folders as well. - */ - NSString *uri, *objectId, *method, *ps; - - objectId = [NSClassFromString(@"SOGoAppointmentFolder") - globallyUniqueObjectId]; - if ([objectId length] == 0) { - return [NSException exceptionWithHTTPStatus:500 /* Internal Error */ - reason:@"could not create a unique ID"]; - } - - method = [NSString stringWithFormat:@"Calendar/%@/editAsAppointment", objectId]; - method = [[self userFolderPath] stringByAppendingPathComponent:method]; - - /* check if participants have already been provided */ - ps = [self queryParameterForKey:@"ps"]; -// if (ps) { -// [self setQueryParameter:ps forKey:@"ps"]; -// } - if (!ps - && [[self clientObject] respondsToSelector:@selector(calendarUIDs)]) { - AgenorUserManager *um; - NSArray *uids; - NSMutableArray *emails; - unsigned i, count; - - /* add all current calendarUIDs as default participants */ - - um = [AgenorUserManager sharedUserManager]; - uids = [[self clientObject] calendarUIDs]; - count = [uids count]; - emails = [NSMutableArray arrayWithCapacity:count]; - - for (i = 0; i < count; i++) { - NSString *email; - - email = [um getEmailForUID:[uids objectAtIndex:i]]; - if (email) - [emails addObject:email]; + static NSArray *reminderItems = nil; + + if (!reminderItems) + { + reminderItems = [NSArray arrayWithObjects: @"5_MINUTES_BEFORE", + @"10_MINUTES_BEFORE", + @"15_MINUTES_BEFORE", + @"30_MINUTES_BEFORE", + @"45_MINUTES_BEFORE", + @"-", + @"1_HOUR_BEFORE", + @"2_HOURS_BEFORE", + @"5_HOURS_BEFORE", + @"15_HOURS_BEFORE", + @"-", + @"1_DAY_BEFORE", + @"2_DAYS_BEFORE", + @"1_WEEK_BEFORE", + @"-", + @"CUSTOM", + nil]; + [reminderItems retain]; } - ps = [emails componentsJoinedByString:@","]; - [self setQueryParameter:ps forKey:@"ps"]; - } - uri = [self completeHrefForMethod:method]; - return [self redirectToLocation:uri]; + + return reminderItems; } -/* save */ +// - (void) setReminder: (NSString *) reminder +// { +// ASSIGN(reminder, _reminder); +// } -- (void) loadValuesFromAppointment: (iCalEvent *) appointment -{ - NSTimeZone *uTZ; +// - (NSString *) reminder +// { +// return reminder; +// } - [self loadValuesFromComponent: appointment]; +- (NSString *) itemReminderText +{ + NSString *text; - uTZ = [[self clientObject] userTimeZone]; - endDate = [appointment endDate]; - if (!endDate) - endDate = [[self startDate] dateByAddingYears: 0 months: 0 days: 0 - hours: 1 minutes: 0 seconds: 0]; + if ([item isEqualToString: @"-"]) + text = item; + else + text = [self labelForKey: [NSString stringWithFormat: @"reminder_%@", item]]; - [endDate setTimeZone: uTZ]; - [endDate retain]; + return text; } -- (void) saveValuesIntoAppointment: (iCalEvent *) _appointment +- (NSString *) repeat { - /* merge in form values */ - NSArray *attendees, *lResources; - iCalRecurrenceRule *rrule; - - [_appointment setStartDate:[self aptStartDate]]; - [_appointment setEndDate:[self aptEndDate]]; - - [_appointment setSummary: [self title]]; - [_appointment setUrl: [self url]]; - [_appointment setLocation: [self location]]; - [_appointment setComment: [self comment]]; - [_appointment setPriority:[self priority]]; - [_appointment setAccessClass: [self privacy]]; - [_appointment setStatus: [self status]]; - -// [_appointment setCategories: [[self categories] componentsJoinedByString: @","]]; - - [_appointment setTransparency: [self transparency]]; - -#if 0 - /* - Note: bad, bad, bad! - Organizer is no form value, thus we MUST NOT change it - */ - [_appointment setOrganizer:organizer]; -#endif - attendees = [self participants]; - lResources = [self resources]; - if ([lResources count] > 0) { - attendees = ([attendees count] > 0) - ? [attendees arrayByAddingObjectsFromArray: lResources] - : lResources; - } - [attendees makeObjectsPerformSelector: @selector (setTag:) - withObject: @"attendee"]; - [_appointment setAttendees: attendees]; - - /* cycles */ - [_appointment removeAllRecurrenceRules]; - rrule = [self rrule]; - if (rrule) - [_appointment addToRecurrenceRules: rrule]; + return @""; } -- (iCalEvent *) appointmentFromString: (NSString *) _iCalString +- (void) setRepeat: (NSString *) newRepeat { - iCalCalendar *calendar; - iCalEvent *appointment; - - calendar = [iCalCalendar parseSingleFromSource: _iCalString]; - appointment = (iCalEvent *) [calendar firstChildWithTag: @"vevent"]; - - return appointment; } -/* conflict management */ +- (NSString *) reminder +{ + return @""; +} -- (BOOL) containsConflict: (id) _apt +- (void) setReminder: (NSString *) newReminder { - NSArray *attendees, *uids; - SOGoAppointmentFolder *groupCalendar; - NSArray *infos; - NSArray *ranges; - id folder; - - [self logWithFormat:@"search from %@ to %@", - [_apt startDate], [_apt endDate]]; - - folder = [[self clientObject] container]; - attendees = [_apt attendees]; - uids = [folder uidsFromICalPersons:attendees]; - if ([uids count] == 0) { - [self logWithFormat:@"Note: no UIDs selected."]; - return NO; - } - - groupCalendar = [folder lookupGroupCalendarFolderForUIDs:uids - inContext:[self context]]; - [self debugWithFormat:@"group calendar: %@", groupCalendar]; - - if (![groupCalendar respondsToSelector:@selector(fetchFreeBusyInfosFrom:to:)]) { - [self errorWithFormat:@"invalid folder to run freebusy query on!"]; - return NO; - } - - infos = [groupCalendar fetchFreeBusyInfosFrom:[_apt startDate] - to:[_apt endDate]]; - [self debugWithFormat:@" process: %d events", [infos count]]; - - ranges = [infos arrayByCreatingDateRangesFromObjectsWithStartDateKey: @"startDate" - andEndDateKey: @"endDate"]; - ranges = [ranges arrayByCompactingContainedDateRanges]; - [self debugWithFormat:@" blocked ranges: %@", ranges]; - - return [ranges count] != 0 ? YES : NO; } /* actions */ - -- (id) testAction +- (NSCalendarDate *) newStartDate { - /* for testing only */ - WORequest *req; - iCalEvent *apt; - NSString *content; - - req = [[self context] request]; - apt = [self appointmentFromString: [self iCalString]]; - [self saveValuesIntoAppointment:apt]; - content = [[apt parent] versitString]; - [self logWithFormat:@"%s -- iCal:\n%@", - __PRETTY_FUNCTION__, - content]; + NSCalendarDate *newStartDate, *now; + int hour; + + newStartDate = [self selectedDate]; + if ([[self queryParameterForKey: @"hm"] length] == 0) + { + now = [NSCalendarDate calendarDate]; + [now setTimeZone: [[self clientObject] userTimeZone]]; + if ([now isDateOnSameDay: newStartDate]) + { + hour = [now hourOfDay]; + if (hour < 8) + newStartDate = [now hour: 8 minute: 0]; + else if (hour > 18) + newStartDate = [[now tomorrow] hour: 8 minute: 0]; + else + newStartDate = now; + } + else + newStartDate = [newStartDate hour: 8 minute: 0]; + } - return self; + return newStartDate; } -- (id) defaultAction +- (id ) defaultAction { - NSString *ical; - - /* load iCalendar file */ - - // TODO: can't we use [clientObject contentAsString]? -// ical = [[self clientObject] valueForKey:@"iCalString"]; - ical = [[self clientObject] contentAsString]; - if ([ical length] == 0) /* a new appointment */ - ical = [self iCalStringTemplate]; - - [self setICalString:ical]; - [self loadValuesFromAppointment: [self appointmentFromString: ical]]; - -// if (![self canEditComponent]) { -// /* TODO: we need proper ACLs */ -// return [self redirectToLocation: [self completeURIForMethod: @"../view"]]; -// } + NSCalendarDate *startDate, *endDate; + NSString *duration; + unsigned int minutes; + + event = (iCalEvent *) [[self clientObject] component: NO]; + if (event) + { + startDate = [event startDate]; + endDate = [event endDate]; + } + else + { + startDate = [self newStartDate]; + duration = [self queryParameterForKey:@"dur"]; + if ([duration length] > 0) + minutes = [duration intValue]; + else + minutes = 60; + endDate + = [startDate dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: minutes seconds: 0]; + } + + ASSIGN (aptStartDate, startDate); + ASSIGN (aptEndDate, endDate); + + /* here comes the code for initializing repeat, reminder and isAllDay... */ + return self; } -- (id ) saveAction +- (id ) newAction { - iCalEvent *apt; - iCalPerson *p; + NSString *objectId, *method, *uri; id result; - NSString *content; - NSException *ex; - - if (![self isWriteableClientObject]) { - /* return 400 == Bad Request */ - return [NSException exceptionWithHTTPStatus:400 - reason:@"method cannot be invoked on " - @"the specified object"]; - } - - apt = [self appointmentFromString: [self iCalString]]; - if (apt == nil) { - NSString *s; - - s = [self labelForKey:@"Invalid iCal data!"]; - [self setErrorText:s]; - return self; - } - - [self saveValuesIntoAppointment:apt]; - p = [apt findParticipantWithEmail:[self emailForUser]]; - if (p) { - [p setParticipationStatus:iCalPersonPartStatAccepted]; - } - - if ([self checkForConflicts]) { - if ([self containsConflict:apt]) { - NSString *s; - - s = [self labelForKey:@"Conflicts found!"]; - [self setErrorText:s]; - - return self; + Class clientKlazz; + + clientKlazz = [[self clientObject] class]; + objectId = [clientKlazz globallyUniqueObjectId]; + if ([objectId length] > 0) + { + method = [NSString stringWithFormat:@"%@/Calendar/%@/editAsAppointment", + [self userFolderPath], objectId]; + uri = [self completeHrefForMethod: method]; + result = [self redirectToLocation: uri]; } - } - content = [[apt parent] versitString]; -// [apt release]; apt = nil; - - if (content == nil) { - NSString *s; - - s = [self labelForKey:@"Could not create iCal data!"]; - [self setErrorText:s]; - return self; - } - - ex = [[self clientObject] saveContentString:content]; - if (ex != nil) { - [self setErrorText:[ex reason]]; - return self; - } - - if ([[[[self context] request] formValueForKey: @"nojs"] intValue]) - result = [self redirectToLocation: [self applicationPath]]; else - result = [self jsCloseWithRefreshMethod: @"refreshAppointmentsAndDisplay()"]; + result = [NSException exceptionWithHTTPStatus: 500 /* Internal Error */ + reason: @"could not create a unique ID"]; return result; } -- (NSString *) saveUrl +- (id ) saveAction { - return [NSString stringWithFormat: @"%@/saveAsAppointment", - [[self clientObject] baseURL]]; + SOGoAppointmentObject *clientObject; + NSString *iCalString; + + clientObject = [self clientObject]; + iCalString = [[clientObject calendar: NO] versitString]; + [clientObject saveContentString: iCalString]; + + return [self jsCloseWithRefreshMethod: @"refreshAppointmentsAndDisplay()"]; } -- (id) acceptAction +- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request + inContext: (WOContext*) context { - return [self acceptOrDeclineAction:YES]; + return ([[self clientObject] isKindOfClass: [SOGoAppointmentObject class]] + && [[request method] isEqualToString: @"POST"]); } -- (id) declineAction +- (void) takeValuesFromRequest: (WORequest *) _rq + inContext: (WOContext *) _ctx { - return [self acceptOrDeclineAction:NO]; + SOGoAppointmentObject *clientObject; + + clientObject = [self clientObject]; + event = (iCalEvent *) [clientObject component: YES]; + + [super takeValuesFromRequest: _rq inContext: _ctx]; + + [event setStartDate: aptStartDate]; + [event setEndDate: aptEndDate]; + if ([clientObject isNew]) + [event setTransparency: @"OPAQUE"]; } // TODO: add tentatively - (id) acceptOrDeclineAction: (BOOL) _accept { - // TODO: this should live in the SoObjects - NSException *ex; - - if ((ex = [self validateObjectForStatusChange]) != nil) - return ex; - - ex = [[self clientObject] changeParticipationStatus: - _accept ? @"ACCEPTED" : @"DECLINED" - inContext:[self context]]; - if (ex != nil) return ex; - - return self; -// return [self redirectToLocation: [self completeURIForMethod: @"../view"]]; + [[self clientObject] changeParticipationStatus: + _accept ? @"ACCEPTED" : @"DECLINED" + inContext: [self context]]; + + return self; +} + +- (id) acceptAction +{ + return [self acceptOrDeclineAction:YES]; +} + +- (id) declineAction +{ + return [self acceptOrDeclineAction:NO]; } -@end /* UIxAppointmentEditor */ +@end diff --git a/UI/Scheduler/UIxAppointmentView.m b/UI/Scheduler/UIxAppointmentView.m index 14b3b6ea..d1aeb426 100644 --- a/UI/Scheduler/UIxAppointmentView.m +++ b/UI/Scheduler/UIxAppointmentView.m @@ -167,7 +167,7 @@ if (!appointment) { clientObject = [self clientObject]; - appointment = (iCalEvent *) [clientObject component]; + appointment = (iCalEvent *) [clientObject component: NO]; [appointment retain]; } diff --git a/UI/Scheduler/UIxComponentEditor.h b/UI/Scheduler/UIxComponentEditor.h index b240e9e9..f6798187 100644 --- a/UI/Scheduler/UIxComponentEditor.h +++ b/UI/Scheduler/UIxComponentEditor.h @@ -37,33 +37,36 @@ @interface UIxComponentEditor : UIxComponent { - NSString *iCalString; - NSString *errorText; + iCalRepeatableEntityObject *component; id item; + + NSString *saveURL; /* individual values */ - NSCalendarDate *startDate; NSCalendarDate *cycleUntilDate; NSString *title; NSString *location; NSString *comment; NSString *url; - iCalPerson *organizer; - NSArray *participants; /* array of iCalPerson's */ - NSArray *resources; /* array of iCalPerson's */ NSString *priority; NSString *privacy; NSString *status; NSArray *categories; - BOOL checkForConflicts; /* default: NO */ NSDictionary *cycle; NSString *cycleEnd; + iCalPerson *organizer; NSString *componentOwner; - BOOL componentLoaded; + NSString *attendeesNames; + NSString *attendeesEmails; } -- (NSArray *) categoryItems; +- (void) setComponent: (iCalRepeatableEntityObject *) newComponent; + +- (void) setSaveURL: (NSString *) newSaveURL; +- (NSString *) saveURL; + +- (NSArray *) categoryList; - (void) setCategories: (NSArray *) _categories; - (NSArray *) categories; - (NSString *) itemCategoryText; @@ -87,18 +90,6 @@ - (id) item; - (NSString *) itemPriorityText; -- (void) setErrorText: (NSString *) _txt; -- (NSString *) errorText; -- (BOOL) hasErrorText; - -- (void) setICalString: (NSString *) _s; -- (NSString *) iCalString; - -- (NSCalendarDate *) newStartDate; - -- (void) setStartDate: (NSCalendarDate *) _date; -- (NSCalendarDate *) startDate; - - (void) setTitle: (NSString *) _value; - (NSString *) title; @@ -111,14 +102,11 @@ - (void) setUrl: (NSString *) _url; - (NSString *) url; -- (void) setParticipants: (NSArray *) _parts; -- (NSArray *) participants; +- (void) setAttendeesNames: (NSString *) newAttendeesNames; +- (NSString *) attendeesNames; -- (void) setResources: (NSArray *) _res; -- (NSArray *) resources; - -- (void) setCheckForConflicts: (BOOL) _checkForConflicts; -- (BOOL) checkForConflicts; +- (void) setAttendeesEmails: (NSString *) newAttendeesEmails; +- (NSString *) attendeesEmails; - (NSArray *) cycles; - (void) setCycle: (NSDictionary *) _cycle; @@ -150,17 +138,12 @@ - (BOOL) isWriteableClientObject; - (NSException *) validateObjectForStatusChange; -/* subclasses */ -- (void) loadValuesFromComponent: (iCalRepeatableEntityObject *) component; - -- (NSString *) iCalStringTemplate; - (NSString *) iCalParticipantsAndResourcesStringFromQueryParameters; - (NSString *) iCalParticipantsStringFromQueryParameters; - (NSString *) iCalResourcesStringFromQueryParameters; - (NSString *) iCalStringFromQueryParameter: (NSString *) _qp format: (NSString *) _format; - (NSString *) iCalOrganizerString; -- (NSString *) toolbar; @end diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index 41eb62bc..03479b99 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -55,11 +55,13 @@ { if ((self = [super init])) { + component = nil; [self setPrivacy: @"PUBLIC"]; - [self setCheckForConflicts: NO]; [self setIsCycleEndNever]; componentOwner = @""; - componentLoaded = NO; + organizer = nil; + attendeesNames = nil; + attendeesEmails = nil; } return self; @@ -67,31 +69,102 @@ - (void) dealloc { - [iCalString release]; - [errorText release]; [item release]; - [startDate release]; [cycleUntilDate release]; [title release]; [location release]; [organizer release]; [comment release]; - [participants release]; - [resources release]; [priority release]; [categories release]; [cycle release]; [cycleEnd release]; [url release]; + [attendeesNames release]; + [attendeesEmails release]; [super dealloc]; } +- (void) _loadAttendees +{ + NSEnumerator *attendees; + iCalPerson *currentAttendee; + NSMutableString *names, *emails; + + names = [NSMutableString new]; + emails = [NSMutableString new]; + + attendees = [[component attendees] objectEnumerator]; + currentAttendee = [attendees nextObject]; + while (currentAttendee) + { + NSLog (@"currentCN: %@", [currentAttendee cn]); + [names appendFormat: @"%@,", [currentAttendee cn]]; + [emails appendFormat: @"%@,", [currentAttendee rfc822Email]]; + currentAttendee = [attendees nextObject]; + } + + if ([names length] > 0) + { + ASSIGN (attendeesNames, [names substringToIndex: [names length] - 1]); + ASSIGN (attendeesEmails, + [emails substringToIndex: [emails length] - 1]); + } + + [names release]; + [emails release]; +} + +/* warning: we use this method which will be triggered by the template system + when the page is instantiated, but we should find another and cleaner way of + doing this... for example, when the clientObject is set */ +- (void) setComponent: (iCalRepeatableEntityObject *) newComponent +{ +// iCalRecurrenceRule *rrule; + SOGoObject *co; + + if (!component) + { + component = newComponent; + + co = [self clientObject]; + componentOwner = [co ownerInContext: nil]; + + ASSIGN (title, [component summary]); + ASSIGN (location, [component location]); + ASSIGN (comment, [component comment]); + ASSIGN (url, [[component url] absoluteString]); + ASSIGN (privacy, [component accessClass]); + ASSIGN (priority, [component priority]); + ASSIGN (status, [component status]); + ASSIGN (categories, [[component categories] commaSeparatedValues]); + ASSIGN (organizer, [component organizer]); + [self _loadAttendees]; + } +// /* cycles */ +// if ([component isRecurrent]) +// { +// rrule = [[component recurrenceRules] objectAtIndex: 0]; +// [self adjustCycleControlsForRRule: rrule]; +// } +} + +- (void) setSaveURL: (NSString *) newSaveURL +{ + saveURL = newSaveURL; +} + +- (NSString *) saveURL +{ + return saveURL; +} + /* accessors */ - (void) setItem: (id) _item { - ASSIGN(item, _item); + ASSIGN (item, _item); } - (id) item @@ -114,54 +187,49 @@ return [self labelForKey: [NSString stringWithFormat: @"status_%@", item]]; } -- (void) setErrorText: (NSString *) _txt -{ - ASSIGNCOPY(errorText, _txt); -} - -- (NSString *) errorText +- (void) setTitle: (NSString *) _value { - return errorText; + ASSIGN (title, _value); } -- (BOOL) hasErrorText +- (NSString *) title { - return [errorText length] > 0 ? YES : NO; + return title; } -- (void) setStartDate: (NSCalendarDate *) _date +- (void) setUrl: (NSString *) _url { - ASSIGN(startDate, _date); + ASSIGN (url, _url); } -- (NSCalendarDate *) startDate +- (NSString *) url { - return startDate; + return url; } -- (void) setTitle: (NSString *) _value +- (void) setAttendeesNames: (NSString *) newAttendeesNames { - ASSIGNCOPY(title, _value); + ASSIGN (attendeesNames, newAttendeesNames); } -- (NSString *) title +- (NSString *) attendeesNames { - return title; + return attendeesNames; } -- (void) setUrl: (NSString *) _url +- (void) setAttendeesEmails: (NSString *) newAttendeesEmails { - ASSIGNCOPY(url, _url); + ASSIGN (attendeesEmails, newAttendeesEmails); } -- (NSString *) url +- (NSString *) attendeesEmails { - return url; + return attendeesEmails; } - (void) setLocation: (NSString *) _value { - ASSIGNCOPY(location, _value); + ASSIGN (location, _value); } - (NSString *) location @@ -171,7 +239,7 @@ - (void) setComment: (NSString *) _value { - ASSIGNCOPY(comment, _value); + ASSIGN (comment, _value); } - (NSString *) comment @@ -179,28 +247,34 @@ return comment; } -- (NSArray *) categoryItems +- (NSArray *) categoryList { - // TODO: make this configurable? - /* - Tasks categories will be modified as follow : - – by default (a simple logo or no logo at all), - – task, - – outside, - – meeting, - – holidays, - – phone. - */ static NSArray *categoryItems = nil; - + if (!categoryItems) { - categoryItems = [NSArray arrayWithObjects: @"APPOINTMENT", - @"NOT IN OFFICE", - @"MEETING", - @"HOLIDAY", - @"PHONE CALL", - nil]; + categoryItems = [NSArray arrayWithObjects: @"ANNIVERSARY", + @"BIRTHDAY", + @"BUSINESS", + @"CALLS", + @"CLIENTS", + @"COMPETITION", + @"CUSTOMER", + @"FAVORITES", + @"FOLLOW UP", + @"GIFTS", + @"HOLIDAYS", + @"IDEAS", + @"ISSUES", + @"MISCELLANEOUS", + @"PERSONAL", + @"PROJECTS", + @"PUBLIC HOLIDAY", + @"STATUS", + @"SUPPLIERS", + @"TRAVEL", + @"VACATION", + nil]; [categoryItems retain]; } @@ -209,7 +283,7 @@ - (void) setCategories: (NSArray *) _categories { - ASSIGN(categories, _categories); + ASSIGN (categories, _categories); } - (NSArray *) categories @@ -219,7 +293,7 @@ - (NSString *) itemCategoryText { - return [[self labelForKey: item] stringByEscapingHTMLString]; + return [self labelForKey: [NSString stringWithFormat: @"category_%@", item]]; } /* priorities */ @@ -234,7 +308,7 @@ if (!priorities) { - priorities = [NSArray arrayWithObjects:@"0", @"5", @"1", nil]; + priorities = [NSArray arrayWithObjects: @"0", @"5", @"1", nil]; [priorities retain]; } @@ -243,7 +317,7 @@ - (void) setPriority: (NSString *) _priority { - ASSIGN(priority, _priority); + ASSIGN (priority, _priority); } - (NSString *) priority @@ -267,7 +341,7 @@ - (void) setPrivacy: (NSString *) _privacy { - ASSIGN(privacy, _privacy); + ASSIGN (privacy, _privacy); } - (NSString *) privacy @@ -290,7 +364,7 @@ - (void) setStatus: (NSString *) _status { - ASSIGN(status, _status); + ASSIGN (status, _status); } - (NSString *) status @@ -298,36 +372,6 @@ return status; } -- (void) setParticipants: (NSArray *) _parts -{ - ASSIGN(participants, _parts); -} - -- (NSArray *) participants -{ - return participants; -} - -- (void) setResources: (NSArray *) _res -{ - ASSIGN(resources, _res); -} - -- (NSArray *) resources -{ - return resources; -} - -- (void) setCheckForConflicts: (BOOL) _checkForConflicts -{ - checkForConflicts = _checkForConflicts; -} - -- (BOOL) checkForConflicts -{ - return checkForConflicts; -} - - (NSArray *) cycles { NSBundle *bundle; @@ -337,7 +381,7 @@ if (!cycles) { bundle = [NSBundle bundleForClass:[self class]]; - path = [bundle pathForResource:@"cycles" ofType:@"plist"]; + path = [bundle pathForResource: @"cycles" ofType: @"plist"]; NSAssert(path != nil, @"Cannot find cycles.plist!"); cycles = [[NSArray arrayWithContentsOfFile:path] retain]; NSAssert(cycles != nil, @"Cannot instantiate cycles from cycles.plist!"); @@ -348,7 +392,7 @@ - (void) setCycle: (NSDictionary *) _cycle { - ASSIGN(cycle, _cycle); + ASSIGN (cycle, _cycle); } - (NSDictionary *) cycle @@ -365,21 +409,21 @@ { NSString *key; - key = [(NSDictionary *)item objectForKey:@"label"]; + key = [(NSDictionary *)item objectForKey: @"label"]; return [self labelForKey:key]; } - (void) setCycleUntilDate: (NSCalendarDate *) _cycleUntilDate { - NSCalendarDate *until; +// NSCalendarDate *until; - /* copy hour/minute/second from startDate */ - until = [_cycleUntilDate hour: [startDate hourOfDay] - minute: [startDate minuteOfHour] - second: [startDate secondOfMinute]]; - [until setTimeZone: [startDate timeZone]]; - ASSIGN(cycleUntilDate, until); +// /* copy hour/minute/second from startDate */ +// until = [_cycleUntilDate hour: [startDate hourOfDay] +// minute: [startDate minuteOfHour] +// second: [startDate secondOfMinute]]; +// [until setTimeZone: [startDate timeZone]]; +// ASSIGN (cycleUntilDate, until); } - (NSCalendarDate *) cycleUntilDate @@ -394,7 +438,7 @@ if (![self hasCycle]) return nil; - ruleRep = [cycle objectForKey:@"rule"]; + ruleRep = [cycle objectForKey: @"rule"]; rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:ruleRep]; if (cycleUntilDate && [self isCycleEndUntil]) @@ -405,20 +449,20 @@ - (void) adjustCycleControlsForRRule: (iCalRecurrenceRule *) _rrule { - NSDictionary *c; - NSCalendarDate *until; +// NSDictionary *c; +// NSCalendarDate *until; - c = [self cycleMatchingRRule:_rrule]; - [self setCycle:c]; +// c = [self cycleMatchingRRule:_rrule]; +// [self setCycle:c]; - until = [[[_rrule untilDate] copy] autorelease]; - if (!until) - until = startDate; - else - [self setIsCycleEndUntil]; +// until = [[[_rrule untilDate] copy] autorelease]; +// if (!until) +// until = startDate; +// else +// [self setIsCycleEndUntil]; - [until setTimeZone:[[self clientObject] userTimeZone]]; - [self setCycleUntilDate:until]; +// [until setTimeZone:[[self clientObject] userTimeZone]]; +// [self setCycleUntilDate:until]; } /* @@ -448,11 +492,11 @@ NSString *cr; c = [cycles objectAtIndex:i]; - cr = [c objectForKey:@"rule"]; + cr = [c objectForKey: @"rule"]; if ([cr isEqualToString:cycleRep]) return c; } - [self warnWithFormat:@"No default cycle for rrule found! -> %@", _rrule]; + [self warnWithFormat: @"No default cycle for rrule found! -> %@", _rrule]; return nil; } @@ -473,7 +517,7 @@ - (void) setCycleEnd: (NSString *) _cycleEnd { - ASSIGNCOPY(cycleEnd, _cycleEnd); + ASSIGN (cycleEnd, _cycleEnd); } - (NSString *) cycleEnd @@ -483,18 +527,17 @@ - (BOOL) isCycleEndUntil { - return (cycleEnd && - [cycleEnd isEqualToString:@"cycle_end_until"]); + return (cycleEnd && [cycleEnd isEqualToString: @"cycle_end_until"]); } - (void) setIsCycleEndUntil { - [self setCycleEnd:@"cycle_end_until"]; + [self setCycleEnd: @"cycle_end_until"]; } - (void) setIsCycleEndNever { - [self setCycleEnd:@"cycle_end_never"]; + [self setCycleEnd: @"cycle_end_never"]; } /* helpers */ @@ -517,13 +560,13 @@ uri = [[[self context] request] uri]; /* first: identify query parameters */ - r = [uri rangeOfString:@"?" options:NSBackwardsSearch]; + r = [uri rangeOfString: @"?" options:NSBackwardsSearch]; if (r.length > 0) uri = [uri substringToIndex:r.location]; /* next: append trailing slash */ - if (![uri hasSuffix:@"/"]) - uri = [uri stringByAppendingString:@"/"]; + if (![uri hasSuffix: @"/"]) + uri = [uri stringByAppendingString: @"/"]; /* next: append method */ uri = [uri stringByAppendingString:_method]; @@ -535,13 +578,7 @@ - (BOOL) isWriteableClientObject { return [[self clientObject] - respondsToSelector:@selector(saveContentString:)]; -} - -- (BOOL) shouldTakeValuesFromRequest: (WORequest *) _rq - inContext: (WOContext*) _c -{ - return YES; + respondsToSelector: @selector(saveContentString:)]; } - (BOOL) containsConflict: (id) _component @@ -593,81 +630,6 @@ : @"visibility: hidden;"); } -/* subclasses */ -- (NSCalendarDate *) newStartDate -{ - NSCalendarDate *newStartDate, *now; - int hour; - - newStartDate = [self selectedDate]; - if ([[self queryParameterForKey: @"hm"] length] == 0) - { - now = [NSCalendarDate calendarDate]; - [now setTimeZone: [[self clientObject] userTimeZone]]; - if ([now isDateOnSameDay: newStartDate]) - { - hour = [now hourOfDay]; - if (hour < 8) - newStartDate = [now hour: 8 minute: 0]; - else if (hour > 18) - newStartDate = [[now tomorrow] hour: 8 minute: 0]; - else - newStartDate = now; - } - else - newStartDate = [newStartDate hour: 8 minute: 0]; - } - - return newStartDate; -} - -- (void) loadValuesFromComponent: (iCalRepeatableEntityObject *) component -{ - iCalRecurrenceRule *rrule; - NSTimeZone *uTZ; - SOGoObject *co; - - co = [self clientObject]; - componentOwner = [co ownerInContext: nil]; - componentLoaded = YES; - - startDate = [component startDate]; -// if ((startDate = [component startDate]) == nil) -// startDate = [[NSCalendarDate date] hour:11 minute:0]; - uTZ = [co userTimeZone]; - if (startDate) - { - [startDate setTimeZone: uTZ]; - [startDate retain]; - } - - title = [[component summary] copy]; - location = [[component location] copy]; - comment = [[component comment] copy]; - url = [[[component url] absoluteString] copy]; - privacy = [[component accessClass] copy]; - priority = [[component priority] copy]; - status = [[component status] copy]; - categories = [[[component categories] commaSeparatedValues] retain]; - organizer = [[component organizer] retain]; - participants = [[component participants] retain]; - resources = [[component resources] retain]; - - /* cycles */ - if ([component isRecurrent]) - { - rrule = [[component recurrenceRules] objectAtIndex: 0]; - [self adjustCycleControlsForRRule: rrule]; - } -} - -- (NSString *) iCalStringTemplate -{ - [self subclassResponsibility: _cmd]; - - return @""; -} - - (NSString *) iCalParticipantsAndResourcesStringFromQueryParameters { NSString *s; @@ -709,7 +671,7 @@ NSArray *es; unsigned i, count; - es = [s componentsSeparatedByString:@","]; + es = [s componentsSeparatedByString: @","]; count = [es count]; for(i = 0; i < count; i++) { NSString *email, *cn; @@ -728,13 +690,6 @@ [self cnForUser], [self emailForUser]]; } -- (NSString *) saveUrl -{ - [self subclassResponsibility: _cmd]; - - return @""; -} - - (NSException *) validateObjectForStatusChange { id co; @@ -751,16 +706,6 @@ /* contact editor compatibility */ -- (void) setICalString: (NSString *) _s -{ - ASSIGNCOPY(iCalString, _s); -} - -- (NSString *) iCalString -{ - return iCalString; -} - - (NSArray *) availableCalendars { NSEnumerator *rawContacts; @@ -807,58 +752,87 @@ return classes; } -- (NSString *) _toolbarForCalObject: (iCalEntityObject *) calObject +- (void) _handleAttendeesEdition { - NSString *filename, *myEmail; - iCalPerson *person; - NSEnumerator *persons; - iCalPersonPartStat myParticipationStatus; - BOOL found; + NSArray *names, *emails; + NSMutableArray *newAttendees; + unsigned int count, max; + NSString *currentEmail; + iCalPerson *currentAttendee; - myEmail = [[[self context] activeUser] email]; - if ([[organizer rfc822Email] isEqualToString: myEmail]) - filename = @"SOGoAppointmentObject.toolbar"; - else + newAttendees = [NSMutableArray new]; + if ([attendeesNames length] > 0) { - filename = @""; - found = NO; - persons = [participants objectEnumerator]; - person = [persons nextObject]; - while (person && !found) - if ([[person rfc822Email] isEqualToString: myEmail]) - { - found = YES; - myParticipationStatus = [person participationStatus]; - if (myParticipationStatus == iCalPersonPartStatAccepted) - filename = @"SOGoAppointmentObjectDecline.toolbar"; - else if (myParticipationStatus == iCalPersonPartStatDeclined) - filename = @"SOGoAppointmentObjectAccept.toolbar"; - else - filename = @"SOGoAppointmentObjectAcceptOrDecline.toolbar"; - } - else - person = [persons nextObject]; + names = [attendeesNames componentsSeparatedByString: @","]; + emails = [attendeesEmails componentsSeparatedByString: @","]; + max = [emails count]; + for (count = 0; count < max; count++) + { + currentEmail = [emails objectAtIndex: count]; + currentAttendee = [component findParticipantWithEmail: currentEmail]; + if (!currentAttendee) + { + currentAttendee = [iCalPerson elementWithTag: @"attendee"]; + [currentAttendee setCn: [names objectAtIndex: count]]; + [currentAttendee setEmail: currentEmail]; + [currentAttendee setRole: @"REQ-PARTICIPANT"]; + [currentAttendee + setParticipationStatus: iCalPersonPartStatNeedsAction]; + } + [newAttendees addObject: currentAttendee]; + } } - return filename; + [component setAttendees: newAttendees]; + [newAttendees release]; } -- (NSString *) toolbar +- (void) _handleOrganizer { - NSString *filename; - iCalEntityObject *calObject; - SOGoCalendarComponent *co; + NSString *organizerEmail; - if (componentLoaded) + organizerEmail = [[component organizer] email]; + if ([organizerEmail length] == 0) { - co = [self clientObject]; - calObject = [co component]; - filename = [self _toolbarForCalObject: calObject]; + if ([[component attendees] count] > 0) + { + ASSIGN (organizer, [iCalPerson elementWithTag: @"organizer"]); + [organizer setCn: [self cnForUser]]; + [organizer setEmail: [self emailForUser]]; + [component setOrganizer: organizer]; + } } else - filename = @""; + { + if ([[component attendees] count] == 0) + { + ASSIGN (organizer, [iCalPerson elementWithTag: @"organizer"]); + [component setOrganizer: organizer]; + } + } +} + +- (void) takeValuesFromRequest: (WORequest *) _rq + inContext: (WOContext *) _ctx +{ + NSCalendarDate *now; - return filename; + [super takeValuesFromRequest: _rq inContext: _ctx]; + + now = [NSCalendarDate calendarDate]; + [component setSummary: title]; + [component setLocation: location]; + [component setComment: comment]; + [component setUrl: url]; + [self _handleAttendeesEdition]; + [self _handleOrganizer]; + if ([[self clientObject] isNew]) + { + [component setCreated: now]; + [component setTimeStampAsDate: now]; + [component setPriority: @"0"]; + } + [component setLastModified: now]; } @end diff --git a/UI/Scheduler/UIxFreeBusyUserSelector.h b/UI/Scheduler/UIxFreeBusyUserSelector.h deleted file mode 100644 index 44ff82ec..00000000 --- a/UI/Scheduler/UIxFreeBusyUserSelector.h +++ /dev/null @@ -1,62 +0,0 @@ -/* UIxFreeBusyUserSelector.h - this file is part of SOGo - * - * Copyright (C) 2006 Inverse groupe conseil - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef UIXFREEBUSYUSERSELECTOR_H -#define UIXFREEBUSYUSERSELECTOR_H - -#import - -@class NSArray; -@class NSMutableArray; -@class NSCalendarDate; -@class NSNumber; -@class iCalPerson; - -@interface UIxFreeBusyUserSelector : UIxComponent -{ - NSCalendarDate *startDate; - NSCalendarDate *endDate; - NSNumber *dayStartHour; - NSNumber *dayEndHour; - - NSArray *contacts; - - NSString *selectorId; -} - -- (void) setStartDate: (NSCalendarDate *) newStartDate; -- (void) setEndDate: (NSCalendarDate *) newEndDate; - -- (void) setDayStartHour: (NSNumber *) newDayStartHour; -- (NSNumber *) dayStartHour; -- (void) setDayEndHour: (NSNumber *) newDayEndHour; -- (NSNumber *) dayEndHour; - -- (void) setContacts: (NSArray *) contacts; -- (NSArray *) contacts; - -- (void) setSelectorId: (NSString *) newSelectorId; -- (NSString *) selectorId; - -@end - -#endif /* UIXFREEBUSYUSERSELECTOR_H */ diff --git a/UI/Scheduler/UIxFreeBusyUserSelector.m b/UI/Scheduler/UIxFreeBusyUserSelector.m deleted file mode 100644 index 13f4c3aa..00000000 --- a/UI/Scheduler/UIxFreeBusyUserSelector.m +++ /dev/null @@ -1,163 +0,0 @@ -/* UIxFreeBusyUserSelector.m - this file is part of SOGo - * - * Copyright (C) 2006 Inverse groupe conseil - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import - -#import -#import - -#import - -#import "UIxComponent+Agenor.h" -#import "UIxFreeBusyUserSelector.h" - -@implementation UIxFreeBusyUserSelector - -- (id) init -{ - if ((self = [super init])) - { - startDate = nil; - endDate = nil; - dayStartHour = [NSNumber numberWithInt: 8]; - [dayStartHour retain]; - dayEndHour = [NSNumber numberWithInt: 18]; - [dayEndHour retain]; - contacts = nil; - selectorId = nil; - } - - return self; -} - -- (void) dealloc -{ - [dayStartHour release]; - [dayEndHour release]; - if (contacts) - [contacts release]; - if (selectorId) - [selectorId release]; - [super dealloc]; -} - -- (void) setStartDate: (NSCalendarDate *) newStartDate -{ - startDate = newStartDate; -} - -- (NSCalendarDate *) startDate -{ - return startDate; -} - -- (void) setEndDate: (NSCalendarDate *) newEndDate -{ - endDate = newEndDate; -} - -- (NSCalendarDate *) endDate -{ - return endDate; -} - -- (void) setDayStartHour: (NSNumber *) newDayStartHour -{ - ASSIGN (dayStartHour, newDayStartHour); -} - -- (NSNumber *) dayStartHour -{ - return dayStartHour; -} - -- (void) setDayEndHour: (NSNumber *) newDayEndHour -{ - ASSIGN (dayEndHour, newDayEndHour); -} - -- (NSNumber *) dayEndHour -{ - return dayEndHour; -} - -- (void) setSelectorId: (NSString *) newSelectorId -{ - ASSIGN (selectorId, newSelectorId); -} - -- (NSString *) selectorId -{ - return selectorId; -} - -- (void) setContacts: (NSArray *) newContacts -{ - ASSIGN (contacts, newContacts); -} - -- (NSArray *) contacts -{ - return contacts; -} - -/* callbacks */ -- (void) takeValuesFromRequest: (WORequest *) request - inContext: (WOContext *) context -{ - NSArray *newContacts; - - newContacts - = [self getICalPersonsFromValue: [request formValueForKey: selectorId]]; - ASSIGN (contacts, newContacts); - if ([contacts count] > 0) - NSLog (@"got %i attendees: %@", [contacts count], contacts); - else - NSLog (@"got no attendees!"); -} - -/* in-template operations */ -- (NSString *) initialContactsAsString -{ - NSEnumerator *persons; - iCalPerson *person; - NSMutableArray *participants; - - participants = [NSMutableArray arrayWithCapacity: [contacts count]]; - persons = [contacts objectEnumerator]; - person = [persons nextObject]; - while (person) - { - [participants addObject: [person cn]]; - person = [persons nextObject]; - } - - return [participants componentsJoinedByString: @","]; -} - -- (NSString *) freeBusyViewId -{ - return [NSString stringWithFormat: @"parentOf%@", [selectorId capitalizedString]]; -} - -@end diff --git a/UI/Scheduler/UIxFreeBusyUserSelectorTable.h b/UI/Scheduler/UIxFreeBusyUserSelectorTable.h deleted file mode 100644 index 969695f7..00000000 --- a/UI/Scheduler/UIxFreeBusyUserSelectorTable.h +++ /dev/null @@ -1,82 +0,0 @@ -/* UIxFreeBusyUserSelectorTable.h - this file is part of SOGo - * - * Copyright (C) 2006 Inverse groupe conseil - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef UIXFREEBUSYUSERSELECTORTABLE_H -#define UIXFREEBUSYUSERSELECTORTABLE_H - -#import - -@class NSArray; -@class NSCalendarDate; -@class NSNumber; - -@class iCalPerson; -@class SOGoDateFormatter; - -@interface UIxFreeBusyUserSelectorTable : UIxComponent -{ - BOOL standAlone; - NSMutableArray *daysToDisplay; - NSMutableArray *hoursToDisplay; - SOGoDateFormatter *dateFormatter; - - NSArray *contacts; - NSNumber *dayStartHour; - NSNumber *dayEndHour; - NSCalendarDate *startDate; - NSCalendarDate *endDate; - - iCalPerson *currentContact; - NSNumber *currentHourToDisplay; - NSCalendarDate *currentDayToDisplay; -} - -- (void) setContacts: (NSArray *) newContacts; -- (NSArray *) contacts; - -- (void) setStartDate: (NSCalendarDate *) newStartDate; -- (void) setEndDate: (NSCalendarDate *) newEndDate; - -- (void) setDayStartHour: (NSNumber *) newDayStartHour; -- (NSNumber *) dayStartHour; -- (void) setDayEndHour: (NSNumber *) newDayEndHour; -- (NSNumber *) dayEndHour; - -- (void) setCurrentContact: (iCalPerson *) newCurrentContact; -- (iCalPerson *) currentContact; -- (NSString *) currentContactId; -- (NSString *) currentContactName; - -- (void) setCurrentDayToDisplay: (NSCalendarDate *) newCurrentDayToDisplay; -- (NSCalendarDate *) currentDayToDisplay; - -- (void) setCurrentHourToDisplay: (NSNumber *) newCurrentHourToDisplay; -- (NSNumber *) currentHourToDisplay; - -- (NSString *) currentFormattedDay; - -- (NSArray *) daysToDisplay; -- (NSArray *) hoursToDisplay; - -@end - -#endif /* UIXFREEBUSYUSERSELECTORTABLE_H */ diff --git a/UI/Scheduler/UIxFreeBusyUserSelectorTable.m b/UI/Scheduler/UIxFreeBusyUserSelectorTable.m deleted file mode 100644 index 4e3df859..00000000 --- a/UI/Scheduler/UIxFreeBusyUserSelectorTable.m +++ /dev/null @@ -1,292 +0,0 @@ -/* UIxFreeBusyUserSelectorTable.m - this file is part of SOGo - * - * Copyright (C) 2006 Inverse groupe conseil - * - * Author: Wolfgang Sourdeau - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#import -#import -#import -#import - -#import -#import - -#import -#import -#import - -#import "UIxComponent+Agenor.h" -#import "UIxFreeBusyUserSelectorTable.h" - -@implementation UIxFreeBusyUserSelectorTable - -- (id) init -{ - if ((self = [super init])) - { - standAlone = NO; - startDate = nil; - endDate = nil; - contacts = nil; - hoursToDisplay = nil; - daysToDisplay = nil; - dateFormatter - = [[SOGoDateFormatter alloc] initWithLocale: [self locale]]; - } - - return self; -} - -- (void) dealloc -{ - [dateFormatter release]; - if (hoursToDisplay) - [hoursToDisplay release]; - if (daysToDisplay) - [daysToDisplay release]; - if (standAlone) - { - if (startDate) - [startDate release]; - if (endDate) - [endDate release]; - if (contacts) - [contacts release]; - } - [super dealloc]; -} - -- (void) setContacts: (NSArray *) newContacts -{ - contacts = newContacts; -} - -- (NSArray *) contacts -{ - return contacts; -} - -- (void) setStartDate: (NSCalendarDate *) newStartDate -{ - startDate = newStartDate; - if (daysToDisplay) - { - [daysToDisplay release]; - daysToDisplay = nil; - } -} - -- (NSCalendarDate *) startDate -{ - return startDate; -} - -- (void) setEndDate: (NSCalendarDate *) newEndDate -{ - endDate = newEndDate; - if (daysToDisplay) - { - [daysToDisplay release]; - daysToDisplay = nil; - } -} - -- (NSCalendarDate *) endDate -{ - return endDate; -} - -- (void) setDayStartHour: (NSNumber *) newDayStartHour -{ - dayStartHour = newDayStartHour; - if (hoursToDisplay) - { - [hoursToDisplay release]; - hoursToDisplay = nil; - } -} - -- (NSNumber *) dayStartHour -{ - return dayStartHour; -} - -- (void) setDayEndHour: (NSNumber *) newDayEndHour -{ - dayEndHour = newDayEndHour; - if (hoursToDisplay) - { - [hoursToDisplay release]; - hoursToDisplay = nil; - } -} - -- (NSNumber *) dayEndHour -{ - return dayEndHour; -} - -/* template operations */ -- (NSArray *) daysToDisplay -{ - NSCalendarDate *currentDay, *finalDay; - - if (!daysToDisplay) - { - daysToDisplay = [NSMutableArray new]; - finalDay = [endDate dateByAddingYears: 0 months: 0 days: 2]; - currentDay = startDate; - [daysToDisplay addObject: currentDay]; - while (![currentDay isDateOnSameDay: finalDay]) - { - currentDay = [currentDay dateByAddingYears: 0 - months: 0 - days: 1]; - [daysToDisplay addObject: currentDay]; - } - } - - return daysToDisplay; -} - -- (NSArray *) hoursToDisplay -{ - NSNumber *currentHour; - - if (!hoursToDisplay) - { - hoursToDisplay = [NSMutableArray new]; - currentHour = dayStartHour; - [hoursToDisplay addObject: currentHour]; - while (![currentHour isEqual: dayEndHour]) - { - currentHour = [NSNumber numberWithInt: [currentHour intValue] + 1]; - [hoursToDisplay addObject: currentHour]; - } - } - - return hoursToDisplay; -} - -- (void) setCurrentContact: (iCalPerson *) newCurrentContact -{ - currentContact = newCurrentContact; -} - -- (iCalPerson *) currentContact -{ - return currentContact; -} - -- (BOOL) currentContactHasStatus -{ - return ([currentContact participationStatus] != 0); -} - -- (NSString *) currentContactStatusImage -{ - NSString *basename; - - basename = [[currentContact partStatWithDefault] lowercaseString]; - - return [self urlForResourceFilename: [NSString stringWithFormat: @"%@.png", basename]];; -} - -- (NSString *) currentContactId -{ - return [currentContact cn]; -} - -- (NSString *) currentContactName -{ - return [currentContact cn]; -} - -- (void) setCurrentDayToDisplay: (NSCalendarDate *) newCurrentDayToDisplay -{ - currentDayToDisplay = newCurrentDayToDisplay; -} - -- (void) setCurrentHourToDisplay: (NSNumber *) newCurrentHourToDisplay -{ - currentHourToDisplay = newCurrentHourToDisplay; -} - -- (NSCalendarDate *) currentDayToDisplay -{ - return currentDayToDisplay; -} - -- (NSNumber *) currentHourToDisplay -{ - return currentHourToDisplay; -} - -- (NSString *) currentFormattedDay -{ - return [NSString stringWithFormat: @"%@, %.4d-%.2d-%.2d", - [dateFormatter shortDayOfWeek: [currentDayToDisplay dayOfWeek]], - [currentDayToDisplay yearOfCommonEra], - [currentDayToDisplay monthOfYear], - [currentDayToDisplay dayOfMonth]]; -} - -/* as stand-alone component... */ - -- (id ) defaultAction -{ - SOGoFreeBusyObject *co; - NSString *queryParam; - NSTimeZone *uTZ; - - co = [self clientObject]; - uTZ = [co userTimeZone]; - - queryParam = [self queryParameterForKey: @"sday"]; - if ([queryParam length] > 0) - { - [self setStartDate: [NSCalendarDate dateFromShortDateString: queryParam - andShortTimeString: @"0000" - inTimeZone: uTZ]]; - [startDate retain]; - } - queryParam = [self queryParameterForKey: @"eday"]; - if ([queryParam length] > 0) - { - [self setEndDate: [NSCalendarDate dateFromShortDateString: queryParam - andShortTimeString: @"0000" - inTimeZone: uTZ]]; - [endDate retain]; - } - queryParam = [self queryParameterForKey: @"attendees"]; - if ([queryParam length] > 0) - { - [self setContacts: [self getICalPersonsFromValue: queryParam]]; - [contacts retain]; - } - dayStartHour = [NSNumber numberWithInt: 8]; - dayEndHour = [NSNumber numberWithInt: 18]; - - standAlone = YES; - - return self; -} - -@end diff --git a/UI/Scheduler/UIxTaskEditor.h b/UI/Scheduler/UIxTaskEditor.h index df43674e..871593c4 100644 --- a/UI/Scheduler/UIxTaskEditor.h +++ b/UI/Scheduler/UIxTaskEditor.h @@ -1,6 +1,6 @@ /* UIxTaskEditor.h - this file is part of SOGo * - * Copyright (C) 2006 Inverse groupe conseil + * Copyright (C) 2007 Inverse groupe conseil * * Author: Wolfgang Sourdeau * @@ -20,59 +20,41 @@ * Boston, MA 02111-1307, USA. */ -#ifndef UIXTASKEDITOR_H -#define UIXTASKEDITOR_H +#ifndef UIXAPPOINTMENTEDITOR_H +#define UIXAPPOINTMENTEDITOR_H -#import "UIxComponentEditor.h" +#import -@class NSString; -@class iCalPerson; -@class iCalRecurrenceRule; @class iCalToDo; +@class NSString; -@interface UIxTaskEditor : UIxComponentEditor +@interface UIxTaskEditor : UIxComponent { - NSCalendarDate *dueDate; + iCalToDo *todo; + NSCalendarDate *taskStartDate; + NSCalendarDate *taskDueDate; + NSCalendarDate *statusDate; + NSString *status; + NSString *statusPercent; BOOL hasStartDate; BOOL hasDueDate; - BOOL newTask; + NSString *item; } +/* template values */ +- (NSString *) saveURL; +- (iCalToDo *) todo; + +/* icalendar values */ - (void) setTaskStartDate: (NSCalendarDate *) _date; - (NSCalendarDate *) taskStartDate; - (void) setTaskDueDate: (NSCalendarDate *) _date; - (NSCalendarDate *) taskDueDate; -/* iCal */ - -- (NSString *) iCalStringTemplate; - -/* new */ - -- (id) newAction; - -/* save */ - -- (void) loadValuesFromTask: (iCalToDo *) _task; -- (void) saveValuesIntoTask: (iCalToDo *) _task; -- (iCalToDo *) taskFromString: (NSString *) _iCalString; - -/* conflict management */ - -- (BOOL) containsConflict: (id) _task; -- (id ) defaultAction; -- (id ) saveAction; -- (id) changeStatusAction; -- (id) acceptAction; -- (id) declineAction; - -- (NSString *) saveUrl; - -// TODO: add tentatively - -- (id) acceptOrDeclineAction: (BOOL) _accept; +- (NSString *) repeat; +- (void) setRepeat: (NSString *) newRepeat; @end -#endif /* UIXTASKEDITOR_H */ +#endif /* UIXAPPOINTMENTEDITOR_H */ diff --git a/UI/Scheduler/UIxTaskEditor.m b/UI/Scheduler/UIxTaskEditor.m index 8ff0ff6e..d4330911 100644 --- a/UI/Scheduler/UIxTaskEditor.m +++ b/UI/Scheduler/UIxTaskEditor.m @@ -1,503 +1,479 @@ -/* - Copyright (C) 2004-2005 SKYRIX Software AG - - This file is part of OpenGroupware.org. +/* UIxTaskEditor.m - this file is part of SOGo + * + * Copyright (C) 2007 Inverse groupe conseil + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import +#import + +#import +#import + +#import +#import +#import +#import +#import + +#import "UIxComponentEditor.h" +#import "UIxTaskEditor.h" - OGo is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. +@implementation UIxTaskEditor - OGo is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. +- (id) init +{ + if ((self = [super init])) + { + taskStartDate = nil; + taskDueDate = nil; + statusDate = nil; + hasStartDate = NO; + hasDueDate = NO; + status = nil; + statusPercent = nil; + item = nil; + todo = nil; + } - You should have received a copy of the GNU Lesser General Public - License along with OGo; see the file COPYING. If not, write to the - Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. -*/ + return self; +} -#import +- (void) dealloc +{ + [taskStartDate release]; + [taskDueDate release]; + [statusDate release]; + [status release]; + [statusPercent release]; + [super dealloc]; +} -#import "UIxTaskEditor.h" +/* template values */ +- (iCalToDo *) todo +{ + return todo; +} -/* TODO: CLEAN UP */ +- (NSString *) saveURL +{ + return [NSString stringWithFormat: @"%@/saveAsTask", + [[self clientObject] baseURL]]; +} -#import "common.h" -#import -#import -#import -#import -#import -#import -#import "UIxComponent+Agenor.h" +- (NSString *) _toolbarForCalObject +{ + SOGoUser *currentUser; + SOGoTaskObject *clientObject; + NSString *filename, *email; + iCalPerson *person; + iCalPersonPartStat participationStatus; + + clientObject = [self clientObject]; + currentUser = [[self context] activeUser]; + email = [currentUser email]; + if ([clientObject isOrganizer: email + orOwner: [currentUser login]]) + filename = @"SOGoTaskObject.toolbar"; + else + { + if ([clientObject isParticipant: email]) + { + person = [[clientObject component: NO] findParticipantWithEmail: email]; + participationStatus = [person participationStatus]; + if (participationStatus == iCalPersonPartStatAccepted) + filename = @"SOGoTaskObjectDecline.toolbar"; + else if (participationStatus == iCalPersonPartStatDeclined) + filename = @"SOGoTaskObjectAccept.toolbar"; + else + filename = @"SOGoTaskObjectAcceptOrDecline.toolbar"; + } + else + filename = @"SOGoComponentClose.toolbar"; + } -@implementation UIxTaskEditor + return filename; +} -- (void) dealloc +- (NSString *) toolbar { - [dueDate release]; - [super dealloc]; + return ([self _toolbarForCalObject]); } -- (void) setTaskStartDate: (NSCalendarDate *) _date +/* icalendar values */ +- (void) setTaskStartDate: (NSCalendarDate *) newTaskStartDate { - [self setStartDate: _date]; + ASSIGN (taskStartDate, newTaskStartDate); } - (NSCalendarDate *) taskStartDate { - return [self startDate]; + return taskStartDate; +} + +- (void) setHasStartDate: (BOOL) newHasStartDate +{ + hasStartDate = newHasStartDate; } -- (void) setTaskDueDate: (NSCalendarDate *) _date +- (BOOL) hasStartDate { - ASSIGN(dueDate, _date); + return hasStartDate; +} + +- (BOOL) startDateDisabled +{ + return !hasStartDate; +} + +- (void) setTaskDueDate: (NSCalendarDate *) newTaskDueDate +{ + ASSIGN (taskDueDate, newTaskDueDate); } - (NSCalendarDate *) taskDueDate { - return dueDate; -} - -/* iCal */ - -- (NSString *) iCalStringTemplate -{ - static NSString *iCalStringTemplate = \ - @"BEGIN:VCALENDAR\r\n" - @"METHOD:REQUEST\r\n" - @"PRODID://Inverse groupe conseil/SOGo 0.9\r\n" - @"VERSION:2.0\r\n" - @"BEGIN:VTODO\r\n" - @"UID:%@\r\n" - @"CLASS:PUBLIC\r\n" - @"STATUS:NEEDS-ACTION\r\n" /* confirmed by default */ - @"PERCENT-COMPLETE:0\r\n" - @"DTSTART:%@Z\r\n" - @"DUE:%@Z\r\n" - @"DTSTAMP:%@Z\r\n" - @"SEQUENCE:1\r\n" - @"PRIORITY:5\r\n" - @"%@" /* organizer */ - @"%@" /* participants and resources */ - @"END:VTODO\r\n" - @"END:VCALENDAR"; - - NSCalendarDate *stamp, *lStartDate, *lDueDate; - NSString *template, *s; - NSTimeZone *utc; - unsigned minutes; - - s = [self queryParameterForKey:@"dur"]; - if ([s length] > 0) - minutes = [s intValue]; - else - minutes = 60; - - utc = [NSTimeZone timeZoneWithName: @"GMT"]; - lStartDate = [self newStartDate]; - [lStartDate setTimeZone: utc]; - lDueDate = [lStartDate dateByAddingYears: 0 months: 0 days: 0 - hours: 0 minutes: minutes seconds: 0]; - stamp = [NSCalendarDate calendarDate]; - [stamp setTimeZone: utc]; - - s = [self iCalParticipantsAndResourcesStringFromQueryParameters]; - template = [NSString stringWithFormat:iCalStringTemplate, - [[self clientObject] nameInContainer], - [lStartDate iCalFormattedDateTimeString], - [lDueDate iCalFormattedDateTimeString], - [stamp iCalFormattedDateTimeString], - [self iCalOrganizerString], - s]; - return template; -} - -/* new */ - -- (id) newAction -{ - /* - This method creates a unique ID and redirects to the "edit" method on the - new ID. - It is actually a folder method and should be defined on the folder. - - Note: 'clientObject' is the SOGoAppointmentFolder! - Update: remember that there are group folders as well. - */ - NSString *uri, *objectId, *method, *ps; - - objectId = [NSClassFromString(@"SOGoAppointmentFolder") - globallyUniqueObjectId]; - if ([objectId length] == 0) { - return [NSException exceptionWithHTTPStatus:500 /* Internal Error */ - reason:@"could not create a unique ID"]; - } - - method = [NSString stringWithFormat:@"Calendar/%@/editAsTask", objectId]; - method = [[self userFolderPath] stringByAppendingPathComponent:method]; - - /* check if participants have already been provided */ - ps = [self queryParameterForKey:@"ps"]; -// if (ps) { -// [self setQueryParameter:ps forKey:@"ps"]; -// } - if (!ps - && [[self clientObject] respondsToSelector:@selector(calendarUIDs)]) { - AgenorUserManager *um; - NSArray *uids; - NSMutableArray *emails; - unsigned i, count; - - /* add all current calendarUIDs as default participants */ - - um = [AgenorUserManager sharedUserManager]; - uids = [[self clientObject] calendarUIDs]; - count = [uids count]; - emails = [NSMutableArray arrayWithCapacity:count]; - - for (i = 0; i < count; i++) { - NSString *email; - - email = [um getEmailForUID:[uids objectAtIndex:i]]; - if (email) - [emails addObject:email]; - } - ps = [emails componentsJoinedByString:@","]; - [self setQueryParameter:ps forKey:@"ps"]; - } - uri = [self completeHrefForMethod:method]; - return [self redirectToLocation:uri]; + return taskDueDate; } -/* save */ +- (void) setHasDueDate: (BOOL) newHasDueDate +{ + hasDueDate = newHasDueDate; +} + +- (BOOL) hasDueDate +{ + return hasDueDate; +} -- (void) loadValuesFromTask: (iCalToDo *) _task +- (BOOL) dueDateDisabled { - NSTimeZone *uTZ; + return !hasDueDate; +} - [self loadValuesFromComponent: _task]; +- (NSArray *) repeatList +{ + static NSArray *repeatItems = nil; - uTZ = [[self clientObject] userTimeZone]; - dueDate = [_task due]; -// if (!dueDate) -// dueDate = [[self startDate] dateByAddingYears: 0 months: 0 days: 0 -// hours: 1 minutes: 0 seconds: 0]; - if (dueDate) + if (!repeatItems) { - [dueDate setTimeZone: uTZ]; - [dueDate retain]; + repeatItems = [NSArray arrayWithObjects: @"DAILY", + @"WEEKLY", + @"BI-WEEKLY", + @"EVERY WEEKDAY", + @"MONTHLY", + @"YEARLY", + @"-", + @"CUSTOM", + nil]; + [repeatItems retain]; } + + return repeatItems; } -- (void) saveValuesIntoTask: (iCalToDo *) _task +- (NSString *) itemRepeatText { - /* merge in form values */ - NSArray *attendees, *lResources; - iCalRecurrenceRule *rrule; - NSCalendarDate *dateTime; + NSString *text; - if (hasStartDate) - dateTime = [self taskStartDate]; + if ([item isEqualToString: @"-"]) + text = item; else - dateTime = nil; - [_task setStartDate: dateTime]; - if (hasDueDate) - dateTime = [self taskDueDate]; - else - dateTime = nil; - [_task setDue: dateTime]; - - [_task setSummary: [self title]]; - [_task setUrl: [self url]]; - [_task setLocation: [self location]]; - [_task setComment: [self comment]]; - [_task setPriority:[self priority]]; - [_task setAccessClass: [self privacy]]; - [_task setStatus: [self status]]; - -// [_task setCategories: [[self categories] componentsJoinedByString: @","]]; - -#if 0 - /* - Note: bad, bad, bad! - Organizer is no form value, thus we MUST NOT change it - */ - [_task setOrganizer:organizer]; -#endif - attendees = [self participants]; - lResources = [self resources]; - if ([lResources count] > 0) { - attendees = ([attendees count] > 0) - ? [attendees arrayByAddingObjectsFromArray:lResources] - : lResources; - } - [attendees makeObjectsPerformSelector: @selector (setTag:) - withObject: @"attendee"]; - [_task setAttendees:attendees]; - - /* cycles */ - [_task removeAllRecurrenceRules]; - rrule = [self rrule]; - if (rrule) - [_task addToRecurrenceRules: rrule]; -} - -- (iCalToDo *) taskFromString: (NSString *) _iCalString -{ - iCalCalendar *calendar; - iCalToDo *task; - - calendar = [iCalCalendar parseSingleFromSource: _iCalString]; - task = (iCalToDo *) [calendar firstChildWithTag: @"vtodo"]; - - return task; -} - -/* conflict management */ - -- (BOOL) containsConflict: (id) _task -{ - NSArray *attendees, *uids; - SOGoAppointmentFolder *groupCalendar; - NSArray *infos; - NSArray *ranges; - id folder; - - [self logWithFormat:@"search from %@ to %@", - [_task startDate], [_task due]]; - - folder = [[self clientObject] container]; - attendees = [_task attendees]; - uids = [folder uidsFromICalPersons:attendees]; - if ([uids count] == 0) { - [self logWithFormat:@"Note: no UIDs selected."]; - return NO; - } - - groupCalendar = [folder lookupGroupCalendarFolderForUIDs:uids - inContext:[self context]]; - [self debugWithFormat:@"group calendar: %@", groupCalendar]; - - if (![groupCalendar respondsToSelector:@selector(fetchFreeBusyInfosFrom:to:)]) { - [self errorWithFormat:@"invalid folder to run freebusy query on!"]; - return NO; - } - - infos = [groupCalendar fetchFreeBusyInfosFrom:[_task startDate] - to:[_task due]]; - [self debugWithFormat:@" process: %d tasks", [infos count]]; - - ranges = [infos arrayByCreatingDateRangesFromObjectsWithStartDateKey:@"startDate" - andEndDateKey:@"dueDate"]; - ranges = [ranges arrayByCompactingContainedDateRanges]; - [self debugWithFormat:@" blocked ranges: %@", ranges]; - - return [ranges count] != 0 ? YES : NO; + text = [self labelForKey: [NSString stringWithFormat: @"repeat_%@", item]]; + + return text; } -- (id ) defaultAction +- (NSArray *) statusList { - NSString *ical; + static NSArray *statusItems = nil; - /* load iCalendar file */ - - // TODO: can't we use [clientObject contentAsString]? -// ical = [[self clientObject] valueForKey:@"iCalString"]; - ical = [[self clientObject] contentAsString]; - if ([ical length] == 0) + if (!statusItems) { - newTask = YES; - ical = [self iCalStringTemplate]; + statusItems = [NSArray arrayWithObjects: @"NEEDS-ACTION", + @"IN-PROCESS", + @"COMPLETED", + @"CANCELLED", + nil]; + [statusItems retain]; } - else - newTask = NO; - [self setICalString:ical]; - [self loadValuesFromTask: [self taskFromString: ical]]; - -// if (![self canEditComponent]) { -// /* TODO: we need proper ACLs */ -// return [self redirectToLocation: [self completeURIForMethod: @"../view"]]; -// } + return statusItems; +} - return self; +- (NSString *) itemStatusText +{ + return [self labelForKey: [NSString stringWithFormat: @"status_%@", item]]; } -- (id ) saveAction +- (void) setItem: (NSString *) newItem { - iCalToDo *task; - iCalPerson *p; - id result; - NSString *content; - NSException *ex; - - if (![self isWriteableClientObject]) { - /* return 400 == Bad Request */ - return [NSException exceptionWithHTTPStatus:400 - reason: @"method cannot be invoked on " - @"the specified object"]; - } - - task = [self taskFromString: [self iCalString]]; - if (task == nil) { - NSString *s; - - s = [self labelForKey: @"Invalid iCal data!"]; - [self setErrorText: s]; - - return self; - } - - [self saveValuesIntoTask:task]; - p = [task findParticipantWithEmail:[self emailForUser]]; - if (p) { - [p setParticipationStatus:iCalPersonPartStatAccepted]; - } - - if ([self checkForConflicts]) { - if ([self containsConflict:task]) { - NSString *s; - - s = [self labelForKey:@"Conflicts found!"]; - [self setErrorText:s]; - - return self; - } - } - content = [[task parent] versitString]; -// [task release]; task = nil; - - if (content == nil) { - NSString *s; - - s = [self labelForKey: @"Could not create iCal data!"]; - [self setErrorText: s]; - return self; - } - - ex = [[self clientObject] saveContentString:content]; - if (ex != nil) { - [self setErrorText:[ex reason]]; - return self; - } - - if ([[[[self context] request] formValueForKey: @"nojs"] intValue]) - result = [self redirectToLocation: [self applicationPath]]; - else - result = [self jsCloseWithRefreshMethod: @"refreshTasks()"]; + item = newItem; +} - return result; +- (NSString *) item +{ + return item; } -- (id) changeStatusAction +- (NSString *) repeat { - iCalToDo *task; - SOGoTaskObject *taskObject; - NSString *content; - id ex; - int newStatus; + return @""; +} - newStatus = [[self queryParameterForKey: @"status"] intValue]; +- (void) setRepeat: (NSString *) newRepeat +{ +} - taskObject = [self clientObject]; - task = (iCalToDo *) [taskObject component]; - switch (newStatus) - { - case 1: - [task setCompleted: [NSCalendarDate calendarDate]]; - break; - case 2: - [task setStatus: @"IN-PROCESS"]; - break; - case 3: - [task setStatus: @"CANCELLED"]; - break; - default: - [task setStatus: @"NEEDS-ACTION"]; - } +- (NSString *) status +{ + return status; +} - content = [[task parent] versitString]; - ex = [[self clientObject] saveContentString: content]; - if (ex != nil) { - [self setErrorText:[ex reason]]; - return self; - } - - return [self redirectToLocation: [self completeURIForMethod: @".."]]; +- (void) setStatus: (NSString *) newStatus +{ + status = newStatus; } -- (id)acceptAction { - return [self acceptOrDeclineAction:YES]; +- (void) setStatusDate: (NSCalendarDate *) newStatusDate +{ + ASSIGN (statusDate, newStatusDate); } -- (id)declineAction { - return [self acceptOrDeclineAction:NO]; +- (NSCalendarDate *) statusDate +{ + return statusDate; } -- (NSString *) saveUrl +- (BOOL) statusDateDisabled { - return [NSString stringWithFormat: @"%@/saveAsTask", - [[self clientObject] baseURL]]; + return ![status isEqualToString: @"COMPLETED"]; } -// TODO: add tentatively +- (BOOL) statusPercentDisabled +{ + NSLog (@"status: '%@'", status); + return ([status length] == 0 + || [status isEqualToString: @"CANCELLED"]); +} + +- (void) setStatusPercent: (NSString *) newStatusPercent +{ + ASSIGN (statusPercent, newStatusPercent); +} + +- (NSString *) statusPercent +{ + return statusPercent; +} + +/* actions */ +- (NSCalendarDate *) newStartDate +{ + NSCalendarDate *newStartDate, *now; + int hour; + + newStartDate = [self selectedDate]; + if ([[self queryParameterForKey: @"hm"] length] == 0) + { + now = [NSCalendarDate calendarDate]; + [now setTimeZone: [[self clientObject] userTimeZone]]; + if ([now isDateOnSameDay: newStartDate]) + { + hour = [now hourOfDay]; + if (hour < 8) + newStartDate = [now hour: 8 minute: 0]; + else if (hour > 18) + newStartDate = [[now tomorrow] hour: 8 minute: 0]; + else + newStartDate = now; + } + else + newStartDate = [newStartDate hour: 8 minute: 0]; + } + + return newStartDate; +} + +- (id ) defaultAction +{ + NSCalendarDate *startDate, *dueDate; + NSString *duration; + unsigned int minutes; + + todo = (iCalToDo *) [[self clientObject] component: NO]; + if (todo) + { + startDate = [todo startDate]; + dueDate = [todo due]; + hasStartDate = (startDate != nil); + hasDueDate = (dueDate != nil); + ASSIGN (status, [todo status]); + if ([status isEqualToString: @"COMPLETED"]) + ASSIGN (statusDate, [todo completed]); + else + ASSIGN (statusDate, [self newStartDate]); + ASSIGN (statusPercent, [todo percentComplete]); + } + else + { + startDate = [self newStartDate]; + duration = [self queryParameterForKey:@"dur"]; + if ([duration length] > 0) + minutes = [duration intValue]; + else + minutes = 60; + dueDate = [startDate dateByAddingYears: 0 months: 0 days: 0 + hours: 0 minutes: minutes seconds: 0]; + hasStartDate = NO; + hasDueDate = NO; + ASSIGN (statusDate, [self newStartDate]); + ASSIGN (status, @""); + ASSIGN (statusPercent, @""); + } + + ASSIGN (taskStartDate, startDate); + ASSIGN (taskDueDate, dueDate); + + /* here comes the code for initializing repeat, reminder and isAllDay... */ -- (id)acceptOrDeclineAction:(BOOL)_accept { - // TODO: this should live in the SoObjects - NSException *ex; - - if ((ex = [self validateObjectForStatusChange]) != nil) - return ex; - - ex = [[self clientObject] changeParticipationStatus: - _accept ? @"ACCEPTED" : @"DECLINED" - inContext:[self context]]; - if (ex != nil) return ex; - return self; -// return [self redirectToLocation: [self completeURIForMethod:@"../view"]]; } -- (void) setHasStartDate: (BOOL) aBool +- (id ) newAction { - hasStartDate = aBool; + NSString *objectId, *method, *uri; + id result; + Class clientKlazz; + + clientKlazz = [[self clientObject] class]; + objectId = [clientKlazz globallyUniqueObjectId]; + if ([objectId length] > 0) + { + method = [NSString stringWithFormat:@"%@/Calendar/%@/editAsTask", + [self userFolderPath], objectId]; + uri = [self completeHrefForMethod: method]; + result = [self redirectToLocation: uri]; + } + else + result = [NSException exceptionWithHTTPStatus: 500 /* Internal Error */ + reason: @"could not create a unique ID"]; + + return result; } -- (BOOL) hasStartDate +- (id ) saveAction { - return (!newTask && [self taskStartDate] != nil); + SOGoTaskObject *clientObject; + NSString *iCalString; + + clientObject = [self clientObject]; + iCalString = [[clientObject calendar: NO] versitString]; + [clientObject saveContentString: iCalString]; + + return [self jsCloseWithRefreshMethod: @"refreshTasks()"]; } -- (BOOL) startDateDisabled +- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request + inContext: (WOContext*) context { - return (![self hasStartDate]); + return ([[self clientObject] isKindOfClass: [SOGoTaskObject class]] + && [[request method] isEqualToString: @"POST"]); } -- (void) setHasDueDate: (BOOL) aBool +- (void) takeValuesFromRequest: (WORequest *) _rq + inContext: (WOContext *) _ctx { - hasDueDate = aBool; + SOGoTaskObject *clientObject; + + clientObject = [self clientObject]; + todo = (iCalToDo *) [clientObject component: YES]; + + [super takeValuesFromRequest: _rq inContext: _ctx]; + + if (hasStartDate) + [todo setStartDate: taskStartDate]; + if (hasDueDate) + [todo setDue: taskDueDate]; + if ([status isEqualToString: @"COMPLETED"]) + [todo setCompleted: statusDate]; + else + [todo setCompleted: nil]; + if ([status length] > 0) + { + [todo setStatus: status]; + [todo setPercentComplete: statusPercent]; + } + else + { + [todo setStatus: @""]; + [todo setPercentComplete: @""]; + } } -- (BOOL) hasDueDate +// TODO: add tentatively + +- (id) acceptOrDeclineAction: (BOOL) _accept { - return (!newTask && [self taskDueDate] != nil); + [[self clientObject] changeParticipationStatus: + _accept ? @"ACCEPTED" : @"DECLINED" + inContext: [self context]]; + + return self; } -- (BOOL) dueDateDisabled +- (id) acceptAction { - return (![self hasDueDate]); + return [self acceptOrDeclineAction:YES]; } -- (void) setDueDateDisabled: (BOOL) aBool +- (id) declineAction { + return [self acceptOrDeclineAction:NO]; } -- (void) setStartDateDisabled: (BOOL) aBool +- (id) changeStatusAction { + SOGoTaskObject *clientObject; + NSString *newStatus, *iCalString; + + clientObject = [self clientObject]; + todo = (iCalToDo *) [clientObject component: NO]; + if (todo) + { + newStatus = [self queryParameterForKey: @"status"]; + if ([newStatus intValue]) + [todo setCompleted: [NSCalendarDate date]]; + else + { + [todo setCompleted: nil]; + [todo setPercentComplete: 0]; + [todo setStatus: @"IN-PROCESS"]; + } + + iCalString = [[clientObject calendar: NO] versitString]; + [clientObject saveContentString: iCalString]; + } + + return self; } -@end /* UIxTaskEditor */ +@end diff --git a/UI/Scheduler/UIxTaskView.m b/UI/Scheduler/UIxTaskView.m index 0f31e924..c2004dd8 100644 --- a/UI/Scheduler/UIxTaskView.m +++ b/UI/Scheduler/UIxTaskView.m @@ -167,7 +167,7 @@ if (!task) { clientObject = [self clientObject]; - task = (iCalToDo *) [clientObject component]; + task = (iCalToDo *) [clientObject component: NO]; [task retain]; } diff --git a/UI/Scheduler/UIxTimeDateControl.m b/UI/Scheduler/UIxTimeDateControl.m index 88ee2f69..f4606cf0 100644 --- a/UI/Scheduler/UIxTimeDateControl.m +++ b/UI/Scheduler/UIxTimeDateControl.m @@ -70,6 +70,7 @@ } - (void)setDate:(NSCalendarDate *)_date { + NSLog (@"^^^^^^ %@: setDate: %@", self, _date); int minuteValue; if (!_date) _date = [NSCalendarDate date]; @@ -95,8 +96,10 @@ } - (void)setHour:(id)_hour { + NSLog (@"---------------- setHour:"); ASSIGN(hour, _hour); } + - (id)hour { return hour; } @@ -134,6 +137,7 @@ - (void) setDayStartHour: (unsigned int) aStartHour { + NSLog (@"******************** setDayStartHour..."); startHour = aStartHour; } @@ -248,6 +252,7 @@ NSCalendarDate *d; unsigned _year, _month, _day, _hour, _minute, _second; + NSLog (@"******************** %@: take values...", self); /* call super, so that the form values are applied on the popups */ [super takeValuesFromRequest:_rq inContext:_ctx]; diff --git a/UI/Scheduler/product.plist b/UI/Scheduler/product.plist index f08d54d5..8b9b78fb 100644 --- a/UI/Scheduler/product.plist +++ b/UI/Scheduler/product.plist @@ -107,6 +107,10 @@ pageName = "UIxCalMainView"; actionName = "checkRights"; }; + editAttendees = { + protectedBy = "View"; + pageName = "UIxAttendeesEditor"; + }; freeBusyTable = { protectedBy = "View"; pageName = "UIxFreeBusyUserSelectorTable"; diff --git a/UI/Templates/ContactsUI/UIxContactEditor.wox b/UI/Templates/ContactsUI/UIxContactEditor.wox index 6e3f0f94..c4cc0e9e 100644 --- a/UI/Templates/ContactsUI/UIxContactEditor.wox +++ b/UI/Templates/ContactsUI/UIxContactEditor.wox @@ -15,18 +15,16 @@
  • -
  • -
  • +
  • +
+ - - -
-
-
-
-
- -
+ - - - - - - + + -
- -
- - -
- - +
+
-
- - + +
+ +
+ + - - - + + +
-
-
- -
-
-
+ +
+ - + + + + + + + + + + + +
- - - + + +
+
+ + + +
+ +
+
+ + + + - + +
+ +
-
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - -
- - - -
- -
- -
- - - -
- -
- -
diff --git a/UI/Templates/ContactsUI/UIxContactView.wox b/UI/Templates/ContactsUI/UIxContactView.wox index 7d3e078d..e7094c44 100644 --- a/UI/Templates/ContactsUI/UIxContactView.wox +++ b/UI/Templates/ContactsUI/UIxContactView.wox @@ -17,7 +17,9 @@ >

- - - + + + + - + -
- - -
-
- -
-
+ + + +
+
+ +
+
-
    -
  • -
  • -
+
    +
  • +
  • +
- -
- -
-
-
+ +
+ +
+
+
-
- +
+ -
- +
+ -
- -
+
+ +
-
+
-
-
- +
+
+ - - + + - -
+ +
- - - + + + - -
+ + diff --git a/UI/Templates/MainUI/SOGoUserHomePage.wox b/UI/Templates/MainUI/SOGoUserHomePage.wox index a38fe693..68f7ddb0 100644 --- a/UI/Templates/MainUI/SOGoUserHomePage.wox +++ b/UI/Templates/MainUI/SOGoUserHomePage.wox @@ -1,63 +1,3 @@ - - - - - -
- -
-
-
- - - - - - - - - -
-

- -

-
- - - - - - - - - - - -
-
- - - -
- - -
-
-
- - + + diff --git a/UI/Templates/SchedulerUI/UIxAppointmentEditor.wox b/UI/Templates/SchedulerUI/UIxAppointmentEditor.wox index 663ae471..01f27422 100644 --- a/UI/Templates/SchedulerUI/UIxAppointmentEditor.wox +++ b/UI/Templates/SchedulerUI/UIxAppointmentEditor.wox @@ -1,170 +1,47 @@ - - - -
- -

-
-
- -
-
    -
  • -
  • -
  • -
- -
- - -
- - - - -
- - - - - - -
-