"Fossies" - the Fresh Open Source Software Archive

Member "opengroupware-5.5rc3/WebUI/JobUI/LSWJobEditor.m" (5 Dec 2015, 35647 Bytes) of package /linux/privat/opengroupware-5.5rc3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Matlab source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "LSWJobEditor.m" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.5rc2_vs_5.5rc3.

    1 /*
    2   Copyright (C) 2000-2005 SKYRIX Software AG
    3 
    4   This file is part of OpenGroupware.org.
    5 
    6   OGo is free software; you can redistribute it and/or modify it under
    7   the terms of the GNU Lesser General Public License as published by the
    8   Free Software Foundation; either version 2, or (at your option) any
    9   later version.
   10 
   11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
   12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
   14   License for more details.
   15 
   16   You should have received a copy of the GNU Lesser General Public
   17   License along with OGo; see the file COPYING.  If not, write to the
   18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   19   02111-1307, USA.
   20 */
   21 
   22 #include <OGoFoundation/LSWEditorPage.h>
   23 
   24 @class NSString, NSArray, NSMutableArray, NSDictionary;
   25 
   26 @interface LSWJobEditor : LSWEditorPage
   27 {
   28 @protected
   29   id             item;
   30   int            idx;
   31   id             project;
   32   BOOL           notifyExecutant;
   33   BOOL           isImportMode;
   34   BOOL           isProjectLinkMode;
   35   BOOL           isEnterpriseLinkMode;  
   36   NSArray        *notifyList;
   37   NSArray        *teams;
   38 
   39   id             team;
   40   id             executantSelection;
   41   
   42   NSMutableArray *resultList;
   43 
   44   NSString       *searchAccount;
   45 
   46   NSDictionary   *snapshotCopy;
   47   NSArray        *notifyLabels;
   48 
   49   BOOL           isProjectEnabled;
   50   NSString       *accountLabelFormat;
   51   int            noOfCols;
   52 
   53   id             referredPerson;
   54 
   55   NSArray        *selPrefAccounts;
   56 }
   57 
   58 - (void)setProject:(id)_project;
   59 - (id)project;
   60 
   61 @end
   62 
   63 #include "LSWJobMailPage.h"
   64 #include "NSArray+JobIntNums.h"
   65 #include "common.h"
   66 #include <NGObjWeb/WOMailDelivery.h>
   67 #include <OGoFoundation/LSWNotifications.h>
   68 #include <EOControl/EOControl.h>
   69 #include <LSFoundation/OGoObjectLinkManager.h>
   70 #include <LSFoundation/OGoObjectLink.h>
   71 #include <OGoFoundation/LSWMailEditorComponent.h>
   72 
   73 #include <OGoJobs/SkyPersonJobDataSource.h>
   74 
   75 static int compareAccounts(id e1, id e2, void* context) {
   76   BOOL isTeam1 = [[e1 valueForKey:@"isTeam"] boolValue];
   77   BOOL isTeam2 = [[e2 valueForKey:@"isTeam"] boolValue];
   78 
   79   if (isTeam1) {
   80     if (!isTeam2)
   81       return NSOrderedAscending;
   82 
   83     return [[e1 valueForKey:@"description"]
   84                 caseInsensitiveCompare:[e2 objectForKey:@"description"]];
   85   }
   86   if (isTeam2)
   87     return NSOrderedDescending;
   88 
   89   return [[e1 valueForKey:@"name"]
   90               caseInsensitiveCompare:[e2 objectForKey:@"name"]];
   91 }
   92 
   93 @interface LSWJobEditor(Private)
   94 - (id)_save;
   95 - (BOOL)preferredExecutantsEnabled;
   96 @end /* LSWJobEditor(Private) */
   97 
   98 @implementation LSWJobEditor
   99 
  100 static NSArray  *JobAttrsEditor_percentages = nil;
  101 static NSArray  *JobAttrsEditor_priorities  = nil;
  102 static NSArray  *defNotifyList   = nil;
  103 static NSArray  *defNotifyLabels = nil;
  104 static NSString *preferredJobExecutantLinkType = @"Preferred Job Executant";
  105 
  106 static BOOL JobAttributesCollapsible = NO;
  107 static BOOL PreferredAccountsEnabled = NO;
  108 static BOOL HasSkyProject4Desktop    = NO;
  109 
  110 + (void)initialize {
  111   NSUserDefaults  *ud = [NSUserDefaults standardUserDefaults];
  112   NGBundleManager *bm = [NGBundleManager defaultBundleManager];
  113   static BOOL didInit = NO;
  114   if (didInit) return;
  115   didInit = YES;
  116   
  117   JobAttributesCollapsible = [ud boolForKey:@"JobReferredPersonEnabled"];
  118   PreferredAccountsEnabled = [ud boolForKey:@"JobPreferredExecutantsEnabled"];
  119 
  120   HasSkyProject4Desktop = [bm bundleProvidingResource:@"SkyProject4Desktop"
  121                   ofType:@"WOComponents"] != nil ? YES : NO;
  122   
  123   JobAttrsEditor_percentages =
  124     [[[ud arrayForKey:@"JobAttrsEditor_percentages"] 
  125           arrayByConvertingValuesToIntNumbers] copy];
  126   JobAttrsEditor_priorities =
  127     [[[ud arrayForKey:@"JobAttrsEditor_priorities"] 
  128           arrayByConvertingValuesToIntNumbers] copy];
  129 
  130   defNotifyList = [[NSArray alloc] initWithObjects:
  131                      [NSNumber numberWithInt:0],
  132                      [NSNumber numberWithInt:1],
  133                      [NSNumber numberWithInt:2],
  134                    nil];
  135   defNotifyLabels = [[NSArray alloc] initWithObjects:@"Never", @"Always",
  136                        @"OnAcceptDone", nil];
  137 }
  138 
  139 - (id)init {
  140   if (self == [super init]) {
  141     self->isProjectEnabled = HasSkyProject4Desktop;
  142     self->noOfCols         = -1;
  143     
  144     self->resultList      = [[NSMutableArray alloc] initWithCapacity:16];
  145     self->selPrefAccounts = [[NSMutableArray alloc] initWithCapacity:16];
  146   }
  147   return self;
  148 }
  149 
  150 - (void)dealloc {
  151   [self->item                release];
  152   [self->project             release];
  153   [self->notifyList          release];
  154   [self->teams               release];
  155   [self->team                release];
  156   [self->executantSelection  release];
  157   [self->resultList          release];
  158   [self->searchAccount       release];
  159   [self->snapshotCopy        release];
  160   [self->notifyLabels        release];
  161   [self->accountLabelFormat  release];
  162   [self->referredPerson      release];
  163   [self->selPrefAccounts     release];
  164   [super dealloc];
  165 }
  166 
  167 /* accessors */
  168 
  169 - (NSArray *)sensitivities {
  170   // TODO: is it important to have numbers? otherwise we have 
  171   //       JobAttrsEditor_sensitivities
  172   return [NSArray arrayWithObjects:
  173                   [NSNumber numberWithInt:1],
  174                   [NSNumber numberWithInt:2],
  175                   [NSNumber numberWithInt:3],
  176                   [NSNumber numberWithInt:4],
  177                   nil];
  178 }
  179 
  180 - (NSString *)sensitivity {
  181   NSString *s;
  182 
  183   s = [@"sensitivity_" stringByAppendingString:[item stringValue]];
  184   return [[self labels] valueForKey:s];
  185 }
  186 
  187 - (void)setProject:(id)_project { 
  188   ASSIGN(self->project, _project);
  189 }
  190 - (id)project {
  191   return self->project;
  192 }
  193 
  194 - (void)setNotifyExecutant:(BOOL)_notify {
  195   self->notifyExecutant = _notify;
  196 }
  197 - (BOOL)notifyExecutant {
  198   return self->notifyExecutant;
  199 }
  200 
  201 - (void)setNotifyList:(NSArray *)_notifyList {
  202   ASSIGN(self->notifyList, _notifyList);
  203 }
  204 - (NSArray *)notifyList {
  205   return self->notifyList;
  206 }
  207 
  208 - (void)setItem:(id)_item { 
  209   ASSIGN(self->item, _item);
  210 }
  211 - (id)item {
  212   return self->item;
  213 }
  214 
  215 - (void)setExecutantSelection:(id)_ex {
  216   ASSIGN(self->executantSelection, _ex);
  217 }
  218 - (id)executantSelection {
  219   return self->executantSelection;
  220 }
  221 
  222 - (void)setIsSelected:(BOOL)_flag {
  223   if (_flag)
  224     [self setExecutantSelection:self->item];
  225 }
  226 - (BOOL)isSelected {
  227   return [self->item isEqual:self->executantSelection];
  228 }
  229 
  230 - (void)setTeam:(id)_id {
  231   ASSIGN(self->team, _id);
  232 }
  233 - (id)team {
  234   return self->team;
  235 }
  236 
  237 - (void)setSearchAccount:(NSString *)_str {
  238   ASSIGNCOPY(self->searchAccount, _str);
  239 }
  240 - (NSString *)searchAccount {
  241   return self->searchAccount;
  242 }
  243 
  244 - (void)setIdx:(int)_idx {
  245   self->idx = _idx;
  246 }
  247 - (int)idx {
  248   return self->idx;
  249 }
  250 
  251 - (NSString *)priorityName {
  252   NSString *pri;
  253   char buf[64];
  254   
  255   snprintf(buf, sizeof(buf), "priority_%d", [self->item unsignedIntValue]);
  256   pri = [[self labels] valueForKey:[NSString stringWithCString:buf]];
  257   
  258   if (pri == nil)
  259     pri = [self->item stringValue];
  260   return pri;
  261 }
  262 
  263 - (NSString *)notifyItem {
  264   NSString *s;
  265 
  266   s = [self->notifyLabels objectAtIndex:[self->item intValue]];
  267   return [[self labels] valueForKey:s];
  268 }
  269 
  270 - (NSDictionary *)job {
  271   return [self snapshot];
  272 }
  273 
  274 - (BOOL)isCommentMode {
  275   return ([self isInNewMode] || self->isImportMode);
  276 }
  277 
  278 - (NSString *)jobUrl:(id)_job {
  279   WOContext *ctx;
  280   NSString  *urlPrefix;
  281   NSString  *url;
  282 
  283   // TODO: use the WOContext URL generation methods!
  284   ctx       = [self context];
  285   urlPrefix = [ctx urlSessionPrefix];
  286   url       = [[ctx request] headerForKey:@"x-webobjects-server-url"];
  287   
  288   if (url != nil && [url length] > 0) {
  289     return [NSString stringWithFormat:
  290                      @"%@%@/wa/LSWViewAction/viewJob?jobId=%@",
  291                      url, urlPrefix, [_job valueForKey:@"jobId"]];
  292   }
  293   else {
  294     return [NSString stringWithFormat:
  295                      @"http://%@%@/wa/LSWViewAction/viewJob?jobId=%@",
  296                      [[ctx request] headerForKey:@"host"],
  297                      urlPrefix, [_job valueForKey:@"jobId"]];
  298   }
  299 }
  300 
  301 
  302 - (BOOL)showProjectName {
  303   return (self->isProjectLinkMode || self->isEnterpriseLinkMode ||
  304           (self->project != nil));
  305 }
  306 
  307 - (BOOL)showProjectSelection {
  308   return !(self->isProjectLinkMode || self->isEnterpriseLinkMode);
  309 }
  310 
  311 - (NSArray *)teams {
  312   return self->teams;
  313 }
  314 
  315 - (NSString *)noProjectLabel {
  316   NSString *l;
  317 
  318   l = [[self labels] valueForKey:@"noProject"];
  319   return (l != nil) ? l : (NSString *)@"- no project -";
  320 }
  321 
  322 - (BOOL)hasExecutants {
  323   return ([self->executantSelection isNotNull] ||
  324           [self->resultList count] > 0) ? YES : NO;
  325 }
  326 
  327 - (BOOL)hasResultList {
  328   return ([self->resultList count] > 0) ? YES : NO;
  329 }
  330 
  331 - (NSArray *)resultList {
  332   if (([self->resultList count] == 0) &&
  333       ([self->executantSelection isNotNull]))
  334     [self->resultList addObject:self->executantSelection];
  335   return self->resultList;
  336 }
  337 
  338 - (void)setExecutant:(id)_ex {
  339   [self setExecutantSelection:_ex];
  340 }
  341 
  342 - (BOOL)isTeamSelected {
  343   return [[self->executantSelection objectForKey:@"isTeam"] boolValue];
  344 }
  345 
  346 - (int)noOfCols {
  347   if (self->noOfCols == -1) {
  348     id  d;
  349     int n;
  350     
  351     d = [[[self session] userDefaults] objectForKey:@"job_no_of_cols"];
  352     n = [d intValue];
  353     self->noOfCols =  (n > 0) ? n : 3;
  354   }
  355   return self->noOfCols;
  356 }
  357 
  358 - (BOOL)startNewLine {
  359   if (self->idx == 0) return NO; // first line always exists
  360   return ((self->idx % [self noOfCols]) == 0) ? YES : NO;
  361 }
  362 
  363 /* TODO: the following is weird, just register the default default */
  364 - (NSString *)defaultAccountLableFormat {
  365   return @"$name$, $firstname$ ($login$)";
  366 }
  367 - (NSString *)accountLabelFormat {
  368   if (self->accountLabelFormat == nil) {
  369     NSUserDefaults *ud;
  370     
  371     ud = [[self session] userDefaults];
  372     self->accountLabelFormat = 
  373       [ud stringForKey:@"job_editor_account_format"];
  374     if ([self->accountLabelFormat length] == 0)
  375       self->accountLabelFormat = [self defaultAccountLableFormat];
  376     self->accountLabelFormat = [self->accountLabelFormat copy];
  377   }
  378   return self->accountLabelFormat;
  379 }
  380 
  381 - (NSString *)accountDescription {
  382   return [[self accountLabelFormat]
  383                 stringByReplacingVariablesWithBindings:self->item
  384                 stringForUnknownBindings:@""];
  385 }
  386 
  387 - (NSString *)teamDescription {
  388   return [NSString stringWithFormat:@"Team: %@",
  389                    [self->item valueForKey:@"description"]];
  390 }
  391 
  392 - (NSString *)labelForAccount:(id)_part {
  393   if ([[_part valueForKey:@"isTeam"] boolValue])
  394     return [_part valueForKey:@"description"];
  395 
  396   return [[self accountLabelFormat]
  397                 stringByReplacingVariablesWithBindings:_part
  398                 stringForUnknownBindings:@""];
  399 }
  400 
  401 - (BOOL)hasTeam {
  402   return (self->team == nil) ? NO : YES;
  403 }
  404 
  405 - (NSString *)teamName {
  406   return [self->item valueForKey:@"description"];
  407 }
  408 
  409 - (BOOL)isProjectEnabled {
  410   return self->isProjectEnabled;
  411 }
  412 
  413 - (BOOL)userIsCreator {
  414   return [[[[self session] activeAccount] valueForKey:@"companyId"]
  415                   isEqual:[[self object] valueForKey:@"creatorId"]];
  416 }
  417 - (BOOL)showExecutantSelection {
  418   if ([self isInNewMode]) return YES;
  419   return [self userIsCreator];
  420 }
  421 
  422 - (NSArray *)percentList {
  423   NSArray  *pList;
  424   id       jobPercent;
  425   
  426   pList = JobAttrsEditor_percentages;
  427   
  428   jobPercent = [[self job] valueForKey:@"percentComplete"];
  429   if ([jobPercent isNotNull] && ![pList containsObject:jobPercent]) {
  430     NSMutableArray *md;
  431     
  432     md = [pList mutableCopy];
  433     [md addObject:[NSNumber numberWithInt:[jobPercent intValue]]];
  434     pList = [md sortedArrayUsingSelector:@selector(compare:)];
  435     [md release];
  436   }
  437   return pList;
  438 }
  439 
  440 - (void)clearEditor {
  441   [self->item      release]; self->item      = nil;
  442   [super clearEditor];        
  443 }
  444 
  445 /* commands */
  446 
  447 - (id)_fetchProject:(id)_projectId {
  448   NSArray *p = nil;
  449 
  450   p = [self runCommand:@"project::get",
  451             @"projectId", _projectId, nil];
  452 
  453   return ([p count] > 0) ? [p lastObject] : nil;
  454 }
  455 
  456 - (NSArray *)_resolveAccountsOfTeamEO:(id)_team {
  457   return [self runCommand:@"team::resolveaccounts", @"staff", _team, nil];
  458 }
  459 
  460 - (NSArray *)_fetchMemberEOsOfTeamEO:(id)_team {
  461   return [self runCommand:@"team::members", @"object", _team, nil];
  462 }
  463 
  464 - (NSArray *)_searchAccountEOsMatchingString:(NSString *)_s {
  465   return [self runCommand:@"account::extended-search",
  466              @"operator",    @"OR",
  467                  @"name",        _s,
  468                  @"firstname",   _s,
  469                  @"description", _s,
  470                  @"login",       _s,
  471            nil];
  472 }
  473 
  474 - (id)_createJobWithArguments:(NSDictionary *)_args {
  475   return [self runCommand:@"job::new" arguments:_args];
  476 }
  477 
  478 - (void)_addLogForGlobalID:(EOKeyGlobalID *)_gid 
  479   action:(NSString *)_action comment:(NSString *)_comment
  480 {
  481   [self runCommand:@"object::add-log",
  482       @"objectId", [_gid keyValues][0], 
  483       @"logText", _comment,
  484       @"action",  _action, nil];
  485 }
  486 - (void)_commentJobEO:(id)_eo comment:(NSString *)_comment {
  487   [self runCommand:@"job::jobaction", 
  488       @"object",  _eo,
  489       @"action",  @"comment",
  490       @"comment", _comment,
  491     nil];
  492 }
  493 
  494 /* activation */
  495 
  496 - (BOOL)activateForImport {
  497   NSMutableDictionary *snap;
  498   
  499   [self setObject:[[self session] getTransferObject]];
  500 
  501   snap = [[self object] mutableCopy];
  502   [self setSnapshot:snap];
  503   [snap release];
  504   
  505   self->project =
  506     [[self _fetchProject:[[self object] valueForKey:@"projectId"]] retain];
  507   return YES;
  508 }
  509 
  510 - (BOOL)prepareForActivationCommand:(NSString *)_command
  511   type:(NGMimeType *)_type
  512   configuration:(NSDictionary *)_cmdCfg
  513 {
  514   // TODO: clean up!
  515   WOSession *sn;
  516 
  517   sn = [self session];
  518   
  519   self->isProjectLinkMode    = [[_type subType]
  520                                        isEqualToString:@"project-job"];
  521   self->isEnterpriseLinkMode = [[_type subType]
  522                                        isEqualToString:@"enterprise-job"];  
  523   self->isImportMode         = [[_type type] isEqualToString:@"dict"];
  524 
  525   if (self->teams == nil) {
  526     NSArray *a;
  527     
  528     // TODO: was sorted before
  529     a = [self runCommand:@"team::get",
  530             @"returnType", intObj(LSDBReturnType_ManyObjects), 
  531           nil];
  532     self->teams = [a retain];
  533   }
  534   
  535   if (self->isImportMode)
  536     return [self activateForImport];
  537   
  538   if ((self->isProjectLinkMode) ||
  539       (self->isEnterpriseLinkMode))
  540     self->project = [[self session] getTransferObject];
  541   
  542   ASSIGN(self->notifyList,   defNotifyList);
  543   ASSIGN(self->notifyLabels, defNotifyLabels);
  544   
  545   return [super prepareForActivationCommand:_command type:_type
  546                 configuration:_cmdCfg];
  547 }
  548 
  549 - (BOOL)prepareForNewCommand:(NSString *)_command
  550   type:(NGMimeType *)_type
  551   configuration:(NSDictionary *)_cmdCfg
  552 {
  553   WOSession *sn;
  554   id        job;
  555   id        pJob = nil;
  556   NSCalendarDate *now;
  557   
  558   now = [NSCalendarDate date];
  559   sn  = [self session];
  560   job = [self snapshot];
  561   
  562   [job takeValue:now forKey:@"startDate"];
  563   {
  564     NSCalendarDate *date;
  565     id  tmp;
  566     int days;
  567     
  568     tmp = [[sn userDefaults] objectForKey:@"job_defaultDuration"];
  569     if (tmp == nil) days = 7;
  570     else {
  571       days = [tmp intValue];
  572       if (days < 0) days = 7;
  573     }
  574     date = [now dateByAddingYears:0 months:0 days:days
  575                  hours:0 minutes:0 seconds:0];
  576     [job takeValue:date forKey:@"endDate"];
  577   }
  578   
  579   [job takeValue:[NSNumber numberWithInt:3] forKey:@"priority"];
  580   
  581   if (!self->isProjectLinkMode && !self->isEnterpriseLinkMode) {
  582     pJob = [sn getTransferObject];
  583     
  584     if (pJob != nil) {
  585       id p;
  586       
  587       p = [self _fetchProject:[pJob valueForKey:@"projectId"]];
  588       self->project = [p retain];;
  589     }
  590   }
  591 
  592   ASSIGN(self->executantSelection, [[self session] activeAccount]);
  593   
  594   return YES;
  595 }
  596 
  597 - (BOOL)prepareForEditCommand:(NSString *)_command
  598   type:(NGMimeType *)_type
  599   configuration:(NSDictionary *)_cmdCfg
  600 {
  601   id job;
  602   id obj;
  603   id p;
  604   
  605   job = [self snapshot];
  606   obj = [self object];
  607   p   = [self _fetchProject:[obj valueForKey:@"projectId"]];
  608   
  609   RELEASE(self->executantSelection);
  610   self->executantSelection = [obj valueForKey:@"executant"];
  611   RETAIN(self->executantSelection);
  612   [job takeValue:self->executantSelection forKey:@"executant"];
  613 
  614   ASSIGN(self->project, p);
  615   self->snapshotCopy = [[self snapshot] copyWithZone:[self zone]];
  616 
  617   return YES;
  618 }
  619 
  620 /* accessors */
  621 
  622 - (NSArray *)priorities {
  623   return JobAttrsEditor_priorities;
  624 }
  625 
  626 - (BOOL)checkPreferredExecutantsConstraintsForSave {
  627         NSArray      *acc;
  628         id            obj;
  629         NSEnumerator *enumerator;
  630     
  631   if (![self preferredExecutantsEnabled])
  632     return YES;
  633   if ([self->selPrefAccounts count] == 0)
  634     return YES;
  635 
  636   if (![[self->executantSelection entityName] isEqual:@"Team"]) {
  637     NSString *s;
  638     
  639     s = [[self labels] valueForKey:
  640           @"Preferred executants allowed only if executant is a team"];
  641     [self setErrorString:s];
  642     return NO;
  643   }
  644   
  645   acc = [self _resolveAccountsOfTeamEO:self->executantSelection];
  646   enumerator = [self->selPrefAccounts objectEnumerator];
  647   
  648   while ((obj = [enumerator nextObject])) {
  649     NSString *s;
  650     
  651     if ([acc containsObject:obj])
  652       continue;
  653     
  654     s = @"Preferred executant %@ is not in selected team %@.";
  655     s = [NSString stringWithFormat:[[self labels] valueForKey:s],
  656             [self labelForAccount:obj],
  657             [self labelForAccount:self->executantSelection]];
  658     [self setErrorString:s];
  659   }
  660   return YES;
  661 }
  662 - (BOOL)checkConstraintsForSave {
  663   // TODO: fix this huge method
  664   NSMutableString *error = nil;
  665   id              labels = nil;
  666   id              job    = nil;
  667   NSTimeZone      *tz    = nil;
  668   NSString        *n     = nil;
  669   NSCalendarDate  *sD    = nil;
  670   NSCalendarDate  *eD    = nil;
  671 
  672   tz  = [[self session] timeZone];
  673   job = [self snapshot];
  674   n   = [job valueForKey:@"name"];
  675   sD  = [job valueForKey:@"startDate"];
  676   eD  = [job valueForKey:@"endDate"];
  677 
  678   labels = [self labels];
  679   error  = [NSMutableString stringWithCapacity:128];
  680   
  681   if (![n isNotNull]) {
  682     [error appendString:[labels valueForKey:@"error_no_name"]];
  683   }
  684   else if (![sD isNotNull]) {
  685     [error appendString:[labels valueForKey:@"error_no_start_date"]];
  686   }
  687   else if (![eD isNotNull]) {
  688     [error appendString:[labels valueForKey:@"error_no_end_date"]];
  689   }
  690   else {
  691     [sD setTimeZone:tz];
  692     [eD setTimeZone:tz];
  693 
  694     if ([[sD beginOfDay] compare:[eD beginOfDay]] == NSOrderedDescending) {
  695       [error appendString:[labels valueForKey:@"error_end_before_start"]];
  696     }
  697     else {
  698       id p;
  699       
  700       if ([self isInNewMode]) {
  701         NSCalendarDate *nowDate = [NSCalendarDate date];
  702 
  703         [nowDate setTimeZone:tz];
  704 
  705         if ([[nowDate beginOfDay] compare:[sD beginOfDay]]
  706             == NSOrderedDescending) {
  707           [error appendString:[labels valueForKey:@"error_start_before_now"]];
  708         }
  709         p = self->project;
  710       }
  711       else {
  712         p = self->project;
  713       }
  714     }
  715   }
  716   if ([error length] > 0) {
  717     [self setErrorString:error];
  718     return NO;
  719   }
  720 
  721   if (![self checkPreferredExecutantsConstraintsForSave])
  722     return NO;
  723   
  724   [self setErrorString:nil];
  725   return [super checkConstraintsForSave];
  726 }
  727   
  728 - (NSString *)changeStringForLabel:(NSString *)_labelName
  729   withValue:(id)_value
  730 {
  731   NSString *label;
  732 
  733   label = [[self labels] valueForKey:_labelName];
  734   return [NSString stringWithFormat:@"%@: %@\n", label, _value];
  735 }
  736 
  737 /* notifications */
  738 
  739 - (NSString *)insertNotificationName {
  740   NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
  741   
  742   [nc postNotificationName:SkyNewJobNotification object:nil];
  743 
  744   return LSWJobHasChanged;
  745 }
  746 
  747 - (NSString *)updateNotificationName {
  748   NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
  749   
  750   [nc postNotificationName:SkyUpdatedJobNotification object:nil];
  751 
  752   return LSWJobHasChanged;
  753 }
  754 
  755 - (NSString *)deleteNotificationName {
  756   NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
  757   
  758   [nc postNotificationName:SkyDeletedJobNotification object:nil];
  759 
  760   return LSWJobHasChanged;
  761 }
  762 
  763 /* actions */
  764 
  765 - (id)search {
  766   NSArray *result = nil;
  767 
  768   [self setErrorString:nil];
  769   [self->resultList removeAllObjects];
  770   
  771   if (![self->team isNotNull]) { /* Search accounts with searchAccount field */
  772     result = [self _searchAccountEOsMatchingString:self->searchAccount];
  773     if (result != nil)
  774       [self->resultList addObjectsFromArray:result];
  775   }
  776   else { /* take accounts from team */    
  777     NSArray *acc;
  778 
  779     if ([[[self->executantSelection globalID] entityName] isEqual:@"Team"]) {
  780       ASSIGN(self->executantSelection, nil);
  781       [self->resultList removeObject:self->executantSelection];
  782     }
  783     
  784     acc = [self _resolveAccountsOfTeamEO:self->team];
  785     if (acc != nil)
  786       [self->resultList addObjectsFromArray:acc];
  787 
  788     if (![self->resultList containsObject:self->team])
  789       [self->resultList addObject:self->team];
  790   }
  791   if (self->executantSelection &&
  792       ![self->resultList containsObject:self->executantSelection])
  793     [self->resultList addObject:self->executantSelection];
  794 
  795   [self->resultList sortUsingFunction:compareAccounts context:NULL];
  796     
  797   if ([self->resultList count] > 0)
  798     self->executantSelection = [[self->resultList objectAtIndex:0] retain];
  799   
  800   return nil;
  801 }
  802 
  803 - (NSString *)emailSubjectForJobEO:(id)_job {
  804   NSString *subject;
  805   
  806   subject = [NSString stringWithFormat:@"%@: '%@' %@ %@",
  807                       [[self labels] valueForKey:@"job"],
  808                       [_job valueForKey:@"name"],
  809                       [[self labels] valueForKey:@"createLabel"],
  810                       [[[self session] activeAccount] valueForKey:@"login"]];
  811   return subject;
  812 }
  813 - (id)jobObjectForSend {
  814   id obj;
  815   
  816   obj = [self object];
  817   if (![obj isKindOfClass:[NSArray class]])
  818     return obj;
  819   
  820   return ([(NSArray *)obj count] > 0) ? [obj objectAtIndex:0] : nil;
  821 }
  822 
  823 - (id)sendMessage {
  824   id<LSWMailEditorComponent,OGoContentPage> editor;
  825   id obj;
  826   
  827   if ((obj = [self jobObjectForSend]) == nil) {
  828     [self setErrorString:@"No job found for email send!"];
  829     return nil;
  830   }
  831   
  832   /* setup email editor */
  833   
  834   editor = (id)[self pageWithName:@"LSWImapMailEditor"];
  835   [self enterPage:editor];
  836   [editor setSubject:[self emailSubjectForJobEO:obj]];
  837   [editor setContentWithoutSign:@""];
  838   
  839   /* add recipients */
  840   
  841   if ([self isTeamSelected]) {
  842     NSArray *m = [self->executantSelection valueForKey:@"members"];
  843     int     i, cnt = 0;
  844 
  845     if (m == nil)
  846       m = [self _fetchMemberEOsOfTeamEO:self->executantSelection];
  847     
  848     for (i = 0, cnt = [m count]; i < cnt; i++)
  849       [editor addReceiver:[m objectAtIndex:i] type:@"to"];
  850   }
  851   else 
  852     [editor addReceiver:self->executantSelection type:@"to"];
  853 
  854   /* add attachments */
  855 
  856   [editor addAttachment:obj type:[NGMimeType mimeType:@"eo" subType:@"job"]];
  857 
  858   return [editor send];
  859 }
  860 
  861 - (void)_applyPreferredExecutantsOnJobEO:(id)job {
  862   NSEnumerator         *enumerator;
  863   id                   account, l;
  864   OGoObjectLinkManager *lm;
  865   EOKeyGlobalID        *gid;
  866   NSString             *teamLabel;
  867   
  868   enumerator = [self->selPrefAccounts objectEnumerator];
  869   lm         = [[[self session] commandContext] linkManager];
  870   gid        = (EOKeyGlobalID *)[job globalID];
  871   l          = [self labels];
  872   teamLabel  = [self labelForAccount:self->executantSelection];
  873 
  874   while ((account = [enumerator nextObject]) != nil) {
  875     OGoObjectLink *link;
  876     EOKeyGlobalID *targetGID;
  877     NSString      *label, *comment;
  878     
  879     /* create object link */
  880     
  881     label     = [self labelForAccount:account];
  882     targetGID = (EOKeyGlobalID *)[account globalID];
  883     link      = [[OGoObjectLink alloc] initWithSource:gid target:targetGID
  884                        type:preferredJobExecutantLinkType
  885                        label:label];
  886     [lm createLink:link];
  887     [link release]; link = nil;
  888     
  889     /* add an object log */
  890     
  891     comment = [l valueForKey:@"Add job link to %@ [team:%@]."];
  892     comment = [NSString stringWithFormat:comment, label, teamLabel];
  893     [self _addLogForGlobalID:gid action:@"05_changed" comment:comment];
  894   }
  895 }
  896 
  897 - (void)_applyValuesOnSnapshot:(id)job {
  898   [job takeValue:[NSNumber numberWithBool:[self isTeamSelected]]
  899        forKey:@"isTeamJob"];
  900   [job takeValue:[self->executantSelection valueForKey:@"companyId"]
  901        forKey:@"executantId"];
  902   if ([self->project isNotNull]) {
  903     [job takeValue:[self->project valueForKey:@"projectId"]
  904          forKey:@"projectId"];
  905   }
  906   else {
  907     [job takeValue:[NSNull null] forKey:@"projectId"];
  908   }
  909 }
  910 
  911 - (id)insertObject {
  912   id job;
  913 
  914   job = [self snapshot];
  915   [self _applyValuesOnSnapshot:job];
  916   
  917   job = [self _createJobWithArguments:job];
  918   
  919   if ([self preferredExecutantsEnabled])
  920     [self _applyPreferredExecutantsOnJobEO:job];
  921   
  922   return job;
  923 }
  924 
  925 - (id)updateObject {
  926   id job;
  927   
  928   job = [self snapshot];
  929   [job removeObjectForKey:@"executant"];
  930   [job removeObjectForKey:@"object"];
  931   
  932   [self _applyValuesOnSnapshot:job];
  933   
  934   return [self runCommand:@"job::set" arguments:job];
  935 }
  936 
  937 - (id)deleteObject {
  938   return [self runCommand:@"job::delete", 
  939              @"object",       [self object],
  940          @"reallyDelete", [NSNumber numberWithBool:NO],
  941            nil];
  942 }
  943 
  944 - (id)_saveExistingJob {
  945   BOOL access;
  946   id   acManager;
  947   
  948   if (![self->executantSelection isNotNull]) {
  949     [self setErrorString:
  950             [[self labels] valueForKey:@"NoTeamAndExecutantsWasSelected"]];
  951     return nil;
  952   }
  953 
  954   // TODO: document, what is referredPerson?
  955   
  956   if (self->referredPerson == nil)
  957     return [self _save];
  958   
  959   acManager = [[[self session] commandContext] accessManager];
  960   access    = [acManager operation:@"rw"
  961              allowedOnObjectID:
  962                              [self->referredPerson globalID]
  963              forAccessGlobalID:
  964                              [self->executantSelection globalID]];
  965   if (!access) {
  966     [self setWarningOkAction:@"editAccessAndSave"];
  967     [self setWarningPhrase:[[self labels] valueForKey:@"AccessWarning"]];
  968     [self setIsInWarningMode:YES];
  969     return nil;
  970   }
  971   return [self _save];
  972 }
  973 
  974 - (id)save {
  975   return [self isInNewMode] ? [self _save] : [self _saveExistingJob];
  976 }
  977 
  978 - (BOOL)hasJobAttributes {
  979   if (!JobAttributesCollapsible)
  980     return NO;
  981 
  982   // TODO: was: return self->referredPerson != nil > 0 ? YES : NO;
  983   //       what do we want here? :-)
  984   return self->referredPerson != nil ? YES : NO;
  985 }
  986 
  987 - (void)setReferredPerson:(NSString *)_per {
  988   ASSIGN(self->referredPerson, _per);
  989 }
  990 - (id)referredPerson {
  991   return self->referredPerson;
  992 }
  993 
  994 - (NSString *)referredPersonLabel {
  995   NSMutableString *str;
  996   id              eo;
  997   NSString        *fn;
  998 
  999   if ((eo = [self referredPerson]) == nil)
 1000     return nil;
 1001   
 1002   str = [NSMutableString stringWithCapacity:30];
 1003   
 1004   [str appendString:[[eo valueForKey:@"name"] stringValue]];
 1005   
 1006   fn = [eo valueForKey:@"firstname"];
 1007   
 1008   if ([fn isNotNull] && [[fn stringValue] length] > 0) {
 1009     [str appendString:@", "];
 1010     [str appendString:[fn stringValue]];
 1011   }
 1012   return str;
 1013 }
 1014 
 1015 - (NSString *)referredPersonAction {
 1016   NSDictionary *dict;
 1017 
 1018   if (self->referredPerson == nil)
 1019     return nil;
 1020   
 1021   dict = [NSDictionary dictionaryWithObjectsAndKeys:
 1022                        [self->referredPerson valueForKey:@"companyId"],
 1023                        @"companyId",
 1024                        [self referredPersonLabel], @"label", nil];
 1025   
 1026   return [[self context]
 1027                 directActionURLForActionNamed:@"LSWViewAction/viewPerson"
 1028                 queryDictionary:dict];
 1029 }
 1030 
 1031 - (id)editAccessAndSave {
 1032   id       acManager;
 1033 
 1034   [self setIsInWarningMode:NO];
 1035   
 1036   acManager = [[[self session] commandContext] accessManager];
 1037 
 1038   if (![acManager setOperation:@"rw"
 1039                   onObjectID:[self->referredPerson globalID]
 1040                   forAccessGlobalID:[self->executantSelection globalID]]) {
 1041     [self setErrorString:@"CouldntSetAccessRights"];
 1042     return nil;
 1043   }
 1044   
 1045   return [self _save];
 1046 }
 1047 
 1048 - (id)_saveInImportModeWithSnapshot:(id)s {
 1049   /* called by _save */
 1050   id obj    = [self object];      
 1051   id projId = nil;
 1052 
 1053   NSAssert2([obj isKindOfClass:[NSMutableDictionary class]],
 1054         @"Imported job is not a NSMutableDictionary %@ %@",
 1055         [obj class], obj);
 1056 
 1057   [obj addEntriesFromDictionary:s];
 1058   projId = [self->project valueForKey:@"projectId"];
 1059 
 1060   NSAssert(projId != nil, @"no projectId");
 1061   [obj takeValue:projId forKey:@"projectId"];
 1062 
 1063   return [self leavePage];
 1064 }
 1065 - (void)_processInsertProperties {
 1066   id           pm;
 1067   NSDictionary *dic;
 1068   
 1069   if (![self hasJobAttributes])
 1070     return;
 1071   if (self->referredPerson == nil)
 1072     return;
 1073   
 1074   pm = [[[self session] commandContext] propertyManager];
 1075 
 1076   dic = [NSDictionary dictionaryWithObjectsAndKeys:
 1077                           [self referredPersonAction],
 1078                           @"{person}referredPerson", nil];
 1079   [pm takeProperties:dic namespace:nil globalID:[[self object] globalID]];
 1080 }
 1081 
 1082 - (void)_postLinkSaveNotificationsOnJobEO:(id)obj {
 1083   NSEnumerator *enumerator;
 1084   
 1085   if (!((self->isProjectLinkMode) || (self->isEnterpriseLinkMode)))
 1086     return;
 1087   
 1088   if (![obj isKindOfClass:[NSArray class]])
 1089     obj = [NSArray arrayWithObject:obj];
 1090   
 1091   enumerator = [obj objectEnumerator];
 1092   while ((obj = [enumerator nextObject]) != nil) {
 1093     NSDictionary *dict;
 1094     
 1095     dict = [NSDictionary dictionaryWithObjectsAndKeys:
 1096                                [self jobUrl:obj], @"LinkUrl",
 1097                                [obj valueForKey:@"name"],
 1098                                @"LinkTitle",
 1099                                @"job", @"fileType", nil];
 1100     
 1101     if (self->isProjectLinkMode) {
 1102       [self postChange:LSWNewObjectLinkProjectNotificationName onObject:dict];
 1103     }
 1104     else if (self->isEnterpriseLinkMode) {
 1105       [self postChange:LSWNewObjectLinkEnterpriseNotificationName
 1106         onObject:dict];
 1107     }
 1108   }
 1109 }
 1110 
 1111 static BOOL _isIntKeyEq(id a, id b, NSString *key) {
 1112   a = [a valueForKey:key];
 1113   b = [b valueForKey:key];
 1114   if (a == b) return YES;
 1115   return [a intValue] == [b intValue] ? YES : NO;
 1116 }
 1117 
 1118 - (NSString *)_buildCommentWithOldSnapshot:(id)sc andNewSnapshot:(id)s{
 1119   // TODO: clean up this mess!
 1120   NSMutableString *comment;
 1121   NSFormatter     *form    = nil;
 1122   BOOL nameChanged, startDateChanged, endDateChanged, notifyChanged;
 1123   BOOL projectChanged, executantChanged;
 1124   id   l, tmp;
 1125 
 1126   /* detect changes */
 1127 
 1128   nameChanged = ![[sc valueForKey:@"name"] isEqual:[s valueForKey:@"name"]];
 1129   startDateChanged = ![[sc valueForKey:@"startDate"]
 1130                            isEqual:[s valueForKey:@"startDate"]];
 1131   endDateChanged = ![[sc valueForKey:@"endDate"]
 1132                            isEqual:[s valueForKey:@"endDate"]];
 1133 
 1134   notifyChanged  = !_isIntKeyEq(sc, s, @"notify");
 1135   projectChanged = !_isIntKeyEq(sc, s, @"projectId");
 1136   
 1137   executantChanged = ![[sc valueForKey:@"executantId"]
 1138                            isEqual:[s valueForKey:@"executantId"]];
 1139 
 1140 
 1141   form = [[self session] formatDate];
 1142   l    = [self labels];
 1143 
 1144   comment = [NSMutableString stringWithCapacity:256];
 1145   [comment appendString:[l valueForKey:@"oldJob"]];
 1146   [comment appendString:@"\n"];
 1147   
 1148   tmp = [sc valueForKey:@"name"];
 1149   tmp = [self changeStringForLabel:@"jobName" withValue:tmp];
 1150   [comment appendString:tmp];
 1151   
 1152   if (startDateChanged) {
 1153     tmp = [form stringForObjectValue:[sc valueForKey:@"startDate"]];
 1154     tmp = [self changeStringForLabel:@"startDate" withValue:tmp];
 1155     [comment appendString:tmp];
 1156   }
 1157   if (endDateChanged) {
 1158     tmp = [form stringForObjectValue:[sc valueForKey:@"endDate"]];
 1159     tmp = [self changeStringForLabel:@"endDate" withValue:tmp];
 1160     [comment appendString:tmp];
 1161   }
 1162   
 1163   if (notifyChanged) {
 1164     tmp = [sc valueForKey:@"notify"];
 1165     tmp = [self->notifyLabels objectAtIndex:[tmp intValue]];
 1166     tmp = [l valueForKey:tmp];
 1167     tmp = [self changeStringForLabel:@"notifyCreator" withValue:tmp];
 1168     [comment appendString:tmp];
 1169   }
 1170 
 1171   if (projectChanged) {
 1172     tmp = [self _fetchProject:[sc valueForKey:@"projectId"]];
 1173     tmp = [tmp valueForKey:@"name"];
 1174     tmp = [self changeStringForLabel:@"projectLabel" withValue:tmp];
 1175     [comment appendString:tmp];
 1176   }
 1177 
 1178   if (executantChanged) {
 1179     tmp = [self labelForAccount:[sc valueForKey:@"executant"]];
 1180     tmp = [self changeStringForLabel:@"executant" withValue:tmp];
 1181     [comment appendString:tmp];
 1182     
 1183     // TODO: weird side effect, why is that?
 1184     [[self object] takeValue:[EONull null] forKey:@"executant"];
 1185   }
 1186   [comment appendString:@"\n"];
 1187         
 1188   [comment appendString:[l valueForKey:@"newJob"]];
 1189   [comment appendString:@"\n"];
 1190   
 1191   tmp = [s valueForKey:@"name"];
 1192   tmp = [self changeStringForLabel:@"jobName" withValue:tmp];
 1193   [comment appendString:tmp];
 1194   
 1195   if (startDateChanged) {
 1196     tmp = [form stringForObjectValue:[s valueForKey:@"startDate"]];
 1197     tmp = [self changeStringForLabel:@"startDate" withValue:tmp];
 1198     [comment appendString:tmp];
 1199   }
 1200   if (endDateChanged) {
 1201     tmp = [form stringForObjectValue:[s valueForKey:@"endDate"]];
 1202     tmp = [self changeStringForLabel:@"endDate" withValue:tmp];
 1203     [comment appendString:tmp];
 1204   }
 1205 
 1206   if (notifyChanged) {
 1207     tmp = [s valueForKey:@"notify"];
 1208     tmp = [self->notifyLabels objectAtIndex:[tmp intValue]];
 1209     tmp = [l valueForKey:tmp];
 1210     tmp = [self changeStringForLabel:@"notifyCreator" withValue:tmp];
 1211     [comment appendString:tmp];
 1212   }
 1213   
 1214   if (projectChanged) {
 1215     tmp = [self->project valueForKey:@"name"];
 1216     tmp = [self changeStringForLabel:@"projectLabel" withValue:tmp];
 1217     [comment appendString:tmp];
 1218   }
 1219   
 1220   if (executantChanged) {
 1221     tmp = [self labelForAccount:self->executantSelection];
 1222     tmp = [self changeStringForLabel:@"executant" withValue:tmp];
 1223     [comment appendString:tmp];
 1224   }
 1225   return comment;
 1226 }
 1227 
 1228 - (BOOL)shouldSendMessage {
 1229   // kinda hack
 1230   return [[self navigation] activePage] != self ? YES : NO;
 1231 }
 1232 
 1233 - (id)_save {
 1234   if (self->isImportMode)
 1235     return [self _saveInImportModeWithSnapshot:[self snapshot]];
 1236   
 1237   [self setErrorString:nil];
 1238   [self saveAndGoBackWithCount:1];
 1239 
 1240   if ([[self errorString] length] > 0)
 1241     return nil;
 1242   
 1243   if ([self shouldSendMessage]) {
 1244     id notifyValue;
 1245     
 1246     notifyValue = [[self snapshot] objectForKey:@"notify"];
 1247     
 1248     if (self->notifyExecutant ||
 1249     ([notifyValue isNotNull] && [notifyValue intValue] == 1)) {
 1250       
 1251       if (![self isInNewMode]) {
 1252     [self setExecutantSelection:
 1253                 [[self object] valueForKey:@"executant"]];
 1254       }
 1255       [self sendMessage];
 1256     }
 1257 
 1258     if (![self isInNewMode]) {
 1259       // TODO: clean up this mess!
 1260       NSString *comment;
 1261     
 1262       comment = [self _buildCommentWithOldSnapshot:self->snapshotCopy
 1263               andNewSnapshot:[self snapshot]];
 1264       [self _commentJobEO:[self object] comment:comment];
 1265     
 1266       if (![self commit]) {
 1267     [self setErrorString:@"Could not commit jobaction command "
 1268           @"(rolled back) !"];
 1269     [self rollback];
 1270     return nil;
 1271       }
 1272     }
 1273       
 1274     [self _postLinkSaveNotificationsOnJobEO:[self object]];
 1275   }
 1276   
 1277   if ([self isInNewMode] && [[self object] globalID] != nil)
 1278     [self _processInsertProperties];
 1279   
 1280   return nil;
 1281 }
 1282 
 1283 - (BOOL)preferredExecutantsEnabled {
 1284   return PreferredAccountsEnabled;
 1285 }
 1286 
 1287 - (void)setSelPrefAccounts:(id)_o {
 1288   ASSIGN(self->selPrefAccounts, _o);
 1289 }
 1290 - (NSArray *)selPrefAccounts {
 1291   return self->selPrefAccounts;
 1292 }
 1293 
 1294 - (NSArray *)prefAccountList {
 1295   if (self->team == nil)
 1296     return nil;
 1297   
 1298   return [self _resolveAccountsOfTeamEO:self->team];
 1299 }
 1300 
 1301 - (NSString *)searchPreferredAccountsLabel {
 1302   NSString *fmt, *s;
 1303   id l;
 1304   
 1305   l   = [self labels];
 1306   fmt = [l valueForKey:@"searchPreferredAccounts"];
 1307   s   = (self->team != nil)
 1308     ? [self labelForAccount:self->team]
 1309     : (NSString *)[l valueForKey:@"noTeamSelected"];
 1310   return [NSString stringWithFormat:fmt, s];
 1311 }
 1312 
 1313 /* KVC */
 1314 
 1315 - (void)takeValue:(id)_v forKey:(id)_key {
 1316   // TODO: why is that?! should be mapped automatically?!
 1317   if ([_key isEqualToString:@"referredPerson"])
 1318     [self setReferredPerson:_v];
 1319   else
 1320     [super takeValue:_v forKey:_key];
 1321 }
 1322 
 1323 @end /* LSWJobEditor */