From b2ed7e3f6baf79e1aaa580375b050fa2870697bd Mon Sep 17 00:00:00 2001 From: Alexandre Cloutier Date: Thu, 8 May 2014 17:17:05 -0400 Subject: [PATCH 1/5] search capabilites --- .../English.lproj/Localizable.strings | 8 +- UI/Scheduler/UIxCalFilterPanel.m | 4 +- UI/Scheduler/UIxCalListingActions.h | 3 +- UI/Scheduler/UIxCalListingActions.m | 1195 +++++++++-------- UI/Scheduler/UIxCalMainView.m | 27 +- .../SchedulerUI/UIxCalFilterPanel.wox | 10 +- UI/Templates/SchedulerUI/UIxCalMainView.wox | 21 +- UI/WebServerResources/SchedulerUI.js | 141 +- UI/WebServerResources/generic.css | 3 +- 9 files changed, 792 insertions(+), 620 deletions(-) diff --git a/UI/Scheduler/English.lproj/Localizable.strings b/UI/Scheduler/English.lproj/Localizable.strings index 900fa9614..96f9c753c 100644 --- a/UI/Scheduler/English.lproj/Localizable.strings +++ b/UI/Scheduler/English.lproj/Localizable.strings @@ -225,8 +225,14 @@ "view_future" = "All Future Events"; "view_selectedday" = "Selected Day"; +"view_current" = "Current tasks"; +"view_not_started" = "Not started tasks"; +"view_overdue" = "Overdue tasks"; +"view_incomplete" = "Incomplete tasks"; + "View:" = "View:"; -"Title or Description" = "Title or Description"; +"Title, category or location" = "Title, category or location"; +"Entire content" = "Entire content"; "Search" = "Search"; "Search attendees" = "Search attendees"; diff --git a/UI/Scheduler/UIxCalFilterPanel.m b/UI/Scheduler/UIxCalFilterPanel.m index c9ebf5be9..21a5b2a57 100644 --- a/UI/Scheduler/UIxCalFilterPanel.m +++ b/UI/Scheduler/UIxCalFilterPanel.m @@ -39,14 +39,14 @@ static NSArray *filters = nil; + (void) initialize { static NSString *quals[] - = { // @"view_all", + = {@"view_all", @"view_today", @"view_next7", @"view_next14", @"view_next31", @"view_thismonth", @"view_future", @"view_selectedday" }; if (!filters) { - filters = [NSArray arrayWithObjects: quals count: 7]; + filters = [NSArray arrayWithObjects: quals count: 8]; [filters retain]; } } diff --git a/UI/Scheduler/UIxCalListingActions.h b/UI/Scheduler/UIxCalListingActions.h index 02c13488e..4f12d8cfe 100644 --- a/UI/Scheduler/UIxCalListingActions.h +++ b/UI/Scheduler/UIxCalListingActions.h @@ -40,7 +40,8 @@ NSMutableDictionary *componentsData; NSCalendarDate *startDate; NSCalendarDate *endDate; - NSString *title; + NSString *value; + NSString *criteria; NSString *userLogin; BOOL dayBasedView; WORequest *request; diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m index 5f99777a7..337e2e80d 100644 --- a/UI/Scheduler/UIxCalListingActions.m +++ b/UI/Scheduler/UIxCalListingActions.m @@ -37,6 +37,8 @@ #import #import #import +#import +#import #import #import @@ -70,57 +72,57 @@ static NSArray *tasksFields = nil; #define quarterLength 900 // number of seconds in 15 minutes #define offsetHours (24 * 5) // number of hours in invitation window #define offsetSeconds (offsetHours * 60 * 60) // number of seconds in - // invitation window +// invitation window /* 1 block = 15 minutes */ #define offsetBlocks (offsetHours * 4) // number of 15-minute blocks in invitation window #define maxBlocks (offsetBlocks * 2) // maximum number of blocks to search - // for a free slot (10 days) +// for a free slot (10 days) @implementation UIxCalListingActions + (void) initialize { if (!eventsFields) - { - eventsFields = [NSArray arrayWithObjects: @"c_name", @"c_folder", - @"calendarName", - @"c_status", @"c_title", @"c_startdate", - @"c_enddate", @"c_location", @"c_isallday", - @"c_classification", @"c_category", - @"c_partmails", @"c_partstates", @"c_owner", - @"c_iscycle", @"c_nextalarm", - @"c_recurrence_id", @"isException", @"editable", - @"erasable", @"ownerIsOrganizer", nil]; - [eventsFields retain]; - } + { + eventsFields = [NSArray arrayWithObjects: @"c_name", @"c_folder", + @"calendarName", + @"c_status", @"c_title", @"c_startdate", + @"c_enddate", @"c_location", @"c_isallday", + @"c_classification", @"c_category", + @"c_partmails", @"c_partstates", @"c_owner", + @"c_iscycle", @"c_nextalarm", + @"c_recurrence_id", @"isException", @"editable", + @"erasable", @"ownerIsOrganizer", nil]; + [eventsFields retain]; + } if (!tasksFields) - { - tasksFields = [NSArray arrayWithObjects: @"c_name", @"c_folder", - @"calendarName", - @"c_status", @"c_title", @"c_enddate", - @"c_classification", @"c_location", @"c_category", - @"editable", @"erasable", - @"c_priority", nil]; - [tasksFields retain]; - } + { + tasksFields = [NSArray arrayWithObjects: @"c_name", @"c_folder", + @"calendarName", + @"c_status", @"c_title", @"c_enddate", + @"c_classification", @"c_location", @"c_category", + @"editable", @"erasable", + @"c_priority", nil]; + [tasksFields retain]; + } } - (id) initWithRequest: (WORequest *) newRequest { SOGoUser *user; - + if ((self = [super initWithRequest: newRequest])) - { - componentsData = [NSMutableDictionary new]; - startDate = nil; - endDate = nil; - ASSIGN (request, newRequest); - user = [[self context] activeUser]; - ASSIGN (dateFormatter, [user dateFormatterInContext: context]); - ASSIGN (userTimeZone, [[user userDefaults] timeZone]); - dayBasedView = NO; - } - + { + componentsData = [NSMutableDictionary new]; + startDate = nil; + endDate = nil; + ASSIGN (request, newRequest); + user = [[self context] activeUser]; + ASSIGN (dateFormatter, [user dateFormatterInContext: context]); + ASSIGN (userTimeZone, [[user userDefaults] timeZone]); + dayBasedView = NO; + } + return self; } @@ -134,121 +136,122 @@ static NSArray *tasksFields = nil; } - (void) _setupDatesWithPopup: (NSString *) popupValue - andUserTZ: (NSTimeZone *) userTZ + andUserTZ: (NSTimeZone *) userTZ { NSCalendarDate *newDate; NSString *param; - + if ([popupValue isEqualToString: @"view_today"]) - { - newDate = [NSCalendarDate calendarDate]; - [newDate setTimeZone: userTZ]; - startDate = [newDate beginOfDay]; - endDate = [newDate endOfDay]; - } + { + newDate = [NSCalendarDate calendarDate]; + [newDate setTimeZone: userTZ]; + startDate = [newDate beginOfDay]; + endDate = [newDate endOfDay]; + } else if ([popupValue isEqualToString: @"view_all"]) - { - startDate = nil; - endDate = nil; - } + { + startDate = nil; + endDate = nil; + } else if ([popupValue isEqualToString: @"view_next7"]) - { - newDate = [NSCalendarDate calendarDate]; - [newDate setTimeZone: userTZ]; - startDate = [newDate beginOfDay]; - endDate = [[startDate dateByAddingYears: 0 months: 0 days: 6] endOfDay]; - } + { + newDate = [NSCalendarDate calendarDate]; + [newDate setTimeZone: userTZ]; + startDate = [newDate beginOfDay]; + endDate = [[startDate dateByAddingYears: 0 months: 0 days: 6] endOfDay]; + } else if ([popupValue isEqualToString: @"view_next14"]) - { - newDate = [NSCalendarDate calendarDate]; - [newDate setTimeZone: userTZ]; - startDate = [newDate beginOfDay]; - endDate = [[startDate dateByAddingYears: 0 months: 0 days: 13] endOfDay]; - } + { + newDate = [NSCalendarDate calendarDate]; + [newDate setTimeZone: userTZ]; + startDate = [newDate beginOfDay]; + endDate = [[startDate dateByAddingYears: 0 months: 0 days: 13] endOfDay]; + } else if ([popupValue isEqualToString: @"view_next31"]) - { - newDate = [NSCalendarDate calendarDate]; - [newDate setTimeZone: userTZ]; - startDate = [newDate beginOfDay]; - endDate = [[startDate dateByAddingYears: 0 months: 0 days: 30] endOfDay]; - } + { + newDate = [NSCalendarDate calendarDate]; + [newDate setTimeZone: userTZ]; + startDate = [newDate beginOfDay]; + endDate = [[startDate dateByAddingYears: 0 months: 0 days: 30] endOfDay]; + } else if ([popupValue isEqualToString: @"view_thismonth"]) - { - newDate = [NSCalendarDate calendarDate]; - [newDate setTimeZone: userTZ]; - startDate = [[newDate firstDayOfMonth] beginOfDay]; - endDate = [[newDate lastDayOfMonth] endOfDay]; - } + { + newDate = [NSCalendarDate calendarDate]; + [newDate setTimeZone: userTZ]; + startDate = [[newDate firstDayOfMonth] beginOfDay]; + endDate = [[newDate lastDayOfMonth] endOfDay]; + } else if ([popupValue isEqualToString: @"view_future"]) + { + newDate = [NSCalendarDate calendarDate]; + [newDate setTimeZone: userTZ]; + startDate = [newDate beginOfDay]; + endDate = nil; + } + else if ([popupValue isEqualToString: @"view_selectedday"]) + { + param = [request formValueForKey: @"day"]; + if ([param length] > 0) + startDate = [[NSCalendarDate dateFromShortDateString: param + andShortTimeString: nil + inTimeZone: userTZ] beginOfDay]; + else { newDate = [NSCalendarDate calendarDate]; [newDate setTimeZone: userTZ]; startDate = [newDate beginOfDay]; - endDate = nil; - } - else if ([popupValue isEqualToString: @"view_selectedday"]) - { - param = [request formValueForKey: @"day"]; - if ([param length] > 0) - startDate = [[NSCalendarDate dateFromShortDateString: param - andShortTimeString: nil - inTimeZone: userTZ] beginOfDay]; - else - { - newDate = [NSCalendarDate calendarDate]; - [newDate setTimeZone: userTZ]; - startDate = [newDate beginOfDay]; - } - endDate = [startDate endOfDay]; } + endDate = [startDate endOfDay]; + } } - (void) _setupContext { SOGoUser *user; NSString *param; - + user = [context activeUser]; userLogin = [user login]; - - title = [request formValueForKey: @"search"]; + + criteria = [request formValueForKey: @"search"]; + value = [request formValueForKey: @"value"]; param = [request formValueForKey: @"filterpopup"]; if ([param length]) - { - [self _setupDatesWithPopup: param andUserTZ: userTimeZone]; - } + { + [self _setupDatesWithPopup: param andUserTZ: userTimeZone]; + } else - { - param = [request formValueForKey: @"sd"]; - if ([param length] > 0) - startDate = [[NSCalendarDate dateFromShortDateString: param - andShortTimeString: nil - inTimeZone: userTimeZone] beginOfDay]; - else - startDate = nil; - - param = [request formValueForKey: @"ed"]; - if ([param length] > 0) - endDate = [[NSCalendarDate dateFromShortDateString: param - andShortTimeString: nil - inTimeZone: userTimeZone] endOfDay]; - else - endDate = nil; - - param = [request formValueForKey: @"view"]; - dayBasedView = ![param isEqualToString: @"monthview"]; - } + { + param = [request formValueForKey: @"sd"]; + if ([param length] > 0) + startDate = [[NSCalendarDate dateFromShortDateString: param + andShortTimeString: nil + inTimeZone: userTimeZone] beginOfDay]; + else + startDate = nil; + + param = [request formValueForKey: @"ed"]; + if ([param length] > 0) + endDate = [[NSCalendarDate dateFromShortDateString: param + andShortTimeString: nil + inTimeZone: userTimeZone] endOfDay]; + else + endDate = nil; + + param = [request formValueForKey: @"view"]; + dayBasedView = ![param isEqualToString: @"monthview"]; + } } - (void) _fixComponentTitle: (NSMutableDictionary *) component withType: (NSString *) type { NSString *labelKey; - + labelKey = [NSString stringWithFormat: @"%@_class%@", - type, [component objectForKey: @"c_classification"]]; + type, [component objectForKey: @"c_classification"]]; [component setObject: [self labelForKey: labelKey] - forKey: @"c_title"]; + forKey: @"c_title"]; } /* @@ -264,173 +267,255 @@ static NSArray *tasksFields = nil; int daylightOffset; unsigned int count; static NSString *fields[] = { @"startDate", @"c_startdate", - @"endDate", @"c_enddate" }; + @"endDate", @"c_enddate" }; /* WARNING: This condition has been put and removed many times, please leave - it. Here is the story... - If _fixDates: is conditional to dayBasedView, the recurrences are computed - properly but the display time is wrong. - If _fixDates: is non-conditional, the reverse occurs. - If only this part of _fixDates: is conditional, both are right. - - Regarding all day events, we need to execute this code no matter what the - date and the view are, otherwise the event will span on two days. - - ref bugs: - http://www.sogo.nu/bugs/view.php?id=909 - http://www.sogo.nu/bugs/view.php?id=678 - ... - */ + it. Here is the story... + If _fixDates: is conditional to dayBasedView, the recurrences are computed + properly but the display time is wrong. + If _fixDates: is non-conditional, the reverse occurs. + If only this part of _fixDates: is conditional, both are right. + + Regarding all day events, we need to execute this code no matter what the + date and the view are, otherwise the event will span on two days. + + ref bugs: + http://www.sogo.nu/bugs/view.php?id=909 + http://www.sogo.nu/bugs/view.php?id=678 + ... + */ //NSLog(@"***[UIxCalListingActions _fixDates:] %@", [theRecord objectForKey: @"c_title"]); if (dayBasedView || [[theRecord objectForKey: @"c_isallday"] boolValue]) + { + for (count = 0; count < 2; count++) { - for (count = 0; count < 2; count++) - { - aDateField = fields[count * 2]; - aDate = [theRecord objectForKey: aDateField]; - daylightOffset = (int) ([userTimeZone secondsFromGMTForDate: aDate] - - [userTimeZone secondsFromGMTForDate: startDate]); - //NSLog(@"***[UIxCalListingActions _fixDates:] %@ = %@ (%i)", aDateField, aDate, daylightOffset); - if (daylightOffset) - { - aDate = [aDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 - minutes: 0 seconds: daylightOffset]; - [theRecord setObject: aDate forKey: aDateField]; - aDateValue = [NSNumber numberWithInt: [aDate timeIntervalSince1970]]; - [theRecord setObject: aDateValue forKey: fields[count * 2 + 1]]; - } - } + aDateField = fields[count * 2]; + aDate = [theRecord objectForKey: aDateField]; + daylightOffset = (int) ([userTimeZone secondsFromGMTForDate: aDate] + - [userTimeZone secondsFromGMTForDate: startDate]); + //NSLog(@"***[UIxCalListingActions _fixDates:] %@ = %@ (%i)", aDateField, aDate, daylightOffset); + if (daylightOffset) + { + aDate = [aDate dateByAddingYears: 0 months: 0 days: 0 hours: 0 + minutes: 0 seconds: daylightOffset]; + [theRecord setObject: aDate forKey: aDateField]; + aDateValue = [NSNumber numberWithInt: [aDate timeIntervalSince1970]]; + [theRecord setObject: aDateValue forKey: fields[count * 2 + 1]]; + } } + } } - (NSArray *) _fetchFields: (NSArray *) fields - forComponentOfType: (NSString *) component + forComponentOfType: (NSString *) component { NSEnumerator *folders, *currentInfos; - SOGoAppointmentFolder *currentFolder; NSMutableDictionary *newInfo; - NSMutableArray *infos, *newInfoForComponent; + NSMutableArray *infos, *newInfoForComponent, *quickInfos, *allInfos, *quickInfosName; NSNull *marker; + NSString *owner, *role, *calendarName, *filters, *iCalString; + NSRange match; + iCalCalendar *calendar; + iCalObject *master; + SOGoAppointmentFolder *currentFolder; SOGoAppointmentFolders *clientObject; SOGoUser *ownerUser; - NSString *owner, *role, *calendarName; - BOOL isErasable, folderIsRemote; + + BOOL isErasable, folderIsRemote, quickInfosFlag = NO; id currentInfo; int i, count; - + infos = [NSMutableArray array]; marker = [NSNull null]; clientObject = [self clientObject]; - + folders = [[clientObject subFolders] objectEnumerator]; while ((currentFolder = [folders nextObject])) + { + if ([currentFolder isActive]) { - if ([currentFolder isActive]) + folderIsRemote = [currentFolder isKindOfClass: [SOGoWebAppointmentFolder class]]; + + if ([criteria isEqualToString:@"title_Category_Location"]) + currentInfos = [[currentFolder fetchCoreInfosFrom: startDate + to: endDate + title: value + component: component + additionalFilters: criteria] objectEnumerator]; + + else if ([criteria isEqualToString:@"entireContent"]) + { + // First research : Through the quick table inside the location, category and title columns + quickInfos = [currentFolder fetchCoreInfosFrom: startDate + to: endDate + title: value + component: component + additionalFilters: criteria]; + + // Save the c_name in another array to compare with + if ([quickInfos count] > 0) { - folderIsRemote - = [currentFolder isKindOfClass: [SOGoWebAppointmentFolder class]]; - currentInfos - = [[currentFolder fetchCoreInfosFrom: startDate - to: endDate - title: title - component: component] objectEnumerator]; - owner = [currentFolder ownerInContext: context]; - ownerUser = [SOGoUser userWithLogin: owner]; - isErasable = ([owner isEqualToString: userLogin] - || [[currentFolder aclsForUser: userLogin] containsObject: SOGoRole_ObjectEraser]); - while ((newInfo = [currentInfos nextObject])) + quickInfosFlag = YES; + quickInfosName = [NSMutableArray arrayWithObject:[[quickInfos objectAtIndex:0] objectForKey:@"c_name"]]; + for (i = 1; i < [quickInfos count]; i++) + [quickInfosName addObject:[[quickInfos objectAtIndex:i] objectForKey:@"c_name"]]; + } + + // Second research : Every objects except for those already in the quickInfos array + allInfos = [currentFolder fetchCoreInfosFrom: startDate + to: endDate + title: nil + component: component]; + + for (i = 0; i < [allInfos count]; i++) + { + if (quickInfosFlag) + { + if (![quickInfosName containsObject:[[allInfos objectAtIndex:i] objectForKey:@"c_name"]]) { - if ([fields containsObject: @"editable"]) + iCalString = [[allInfos objectAtIndex:i] objectForKey:@"c_content"]; + calendar = [iCalCalendar parseSingleFromSource: iCalString]; + master = [calendar firstChildWithTag:@"vevent"]; + if (!master) + master = [calendar firstChildWithTag:@"vtodo"]; + + if (master) { + if ([[master comment] length] > 0) { - if (folderIsRemote) - // .ics subscriptions are not editable - [newInfo setObject: [NSNumber numberWithInt: 0] - forKey: @"editable"]; - else - { - // Identifies whether the active user can edit the event. - role = - [currentFolder roleForComponentsWithAccessClass: - [[newInfo objectForKey: @"c_classification"] intValue] - forUser: userLogin]; - if ([role isEqualToString: @"ComponentModifier"] - || [role length] == 0) - [newInfo setObject: [NSNumber numberWithInt: 1] - forKey: @"editable"]; - else - [newInfo setObject: [NSNumber numberWithInt: 0] - forKey: @"editable"]; - } + match = [[master comment] rangeOfString:value]; + if (match.length > 0) { + [quickInfos addObject:[allInfos objectAtIndex:i]]; + } } - if ([fields containsObject: @"ownerIsOrganizer"]) - { - // Identifies whether the active user is the organizer - // of this event. - NSString *c_orgmail; - c_orgmail = [newInfo objectForKey: @"c_orgmail"]; + } + } + } + else + { + iCalString = [[allInfos objectAtIndex:i] objectForKey:@"c_content"]; + calendar = [iCalCalendar parseSingleFromSource: iCalString]; + master = [calendar firstChildWithTag:@"vevent"]; + if (!master) + master = [calendar firstChildWithTag:@"vtodo"]; + + if (master) { + if ([[master comment] length] > 0) + { + match = [[master comment] rangeOfString:value]; + if (match.length > 0) { + [quickInfos addObject:[allInfos objectAtIndex:i]]; + } + } + } + } + } + currentInfos = [quickInfos objectEnumerator]; + } + - if ([c_orgmail isKindOfClass: [NSString class]] && [ownerUser hasEmail: c_orgmail]) - [newInfo setObject: [NSNumber numberWithInt: 1] - forKey: @"ownerIsOrganizer"]; - else - [newInfo setObject: [NSNumber numberWithInt: 0] - forKey: @"ownerIsOrganizer"]; - } + else + currentInfos = [[currentFolder fetchCoreInfosFrom: startDate + to: endDate + title: value + component: component] objectEnumerator]; + + owner = [currentFolder ownerInContext: context]; + ownerUser = [SOGoUser userWithLogin: owner]; + isErasable = ([owner isEqualToString: userLogin] || [[currentFolder aclsForUser: userLogin] containsObject: SOGoRole_ObjectEraser]); + + while ((newInfo = [currentInfos nextObject])) + { + if ([fields containsObject: @"editable"]) + { + if (folderIsRemote) + // .ics subscriptions are not editable + [newInfo setObject: [NSNumber numberWithInt: 0] + forKey: @"editable"]; + else + { + // Identifies whether the active user can edit the event. + role = + [currentFolder roleForComponentsWithAccessClass: + [[newInfo objectForKey: @"c_classification"] intValue] + forUser: userLogin]; + if ([role isEqualToString: @"ComponentModifier"] + || [role length] == 0) + [newInfo setObject: [NSNumber numberWithInt: 1] + forKey: @"editable"]; + else + [newInfo setObject: [NSNumber numberWithInt: 0] + forKey: @"editable"]; + } + } + if ([fields containsObject: @"ownerIsOrganizer"]) + { + // Identifies whether the active user is the organizer + // of this event. + NSString *c_orgmail; + c_orgmail = [newInfo objectForKey: @"c_orgmail"]; + + if ([c_orgmail isKindOfClass: [NSString class]] && [ownerUser hasEmail: c_orgmail]) + [newInfo setObject: [NSNumber numberWithInt: 1] + forKey: @"ownerIsOrganizer"]; + else + [newInfo setObject: [NSNumber numberWithInt: 0] + forKey: @"ownerIsOrganizer"]; + } if (isErasable) - [newInfo setObject: [NSNumber numberWithInt: 1] - forKey: @"erasable"]; + [newInfo setObject: [NSNumber numberWithInt: 1] + forKey: @"erasable"]; else - [newInfo setObject: [NSNumber numberWithInt: 0] - forKey: @"erasable"]; + [newInfo setObject: [NSNumber numberWithInt: 0] + forKey: @"erasable"]; [newInfo setObject: [currentFolder nameInContainer] - forKey: @"c_folder"]; - [newInfo setObject: [currentFolder ownerInContext: context] - forKey: @"c_owner"]; - calendarName = [currentFolder displayName]; - if (calendarName == nil) - calendarName = @""; - [newInfo setObject: calendarName - forKey: @"calendarName"]; - if (![[newInfo objectForKey: @"c_title"] length]) - [self _fixComponentTitle: newInfo withType: component]; + forKey: @"c_folder"]; + [newInfo setObject: [currentFolder ownerInContext: context] + forKey: @"c_owner"]; + calendarName = [currentFolder displayName]; + if (calendarName == nil) + calendarName = @""; + [newInfo setObject: calendarName + forKey: @"calendarName"]; + if (![[newInfo objectForKey: @"c_title"] length]) + [self _fixComponentTitle: newInfo withType: component]; // Possible improvement: only call _fixDates if event is recurrent // or the view range span a daylight saving time change - [self _fixDates: newInfo]; - newInfoForComponent = [NSMutableArray arrayWithArray: [newInfo objectsForKeys: fields - notFoundMarker: marker]]; - // Escape HTML - count = [newInfoForComponent count]; - for (i = 0; i < count; i++) - { - currentInfo = [newInfoForComponent objectAtIndex: i]; - if ([currentInfo respondsToSelector: @selector (stringByEscapingHTMLString)]) - [newInfoForComponent replaceObjectAtIndex: i withObject: [currentInfo stringByEscapingHTMLString]]; - } - [infos addObject: newInfoForComponent]; - } + [self _fixDates: newInfo]; + newInfoForComponent = [NSMutableArray arrayWithArray: [newInfo objectsForKeys: fields + notFoundMarker: marker]]; + // Escape HTML + count = [newInfoForComponent count]; + for (i = 0; i < count; i++) + { + currentInfo = [newInfoForComponent objectAtIndex: i]; + if ([currentInfo respondsToSelector: @selector (stringByEscapingHTMLString)]) + [newInfoForComponent replaceObjectAtIndex: i withObject: [currentInfo stringByEscapingHTMLString]]; } + [infos addObject: newInfoForComponent]; + } } - + } + return infos; } - (WOResponse *) _responseWithData: (NSArray *) data { WOResponse *response; - + response = [self responseWithStatus: 200]; [response appendContentString: [data jsonRepresentation]]; - + return response; } - (NSString *) _formattedDateForSeconds: (unsigned int) seconds - forAllDay: (BOOL) forAllDay + forAllDay: (BOOL) forAllDay { NSCalendarDate *date; NSString *formattedDate; - + date = [NSCalendarDate dateWithTimeIntervalSince1970: seconds]; // Adjust for daylight saving time? (wrt to startDate) //NSLog(@"***[UIxCalListingActions _formattedDateForSeconds] user timezone is %@", userTimeZone); @@ -439,13 +524,13 @@ static NSArray *tasksFields = nil; formattedDate = [dateFormatter formattedDate: date]; else formattedDate = [dateFormatter formattedDateAndTime: date]; - - return formattedDate; + + return formattedDate; } // // We return: -// +// // [[calendar name (full path), complete Event ID (full path), Fire date (UTC)], ..] // // Called when each module is loaded or whenever a calendar component is created, modified, deleted @@ -466,42 +551,42 @@ static NSArray *tasksFields = nil; NSEnumerator *folders; WOResponse *response; unsigned int browserTime, laterTime; - + // We look for alarms in the next 48 hours browserTime = [[[context request] formValueForKey: @"browserTime"] intValue]; laterTime = browserTime + 60*60*48; clientObject = [self clientObject]; allAlarms = [NSMutableArray array]; - + folders = [[clientObject subFolders] objectEnumerator]; while ((currentFolder = [folders nextObject])) + { + if ([currentFolder isActive] && [currentFolder showCalendarAlarms]) { - if ([currentFolder isActive] && [currentFolder showCalendarAlarms]) - { - NSDictionary *entry; - NSArray *alarms; - BOOL isCycle; - int i; - - alarms = [currentFolder fetchAlarmInfosFrom: [NSNumber numberWithInt: browserTime] - to: [NSNumber numberWithInt: laterTime]]; - - for (i = 0; i < [alarms count]; i++) + NSDictionary *entry; + NSArray *alarms; + BOOL isCycle; + int i; + + alarms = [currentFolder fetchAlarmInfosFrom: [NSNumber numberWithInt: browserTime] + to: [NSNumber numberWithInt: laterTime]]; + + for (i = 0; i < [alarms count]; i++) { entry = [alarms objectAtIndex: i]; isCycle = [[entry objectForKey: @"c_iscycle"] boolValue]; if (!isCycle) - { - [allAlarms addObject: [NSArray arrayWithObjects: - [currentFolder nameInContainer], - [entry objectForKey: @"c_name"], - [entry objectForKey: @"c_nextalarm"], - nil]]; - } + { + [allAlarms addObject: [NSArray arrayWithObjects: + [currentFolder nameInContainer], + [entry objectForKey: @"c_name"], + [entry objectForKey: @"c_nextalarm"], + nil]]; + } } - } } + } response = [self responseWithStatus: 200]; @@ -514,16 +599,16 @@ static NSArray *tasksFields = nil; { NSString *filter; SOGoUserSettings *us; - + filter = [[context request] formValueForKey: @"filterpopup"]; if ([filter length] && ![filter isEqualToString: @"view_all"] && ![filter isEqualToString: @"view_future"]) - { - us = [[context activeUser] userSettings]; - [us setObject: filter forKey: @"CalendarDefaultFilter"]; - [us synchronize]; - } + { + us = [[context activeUser] userSettings]; + [us setObject: filter forKey: @"CalendarDefaultFilter"]; + [us synchronize]; + } } - (WOResponse *) eventsListAction @@ -534,25 +619,25 @@ static NSArray *tasksFields = nil; unsigned int interval; BOOL isAllDay; NSString *sort, *ascending; - + [self _setupContext]; [self checkFilterValue]; - + newEvents = [NSMutableArray array]; events = [[self _fetchFields: eventsFields - forComponentOfType: @"vevent"] objectEnumerator]; + forComponentOfType: @"vevent"] objectEnumerator]; while ((oldEvent = [events nextObject])) - { - newEvent = [NSMutableArray arrayWithArray: oldEvent]; - isAllDay = [[oldEvent objectAtIndex: eventIsAllDayIndex] boolValue]; - interval = [[oldEvent objectAtIndex: eventStartDateIndex] intValue]; - [newEvent addObject: [self _formattedDateForSeconds: interval - forAllDay: isAllDay]]; - interval = [[oldEvent objectAtIndex: eventEndDateIndex] intValue]; - [newEvent addObject: [self _formattedDateForSeconds: interval - forAllDay: isAllDay]]; - [newEvents addObject: newEvent]; - } + { + newEvent = [NSMutableArray arrayWithArray: oldEvent]; + isAllDay = [[oldEvent objectAtIndex: eventIsAllDayIndex] boolValue]; + interval = [[oldEvent objectAtIndex: eventStartDateIndex] intValue]; + [newEvent addObject: [self _formattedDateForSeconds: interval + forAllDay: isAllDay]]; + interval = [[oldEvent objectAtIndex: eventEndDateIndex] intValue]; + [newEvent addObject: [self _formattedDateForSeconds: interval + forAllDay: isAllDay]]; + [newEvents addObject: newEvent]; + } sort = [[context request] formValueForKey: @"sort"]; if ([sort isEqualToString: @"title"]) @@ -565,26 +650,25 @@ static NSArray *tasksFields = nil; [newEvents sortUsingSelector: @selector (compareEventsCalendarNameAscending:)]; else [newEvents sortUsingSelector: @selector (compareEventsStartDateAscending:)]; - + ascending = [[context request] formValueForKey: @"asc"]; if (![ascending boolValue]) [newEvents reverseArray]; - + return [self _responseWithData: newEvents]; } -static inline void -_feedBlockWithDayBasedData (NSMutableDictionary *block, unsigned int start, - unsigned int end, unsigned int dayStart) +static inline void _feedBlockWithDayBasedData (NSMutableDictionary *block, unsigned int start, + unsigned int end, unsigned int dayStart) { unsigned int delta, quarterStart, length, swap; if (start > end) - { - swap = end; - end = start; - start = swap; - } + { + swap = end; + end = start; + start = swap; + } quarterStart = (start - dayStart) / quarterLength; delta = end - dayStart; if ((delta % quarterLength)) @@ -593,9 +677,9 @@ _feedBlockWithDayBasedData (NSMutableDictionary *block, unsigned int start, if (!length) length = 1; [block setObject: [NSNumber numberWithUnsignedInt: quarterStart] - forKey: @"start"]; + forKey: @"start"]; [block setObject: [NSNumber numberWithUnsignedInt: length] - forKey: @"length"]; + forKey: @"length"]; } static inline void @@ -605,26 +689,26 @@ _feedBlockWithMonthBasedData (NSMutableDictionary *block, unsigned int start, { NSCalendarDate *eventStartDate; NSString *startHour; - + eventStartDate = [NSCalendarDate dateWithTimeIntervalSince1970: start]; [eventStartDate setTimeZone: userTimeZone]; startHour = [dateFormatter formattedTime: eventStartDate]; [block setObject: startHour forKey: @"starthour"]; [block setObject: [NSNumber numberWithUnsignedInt: start] - forKey: @"start"]; + forKey: @"start"]; } - (NSMutableDictionary *) _eventBlockWithStart: (unsigned int) start - end: (unsigned int) end - number: (NSNumber *) number - onDay: (unsigned int) dayStart - recurrenceTime: (unsigned int) recurrenceTime - userState: (iCalPersonPartStat) userState + end: (unsigned int) end + number: (NSNumber *) number + onDay: (unsigned int) dayStart + recurrenceTime: (unsigned int) recurrenceTime + userState: (iCalPersonPartStat) userState { NSMutableDictionary *block; - + block = [NSMutableDictionary dictionary]; - + if (dayBasedView) _feedBlockWithDayBasedData (block, start, end, dayStart); else @@ -632,11 +716,11 @@ _feedBlockWithMonthBasedData (NSMutableDictionary *block, unsigned int start, [block setObject: number forKey: @"nbr"]; if (recurrenceTime) [block setObject: [NSNumber numberWithInt: recurrenceTime] - forKey: @"recurrenceTime"]; + forKey: @"recurrenceTime"]; if (userState != iCalPersonPartStatOther) [block setObject: [NSNumber numberWithInt: userState] - forKey: @"userState"]; - + forKey: @"userState"]; + return block; } @@ -648,155 +732,155 @@ _userStateInEvent (NSArray *event) NSString *partList, *stateList; NSArray *participants, *states; SOGoUser *user; - + participants = nil; state = iCalPersonPartStatOther; - + partList = [event objectAtIndex: eventPartMailsIndex]; stateList = [event objectAtIndex: eventPartStatesIndex]; if ([partList length] && [stateList length]) + { + participants = [partList componentsSeparatedByString: @"\n"]; + states = [stateList componentsSeparatedByString: @"\n"]; + count = 0; + max = [participants count]; + while (state == iCalPersonPartStatOther && count < max) { - participants = [partList componentsSeparatedByString: @"\n"]; - states = [stateList componentsSeparatedByString: @"\n"]; - count = 0; - max = [participants count]; - while (state == iCalPersonPartStatOther && count < max) - { - user = [SOGoUser userWithLogin: [event objectAtIndex: eventOwnerIndex] - roles: nil]; - if ([user hasEmail: [participants objectAtIndex: count]]) - state = [[states objectAtIndex: count] intValue]; - else - count++; - } + user = [SOGoUser userWithLogin: [event objectAtIndex: eventOwnerIndex] + roles: nil]; + if ([user hasEmail: [participants objectAtIndex: count]]) + state = [[states objectAtIndex: count] intValue]; + else + count++; } - + } + return state; } - (void) _fillBlocks: (NSArray *) blocks - withEvent: (NSArray *) event - withNumber: (NSNumber *) number + withEvent: (NSArray *) event + withNumber: (NSNumber *) number { int currentDayStart, startSecs, endsSecs, currentStart, eventStart, - eventEnd, computedEventEnd, offset, recurrenceTime, swap; + eventEnd, computedEventEnd, offset, recurrenceTime, swap; NSMutableArray *currentDay; NSMutableDictionary *eventBlock; iCalPersonPartStat userState; - + eventStart = [[event objectAtIndex: eventStartDateIndex] intValue]; if (eventStart < 0) [self errorWithFormat: @"event '%@' has negative start: %d (skipped)", - [event objectAtIndex: eventNameIndex], eventStart]; + [event objectAtIndex: eventNameIndex], eventStart]; else + { + eventEnd = [[event objectAtIndex: eventEndDateIndex] intValue]; + if (eventEnd < 0) + [self errorWithFormat: @"event '%@' has negative end: %d (skipped)", + [event objectAtIndex: eventNameIndex], eventEnd]; + else { - eventEnd = [[event objectAtIndex: eventEndDateIndex] intValue]; - if (eventEnd < 0) - [self errorWithFormat: @"event '%@' has negative end: %d (skipped)", - [event objectAtIndex: eventNameIndex], eventEnd]; + if (eventEnd < eventStart) + { + swap = eventStart; + eventStart = eventEnd; + eventEnd = swap; + [self warnWithFormat: @"event '%@' has end < start: %d < %d", + [event objectAtIndex: eventNameIndex], eventEnd, eventStart]; + } + + startSecs = (unsigned int) [startDate timeIntervalSince1970]; + endsSecs = (unsigned int) [endDate timeIntervalSince1970]; + + if ([[event objectAtIndex: eventIsCycleIndex] boolValue]) + recurrenceTime = [[event objectAtIndex: eventRecurrenceIdIndex] unsignedIntValue]; else + recurrenceTime = 0; + + currentStart = eventStart; + if (currentStart < startSecs) + { + currentStart = startSecs; + offset = 0; + } + else + offset = ((currentStart - startSecs) + / dayLength); + if (offset >= [blocks count]) + [self errorWithFormat: @"event '%@' has a computed offset that" + @" overflows the amount of blocks (skipped)", + [event objectAtIndex: eventNameIndex]]; + else + { + currentDay = [blocks objectAtIndex: offset]; + currentDayStart = startSecs + dayLength * offset; + + if (eventEnd > endsSecs) + eventEnd = endsSecs; + + if (eventEnd < startSecs) + // The event doesn't end in the covered period. + // This special case occurs with a DST change. + return; + + userState = _userStateInEvent (event); + while (currentDayStart + dayLength < eventEnd) { - if (eventEnd < eventStart) - { - swap = eventStart; - eventStart = eventEnd; - eventEnd = swap; - [self warnWithFormat: @"event '%@' has end < start: %d < %d", - [event objectAtIndex: eventNameIndex], eventEnd, eventStart]; - } - - startSecs = (unsigned int) [startDate timeIntervalSince1970]; - endsSecs = (unsigned int) [endDate timeIntervalSince1970]; - - if ([[event objectAtIndex: eventIsCycleIndex] boolValue]) - recurrenceTime = [[event objectAtIndex: eventRecurrenceIdIndex] unsignedIntValue]; - else - recurrenceTime = 0; - - currentStart = eventStart; - if (currentStart < startSecs) - { - currentStart = startSecs; - offset = 0; - } - else - offset = ((currentStart - startSecs) - / dayLength); - if (offset >= [blocks count]) - [self errorWithFormat: @"event '%@' has a computed offset that" - @" overflows the amount of blocks (skipped)", - [event objectAtIndex: eventNameIndex]]; - else - { - currentDay = [blocks objectAtIndex: offset]; - currentDayStart = startSecs + dayLength * offset; - - if (eventEnd > endsSecs) - eventEnd = endsSecs; - - if (eventEnd < startSecs) - // The event doesn't end in the covered period. - // This special case occurs with a DST change. - return; - - userState = _userStateInEvent (event); - while (currentDayStart + dayLength < eventEnd) - { - eventBlock = [self _eventBlockWithStart: currentStart - end: currentDayStart + dayLength - 1 - number: number - onDay: currentDayStart - recurrenceTime: recurrenceTime - userState: userState]; - [currentDay addObject: eventBlock]; - currentDayStart += dayLength; - currentStart = currentDayStart; - offset++; - currentDay = [blocks objectAtIndex: offset]; - } - + eventBlock = [self _eventBlockWithStart: currentStart + end: currentDayStart + dayLength - 1 + number: number + onDay: currentDayStart + recurrenceTime: recurrenceTime + userState: userState]; + [currentDay addObject: eventBlock]; + currentDayStart += dayLength; + currentStart = currentDayStart; + offset++; + currentDay = [blocks objectAtIndex: offset]; + } + computedEventEnd = eventEnd; - + // We add 5 mins to the end date of an event if the end date // is equal or smaller than the event's start date. if (eventEnd <= currentStart) - computedEventEnd = currentStart + (5*60); + computedEventEnd = currentStart + (5*60); eventBlock = [self _eventBlockWithStart: currentStart - end: computedEventEnd - number: number - onDay: currentDayStart - recurrenceTime: recurrenceTime - userState: userState]; + end: computedEventEnd + number: number + onDay: currentDayStart + recurrenceTime: recurrenceTime + userState: userState]; [currentDay addObject: eventBlock]; } - } } + } } - (void) _prepareEventBlocks: (NSMutableArray **) blocks - withAllDays: (NSMutableArray **) allDayBlocks + withAllDays: (NSMutableArray **) allDayBlocks { unsigned int count, nbrDays; int seconds; - + seconds = [endDate timeIntervalSinceDate: startDate]; if (seconds > 0) + { + nbrDays = 1 + (unsigned int) (seconds / dayLength); + *blocks = [NSMutableArray arrayWithCapacity: nbrDays]; + *allDayBlocks = [NSMutableArray arrayWithCapacity: nbrDays]; + for (count = 0; count < nbrDays; count++) { - nbrDays = 1 + (unsigned int) (seconds / dayLength); - *blocks = [NSMutableArray arrayWithCapacity: nbrDays]; - *allDayBlocks = [NSMutableArray arrayWithCapacity: nbrDays]; - for (count = 0; count < nbrDays; count++) - { - [*blocks addObject: [NSMutableArray array]]; - [*allDayBlocks addObject: [NSMutableArray array]]; - } + [*blocks addObject: [NSMutableArray array]]; + [*allDayBlocks addObject: [NSMutableArray array]]; } + } else - { - *blocks = nil; - *allDayBlocks = nil; - } + { + *blocks = nil; + *allDayBlocks = nil; + } } - (NSArray *) _horizontalBlocks: (NSMutableArray *) day @@ -805,119 +889,115 @@ _userStateInEvent (NSArray *event) NSMutableArray *currentBlock, *blocks; NSDictionary *currentEvent; unsigned int count, max, qCount, qMax, qOffset; - + blocks = [NSMutableArray array]; - + bzero (quarters, 96 * sizeof (NSMutableArray *)); - + max = [day count]; for (count = 0; count < max; count++) + { + currentEvent = [day objectAtIndex: count]; + qMax = [[currentEvent objectForKey: @"length"] unsignedIntValue]; + qOffset = [[currentEvent objectForKey: @"start"] unsignedIntValue]; + for (qCount = 0; qCount < qMax; qCount++) { - currentEvent = [day objectAtIndex: count]; - qMax = [[currentEvent objectForKey: @"length"] unsignedIntValue]; - qOffset = [[currentEvent objectForKey: @"start"] unsignedIntValue]; - for (qCount = 0; qCount < qMax; qCount++) - { - currentBlock = quarters[qCount + qOffset]; - if (!currentBlock) + currentBlock = quarters[qCount + qOffset]; + if (!currentBlock) { currentBlock = [NSMutableArray array]; quarters[qCount + qOffset] = currentBlock; [blocks addObject: currentBlock]; } - [currentBlock addObject: currentEvent]; - } + [currentBlock addObject: currentEvent]; } - + } + return blocks; } -static inline unsigned int -_computeMaxBlockSiblings (NSArray *block) +static inline unsigned int _computeMaxBlockSiblings (NSArray *block) { unsigned int count, max, maxSiblings, siblings; NSNumber *nbrEvents; - + max = [block count]; maxSiblings = max; for (count = 0; count < max; count++) + { + nbrEvents = [[block objectAtIndex: count] objectForKey: @"siblings"]; + if (nbrEvents) { - nbrEvents = [[block objectAtIndex: count] objectForKey: @"siblings"]; - if (nbrEvents) - { - siblings = [nbrEvents unsignedIntValue]; - if (siblings > maxSiblings) - maxSiblings = siblings; - } + siblings = [nbrEvents unsignedIntValue]; + if (siblings > maxSiblings) + maxSiblings = siblings; } - + } + return maxSiblings; } -static inline void -_propagateBlockSiblings (NSArray *block, NSNumber *maxSiblings) +static inline void _propagateBlockSiblings (NSArray *block, NSNumber *maxSiblings) { unsigned int count, max; NSMutableDictionary *event; NSNumber *realSiblings; - + max = [block count]; realSiblings = [NSNumber numberWithUnsignedInt: max]; for (count = 0; count < max; count++) - { - event = [block objectAtIndex: count]; - [event setObject: maxSiblings forKey: @"siblings"]; - [event setObject: realSiblings forKey: @"realSiblings"]; - } + { + event = [block objectAtIndex: count]; + [event setObject: maxSiblings forKey: @"siblings"]; + [event setObject: realSiblings forKey: @"realSiblings"]; + } } /* this requires two vertical passes */ -static inline void -_computeBlocksSiblings (NSArray *blocks) +static inline void _computeBlocksSiblings (NSArray *blocks) { NSArray *currentBlock; unsigned int count, max, maxSiblings; - + max = [blocks count]; for (count = 0; count < max; count++) - { - currentBlock = [blocks objectAtIndex: count]; - maxSiblings = _computeMaxBlockSiblings (currentBlock); - _propagateBlockSiblings (currentBlock, - [NSNumber numberWithUnsignedInt: maxSiblings]); - } + { + currentBlock = [blocks objectAtIndex: count]; + maxSiblings = _computeMaxBlockSiblings (currentBlock); + _propagateBlockSiblings (currentBlock, + [NSNumber numberWithUnsignedInt: maxSiblings]); + } } -static inline void -_computeBlockPosition (NSArray *block) +static inline void _computeBlockPosition (NSArray *block) { unsigned int count, max, j, siblings; NSNumber *position; NSMutableDictionary *event; NSMutableDictionary **positions; - + max = [block count]; event = [block objectAtIndex: 0]; siblings = [[event objectForKey: @"siblings"] unsignedIntValue]; positions = NSZoneCalloc (NULL, siblings, sizeof (NSMutableDictionary *)); - + for (count = 0; count < max; count++) + { + event = [block objectAtIndex: count]; + position = [event objectForKey: @"position"]; + if (position) + *(positions + [position unsignedIntValue]) = event; + else { - event = [block objectAtIndex: count]; - position = [event objectForKey: @"position"]; - if (position) - *(positions + [position unsignedIntValue]) = event; - else - { - j = 0; - while (j < max && *(positions + j)) - j++; - *(positions + j) = event; - [event setObject: [NSNumber numberWithUnsignedInt: j] - forKey: @"position"]; - } + j = 0; + while (j < max && *(positions + j)) + j++; + *(positions + j) = event; + [event setObject: [NSNumber numberWithUnsignedInt: j] + forKey: @"position"]; } - + } + NSZoneFree (NULL, positions); } @@ -961,22 +1041,22 @@ _computeBlocksPosition (NSArray *blocks) { NSArray *block; unsigned int count, max; -// NSMutableDictionary **positions; - + // NSMutableDictionary **positions; + max = [blocks count]; for (count = 0; count < max; count++) - { - block = [blocks objectAtIndex: count]; - _computeBlockPosition (block); -// _addBlockMultipliers (block, positions); -// NSZoneFree (NULL, positions); - } + { + block = [blocks objectAtIndex: count]; + _computeBlockPosition (block); + // _addBlockMultipliers (block, positions); + // NSZoneFree (NULL, positions); + } } - (void) _addBlocksWidth: (NSMutableArray *) day { NSArray *blocks; - + blocks = [self _horizontalBlocks: day]; _computeBlocksSiblings (blocks); _computeBlocksSiblings (blocks); @@ -991,73 +1071,73 @@ _computeBlocksPosition (NSArray *blocks) NSMutableArray *allDayBlocks, *blocks, *currentDay; NSNumber *eventNbr; BOOL isAllDay; - + [self _setupContext]; - + [self _prepareEventBlocks: &blocks withAllDays: &allDayBlocks]; events = [self _fetchFields: eventsFields - forComponentOfType: @"vevent"]; + forComponentOfType: @"vevent"]; eventsBlocks - = [NSArray arrayWithObjects: events, allDayBlocks, blocks, nil]; + = [NSArray arrayWithObjects: events, allDayBlocks, blocks, nil]; max = [events count]; for (count = 0; count < max; count++) - { - event = [events objectAtIndex: count]; -// NSLog(@"***[UIxCalListingActions eventsBlocksAction] %i = %@ : %@ / %@ / %@", count, -// [event objectAtIndex: eventTitleIndex], -// [event objectAtIndex: eventStartDateIndex], -// [event objectAtIndex: eventEndDateIndex], -// [event objectAtIndex: eventRecurrenceIdIndex]); - eventNbr = [NSNumber numberWithUnsignedInt: count]; - isAllDay = [[event objectAtIndex: eventIsAllDayIndex] boolValue]; - if (dayBasedView && isAllDay) - [self _fillBlocks: allDayBlocks withEvent: event withNumber: eventNbr]; - else - [self _fillBlocks: blocks withEvent: event withNumber: eventNbr]; - } - + { + event = [events objectAtIndex: count]; + // NSLog(@"***[UIxCalListingActions eventsBlocksAction] %i = %@ : %@ / %@ / %@", count, + // [event objectAtIndex: eventTitleIndex], + // [event objectAtIndex: eventStartDateIndex], + // [event objectAtIndex: eventEndDateIndex], + // [event objectAtIndex: eventRecurrenceIdIndex]); + eventNbr = [NSNumber numberWithUnsignedInt: count]; + isAllDay = [[event objectAtIndex: eventIsAllDayIndex] boolValue]; + if (dayBasedView && isAllDay) + [self _fillBlocks: allDayBlocks withEvent: event withNumber: eventNbr]; + else + [self _fillBlocks: blocks withEvent: event withNumber: eventNbr]; + } + max = [blocks count]; for (count = 0; count < max; count++) - { - currentDay = [blocks objectAtIndex: count]; - [currentDay sortUsingSelector: @selector (compareEventByStart:)]; - [self _addBlocksWidth: currentDay]; - } - + { + currentDay = [blocks objectAtIndex: count]; + [currentDay sortUsingSelector: @selector (compareEventByStart:)]; + [self _addBlocksWidth: currentDay]; + } + return [self _responseWithData: eventsBlocks]; -// timeIntervalSinceDate: + // timeIntervalSinceDate: } - (NSString *) _getStatusClassForStatusCode: (int) statusCode - andEndDateStamp: (unsigned int) endDateStamp + andEndDateStamp: (unsigned int) endDateStamp { NSCalendarDate *taskDate, *now; NSString *statusClass; - + if (statusCode == 1) statusClass = @"completed"; else + { + if (endDateStamp) { - if (endDateStamp) - { - now = [NSCalendarDate calendarDate]; - taskDate + now = [NSCalendarDate calendarDate]; + taskDate = [NSCalendarDate dateWithTimeIntervalSince1970: endDateStamp]; - [taskDate setTimeZone: userTimeZone]; - if ([taskDate earlierDate: now] == taskDate) - statusClass = @"overdue"; - else - { - if ([taskDate isToday]) - statusClass = @"duetoday"; - else - statusClass = @"duelater"; - } - } + [taskDate setTimeZone: userTimeZone]; + if ([taskDate earlierDate: now] == taskDate) + statusClass = @"overdue"; else - statusClass = @"noduedate"; + { + if ([taskDate isToday]) + statusClass = @"duetoday"; + else + statusClass = @"duelater"; + } } - + else + statusClass = @"noduedate"; + } + return statusClass; } @@ -1065,46 +1145,67 @@ _computeBlocksPosition (NSArray *blocks) { NSMutableArray *filteredTasks, *filteredTask; NSString *sort, *ascending; - NSString *statusFlag; + NSString *statusFlag, *tasksView; SOGoUserSettings *us; NSEnumerator *tasks; NSArray *task; - + unsigned int endDateStamp; BOOL showCompleted; int statusCode; - + int startSecs; + int endsSecs; + filteredTasks = [NSMutableArray array]; - + [self _setupContext]; - + + startSecs = (unsigned int) [startDate timeIntervalSince1970]; + endsSecs = (unsigned int) [endDate timeIntervalSince1970]; + tasksView = [request formValueForKey: @"filterpopup"]; + #warning see TODO in SchedulerUI.js about "setud" showCompleted = [[request formValueForKey: @"show-completed"] intValue]; if ([request formValueForKey: @"setud"]) - { - us = [[context activeUser] userSettings]; - [us setBool: showCompleted forKey: @"ShowCompletedTasks"]; - [us synchronize]; - } - + { + us = [[context activeUser] userSettings]; + [us setBool: showCompleted forKey: @"ShowCompletedTasks"]; + [us synchronize]; + } + tasks = [[self _fetchFields: tasksFields - forComponentOfType: @"vtodo"] objectEnumerator]; + forComponentOfType: @"vtodo"] objectEnumerator]; + while ((task = [tasks nextObject])) + { + statusCode = [[task objectAtIndex: 3] intValue]; + if (statusCode != 1 || showCompleted) { - statusCode = [[task objectAtIndex: 3] intValue]; - if (statusCode != 1 || showCompleted) - { - filteredTask = [NSMutableArray arrayWithArray: task]; - endDateStamp = [[task objectAtIndex: 5] intValue]; - statusFlag = [self _getStatusClassForStatusCode: statusCode - andEndDateStamp: endDateStamp]; - [filteredTask addObject: statusFlag]; - if (endDateStamp > 0) - [filteredTask addObject: [self _formattedDateForSeconds: endDateStamp - forAllDay: NO]]; - [filteredTasks addObject: filteredTask]; - } + filteredTask = [NSMutableArray arrayWithArray: task]; + endDateStamp = [[task objectAtIndex: 5] intValue]; + statusFlag = [self _getStatusClassForStatusCode: statusCode + andEndDateStamp: endDateStamp]; + [filteredTask addObject: statusFlag]; + if (endDateStamp > 0) + [filteredTask addObject: [self _formattedDateForSeconds: endDateStamp + forAllDay: NO]]; + + if (([tasksView isEqualToString:@"view_today"] || + [tasksView isEqualToString:@"view_next7"] || + [tasksView isEqualToString:@"view_next14"] || + [tasksView isEqualToString:@"view_next31"] || + [tasksView isEqualToString:@"view_thismonth"]) && ((endDateStamp <= endsSecs) && (endDateStamp >= startSecs))) + [filteredTasks addObject: filteredTask]; + else if ([tasksView isEqualToString:@"view_all"]) + [filteredTasks addObject: filteredTask]; + else if (([tasksView isEqualToString:@"view_overdue"]) && ([[filteredTask objectAtIndex:12] isEqualToString:@"overdue"])) + [filteredTasks addObject: filteredTask]; + else if ([tasksView isEqualToString:@"view_incomplete"] && (![[filteredTask objectAtIndex:12] isEqualToString:@"completed"])) + [filteredTasks addObject: filteredTask]; + else if ([tasksView isEqualToString:@"view_not_started"] && ([[[filteredTask objectAtIndex:3] stringValue] isEqualToString:@"0"])) + [filteredTasks addObject: filteredTask]; } + } sort = [[context request] formValueForKey: @"sort"]; if ([sort isEqualToString: @"title"]) [filteredTasks sortUsingSelector: @selector (compareTasksTitleAscending:)]; @@ -1118,13 +1219,13 @@ _computeBlocksPosition (NSArray *blocks) [filteredTasks sortUsingSelector: @selector (compareTasksCategoryAscending:)]; else if ([sort isEqualToString: @"calendarname"]) [filteredTasks sortUsingSelector: @selector (compareTasksCalendarNameAscending:)]; - else + else [filteredTasks sortUsingSelector: @selector (compareTasksAscending:)]; - + ascending = [[context request] formValueForKey: @"asc"]; if (![ascending boolValue]) [filteredTasks reverseArray]; - + return [self _responseWithData: filteredTasks]; } diff --git a/UI/Scheduler/UIxCalMainView.m b/UI/Scheduler/UIxCalMainView.m index 7846b43f8..58362137f 100644 --- a/UI/Scheduler/UIxCalMainView.m +++ b/UI/Scheduler/UIxCalMainView.m @@ -1,8 +1,7 @@ /* UIxCalMainView.m - this file is part of SOGo * - * Copyright (C) 2006-2009 Inverse inc. + * Copyright (C) 2006-2014 Inverse inc. * - * 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 @@ -132,6 +131,30 @@ return yearMenuItems; } +- (NSArray *) tasksFilters +{ + return [NSArray arrayWithObjects: @"view_all", @"view_today", @"view_next7", + @"view_next14", @"view_next31", @"view_thismonth", + @"view_not_started", @"view_overdue", @"view_incomplete", nil]; +} + +- (NSString *) tasksFilterLabel +{ + return [self labelForKey: [self valueForKey:@"taskFilter"]]; +} + +- (NSString *) selectedTasksFilter +{ + NSString *selectedFilter; + + selectedFilter = [self queryParameterForKey: @"tasksFilterpopup"]; + + if (![selectedFilter length]) + selectedFilter = @"view_today"; + + return selectedFilter; +} + - (void) setYearMenuItem: (NSNumber *) aYearMenuItem { yearMenuItem = aYearMenuItem; diff --git a/UI/Templates/SchedulerUI/UIxCalFilterPanel.wox b/UI/Templates/SchedulerUI/UIxCalFilterPanel.wox index b98d49bdb..90c01a982 100644 --- a/UI/Templates/SchedulerUI/UIxCalFilterPanel.wox +++ b/UI/Templates/SchedulerUI/UIxCalFilterPanel.wox @@ -3,12 +3,14 @@ xmlns="http://www.w3.org/1999/xhtml" xmlns:var="http://www.skyrix.com/od/binding" xmlns:const="http://www.skyrix.com/od/constant" - xmlns:label="OGo:label" - > + xmlns:label="OGo:label"> +
@@ -25,6 +27,6 @@ item="filter" string="filterLabel" value="filter" selection="selectedFilter" const:name="filterpopup" - const:onchange="return onListFilterChange();" /> + const:onchange="return onEventsListFilterChange();" />
diff --git a/UI/Templates/SchedulerUI/UIxCalMainView.wox b/UI/Templates/SchedulerUI/UIxCalMainView.wox index f59426452..56b17e961 100644 --- a/UI/Templates/SchedulerUI/UIxCalMainView.wox +++ b/UI/Templates/SchedulerUI/UIxCalMainView.wox @@ -165,6 +165,7 @@ + @@ -175,7 +176,9 @@
@@ -184,7 +187,19 @@ autocomplete="off" type="text" menuid="taskSearchMenu" /> - + + + + + + + +
@@ -194,7 +209,7 @@ - + diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index 897d21a06..9240ec5f0 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -1,4 +1,5 @@ -var listFilter = 'view_today'; +var eventListFilter = 'view_today'; +var taskListFilter = 'view_today'; var listOfSelection = null; var selectedCalendarCell = null; @@ -885,7 +886,7 @@ function performDeleteEventCallback(http) { /* in dateselector */ function onDaySelect(node) { var day = node.getAttribute('day'); - var needRefresh = (listFilter == 'view_selectedday' + var needRefresh = (eventListFilter == 'view_selectedday' && day != currentDay); var td = $(node).getParentWithTagName("td"); @@ -918,7 +919,7 @@ function onDateSelectorGotoMonth(event) { function onCalendarGotoDay(node) { var day = node.getAttribute("date"); - var needRefresh = (listFilter == 'view_selectedday' && day != currentDay); + var needRefresh = (eventListFilter == 'view_selectedday' && day != currentDay); changeDateSelectorDisplay(day); changeCalendarDisplay( { "day": day } ); @@ -1027,7 +1028,13 @@ function eventsListCallback(http) { td.observe("mousedown", listRowMouseDownHandler, true); if (data[i][7]) td.update(data[i][7]); // location - + + td = createElement("td"); + row.appendChild(td); + td.observe("mousedown", listRowMouseDownHandler, true); + if (data[i][10]) + td.update(data[i][10]); // category + td = createElement("td"); row.appendChild(td); td.observe("mousedown", listRowMouseDownHandler, true); @@ -2228,41 +2235,44 @@ function refreshCurrentFolder(id) { /* refreshes the "unifinder" list */ function refreshEvents() { - var titleSearch; + var specificSearch; var value = search["events"]["value"]; if (value && value.length) - titleSearch = "&search=" + escape(value.utf8encode()); + specificSearch = ("&search=" + search["events"]["criteria"] + + "&value=" + escape(value.utf8encode())); else - titleSearch = ""; + specificSearch = ""; refreshAlarms(); return _loadEventHref("eventslist?asc=" + sorting["event-ascending"] + "&sort=" + sorting["event-attribute"] + "&day=" + currentDay - + titleSearch - + "&filterpopup=" + listFilter); + + specificSearch + + "&filterpopup=" + eventListFilter); } function refreshTasks(setUserDefault) { - var titleSearch; + var specificSearch; var value = search["tasks"]["value"]; if (value && value.length) - titleSearch = "&search=" + escape(value.utf8encode()); + specificSearch = ("&search=" + search["tasks"]["criteria"] + + "&value=" + escape(value.utf8encode())); else - titleSearch = ""; + specificSearch = ""; if (setUserDefault == 1) - titleSearch += "&setud=1"; + specificSearch += "&setud=1"; refreshAlarms(); return _loadTasksHref("taskslist?show-completed=" + showCompletedTasks + "&asc=" + sorting["task-ascending"] + "&sort=" + sorting["task-attribute"] - + titleSearch); + + specificSearch + + "&filterpopup=" + taskListFilter); } function refreshEventsAndDisplay() { @@ -2270,15 +2280,26 @@ function refreshEventsAndDisplay() { changeCalendarDisplay(); } -function onListFilterChange() { +function onEventsListFilterChange() { var node = $("filterpopup"); - listFilter = node.value; -// log ("listFilter = " + listFilter); + eventListFilter = node.value; return refreshEvents(); } +function onTasksListFilterChange() { + var node = $("tasksFilterpopup"); + + taskListFilter = node.value; + + $("showHideCompletedTasks").disabled = taskListFilter == "view_overdue" || + taskListFilter == "view_incomplete" || + taskListFilter == "view_not_started"; + + return refreshTasks(); +} + function selectMonthInMenu(menu, month) { var entries = $(menu).select("LI"); for (i = 0; i < entries.length; i++) { @@ -2473,7 +2494,7 @@ function onCalendarSelectEvent(event, willShowContextualMenu) { function onCalendarSelectDay(event) { var day = this.getAttribute("day"); - var needRefresh = (listFilter == 'view_selectedday' && day != currentDay); + var needRefresh = (eventListFilter == 'view_selectedday' && day != currentDay); setSelectedDayDate(day); changeDateSelectorDisplay(day); @@ -2735,44 +2756,44 @@ function onTasksListMenuPrepareVisibility() { return true; } function getMenus() { - var menus = {}; - - var dateMenu = []; - for (var i = 0; i < 12; i++) - dateMenu.push(onMonthMenuItemClick); - menus["monthListMenu"] = dateMenu; - - dateMenu = []; - for (var i = 0; i < 11; i++) - dateMenu.push(onYearMenuItemClick); - menus["yearListMenu"] = dateMenu; - - menus["eventsListMenu"] = new Array(onMenuNewEventClick, "-", - onMenuNewTaskClick, - editEvent, deleteEvent, "-", - onSelectAll, "-", - null, null); - menus["calendarsMenu"] = new Array(onCalendarModify, - "-", - onCalendarNew, onCalendarRemove, - "-", onCalendarExport, onCalendarImport, - null, "-", null, "-", onMenuSharing); - menus["eventSearchMenu"] = new Array(setSearchCriteria); - - menus["tasksListMenu"] = new Array (editEvent, newTask, "-", - marksTasksAsCompleted, deleteEvent, "-", - onMenuRawTask); - menus["taskSearchMenu"] = new Array(setSearchCriteria); - - var calendarsMenu = $("calendarsMenu"); - if (calendarsMenu) - calendarsMenu.prepareVisibility = onCalendarsMenuPrepareVisibility; - - var tasksListMenu = $("tasksListMenu"); - if (tasksListMenu) - tasksListMenu.prepareVisibility = onTasksListMenuPrepareVisibility; - - return menus; + var menus = {}; + + var dateMenu = []; + for (var i = 0; i < 12; i++) + dateMenu.push(onMonthMenuItemClick); + menus["monthListMenu"] = dateMenu; + + dateMenu = []; + for (var i = 0; i < 11; i++) + dateMenu.push(onYearMenuItemClick); + menus["yearListMenu"] = dateMenu; + + menus["eventsListMenu"] = new Array(onMenuNewEventClick, "-", + onMenuNewTaskClick, + editEvent, deleteEvent, "-", + onSelectAll, "-", + null, null); + menus["calendarsMenu"] = new Array(onCalendarModify, + "-", + onCalendarNew, onCalendarRemove, + "-", onCalendarExport, onCalendarImport, + null, "-", null, "-", onMenuSharing); + menus["eventSearchMenu"] = new Array(setSearchCriteria, setSearchCriteria, setSearchCriteria); + + menus["tasksListMenu"] = new Array (editEvent, newTask, "-", + marksTasksAsCompleted, deleteEvent, "-", + onMenuRawTask); + menus["taskSearchMenu"] = new Array(setSearchCriteria, setSearchCriteria, setSearchCriteria); + + var calendarsMenu = $("calendarsMenu"); + if (calendarsMenu) + calendarsMenu.prepareVisibility = onCalendarsMenuPrepareVisibility; + + var tasksListMenu = $("tasksListMenu"); + if (tasksListMenu) + tasksListMenu.prepareVisibility = onTasksListMenuPrepareVisibility; + + return menus; } function newTask () { @@ -3267,9 +3288,10 @@ function configureLists() { var input = $("showHideCompletedTasks"); input.observe("click", onShowCompletedTasks); + if (showCompletedTasks) input.checked = true; - + // EVENT LIST list = $("eventsList"); list.multiselect = true; @@ -3428,19 +3450,20 @@ function initScheduler() { if (!$(document.body).hasClassName("popup")) { var node = $("filterpopup"); - listFilter = node.value; + eventListFilter = node.value; var tabsContainer = $("schedulerTabs"); var controller = new SOGoTabsController(); controller.attachToTabsContainer(tabsContainer); tabsContainer.on("tabs:click", saveTabState); - + if (UserSettings['ShowCompletedTasks']) { showCompletedTasks = parseInt(UserSettings['ShowCompletedTasks']); } else { showCompletedTasks = 0; } + initDateSelectorEvents(); initCalendarSelector(); configureSearchField(); diff --git a/UI/WebServerResources/generic.css b/UI/WebServerResources/generic.css index 1754d73f2..3da86b4cf 100644 --- a/UI/WebServerResources/generic.css +++ b/UI/WebServerResources/generic.css @@ -428,7 +428,8 @@ TD.headerTitle { width: 20%; } TD.headerLocation, -TD.headerCalendarName +TD.headerCalendarName, +TD.headerCategory { min-width: 15em; width: 15em; } From 593b44ed01212d5ffb12dc513b9c1f5be8f4aca9 Mon Sep 17 00:00:00 2001 From: Alexandre Cloutier Date: Thu, 8 May 2014 17:53:20 -0400 Subject: [PATCH 2/5] forgotten code --- SoObjects/Appointments/SOGoAppointmentFolder.m | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 8552ae72f..42ff94462 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -1280,13 +1280,20 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir [baseWhere addObject: privacySQLString]; if ([title length]) - [baseWhere - addObject: [NSString stringWithFormat: @"c_title isCaseInsensitiveLike: '%%%@%%'", + if ([filters length]) + { + if ([filters isEqualToString:@"title_Category_Location"] || [filters isEqualToString:@"entireContent"]) + { + [baseWhere addObject: [NSString stringWithFormat: @"(c_title isCaseInsensitiveLike: '%%%@%%' OR c_category isCaseInsensitiveLike: '%%%@%%' OR c_location isCaseInsensitiveLike: '%%%@%%')", + [title stringByReplacingString: @"'" withString: @"\\'\\'"], + [title stringByReplacingString: @"'" withString: @"\\'\\'"], + [title stringByReplacingString: @"'" withString: @"\\'\\'"]]]; + } + } + else + [baseWhere addObject: [NSString stringWithFormat: @"c_title isCaseInsensitiveLike: '%%%@%%'", [title stringByReplacingString: @"'" withString: @"\\'\\'"]]]; - if ([filters length]) - [baseWhere addObject: [NSString stringWithFormat: @"(%@)", filters]]; - /* prepare mandatory fields */ fields = [NSMutableArray arrayWithArray: _fields]; From bf67b6435fd1376132ad8bef2eefa34736f858ff Mon Sep 17 00:00:00 2001 From: Alexandre Cloutier Date: Tue, 13 May 2014 13:21:18 -0400 Subject: [PATCH 3/5] applies comments --- UI/Scheduler/UIxCalListingActions.m | 47 +++++++++++------------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m index 337e2e80d..d3f73c3f7 100644 --- a/UI/Scheduler/UIxCalListingActions.m +++ b/UI/Scheduler/UIxCalListingActions.m @@ -357,8 +357,8 @@ static NSArray *tasksFields = nil; if ([quickInfos count] > 0) { quickInfosFlag = YES; - quickInfosName = [NSMutableArray arrayWithObject:[[quickInfos objectAtIndex:0] objectForKey:@"c_name"]]; - for (i = 1; i < [quickInfos count]; i++) + quickInfosName = [NSMutableArray arrayWithCapacity:[quickInfos count]]; + for (i = 0; i < [quickInfos count]; i++) [quickInfosName addObject:[[quickInfos objectAtIndex:i] objectForKey:@"c_name"]]; } @@ -370,23 +370,17 @@ static NSArray *tasksFields = nil; for (i = 0; i < [allInfos count]; i++) { - if (quickInfosFlag) + if (quickInfosFlag && ![quickInfosName containsObject:[[allInfos objectAtIndex:i] objectForKey:@"c_name"]]) { - if (![quickInfosName containsObject:[[allInfos objectAtIndex:i] objectForKey:@"c_name"]]) - { - iCalString = [[allInfos objectAtIndex:i] objectForKey:@"c_content"]; - calendar = [iCalCalendar parseSingleFromSource: iCalString]; - master = [calendar firstChildWithTag:@"vevent"]; - if (!master) - master = [calendar firstChildWithTag:@"vtodo"]; - - if (master) { - if ([[master comment] length] > 0) - { - match = [[master comment] rangeOfString:value]; - if (match.length > 0) { - [quickInfos addObject:[allInfos objectAtIndex:i]]; - } + iCalString = [[allInfos objectAtIndex:i] objectForKey:@"c_content"]; + calendar = [iCalCalendar parseSingleFromSource: iCalString]; + master = [calendar firstChildWithTag:component]; + if (master) { + if ([[master comment] length] > 0) + { + match = [[master comment] rangeOfString:value options:NSCaseInsensitiveSearch]; + if (match.length > 0) { + [quickInfos addObject:[allInfos objectAtIndex:i]]; } } } @@ -395,14 +389,11 @@ static NSArray *tasksFields = nil; { iCalString = [[allInfos objectAtIndex:i] objectForKey:@"c_content"]; calendar = [iCalCalendar parseSingleFromSource: iCalString]; - master = [calendar firstChildWithTag:@"vevent"]; - if (!master) - master = [calendar firstChildWithTag:@"vtodo"]; - + master = [calendar firstChildWithTag:component]; if (master) { if ([[master comment] length] > 0) { - match = [[master comment] rangeOfString:value]; + match = [[master comment] rangeOfString:value options:NSCaseInsensitiveSearch]; if (match.length > 0) { [quickInfos addObject:[allInfos objectAtIndex:i]]; } @@ -435,12 +426,9 @@ static NSArray *tasksFields = nil; else { // Identifies whether the active user can edit the event. - role = - [currentFolder roleForComponentsWithAccessClass: - [[newInfo objectForKey: @"c_classification"] intValue] - forUser: userLogin]; - if ([role isEqualToString: @"ComponentModifier"] - || [role length] == 0) + role = [currentFolder roleForComponentsWithAccessClass:[[newInfo objectForKey: @"c_classification"] intValue] + forUser: userLogin]; + if ([role isEqualToString: @"ComponentModifier"] || [role length] == 0) [newInfo setObject: [NSNumber numberWithInt: 1] forKey: @"editable"]; else @@ -468,6 +456,7 @@ static NSArray *tasksFields = nil; else [newInfo setObject: [NSNumber numberWithInt: 0] forKey: @"erasable"]; + [newInfo setObject: [currentFolder nameInContainer] forKey: @"c_folder"]; [newInfo setObject: [currentFolder ownerInContext: context] From db8d9bb16cc94b3bbc5fb5f20daf9953202791fb Mon Sep 17 00:00:00 2001 From: Alexandre Cloutier Date: Fri, 16 May 2014 11:29:37 -0400 Subject: [PATCH 4/5] Repair broken logic --- UI/Scheduler/UIxCalListingActions.m | 47 ++++++++++++----------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m index d3f73c3f7..25bd11425 100644 --- a/UI/Scheduler/UIxCalListingActions.m +++ b/UI/Scheduler/UIxCalListingActions.m @@ -367,41 +367,32 @@ static NSArray *tasksFields = nil; to: endDate title: nil component: component]; + if (quickInfosFlag == YES) + { + for (i = 0; i < [allInfos count]; i++) { + if([quickInfosName containsObject:[[allInfos objectAtIndex:i] objectForKey:@"c_name"]]) + [allInfos removeObjectAtIndex:i]; + } + } + for (i = 0; i < [allInfos count]; i++) { - if (quickInfosFlag && ![quickInfosName containsObject:[[allInfos objectAtIndex:i] objectForKey:@"c_name"]]) - { - iCalString = [[allInfos objectAtIndex:i] objectForKey:@"c_content"]; - calendar = [iCalCalendar parseSingleFromSource: iCalString]; - master = [calendar firstChildWithTag:component]; - if (master) { - if ([[master comment] length] > 0) - { - match = [[master comment] rangeOfString:value options:NSCaseInsensitiveSearch]; - if (match.length > 0) { - [quickInfos addObject:[allInfos objectAtIndex:i]]; - } - } - } - } - else - { - iCalString = [[allInfos objectAtIndex:i] objectForKey:@"c_content"]; - calendar = [iCalCalendar parseSingleFromSource: iCalString]; - master = [calendar firstChildWithTag:component]; - if (master) { - if ([[master comment] length] > 0) - { - match = [[master comment] rangeOfString:value options:NSCaseInsensitiveSearch]; - if (match.length > 0) { - [quickInfos addObject:[allInfos objectAtIndex:i]]; - } + iCalString = [[allInfos objectAtIndex:i] objectForKey:@"c_content"]; + calendar = [iCalCalendar parseSingleFromSource: iCalString]; + master = [calendar firstChildWithTag:component]; + if (master) { + if ([[master comment] length] > 0) + { + match = [[master comment] rangeOfString:value options:NSCaseInsensitiveSearch]; + if (match.length > 0) { + [quickInfos addObject:[allInfos objectAtIndex:i]]; } } } } - currentInfos = [quickInfos objectEnumerator]; + + currentInfos = [quickInfos objectEnumerator]; } From b488721f83fb7d2838b2b5e2ffe40d9df303039f Mon Sep 17 00:00:00 2001 From: Alexandre Cloutier Date: Fri, 16 May 2014 15:00:47 -0400 Subject: [PATCH 5/5] Reverse loop to make sure no objects is skipped --- UI/Scheduler/UIxCalListingActions.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m index 25bd11425..62eaac684 100644 --- a/UI/Scheduler/UIxCalListingActions.m +++ b/UI/Scheduler/UIxCalListingActions.m @@ -369,7 +369,7 @@ static NSArray *tasksFields = nil; component: component]; if (quickInfosFlag == YES) { - for (i = 0; i < [allInfos count]; i++) { + for (i = ([allInfos count] - 1); i >= 0 ; i--) { if([quickInfosName containsObject:[[allInfos objectAtIndex:i] objectForKey:@"c_name"]]) [allInfos removeObjectAtIndex:i]; }