]> err.no Git - scalable-opengroupware.org/blob - UI/MailerUI/UIxMailListView.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1086 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / MailerUI / UIxMailListView.m
1 /*
2   Copyright (C) 2004-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 /*
23   UIxMailListView
24   
25   This component represent a list of mails and is attached to an SOGoMailFolder
26   object.
27 */
28
29 #define messagesPerPage 50
30
31 #include "common.h"
32 #include <SoObjects/Mailer/SOGoMailFolder.h>
33 #include <SoObjects/Mailer/SOGoMailObject.h>
34 #include <NGObjWeb/SoObject+SoDAV.h>
35
36 #import "UIxMailListView.h"
37
38 static int attachmentFlagSize = 8096;
39
40 @implementation UIxMailListView
41
42 - (void) dealloc 
43 {
44   [self->qualifier  release];
45   [self->sortedUIDs release];
46   [self->messages   release];
47   [self->message    release];
48   [super dealloc];
49 }
50
51 /* notifications */
52
53 - (void) sleep 
54 {
55   [self->qualifier  release]; self->qualifier  = nil;
56   [self->sortedUIDs release]; self->sortedUIDs = nil;
57   [self->messages   release]; self->messages   = nil;
58   [self->message    release]; self->message    = nil;
59   [super sleep];
60 }
61
62 /* accessors */
63
64 - (void)setMessage:(id)_msg
65 {
66   ASSIGN(self->message, _msg);
67 }
68
69 - (id) message 
70 {
71   return self->message;
72 }
73
74 - (void) setQualifier: (EOQualifier *) _msg 
75 {
76   ASSIGN(self->qualifier, _msg);
77 }
78
79 - (EOQualifier *) qualifier 
80 {
81   return self->qualifier;
82 }
83
84 - (BOOL) showToAddress 
85 {
86   NSString *ftype;
87   
88   ftype = [[self clientObject] valueForKey:@"outlookFolderClass"];
89   return [ftype isEqual:@"IPF.Sent"];
90 }
91
92 /* title */
93
94 - (NSString *) objectTitle 
95 {
96   return [[self clientObject] nameInContainer];
97 }
98
99 - (NSString *) panelTitle 
100 {
101   NSString *s;
102   
103   s = [self labelForKey:@"View Mail Folder"];
104   s = [s stringByAppendingString:@": "];
105   s = [s stringByAppendingString:[self objectTitle]];
106   return s;
107 }
108
109 /* derived accessors */
110
111 - (BOOL) isMessageDeleted 
112 {
113   NSArray *flags;
114   
115   flags = [[self message] valueForKey:@"flags"];
116   return [flags containsObject:@"deleted"];
117 }
118
119 - (BOOL) isMessageRead 
120 {
121   NSArray *flags;
122   
123   flags = [[self message] valueForKey:@"flags"];
124   return [flags containsObject:@"seen"];
125 }
126 - (NSString *) messageUidString 
127 {
128   return [[[self message] valueForKey:@"uid"] stringValue];
129 }
130
131 - (NSString *) messageRowStyleClass 
132 {
133   return [self isMessageDeleted]
134     ? @"mailer_listcell_deleted"
135     : @"mailer_listcell_regular";
136 }
137
138 - (NSString *) messageSubjectCellStyleClass 
139 {
140   return ([self isMessageRead]
141           ? @"mailer_readmailsubject"
142           : @"mailer_unreadmailsubject");
143 }
144
145 - (BOOL) hasMessageAttachment 
146 {
147   /* we detect attachments by size ... */
148   unsigned size;
149   
150   size = [[[self message] valueForKey:@"size"] intValue];
151   return size > attachmentFlagSize;
152 }
153
154 /* fetching messages */
155
156 - (NSArray *) fetchKeys 
157 {
158   /* Note: see SOGoMailManager.m for allowed IMAP4 keys */
159   static NSArray *keys = nil;
160   if (keys == nil) {
161     keys = [[NSArray alloc] initWithObjects:
162                               @"FLAGS", @"ENVELOPE", @"RFC822.SIZE", nil];
163   }
164   return keys;
165 }
166
167 - (NSString *) defaultSortKey 
168 {
169   return @"DATE";
170 }
171
172 - (NSString *) imap4SortKey 
173 {
174   NSString *sort;
175   
176   sort = [[[self context] request] formValueForKey:@"sort"];
177
178   if ([sort length] == 0)
179     sort = [self defaultSortKey];
180
181   return [sort uppercaseString];
182 }
183
184 - (BOOL) isSortedDescending
185 {
186   NSString *desc;
187
188   desc = [[[self context] request] formValueForKey:@"desc"];
189
190   return ((desc)
191           ? [desc boolValue]
192           : YES);
193 }
194
195 - (NSString *) imap4SortOrdering 
196 {
197   NSString *sort;
198
199   sort = [self imap4SortKey];
200
201   if ([self isSortedDescending])
202     sort = [@"REVERSE " stringByAppendingString: sort];
203
204   return sort;
205 }
206
207 - (NSRange) fetchRange 
208 {
209   if (self->firstMessageNumber == 0)
210     return NSMakeRange(0, messagesPerPage);
211   return NSMakeRange(self->firstMessageNumber - 1, messagesPerPage);
212 }
213
214 - (NSArray *) sortedUIDs 
215 {
216   if (!sortedUIDs)
217     {
218       sortedUIDs 
219         = [[self clientObject] fetchUIDsMatchingQualifier: [self qualifier]
220                                sortOrdering: [self imap4SortOrdering]];
221       [sortedUIDs retain];
222     }
223
224   return self->sortedUIDs;
225 }
226
227 - (unsigned int) totalMessageCount 
228 {
229   return [self->sortedUIDs count];
230 }
231
232 - (BOOL) showsAllMessages 
233 {
234   return ([[self sortedUIDs] count] <= [self fetchRange].length) ? YES : NO;
235 }
236
237 - (NSRange) fetchBlock 
238 {
239   NSRange  r;
240   unsigned len;
241   NSArray  *uids;
242   
243   r    = [self fetchRange];
244   uids = [self sortedUIDs];
245   
246   /* only need to restrict if we have a lot */
247   if ((len = [uids count]) <= r.length) {
248     r.location = 0;
249     r.length   = len;
250     return r;
251   }
252   
253   if (len < r.location) {
254     // TODO: CHECK CONDITION (< vs <=)
255     /* out of range, recover at first block */
256     r.location = 0;
257     return r;
258   }
259   
260   if (r.location + r.length > len)
261     r.length = len - r.location;
262   return r;
263 }
264
265 - (unsigned int) firstMessageNumber 
266 {
267   return [self fetchBlock].location + 1;
268 }
269
270 - (unsigned int) lastMessageNumber 
271 {
272   NSRange r;
273   
274   r = [self fetchBlock];
275   return r.location + r.length;
276 }
277
278 - (BOOL) hasPrevious 
279 {
280   return [self fetchBlock].location == 0 ? NO : YES;
281 }
282
283 - (BOOL) hasNext 
284 {
285   NSRange r = [self fetchBlock];
286   return r.location + r.length >= [[self sortedUIDs] count] ? NO : YES;
287 }
288
289 - (unsigned int) nextFirstMessageNumber 
290 {
291   return [self firstMessageNumber] + [self fetchRange].length;
292 }
293
294 - (unsigned int) prevFirstMessageNumber 
295 {
296   NSRange  r;
297   unsigned idx;
298   
299   idx = [self firstMessageNumber];
300   r   = [self fetchRange];
301   if (idx > r.length)
302     return (idx - r.length);
303   return 1;
304 }
305
306 - (NSArray *) messages 
307 {
308   NSArray  *uids;
309   NSArray  *msgs;
310   NSRange  r;
311   unsigned len;
312   
313   if (self->messages != nil)
314     return self->messages;
315   
316   r    = [self fetchBlock];
317   uids = [self sortedUIDs];
318   if ((len = [uids count]) > r.length)
319     /* only need to restrict if we have a lot */
320     uids = [uids subarrayWithRange:r];
321   
322   msgs = [[self clientObject] fetchUIDs:uids parts:[self fetchKeys]];
323   self->messages = [[msgs valueForKey:@"fetch"] retain];
324   return self->messages;
325 }
326
327 /* URL processing */
328
329 - (NSString *) messageViewTarget
330 {
331   return [NSString stringWithFormat: @"SOGo_msg_%@",
332                    [self messageUidString]];
333 }
334
335 - (NSString *) messageViewURL 
336 {
337   // TODO: noframe only when view-target is empty
338   // TODO: markread only if the message is unread
339   NSString *s;
340   
341   s = [[self messageUidString] stringByAppendingString:@"/view?noframe=1"];
342   if (![self isMessageRead]) s = [s stringByAppendingString:@"&markread=1"];
343   return s;
344 }
345 - (NSString *) markReadURL 
346 {
347   return [@"markMessageRead?uid=" stringByAppendingString:
348              [self messageUidString]];
349 }
350 - (NSString *) markUnreadURL 
351 {
352   return [@"markMessageUnread?uid=" stringByAppendingString:
353              [self messageUidString]];
354 }
355
356 /* JavaScript */
357
358 - (NSString *)msgRowID
359 {
360   return [@"row_" stringByAppendingString:[self messageUidString]];
361 }
362
363 - (NSString *)msgDivID
364 {
365   return [@"div_" stringByAppendingString:[self messageUidString]];
366 }
367
368 - (NSString *)msgIconReadImgID
369 {
370   return [@"readdiv_" stringByAppendingString:[self messageUidString]];
371 }
372
373 - (NSString *)msgIconUnreadImgID
374 {
375   return [@"unreaddiv_" stringByAppendingString:[self messageUidString]];
376 }
377
378 /* error redirects */
379
380 - (id) redirectToViewWithError: (id) _error 
381 {
382   // TODO: DUP in UIxMailAccountView
383   // TODO: improve, localize
384   // TODO: there is a bug in the treeview which preserves the current URL for
385   //       the active object (displaying the error again)
386   id url;
387   
388   if (![_error isNotNull])
389     return [self redirectToLocation:@"view"];
390   
391   if ([_error isKindOfClass:[NSException class]])
392     _error = [_error reason];
393   else if ([_error isKindOfClass:[NSString class]])
394     _error = [_error stringValue];
395   
396   url = [_error stringByEscapingURL];
397   url = [@"view?error=" stringByAppendingString:url];
398   return [self redirectToLocation:url];
399 }
400
401 /* active message */
402
403 - (SOGoMailObject *) lookupActiveMessage 
404 {
405   NSString *uid;
406   
407   if ((uid = [[[self context] request] formValueForKey:@"uid"]) == nil)
408     return nil;
409
410   return [[self clientObject] lookupName:uid inContext:[self context]
411                               acquire:NO];
412 }
413
414 /* actions */
415
416 - (BOOL) isJavaScriptRequest 
417 {
418   return [[[[self context] request] formValueForKey:@"jsonly"] boolValue];
419 }
420
421 - (id) javaScriptOK 
422 {
423   WOResponse *r;
424
425   r = [[self context] response];
426   [r setStatus:200 /* OK */];
427   return r;
428 }
429
430 - (int) firstMessageOfPageFor: (int) messageNbr
431 {
432   NSArray *messageNbrs;
433   int nbrInArray;
434   int firstMessage;
435
436   messageNbrs = [self sortedUIDs];
437   nbrInArray
438     = [messageNbrs indexOfObject: [NSNumber numberWithInt: messageNbr]];
439   if (nbrInArray > -1)
440     firstMessage = ((int) (nbrInArray / messagesPerPage)
441                     * messagesPerPage) + 1;
442   else
443     firstMessage = 1;
444
445   return firstMessage;
446 }
447
448 - (id) defaultAction 
449 {
450   WORequest *request;
451   NSString *specificMessage;
452
453   request = [[self context] request];
454
455   [[self clientObject] flushMailCaches];
456
457   specificMessage = [request formValueForKey: @"pageforuid"];
458   self->firstMessageNumber
459     = ((specificMessage)
460        ? [self firstMessageOfPageFor: [specificMessage intValue]]
461        : [[request formValueForKey:@"idx"] intValue]);
462
463   return self;
464 }
465
466 - (id) viewAction 
467 {
468   return [self defaultAction];
469 }
470
471 - (id) markMessageUnreadAction 
472 {
473   NSException *error;
474   
475   if ((error = [[self lookupActiveMessage] removeFlags:@"seen"]) != nil)
476     // TODO: improve error handling
477     return error;
478
479   if ([self isJavaScriptRequest])
480     return [self javaScriptOK];
481   
482   return [self redirectToLocation:@"view"];
483 }
484
485 - (id) markMessageReadAction 
486 {
487   NSException *error;
488   
489   if ((error = [[self lookupActiveMessage] addFlags:@"seen"]) != nil)
490     // TODO: improve error handling
491     return error;
492   
493   if ([self isJavaScriptRequest])
494     return [self javaScriptOK];
495   
496   return [self redirectToLocation:@"view"];
497 }
498
499 - (id) getMailAction 
500 {
501   // TODO: we might want to flush the caches?
502   id client;
503
504   if ((client = [self clientObject]) == nil) {
505     return [NSException exceptionWithHTTPStatus:404 /* Not Found */
506                         reason:@"did not find mail folder"];
507   }
508
509   if (![client respondsToSelector:@selector(flushMailCaches) ]) 
510     {
511       return [NSException exceptionWithHTTPStatus: 500 /* Server Error */
512                           reason:
513                             @"invalid client object (does not support flush)"];
514     }
515
516   [client flushMailCaches];
517
518   return [self redirectToLocation:@"view"];
519 }
520
521 - (id) expungeAction 
522 {
523   // TODO: we might want to flush the caches?
524   NSException *error;
525   id client;
526   
527   if ((client = [self clientObject]) == nil) {
528     return [NSException exceptionWithHTTPStatus:404 /* Not Found */
529                         reason:@"did not find mail folder"];
530   }
531   
532   if ((error = [[self clientObject] expunge]) != nil)
533     return error;
534   
535   if ([client respondsToSelector:@selector(flushMailCaches)])
536     [client flushMailCaches];
537   return [self redirectToLocation:@"view"];
538 }
539
540 @end
541
542 /* UIxMailListView */