1/* UIxComponentEditor.m - this file is part of SOGo 2 * 3 * Copyright (C) 2006-2015 Inverse inc. 4 * 5 * This file is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2, or (at your option) 8 * any later version. 9 * 10 * This file is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; see the file COPYING. If not, write to 17 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 * Boston, MA 02111-1307, USA. 19 */ 20 21#import <Foundation/NSArray.h> 22#import <Foundation/NSBundle.h> 23#import <Foundation/NSEnumerator.h> 24#import <Foundation/NSException.h> 25#import <Foundation/NSCalendarDate.h> 26#import <Foundation/NSKeyValueCoding.h> 27#import <Foundation/NSString.h> 28#import <Foundation/NSValue.h> 29#import <Foundation/NSURL.h> 30 31#import <NGCards/iCalAlarm.h> 32#import <NGCards/iCalByDayMask.h> 33#import <NGCards/iCalDateTime.h> 34#import <NGCards/iCalEvent.h> 35#import <NGCards/iCalPerson.h> 36#import <NGCards/iCalRepeatableEntityObject.h> 37#import <NGCards/iCalRecurrenceRule.h> 38#import <NGCards/iCalToDo.h> 39#import <NGCards/iCalTrigger.h> 40 41 42#import <NGCards/NSString+NGCards.h> 43#import <NGCards/NSCalendarDate+NGCards.h> 44#import <NGObjWeb/SoSecurityManager.h> 45#import <NGObjWeb/NSException+HTTP.h> 46#import <NGObjWeb/WOApplication.h> 47#import <NGObjWeb/WORequest.h> 48#import <NGObjWeb/WOResponse.h> 49#import <NGExtensions/NSCalendarDate+misc.h> 50#import <NGExtensions/NSObject+Logs.h> 51#import <NGExtensions/NSNull+misc.h> 52#import <NGExtensions/NSString+misc.h> 53 54#import <Appointments/iCalAlarm+SOGo.h> 55#import <Appointments/iCalEntityObject+SOGo.h> 56#import <Appointments/iCalPerson+SOGo.h> 57#import <Appointments/SOGoAppointmentFolder.h> 58#import <Appointments/SOGoWebAppointmentFolder.h> 59#import <Appointments/SOGoAppointmentFolders.h> 60#import <Appointments/SOGoAppointmentObject.h> 61#import <Appointments/SOGoAppointmentOccurence.h> 62#import <Appointments/SOGoTaskObject.h> 63#import <SOGo/NSArray+Utilities.h> 64#import <SOGo/NSDictionary+Utilities.h> 65#import <SOGo/NSString+Utilities.h> 66#import <SOGo/SOGoUser.h> 67#import <SOGo/SOGoUserDefaults.h> 68#import <SOGo/SOGoUserManager.h> 69#import <SOGo/SOGoSource.h> 70#import <SOGo/SOGoPermissions.h> 71#import <SOGo/SOGoSystemDefaults.h> 72#import <SOGo/WOResourceManager+SOGo.h> 73 74#import "../../Main/SOGo.h" 75 76#import "UIxComponentEditor.h" 77#import "UIxDatePicker.h" 78 79static NSArray *reminderItems = nil; 80static NSArray *reminderValues = nil; 81 82#define iREPEAT(X) \ 83- (NSString *) repeat##X; \ 84- (void) setRepeat##X: (NSString *) theValue 85 86#define iRANGE(X) \ 87- (NSString *) range##X; \ 88- (void) setRange##X: (NSString *) theValue 89 90 91@interface UIxComponentEditor (Private) 92 93iREPEAT(1); 94iREPEAT(2); 95iREPEAT(3); 96iREPEAT(4); 97iREPEAT(5); 98iREPEAT(6); 99iREPEAT(7); 100iRANGE(1); 101iRANGE(2); 102 103@end 104 105#define REPEAT(X) \ 106- (NSString *) repeat##X { return repeat##X; } \ 107- (void) setRepeat##X: (NSString *) theValue { ASSIGN(repeat##X, theValue); } \ 108 109#define RANGE(X) \ 110- (NSString *) range##X { return range##X; } \ 111- (void) setRange##X: (NSString *) theValue { ASSIGN(range##X, theValue); } 112 113@implementation UIxComponentEditor 114 115+ (void) initialize 116{ 117 if (!reminderItems && !reminderValues) 118 { 119 reminderItems = [NSArray arrayWithObjects: 120 @"5_MINUTES_BEFORE", 121 @"10_MINUTES_BEFORE", 122 @"15_MINUTES_BEFORE", 123 @"30_MINUTES_BEFORE", 124 @"45_MINUTES_BEFORE", 125 @"-", 126 @"1_HOUR_BEFORE", 127 @"2_HOURS_BEFORE", 128 @"5_HOURS_BEFORE", 129 @"15_HOURS_BEFORE", 130 @"-", 131 @"1_DAY_BEFORE", 132 @"2_DAYS_BEFORE", 133 @"1_WEEK_BEFORE", 134 @"-", 135 @"CUSTOM", 136 nil]; 137 reminderValues = [NSArray arrayWithObjects: 138 @"-PT5M", 139 @"-PT10M", 140 @"-PT15M", 141 @"-PT30M", 142 @"-PT45M", 143 @"", 144 @"-PT1H", 145 @"-PT2H", 146 @"-PT5H", 147 @"-PT15H", 148 @"", 149 @"-P1D", 150 @"-P2D", 151 @"-P1W", 152 @"", 153 @"", 154 nil]; 155 156 [reminderItems retain]; 157 [reminderValues retain]; 158 } 159} 160 161- (id) init 162{ 163 UIxDatePicker *datePicker; 164 165 if ((self = [super init])) 166 { 167 // We must instanciate a UIxDatePicker object to retrieve 168 // the proper date format to use. 169 datePicker = [[UIxDatePicker alloc] initWithContext: context]; 170 dateFormat = [datePicker dateFormat]; 171 [datePicker release]; 172 173 component = nil; 174 componentCalendar = nil; 175 classification = nil; 176 [self setIsCycleEndNever]; 177 componentOwner = @""; 178 organizer = nil; 179 organizerProfile = nil; 180 ownerAsAttendee = nil; 181 attendee = nil; 182 jsonAttendees = nil; 183 calendarList = nil; 184 repeat = nil; 185 reminder = nil; 186 reminderQuantity = nil; 187 reminderUnit = nil; 188 reminderRelation = nil; 189 reminderReference = nil; 190 reminderAction = nil; 191 reminderEmailOrganizer = NO; 192 reminderEmailAttendees = NO; 193 repeatType = nil; 194 repeat1 = nil; 195 repeat2 = nil; 196 repeat3 = nil; 197 repeat4 = nil; 198 repeat5 = nil; 199 repeat6 = nil; 200 repeat7 = nil; 201 range1 = nil; 202 range2 = nil; 203 } 204 205 return self; 206} 207 208- (void) dealloc 209{ 210 [item release]; 211 [cycleUntilDate release]; 212 [title release]; 213 [location release]; 214 [organizer release]; 215 [organizerProfile release]; 216 [ownerAsAttendee release]; 217 [comment release]; 218 [priority release]; 219 [classification release]; 220 [categories release]; 221 [cycle release]; 222 [cycleEnd release]; 223 [attachUrl release]; 224 [attendee release]; 225 [jsonAttendees release]; 226 [calendarList release]; 227 228 [reminder release]; 229 [reminderQuantity release]; 230 [reminderUnit release]; 231 [reminderRelation release]; 232 [reminderReference release]; 233 234 [repeat release]; 235 [repeatType release]; 236 [repeat1 release]; 237 [repeat2 release]; 238 [repeat3 release]; 239 [repeat4 release]; 240 [repeat5 release]; 241 [repeat6 release]; 242 [repeat7 release]; 243 [range1 release]; 244 [range2 release]; 245 246 [component release]; 247 [componentCalendar release]; 248 249 [super dealloc]; 250} 251 252- (void) _loadAttendees 253{ 254 NSEnumerator *attendees; 255 NSMutableDictionary *currentAttendeeData; 256 NSString *uid, *domain; 257 NSArray *contacts; 258 NSDictionary *contact; 259 iCalPerson *currentAttendee; 260 SOGoUserManager *um; 261 NSObject <SOGoSource> *source; 262 263 jsonAttendees = [NSMutableDictionary new]; 264 um = [SOGoUserManager sharedUserManager]; 265 266 attendees = [[component attendees] objectEnumerator]; 267 while ((currentAttendee = [attendees nextObject])) 268 { 269 currentAttendeeData = [NSMutableDictionary dictionary]; 270 271 if ([[currentAttendee cn] length]) 272 [currentAttendeeData setObject: [currentAttendee cn] 273 forKey: @"name"]; 274 275 [currentAttendeeData setObject: [currentAttendee rfc822Email] 276 forKey: @"email"]; 277 278 uid = [um getUIDForEmail: [currentAttendee rfc822Email]]; 279 if (uid != nil) 280 [currentAttendeeData setObject: uid 281 forKey: @"uid"]; 282 else 283 { 284 domain = [[context activeUser] domain]; 285 contacts = [um fetchContactsMatching: [currentAttendee rfc822Email] inDomain: domain]; 286 if ([contacts count] == 1) 287 { 288 contact = [contacts lastObject]; 289 source = [contact objectForKey: @"source"]; 290 if ([source conformsToProtocol: @protocol (SOGoDNSource)] && 291 [[(NSObject <SOGoDNSource>*) source MSExchangeHostname] length]) 292 { 293 uid = [NSString stringWithFormat: @"%@:%@", [[context activeUser] login], 294 [contact valueForKey: @"c_uid"]]; 295 [currentAttendeeData setObject: uid forKey: @"uid"]; 296 } 297 } 298 } 299 300 [currentAttendeeData setObject: [[currentAttendee partStat] lowercaseString] 301 forKey: @"partstat"]; 302 [currentAttendeeData setObject: [[currentAttendee role] lowercaseString] 303 forKey: @"role"]; 304 305 if ([[currentAttendee delegatedTo] length]) 306 [currentAttendeeData setObject: [[currentAttendee delegatedTo] rfc822Email] 307 forKey: @"delegated-to"]; 308 309 if ([[currentAttendee delegatedFrom] length]) 310 [currentAttendeeData setObject: [[currentAttendee delegatedFrom] rfc822Email] 311 forKey: @"delegated-from"]; 312 313 [jsonAttendees setObject: currentAttendeeData 314 forKey: [currentAttendee rfc822Email]]; 315 } 316} 317 318- (void) _loadCategories 319{ 320 NSString *simpleCategory; 321 NSArray *compCategories; 322 323 compCategories = [component categories]; 324 if ([compCategories count] > 0) 325 { 326 simpleCategory = [compCategories objectAtIndex: 0]; 327 ASSIGN (category, simpleCategory); 328 } 329} 330 331- (void) _loadRRules 332{ 333 SOGoUserDefaults *ud; 334 335 // We initialize our repeat ivars 336 if ([component hasRecurrenceRules]) 337 { 338 iCalRecurrenceRule *rule; 339 340 [self setRepeat: @"CUSTOM"]; 341 342 rule = [[component recurrenceRules] lastObject]; 343 344 /* DAILY */ 345 if ([rule frequency] == iCalRecurrenceFrequenceDaily) 346 { 347 repeatType = @"0"; 348 349 if ([[rule byDayMask] isWeekDays]) 350 { 351 if ([rule isInfinite]) 352 repeat = @"EVERY WEEKDAY"; 353 repeat1 = @"1"; 354 } 355 else 356 { 357 repeat1 = @"0"; 358 359 if ([rule repeatInterval] == 1 && [rule isInfinite]) 360 repeat = @"DAILY"; 361 362 [self setRepeat2: [NSString stringWithFormat: @"%d", [rule repeatInterval]]]; 363 } 364 } 365 366 /* WEEKLY */ 367 else if ([rule frequency] == iCalRecurrenceFrequenceWeekly) 368 { 369 repeatType = @"1"; 370 [self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]]; 371 372 if (![[rule byDay] length]) 373 { 374 if ([rule repeatInterval] == 1) 375 repeat = @"WEEKLY"; 376 else if ([rule repeatInterval] == 2) 377 repeat = @"BI-WEEKLY"; 378 } 379 else 380 { 381 [self setRepeat2: [[rule byDayMask] asRuleStringWithIntegers]]; 382 } 383 } 384 385 /* MONTHLY */ 386 else if ([rule frequency] == iCalRecurrenceFrequenceMonthly) 387 { 388 repeatType = @"2"; 389 390 if ([[rule byDay] length]) 391 { 392 int firstOccurrence; 393 iCalByDayMask *dayMask; 394 395 dayMask = [rule byDayMask]; 396 firstOccurrence = [dayMask firstOccurrence] - 1; 397 if (firstOccurrence < 0) 398 firstOccurrence = 5; 399 400 [self setRepeat2: @"0"]; 401 [self setRepeat3: [NSString stringWithFormat: @"%d", firstOccurrence]]; 402 [self setRepeat4: [NSString stringWithFormat: @"%d", [dayMask firstDay]]]; 403 } 404 else if ([[rule byMonthDay] count]) 405 { 406 NSArray *days; 407 408 days = [rule byMonthDay]; 409 if ([days count] > 0 && [[days objectAtIndex: 0] intValue] < 0) 410 { 411 // BYMONTHDAY=-1 412 [self setRepeat2: @"0"]; 413 [self setRepeat3: @"5"]; // last .. 414 [self setRepeat4: @"7"]; // .. day of the month 415 } 416 else 417 { 418 [self setRepeat2: @"1"]; 419 [self setRepeat5: [[rule byMonthDay] componentsJoinedByString: @","]]; 420 } 421 } 422 else if ([rule repeatInterval] == 1) 423 repeat = @"MONTHLY"; 424 425 [self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]]; 426 } 427 428 /* YEARLY */ 429 else 430 { 431 repeatType = @"3"; 432 433 if ([[rule flattenedValuesForKey: @"bymonth"] length]) 434 { 435 if ([[rule byDay] length]) 436 { 437 int firstOccurrence; 438 iCalByDayMask *dayMask; 439 440 dayMask = [rule byDayMask]; 441 firstOccurrence = [dayMask firstOccurrence] - 1; 442 if (firstOccurrence < 0) 443 firstOccurrence = 5; 444 445 [self setRepeat2: @"1"]; 446 [self setRepeat5: [NSString stringWithFormat: @"%d", firstOccurrence]]; 447 [self setRepeat6: [NSString stringWithFormat: @"%d", [dayMask firstDay]]]; 448 [self setRepeat7: [NSString stringWithFormat: @"%d", [[rule flattenedValuesForKey: @"bymonth"] intValue]-1]]; 449 } 450 else 451 { 452 [self setRepeat2: @"0"]; 453 [self setRepeat3: [rule flattenedValuesForKey: @"bymonthday"]]; 454 [self setRepeat4: [NSString stringWithFormat: @"%d", [[rule flattenedValuesForKey: @"bymonth"] intValue]-1]]; 455 } 456 } 457 else if ([rule repeatInterval] == 1) 458 repeat = @"YEARLY"; 459 460 [self setRepeat1: [NSString stringWithFormat: @"%d", [rule repeatInterval]]]; 461 } 462 463 /* We decode the proper end date, recurrences count, etc. */ 464 if ([rule repeatCount]) 465 { 466 repeat = @"CUSTOM"; 467 [self setRange1: @"1"]; 468 [self setRange2: [rule flattenedValuesForKey: @"count"]]; 469 } 470 else if ([rule untilDate]) 471 { 472 NSCalendarDate *date; 473 474 repeat = @"CUSTOM"; 475 date = [[rule untilDate] copy]; 476 477 ud = [[context activeUser] userDefaults]; 478 [date setTimeZone: [ud timeZone]]; 479 [self setRange1: @"2"]; 480 [self setRange2: [date descriptionWithCalendarFormat: dateFormat]]; 481 [date release]; 482 } 483 else 484 [self setRange1: @"0"]; 485 } 486 else 487 { 488 DESTROY(repeat); 489 repeatType = @"0"; 490 repeat1 = @"0"; 491 repeat2 = @"1"; 492 } 493} 494 495- (void) _loadEMailAlarm: (iCalAlarm *) anAlarm 496{ 497 NSArray *attendees; 498 iCalPerson *aAttendee; 499 SOGoUser *owner; 500 NSString *ownerId, *email; 501 int count, max; 502 503 attendees = [anAlarm attendees]; 504 reminderEmailOrganizer = NO; 505 reminderEmailAttendees = NO; 506 507 ownerId = [[self clientObject] ownerInContext: nil]; 508 owner = [SOGoUser userWithLogin: ownerId]; 509 email = [[owner defaultIdentity] objectForKey: @"email"]; 510 511 max = [attendees count]; 512 for (count = 0; 513 !(reminderEmailOrganizer && reminderEmailAttendees) 514 && count < max; 515 count++) 516 { 517 aAttendee = [attendees objectAtIndex: count]; 518 if ([[aAttendee rfc822Email] isEqualToString: email]) 519 reminderEmailOrganizer = YES; 520 else 521 reminderEmailAttendees = YES; 522 } 523} 524 525- (void) _loadAlarms 526{ 527 iCalAlarm *anAlarm; 528 iCalTrigger *aTrigger; 529 NSString *duration, *quantity; 530 unichar c; 531 NSUInteger i; 532 533 if ([component hasAlarms]) 534 { 535 // We currently have the following limitations for alarms: 536 // - the alarm's action must be of type DISPLAY or AUDIO (considered as DISPLAY) 537 // - the alarm's trigger value type must be DURATION. 538 539 anAlarm = [component firstSupportedAlarm]; 540 aTrigger = [anAlarm trigger]; 541 ASSIGN (reminderAction, [[anAlarm action] lowercaseString]); 542 543 // The default value type is DURATION. See http://tools.ietf.org/html/rfc5545#section-3.8.6.3 544 if (![[aTrigger valueType] length] || 545 [[aTrigger valueType] caseInsensitiveCompare: @"DURATION"] == NSOrderedSame) 546 { 547 duration = [aTrigger flattenedValuesForKey: @""]; 548 i = [reminderValues indexOfObject: duration]; 549 550 if (i == NSNotFound || [reminderAction isEqualToString: @"email"]) 551 { 552 // Custom alarm 553 ASSIGN (reminder, @"CUSTOM"); 554 ASSIGN (reminderRelation, [aTrigger relationType]); 555 556 i = 0; 557 c = [duration characterAtIndex: i]; 558 if (c == '-') 559 { 560 ASSIGN (reminderReference, @"BEFORE"); 561 i++; 562 } 563 else 564 { 565 ASSIGN (reminderReference, @"AFTER"); 566 } 567 568 c = [duration characterAtIndex: i]; 569 if (c == 'P') 570 { 571 quantity = @""; 572 // Parse duration -- ignore first character (P) 573 for (i++; i < [duration length]; i++) 574 { 575 c = [duration characterAtIndex: i]; 576 if (c == 't' || c == 'T') 577 // time -- ignore character 578 continue; 579 else if (isdigit (c)) 580 quantity = [quantity stringByAppendingFormat: @"%c", c]; 581 else 582 { 583 switch (c) 584 { 585 case 'D': /* day */ 586 ASSIGN (reminderUnit, @"DAYS"); 587 break; 588 case 'H': /* hour */ 589 ASSIGN (reminderUnit, @"HOURS"); 590 break; 591 case 'M': /* min */ 592 ASSIGN (reminderUnit, @"MINUTES"); 593 break; 594 default: 595 //NSLog(@"Cannot process duration unit: '%c'", c); 596 break; 597 } 598 } 599 } 600 if ([quantity length]) 601 ASSIGN (reminderQuantity, quantity); 602 603 if ([reminderAction isEqualToString: @"email"]) 604 [self _loadEMailAlarm: anAlarm]; 605 } 606 } 607 else 608 // Matches one of the predefined alarms 609 ASSIGN (reminder, [reminderItems objectAtIndex: i]); 610 } 611 } 612} 613 614/* warning: we use this method which will be triggered by the template system 615 when the page is instantiated, but we should find another and cleaner way of 616 doing this... for example, when the clientObject is set */ 617- (void) setComponent: (iCalRepeatableEntityObject *) newComponent 618{ 619 SOGoCalendarComponent *co; 620 SOGoUserManager *um; 621 NSString *owner, *ownerEmail; 622 iCalRepeatableEntityObject *masterComponent; 623 SOGoUserDefaults *defaults; 624 NSString *tag; 625 626 if (!component) 627 { 628 ASSIGN (component, newComponent); 629 630 co = [self clientObject]; 631 componentOwner = [co ownerInContext: nil]; 632 if (component) 633 { 634 ASSIGN (title, [component summary]); 635 ASSIGN (location, [component location]); 636 ASSIGN (comment, [component comment]); 637 ASSIGN (attachUrl, [[component attach] absoluteString]); 638 ASSIGN (classification, [component accessClass]); 639 if ([co isNew] && [classification length] == 0) 640 { 641 defaults = [[context activeUser] userDefaults]; 642 tag = [co componentTag]; 643 [classification release]; 644 if ([tag isEqualToString: @"vevent"]) 645 classification = [defaults calendarEventsDefaultClassification]; 646 else 647 classification = [defaults calendarTasksDefaultClassification]; 648 649 if ([classification length] == 0) 650 classification = @"PUBLIC"; 651 [classification retain]; 652 } 653 654 ASSIGN (priority, [component priority]); 655 ASSIGN (status, [component status]); 656 ASSIGN (categories, [component categories]); 657 if ([[[component organizer] rfc822Email] length]) 658 { 659 ASSIGN (organizer, [component organizer]); 660 } 661 else 662 { 663 masterComponent = [[[component parent] allObjects] objectAtIndex: 0]; 664 ASSIGN (organizer, [masterComponent organizer]); 665 } 666 667 [self _loadCategories]; 668 if (!jsonAttendees) 669 [self _loadAttendees]; 670 [self _loadRRules]; 671 [self _loadAlarms]; 672 673 [componentCalendar release]; 674 componentCalendar = [co container]; 675 if ([componentCalendar isKindOfClass: [SOGoCalendarComponent class]]) 676 componentCalendar = [componentCalendar container]; 677 [componentCalendar retain]; 678 679 um = [SOGoUserManager sharedUserManager]; 680 owner = [componentCalendar ownerInContext: context]; 681 ownerEmail = [um getEmailForUID: owner]; 682 ASSIGN (ownerAsAttendee, [component findAttendeeWithEmail: (id)ownerEmail]); 683 } 684 } 685} 686 687- (void) setRSVPURL: (NSString *) theURL 688{ 689 rsvpURL = theURL; 690} 691 692- (NSString *) rsvpURL 693{ 694 return rsvpURL; 695} 696 697- (void) setSaveURL: (NSString *) newSaveURL 698{ 699 saveURL = newSaveURL; 700} 701 702- (NSString *) saveURL 703{ 704 return saveURL; 705} 706 707/* accessors */ 708 709- (BOOL) isChildOccurence 710{ 711 return [[self clientObject] isKindOfClass: [SOGoComponentOccurence class]]; 712} 713 714- (void) setItem: (id) _item 715{ 716 ASSIGN (item, _item); 717} 718 719- (id) item 720{ 721 return item; 722} 723 724- (NSString *) itemPriorityText 725{ 726 return [self labelForKey: [NSString stringWithFormat: @"prio_%@", item]]; 727} 728 729- (NSString *) itemClassificationText 730{ 731 NSString *tag; 732 733 tag = [[component tag] lowercaseString]; 734 735 return [self labelForKey: [NSString stringWithFormat: @"%@_%@", item, tag]]; 736} 737 738- (NSString *) itemStatusText 739{ 740 return [self labelForKey: [NSString stringWithFormat: @"status_%@", item]]; 741} 742 743- (void) setTitle: (NSString *) _value 744{ 745 ASSIGN (title, _value); 746} 747 748- (NSString *) title 749{ 750 SOGoCalendarComponent *co; 751 NSString *tag; 752 753 co = [self clientObject]; 754 if ([co isNew] && [co isKindOfClass: [SOGoCalendarComponent class]]) 755 { 756 tag = [co componentTag]; 757 if ([tag isEqualToString: @"vevent"]) 758 [self setTitle: [self labelForKey: @"New Event"]]; 759 else if ([tag isEqualToString: @"vtodo"]) 760 [self setTitle: [self labelForKey: @"New Task"]]; 761 } 762 763 return title; 764} 765 766- (void) setAttach: (NSString *) _attachUrl 767{ 768 ASSIGN (attachUrl, _attachUrl); 769} 770 771- (NSString *) attach 772{ 773 return attachUrl; 774} 775 776- (NSDictionary *) organizerProfile 777{ 778 NSMutableDictionary *profile; 779 NSDictionary *ownerIdentity; 780 NSString *uid, *name, *email, *partstat, *role; 781 SOGoUserManager *um; 782 SOGoCalendarComponent *co; 783 SOGoUser *ownerUser; 784 785 if (organizerProfile == nil) 786 { 787 profile = [NSMutableDictionary dictionary]; 788 email = [organizer rfc822Email]; 789 role = nil; 790 partstat = nil; 791 792 if ([email length]) 793 { 794 um = [SOGoUserManager sharedUserManager]; 795 796 name = [organizer cn]; 797 uid = [um getUIDForEmail: email]; 798 799 partstat = [[organizer partStat] lowercaseString]; 800 role = [[organizer role] lowercaseString]; 801 } 802 else 803 { 804 // No organizer defined in vEvent; use calendar owner 805 co = [self clientObject]; 806 uid = [[co container] ownerInContext: context]; 807 ownerUser = [SOGoUser userWithLogin: uid roles: nil]; 808 ownerIdentity = [ownerUser defaultIdentity]; 809 810 name = [ownerIdentity objectForKey: @"fullName"]; 811 email = [ownerIdentity objectForKey: @"email"]; 812 } 813 814 if (uid != nil) 815 [profile setObject: uid 816 forKey: @"uid"]; 817 else 818 uid = email; 819 820 [profile setObject: name 821 forKey: @"name"]; 822 823 [profile setObject: email 824 forKey: @"email"]; 825 826 if (partstat == nil || ![partstat length]) 827 partstat = @"accepted"; 828 829 [profile setObject: partstat 830 forKey: @"partstat"]; 831 832 if (role == nil || ![role length]) 833 role = @"chair"; 834 835 [profile setObject: role 836 forKey: @"role"]; 837 838 organizerProfile = [NSDictionary dictionaryWithObject: profile forKey: uid]; 839 [organizerProfile retain]; 840 } 841 842 return organizerProfile; 843} 844 845 846- (BOOL) hasCreatedBy 847{ 848 return ([[[component firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""] length] > 0); 849} 850 851- (NSString *) createdBy 852{ 853 return [[component firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""]; 854} 855 856- (NSString *) createdByLink 857{ 858 return [NSString stringWithFormat: @"mailto:%@", 859 [[component firstChildWithTag: @"X-SOGo-Component-Created-By"] flattenedValuesForKey: @""]]; 860} 861 862- (NSString *) createdByName 863{ 864 NSString *login; 865 SOGoUser *user; 866 867 login = [[SOGoUserManager sharedUserManager] getUIDForEmail: [self createdBy]]; 868 869 if (login) 870 { 871 user = [SOGoUser userWithLogin: login]; 872 873 if (user) 874 return [user cn]; 875 } 876 877 return @""; 878} 879 880- (NSString *) organizerName 881{ 882 NSDictionary *profile; 883 NSString *s; 884 885 profile = [[[self organizerProfile] allValues] lastObject]; 886 887 s = [profile objectForKey: @"name"]; 888 889 if ([s length] == 0) 890 s = [profile objectForKey: @"email"]; 891 892 return s; 893} 894 895- (NSString *) jsonOrganizer 896{ 897 return [[[[self organizerProfile] allValues] lastObject] jsonRepresentation]; 898} 899 900- (BOOL) hasOrganizer 901{ 902 // We check if there's an organizer and if it's not ourself 903 NSString *value; 904 905 value = [organizer rfc822Email]; 906 907 if ([value length]) 908 { 909 NSDictionary *currentIdentity; 910 NSEnumerator *identities; 911 NSArray *allIdentities; 912 913 allIdentities = [[context activeUser] allIdentities]; 914 identities = [allIdentities objectEnumerator]; 915 currentIdentity = nil; 916 917 while ((currentIdentity = [identities nextObject])) 918 if ([[currentIdentity objectForKey: @"email"] 919 caseInsensitiveCompare: value] 920 == NSOrderedSame) 921 return NO; 922 923 return YES; 924 } 925 926 return NO; 927} 928 929- (BOOL) hasAttendees 930{ 931 return ([[component attendees] count] > 0); 932} 933 934- (void) setAttendee: (id) _attendee 935{ 936 ASSIGN (attendee, _attendee); 937} 938 939- (id) attendee 940{ 941 return attendee; 942} 943 944- (NSString *) attendeeForDisplay 945{ 946 NSString *fn, *result; 947 948 fn = [attendee cnWithoutQuotes]; 949 if ([fn length]) 950 result = fn; 951 else 952 result = [attendee rfc822Email]; 953 954 return result; 955} 956 957- (NSString *) jsonAttendees 958{ 959 return [jsonAttendees jsonRepresentation]; 960} 961 962- (void) setLocation: (NSString *) _value 963{ 964 ASSIGN (location, _value); 965} 966 967- (NSString *) location 968{ 969 return location; 970} 971 972- (BOOL) hasLocation 973{ 974 return [location length] > 0; 975} 976 977- (void) setComment: (NSString *) _value 978{ 979#warning should we do the same for "location" and "summary"? What about ContactsUI? 980 ASSIGN (comment, [_value stringByReplacingString: @"\r\n" withString: @"\n"]); 981} 982 983- (NSString *) comment 984{ 985 return [comment stringByReplacingString: @"\n" withString: @"\r\n"]; 986} 987 988- (BOOL) hasComment 989{ 990 return [comment length] > 0; 991} 992 993- (NSArray *) categoryList 994{ 995 NSMutableArray *categoryList; 996 NSArray *categoryLabels; 997 SOGoUserDefaults *defaults; 998 999 defaults = [[context activeUser] userDefaults]; 1000 categoryLabels = [defaults calendarCategories]; 1001 if (!categoryLabels) 1002 categoryLabels = [[self labelForKey: @"category_labels"] 1003 componentsSeparatedByString: @","]; 1004 categoryList 1005 = [NSMutableArray arrayWithCapacity: [categoryLabels count] + 1]; 1006 if ([category length] && ![categoryLabels containsObject: category]) 1007 [categoryList addObject: category]; 1008 [categoryList addObjectsFromArray: 1009 [categoryLabels sortedArrayUsingSelector: 1010 @selector (localizedCaseInsensitiveCompare:)]]; 1011 1012 return categoryList; 1013} 1014 1015- (void) setCategories: (NSArray *) _categories 1016{ 1017 ASSIGN (categories, _categories); 1018} 1019 1020- (NSArray *) categories 1021{ 1022 return categories; 1023} 1024 1025- (void) setCategory: (NSString *) newCategory 1026{ 1027 if (newCategory) 1028 ASSIGN (categories, [NSArray arrayWithObject: newCategory]); 1029 else 1030 { 1031 [categories release]; 1032 categories = nil; 1033 } 1034} 1035 1036- (NSString *) category 1037{ 1038 return category; 1039} 1040 1041- (BOOL) hasCategory 1042{ 1043 return [category length] > 0; 1044} 1045 1046- (NSArray *) repeatList 1047{ 1048 static NSArray *repeatItems = nil; 1049 1050 if (!repeatItems) 1051 { 1052 repeatItems = [NSArray arrayWithObjects: @"DAILY", 1053 @"WEEKLY", 1054 @"BI-WEEKLY", 1055 @"EVERY WEEKDAY", 1056 @"MONTHLY", 1057 @"YEARLY", 1058 @"-", 1059 @"CUSTOM", 1060 nil]; 1061 [repeatItems retain]; 1062 } 1063 1064 return repeatItems; 1065} 1066 1067- (NSString *) itemRepeatText 1068{ 1069 NSString *text; 1070 1071 if ([item isEqualToString: @"-"]) 1072 text = item; 1073 else 1074 text = [self labelForKey: [NSString stringWithFormat: @"repeat_%@", item]]; 1075 1076 return text; 1077} 1078 1079- (NSString *) repeatLabel 1080{ 1081 NSString *rc; 1082 1083 if ([self repeat]) 1084 rc = [self labelForKey: [NSString stringWithFormat: @"repeat_%@", [self repeat]]]; 1085 else 1086 rc = [self labelForKey: @"repeat_NEVER"]; 1087 1088 return rc; 1089} 1090 1091- (NSArray *) reminderList 1092{ 1093 return reminderItems; 1094} 1095 1096- (void) setReminder: (NSString *) theReminder 1097{ 1098 ASSIGN(reminder, theReminder); 1099} 1100 1101- (NSString *) reminder 1102{ 1103 if ([[self clientObject] isNew]) 1104 { 1105 NSString *value; 1106 NSUInteger index; 1107 1108 value = [userDefaults calendarDefaultReminder]; 1109 index = [reminderValues indexOfObject: value]; 1110 1111 if (index != NSNotFound) 1112 return [reminderItems objectAtIndex: index]; 1113 1114 return @"NONE"; 1115 } 1116 1117 return reminder; 1118} 1119 1120- (void) setReminderQuantity: (NSString *) theReminderQuantity 1121{ 1122 ASSIGN(reminderQuantity, theReminderQuantity); 1123} 1124 1125- (NSString *) reminderQuantity 1126{ 1127 return reminderQuantity; 1128} 1129 1130- (NSString *) itemReminderText 1131{ 1132 NSString *text; 1133 1134 if ([item isEqualToString: @"-"]) 1135 text = item; 1136 else 1137 text = [self labelForKey: [NSString stringWithFormat: @"reminder_%@", item]]; 1138 1139 return text; 1140} 1141 1142- (void) setReminderAction: (NSString *) newValue 1143{ 1144 ASSIGN (reminderAction, newValue); 1145} 1146 1147- (NSString *) reminderAction 1148{ 1149 return reminderAction; 1150} 1151 1152- (void) setReminderEmailOrganizer: (NSString *) newValue 1153{ 1154 reminderEmailOrganizer = [newValue isEqualToString: @"true"]; 1155} 1156 1157- (NSString *) reminderEmailOrganizer 1158{ 1159 return (reminderEmailOrganizer ? @"true" : @"false"); 1160} 1161 1162- (void) setReminderEmailAttendees: (NSString *) newValue 1163{ 1164 reminderEmailAttendees = [newValue isEqualToString: @"true"]; 1165} 1166 1167- (NSString *) reminderEmailAttendees 1168{ 1169 return (reminderEmailAttendees ? @"true" : @"false"); 1170} 1171 1172- (NSString *) repeat 1173{ 1174 return repeat; 1175} 1176 1177- (void) setRepeat: (NSString *) newRepeat 1178{ 1179 ASSIGN(repeat, newRepeat); 1180} 1181 1182- (BOOL) hasRepeat 1183{ 1184 return [repeat length] > 0; 1185} 1186 1187- (NSString *) itemReplyText 1188{ 1189 NSString *word; 1190 1191 switch ([item intValue]) 1192 { 1193 case iCalPersonPartStatAccepted: 1194 word = @"ACCEPTED"; 1195 break; 1196 case iCalPersonPartStatDeclined: 1197 word = @"DECLINED"; 1198 break; 1199 case iCalPersonPartStatNeedsAction: 1200 word = @"NEEDS-ACTION"; 1201 break; 1202 case iCalPersonPartStatTentative: 1203 word = @"TENTATIVE"; 1204 break; 1205 case iCalPersonPartStatDelegated: 1206 word = @"DELEGATED"; 1207 break; 1208 default: 1209 word = @"UNKNOWN"; 1210 } 1211 1212 return [self labelForKey: [NSString stringWithFormat: @"partStat_%@", word]]; 1213} 1214 1215- (NSArray *) replyList 1216{ 1217 return [NSArray arrayWithObjects: 1218 [NSNumber numberWithInt: iCalPersonPartStatAccepted], 1219 [NSNumber numberWithInt: iCalPersonPartStatDeclined], 1220 [NSNumber numberWithInt: iCalPersonPartStatNeedsAction], 1221 [NSNumber numberWithInt: iCalPersonPartStatTentative], 1222 [NSNumber numberWithInt: iCalPersonPartStatDelegated], 1223 nil]; 1224} 1225 1226- (NSNumber *) reply 1227{ 1228 iCalPersonPartStat participationStatus; 1229 1230 participationStatus = [ownerAsAttendee participationStatus]; 1231 1232 return [NSNumber numberWithInt: participationStatus]; 1233} 1234 1235- (NSArray *) calendarList 1236{ 1237 SOGoAppointmentFolder *calendar, *currentCalendar; 1238 SOGoAppointmentFolders *calendarParent; 1239 NSEnumerator *allCalendars; 1240 SoSecurityManager *sm; 1241 NSString *perm; 1242 1243 if (!calendarList) 1244 { 1245 calendarList = [NSMutableArray new]; 1246 calendar = [self componentCalendar]; 1247 sm = [SoSecurityManager sharedSecurityManager]; 1248 1249 perm = SoPerm_DeleteObjects; 1250 if (![[self clientObject] isNew] && 1251 [sm validatePermission: perm 1252 onObject: calendar 1253 inContext: context]) 1254 { 1255 // User can't delete components from this calendar; 1256 // don't add any calendar other than the current one 1257 // unless it's a new component 1258 [calendarList addObject: calendar]; 1259 } 1260 else 1261 { 1262 // Find which calendars user has creation rights 1263 perm = SoPerm_AddDocumentsImagesAndFiles; 1264 calendarParent 1265 = [[context activeUser] calendarsFolderInContext: context]; 1266 allCalendars = [[calendarParent subFolders] objectEnumerator]; 1267 while ((currentCalendar = [allCalendars nextObject])) 1268 if ([calendar isEqual: currentCalendar] || 1269 ![sm validatePermission: perm 1270 onObject: currentCalendar 1271 inContext: context]) 1272 [calendarList addObject: currentCalendar]; 1273 } 1274 } 1275 1276 return calendarList; 1277} 1278 1279/** 1280 * This method is called from the wox template and uses to display the event 1281 * organizer in the edition window of the attendees. 1282 * Returns an array of the two elements : 1283 * - array of calendar owners 1284 * - dictionary of owners profiles 1285 */ 1286- (NSArray *) calendarOwnerList 1287{ 1288 NSArray *calendars; 1289 NSMutableArray *owners; 1290 NSDictionary *currentOwnerIdentity; 1291 NSMutableDictionary *profiles, *currentOwnerProfile; 1292 NSString *currentOwner; 1293 SOGoAppointmentFolder *currentCalendar; 1294 SOGoUser *currentUser; 1295 NSUInteger i; 1296 1297 calendars = [self calendarList]; 1298 owners = [NSMutableArray arrayWithCapacity: [calendars count]]; 1299 profiles = [NSMutableDictionary dictionaryWithDictionary: [self organizerProfile]]; 1300 1301 for (i = 0; i < [calendars count]; i++) 1302 { 1303 currentCalendar = [calendars objectAtIndex: i]; 1304 currentOwner = [currentCalendar ownerInContext: context]; 1305 [owners addObject: currentOwner]; 1306 1307 if ([profiles objectForKey: currentOwner] == nil) 1308 { 1309 currentUser = [SOGoUser userWithLogin: currentOwner roles: nil]; 1310 currentOwnerIdentity = [currentUser defaultIdentity]; 1311 1312 currentOwnerProfile = [NSMutableDictionary dictionary]; 1313 [currentOwnerProfile setObject: ([currentOwnerIdentity objectForKey: @"fullName"] == nil ? @"" : [currentOwnerIdentity objectForKey: @"fullName"]) 1314 forKey: @"name"]; 1315 [currentOwnerProfile setObject: ([currentOwnerIdentity objectForKey: @"email"] == nil ? @"" : [currentOwnerIdentity objectForKey: @"email"]) 1316 forKey: @"email"]; 1317 [currentOwnerProfile setObject: @"accepted" 1318 forKey: @"partstat"]; 1319 [currentOwnerProfile setObject: @"chair" 1320 forKey: @"role"]; 1321 1322 [profiles setObject: currentOwnerProfile forKey: currentOwner]; 1323 } 1324 } 1325 1326 return [NSArray arrayWithObjects: owners, profiles, nil]; 1327} 1328 1329- (NSString *) calendarDisplayName 1330{ 1331 NSString *fDisplayName; 1332 SOGoAppointmentFolder *folder; 1333 SOGoAppointmentFolders *parentFolder; 1334 1335 fDisplayName = [item displayName]; 1336 folder = [self componentCalendar]; 1337 parentFolder = [folder container]; 1338 if ([fDisplayName isEqualToString: [parentFolder defaultFolderName]]) 1339 fDisplayName = [self labelForKey: fDisplayName]; 1340 1341 return fDisplayName; 1342} 1343 1344- (NSString *) calendarsFoldersList 1345{ 1346 NSArray *calendars; 1347 1348 calendars = [[self calendarList] valueForKey: @"nameInContainer"]; 1349 1350 return [calendars componentsJoinedByString: @","]; 1351} 1352 1353 1354- (SOGoAppointmentFolder *) componentCalendar 1355{ 1356 return componentCalendar; 1357} 1358 1359- (NSString *) componentCalendarName 1360{ 1361 return [componentCalendar displayName]; 1362} 1363 1364- (void) setComponentCalendar: (SOGoAppointmentFolder *) _componentCalendar 1365{ 1366 if (_componentCalendar) 1367 ASSIGN(componentCalendar, _componentCalendar); 1368} 1369 1370/* priorities */ 1371 1372- (NSArray *) priorities 1373{ 1374 /* 0 == undefined 1375 9 == low 1376 5 == medium 1377 1 == high 1378 */ 1379 static NSArray *priorities = nil; 1380 1381 if (!priorities) 1382 { 1383 priorities = [NSArray arrayWithObjects: @"9", @"5", @"1", nil]; 1384 [priorities retain]; 1385 } 1386 1387 return priorities; 1388} 1389 1390- (void) setPriority: (NSString *) _priority 1391{ 1392 ASSIGN (priority, _priority); 1393} 1394 1395- (NSString *) priority 1396{ 1397 return priority; 1398} 1399 1400- (BOOL) hasPriority 1401{ 1402 return [priority length] > 0; 1403} 1404 1405- (NSArray *) classificationClasses 1406{ 1407 static NSArray *classes = nil; 1408 1409 if (!classes) 1410 { 1411 classes = [NSArray arrayWithObjects: @"PUBLIC", 1412 @"CONFIDENTIAL", @"PRIVATE", nil]; 1413 [classes retain]; 1414 } 1415 1416 return classes; 1417} 1418 1419- (void) setClassification: (NSString *) _classification 1420{ 1421 ASSIGN (classification, _classification); 1422} 1423 1424- (NSString *) classification 1425{ 1426 return classification; 1427} 1428 1429- (void) setStatus: (NSString *) _status 1430{ 1431 ASSIGN (status, _status); 1432} 1433 1434- (NSString *) status 1435{ 1436 return status; 1437} 1438 1439- (void) setRepeatType: (NSString *) theValue 1440{ 1441 ASSIGN (repeatType, theValue); 1442} 1443 1444- (NSString *) repeatType 1445{ 1446 return repeatType; 1447} 1448 1449REPEAT(1); 1450REPEAT(2); 1451REPEAT(3); 1452REPEAT(4); 1453REPEAT(5); 1454REPEAT(6); 1455REPEAT(7); 1456RANGE(1); 1457RANGE(2); 1458 1459////////////////////////////////// JUNK //////////////////////////////////////// 1460////////////////////////////////// JUNK //////////////////////////////////////// 1461////////////////////////////////// JUNK //////////////////////////////////////// 1462- (NSArray *) cycles 1463{ 1464 NSString *path; 1465 static NSArray *cycles = nil; 1466 1467 if (!cycles) 1468 { 1469 path = [[self componentBundle] pathForResource: @"cycles" ofType: @"plist"]; 1470 NSAssert(path != nil, @"Cannot find cycles.plist!"); 1471 cycles = [[NSArray arrayWithContentsOfFile:path] retain]; 1472 NSAssert(cycles != nil, @"Cannot instantiate cycles from cycles.plist!"); 1473 } 1474 1475 return cycles; 1476} 1477 1478- (void) setCycle: (NSDictionary *) _cycle 1479{ 1480 ASSIGN (cycle, _cycle); 1481} 1482 1483- (NSDictionary *) cycle 1484{ 1485 return cycle; 1486} 1487 1488- (BOOL) hasCycle 1489{ 1490 return ([cycle objectForKey: @"rule"] != nil); 1491} 1492 1493- (NSString *) cycleLabel 1494{ 1495 NSString *key; 1496 1497 key = [(NSDictionary *)item objectForKey: @"label"]; 1498 1499 return [self labelForKey:key]; 1500} 1501 1502- (void) setCycleUntilDate: (NSCalendarDate *) _cycleUntilDate 1503{ 1504// NSCalendarDate *until; 1505 1506// /* copy hour/minute/second from startDate */ 1507// until = [_cycleUntilDate hour: [startDate hourOfDay] 1508// minute: [startDate minuteOfHour] 1509// second: [startDate secondOfMinute]]; 1510// [until setTimeZone: [startDate timeZone]]; 1511// ASSIGN (cycleUntilDate, until); 1512} 1513 1514- (NSCalendarDate *) cycleUntilDate 1515{ 1516 return cycleUntilDate; 1517} 1518 1519- (iCalRecurrenceRule *) rrule 1520{ 1521 NSString *ruleRep; 1522 iCalRecurrenceRule *rule; 1523 1524 if (![self hasCycle]) 1525 return nil; 1526 ruleRep = [cycle objectForKey: @"rule"]; 1527 rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:ruleRep]; 1528 1529 if (cycleUntilDate && [self isCycleEndUntil]) 1530 [rule setUntilDate:cycleUntilDate]; 1531 1532 return rule; 1533} 1534 1535- (void) adjustCycleControlsForRRule: (iCalRecurrenceRule *) _rrule 1536{ 1537// NSDictionary *c; 1538// NSCalendarDate *until; 1539 1540// c = [self cycleMatchingRRule:_rrule]; 1541// [self setCycle:c]; 1542 1543// until = [[[_rrule untilDate] copy] autorelease]; 1544// if (!until) 1545// until = startDate; 1546// else 1547// [self setIsCycleEndUntil]; 1548 1549// [until setTimeZone:[[self clientObject] userTimeZone]]; 1550// [self setCycleUntilDate:until]; 1551} 1552 1553/* 1554 This method is necessary, because we have a fixed sets of cycles in the UI. 1555 The model is able to represent arbitrary rules, however. 1556 There SHOULD be a different UI, similar to iCal.app, to allow modelling 1557 of more complex rules. 1558 1559 This method obviously cannot map all existing rules back to the fixed list 1560 in cycles.plist. This should be fixed in a future version when interop 1561 becomes more important. 1562 */ 1563- (NSDictionary *) cycleMatchingRRule: (iCalRecurrenceRule *) _rrule 1564{ 1565 NSString *cycleRep; 1566 NSArray *cycles; 1567 NSUInteger i, count; 1568 1569 if (!_rrule) 1570 return [[self cycles] objectAtIndex:0]; 1571 1572 cycleRep = [_rrule versitString]; 1573 cycles = [self cycles]; 1574 count = [cycles count]; 1575 for (i = 1; i < count; i++) { 1576 NSDictionary *c; 1577 NSString *cr; 1578 1579 c = [cycles objectAtIndex:i]; 1580 cr = [c objectForKey: @"rule"]; 1581 if ([cr isEqualToString:cycleRep]) 1582 return c; 1583 } 1584 [self warnWithFormat: @"No default cycle for rrule found! -> %@", _rrule]; 1585 return nil; 1586} 1587 1588/* cycle "ends" - supposed to be 'never', 'COUNT' or 'UNTIL' */ 1589- (NSArray *) cycleEnds 1590{ 1591 static NSArray *ends = nil; 1592 1593 if (!ends) 1594 { 1595 ends = [NSArray arrayWithObjects: @"cycle_end_never", 1596 @"cycle_end_until", nil]; 1597 [ends retain]; 1598 } 1599 1600 return ends; 1601} 1602 1603- (void) setCycleEnd: (NSString *) _cycleEnd 1604{ 1605 ASSIGN (cycleEnd, _cycleEnd); 1606} 1607 1608- (NSString *) cycleEnd 1609{ 1610 return cycleEnd; 1611} 1612 1613- (BOOL) isCycleEndUntil 1614{ 1615 return (cycleEnd && [cycleEnd isEqualToString: @"cycle_end_until"]); 1616} 1617 1618- (void) setIsCycleEndUntil 1619{ 1620 [self setCycleEnd: @"cycle_end_until"]; 1621} 1622 1623- (void) setIsCycleEndNever 1624{ 1625 [self setCycleEnd: @"cycle_end_never"]; 1626} 1627////////////////////////////////// JUNK //////////////////////////////////////// 1628////////////////////////////////// JUNK //////////////////////////////////////// 1629////////////////////////////////// JUNK //////////////////////////////////////// 1630 1631 1632/* helpers */ 1633- (NSString *) completeURIForMethod: (NSString *) _method 1634{ 1635 NSString *uri; 1636 NSRange r; 1637 1638 uri = [[[self context] request] uri]; 1639 1640 /* first: identify query parameters */ 1641 r = [uri rangeOfString: @"?" options:NSBackwardsSearch]; 1642 if (r.length > 0) 1643 uri = [uri substringToIndex:r.location]; 1644 1645 /* next: append trailing slash */ 1646 if (![uri hasSuffix: @"/"]) 1647 uri = [uri stringByAppendingString: @"/"]; 1648 1649 /* next: append method */ 1650 uri = [uri stringByAppendingString:_method]; 1651 1652 /* next: append query parameters */ 1653 return [self completeHrefForMethod:uri]; 1654} 1655 1656- (BOOL) isWriteableClientObject 1657{ 1658 return [[self clientObject] 1659 respondsToSelector: @selector(saveCompontent:)]; 1660} 1661 1662/* access */ 1663 1664- (BOOL) canEditComponent 1665{ 1666 return ([[context activeUser] hasEmail: [organizer rfc822Email]]); 1667} 1668 1669/* response generation */ 1670 1671- (NSString *) initialCycleVisibility 1672{ 1673 return ([self hasCycle] 1674 ? @"visibility: visible;" 1675 : @"visibility: hidden;"); 1676} 1677 1678- (NSString *) initialCycleEndUntilVisibility { 1679 return ([self isCycleEndUntil] 1680 ? @"visibility: visible;" 1681 : @"visibility: hidden;"); 1682} 1683 1684// - (NSString *) iCalParticipantsAndResourcesStringFromQueryParameters 1685// { 1686// NSString *s; 1687 1688// s = [self iCalParticipantsStringFromQueryParameters]; 1689// return [s stringByAppendingString: 1690// [self iCalResourcesStringFromQueryParameters]]; 1691// } 1692 1693// - (NSString *) iCalParticipantsStringFromQueryParameters 1694// { 1695// static NSString *iCalParticipantString = @"ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=\"%@\":MAILTO:%@\r\n"; 1696 1697// return [self iCalStringFromQueryParameter: @"ps" 1698// format: iCalParticipantString]; 1699// } 1700 1701// - (NSString *) iCalResourcesStringFromQueryParameters 1702// { 1703// static NSString *iCalResourceString = @"ATTENDEE;ROLE=NON-PARTICIPANT;CN=\"%@\":MAILTO:%@\r\n"; 1704 1705// return [self iCalStringFromQueryParameter: @"rs" 1706// format: iCalResourceString]; 1707// } 1708 1709// - (NSString *) iCalStringFromQueryParameter: (NSString *) _qp 1710// format: (NSString *) _format 1711// { 1712// LDAPUserManager *um; 1713// NSMutableString *iCalRep; 1714// NSString *s; 1715 1716// um = [LDAPUserManager sharedUserManager]; 1717// iCalRep = (NSMutableString *)[NSMutableString string]; 1718// s = [self queryParameterForKey:_qp]; 1719// if(s && [s length] > 0) { 1720// NSArray *es; 1721// NSUInteger i, count; 1722 1723// es = [s componentsSeparatedByString: @","]; 1724// count = [es count]; 1725// for(i = 0; i < count; i++) { 1726// NSString *email, *cn; 1727 1728// email = [es objectAtIndex:i]; 1729// cn = [um getCNForUID:[um getUIDForEmail:email]]; 1730// [iCalRep appendFormat:_format, cn, email]; 1731// } 1732// } 1733// return iCalRep; 1734// } 1735 1736- (NSException *) validateObjectForStatusChange 1737{ 1738 id co; 1739 1740 co = [self clientObject]; 1741 if (![co respondsToSelector: @selector(changeParticipationStatus:)]) 1742 return [NSException exceptionWithHTTPStatus: 400 /* Bad Request */ 1743 reason: 1744 @"method cannot be invoked on the specified object"]; 1745 1746 return nil; 1747} 1748 1749/* contact editor compatibility */ 1750 1751/*- (NSString *) urlButtonClasses 1752{ 1753 NSString *classes; 1754 1755 if ([url length]) 1756 classes = @"button"; 1757 else 1758 classes = @"button _disabled"; 1759 1760 return classes; 1761 }*/ 1762 1763- (void) _handleAttendeesEdition 1764{ 1765 NSMutableArray *newAttendees; 1766 NSUInteger count, max; 1767 NSString *currentEmail; 1768 iCalPerson *currentAttendee; 1769 NSString *json, *role, *partstat; 1770 NSDictionary *attendeesData; 1771 NSArray *attendees; 1772 NSDictionary *currentData; 1773 WORequest *request; 1774 1775 request = [context request]; 1776 json = [request formValueForKey: @"attendees"]; 1777 if ([json length]) 1778 { 1779 attendees = [NSArray array]; 1780 attendeesData = [json objectFromJSONString]; 1781 if (attendeesData && [attendeesData isKindOfClass: [NSDictionary class]]) 1782 { 1783 newAttendees = [NSMutableArray array]; 1784 attendees = [attendeesData allValues]; 1785 max = [attendees count]; 1786 for (count = 0; count < max; count++) 1787 { 1788 currentData = [attendees objectAtIndex: count]; 1789 currentEmail = [currentData objectForKey: @"email"]; 1790 if ([currentEmail length] > 0) 1791 { 1792 role = [[currentData objectForKey: @"role"] uppercaseString]; 1793 if (!role) 1794 role = @"REQ-PARTICIPANT"; 1795 if ([role isEqualToString: @"NON-PARTICIPANT"]) 1796 partstat = @""; 1797 else 1798 { 1799 partstat = [[currentData objectForKey: @"partstat"] 1800 uppercaseString]; 1801 if (!partstat) 1802 partstat = @"NEEDS-ACTION"; 1803 } 1804 currentAttendee = [component findAttendeeWithEmail: currentEmail]; 1805 if (!currentAttendee) 1806 { 1807 currentAttendee = [iCalPerson elementWithTag: @"attendee"]; 1808 [currentAttendee setCn: [currentData objectForKey: @"name"]]; 1809 [currentAttendee setEmail: currentEmail]; 1810 // [currentAttendee 1811 // setParticipationStatus: iCalPersonPartStatNeedsAction]; 1812 } 1813 [currentAttendee 1814 setRsvp: ([role isEqualToString: @"NON-PARTICIPANT"] 1815 ? @"FALSE" 1816 : @"TRUE")]; 1817 [currentAttendee setRole: role]; 1818 [currentAttendee setPartStat: partstat]; 1819 [newAttendees addObject: currentAttendee]; 1820 } 1821 } 1822 [component setAttendees: newAttendees]; 1823 } 1824 else 1825 { 1826 //NSLog(@"Error scanning following JSON:\n%@", json); 1827 } 1828 } 1829} 1830 1831- (void) _handleOrganizer 1832{ 1833 NSString *owner, *login, *currentEmail; 1834 BOOL isOwner, hasAttendees; 1835 1836 //owner = [[self clientObject] ownerInContext: context]; 1837 owner = [componentCalendar ownerInContext: context]; 1838 login = [[context activeUser] login]; 1839 isOwner = [owner isEqualToString: login]; 1840 hasAttendees = [self hasAttendees]; 1841 currentEmail = [[[context activeUser] allEmails] objectAtIndex: 0]; 1842 1843 if (hasAttendees) 1844 { 1845 SOGoUser *user; 1846 id identity; 1847 1848 ASSIGN (organizer, [iCalPerson elementWithTag: @"organizer"]); 1849 [component setOrganizer: organizer]; 1850 1851 user = [SOGoUser userWithLogin: owner roles: nil]; 1852 identity = [user defaultIdentity]; 1853 [organizer setCn: [identity objectForKey: @"fullName"]]; 1854 [organizer setEmail: [identity objectForKey: @"email"]]; 1855 1856 if (!isOwner) 1857 { 1858 NSString *quotedEmail; 1859 1860 quotedEmail = [NSString stringWithFormat: @"\"MAILTO:%@\"", 1861 currentEmail]; 1862 [organizer setValue: 0 ofAttribute: @"SENT-BY" 1863 to: quotedEmail]; 1864 } 1865 } 1866 else 1867 { 1868 organizer = nil; 1869 } 1870 [component setOrganizer: organizer]; 1871 1872 // In case of a new component, if the current user isn't the owner of the calendar, we 1873 // add the "X-SOGo-Component-Created-By: <email address>" attribute 1874 if ([[self clientObject] isNew] && 1875 !isOwner && 1876 [currentEmail length]) 1877 { 1878 [component addChild: [CardElement simpleElementWithTag: @"X-SOGo-Component-Created-By" 1879 value: currentEmail]]; 1880 } 1881 1882} 1883 1884- (void) _handleCustomRRule: (iCalRecurrenceRule *) theRule 1885 1886{ 1887 int type, range; 1888 NSMutableArray *values; 1889 1890 // We decode the range 1891 range = [[self range1] intValue]; 1892 1893 // Create X appointments 1894 if (range == 1) 1895 { 1896 [theRule setRepeatCount: [[self range2] intValue]]; 1897 } 1898 // Repeat until date 1899 else if (range == 2) 1900 { 1901 NSCalendarDate *date; 1902 SOGoUserDefaults *ud; 1903 1904 date = [NSCalendarDate dateWithString: [self range2] 1905 calendarFormat: dateFormat 1906 locale: locale]; 1907 1908 // Adjust timezone 1909 ud = [[context activeUser] userDefaults]; 1910 date = [NSCalendarDate dateWithYear: [date yearOfCommonEra] 1911 month: [date monthOfYear] 1912 day: [date dayOfMonth] 1913 hour: 0 minute: 0 second: 0 1914 timeZone: [ud timeZone]]; 1915 1916 [theRule setUntilDate: date]; 1917 } 1918 // No end date. 1919 else 1920 { 1921 // Do nothing? 1922 } 1923 1924 1925 // We decode the type and the rest accordingly. 1926 type = [[self repeatType] intValue]; 1927 1928 switch (type) 1929 { 1930 // DAILY (0) 1931 // 1932 // repeat1 holds the value of the frequency radio button: 1933 // 0 -> Every X days 1934 // 1 -> Every weekday 1935 // 1936 // repeat2 holds the value of X when repeat1 equals 0 1937 // 1938 case 0: 1939 { 1940 [theRule setFrequency: iCalRecurrenceFrequenceDaily]; 1941 1942 if ([[self repeat1] intValue] > 0) 1943 { 1944 [theRule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]]; 1945 } 1946 else 1947 { 1948 // Make sure we haven't received any junk.... 1949 if ([[self repeat2] intValue] < 1) 1950 [self setRepeat2: @"1"]; 1951 1952 [theRule setInterval: [self repeat2]]; 1953 } 1954 } 1955 break; 1956 1957 // WEEKLY (1) 1958 // 1959 // repeat1 holds the value of "Every X week(s)" 1960 // 1961 // repeat2 holds which days are part of the recurrence rule 1962 // 1 -> Monday 1963 // 2 -> Tuesday .. and so on. 1964 // The list is separated by commas, like: 1,3,4 1965 case 1: 1966 { 1967 if ([[self repeat1] intValue] > 0) 1968 { 1969 NSArray *v; 1970 int c, day; 1971 iCalWeekOccurrences days; 1972 1973 [theRule setFrequency: iCalRecurrenceFrequenceWeekly]; 1974 [theRule setInterval: [self repeat1]]; 1975 1976 if ([[self repeat2] length]) 1977 { 1978 v = [[self repeat2] componentsSeparatedByString: @","]; 1979 c = [v count]; 1980 memset(days, 0, 7 * sizeof(iCalWeekOccurrence)); 1981 1982 while (c--) 1983 { 1984 day = [[v objectAtIndex: c] intValue]; 1985 if (day >= 0 && day <= 7) 1986 days[day] = iCalWeekOccurrenceAll; 1987 } 1988 [theRule setByDayMask: [iCalByDayMask byDayMaskWithDays: days]]; 1989 } 1990 } 1991 } 1992 break; 1993 1994 // MONTHLY (2) 1995 // 1996 // repeat1 holds the value of "Every X month(s)" 1997 // 1998 // repeat2 holds the value of the radio-button "The" / "Recur on day(s)" 1999 // 0 -> The 2000 // 1 -> Recur on day(s) 2001 // 2002 // repeat3 holds the value of the first popup 2003 // 0 -> First 2004 // 1 -> Second ... and so on. 2005 // 2006 // repeat4 holds the value of the second popop 2007 // 0 -> Sunday 2008 // 1 -> Monday ... and so on. 2009 // 7 -> Day of the month 2010 // 2011 // repeat5 holds the selected days when "Recur on day(s)" 2012 // is chosen. The value starts at 1. 2013 // 2014 case 2: 2015 { 2016 if ([[self repeat1] intValue] > 0) 2017 { 2018 [theRule setFrequency: iCalRecurrenceFrequenceMonthly]; 2019 [theRule setInterval: [self repeat1]]; 2020 2021 // We recur on specific days... 2022 if ([[self repeat2] intValue] == 0) 2023 { 2024 NSString *day; 2025 int occurence; 2026 2027 day = [theRule iCalRepresentationForWeekDay: [[self repeat4] intValue]]; 2028 occurence = [[self repeat3] intValue] + 1; 2029 if (occurence > 5) // the first/second/third/fourth/fifth .. 2030 occurence = -1; // the last .. 2031 2032 // Day of the month (last, fourth, etc.) 2033 if ([[self repeat4] intValue] == 7) 2034 { 2035 [theRule setSingleValue: [NSString stringWithFormat: @"%d",occurence] 2036 forKey: @"bymonthday"]; 2037 } 2038 else 2039 [theRule setSingleValue: [NSString stringWithFormat: @"%d%@", 2040 occurence, day] 2041 forKey: @"byday"]; 2042 } 2043 else 2044 { 2045 if ([[self repeat5] intValue] > 0) 2046 { 2047 values = [[[self repeat5] 2048 componentsSeparatedByString: @","] 2049 mutableCopy]; 2050 [theRule setValues: values 2051 atIndex: 0 forKey: @"bymonthday"]; 2052 [values release]; 2053 } 2054 } 2055 } 2056 } 2057 break; 2058 2059 // YEARLY (3) 2060 // 2061 // repeat1 holds the value of "Every X year(s)" 2062 // 2063 // repeat2 holds the value of the radio-button "Every" / "Every .. of .." 2064 // 0 -> Every 2065 // 1 -> Every .. of .. 2066 // 2067 // repeat3 holds the value of the DAY parameter 2068 // repeat4 holds the value of the MONTH parameter (0 -> January, 1 -> February ... ) 2069 // ex: 3 February 2070 // 2071 // repeat5 holds the value of the OCCURENCE parameter (0 -> First, 1 -> Second .., 5 -> Last) 2072 // repeat6 holds the value of the DAY parameter (0 -> Sunday, 1 -> Monday, etc..) 2073 // repeat7 holds the value of the MONTH parameter (0 -> January, 1 -> February ... ) 2074 // 2075 case 3: 2076 default: 2077 { 2078 if ([[self repeat1] intValue] > 0) 2079 { 2080 [theRule setFrequency: iCalRecurrenceFrequenceYearly]; 2081 [theRule setInterval: [self repeat1]]; 2082 2083 // We recur Every .. of .. 2084 if ([[self repeat2] intValue] == 1) 2085 { 2086 NSString *day; 2087 int occurence; 2088 2089 day = [theRule iCalRepresentationForWeekDay: [[self repeat6] intValue]]; 2090 occurence = [[self repeat5] intValue] + 1; 2091 if (occurence > 5) // the first/second/third/fourth/fifth .. 2092 occurence = -1; // the last .. 2093 [theRule setSingleValue: [NSString stringWithFormat: @"%d%@", 2094 occurence, day] 2095 forKey: @"byday"]; 2096 [theRule setSingleValue: [NSString stringWithFormat: @"%d", 2097 [[self repeat7] intValue] + 1] 2098 forKey: @"bymonth"]; 2099 } 2100 else 2101 { 2102 if ([[self repeat3] intValue] > 0 2103 && [[self repeat4] intValue] > 0) 2104 { 2105 values = [[[self repeat3] 2106 componentsSeparatedByString: @","] 2107 mutableCopy]; 2108 [theRule setValues: values atIndex: 0 2109 forKey: @"bymonthday"]; 2110 [values release]; 2111 [theRule setSingleValue: [NSString stringWithFormat: @"%d", 2112 [[self repeat4] intValue] + 1] 2113 forKey: @"bymonth"]; 2114 } 2115 } 2116 } 2117 } 2118 break; 2119 } 2120} 2121 2122 2123 2124- (void) takeValuesFromRequest: (WORequest *) _rq 2125 inContext: (WOContext *) _ctx 2126{ 2127 SOGoCalendarComponent *clientObject; 2128 iCalRecurrenceRule *rule; 2129 NSCalendarDate *now; 2130 2131 [super takeValuesFromRequest: _rq inContext: _ctx]; 2132 2133 now = [NSCalendarDate calendarDate]; 2134 [component setSummary: title]; 2135 [component setLocation: location]; 2136 [component setComment: comment]; 2137 [component setAttach: attachUrl]; 2138 [component setAccessClass: classification]; 2139 [component setCategories: categories]; 2140 [self _handleAttendeesEdition]; 2141 [self _handleOrganizer]; 2142 clientObject = [self clientObject]; 2143 if ([clientObject isNew]) 2144 { 2145 [component setCreated: now]; 2146 [component setTimeStampAsDate: now]; 2147 } 2148 [component setPriority: priority]; 2149 [component setLastModified: now]; 2150 2151 if (!reminder || [reminder caseInsensitiveCompare: @"-"] == NSOrderedSame) 2152 // No alarm selected -- if there was an unsupported alarm defined in 2153 // the event, it will be deleted. 2154 [component removeAllAlarms]; 2155 else 2156 { 2157 iCalAlarm *anAlarm; 2158 NSString *aValue; 2159 NSUInteger index; 2160 2161 index = [reminderItems indexOfObject: reminder]; 2162 aValue = [reminderValues objectAtIndex: index]; 2163 2164 // Predefined alarm 2165 if ([aValue length]) 2166 { 2167 iCalTrigger *aTrigger; 2168 2169 anAlarm = [[[iCalAlarm alloc] init] autorelease]; 2170 aTrigger = [iCalTrigger elementWithTag: @"TRIGGER"]; 2171 [aTrigger setValueType: @"DURATION"]; 2172 [anAlarm setTrigger: aTrigger]; 2173 [anAlarm setAction: @"DISPLAY"]; 2174 [aTrigger setSingleValue: aValue forKey: @""]; 2175 } 2176 else 2177 { 2178 // Custom alarm 2179 anAlarm = [iCalAlarm alarmForEvent: component 2180 owner: [[self clientObject] ownerInContext: context] 2181 action: reminderAction 2182 unit: reminderUnit 2183 quantity: reminderQuantity 2184 reference: reminderReference 2185 reminderRelation: reminderRelation 2186 emailAttendees: reminderEmailAttendees 2187 emailOrganizer: reminderEmailOrganizer]; 2188 } 2189 2190 if (anAlarm) 2191 { 2192 [component removeAllAlarms]; 2193 [component addToAlarms: anAlarm]; 2194 } 2195 } 2196 2197 if (![self isChildOccurence]) 2198 { 2199 // We remove any repeat rules 2200 if (!repeat && [component hasRecurrenceRules]) 2201 [component removeAllRecurrenceRules]; 2202 else if ([repeat caseInsensitiveCompare: @"-"] != NSOrderedSame) 2203 { 2204 rule = [iCalRecurrenceRule new]; 2205 2206 [rule setInterval: @"1"]; 2207 2208 if ([repeat caseInsensitiveCompare: @"BI-WEEKLY"] == NSOrderedSame) 2209 { 2210 [rule setFrequency: iCalRecurrenceFrequenceWeekly]; 2211 [rule setInterval: @"2"]; 2212 } 2213 else if ([repeat caseInsensitiveCompare: @"EVERY WEEKDAY"] == NSOrderedSame) 2214 { 2215 [rule setByDayMask: [iCalByDayMask byDayMaskWithWeekDays]]; 2216 [rule setFrequency: iCalRecurrenceFrequenceDaily]; 2217 } 2218 else if ([repeat caseInsensitiveCompare: @"MONTHLY"] == NSOrderedSame 2219 || [repeat caseInsensitiveCompare: @"DAILY"] == NSOrderedSame 2220 || [repeat caseInsensitiveCompare: @"WEEKLY"] == NSOrderedSame 2221 || [repeat caseInsensitiveCompare: @"YEARLY"] == NSOrderedSame) 2222 { 2223 [rule setInterval: @"1"]; 2224 [rule setFrequency: 2225 (iCalRecurrenceFrequency) [rule valueForFrequency: repeat]]; 2226 } 2227 else 2228 { 2229 // We have a CUSTOM recurrence. Let's decode what kind of custome recurrence 2230 // we have and set that. 2231 [self _handleCustomRRule: rule]; 2232 } 2233 2234 [component setRecurrenceRules: [NSArray arrayWithObject: rule]]; 2235 [rule release]; 2236 } 2237 } 2238} 2239 2240#warning the following methods probably share some code... 2241- (NSString *) _toolbarForOwner: (SOGoUser *) ownerUser 2242 andClientObject: (SOGoContentObject 2243 <SOGoComponentOccurence> *) clientObject 2244{ 2245 NSString *toolbarFilename; 2246 BOOL isOrganizer; 2247 2248 // We determine if we're the organizer of the component beeing modified. 2249 // If we created an event on behalf of someone else -userIsOrganizer will 2250 // return us YES. This is OK because we're in the SENT-BY. But, Alice 2251 // should be able to accept/decline an invitation if she created the event 2252 // in Bob's calendar and added herself in the attendee list. 2253 isOrganizer = [component userIsOrganizer: ownerUser]; 2254 2255 if (isOrganizer) 2256 isOrganizer = ![ownerUser hasEmail: [[component organizer] sentBy]]; 2257 2258 if ([componentCalendar isKindOfClass: [SOGoWebAppointmentFolder class]] 2259 || ([component userIsAttendee: ownerUser] 2260 && !isOrganizer 2261 // Lightning does not manage participation status within tasks, 2262 // so we also ignore the participation status of tasks in the 2263 // web interface. 2264 && ![[component tag] isEqualToString: @"VTODO"])) 2265 toolbarFilename = @"SOGoEmpty.toolbar"; 2266 else 2267 { 2268 if ([clientObject isKindOfClass: [SOGoAppointmentObject class]] 2269 || [clientObject isKindOfClass: [SOGoAppointmentOccurence class]]) 2270 toolbarFilename = @"SOGoAppointmentObject.toolbar"; 2271 else 2272 toolbarFilename = @"SOGoTaskObject.toolbar"; 2273 } 2274 2275 return toolbarFilename; 2276} 2277 2278- (NSString *) _toolbarForDelegate: (SOGoUser *) ownerUser 2279 andClientObject: (SOGoContentObject 2280 <SOGoComponentOccurence> *) clientObject 2281{ 2282 SoSecurityManager *sm; 2283 NSString *toolbarFilename; 2284 2285 sm = [SoSecurityManager sharedSecurityManager]; 2286 2287 if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent 2288 onObject: clientObject 2289 inContext: context]) 2290 toolbarFilename = [self _toolbarForOwner: ownerUser 2291 andClientObject: clientObject]; 2292 else 2293 toolbarFilename = @"SOGoEmpty.toolbar"; 2294 2295 return toolbarFilename; 2296} 2297 2298- (NSString *) toolbar 2299{ 2300 SOGoContentObject <SOGoComponentOccurence> *clientObject; 2301 NSString *toolbarFilename; 2302 SOGoUser *ownerUser; 2303 2304 clientObject = [self clientObject]; 2305 ownerUser = [SOGoUser userWithLogin: [clientObject ownerInContext: context] 2306 roles: nil]; 2307 2308 if ([ownerUser isEqual: [context activeUser]]) 2309 toolbarFilename = [self _toolbarForOwner: ownerUser 2310 andClientObject: clientObject]; 2311 else 2312 toolbarFilename = [self _toolbarForDelegate: ownerUser 2313 andClientObject: clientObject]; 2314 2315 2316 return toolbarFilename; 2317} 2318 2319 2320- (int) ownerIsAttendee: (SOGoUser *) ownerUser 2321 andClientObject: (SOGoContentObject 2322 <SOGoComponentOccurence> *) clientObject 2323{ 2324 BOOL isOrganizer; 2325 iCalPerson *ownerAttendee; 2326 int rc; 2327 2328 rc = 0; 2329 2330 isOrganizer = [component userIsOrganizer: ownerUser]; 2331 if (isOrganizer) 2332 isOrganizer = ![ownerUser hasEmail: [[component organizer] sentBy]]; 2333 2334 if (!isOrganizer && ![[component tag] isEqualToString: @"VTODO"]) 2335 { 2336 ownerAttendee = [component userAsAttendee: ownerUser]; 2337 if (ownerAttendee) 2338 rc = 1; 2339 } 2340 2341 return rc; 2342} 2343 2344- (int) delegateIsAttendee: (SOGoUser *) ownerUser 2345 andClientObject: (SOGoContentObject 2346 <SOGoComponentOccurence> *) clientObject 2347{ 2348 SoSecurityManager *sm; 2349 iCalPerson *ownerAttendee; 2350 int rc; 2351 2352 rc = 0; 2353 2354 sm = [SoSecurityManager sharedSecurityManager]; 2355 if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent 2356 onObject: clientObject 2357 inContext: context]) 2358 rc = [self ownerIsAttendee: ownerUser 2359 andClientObject: clientObject]; 2360 else if (![sm validatePermission: SOGoCalendarPerm_RespondToComponent 2361 onObject: clientObject 2362 inContext: context]) 2363 { 2364 ownerAttendee = [component userAsAttendee: ownerUser]; 2365 if ([[ownerAttendee rsvp] isEqualToString: @"true"] 2366 && ![component userIsOrganizer: ownerUser]) 2367 rc = 1; 2368 else 2369 rc = 2; 2370 } 2371 else 2372 rc = 2; // not invited, just RO 2373 2374 return rc; 2375} 2376 2377- (int) getEventRWType 2378{ 2379 SOGoContentObject <SOGoComponentOccurence> *clientObject; 2380 SOGoUser *ownerUser; 2381 int rc; 2382 2383 clientObject = [self clientObject]; 2384 ownerUser 2385 = [SOGoUser userWithLogin: [clientObject ownerInContext: context]]; 2386 if ([componentCalendar isKindOfClass: [SOGoWebAppointmentFolder class]]) 2387 rc = 2; 2388 else 2389 { 2390 if ([ownerUser isEqual: [context activeUser]]) 2391 rc = [self ownerIsAttendee: ownerUser 2392 andClientObject: clientObject]; 2393 else 2394 rc = [self delegateIsAttendee: ownerUser 2395 andClientObject: clientObject]; 2396 } 2397 2398 return rc; 2399} 2400 2401- (BOOL) eventIsReadOnly 2402{ 2403 return [self getEventRWType] != 0; 2404} 2405 2406- (NSString *) emailAlarmsEnabled 2407{ 2408 SOGoSystemDefaults *sd; 2409 2410 sd = [SOGoSystemDefaults sharedSystemDefaults]; 2411 2412 return ([sd enableEMailAlarms] 2413 ? @"true" 2414 : @"false"); 2415} 2416 2417- (BOOL) userHasRSVP 2418{ 2419 return ([self getEventRWType] == 1); 2420} 2421 2422- (NSString *) currentAttendeeClasses 2423{ 2424 NSMutableArray *classes; 2425 iCalPerson *ownerAttendee; 2426 SOGoUser *ownerUser; 2427 NSString *role, *partStat; 2428 SOGoCalendarComponent *co; 2429 2430 classes = [NSMutableArray arrayWithCapacity: 5]; 2431 2432 /* rsvp class */ 2433 if (![[attendee rsvp] isEqualToString: @"true"]) 2434 [classes addObject: @"not-rsvp"]; 2435 2436 /* partstat class */ 2437 partStat = [[attendee partStat] lowercaseString]; 2438 if (![partStat length]) 2439 partStat = @"no-partstat"; 2440 [classes addObject: partStat]; 2441 2442 /* role class */ 2443 role = [[attendee role] lowercaseString]; 2444 if (![partStat length]) 2445 role = @"no-role"; 2446 [classes addObject: role]; 2447 2448 /* attendee class */ 2449 if ([[attendee delegatedFrom] length] > 0) 2450 [classes addObject: @"delegate"]; 2451 2452 /* current attendee class */ 2453 co = [self clientObject]; 2454 ownerUser = [SOGoUser userWithLogin: [co ownerInContext: context]]; 2455 ownerAttendee = [component userAsAttendee: ownerUser]; 2456 if (attendee == ownerAttendee) 2457 [classes addObject: @"attendeeUser"]; 2458 2459 return [classes componentsJoinedByString: @" "]; 2460} 2461 2462- (NSString *) ownerLogin 2463{ 2464 return [[self clientObject] ownerInContext: context]; 2465} 2466 2467- (unsigned int) firstDayOfWeek 2468{ 2469 SOGoUserDefaults *ud; 2470 2471 ud = [[context activeUser] userDefaults]; 2472 2473 return [ud firstDayOfWeek]; 2474} 2475 2476// returns the raw content of the object 2477- (WOResponse *) rawAction 2478{ 2479 NSMutableString *content; 2480 WOResponse *response; 2481 2482 content = [NSMutableString string]; 2483 response = [context response]; 2484 2485 [content appendFormat: @"%@", [[self clientObject] contentAsString]]; 2486 [response setHeader: @"text/plain; charset=utf-8" 2487 forKey: @"content-type"]; 2488 [response appendContentString: content]; 2489 2490 return response; 2491} 2492 2493+ (NSArray *) reminderValues 2494{ 2495 return reminderValues; 2496} 2497 2498@end 2499