merging conflicts
commit
d197ab71b7
|
@ -126,6 +126,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[[o properties] removeObjectForKey: @"SyncKey"];
|
[[o properties] removeObjectForKey: @"SyncKey"];
|
||||||
[[o properties] removeObjectForKey: @"SyncCache"];
|
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||||
[[o properties] removeObjectForKey: @"DateCache"];
|
[[o properties] removeObjectForKey: @"DateCache"];
|
||||||
|
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||||
|
|
||||||
[[o properties] addEntriesFromDictionary: theFolderMetadata];
|
[[o properties] addEntriesFromDictionary: theFolderMetadata];
|
||||||
[o save];
|
[o save];
|
||||||
|
@ -477,21 +478,70 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
lastServerKey: (NSString **) theLastServerKey
|
lastServerKey: (NSString **) theLastServerKey
|
||||||
|
|
||||||
{
|
{
|
||||||
NSMutableDictionary *folderMetadata;
|
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
|
||||||
NSMutableString *s;
|
NSMutableString *s;
|
||||||
|
|
||||||
BOOL more_available;
|
BOOL more_available;
|
||||||
int i, max;
|
int i, max;
|
||||||
|
|
||||||
|
s = [NSMutableString string];
|
||||||
|
|
||||||
|
more_available = NO;
|
||||||
|
|
||||||
|
if (theFolderType == ActiveSyncMailFolder && !([theSyncKey isEqualToString: @"-1"]) && theFilterType)
|
||||||
|
{
|
||||||
|
NSArray *allKeys;
|
||||||
|
NSString *key;
|
||||||
|
int softdelete_count;
|
||||||
|
|
||||||
|
softdelete_count = 0;
|
||||||
|
|
||||||
|
folderMetadata = [self _folderMetadataForKey: [theCollection nameInContainer]];
|
||||||
|
dateCache = [folderMetadata objectForKey: @"DateCache"];
|
||||||
|
syncCache = [folderMetadata objectForKey: @"SyncCache"];
|
||||||
|
|
||||||
|
allKeys = [dateCache allKeys];
|
||||||
|
for (i = 0; i < [allKeys count]; i++)
|
||||||
|
{
|
||||||
|
key = [allKeys objectAtIndex: i];
|
||||||
|
|
||||||
|
if ([[dateCache objectForKey:key] compare: theFilterType] == NSOrderedAscending)
|
||||||
|
{
|
||||||
|
[s appendString: @"<SoftDelete xmlns=\"AirSync:\">"];
|
||||||
|
[s appendFormat: @"<ServerId xmlns=\"AirSync:\">%@</ServerId>", key];
|
||||||
|
[s appendString: @"</SoftDelete>"];
|
||||||
|
|
||||||
|
[syncCache removeObjectForKey: key];
|
||||||
|
[dateCache removeObjectForKey: key];
|
||||||
|
|
||||||
|
softdelete_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (softdelete_count >= theWindowSize)
|
||||||
|
{
|
||||||
|
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
|
||||||
|
[self _setFolderMetadata: folderMetadata forKey: [theCollection nameInContainer]];
|
||||||
|
|
||||||
|
more_available = YES;
|
||||||
|
*theLastServerKey = theSyncKey;
|
||||||
|
|
||||||
|
// Since WindowSize is reached don't even try to add more to the response, let's just
|
||||||
|
// jump to the end and return the response immediately
|
||||||
|
goto return_response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||||
|
[self _setFolderMetadata: folderMetadata forKey: [theCollection nameInContainer]];
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// No changes in the collection - 2.2.2.19.1.1 Empty Sync Request.
|
// No changes in the collection - 2.2.2.19.1.1 Empty Sync Request.
|
||||||
// We check this and we don't generate any commands if we don't have to.
|
// We check this and we don't generate any commands if we don't have to.
|
||||||
//
|
//
|
||||||
if ([theSyncKey isEqualToString: [theCollection davCollectionTag]])
|
if ([theSyncKey isEqualToString: [theCollection davCollectionTag]] && !([s length]))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
s = [NSMutableString string];
|
|
||||||
|
|
||||||
more_available = NO;
|
more_available = NO;
|
||||||
|
|
||||||
switch (theFolderType)
|
switch (theFolderType)
|
||||||
|
@ -614,7 +664,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
case ActiveSyncMailFolder:
|
case ActiveSyncMailFolder:
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
NSMutableDictionary *syncCache, *dateCache;
|
|
||||||
SOGoSyncCacheObject *lastCacheObject, *aCacheObject;
|
SOGoSyncCacheObject *lastCacheObject, *aCacheObject;
|
||||||
NSMutableArray *allCacheObjects, *sortedBySequence;
|
NSMutableArray *allCacheObjects, *sortedBySequence;
|
||||||
|
|
||||||
|
@ -644,6 +693,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
|
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
|
||||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
|
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether GUID in cache is equal to the GUID from imap - this is to avoid cache corruptions if a folder has been renamed and a new folder
|
// Check whether GUID in cache is equal to the GUID from imap - this is to avoid cache corruptions if a folder has been renamed and a new folder
|
||||||
// with the same name has been created but folderSync has not yet updated the cache
|
// with the same name has been created but folderSync has not yet updated the cache
|
||||||
if (!([[theCollection nameInContainer] isEqualToString:
|
if (!([[theCollection nameInContainer] isEqualToString:
|
||||||
|
@ -805,6 +855,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
break;
|
break;
|
||||||
} // switch (folderType) ...
|
} // switch (folderType) ...
|
||||||
|
|
||||||
|
return_response:
|
||||||
|
|
||||||
if ([s length])
|
if ([s length])
|
||||||
{
|
{
|
||||||
[theBuffer appendString: @"<Commands>"];
|
[theBuffer appendString: @"<Commands>"];
|
||||||
|
@ -1008,7 +1060,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
davCollectionTag = [collection davCollectionTag];
|
davCollectionTag = [collection davCollectionTag];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Generate the response buffer
|
// Generate the response buffer
|
||||||
[theBuffer appendString: @"<Collection>"];
|
[theBuffer appendString: @"<Collection>"];
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#import <NGMime/NGMimeBodyPart.h>
|
#import <NGMime/NGMimeBodyPart.h>
|
||||||
#import <NGMime/NGMimeFileData.h>
|
#import <NGMime/NGMimeFileData.h>
|
||||||
#import <NGMime/NGMimeMultipartBody.h>
|
#import <NGMime/NGMimeMultipartBody.h>
|
||||||
|
#import <NGMime/NGMimeType.h>
|
||||||
#import <NGMail/NGMimeMessageParser.h>
|
#import <NGMail/NGMimeMessageParser.h>
|
||||||
#import <NGMail/NGMimeMessage.h>
|
#import <NGMail/NGMimeMessage.h>
|
||||||
#import <NGMail/NGMimeMessageGenerator.h>
|
#import <NGMail/NGMimeMessageGenerator.h>
|
||||||
|
@ -170,6 +171,41 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
return [o properties];
|
return [o properties];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (unsigned int) _softDeleteCountWithFilter: (NSCalendarDate *) theFilter
|
||||||
|
collectionId: (NSString *) theCollectionId
|
||||||
|
{
|
||||||
|
NSMutableDictionary *dateCache;
|
||||||
|
NSMutableArray *sdUids;
|
||||||
|
SOGoCacheGCSObject *o;
|
||||||
|
NSArray *allKeys;
|
||||||
|
NSString *key;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
sdUids = [NSMutableArray array];
|
||||||
|
|
||||||
|
if (theFilter)
|
||||||
|
{
|
||||||
|
o = [SOGoCacheGCSObject objectWithName: [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], theCollectionId] inContainer: nil];
|
||||||
|
[o setObjectType: ActiveSyncGlobalCacheObject];
|
||||||
|
[o setTableUrl: [self folderTableURL]];
|
||||||
|
[o reloadIfNeeded];
|
||||||
|
|
||||||
|
dateCache = [[o properties] objectForKey: @"DateCache"];
|
||||||
|
allKeys = [dateCache allKeys];
|
||||||
|
|
||||||
|
for (i = 0; i < [allKeys count]; i++)
|
||||||
|
{
|
||||||
|
key = [allKeys objectAtIndex: i];
|
||||||
|
|
||||||
|
if ([[dateCache objectForKey:key] compare: theFilter ] == NSOrderedAscending)
|
||||||
|
[sdUids addObject: [dateCache objectForKey:key]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [sdUids count];
|
||||||
|
}
|
||||||
|
|
||||||
- (id) globallyUniqueIDToIMAPFolderName: (NSString *) theIdToTranslate
|
- (id) globallyUniqueIDToIMAPFolderName: (NSString *) theIdToTranslate
|
||||||
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
||||||
{
|
{
|
||||||
|
@ -919,6 +955,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
sortOrdering: @"REVERSE ARRIVAL"
|
sortOrdering: @"REVERSE ARRIVAL"
|
||||||
threaded: NO];
|
threaded: NO];
|
||||||
count = [uids count];
|
count = [uids count];
|
||||||
|
|
||||||
|
// Add the number of UIDs expected to "soft delete"
|
||||||
|
count += [self _softDeleteCountWithFilter: filter collectionId: realCollectionId];
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1899,9 +1939,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
value = [theDocumentElement getElementsByTagName: @"ReplaceMime"];
|
value = [theDocumentElement getElementsByTagName: @"ReplaceMime"];
|
||||||
|
|
||||||
// ReplaceMime isn't specified so we must NOT use the server copy
|
// ReplaceMime IS specified so we must NOT use the server copy
|
||||||
// but rather take the data as-is from the client.
|
// but rather take the data as-is from the client.
|
||||||
if (![value count])
|
if ([value count])
|
||||||
{
|
{
|
||||||
[self processSendMail: theDocumentElement
|
[self processSendMail: theDocumentElement
|
||||||
inResponse: theResponse];
|
inResponse: theResponse];
|
||||||
|
@ -1921,11 +1961,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
NSData *data;
|
NSData *data;
|
||||||
|
|
||||||
NGMimeMessageGenerator *generator;
|
NGMimeMessageGenerator *generator;
|
||||||
NGMimeBodyPart *bodyPart;
|
NGMimeBodyPart *bodyPart;
|
||||||
NGMutableHashMap *map;
|
NGMutableHashMap *map;
|
||||||
NGMimeFileData *fdata;
|
NGMimeFileData *fdata;
|
||||||
NSException *error;
|
NSException *error;
|
||||||
id body;
|
|
||||||
|
id body, bodyFromSmartForward;
|
||||||
|
|
||||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||||
|
@ -1941,10 +1982,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
parser = [[NGMimeMessageParser alloc] init];
|
parser = [[NGMimeMessageParser alloc] init];
|
||||||
data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding];
|
data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding];
|
||||||
messageFromSmartForward = [parser parsePartFromData: data];
|
messageFromSmartForward = [parser parsePartFromData: data];
|
||||||
|
|
||||||
RELEASE(parser);
|
RELEASE(parser);
|
||||||
|
|
||||||
|
|
||||||
// We create a new MIME multipart/mixed message. The first part will be the text part
|
// We create a new MIME multipart/mixed message. The first part will be the text part
|
||||||
// of our "smart forward" and the second part will be the message/rfc822 part of the
|
// of our "smart forward" and the second part will be the message/rfc822 part of the
|
||||||
// "smart forwarded" message.
|
// "smart forwarded" message.
|
||||||
|
@ -1954,11 +1993,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
|
messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
|
||||||
body = [[[NGMimeMultipartBody alloc] initWithPart: messageToSend] autorelease];
|
body = [[[NGMimeMultipartBody alloc] initWithPart: messageToSend] autorelease];
|
||||||
|
|
||||||
// First part
|
// First part - either a text/* or a multipart/*. If it's a multipart,
|
||||||
|
// we take the first part text/* part we see.
|
||||||
map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease];
|
map = [[[NGMutableHashMap alloc] initWithCapacity: 1] autorelease];
|
||||||
[map setObject: @"text/plain" forKey: @"content-type"];
|
bodyFromSmartForward = nil;
|
||||||
|
|
||||||
|
if ([[messageFromSmartForward body] isKindOfClass: [NGMimeMultipartBody class]])
|
||||||
|
{
|
||||||
|
NGMimeBodyPart *part;
|
||||||
|
NSArray *parts;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
parts = [[messageFromSmartForward body] parts];
|
||||||
|
|
||||||
|
for (i = 0; i < [parts count]; i++)
|
||||||
|
{
|
||||||
|
part = [parts objectAtIndex: i];
|
||||||
|
|
||||||
|
if ([[[part contentType] type] isEqualToString: @"text"])
|
||||||
|
{
|
||||||
|
[map setObject: [[part contentType] stringValue] forKey: @"content-type"];
|
||||||
|
bodyFromSmartForward = [part body];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[map setObject: [[messageFromSmartForward contentType] stringValue] forKey: @"content-type"];
|
||||||
|
bodyFromSmartForward = [messageFromSmartForward body];
|
||||||
|
}
|
||||||
|
|
||||||
bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease];
|
bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease];
|
||||||
[bodyPart setBody: [messageFromSmartForward body]];
|
[bodyPart setBody: bodyFromSmartForward];
|
||||||
[body addBodyPart: bodyPart];
|
[body addBodyPart: bodyPart];
|
||||||
|
|
||||||
// Second part
|
// Second part
|
||||||
|
|
|
@ -238,6 +238,8 @@ struct GlobalObjectId {
|
||||||
|
|
||||||
if (key)
|
if (key)
|
||||||
{
|
{
|
||||||
|
NSString *s, *charset;
|
||||||
|
|
||||||
d = [[self fetchPlainTextParts] objectForKey: key];
|
d = [[self fetchPlainTextParts] objectForKey: key];
|
||||||
|
|
||||||
encoding = [[self lookupInfoForBodyPart: key] objectForKey: @"encoding"];
|
encoding = [[self lookupInfoForBodyPart: key] objectForKey: @"encoding"];
|
||||||
|
@ -246,6 +248,14 @@ struct GlobalObjectId {
|
||||||
d = [d dataByDecodingBase64];
|
d = [d dataByDecodingBase64];
|
||||||
else if ([encoding caseInsensitiveCompare: @"quoted-printable"] == NSOrderedSame)
|
else if ([encoding caseInsensitiveCompare: @"quoted-printable"] == NSOrderedSame)
|
||||||
d = [d dataByDecodingQuotedPrintableTransferEncoding];
|
d = [d dataByDecodingQuotedPrintableTransferEncoding];
|
||||||
|
|
||||||
|
charset = [[[self lookupInfoForBodyPart: key] objectForKey: @"parameterList"] objectForKey: @"charset"];
|
||||||
|
|
||||||
|
if (![charset length])
|
||||||
|
charset = @"us-ascii";
|
||||||
|
|
||||||
|
s = [NSString stringWithData: d usingEncodingNamed: charset];
|
||||||
|
d = [s dataUsingEncoding: NSUTF8StringEncoding];
|
||||||
}
|
}
|
||||||
|
|
||||||
return d;
|
return d;
|
||||||
|
@ -296,13 +306,13 @@ struct GlobalObjectId {
|
||||||
if ([body isKindOfClass: [NSData class]])
|
if ([body isKindOfClass: [NSData class]])
|
||||||
{
|
{
|
||||||
NSString *charset;
|
NSString *charset;
|
||||||
int encoding;
|
|
||||||
|
|
||||||
charset = [[thePart contentType] valueOfParameter: @"charset"];
|
charset = [[thePart contentType] valueOfParameter: @"charset"];
|
||||||
encoding = [NGMimeType stringEncodingForCharset: charset];
|
|
||||||
|
if (![charset length])
|
||||||
|
charset = @"us-ascii";
|
||||||
|
|
||||||
s = [[NSString alloc] initWithData: body encoding: encoding];
|
s = [NSString stringWithData: body usingEncodingNamed: charset];
|
||||||
AUTORELEASE(s);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -396,6 +406,13 @@ struct GlobalObjectId {
|
||||||
{
|
{
|
||||||
if ([type isEqualToString: @"text"])
|
if ([type isEqualToString: @"text"])
|
||||||
{
|
{
|
||||||
|
NSString *s, *charset;
|
||||||
|
|
||||||
|
charset = [[[self lookupInfoForBodyPart: @""] objectForKey: @"parameterList"] objectForKey: @"charset"];
|
||||||
|
|
||||||
|
if (![charset length])
|
||||||
|
charset = @"us-ascii";
|
||||||
|
|
||||||
d = [[self fetchPlainTextParts] objectForKey: @""];
|
d = [[self fetchPlainTextParts] objectForKey: @""];
|
||||||
|
|
||||||
// We check if we have base64 encoded parts. If so, we just
|
// We check if we have base64 encoded parts. If so, we just
|
||||||
|
@ -407,17 +424,15 @@ struct GlobalObjectId {
|
||||||
else if ([encoding caseInsensitiveCompare: @"quoted-printable"] == NSOrderedSame)
|
else if ([encoding caseInsensitiveCompare: @"quoted-printable"] == NSOrderedSame)
|
||||||
d = [d dataByDecodingQuotedPrintableTransferEncoding];
|
d = [d dataByDecodingQuotedPrintableTransferEncoding];
|
||||||
|
|
||||||
|
s = [NSString stringWithData: d usingEncodingNamed: charset];
|
||||||
|
|
||||||
// Check if we must convert html->plain
|
// Check if we must convert html->plain
|
||||||
if (theType == 1 && [subtype isEqualToString: @"html"])
|
if (theType == 1 && [subtype isEqualToString: @"html"])
|
||||||
{
|
{
|
||||||
NSString *s;
|
|
||||||
|
|
||||||
s = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding];
|
|
||||||
AUTORELEASE(s);
|
|
||||||
|
|
||||||
s = [s htmlToText];
|
s = [s htmlToText];
|
||||||
d = [s dataUsingEncoding: NSUTF8StringEncoding];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d = [s dataUsingEncoding: NSUTF8StringEncoding];
|
||||||
}
|
}
|
||||||
else if ([type isEqualToString: @"multipart"])
|
else if ([type isEqualToString: @"multipart"])
|
||||||
{
|
{
|
||||||
|
|
Binary file not shown.
8
NEWS
8
NEWS
|
@ -5,11 +5,19 @@ Enhancements
|
||||||
- contacts photos are now synchronized using ActiveSync (#2807)
|
- contacts photos are now synchronized using ActiveSync (#2807)
|
||||||
- implemented the GetAttachment ActiveSync command (#2808)
|
- implemented the GetAttachment ActiveSync command (#2808)
|
||||||
- implemented the Ping ActiveSync command
|
- implemented the Ping ActiveSync command
|
||||||
|
- added "soft deletes" support for ActiveSync (#2734)
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
- better handling of empty "Flag" messages over ActiveSync (#2806)
|
- better handling of empty "Flag" messages over ActiveSync (#2806)
|
||||||
- fixed Chinese charset handling (#2809)
|
- fixed Chinese charset handling (#2809)
|
||||||
- fixed folder name (calendars and contacts) of new subscriptions (#2801)
|
- fixed folder name (calendars and contacts) of new subscriptions (#2801)
|
||||||
|
- fixed the reply/forward operation over ActiveSync (#2805)
|
||||||
|
- fixed regression when attaching files to a reply
|
||||||
|
- wait 20 seconds (instead of 2) before deleting temporary download forms (#2811)
|
||||||
|
- avoid raising exceptions when the db is down and we try to access the preferences module (#2813)
|
||||||
|
- we now ignore the SCHEDULE-AGENT property when Thunderbird/Ligthning sends it to avoid
|
||||||
|
not-generating invitation responses for externally received IMIP messages
|
||||||
|
- improved charset handling over ActiveSync (#2810)
|
||||||
|
|
||||||
2.2.5 (2014-06-05)
|
2.2.5 (2014-06-05)
|
||||||
------------------
|
------------------
|
||||||
|
|
|
@ -232,7 +232,7 @@ static NSArray *privilegedTagNames = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) characters: (unichar *) _chars
|
- (void) characters: (unichar *) _chars
|
||||||
length: (int) _len
|
length: (NSUInteger) _len
|
||||||
{
|
{
|
||||||
if (_len && _chars)
|
if (_len && _chars)
|
||||||
{
|
{
|
||||||
|
|
|
@ -217,10 +217,10 @@
|
||||||
printf("</%s>\n", [_localName cString]);
|
printf("</%s>\n", [_localName cString]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)characters:(unichar *)_chars length:(int)_len {
|
- (void)characters:(unichar *)_chars length:(NSUInteger)_len {
|
||||||
NSString *str;
|
NSString *str;
|
||||||
id tmp;
|
id tmp;
|
||||||
unsigned i, len;
|
NSUInteger i, len;
|
||||||
|
|
||||||
if (_len == 0) {
|
if (_len == 0) {
|
||||||
[self indent];
|
[self indent];
|
||||||
|
@ -228,7 +228,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < (unsigned)_len; i++) {
|
for (i = 0; i < _len; i++) {
|
||||||
if (_chars[i] > 255) {
|
if (_chars[i] > 255) {
|
||||||
NSLog(@"detected large char: o%04o d%03i h%04X",
|
NSLog(@"detected large char: o%04o d%03i h%04X",
|
||||||
_chars[i], _chars[i], _chars[i]);
|
_chars[i], _chars[i], _chars[i]);
|
||||||
|
@ -246,7 +246,7 @@
|
||||||
[self indent];
|
[self indent];
|
||||||
printf("\"%s\"\n", [str cString]);
|
printf("\"%s\"\n", [str cString]);
|
||||||
}
|
}
|
||||||
- (void)ignorableWhitespace:(unichar *)_chars length:(int)_len {
|
- (void)ignorableWhitespace:(unichar *)_chars length:(NSUInteger)_len {
|
||||||
NSString *data;
|
NSString *data;
|
||||||
id tmp;
|
id tmp;
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,8 @@ typedef enum {
|
||||||
|
|
||||||
- (NSArray *) calendarUIDs;
|
- (NSArray *) calendarUIDs;
|
||||||
|
|
||||||
|
- (NSNumber *) activeTasks;
|
||||||
|
|
||||||
/* vevent UID handling */
|
/* vevent UID handling */
|
||||||
|
|
||||||
- (NSString *) resourceNameForEventUID: (NSString *) _uid;
|
- (NSString *) resourceNameForEventUID: (NSString *) _uid;
|
||||||
|
|
|
@ -230,6 +230,8 @@ static iCalEvent *iCalEventK = nil;
|
||||||
abstract: NO
|
abstract: NO
|
||||||
withEquivalent: SoPerm_AddDocumentsImagesAndFiles
|
withEquivalent: SoPerm_AddDocumentsImagesAndFiles
|
||||||
asChildOf: davElement (@"write", XMLNS_WEBDAV)];
|
asChildOf: davElement (@"write", XMLNS_WEBDAV)];
|
||||||
|
|
||||||
|
/* read-acl and write-acl are defined in RFC3744 */
|
||||||
[aclManager registerDAVPermission: davElement (@"admin", nsI)
|
[aclManager registerDAVPermission: davElement (@"admin", nsI)
|
||||||
abstract: YES
|
abstract: YES
|
||||||
withEquivalent: nil
|
withEquivalent: nil
|
||||||
|
@ -242,6 +244,72 @@ static iCalEvent *iCalEventK = nil;
|
||||||
abstract: YES
|
abstract: YES
|
||||||
withEquivalent: SoPerm_ChangePermissions
|
withEquivalent: SoPerm_ChangePermissions
|
||||||
asChildOf: davElement (@"admin", nsI)];
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
|
|
||||||
|
/* Default permissions for calendars. These are very important so that DAV client can
|
||||||
|
detect permission changes on calendars and reload all items, if necessary */
|
||||||
|
|
||||||
|
/* Public ones */
|
||||||
|
[aclManager registerDAVPermission: davElement (@"viewwhole-public-records", nsI)
|
||||||
|
abstract: YES
|
||||||
|
withEquivalent: SOGoCalendarPerm_ViewWholePublicRecords
|
||||||
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
|
|
||||||
|
[aclManager registerDAVPermission: davElement (@"viewdant-public-records", nsI)
|
||||||
|
abstract: YES
|
||||||
|
withEquivalent: SOGoCalendarPerm_ViewDAndTOfPublicRecords
|
||||||
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
|
|
||||||
|
[aclManager registerDAVPermission: davElement (@"modify-public-records", nsI)
|
||||||
|
abstract: YES
|
||||||
|
withEquivalent: SOGoCalendarPerm_ModifyPublicRecords
|
||||||
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
|
|
||||||
|
[aclManager registerDAVPermission: davElement (@"respondto-public-records", nsI)
|
||||||
|
abstract: YES
|
||||||
|
withEquivalent: SOGoCalendarPerm_RespondToPublicRecords
|
||||||
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
|
|
||||||
|
/* Private ones */
|
||||||
|
[aclManager registerDAVPermission: davElement (@"viewwhole-private-records", nsI)
|
||||||
|
abstract: YES
|
||||||
|
withEquivalent: SOGoCalendarPerm_ViewWholePrivateRecords
|
||||||
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
|
|
||||||
|
[aclManager registerDAVPermission: davElement (@"viewdant-private-records", nsI)
|
||||||
|
abstract: YES
|
||||||
|
withEquivalent: SOGoCalendarPerm_ViewDAndTOfPrivateRecords
|
||||||
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
|
|
||||||
|
[aclManager registerDAVPermission: davElement (@"modify-private-records", nsI)
|
||||||
|
abstract: YES
|
||||||
|
withEquivalent: SOGoCalendarPerm_ModifyPrivateRecords
|
||||||
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
|
|
||||||
|
[aclManager registerDAVPermission: davElement (@"respondto-private-records", nsI)
|
||||||
|
abstract: YES
|
||||||
|
withEquivalent: SOGoCalendarPerm_RespondToPrivateRecords
|
||||||
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
|
|
||||||
|
/* Condifential ones */
|
||||||
|
[aclManager registerDAVPermission: davElement (@"viewwhole-confidential-records", nsI)
|
||||||
|
abstract: YES
|
||||||
|
withEquivalent: SOGoCalendarPerm_ViewWholeConfidentialRecords
|
||||||
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
|
|
||||||
|
[aclManager registerDAVPermission: davElement (@"viewdant-confidential-records", nsI)
|
||||||
|
abstract: YES
|
||||||
|
withEquivalent: SOGoCalendarPerm_ViewDAndTOfConfidentialRecords
|
||||||
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
|
|
||||||
|
[aclManager registerDAVPermission: davElement (@"modify-confidential-records", nsI)
|
||||||
|
abstract: YES
|
||||||
|
withEquivalent: SOGoCalendarPerm_ModifyConfidentialRecords
|
||||||
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
|
|
||||||
|
[aclManager registerDAVPermission: davElement (@"respondto-confidential-records", nsI)
|
||||||
|
abstract: YES
|
||||||
|
withEquivalent: SOGoCalendarPerm_RespondToConfidentialRecords
|
||||||
|
asChildOf: davElement (@"admin", nsI)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return aclManager;
|
return aclManager;
|
||||||
|
@ -522,7 +590,6 @@ static iCalEvent *iCalEventK = nil;
|
||||||
{
|
{
|
||||||
/* this is used for group calendars (this folder just returns itself) */
|
/* this is used for group calendars (this folder just returns itself) */
|
||||||
NSString *s;
|
NSString *s;
|
||||||
|
|
||||||
s = [[self container] nameInContainer];
|
s = [[self container] nameInContainer];
|
||||||
// [self logWithFormat:@"CAL UID: %@", s];
|
// [self logWithFormat:@"CAL UID: %@", s];
|
||||||
return [s isNotNull] ? [NSArray arrayWithObjects:&s count:1] : nil;
|
return [s isNotNull] ? [NSArray arrayWithObjects:&s count:1] : nil;
|
||||||
|
@ -3270,4 +3337,25 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSNumber *) activeTasks
|
||||||
|
{
|
||||||
|
NSArray *tasksList;
|
||||||
|
NSMutableArray *fields;
|
||||||
|
NSNumber *activeTasks;
|
||||||
|
|
||||||
|
fields = [NSMutableArray arrayWithObjects: @"c_component", @"c_status", nil];
|
||||||
|
|
||||||
|
tasksList = [self bareFetchFields: fields
|
||||||
|
from: nil
|
||||||
|
to: nil
|
||||||
|
title: nil
|
||||||
|
component: @"vtodo"
|
||||||
|
additionalFilters: @"c_status != 1 AND c_status != 3"];
|
||||||
|
|
||||||
|
activeTasks = [NSNumber numberWithInt:[tasksList count]];
|
||||||
|
|
||||||
|
return activeTasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@end /* SOGoAppointmentFolder */
|
@end /* SOGoAppointmentFolder */
|
||||||
|
|
|
@ -1428,9 +1428,11 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
//
|
//
|
||||||
- (BOOL) _shouldScheduleEvent: (iCalPerson *) theOrganizer
|
- (BOOL) _shouldScheduleEvent: (iCalPerson *) theOrganizer
|
||||||
{
|
{
|
||||||
|
NSArray *userAgents;
|
||||||
NSString *v;
|
NSString *v;
|
||||||
BOOL b;
|
BOOL b;
|
||||||
|
int i;
|
||||||
|
|
||||||
b = YES;
|
b = YES;
|
||||||
|
|
||||||
if (theOrganizer && (v = [theOrganizer value: 0 ofAttribute: @"SCHEDULE-AGENT"]))
|
if (theOrganizer && (v = [theOrganizer value: 0 ofAttribute: @"SCHEDULE-AGENT"]))
|
||||||
|
@ -1440,6 +1442,27 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||||
b = NO;
|
b = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// If we have to deal with Thunderbird/Lightning, we always send invitation
|
||||||
|
// reponses, as Lightning v2.6 (at least this version) sets SCHEDULE-AGENT
|
||||||
|
// to NONE/CLIENT when responding to an external invitation received by
|
||||||
|
// SOGo - so no invitation responses are ever sent by Lightning. See
|
||||||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=865726 and
|
||||||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=997784
|
||||||
|
//
|
||||||
|
userAgents = [[context request] headersForKey: @"User-Agent"];
|
||||||
|
|
||||||
|
for (i = 0; i < [userAgents count]; i++)
|
||||||
|
{
|
||||||
|
if ([[userAgents objectAtIndex: i] rangeOfString: @"Thunderbird"].location != NSNotFound &&
|
||||||
|
[[userAgents objectAtIndex: i] rangeOfString: @"Lightning"].location != NSNotFound)
|
||||||
|
{
|
||||||
|
b = YES;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -430,7 +430,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) ignorableWhitespace: (unichar *) whitespaces
|
- (void) ignorableWhitespace: (unichar *) whitespaces
|
||||||
length: (int) length
|
length: (NSUInteger) length
|
||||||
{
|
{
|
||||||
showWhoWeAre();
|
showWhoWeAre();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1761,26 +1761,26 @@ static NSString *userAgent = nil;
|
||||||
{
|
{
|
||||||
SOGoUserDefaults *ud;
|
SOGoUserDefaults *ud;
|
||||||
ud = [[context activeUser] userDefaults];
|
ud = [[context activeUser] userDefaults];
|
||||||
|
|
||||||
if ([ud mailAddOutgoingAddresses])
|
if ([ud mailAddOutgoingAddresses])
|
||||||
{
|
{
|
||||||
|
Class contactGCSEntry;
|
||||||
SOGoContactFolders *contactFolders;
|
SOGoContactFolders *contactFolders;
|
||||||
NGMailAddressParser *parser;
|
|
||||||
id parsedRecipient;
|
|
||||||
SOGoContactFolder *folder;
|
SOGoContactFolder *folder;
|
||||||
SOGoContactGCSEntry *newContact;
|
SOGoContactGCSEntry *newContact;
|
||||||
NGVCard *card;
|
NGVCard *card;
|
||||||
Class contactGCSEntry;
|
NGMailAddressParser *parser;
|
||||||
|
NSArray *matchingContacts;
|
||||||
NSMutableArray *recipients;
|
NSMutableArray *recipients;
|
||||||
NSString *recipient, *emailAddress, *addressBook, *uid;
|
NSString *recipient, *emailAddress, *addressBook, *uid;
|
||||||
NSArray *matchingContacts;
|
id parsedRecipient;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
// Get all the addressbooks
|
// Get all the addressbooks
|
||||||
contactFolders = [[[context activeUser] homeFolderInContext: context]
|
contactFolders = [[[context activeUser] homeFolderInContext: context]
|
||||||
lookupName: @"Contacts"
|
lookupName: @"Contacts"
|
||||||
inContext: context
|
inContext: context
|
||||||
acquire: NO];
|
acquire: NO];
|
||||||
// Get all the recipients from the current email
|
// Get all the recipients from the current email
|
||||||
recipients = [self allRecipients];
|
recipients = [self allRecipients];
|
||||||
for (i = 0; i < [recipients count]; i++)
|
for (i = 0; i < [recipients count]; i++)
|
||||||
|
@ -1790,7 +1790,7 @@ static NSString *userAgent = nil;
|
||||||
parser = [NGMailAddressParser mailAddressParserWithString: recipient];
|
parser = [NGMailAddressParser mailAddressParserWithString: recipient];
|
||||||
parsedRecipient = [parser parse];
|
parsedRecipient = [parser parse];
|
||||||
emailAddress = [parsedRecipient address];
|
emailAddress = [parsedRecipient address];
|
||||||
|
|
||||||
matchingContacts = [contactFolders allContactsFromFilter: emailAddress
|
matchingContacts = [contactFolders allContactsFromFilter: emailAddress
|
||||||
excludeGroups: YES
|
excludeGroups: YES
|
||||||
excludeLists: YES];
|
excludeLists: YES];
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
= (@"CREATE TABLE %@ ("
|
= (@"CREATE TABLE %@ ("
|
||||||
@" c_path VARCHAR(255) PRIMARY KEY,"
|
@" c_path VARCHAR(255) PRIMARY KEY,"
|
||||||
@" c_parent_path VARCHAR(255),"
|
@" c_parent_path VARCHAR(255),"
|
||||||
@" c_type TINYINT NOT NULL,"
|
@" c_type TINYINT UNSIGNED NOT NULL,"
|
||||||
@" c_creationdate INT NOT NULL,"
|
@" c_creationdate INT NOT NULL,"
|
||||||
@" c_lastmodified INT NOT NULL,"
|
@" c_lastmodified INT NOT NULL,"
|
||||||
@" c_version INT NOT NULL DEFAULT 0,"
|
@" c_version INT NOT NULL DEFAULT 0,"
|
||||||
|
|
|
@ -329,17 +329,24 @@ static NSArray *childRecordFields = nil;
|
||||||
fc = [cm acquireOpenChannelForURL: folderLocation];
|
fc = [cm acquireOpenChannelForURL: folderLocation];
|
||||||
if (fc)
|
if (fc)
|
||||||
{
|
{
|
||||||
sql
|
// We use an exception handler here in case the database is down when
|
||||||
= [NSString stringWithFormat: (@"SELECT c_foldername FROM %@"
|
// performing the query. This could have unexpected results.
|
||||||
@" WHERE c_path = '%@'"),
|
NS_DURING
|
||||||
[folderLocation gcsTableName], ocsPath];
|
{
|
||||||
[fc evaluateExpressionX: sql];
|
sql
|
||||||
attrs = [fc describeResults: NO];
|
= [NSString stringWithFormat: (@"SELECT c_foldername FROM %@"
|
||||||
row = [fc fetchAttributes: attrs withZone: NULL];
|
@" WHERE c_path = '%@'"),
|
||||||
if (row)
|
[folderLocation gcsTableName], ocsPath];
|
||||||
[self _setDisplayNameFromRow: row];
|
[fc evaluateExpressionX: sql];
|
||||||
[fc cancelFetch];
|
attrs = [fc describeResults: NO];
|
||||||
[cm releaseChannel: fc];
|
row = [fc fetchAttributes: attrs withZone: NULL];
|
||||||
|
if (row)
|
||||||
|
[self _setDisplayNameFromRow: row];
|
||||||
|
[fc cancelFetch];
|
||||||
|
[cm releaseChannel: fc];
|
||||||
|
}
|
||||||
|
NS_HANDLER;
|
||||||
|
NS_ENDHANDLER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -164,34 +164,45 @@ static SoSecurityManager *sm = nil;
|
||||||
NSString *folderName;
|
NSString *folderName;
|
||||||
SOGoGCSFolder *folder;
|
SOGoGCSFolder *folder;
|
||||||
SOGoUser *folderOwner;
|
SOGoUser *folderOwner;
|
||||||
|
SOGoUserDefaults *ud;
|
||||||
|
|
||||||
roles = [[context activeUser] rolesForObject: self inContext: context];
|
roles = [[context activeUser] rolesForObject: self inContext: context];
|
||||||
folderOwner = [SOGoUser userWithLogin: [self ownerInContext: context]];
|
folderOwner = [SOGoUser userWithLogin: [self ownerInContext: context]];
|
||||||
|
|
||||||
|
|
||||||
// We autocreate the calendars if the user is the owner, a superuser or
|
// We autocreate the calendars if the user is the owner, a superuser or
|
||||||
// if it's a resource as we won't necessarily want to login as a resource
|
// if it's a resource as we won't necessarily want to login as a resource
|
||||||
// in order to create its database tables.
|
// in order to create its database tables.
|
||||||
// FolderType is an enum where 0 = Personal and 1 = collected
|
// FolderType is an enum where 0 = Personal and 1 = collected
|
||||||
if ([roles containsObject: SoRole_Owner] ||
|
if ([roles containsObject: SoRole_Owner] ||
|
||||||
(folderOwner && [folderOwner isResource]))
|
(folderOwner && [folderOwner isResource]))
|
||||||
|
{
|
||||||
|
if (folderType == SOGoPersonalFolder)
|
||||||
{
|
{
|
||||||
if (folderType == SOGoPersonalFolder)
|
folderName = @"personal";
|
||||||
{
|
folder = [subFolderClass objectWithName: folderName inContainer: self];
|
||||||
folderName = @"personal";
|
[folder setDisplayName: [self defaultFolderName]];
|
||||||
folder = [subFolderClass objectWithName: folderName inContainer: self];
|
|
||||||
[folder setDisplayName: [self defaultFolderName]];
|
|
||||||
}
|
|
||||||
else if (folderType == SOGoCollectedFolder)
|
|
||||||
{
|
|
||||||
folderName = @"collected";
|
|
||||||
folder = [subFolderClass objectWithName: folderName inContainer: self];
|
|
||||||
[folder setDisplayName: [self collectedFolderName]];
|
|
||||||
}
|
|
||||||
[folder setOCSPath: [NSString stringWithFormat: @"%@/%@", OCSPath, folderName]];
|
[folder setOCSPath: [NSString stringWithFormat: @"%@/%@", OCSPath, folderName]];
|
||||||
|
|
||||||
if ([folder create])
|
if ([folder create])
|
||||||
[subFolders setObject: folder forKey: folderName];
|
[subFolders setObject: folder forKey: folderName];
|
||||||
}
|
}
|
||||||
|
else if (folderType == SOGoCollectedFolder)
|
||||||
|
{
|
||||||
|
ud = [[context activeUser] userDefaults];
|
||||||
|
if ([ud mailAddOutgoingAddresses]) {
|
||||||
|
folderName = @"collected";
|
||||||
|
folder = [subFolderClass objectWithName: folderName inContainer: self];
|
||||||
|
[folder setDisplayName: [self collectedFolderName]];
|
||||||
|
[folder setOCSPath: [NSString stringWithFormat: @"%@/%@", OCSPath, folderName]];
|
||||||
|
|
||||||
|
if ([folder create])
|
||||||
|
[subFolders setObject: folder forKey: folderName];
|
||||||
|
|
||||||
|
[ud setSelectedAddressBook:folderName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSException *) fetchSpecialFolders: (NSString *) sql
|
- (NSException *) fetchSpecialFolders: (NSString *) sql
|
||||||
|
|
|
@ -401,7 +401,17 @@ class DAVCalendarAclTest(DAVAclTest):
|
||||||
else:
|
else:
|
||||||
expStatus = 207
|
expStatus = 207
|
||||||
privileges = self._currentUserPrivilegeSet(self.resource, expStatus)
|
privileges = self._currentUserPrivilegeSet(self.resource, expStatus)
|
||||||
self._comparePrivilegeSets(expectedPrivileges, privileges)
|
|
||||||
|
# When comparing privileges on DAV collection, we remove all 'default'
|
||||||
|
# privileges on the collection.
|
||||||
|
privileges_set = set(privileges);
|
||||||
|
for x in ("public", "private", "confidential"):
|
||||||
|
privileges_set.discard("{urn:inverse:params:xml:ns:inverse-dav}viewwhole-%s-records" % x)
|
||||||
|
privileges_set.discard("{urn:inverse:params:xml:ns:inverse-dav}viewdant-%s-records" % x)
|
||||||
|
privileges_set.discard("{urn:inverse:params:xml:ns:inverse-dav}modify-%s-records" % x)
|
||||||
|
privileges_set.discard("{urn:inverse:params:xml:ns:inverse-dav}respondto-%s-records" %x)
|
||||||
|
|
||||||
|
self._comparePrivilegeSets(expectedPrivileges, list(privileges_set))
|
||||||
|
|
||||||
def _testEventDAVAcl(self, event_class, right, error_code):
|
def _testEventDAVAcl(self, event_class, right, error_code):
|
||||||
icsClass = self.classToICSClass[event_class].lower()
|
icsClass = self.classToICSClass[event_class].lower()
|
||||||
|
|
|
@ -419,9 +419,9 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) _appendStyle: (unichar *) _chars
|
- (void) _appendStyle: (unichar *) _chars
|
||||||
length: (int) _len
|
length: (NSUInteger) _len
|
||||||
{
|
{
|
||||||
unsigned int count, length;
|
NSUInteger count, length;
|
||||||
unichar *start, *currentChar;
|
unichar *start, *currentChar;
|
||||||
|
|
||||||
start = _chars;
|
start = _chars;
|
||||||
|
@ -688,7 +688,7 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) characters: (unichar *) _chars
|
- (void) characters: (unichar *) _chars
|
||||||
length: (int) _len
|
length: (NSUInteger) _len
|
||||||
{
|
{
|
||||||
showWhoWeAre();
|
showWhoWeAre();
|
||||||
if (!ignoredContent)
|
if (!ignoredContent)
|
||||||
|
@ -712,7 +712,7 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) ignorableWhitespace: (unichar *) _chars
|
- (void) ignorableWhitespace: (unichar *) _chars
|
||||||
length: (int) _len
|
length: (NSUInteger) _len
|
||||||
{
|
{
|
||||||
showWhoWeAre();
|
showWhoWeAre();
|
||||||
}
|
}
|
||||||
|
@ -735,7 +735,7 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||||
|
|
||||||
/* SaxLexicalHandler */
|
/* SaxLexicalHandler */
|
||||||
- (void) comment: (unichar *) _chars
|
- (void) comment: (unichar *) _chars
|
||||||
length: (int) _len
|
length: (NSUInteger) _len
|
||||||
{
|
{
|
||||||
showWhoWeAre();
|
showWhoWeAre();
|
||||||
if (inStyle)
|
if (inStyle)
|
||||||
|
|
|
@ -644,8 +644,9 @@ static NSArray *infoKeys = nil;
|
||||||
if (!attachmentAttrs || ![co imap4URL])
|
if (!attachmentAttrs || ![co imap4URL])
|
||||||
{
|
{
|
||||||
[co fetchInfo];
|
[co fetchInfo];
|
||||||
if (![co inReplyTo] && [co IMAP4ID] > -1)
|
if ((![co inReplyTo] || currentAttachment) && [co IMAP4ID] > -1)
|
||||||
{
|
{
|
||||||
|
// When currentAttachment is defined, it means we just attached a new file to the mail
|
||||||
mail = [[[SOGoMailObject alloc] initWithImap4URL: [co imap4URL] inContainer: [co container]] autorelease];
|
mail = [[[SOGoMailObject alloc] initWithImap4URL: [co imap4URL] inContainer: [co container]] autorelease];
|
||||||
a = [mail fetchFileAttachmentKeys];
|
a = [mail fetchFileAttachmentKeys];
|
||||||
ASSIGN (attachmentAttrs, a);
|
ASSIGN (attachmentAttrs, a);
|
||||||
|
|
|
@ -74,7 +74,7 @@ static NSArray *reminderValues = nil;
|
||||||
if (!reminderItems && !reminderValues)
|
if (!reminderItems && !reminderValues)
|
||||||
{
|
{
|
||||||
reminderItems = [NSArray arrayWithObjects:
|
reminderItems = [NSArray arrayWithObjects:
|
||||||
@"5_MINUTES_BEFORE",
|
@"5_MINUTES_BEFORE",
|
||||||
@"10_MINUTES_BEFORE",
|
@"10_MINUTES_BEFORE",
|
||||||
@"15_MINUTES_BEFORE",
|
@"15_MINUTES_BEFORE",
|
||||||
@"30_MINUTES_BEFORE",
|
@"30_MINUTES_BEFORE",
|
||||||
|
@ -90,7 +90,7 @@ static NSArray *reminderValues = nil;
|
||||||
@"1_WEEK_BEFORE",
|
@"1_WEEK_BEFORE",
|
||||||
nil];
|
nil];
|
||||||
reminderValues = [NSArray arrayWithObjects:
|
reminderValues = [NSArray arrayWithObjects:
|
||||||
@"-PT5M",
|
@"-PT5M",
|
||||||
@"-PT10M",
|
@"-PT10M",
|
||||||
@"-PT15M",
|
@"-PT15M",
|
||||||
@"-PT30M",
|
@"-PT30M",
|
||||||
|
@ -699,49 +699,53 @@ static NSArray *reminderValues = nil;
|
||||||
/* We want all the SourceIDS */
|
/* We want all the SourceIDS */
|
||||||
NSMutableArray *folders, *availableAddressBooksID, *availableAddressBooksName;
|
NSMutableArray *folders, *availableAddressBooksID, *availableAddressBooksName;
|
||||||
SOGoParentFolder *contactFolders;
|
SOGoParentFolder *contactFolders;
|
||||||
|
|
||||||
int i, count;
|
int i, count;
|
||||||
BOOL collectedAlreadyExist;
|
BOOL collectedAlreadyExist;
|
||||||
|
|
||||||
contactFolders = [[[context activeUser] homeFolderInContext: context]
|
contactFolders = [[[context activeUser] homeFolderInContext: context]
|
||||||
lookupName: @"Contacts"
|
lookupName: @"Contacts"
|
||||||
inContext: context
|
inContext: context
|
||||||
acquire: NO];
|
acquire: NO];
|
||||||
folders = [NSMutableArray arrayWithArray: [contactFolders subFolders]];
|
folders = [NSMutableArray arrayWithArray: [contactFolders subFolders]];
|
||||||
count = [folders count]-1;
|
count = [folders count]-1;
|
||||||
|
|
||||||
// Inside this loop we remove all the public or shared addressbooks
|
// Inside this loop we remove all the public or shared addressbooks
|
||||||
for (; count >= 0; count--)
|
for (; count >= 0; count--)
|
||||||
{
|
{
|
||||||
if (![[folders objectAtIndex: count] isKindOfClass: [SOGoContactGCSFolder class]])
|
if (![[folders objectAtIndex: count] isKindOfClass: [SOGoContactGCSFolder class]])
|
||||||
[folders removeObjectAtIndex: count];
|
[folders removeObjectAtIndex: count];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the objects in order to have only the displayName of the addressbooks to be displayed on the preferences interface
|
// Parse the objects in order to have only the displayName of the addressbooks to be displayed on the preferences interface
|
||||||
availableAddressBooksID = [NSMutableArray arrayWithCapacity: [folders count]];
|
availableAddressBooksID = [NSMutableArray arrayWithCapacity: [folders count]];
|
||||||
availableAddressBooksName = [NSMutableArray arrayWithCapacity: [folders count]];
|
availableAddressBooksName = [NSMutableArray arrayWithCapacity: [folders count]];
|
||||||
count = [folders count]-1;
|
count = [folders count]-1;
|
||||||
collectedAlreadyExist = NO;
|
collectedAlreadyExist = NO;
|
||||||
|
|
||||||
for (i = 0; i <= count ; i++) {
|
for (i = 0; i <= count ; i++)
|
||||||
[availableAddressBooksID addObject:[[folders objectAtIndex:i] realNameInContainer]];
|
{
|
||||||
[availableAddressBooksName addObject:[[folders objectAtIndex:i] displayName]];
|
[availableAddressBooksID addObject:[[folders objectAtIndex:i] realNameInContainer]];
|
||||||
|
[availableAddressBooksName addObject:[[folders objectAtIndex:i] displayName]];
|
||||||
if ([[availableAddressBooksID objectAtIndex:i] isEqualToString: @"collected"])
|
|
||||||
collectedAlreadyExist = YES;
|
if ([[availableAddressBooksID objectAtIndex:i] isEqualToString: @"collected"])
|
||||||
}
|
collectedAlreadyExist = YES;
|
||||||
|
}
|
||||||
// Create the dictionary for the next function : itemAddressBookText.
|
// Create the dictionary for the next function : itemAddressBookText.
|
||||||
if (!addressBooksIDWithDisplayName)
|
if (!addressBooksIDWithDisplayName)
|
||||||
addressBooksIDWithDisplayName = [[NSMutableDictionary alloc] initWithObjects:availableAddressBooksName
|
{
|
||||||
forKeys:availableAddressBooksID];
|
addressBooksIDWithDisplayName = [[NSMutableDictionary alloc] initWithObjects:availableAddressBooksName
|
||||||
if (!collectedAlreadyExist)
|
forKeys:availableAddressBooksID];
|
||||||
|
}
|
||||||
|
if (!collectedAlreadyExist)
|
||||||
{
|
{
|
||||||
[availableAddressBooksID addObject: @"collected"];
|
[availableAddressBooksID addObject: @"collected"];
|
||||||
[addressBooksIDWithDisplayName setObject: [self labelForKey: @"Collected Address Book"] forKey: @"collected"];
|
[addressBooksIDWithDisplayName setObject: [self labelForKey: @"Collected Address Book"] forKey: @"collected"];
|
||||||
}
|
}
|
||||||
|
|
||||||
return availableAddressBooksID;
|
return availableAddressBooksID;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *) itemAddressBookText
|
- (NSString *) itemAddressBookText
|
||||||
{
|
{
|
||||||
return [addressBooksIDWithDisplayName objectForKey: item];
|
return [addressBooksIDWithDisplayName objectForKey: item];
|
||||||
|
@ -929,7 +933,7 @@ static NSArray *reminderValues = nil;
|
||||||
capabilities = [[self sieveClient] capabilities];
|
capabilities = [[self sieveClient] capabilities];
|
||||||
else
|
else
|
||||||
capabilities = [NSArray array];
|
capabilities = [NSArray array];
|
||||||
[capabilities retain];
|
[capabilities retain];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [capabilities jsonRepresentation];
|
return [capabilities jsonRepresentation];
|
||||||
|
@ -1220,12 +1224,12 @@ static NSArray *reminderValues = nil;
|
||||||
SOGoSieveManager *manager;
|
SOGoSieveManager *manager;
|
||||||
|
|
||||||
if (!client)
|
if (!client)
|
||||||
{
|
{
|
||||||
folder = [[self clientObject] mailAccountsFolder: @"Mail" inContext: context];
|
folder = [[self clientObject] mailAccountsFolder: @"Mail" inContext: context];
|
||||||
account = [folder lookupName: @"0" inContext: context acquire: NO];
|
account = [folder lookupName: @"0" inContext: context acquire: NO];
|
||||||
manager = [SOGoSieveManager sieveManagerForUser: [context activeUser]];
|
manager = [SOGoSieveManager sieveManagerForUser: [context activeUser]];
|
||||||
client = [[manager clientForAccount: account] retain];
|
client = [[manager clientForAccount: account] retain];
|
||||||
}
|
}
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
@ -1273,9 +1277,9 @@ static NSArray *reminderValues = nil;
|
||||||
results = [self responseWithStatus: 502
|
results = [self responseWithStatus: 502
|
||||||
andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: @"Connection error", @"textStatus", nil]];
|
andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: @"Connection error", @"textStatus", nil]];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
results = [self responseWithStatus: 503
|
results = [self responseWithStatus: 503
|
||||||
andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: @"Service temporarily unavailable", @"textStatus", nil]];
|
andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: @"Service temporarily unavailable", @"textStatus", nil]];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
results = self;
|
results = self;
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
/* UIxCalListingActions.m - this file is part of SOGo
|
/* UIxCalListingActions.m - this file is part of SOGo
|
||||||
*
|
*
|
||||||
* Copyright (C) 2006-2011 Inverse inc.
|
* Copyright (C) 2006-2014 Inverse inc.
|
||||||
*
|
|
||||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
|
||||||
* Francis Lachapelle <flachapelle@inverse.ca>
|
|
||||||
*
|
*
|
||||||
* This file is free software; you can redistribute it and/or modify
|
* This file is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -484,7 +481,7 @@ static NSArray *tasksFields = nil;
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (WOResponse *) _responseWithData: (NSArray *) data
|
- (WOResponse *) _responseWithData: (id) data
|
||||||
{
|
{
|
||||||
WOResponse *response;
|
WOResponse *response;
|
||||||
|
|
||||||
|
@ -1282,4 +1279,28 @@ _computeBlocksPosition (NSArray *blocks)
|
||||||
return [self _responseWithData: filteredTasks];
|
return [self _responseWithData: filteredTasks];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (WOResponse *) activeTasksAction
|
||||||
|
{
|
||||||
|
NSMutableDictionary *activeTasksByCalendars;
|
||||||
|
SOGoAppointmentFolder *folder;
|
||||||
|
SOGoAppointmentFolders *co;
|
||||||
|
NSArray *folders;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
co = [self clientObject];
|
||||||
|
folders = [co subFolders];
|
||||||
|
activeTasksByCalendars = [NSMutableDictionary dictionaryWithCapacity: [folders count]];
|
||||||
|
|
||||||
|
for (i = 0; i < [folders count]; i++)
|
||||||
|
{
|
||||||
|
folder = [folders objectAtIndex: i];
|
||||||
|
|
||||||
|
[activeTasksByCalendars setObject: [folder activeTasks]
|
||||||
|
forKey: [folder nameInContainer]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [self _responseWithData: activeTasksByCalendars];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -93,8 +93,8 @@ _intValueFromHex (NSString *hexString)
|
||||||
NSMutableDictionary *calendar;
|
NSMutableDictionary *calendar;
|
||||||
unsigned int count, max;
|
unsigned int count, max;
|
||||||
NSString *folderName, *fDisplayName;
|
NSString *folderName, *fDisplayName;
|
||||||
NSNumber *isActive;
|
NSNumber *isActive, *fActiveTasks;
|
||||||
|
|
||||||
if (!calendars)
|
if (!calendars)
|
||||||
{
|
{
|
||||||
co = [self clientObject];
|
co = [self clientObject];
|
||||||
|
@ -120,6 +120,8 @@ _intValueFromHex (NSString *hexString)
|
||||||
[calendar setObject: isActive forKey: @"active"];
|
[calendar setObject: isActive forKey: @"active"];
|
||||||
[calendar setObject: [folder ownerInContext: context]
|
[calendar setObject: [folder ownerInContext: context]
|
||||||
forKey: @"owner"];
|
forKey: @"owner"];
|
||||||
|
fActiveTasks = [folder activeTasks];
|
||||||
|
[calendar setObject:fActiveTasks forKey:@"activeTasks" ];
|
||||||
[calendars addObject: calendar];
|
[calendars addObject: calendar];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,11 @@
|
||||||
actionClass = "UIxCalListingActions";
|
actionClass = "UIxCalListingActions";
|
||||||
actionName = "tasksList";
|
actionName = "tasksList";
|
||||||
};
|
};
|
||||||
|
activeTasks = {
|
||||||
|
protectedBy = "View";
|
||||||
|
actionClass = "UIxCalListingActions";
|
||||||
|
actionName = "activeTasks";
|
||||||
|
};
|
||||||
dayview = {
|
dayview = {
|
||||||
protectedBy = "View";
|
protectedBy = "View";
|
||||||
pageName = "UIxCalDayView";
|
pageName = "UIxCalDayView";
|
||||||
|
|
|
@ -299,7 +299,8 @@
|
||||||
<dd><input type="checkbox"
|
<dd><input type="checkbox"
|
||||||
const:name="addOutgoingAddresses"
|
const:name="addOutgoingAddresses"
|
||||||
const:id="addOutgoingAddresses"
|
const:id="addOutgoingAddresses"
|
||||||
var:checked="addOutgoingAddresses" />
|
var:checked="addOutgoingAddresses"
|
||||||
|
onChange = "onAddOutgoingAddressesCheck(this);"/>
|
||||||
<var:string label:value="When sending mail, add unknown recipients to my"/><br/>
|
<var:string label:value="When sending mail, add unknown recipients to my"/><br/>
|
||||||
<var:popup list="addressBookList" item="item"
|
<var:popup list="addressBookList" item="item"
|
||||||
const:id="addressBookList"
|
const:id="addressBookList"
|
||||||
|
|
|
@ -38,9 +38,10 @@ div.colorBox.calendarFolder<var:string value="currentCalendar.folder" />
|
||||||
<var:foreach list="calendars" item="currentCalendar"
|
<var:foreach list="calendars" item="currentCalendar"
|
||||||
><li class="denied" var:id="currentCalendar.id" var:owner="currentCalendar.owner" >
|
><li class="denied" var:id="currentCalendar.id" var:owner="currentCalendar.owner" >
|
||||||
<input type="checkbox" class="checkBox" const:disabled="disabled" var:checked="currentCalendar.active" />
|
<input type="checkbox" class="checkBox" const:disabled="disabled" var:checked="currentCalendar.active" />
|
||||||
<div var:class="currentCalendarClass"><entity name="nbsp"/></div
|
<div var:class="currentCalendarClass"><entity name="nbsp"/></div>
|
||||||
><var:string value="currentCalendar.displayName"
|
<var:string value="currentCalendar.displayName" />
|
||||||
/></li>
|
<span id="activeTasks"><var:string value="currentCalendar.activeTasks"/></span>
|
||||||
|
</li>
|
||||||
</var:foreach>
|
</var:foreach>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1749,7 +1749,7 @@ function download(url) {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
form.remove();
|
form.remove();
|
||||||
div.remove();
|
div.remove();
|
||||||
}, 2000);
|
}, 20000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveAttachment(event) {
|
function saveAttachment(event) {
|
||||||
|
|
|
@ -1102,142 +1102,166 @@ function eventsListCallback(http) {
|
||||||
log ("eventsListCallback Ajax error");
|
log ("eventsListCallback Ajax error");
|
||||||
}
|
}
|
||||||
|
|
||||||
function tasksListCallback(http) {
|
function activeTasksCallback(http) {
|
||||||
if (http.readyState == 4
|
if (http.readyState == 4 && http.status == 200) {
|
||||||
&& http.status == 200) {
|
if (http.responseText.length > 0) {
|
||||||
var div = $("tasksListView");
|
var data = http.responseText.evalJSON(true);
|
||||||
document.tasksListAjaxRequest = null;
|
var list = $("calendarList");
|
||||||
var table = $("tasksList");
|
|
||||||
lastClickedRow = -1; // from generic.js
|
var items = list.childNodesWithTag("li");
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
var rows = table.select("TBODY TR");
|
var id = items[i].getAttribute("id").substr(1);
|
||||||
rows.each(function(e) {
|
var number = data[id];
|
||||||
e.remove();
|
var input = items[i].childNodesWithTag("input")[0];
|
||||||
});
|
var activeTasks = items[i].childNodesWithTag("span")[0];
|
||||||
|
$(input).observe("click", clickEventWrapper(updateCalendarStatus));
|
||||||
if (http.responseText.length > 0) {
|
if (number == "0") {
|
||||||
var data = http.responseText.evalJSON(true);
|
activeTasks.innerHTML = "";
|
||||||
|
|
||||||
// [0] Task ID
|
|
||||||
// [1] Calendar ID
|
|
||||||
// [2] Calendar name
|
|
||||||
// [3] Status (0, 1 = completed, 2)
|
|
||||||
// [4] Title
|
|
||||||
// [5] Due date (int)
|
|
||||||
// [6] Classification (0 = public, 1, = private, 2 = confidential)
|
|
||||||
// [7] Location
|
|
||||||
// [8] Category
|
|
||||||
// [9] Editable?
|
|
||||||
// [10] Erasable?
|
|
||||||
// [11] Priority (0, 1 = important, 9 = low)
|
|
||||||
// [12] Status CSS class (duelater, completed, etc)
|
|
||||||
// (13) Due date (formatted)
|
|
||||||
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
|
||||||
var row = createElement("tr");
|
|
||||||
table.tBodies[0].appendChild(row);
|
|
||||||
|
|
||||||
row.on("dblclick", editDoubleClickedEvent);
|
|
||||||
|
|
||||||
var calendar = escape(data[i][1]);
|
|
||||||
var cname = escape(data[i][0]);
|
|
||||||
row.setAttribute("id", calendar + "-" + cname);
|
|
||||||
//listItem.addClassName(data[i][5]); // Classification
|
|
||||||
row.addClassName(data[i][12]); // status
|
|
||||||
row.calendar = calendar;
|
|
||||||
row.cname = cname;
|
|
||||||
row.erasable = data[i][10] || IsSuperUser;
|
|
||||||
if (parseInt(data[i][11]) == 1) {
|
|
||||||
row.addClassName("important");
|
|
||||||
}
|
|
||||||
else if (parseInt(data[i][11]) == 9) {
|
|
||||||
row.addClassName("low");
|
|
||||||
}
|
|
||||||
|
|
||||||
var cell = createElement("td");
|
|
||||||
row.appendChild(cell);
|
|
||||||
var input = createElement("input");
|
|
||||||
input.setAttribute("type", "checkbox");
|
|
||||||
cell.appendChild(input);
|
|
||||||
input.setAttribute("value", "1");
|
|
||||||
if (parseInt(data[i][9]) == 0) // editable?
|
|
||||||
input.setAttribute("disabled", true);
|
|
||||||
input.addClassName("checkBox");
|
|
||||||
if (parseInt(data[i][3]) == 1) // completed?
|
|
||||||
input.setAttribute("checked", "checked");
|
|
||||||
input.observe("click", updateTaskStatus, true);
|
|
||||||
|
|
||||||
cell = createElement("td");
|
|
||||||
row.appendChild(cell);
|
|
||||||
if (data[i][11] != null) {
|
|
||||||
cell.update(_("prio_" + data[i][11])); // Priority
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cell.update(""); // Priority
|
|
||||||
}
|
|
||||||
|
|
||||||
cell = createElement("td");
|
|
||||||
row.appendChild(cell);
|
|
||||||
var colorDiv = createElement("div", false, "colorBox calendarFolder" + calendar);
|
|
||||||
cell.appendChild(colorDiv);
|
|
||||||
colorDiv.update(' ');
|
|
||||||
var t = new Element ("span");
|
|
||||||
cell.appendChild(t);
|
|
||||||
t.update(data[i][4]); // title
|
|
||||||
|
|
||||||
cell = createElement("td");
|
|
||||||
row.appendChild(cell);
|
|
||||||
if (data[i][13])
|
|
||||||
cell.update(data[i][13]); // end date
|
|
||||||
|
|
||||||
cell = createElement("td");
|
|
||||||
row.appendChild(cell);
|
|
||||||
cell.update(data[i][7]); // location
|
|
||||||
|
|
||||||
cell = createElement("td");
|
|
||||||
row.appendChild(cell);
|
|
||||||
cell.update(data[i][8]); // category
|
|
||||||
|
|
||||||
cell = createElement("td");
|
|
||||||
row.appendChild(cell);
|
|
||||||
cell.update(data[i][2]); // calendar name
|
|
||||||
}
|
|
||||||
|
|
||||||
table.scrollTop = table.previousScroll;
|
|
||||||
|
|
||||||
if (sorting["task-attribute"] && sorting["task-attribute"].length > 0) {
|
|
||||||
var sortHeader = $(sorting["task-header"]);
|
|
||||||
|
|
||||||
if (sortHeader) {
|
|
||||||
var sortImages = $(table.tHead).select(".sortImage");
|
|
||||||
$(sortImages).each(function(item) {
|
|
||||||
item.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
var sortImage = createElement("img", "messageSortImage", "sortImage");
|
|
||||||
sortHeader.insertBefore(sortImage, sortHeader.firstChild);
|
|
||||||
if (sorting["task-ascending"])
|
|
||||||
sortImage.src = ResourcesURL + "/arrow-up.png";
|
|
||||||
else
|
|
||||||
sortImage.src = ResourcesURL + "/arrow-down.png";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (http.callbackData) {
|
|
||||||
var selectedNodesId = http.callbackData;
|
|
||||||
for (var i = 0; i < selectedNodesId.length; i++) {
|
|
||||||
// log(selectedNodesId[i] + " (" + i + ") is selected");
|
|
||||||
var node = $(selectedNodesId[i]);
|
|
||||||
if (node) {
|
|
||||||
node.selectElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
log ("tasksListCallback: no data");
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
activeTasks.innerHTML = "(" + number + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
log ("tasksListCallback Ajax error");
|
}
|
||||||
|
|
||||||
|
function tasksListCallback(http) {
|
||||||
|
if (http.readyState == 4
|
||||||
|
&& http.status == 200) {
|
||||||
|
var div = $("tasksListView");
|
||||||
|
document.tasksListAjaxRequest = null;
|
||||||
|
var table = $("tasksList");
|
||||||
|
lastClickedRow = -1; // from generic.js
|
||||||
|
|
||||||
|
var rows = table.select("TBODY TR");
|
||||||
|
rows.each(function(e) {
|
||||||
|
e.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (http.responseText.length > 0) {
|
||||||
|
var data = http.responseText.evalJSON(true);
|
||||||
|
|
||||||
|
// [0] Task ID
|
||||||
|
// [1] Calendar ID
|
||||||
|
// [2] Calendar name
|
||||||
|
// [3] Status (0, 1 = completed, 2)
|
||||||
|
// [4] Title
|
||||||
|
// [5] Due date (int)
|
||||||
|
// [6] Classification (0 = public, 1, = private, 2 = confidential)
|
||||||
|
// [7] Location
|
||||||
|
// [8] Category
|
||||||
|
// [9] Editable?
|
||||||
|
// [10] Erasable?
|
||||||
|
// [11] Priority (0, 1 = important, 9 = low)
|
||||||
|
// [12] Status CSS class (duelater, completed, etc)
|
||||||
|
// (13) Due date (formatted)
|
||||||
|
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
var row = createElement("tr");
|
||||||
|
table.tBodies[0].appendChild(row);
|
||||||
|
|
||||||
|
row.on("dblclick", editDoubleClickedEvent);
|
||||||
|
|
||||||
|
var calendar = escape(data[i][1]);
|
||||||
|
var cname = escape(data[i][0]);
|
||||||
|
row.setAttribute("id", calendar + "-" + cname);
|
||||||
|
//listItem.addClassName(data[i][5]); // Classification
|
||||||
|
row.addClassName(data[i][12]); // status
|
||||||
|
row.calendar = calendar;
|
||||||
|
row.cname = cname;
|
||||||
|
row.erasable = data[i][10] || IsSuperUser;
|
||||||
|
if (parseInt(data[i][11]) == 1) {
|
||||||
|
row.addClassName("important");
|
||||||
|
}
|
||||||
|
else if (parseInt(data[i][11]) == 9) {
|
||||||
|
row.addClassName("low");
|
||||||
|
}
|
||||||
|
|
||||||
|
var cell = createElement("td");
|
||||||
|
row.appendChild(cell);
|
||||||
|
var input = createElement("input");
|
||||||
|
input.setAttribute("type", "checkbox");
|
||||||
|
cell.appendChild(input);
|
||||||
|
input.setAttribute("value", "1");
|
||||||
|
if (parseInt(data[i][9]) == 0) // editable?
|
||||||
|
input.setAttribute("disabled", true);
|
||||||
|
input.addClassName("checkBox");
|
||||||
|
if (parseInt(data[i][3]) == 1) // completed?
|
||||||
|
input.setAttribute("checked", "checked");
|
||||||
|
input.observe("click", updateTaskStatus, true);
|
||||||
|
|
||||||
|
cell = createElement("td");
|
||||||
|
row.appendChild(cell);
|
||||||
|
if (data[i][11] != null) {
|
||||||
|
cell.update(_("prio_" + data[i][11])); // Priority
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cell.update(""); // Priority
|
||||||
|
}
|
||||||
|
|
||||||
|
cell = createElement("td");
|
||||||
|
row.appendChild(cell);
|
||||||
|
var colorDiv = createElement("div", false, "colorBox calendarFolder" + calendar);
|
||||||
|
cell.appendChild(colorDiv);
|
||||||
|
colorDiv.update(' ');
|
||||||
|
var t = new Element ("span");
|
||||||
|
cell.appendChild(t);
|
||||||
|
t.update(data[i][4]); // title
|
||||||
|
|
||||||
|
cell = createElement("td");
|
||||||
|
row.appendChild(cell);
|
||||||
|
if (data[i][13])
|
||||||
|
cell.update(data[i][13]); // end date
|
||||||
|
|
||||||
|
cell = createElement("td");
|
||||||
|
row.appendChild(cell);
|
||||||
|
cell.update(data[i][7]); // location
|
||||||
|
|
||||||
|
cell = createElement("td");
|
||||||
|
row.appendChild(cell);
|
||||||
|
cell.update(data[i][8]); // category
|
||||||
|
|
||||||
|
cell = createElement("td");
|
||||||
|
row.appendChild(cell);
|
||||||
|
cell.update(data[i][2]); // calendar name
|
||||||
|
}
|
||||||
|
|
||||||
|
table.scrollTop = table.previousScroll;
|
||||||
|
|
||||||
|
if (sorting["task-attribute"] && sorting["task-attribute"].length > 0) {
|
||||||
|
var sortHeader = $(sorting["task-header"]);
|
||||||
|
|
||||||
|
if (sortHeader) {
|
||||||
|
var sortImages = $(table.tHead).select(".sortImage");
|
||||||
|
$(sortImages).each(function(item) {
|
||||||
|
item.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
var sortImage = createElement("img", "messageSortImage", "sortImage");
|
||||||
|
sortHeader.insertBefore(sortImage, sortHeader.firstChild);
|
||||||
|
if (sorting["task-ascending"])
|
||||||
|
sortImage.src = ResourcesURL + "/arrow-up.png";
|
||||||
|
else
|
||||||
|
sortImage.src = ResourcesURL + "/arrow-down.png";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (http.callbackData) {
|
||||||
|
var selectedNodesId = http.callbackData;
|
||||||
|
for (var i = 0; i < selectedNodesId.length; i++) {
|
||||||
|
// log(selectedNodesId[i] + " (" + i + ") is selected");
|
||||||
|
var node = $(selectedNodesId[i]);
|
||||||
|
if (node) {
|
||||||
|
node.selectElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log ("tasksListCallback: no data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log ("tasksListCallback Ajax error");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* in dateselector */
|
/* in dateselector */
|
||||||
|
@ -2331,22 +2355,25 @@ function _loadEventHref(href) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _loadTasksHref(href) {
|
function _loadTasksHref(href) {
|
||||||
if (document.tasksListAjaxRequest) {
|
if (document.tasksListAjaxRequest) {
|
||||||
document.tasksListAjaxRequest.aborted = true;
|
document.tasksListAjaxRequest.aborted = true;
|
||||||
document.tasksListAjaxRequest.abort();
|
document.tasksListAjaxRequest.abort();
|
||||||
}
|
}
|
||||||
url = ApplicationBaseURL + "/" + href;
|
url = ApplicationBaseURL + "/" + href;
|
||||||
|
urlActiveTasks = ApplicationBaseURL + "/activeTasks";
|
||||||
var tasksList = $("tasksList");
|
|
||||||
var selectedIds;
|
var tasksList = $("tasksList");
|
||||||
if (tasksList)
|
var selectedIds;
|
||||||
selectedIds = tasksList.getSelectedNodesId();
|
if (tasksList)
|
||||||
else
|
selectedIds = tasksList.getSelectedNodesId();
|
||||||
selectedIds = null;
|
else
|
||||||
document.tasksListAjaxRequest
|
selectedIds = null;
|
||||||
= triggerAjaxRequest(url, tasksListCallback, selectedIds);
|
|
||||||
|
document.tasksListAjaxRequest = triggerAjaxRequest(url, tasksListCallback, selectedIds);
|
||||||
return true;
|
|
||||||
|
triggerAjaxRequest(urlActiveTasks, activeTasksCallback);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onHeaderClick(event) {
|
function onHeaderClick(event) {
|
||||||
|
@ -3038,27 +3065,35 @@ function configureDragHandles() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function initCalendarSelector() {
|
function initCalendarSelector() {
|
||||||
var selector = $("calendarSelector");
|
var selector = $("calendarSelector");
|
||||||
updateCalendarStatus(); // triggers the initial events refresh
|
updateCalendarStatus(); // triggers the initial events refresh
|
||||||
selector.changeNotification = updateCalendarsList;
|
selector.changeNotification = updateCalendarsList;
|
||||||
|
|
||||||
var list = $("calendarList");
|
var list = $("calendarList");
|
||||||
list.on("mousedown", onCalendarSelectionChange);
|
list.on("mousedown", onCalendarSelectionChange);
|
||||||
list.on("dblclick", onCalendarModify);
|
list.on("dblclick", onCalendarModify);
|
||||||
list.on("selectstart", listRowMouseDownHandler);
|
list.on("selectstart", listRowMouseDownHandler);
|
||||||
list.attachMenu("calendarsMenu");
|
list.attachMenu("calendarsMenu");
|
||||||
|
|
||||||
var items = list.childNodesWithTag("li");
|
var items = list.childNodesWithTag("li");
|
||||||
for (var i = 0; i < items.length; i++) {
|
for (var i = 0; i < items.length; i++) {
|
||||||
var input = items[i].childNodesWithTag("input")[0];
|
var input = items[i].childNodesWithTag("input")[0];
|
||||||
$(input).observe("click", clickEventWrapper(updateCalendarStatus));
|
var activeTasks = items[i].childNodesWithTag("span")[0];
|
||||||
|
$(input).observe("click", clickEventWrapper(updateCalendarStatus));
|
||||||
|
if (activeTasks.textContent == "0") {
|
||||||
|
activeTasks.innerHTML = "";
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
var links = $("calendarSelectorButtons").childNodesWithTag("a");
|
activeTasks.innerHTML = "(" + activeTasks.innerText + ")";
|
||||||
$(links[0]).observe("click", clickEventWrapper(onCalendarNew));
|
}
|
||||||
$(links[1]).observe("click", clickEventWrapper(onCalendarWebAdd));
|
|
||||||
$(links[2]).observe("click", clickEventWrapper(onCalendarAdd));
|
}
|
||||||
$(links[3]).observe("click", clickEventWrapper(onCalendarRemove));
|
|
||||||
|
var links = $("calendarSelectorButtons").childNodesWithTag("a");
|
||||||
|
$(links[0]).observe("click", clickEventWrapper(onCalendarNew));
|
||||||
|
$(links[1]).observe("click", clickEventWrapper(onCalendarWebAdd));
|
||||||
|
$(links[2]).observe("click", clickEventWrapper(onCalendarAdd));
|
||||||
|
$(links[3]).observe("click", clickEventWrapper(onCalendarRemove));
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCalendarSelectionChange(event) {
|
function onCalendarSelectionChange(event) {
|
||||||
|
|
|
@ -291,6 +291,7 @@ function initPreferences() {
|
||||||
$("vacationEndDate_date").disable();
|
$("vacationEndDate_date").disable();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
onAddOutgoingAddressesCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
function initSieveFilters() {
|
function initSieveFilters() {
|
||||||
|
@ -1172,8 +1173,15 @@ function serializeContactsCategories() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* / contact categories */
|
/* / contact categories */
|
||||||
|
|
||||||
|
function onAddOutgoingAddressesCheck(checkBox) {
|
||||||
|
if (!checkBox) {
|
||||||
|
checkBox = $("addOutgoingAddresses");
|
||||||
|
}
|
||||||
|
$("addressBookList").disabled = !checkBox.checked;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function onReplyPlacementListChange() {
|
function onReplyPlacementListChange() {
|
||||||
if ($("replyPlacementList").value == 0) {
|
if ($("replyPlacementList").value == 0) {
|
||||||
// Reply placement is above quote, signature can be place before of after quote
|
// Reply placement is above quote, signature can be place before of after quote
|
||||||
|
|
Loading…
Reference in New Issue