2 Copyright (C) 2004-2005 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
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
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.
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
22 #include <SOGoUI/UIxComponent.h>
27 This component represent a list of mails and is attached to an SOGoMailFolder
33 @interface UIxMailListView : UIxComponent
35 NSArray *sortedUIDs; /* we always need to retrieve all anyway! */
37 unsigned firstMessageNumber;
39 EOQualifier *qualifier;
42 - (NSString *)defaultSortKey;
43 - (NSString *)imap4SortKey;
44 - (NSString *)imap4SortOrdering;
46 - (BOOL)isSortedDescending;
51 #include <SoObjects/Mailer/SOGoMailFolder.h>
52 #include <SoObjects/Mailer/SOGoMailObject.h>
54 @implementation UIxMailListView
56 static int attachmentFlagSize = 8096;
59 [self->qualifier release];
60 [self->sortedUIDs release];
61 [self->messages release];
62 [self->message release];
69 [self->qualifier release]; self->qualifier = nil;
70 [self->sortedUIDs release]; self->sortedUIDs = nil;
71 [self->messages release]; self->messages = nil;
72 [self->message release]; self->message = nil;
78 - (void)setMessage:(id)_msg {
79 ASSIGN(self->message, _msg);
85 - (void)setQualifier:(EOQualifier *)_msg {
86 ASSIGN(self->qualifier, _msg);
88 - (EOQualifier *)qualifier {
89 return self->qualifier;
92 - (BOOL)showToAddress {
95 ftype = [[self clientObject] valueForKey:@"outlookFolderClass"];
96 return [ftype isEqual:@"IPF.Sent"];
101 - (NSString *)objectTitle {
102 return [[self clientObject] nameInContainer];
104 - (NSString *)panelTitle {
107 s = [self labelForKey:@"View Mail Folder"];
108 s = [s stringByAppendingString:@": "];
109 s = [s stringByAppendingString:[self objectTitle]];
113 /* derived accessors */
115 - (BOOL)isMessageDeleted {
118 flags = [[self message] valueForKey:@"flags"];
119 return [flags containsObject:@"deleted"];
122 - (BOOL)isMessageRead {
125 flags = [[self message] valueForKey:@"flags"];
126 return [flags containsObject:@"seen"];
128 - (NSString *)messageUidString {
129 return [[[self message] valueForKey:@"uid"] stringValue];
132 - (NSString *)messageSubjectStyleClass {
133 return [self isMessageRead]
134 ? @"mailer_readmailsubject"
135 : @"mailer_unreadmailsubject";
137 - (NSString *)messageCellStyleClass {
138 return [self isMessageDeleted]
139 ? @"mailer_listcell_deleted"
140 : @"mailer_listcell_regular";
143 - (BOOL)hasMessageAttachment {
144 /* we detect attachments by size ... */
147 size = [[[self message] valueForKey:@"size"] intValue];
148 return size > attachmentFlagSize;
151 /* fetching messages */
153 - (NSArray *)fetchKeys {
154 /* Note: see SOGoMailManager.m for allowed IMAP4 keys */
155 static NSArray *keys = nil;
157 keys = [[NSArray alloc] initWithObjects:
158 @"FLAGS", @"ENVELOPE", @"RFC822.SIZE", nil];
163 - (NSString *)defaultSortKey {
166 - (NSString *)imap4SortKey {
169 sort = [[[self context] request] formValueForKey:@"sort"];
171 if ([sort length] == 0)
172 sort = [self defaultSortKey];
173 return [sort uppercaseString];
176 - (BOOL)isSortedDescending {
179 desc = [[[self context] request] formValueForKey:@"desc"];
182 return [desc boolValue] ? YES : NO;
185 - (NSString *)imap4SortOrdering {
188 sort = [self imap4SortKey];
189 if(![self isSortedDescending])
191 return [@"REVERSE " stringByAppendingString:sort];
194 - (NSRange)fetchRange {
195 if (self->firstMessageNumber == 0)
196 return NSMakeRange(0, 50);
197 return NSMakeRange(self->firstMessageNumber - 1, 50);
200 - (NSArray *)sortedUIDs {
201 if (self->sortedUIDs != nil)
202 return self->sortedUIDs;
205 = [[[self clientObject] fetchUIDsMatchingQualifier:[self qualifier]
206 sortOrdering:[self imap4SortOrdering]] retain];
207 return self->sortedUIDs;
209 - (unsigned int)totalMessageCount {
210 return [self->sortedUIDs count];
212 - (BOOL)showsAllMessages {
213 return ([[self sortedUIDs] count] <= [self fetchRange].length) ? YES : NO;
216 - (NSRange)fetchBlock {
221 r = [self fetchRange];
222 uids = [self sortedUIDs];
224 /* only need to restrict if we have a lot */
225 if ((len = [uids count]) <= r.length) {
231 if (len < r.location) {
232 // TODO: CHECK CONDITION (< vs <=)
233 /* out of range, recover at first block */
238 if (r.location + r.length > len)
239 r.length = len - r.location;
242 - (unsigned int)firstMessageNumber {
243 return [self fetchBlock].location + 1;
245 - (unsigned int)lastMessageNumber {
248 r = [self fetchBlock];
249 return r.location + r.length;
251 - (BOOL)hasPrevious {
252 return [self fetchBlock].location == 0 ? NO : YES;
255 NSRange r = [self fetchBlock];
256 return r.location + r.length >= [[self sortedUIDs] count] ? NO : YES;
259 - (unsigned int)nextFirstMessageNumber {
260 return [self firstMessageNumber] + [self fetchRange].length;
262 - (unsigned int)prevFirstMessageNumber {
266 idx = [self firstMessageNumber];
267 r = [self fetchRange];
269 return (idx - r.length);
273 - (NSArray *)messages {
279 if (self->messages != nil)
280 return self->messages;
282 r = [self fetchBlock];
283 uids = [self sortedUIDs];
284 if ((len = [uids count]) > r.length)
285 /* only need to restrict if we have a lot */
286 uids = [uids subarrayWithRange:r];
288 msgs = [[self clientObject] fetchUIDs:uids parts:[self fetchKeys]];
289 self->messages = [[msgs valueForKey:@"fetch"] retain];
290 return self->messages;
295 - (NSString *)messageViewTarget {
296 return [@"SOGo_msg_" stringByAppendingString:[self messageUidString]];
298 - (NSString *)messageViewURL {
299 // TODO: noframe only when view-target is empty
300 // TODO: markread only if the message is unread
303 s = [[self messageUidString] stringByAppendingString:@"/view?noframe=1"];
304 if (![self isMessageRead]) s = [s stringByAppendingString:@"&markread=1"];
307 - (NSString *)markReadURL {
308 return [@"markMessageRead?uid=" stringByAppendingString:
309 [self messageUidString]];
311 - (NSString *)markUnreadURL {
312 return [@"markMessageUnread?uid=" stringByAppendingString:
313 [self messageUidString]];
318 - (NSString *)msgRowID {
319 return [@"row_" stringByAppendingString:[self messageUidString]];
321 - (NSString *)msgDivID {
322 return [@"div_" stringByAppendingString:[self messageUidString]];
325 - (NSString *)msgIconReadDivID {
326 return [@"readdiv_" stringByAppendingString:[self messageUidString]];
328 - (NSString *)msgIconUnreadDivID {
329 return [@"unreaddiv_" stringByAppendingString:[self messageUidString]];
331 - (NSString *)msgIconReadVisibility {
332 return [self isMessageRead] ? nil : @"display: none;";
334 - (NSString *)msgIconUnreadVisibility {
335 return [self isMessageRead] ? @"display: none;" : nil;
338 - (NSString *)clickedMsgJS {
339 /* return 'false' aborts processing */
340 return [NSString stringWithFormat:@"clickedUid(this, '%@'); return false",
341 [self messageUidString]];
344 // the following are unused?
345 - (NSString *)dblClickedMsgJS {
346 return [NSString stringWithFormat:@"doubleClickedUid(this, '%@')",
347 [self messageUidString]];
350 // the following are unused?
351 - (NSString *)highlightRowJS {
352 return [NSString stringWithFormat:@"highlightUid(this, '%@')",
353 [self messageUidString]];
355 - (NSString *)lowlightRowJS {
356 return [NSString stringWithFormat:@"lowlightUid(this, '%@')",
357 [self messageUidString]];
360 - (NSString *)markUnreadJS {
361 return [NSString stringWithFormat:
362 @"mailListMarkMessage(this, 'markMessageUnread', "
364 [self messageUidString]];
366 - (NSString *)markReadJS {
367 return [NSString stringWithFormat:
368 @"mailListMarkMessage(this, 'markMessageRead', "
370 [self messageUidString]];
373 - (NSString *)jsCode {
374 static NSString *script = \
375 @"var rowSelectionCount = 0;\n"
377 @"validateControls();\n"
379 @"function showElement(e, shouldShow) {\n"
380 @" e.style.display = shouldShow ? \"\" : \"none\";\n"
383 @"function enableElement(e, shouldEnable) {\n"
386 @" if(shouldEnable) {\n"
387 @" if(e.hasAttribute(\"disabled\"))\n"
388 @" e.removeAttribute(\"disabled\");\n"
391 @" e.setAttribute(\"disabled\", \"1\");\n"
395 @"function toggleRowSelectionStatus(sender) {\n"
396 @" rowID = sender.value;\n"
397 @" tr = document.getElementById(rowID);\n"
398 @" if(sender.checked) {\n"
399 @" tr.className = \"tableview_selected\";\n"
400 @" rowSelectionCount += 1;\n"
403 @" tr.className = \"tableview\";\n"
404 @" rowSelectionCount -= 1;\n"
406 @" this.validateControls();\n"
409 @"function validateControls() {\n"
410 @" var e = document.getElementById(\"moveto\");\n"
411 @" this.enableElement(e, rowSelectionCount > 0);\n"
414 @"function moveTo(uri) {\n"
415 @" alert(\"MoveTo: \" + uri);\n"
423 - (SOGoMailObject *)lookupActiveMessage {
426 if ((uid = [[[self context] request] formValueForKey:@"uid"]) == nil)
429 return [[self clientObject] lookupName:uid inContext:[self context]
435 - (id)defaultAction {
436 self->firstMessageNumber =
437 [[[[self context] request] formValueForKey:@"idx"] intValue];
441 - (BOOL)isJavaScriptRequest {
442 return [[[[self context] request] formValueForKey:@"jsonly"] boolValue];
447 r = [[self context] response];
448 [r setStatus:200 /* OK */];
452 - (id)markMessageUnreadAction {
455 if ((error = [[self lookupActiveMessage] removeFlags:@"seen"]) != nil)
456 // TODO: improve error handling
459 if ([self isJavaScriptRequest])
460 return [self javaScriptOK];
462 return [self redirectToLocation:@"view"];
464 - (id)markMessageReadAction {
467 if ((error = [[self lookupActiveMessage] addFlags:@"seen"]) != nil)
468 // TODO: improve error handling
471 if ([self isJavaScriptRequest])
472 return [self javaScriptOK];
474 return [self redirectToLocation:@"view"];
477 - (id)getMailAction {
478 // TODO: we might want to flush the caches?
481 if ((client = [self clientObject]) == nil) {
482 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
483 reason:@"did not find mail folder"];
486 if (![client respondsToSelector:@selector(flushMailCaches)]) {
487 return [NSException exceptionWithHTTPStatus:500 /* Server Error */
489 @"invalid client object (does not support flush)"];
492 [client flushMailCaches];
493 return [self redirectToLocation:@"view"];
496 - (id)expungeAction {
497 // TODO: we might want to flush the caches?
501 if ((client = [self clientObject]) == nil) {
502 return [NSException exceptionWithHTTPStatus:404 /* Not Found */
503 reason:@"did not find mail folder"];
506 if ((error = [[self clientObject] expunge]) != nil)
509 if ([client respondsToSelector:@selector(flushMailCaches)])
510 [client flushMailCaches];
511 return [self redirectToLocation:@"view"];
514 @end /* UIxMailListView */