From c37e3fd3ffdd8dfe7c24f203aaff114a627b3e78 Mon Sep 17 00:00:00 2001 From: wolfgang Date: Thu, 6 Dec 2007 22:56:53 +0000 Subject: [PATCH] git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1292 d1b88da0-ebda-0310-925b-ed51d893ca5b --- ChangeLog | 61 +++++++++++++- .../Appointments/SOGoAppointmentFolder.m | 13 ++- .../Appointments/SOGoAppointmentObject.m | 4 +- .../Appointments/SOGoCalendarComponent.m | 19 +++++ SoObjects/Appointments/product.plist | 7 +- SoObjects/Contacts/SOGoContactGCSFolder.m | 15 ++++ SoObjects/Contacts/SOGoContactLDAPFolder.m | 15 ++++ SoObjects/Mailer/SOGoMailBodyPart.m | 17 ++-- SoObjects/SOGo/SOGoFolder.h | 1 + SoObjects/SOGo/SOGoObject.m | 6 ++ UI/Common/UIxPageFrame.m | 24 ++++++ UI/MailerUI/UIxMailEditor.m | 42 ++++++++-- UI/Scheduler/NSArray+Scheduler.h | 6 +- UI/Scheduler/NSArray+Scheduler.m | 44 +++++++++- UI/Scheduler/UIxCalListingActions.m | 18 ++++- UI/Scheduler/UIxComponentEditor.m | 50 +++++++----- .../UIxContactsListViewContainer.wox | 3 - UI/Templates/SchedulerUI/UIxCalMainView.wox | 8 +- UI/Templates/UIxPageFrame.wox | 37 +++++++-- UI/WebServerResources/ContactsUI.js | 34 ++++++-- UI/WebServerResources/SchedulerUI.js | 80 ++++++++++++------- UI/WebServerResources/UIxMailToSelection.js | 65 +++------------ UI/WebServerResources/generic.css | 13 ++- UI/WebServerResources/generic.js | 24 +++++- 24 files changed, 456 insertions(+), 150 deletions(-) diff --git a/ChangeLog b/ChangeLog index af43fe95..419b641c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,62 @@ +2007-12-06 Wolfgang Sourdeau + + * SoObjects/Appointments/SOGoCalendarComponent.m + ([SOGoCalendarComponent -contentAsString]): securize the returned content. + ([SOGoCalendarComponent -aclsForUser:uid]): check whether the user + is an organizer or a participant as well. + + * SoObjects/Appointments/SOGoAppointmentObject.m + ([SOGoAppointmentObject -changeParticipationStatus:_status]): the + user we should change the status of is not the active user but the + owner of the object. This is for delegation. + + * SoObjects/Appointments/SOGoAppointmentFolder.m + ([SOGoAppointmentFolder -fetchContentObjectNames]): fetch the + elements through the fetchFields:from:to:title:component: method + method, so that the result is already filtered depending on the + acl. + + * UI/Scheduler/UIxComponentEditor.m ([UIxComponentEditor + -toolbar]): if the current user is a delegate, take the permission + "ModifyComponent" into account. Also, do not let him/her modify an + event for which he/she is the organizer. + + * SoObjects/Contacts/SOGoContactLDAPFolder.m + ([SOGoContactLDAPFolder -davResourceType]): declared as a carddav + addressbook collection. + + * SoObjects/Contacts/SOGoContactGCSFolder.m ([SOGoContactGCSFolder + -davResourceType]): declared as a carddav addressbook collection. + + * SoObjects/SOGo/SOGoObject.m ([SOGoObject -davLastModified]): + override SoObject's implementation. Otherwise a localized date is returned. + + * UI/Common/UIxPageFrame.m ([UIxPageFrame -isCalendar]) + ([UIxPageFrame -isContacts], [UIxPageFrame -isMail]): getters to + determine which module is the current one. + +2007-12-06 Francis Lachapelle + + * UI/Scheduler/NSArray+Scheduler.m ([NSArray -compareEventsTitleAscending:otherEvent]) + ([NSArray -compareEventsLocationAscending:otherEvent]) + ([NSArray -compareEventsEndDateAscending:otherEvent]) + ([NSArray -reversedArray]): new methods that sort an array of + events depending of various parameters. + + * UI/Scheduler/UIxCalListingActions.m ([UIxCalListingActions + -eventsListAction]): added support for sorting events. + +2007-12-06 Wolfgang Sourdeau + + * UI/MailerUI/UIxMailEditor.m ([UIxMailEditor -saveAction]) + ([UIxMailEditor -sendAction]): make sure that the attachments + having the same filename are correctly sequenced (ex: base.ext, + base-1.ext, base-2.ext). + + * SoObjects/Mailer/SOGoMailBodyPart.m ([SOGoMailBodyPart + -fetchBLOB]): added support for quoted-printable decoding. Also, + we return nil if the type is not supported. + 2007-12-05 Wolfgang Sourdeau * UI/Scheduler/UIxTaskEditor.m ([UIxTaskEditor -newAction]): @@ -25,7 +84,7 @@ 2007-12-05 Francis Lachapelle - * SoObjects/Mailer/SOGoDraftObject.m: The condition for replyToAll + * SoObjects/Mailer/SOGoDraftObject.m: the condition for replyToAll has to be done later to avoid duplicated to and cc addresses. 2007-12-04 Wolfgang Sourdeau diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 6deb3e3c..5be6e558 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -26,6 +26,7 @@ #import #import +#import #import #import #import @@ -230,7 +231,6 @@ static NSNumber *sharedYes = nil; return filterData; } -#warning filters is leaked here - (NSArray *) _parseCalendarFilters: (id ) parentNode { NSEnumerator *children; @@ -1422,6 +1422,17 @@ static NSNumber *sharedYes = nil; // return objectNames; // } +- (NSArray *) fetchContentObjectNames +{ + static NSArray *cNameField = nil; + + if (!cNameField) + cNameField = [[NSArray alloc] initWithObjects: @"c_name", nil]; + + return [[self fetchFields: cNameField from: nil to: nil + title: nil component: nil] objectsForKey: @"c_name"]; +} + /* folder type */ - (NSString *) folderType diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index b8814b8e..c3f28371 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -332,13 +332,15 @@ iCalEvent *event; iCalPerson *attendee; NSException *ex; + SOGoUser *ownerUser; ex = nil; event = [self component: NO secure: NO]; if (event) { - attendee = [event findParticipant: [context activeUser]]; + ownerUser = [SOGoUser userWithLogin: owner roles: nil]; + attendee = [event findParticipant: ownerUser]; if (attendee) ex = [self _handleAttendee: attendee statusChange: _status inEvent: event]; diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m index 4236a620..8411b794 100644 --- a/SoObjects/Appointments/SOGoCalendarComponent.m +++ b/SoObjects/Appointments/SOGoCalendarComponent.m @@ -27,6 +27,7 @@ #import #import #import +#import #import #import #import @@ -142,6 +143,18 @@ static BOOL sendEMailNotifications = NO; return iCalString; } +- (NSString *) contentAsString +{ + NSString *secureContent; + + if ([[context request] isSoWebDAVRequest]) + secureContent = [self secureContentAsString]; + else + secureContent = [super contentAsString]; + + return secureContent; +} + - (iCalCalendar *) calendar: (BOOL) create secure: (BOOL) secure { NSString *componentTag; @@ -559,6 +572,7 @@ static BOOL sendEMailNotifications = NO; NSArray *superAcls; iCalRepeatableEntityObject *component; NSString *accessRole, *ownerRole; + SOGoUser *aclUser; roles = [NSMutableArray array]; superAcls = [super aclsForUser: uid]; @@ -573,6 +587,11 @@ static BOOL sendEMailNotifications = NO; { if (component) { + aclUser = [SOGoUser userWithLogin: uid roles: nil]; + if ([component userIsOrganizer: aclUser]) + [roles addObject: SOGoCalendarRole_Organizer]; + else if ([component userIsParticipant: aclUser]) + [roles addObject: SOGoCalendarRole_Participant]; accessRole = [container roleForComponentsWithAccessClass: [component symbolicAccessClass] forUser: uid]; diff --git a/SoObjects/Appointments/product.plist b/SoObjects/Appointments/product.plist index 4f0cb12b..297b9b23 100644 --- a/SoObjects/Appointments/product.plist +++ b/SoObjects/Appointments/product.plist @@ -35,13 +35,14 @@ }; SOGoCalendarComponent = { superclass = "SOGoContentObject"; -/* defaultAccess = "SeeComponent"; */ defaultRoles = { "ViewAllComponent" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer" ); "ViewDAndT" = ( "Organizer", "Participant", "ComponentDAndTViewer" ); "ModifyComponent" = ( "Owner", "Organizer" ); - "RespondToComponent" = ( "Participant" ); -/* "SeeComponent" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer", "ComponentDAndTViewer" ); */ + "RespondToComponent" = ( "Participant", "ComponentModifier", "ComponentResponder" ); + "Access Object" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer", "ComponentDAndTViewer" ); + "Access Contents Information" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer", "ComponentDAndTViewer" ); + "WebDAV Access" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer", "ComponentDAndTViewer" ); }; }; SOGoAppointmentObject = { diff --git a/SoObjects/Contacts/SOGoContactGCSFolder.m b/SoObjects/Contacts/SOGoContactGCSFolder.m index 074048b8..dfc77cae 100644 --- a/SoObjects/Contacts/SOGoContactGCSFolder.m +++ b/SoObjects/Contacts/SOGoContactGCSFolder.m @@ -267,6 +267,21 @@ return @"vcard-collection"; } +- (NSArray *) davResourceType +{ + NSMutableArray *resourceType; + NSArray *cardDavCollection; + + cardDavCollection + = [NSArray arrayWithObjects: @"addressbook", + @"urn:ietf:params:xml:ns:carddav", nil]; + + resourceType = [NSMutableArray arrayWithArray: [super davResourceType]]; + [resourceType addObject: cardDavCollection]; + + return resourceType; +} + /* sorting */ - (NSComparisonResult) compare: (id) otherFolder { diff --git a/SoObjects/Contacts/SOGoContactLDAPFolder.m b/SoObjects/Contacts/SOGoContactLDAPFolder.m index a23f09a5..f3d17328 100644 --- a/SoObjects/Contacts/SOGoContactLDAPFolder.m +++ b/SoObjects/Contacts/SOGoContactLDAPFolder.m @@ -140,6 +140,21 @@ return @"vcard-collection"; } +- (NSArray *) davResourceType +{ + NSMutableArray *resourceType; + NSArray *cardDavCollection; + + cardDavCollection + = [NSArray arrayWithObjects: @"addressbook", + @"urn:ietf:params:xml:ns:carddav", nil]; + + resourceType = [NSMutableArray arrayWithArray: [super davResourceType]]; + [resourceType addObject: cardDavCollection]; + + return resourceType; +} + - (id) lookupName: (NSString *) objectName inContext: (WOContext *) lookupContext acquire: (BOOL) acquire diff --git a/SoObjects/Mailer/SOGoMailBodyPart.m b/SoObjects/Mailer/SOGoMailBodyPart.m index c60f34b9..0ce0fcd7 100644 --- a/SoObjects/Mailer/SOGoMailBodyPart.m +++ b/SoObjects/Mailer/SOGoMailBodyPart.m @@ -32,6 +32,7 @@ #import #import #import +#import #import #import @@ -209,16 +210,22 @@ static BOOL debugOn = NO; if (enc) { - enc = [enc uppercaseString]; + enc = [enc lowercaseString]; - if ([enc isEqualToString:@"BASE64"]) + if ([enc isEqualToString: @"base64"]) data = [data dataByDecodingBase64]; - else if ([enc isEqualToString:@"7BIT"]) + else if ([enc isEqualToString: @"quoted-printable"]) + data = [data dataByDecodingQuotedPrintableTransferEncoding]; + else if ([enc isEqualToString: @"7bit"] + || [enc isEqualToString: @"8bit"]) ; /* keep data as is */ // TODO: do we need to change encodings? else - [self errorWithFormat:@"unsupported encoding: %@", enc]; + { + data = nil; + [self errorWithFormat: @"unsupported encoding: %@", enc]; + } } - + return data; } diff --git a/SoObjects/SOGo/SOGoFolder.h b/SoObjects/SOGo/SOGoFolder.h index afb55cfc..2e02a9f3 100644 --- a/SoObjects/SOGo/SOGoFolder.h +++ b/SoObjects/SOGo/SOGoFolder.h @@ -40,6 +40,7 @@ /* dav */ - (NSArray *) davNamespaces; +- (NSArray *) davResourceType; /* outlook */ - (NSString *) outlookFolderClass; diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index a9a49003..8bb51066 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -58,6 +58,7 @@ #import "SOGoDAVRendererTypes.h" #import "NSArray+Utilities.h" +#import "NSCalendarDate+SOGo.h" #import "NSDictionary+Utilities.h" #import "NSString+Utilities.h" @@ -629,6 +630,11 @@ static BOOL kontactGroupDAV = YES; return @"text/plain"; } +- (NSString *) davLastModified +{ + return [[NSCalendarDate date] rfc822DateString]; +} + - (WOResponse *) _webDAVResponse: (WOContext *) localContext { WOResponse *response; diff --git a/UI/Common/UIxPageFrame.m b/UI/Common/UIxPageFrame.m index b3565dae..54b70d5d 100644 --- a/UI/Common/UIxPageFrame.m +++ b/UI/Common/UIxPageFrame.m @@ -325,6 +325,30 @@ return ([[self productCSSURL] length] > 0); } +- (BOOL) _moduleIs: (NSString *) moduleName +{ + NSString *frameworkName; + + frameworkName = [[context page] frameworkName]; + + return [frameworkName isEqualToString: moduleName]; +} + +- (BOOL) isCalendar +{ + return [self _moduleIs: @"SchedulerUI"]; +} + +- (BOOL) isContacts +{ + return [self _moduleIs: @"ContactsUI"]; +} + +- (BOOL) isMail +{ + return [self _moduleIs: @"MailerUI"]; +} + - (void) setToolbar: (NSString *) newToolbar { ASSIGN (toolbar, newToolbar); diff --git a/UI/MailerUI/UIxMailEditor.m b/UI/MailerUI/UIxMailEditor.m index 31e33f02..c9c41dc2 100644 --- a/UI/MailerUI/UIxMailEditor.m +++ b/UI/MailerUI/UIxMailEditor.m @@ -67,6 +67,7 @@ /* these are for the inline attachment list */ NSString *attachmentName; NSArray *attachmentNames; + NSMutableArray *attachedFiles; } @end @@ -115,6 +116,7 @@ static NSArray *infoKeys = nil; [bcc release]; [attachmentName release]; [attachmentNames release]; + [attachedFiles release]; [super dealloc]; } @@ -262,6 +264,29 @@ static NSArray *infoKeys = nil; } /* actions */ +- (NSString *) _fixedFilename: (NSString *) filename +{ + NSString *newFilename, *baseFilename, *extension; + unsigned int variation; + + if (!attachedFiles) + attachedFiles = [NSMutableArray new]; + + newFilename = filename; + + baseFilename = [filename stringByDeletingPathExtension]; + extension = [filename pathExtension]; + variation = 0; + while ([attachedFiles containsObject: newFilename]) + { + variation++; + newFilename = [NSString stringWithFormat: @"%@-%d.%@", baseFilename, + variation, extension]; + } + [attachedFiles addObject: newFilename]; + + return newFilename; +} - (NSDictionary *) _scanAttachmentFilenamesInRequest: (id) httpBody { @@ -271,7 +296,7 @@ static NSArray *infoKeys = nil; unsigned int count, max; NGMimeBodyPart *part; NGMimeContentDispositionHeaderField *header; - NSString *mimeType; + NSString *mimeType, *filename; parts = [httpBody parts]; max = [parts count]; @@ -284,8 +309,9 @@ static NSArray *infoKeys = nil; [part headerForKey: @"content-disposition"]; mimeType = [(NGMimeType *) [part headerForKey: @"content-type"] stringValue]; + filename = [self _fixedFilename: [header filename]]; attachment = [NSDictionary dictionaryWithObjectsAndKeys: - [header filename], @"filename", + filename, @"filename", mimeType, @"mimetype", nil]; [filenames setObject: attachment forKey: [header name]]; } @@ -370,12 +396,12 @@ static NSArray *infoKeys = nil; { NSArray *a; - if (attachmentNames != nil) - return attachmentNames; - - a = [[self clientObject] fetchAttachmentNames]; - a = [a sortedArrayUsingSelector: @selector (compare:)]; - attachmentNames = [a copy]; + if (attachmentNames) + { + a = [[self clientObject] fetchAttachmentNames]; + a = [a sortedArrayUsingSelector: @selector (compare:)]; + attachmentNames = [a copy]; + } return attachmentNames; } diff --git a/UI/Scheduler/NSArray+Scheduler.h b/UI/Scheduler/NSArray+Scheduler.h index ac8e8b98..b9da8dc3 100644 --- a/UI/Scheduler/NSArray+Scheduler.h +++ b/UI/Scheduler/NSArray+Scheduler.h @@ -27,8 +27,12 @@ @interface NSArray (SOGoEventComparison) -- (NSComparisonResult) compareEventsAscending: (NSArray *) otherEvent; +- (NSComparisonResult) compareEventsStartDateAscending: (NSArray *) otherEvent; +- (NSComparisonResult) compareEventsEndDateAscending: (NSArray *) otherEvent; +- (NSComparisonResult) compareEventsTitleAscending: (NSArray *) otherEvent; +- (NSComparisonResult) compareEventsLocationAscending: (NSArray *) otherEvent; - (NSComparisonResult) compareTasksAscending: (NSArray *) otherTask; +- (NSArray *) reversedArray; @end diff --git a/UI/Scheduler/NSArray+Scheduler.m b/UI/Scheduler/NSArray+Scheduler.m index 6425f6c5..eaf7f0d4 100644 --- a/UI/Scheduler/NSArray+Scheduler.m +++ b/UI/Scheduler/NSArray+Scheduler.m @@ -47,7 +47,7 @@ return result; } -- (NSComparisonResult) compareEventsAscending: (NSArray *) otherEvent +- (NSComparisonResult) compareEventsStartDateAscending: (NSArray *) otherEvent { NSComparisonResult result; unsigned int selfTime, otherTime; @@ -64,6 +64,43 @@ return result; } +- (NSComparisonResult) compareEventsEndDateAscending: (NSArray *) otherEvent +{ + NSComparisonResult result; + unsigned int selfTime, otherTime; + + selfTime = [[self objectAtIndex: 5] intValue]; + otherTime = [[otherEvent objectAtIndex: 5] intValue]; + if (selfTime > otherTime) + result = NSOrderedDescending; + else if (selfTime < otherTime) + result = NSOrderedAscending; + else + result = NSOrderedSame; + + return result; +} + +- (NSComparisonResult) compareEventsTitleAscending: (NSArray *) otherEvent +{ + NSString *selfTitle, *otherTitle; + + selfTitle = [self objectAtIndex: 3]; + otherTitle = [otherEvent objectAtIndex: 3]; + + return [selfTitle caseInsensitiveCompare: otherTitle]; +} + +- (NSComparisonResult) compareEventsLocationAscending: (NSArray *) otherEvent +{ + NSString *selfTitle, *otherTitle; + + selfTitle = [self objectAtIndex: 6]; + otherTitle = [otherEvent objectAtIndex: 6]; + + return [selfTitle caseInsensitiveCompare: otherTitle]; +} + - (NSComparisonResult) compareTasksAscending: (NSArray *) otherTask { NSComparisonResult result; @@ -96,4 +133,9 @@ return result; } +- (NSArray *) reversedArray +{ + return [[self reverseObjectEnumerator] allObjects]; +} + @end diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m index f5a25d5a..5e8905a3 100644 --- a/UI/Scheduler/UIxCalListingActions.m +++ b/UI/Scheduler/UIxCalListingActions.m @@ -313,6 +313,7 @@ NSMutableArray *newEvents, *newEvent; unsigned int interval; BOOL isAllDay; + NSString *sort, *ascending; [self _setupContext]; @@ -340,10 +341,23 @@ [newEvent addObject: [self _formattedDateForSeconds: interval forAllDay: isAllDay]]; [newEvents addObject: newEvent]; - + oldEvent = [events nextObject]; } - [newEvents sortUsingSelector: @selector (compareEventsAscending:)]; + + sort = [[context request] formValueForKey: @"sort"]; + if ([sort isEqualToString: @"title"]) + [newEvents sortUsingSelector: @selector (compareEventsTitleAscending:)]; + else if ([sort isEqualToString: @"end"]) + [newEvents sortUsingSelector: @selector (compareEventsEndDateAscending:)]; + else if ([sort isEqualToString: @"location"]) + [newEvents sortUsingSelector: @selector (compareEventsLocationAscending:)]; + else + [newEvents sortUsingSelector: @selector (compareEventsStartDateAscending:)]; + + ascending = [[context request] formValueForKey: @"asc"]; + if (![ascending boolValue]) + newEvents = [newEvents reversedArray]; return [self _responseWithData: newEvents]; } diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index 9e5f812f..6af7d172 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -1025,6 +1025,7 @@ SoSecurityManager *sm; NSString *toolbarFilename, *adminToolbar; iCalPersonPartStat participationStatus; + SOGoUser *currentUser; if ([clientObject isKindOfClass: [SOGoAppointmentObject class]]) adminToolbar = @"SOGoAppointmentObject.toolbar"; @@ -1034,28 +1035,37 @@ sm = [SoSecurityManager sharedSecurityManager]; if ([[component attendees] count]) { - if ([component userIsOrganizer: ownerUser] - && ![sm validatePermission: SOGoCalendarPerm_ModifyComponent - onObject: clientObject - inContext: context]) - toolbarFilename = adminToolbar; - else if ([component userIsParticipant: ownerUser] - && ![sm validatePermission: SOGoCalendarPerm_RespondToComponent - onObject: clientObject - inContext: context]) - { - participationStatus - = [[component findParticipant: ownerUser] participationStatus]; - /* Lightning does not manage participation status within tasks */ - if (participationStatus == iCalPersonPartStatAccepted) - toolbarFilename = @"SOGoAppointmentObjectDecline.toolbar"; - else if (participationStatus == iCalPersonPartStatDeclined) - toolbarFilename = @"SOGoAppointmentObjectAccept.toolbar"; + currentUser = [context activeUser]; + if ([component userIsOrganizer: currentUser]) + toolbarFilename = @"SOGoComponentClose.toolbar"; + else + { + if ([component userIsOrganizer: ownerUser] + && ![sm validatePermission: SOGoCalendarPerm_ModifyComponent + onObject: clientObject + inContext: context]) + toolbarFilename = adminToolbar; + else if ([component userIsParticipant: ownerUser] + && !([sm validatePermission: SOGoCalendarPerm_RespondToComponent + onObject: clientObject + inContext: context] + && [sm validatePermission: SOGoCalendarPerm_ModifyComponent + onObject: clientObject + inContext: context])) + { + participationStatus + = [[component findParticipant: ownerUser] participationStatus]; + /* Lightning does not manage participation status within tasks */ + if (participationStatus == iCalPersonPartStatAccepted) + toolbarFilename = @"SOGoAppointmentObjectDecline.toolbar"; + else if (participationStatus == iCalPersonPartStatDeclined) + toolbarFilename = @"SOGoAppointmentObjectAccept.toolbar"; + else + toolbarFilename = @"SOGoAppointmentObjectAcceptOrDecline.toolbar"; + } else - toolbarFilename = @"SOGoAppointmentObjectAcceptOrDecline.toolbar"; + toolbarFilename = @"SOGoComponentClose.toolbar"; } - else - toolbarFilename = @"SOGoComponentClose.toolbar"; } else { diff --git a/UI/Templates/ContactsUI/UIxContactsListViewContainer.wox b/UI/Templates/ContactsUI/UIxContactsListViewContainer.wox index 1733b896..31ccfc8c 100644 --- a/UI/Templates/ContactsUI/UIxContactsListViewContainer.wox +++ b/UI/Templates/ContactsUI/UIxContactsListViewContainer.wox @@ -35,8 +35,6 @@ -
@@ -95,7 +93,6 @@ - diff --git a/UI/Templates/SchedulerUI/UIxCalMainView.wox b/UI/Templates/SchedulerUI/UIxCalMainView.wox index 0431db72..54fac20c 100644 --- a/UI/Templates/SchedulerUI/UIxCalMainView.wox +++ b/UI/Templates/SchedulerUI/UIxCalMainView.wox @@ -92,10 +92,10 @@ - - - - + + + + diff --git a/UI/Templates/UIxPageFrame.wox b/UI/Templates/UIxPageFrame.wox index d942676e..3d936ae6 100644 --- a/UI/Templates/UIxPageFrame.wox +++ b/UI/Templates/UIxPageFrame.wox @@ -52,16 +52,37 @@ - | + + + + + + + | - | + + + + + + + | - | + + + + + + + | 0) { var folderOwner = selected[0].getAttribute("owner"); + var modifyOption = $(this).down("ul").childElements().first(); var sharingOption = $(this).down("ul").childElements().last(); - // Disable the "Sharing" option when address book is not owned by user - if (folderOwner == UserLogin || IsSuperUser) + // Disable the "Sharing" and "Modify" options when address book + // is not owned by user + if (folderOwner == UserLogin || IsSuperUser) { + modifyOption.removeClassName("disabled"); sharingOption.removeClassName("disabled"); - else + } + else { + modifyOption.addClassName("disabled"); sharingOption.addClassName("disabled"); + } } } diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index e4ebdd0d..8dd803d1 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -1,7 +1,5 @@ /* JavaScript for SOGoCalendar */ -var sortOrder = ''; -var sortKey = ''; var listFilter = 'view_today'; var listOfSelection = null; @@ -328,9 +326,6 @@ function eventsListCallback(http) { document.eventsListAjaxRequest = null; var table = $("eventsList"); - var params = parseQueryParameters(http.callbackData); - sortKey = params["sort"]; - sortOrder = params["desc"]; lastClickedRow = -1; // from generic.js if (http.responseText.length > 0) { @@ -374,6 +369,24 @@ function eventsListCallback(http) { Event.observe(td, "mousedown", listRowMouseDownHandler, true); td.appendChild(document.createTextNode(data[i][6])); } + + if (sorting["attribute"] && sorting["attribute"].length > 0) { + var sortHeader = $(sorting["attribute"] + "Header"); + + if (sortHeader) { + var sortImages = $(table.tHead).getElementsByClassName("sortImage"); + $(sortImages).each(function(item) { + item.remove(); + }); + + var sortImage = createElement("img", "messageSortImage", "sortImage"); + sortHeader.insertBefore(sortImage, sortHeader.firstChild); + if (sorting["ascending"]) + sortImage.src = ResourcesURL + "/title_sortdown_12x12.png"; + else + sortImage.src = ResourcesURL + "/title_sortup_12x12.png"; + } + } } } else @@ -995,10 +1008,28 @@ function _loadTasksHref(href) { } function onHeaderClick(event) { - //log("onHeaderClick: " + this.link); - //_loadEventHref(this.link); + var headerId = this.getAttribute("id"); + var newSortAttribute; + if (headerId == "titleHeader") + newSortAttribute = "title"; + else if (headerId == "startHeader") + newSortAttribute = "start"; + else if (headerId == "endHeader") + newSortAttribute = "end"; + else if (headerId == "locationHeader") + newSortAttribute = "location"; + else + newSortAttribute = "start"; + + if (sorting["attribute"] == newSortAttribute) + sorting["ascending"] = !sorting["ascending"]; + else { + sorting["attribute"] = newSortAttribute; + sorting["ascending"] = true; + } + refreshEvents(); - preventDefault(event); + Event.stop(event); } function refreshCurrentFolder() { @@ -1012,9 +1043,9 @@ function refreshEvents() { titleSearch = "&search=" + value; else titleSearch = ""; - - return _loadEventHref("eventslist?desc=" + sortOrder - + "&sort=" + sortKey + + return _loadEventHref("eventslist?asc=" + sorting["ascending"] + + "&sort=" + sorting["attribute"] + "&day=" + currentDay + titleSearch + "&filterpopup=" + listFilter); @@ -1517,25 +1548,13 @@ function appendCalendar(folderName, folderPath) { window.alert(clabels["You have already subscribed to that folder!"]); else { var calendarList = $("calendarList"); - var lis = calendarList.childNodesWithTag("li"); + var items = calendarList.childNodesWithTag("li"); var li = document.createElement("li"); // Add the calendar to the proper place - var previousOwner = null; - for (var i = 0; i < lis.length; i++) { - var currentFolderName = lis[i].lastChild.nodeValue.strip(); - var currentOwner = lis[i].readAttribute('owner'); - if (currentOwner == owner) { - previousOwner = currentOwner; - if (currentFolderName > folderName) - break; - } - else if (previousOwner || - (currentOwner != UserLogin && currentOwner > owner)) - break; - } - if (i != lis.length) // User is subscribed to other calendars of the same owner - calendarList.insertBefore(li, lis[i]); + var i = getListIndexForFolder(items, owner, folderName); + if (i != items.length) // User is subscribed to other calendars of the same owner + calendarList.insertBefore(li, items[i]); else calendarList.appendChild(li); @@ -1544,7 +1563,7 @@ function appendCalendar(folderName, folderPath) { // Generate new color if (calendarColorIndex == null) - calendarColorIndex = lis.length; + calendarColorIndex = items.length; calendarColorIndex++; var colorTable = [1, 1, 1]; var color; @@ -1699,7 +1718,7 @@ function configureLists() { list = $("eventsList"); list.multiselect = true; - //configureSortableTableHeaders(list); + configureSortableTableHeaders(list); TableKit.Resizable.init(list, {'trueResize' : true, 'keepWidth' : true}); Event.observe(list, "mousedown", onEventsSelectionChange.bindAsEventListener(list)); @@ -1722,6 +1741,9 @@ function initDateSelectorEvents() { } function initCalendars() { + sorting["attribute"] = "start"; + sorting["ascending"] = true; + if (!document.body.hasClassName("popup")) { initDateSelectorEvents(); initCalendarSelector(); diff --git a/UI/WebServerResources/UIxMailToSelection.js b/UI/WebServerResources/UIxMailToSelection.js index e76dfd54..8a9a686f 100644 --- a/UI/WebServerResources/UIxMailToSelection.js +++ b/UI/WebServerResources/UIxMailToSelection.js @@ -44,45 +44,11 @@ function hasAddress(email) { return false; } -function rememberAddress(email) { - var list, span, idx; - - list = $('addr_addresses'); - span = document.createElement('span'); - span.id = email; - idx = document.createTextNode(currentIndex); - span.appendChild(idx); - list.appendChild(span); -} - function checkAddresses() { alert("addressCount: " + this.getAddressCount() + " currentIndex: " + currentIndex + " lastIndex: " + lastIndex); } -function addAddress(type, email, uid, sn, cn, dn) { - var shouldAddRow, s, e; - - shouldAddRow = true; - if (cn) - s = this.sanitizedCn(cn) + ' <' + email + '>'; - else - s = email; - - if(this.hasAddress(email)) - return; - - e = $('addr_0'); - if(e.value == '') { - e.value = s; - shouldAddRow = false; - } - if(shouldAddRow) { - this.fancyAddRow(false, s); - } - this.rememberAddress(email); -} - -function fancyAddRow(shouldEdit, text) { +function fancyAddRow(shouldEdit, text, type) { var addr, addressList, lastChild, proto, row, select, input; addr = $('addr_' + lastIndex); @@ -106,7 +72,7 @@ function fancyAddRow(shouldEdit, text) { var rowNodes = row.childNodesWithTag("td"); select = $(rowNodes[0]).childNodesWithTag("select")[0]; select.name = 'popup_' + currentIndex; - select.value = proto.down("select").value; + select.value = (type? type : proto.down("select").value); input = $(rowNodes[1]).childNodesWithTag("input")[0]; input.name = 'addr_' + currentIndex; input.id = 'addr_' + currentIndex; @@ -136,6 +102,16 @@ function addressFieldGotFocus(sender) { function addressFieldLostFocus(sender) { lastIndex = this.getIndexFromIdentifier(sender.id); + var addresses = sender.value.split(','); + if (addresses.length > 0) { + sender.value = addresses[0].strip(); + for (var i = 1; i < addresses.length; i++) { + var addr = addresses[i].strip(); + if (addr.length > 0) + fancyAddRow(false, addr, $(sender).up("tr").down("select").value); + } + } + return false; } @@ -148,27 +124,10 @@ function removeLastEditedRowIfEmpty() { addr = $('addr_' + lastIndex); if (!addr) return; if (addr.value.strip() != '') return; - addr = this.findAddressWithIndex(lastIndex); - if(addr) { - var addresses = $('addr_addresses'); - addresses.removeChild(addr); - } senderRow = $("row_" + lastIndex); addressList.removeChild(senderRow); } -function findAddressWithIndex(idx) { - var list, i, count, addr, idx - list = $('addr_addresses').childNodes; - count = list.length; - for(i = 0; i < count; i++) { - addr = list[i]; - if(addr.innerHTML == idx) - return addr; - } - return null; -} - function getIndexFromIdentifier(id) { return id.split('_')[1]; } diff --git a/UI/WebServerResources/generic.css b/UI/WebServerResources/generic.css index 62776f37..0200a322 100644 --- a/UI/WebServerResources/generic.css +++ b/UI/WebServerResources/generic.css @@ -137,12 +137,17 @@ DIV.linkbanner z-index: 100; color: #aaa; } -DIV.linkbanner A -{ top: 0px; - left: 0px; +DIV.linkbanner A, +DIV.linkbanner SPAN +{ cursor: default; color: #ddd; text-decoration: none; - padding: .2em .5em; } + top: 0px; + left: 0px; + padding: 2px 5px; } + +DIV.linkbanner SPAN +{ color: #afafff; } DIV.linkbanner A:hover { color: #dd5; } diff --git a/UI/WebServerResources/generic.js b/UI/WebServerResources/generic.js index 6313cd2e..054005e0 100644 --- a/UI/WebServerResources/generic.js +++ b/UI/WebServerResources/generic.js @@ -920,7 +920,7 @@ function onSearchKeyDown(event) { if (event.keyCode == 13) { onSearchFormSubmit(); - event.preventDefault(); + preventDefault(event); } else this.timer = setTimeout("onSearchFormSubmit()", 1000); @@ -1070,6 +1070,28 @@ function getSubscribedFolderOwner(serverFolder) { return owner; } +function getListIndexForFolder(items, owner, folderName) { + var i; + var previousOwner = null; + + for (var i = 0; i < items.length; i++) { + var currentFolderName = items[i].lastChild.nodeValue.strip(); + var currentOwner = items[i].readAttribute('owner'); + if (currentOwner == owner) { + previousOwner = currentOwner; + if (currentFolderName > folderName) + break; + } + else if (previousOwner || + (currentOwner != UserLogin && currentOwner > owner)) + break; + else if (currentOwner == "nobody") + break; + } + + return i; +} + function listRowMouseDownHandler(event) { preventDefault(event); //Event.stop(event); -- 2.39.5