mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-09-29 00:50:33 +00:00
Merge to 2.2.6
This commit is contained in:
@@ -40,6 +40,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import <Contacts/NGVCard+SOGo.h>
|
||||
|
||||
#import <SOGo/NSString+Utilities.h>
|
||||
|
||||
#include "NSDate+ActiveSync.h"
|
||||
#include "NSString+ActiveSync.h"
|
||||
|
||||
@@ -199,6 +201,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[s appendFormat: @"<Data>%@</Data>", o];
|
||||
[s appendString: @"</Body>"];
|
||||
}
|
||||
|
||||
if ((o = [self photo]))
|
||||
[s appendFormat: @"<Picture xmlns=\"Contacts:\">%@</Picture>", o];
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -285,13 +290,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if ((o = [theValues objectForKey: @"Email1Address"]))
|
||||
{
|
||||
element = [self elementWithTag: @"email" ofType: @"work"];
|
||||
[element setSingleValue: o forKey: @""];
|
||||
[element setSingleValue: [o pureEMailAddress] forKey: @""];
|
||||
}
|
||||
|
||||
if ((o = [theValues objectForKey: @"Email2Address"]))
|
||||
{
|
||||
element = [self elementWithTag: @"email" ofType: @"home"];
|
||||
[element setSingleValue: o forKey: @""];
|
||||
[element setSingleValue: [o pureEMailAddress] forKey: @""];
|
||||
}
|
||||
|
||||
// SOGo currently only supports 2 email addresses ... but AS clients might send 3
|
||||
@@ -299,7 +304,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if ((o = [theValues objectForKey: @"Email3Address"]))
|
||||
{
|
||||
element = [self elementWithTag: @"email" ofType: @"three"];
|
||||
[element setSingleValue: o forKey: @""];
|
||||
[element setSingleValue: [o pureEMailAddress] forKey: @""];
|
||||
}
|
||||
|
||||
// Formatted name
|
||||
@@ -346,6 +351,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if ((o = [theValues objectForKey: @"NickName"]))
|
||||
[self setNickname: o];
|
||||
|
||||
if ((o = [theValues objectForKey: @"Picture"]))
|
||||
[self setPhoto: o];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -47,6 +47,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
- (NSCalendarDate *) calendarDate;
|
||||
- (NSString *) deviceId;
|
||||
- (NSString *) deviceType;
|
||||
- (NSString *) attachmentName;
|
||||
- (NSString *) command;
|
||||
- (NSData *) convertHexStringToBytes;
|
||||
|
||||
|
||||
@@ -186,6 +186,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
return s;
|
||||
}
|
||||
|
||||
// This method extracts the "AttachmentName" from a URI:
|
||||
//
|
||||
// /SOGo/Microsoft-Server-ActiveSync?Cmd=GetAttachment&User=sogo&DeviceId=HTCa04b4932597acd3f2dc1a918b9728&DeviceType=htcvision&AttachmentName=mail/TestFldr/8/2
|
||||
//
|
||||
- (NSString *) attachmentName
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [self _valueForParameter: @"ATTACHMENTNAME="];
|
||||
|
||||
if (!s)
|
||||
s = @"Unknown";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
@@ -123,8 +123,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
[[o properties] removeObjectForKey: @"SyncKey"];
|
||||
[[o properties] removeObjectForKey: @"SyncCache"];
|
||||
[[o properties] removeObjectForKey: @"DateCache"];
|
||||
[[o properties] removeObjectForKey: @"MoreAvailable"];
|
||||
|
||||
[[o properties] addEntriesFromDictionary: theFolderMetadata];
|
||||
[o save];
|
||||
@@ -476,20 +478,70 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
lastServerKey: (NSString **) theLastServerKey
|
||||
|
||||
{
|
||||
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
|
||||
NSMutableString *s;
|
||||
|
||||
BOOL more_available;
|
||||
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.
|
||||
// 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;
|
||||
|
||||
s = [NSMutableString string];
|
||||
|
||||
more_available = NO;
|
||||
|
||||
switch (theFolderType)
|
||||
@@ -602,12 +654,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[s appendString: @"</Add>"];
|
||||
}
|
||||
} // for ...
|
||||
|
||||
folderMetadata = [NSDictionary dictionaryWithObject: [theCollection davCollectionTag]
|
||||
forKey: @"SyncKey"];
|
||||
[self _setFolderMetadata: folderMetadata
|
||||
forKey: [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]]];
|
||||
}
|
||||
break;
|
||||
case ActiveSyncMailFolder:
|
||||
default:
|
||||
{
|
||||
NSMutableDictionary *syncCache, *dateCache, *folderMetadata;
|
||||
SOGoSyncCacheObject *lastCacheObject, *aCacheObject;
|
||||
NSMutableArray *allCacheObjects, *sortedBySequence;
|
||||
|
||||
@@ -637,6 +693,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
|
||||
[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
|
||||
// with the same name has been created but folderSync has not yet updated the cache
|
||||
if (!([[theCollection nameInContainer] isEqualToString:
|
||||
@@ -782,17 +839,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
}
|
||||
|
||||
if (more_available)
|
||||
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
|
||||
{
|
||||
[folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
|
||||
[folderMetadata setObject: *theLastServerKey forKey: @"SyncKey"];
|
||||
}
|
||||
else
|
||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||
{
|
||||
[folderMetadata removeObjectForKey: @"MoreAvailable"];
|
||||
[folderMetadata setObject: [theCollection davCollectionTag] forKey: @"SyncKey"];
|
||||
}
|
||||
|
||||
|
||||
[self _setFolderMetadata: folderMetadata
|
||||
forKey: [theCollection nameInContainer]];
|
||||
} // default:
|
||||
break;
|
||||
} // switch (folderType) ...
|
||||
|
||||
return_response:
|
||||
|
||||
if ([s length])
|
||||
{
|
||||
[theBuffer appendString: @"<Commands>"];
|
||||
@@ -996,7 +1060,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
davCollectionTag = [collection davCollectionTag];
|
||||
}
|
||||
|
||||
|
||||
// Generate the response buffer
|
||||
[theBuffer appendString: @"<Collection>"];
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#import <NGMime/NGMimeBodyPart.h>
|
||||
#import <NGMime/NGMimeFileData.h>
|
||||
#import <NGMime/NGMimeMultipartBody.h>
|
||||
#import <NGMime/NGMimeType.h>
|
||||
#import <NGMail/NGMimeMessageParser.h>
|
||||
#import <NGMail/NGMimeMessage.h>
|
||||
#import <NGMail/NGMimeMessageGenerator.h>
|
||||
@@ -122,6 +123,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
@interface SOGoActiveSyncDispatcher (Sync)
|
||||
|
||||
- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SOGoActiveSyncDispatcher
|
||||
|
||||
- (id) init
|
||||
@@ -164,6 +171,41 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
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
|
||||
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType
|
||||
{
|
||||
@@ -409,10 +451,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
if (!error)
|
||||
{
|
||||
NSString *syncKey, *key;
|
||||
SOGoCacheGCSObject *o;
|
||||
NSMutableString *s;
|
||||
NSString *syncKey;
|
||||
NSData *d;
|
||||
|
||||
|
||||
//
|
||||
// We mark the cache object as deleted
|
||||
//
|
||||
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], [folderToDelete nameInContainer]];
|
||||
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
[o delete];
|
||||
|
||||
//
|
||||
// We update the FolderSync's synckey
|
||||
//
|
||||
@@ -484,10 +536,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
// Handle new name exist
|
||||
if (!error)
|
||||
{
|
||||
NSString *syncKey, *key;
|
||||
SOGoCacheGCSObject *o;
|
||||
NSMutableString *s;
|
||||
NSString *syncKey;
|
||||
NSData *d;
|
||||
|
||||
//
|
||||
// We update our cache
|
||||
//
|
||||
key = [NSString stringWithFormat: @"%@+folder%@", [context objectForKey: @"DeviceId"], serverId];
|
||||
o = [SOGoCacheGCSObject objectWithName: key inContainer: nil];
|
||||
[o setTableUrl: [self folderTableURL]];
|
||||
[o reloadIfNeeded];
|
||||
|
||||
key = [NSString stringWithFormat: @"/%@+%@", [context objectForKey: @"DeviceId"], [folderToUpdate nameInContainer]];
|
||||
[o changePathTo: key];
|
||||
|
||||
//
|
||||
// We update the FolderSync's synckey
|
||||
//
|
||||
@@ -499,8 +563,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
[self _setFolderSyncKey: syncKey];
|
||||
|
||||
// FIXME - TODO: We *MUST* update the path in our cache! See -changePathTo in SOGoCacheGCSObject
|
||||
|
||||
s = [NSMutableString string];
|
||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
@@ -781,7 +843,51 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
- (void) processGetAttachment: (id <DOMElement>) theDocumentElement
|
||||
inResponse: (WOResponse *) theResponse
|
||||
{
|
||||
NSString *fileReference, *realCollectionId;
|
||||
|
||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||
|
||||
fileReference = [context objectForKey: @"AttachmentName"];
|
||||
|
||||
realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
|
||||
|
||||
if (folderType == ActiveSyncMailFolder)
|
||||
{
|
||||
id currentFolder, currentCollection, currentBodyPart;
|
||||
NSString *folderName, *messageName, *pathToPart;
|
||||
SOGoMailAccounts *accountsFolder;
|
||||
SOGoUserFolder *userFolder;
|
||||
SOGoMailObject *mailObject;
|
||||
|
||||
NSRange r1, r2;
|
||||
|
||||
r1 = [realCollectionId rangeOfString: @"/"];
|
||||
r2 = [realCollectionId rangeOfString: @"/" options: 0 range: NSMakeRange(NSMaxRange(r1)+1, [realCollectionId length]-NSMaxRange(r1)-1)];
|
||||
|
||||
folderName = [realCollectionId substringToIndex: r1.location];
|
||||
messageName = [realCollectionId substringWithRange: NSMakeRange(NSMaxRange(r1), r2.location-r1.location-1)];
|
||||
pathToPart = [realCollectionId substringFromIndex: r2.location+1];
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
currentFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||
|
||||
currentCollection = [currentFolder lookupName: [NSString stringWithFormat: @"folder%@", folderName]
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
|
||||
mailObject = [currentCollection lookupName: messageName inContext: context acquire: NO];
|
||||
currentBodyPart = [mailObject lookupImap4BodyPartKey: pathToPart inContext: context];
|
||||
|
||||
[theResponse setHeader: [NSString stringWithFormat: @"%@/%@", [[currentBodyPart partInfo] objectForKey: @"type"], [[currentBodyPart partInfo] objectForKey: @"subtype"]]
|
||||
forKey: @"Content-Type"];
|
||||
|
||||
[theResponse setContent: [currentBodyPart fetchBLOB] ];
|
||||
}
|
||||
else
|
||||
{
|
||||
[theResponse setStatus: 500];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -849,6 +955,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
sortOrdering: @"REVERSE ARRIVAL"
|
||||
threaded: NO];
|
||||
count = [uids count];
|
||||
|
||||
// Add the number of UIDs expected to "soft delete"
|
||||
count += [self _softDeleteCountWithFilter: filter collectionId: realCollectionId];
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -891,7 +1001,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
fileReference = [[[(id)[theDocumentElement getElementsByTagName: @"FileReference"] lastObject] textValue] stringByUnescapingURL];
|
||||
|
||||
realCollectionId = [fileReference realCollectionIdWithFolderType: &folderType];
|
||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||
|
||||
if (folderType == ActiveSyncMailFolder)
|
||||
{
|
||||
@@ -1218,30 +1327,73 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
}
|
||||
|
||||
//
|
||||
// Ping requests make a little sense because the request
|
||||
// doesn't contain the SyncKey on the client. So we can't
|
||||
// really know if something has changed on the server. What we
|
||||
// do for now is simply return Status=5 with the HeartbeatInterval
|
||||
// set at 60 seconds or we wait 60 seconds before responding with
|
||||
// Status=1
|
||||
// <?xml version="1.0"?>
|
||||
// <!DOCTYPE ActiveSync PUBLIC "-//MICROSOFT//DTD ActiveSync//EN" "http://www.microsoft.com/">
|
||||
// <Ping xmlns="Ping:">
|
||||
// <HeartbeatInterval>3540</HeartbeatInterval>
|
||||
// <Folders>
|
||||
// <Folder>
|
||||
// <Id>mail%2Fsogo_680f_193506d5_0</Id>
|
||||
// <Class>Email</Class>
|
||||
// </Folder>
|
||||
// <Folder>
|
||||
// <Id>vevent/personal</Id>
|
||||
// <Class>Calendar</Class>
|
||||
// </Folder>
|
||||
// <Folder>
|
||||
// <Id>vcard/personal</Id>
|
||||
// <Class>Contacts</Class>
|
||||
// </Folder>
|
||||
// <Folder>
|
||||
// <Id>mail%2Fsogo_680f_193506d5_1</Id>
|
||||
// <Class>Email</Class>
|
||||
// </Folder>
|
||||
// <Folder>
|
||||
// <Id>mail%2Fsogo_680f_193506d5_2</Id>
|
||||
// <Class>Email</Class>
|
||||
// </Folder>
|
||||
// <Folder>
|
||||
// <Id>vtodo/personal</Id>
|
||||
// <Class>Tasks</Class>
|
||||
// </Folder>
|
||||
// <Folder>
|
||||
// <Id>mail%2Fsogo_753e_193511a1_0</Id>
|
||||
// <Class>Email</Class>
|
||||
// </Folder>
|
||||
// <Folder>
|
||||
// <Id>mail%2Fsogo_753e_193511a1_1</Id>
|
||||
// <Class>Email</Class>
|
||||
// </Folder>
|
||||
// </Folders>
|
||||
// </Ping>
|
||||
//
|
||||
- (void) processPing: (id <DOMElement>) theDocumentElement
|
||||
inResponse: (WOResponse *) theResponse
|
||||
{
|
||||
NSString *collectionId, *realCollectionId, *syncKey;
|
||||
NSMutableArray *foldersWithChanges, *allFoldersID;
|
||||
SOGoMicrosoftActiveSyncFolderType folderType;
|
||||
NSMutableDictionary *folderMetadata;
|
||||
SOGoSystemDefaults *defaults;
|
||||
id <DOMElement> aCollection;
|
||||
NSArray *allCollections;
|
||||
|
||||
NSMutableString *s;
|
||||
id collection;
|
||||
NSData *d;
|
||||
|
||||
int heartbeatInterval, defaultInterval, status;
|
||||
|
||||
int i, j, heartbeatInterval, defaultInterval, internalInterval, status;
|
||||
|
||||
defaults = [SOGoSystemDefaults sharedSystemDefaults];
|
||||
defaultInterval = [defaults maximumPingInterval];
|
||||
internalInterval = [defaults internalSyncInterval];
|
||||
|
||||
if (theDocumentElement)
|
||||
heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue];
|
||||
else
|
||||
heartbeatInterval = defaultInterval;
|
||||
|
||||
|
||||
if (heartbeatInterval > defaultInterval || heartbeatInterval == 0)
|
||||
{
|
||||
heartbeatInterval = defaultInterval;
|
||||
@@ -1252,15 +1404,116 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
status = 1;
|
||||
}
|
||||
|
||||
NSLog(@"Got Ping request with valid interval - sleeping for %d seconds.", heartbeatInterval);
|
||||
sleep(heartbeatInterval);
|
||||
// We build the list of folders to "ping". When the payload is empty, we use the list
|
||||
// of "cached" folders.
|
||||
allCollections = (id)[theDocumentElement getElementsByTagName: @"Folders"];
|
||||
allFoldersID = [NSMutableArray array];
|
||||
|
||||
if (![allCollections count])
|
||||
{
|
||||
SOGoMailAccounts *accountsFolder;
|
||||
SOGoMailAccount *accountFolder;
|
||||
SOGoUserFolder *userFolder;
|
||||
NSArray *allValues;
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
|
||||
|
||||
allValues = [[accountFolder imapFolderGUIDs] allValues];
|
||||
|
||||
for (i = 0; i < [allValues count]; i++)
|
||||
[allFoldersID addObject: [NSString stringWithFormat: @"mail/%@", [allValues objectAtIndex: i]]];
|
||||
|
||||
|
||||
// FIXME: handle multiple GCS collecitons
|
||||
[allFoldersID addObject: @"vcard/personal"];
|
||||
[allFoldersID addObject: @"vevent/personal"];
|
||||
[allFoldersID addObject: @"vtodo/personal"];
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < [allCollections count]; i++)
|
||||
{
|
||||
aCollection = [allCollections objectAtIndex: i];
|
||||
collectionId = [[(id) [aCollection getElementsByTagName: @"Id"] lastObject] textValue];
|
||||
[allFoldersID addObject: collectionId];
|
||||
}
|
||||
}
|
||||
|
||||
foldersWithChanges = [NSMutableArray array];
|
||||
|
||||
// We enter our loop detection change
|
||||
for (i = 0; i < (heartbeatInterval/internalInterval); i++)
|
||||
{
|
||||
for (j = 0; j < [allFoldersID count]; j++)
|
||||
{
|
||||
collectionId = [allFoldersID objectAtIndex: j];
|
||||
realCollectionId = [collectionId realCollectionIdWithFolderType: &folderType];
|
||||
realCollectionId = [self globallyUniqueIDToIMAPFolderName: realCollectionId type: folderType];
|
||||
collection = [self collectionFromId: realCollectionId type: folderType];
|
||||
|
||||
switch (folderType)
|
||||
{
|
||||
case ActiveSyncContactFolder:
|
||||
folderMetadata = [self _folderMetadataForKey: [NSString stringWithFormat: @"vcard/%@", [collection nameInContainer]]];
|
||||
break;
|
||||
case ActiveSyncEventFolder:
|
||||
folderMetadata = [self _folderMetadataForKey: [NSString stringWithFormat: @"vevent/%@", [collection nameInContainer]]];
|
||||
break;
|
||||
case ActiveSyncTaskFolder:
|
||||
folderMetadata = [self _folderMetadataForKey: [NSString stringWithFormat: @"vtodo/%@", [collection nameInContainer]]];
|
||||
break;
|
||||
default:
|
||||
folderMetadata = [self _folderMetadataForKey: [collection nameInContainer]];
|
||||
}
|
||||
|
||||
syncKey = [folderMetadata objectForKey: @"SyncKey"];
|
||||
|
||||
if (![syncKey isEqualToString: [collection davCollectionTag]])
|
||||
{
|
||||
[foldersWithChanges addObject: collectionId];
|
||||
}
|
||||
}
|
||||
|
||||
if ([foldersWithChanges count])
|
||||
{
|
||||
NSLog(@"Change detected, we push the content.");
|
||||
status = 2;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Sleeping %d seconds while detecting changes...", internalInterval);
|
||||
sleep(internalInterval);
|
||||
}
|
||||
}
|
||||
|
||||
// We generate our response
|
||||
s = [NSMutableString string];
|
||||
[s appendString: @"<?xml version=\"1.0\" encoding=\"utf-8\"?>"];
|
||||
[s appendString: @"<!DOCTYPE ActiveSync PUBLIC \"-//MICROSOFT//DTD ActiveSync//EN\" \"http://www.microsoft.com/\">"];
|
||||
[s appendString: @"<Ping xmlns=\"Ping:\">"];
|
||||
[s appendFormat: @"<Status>%d</Status>", status];
|
||||
|
||||
if ([foldersWithChanges count])
|
||||
{
|
||||
[s appendString: @"<Folders>"];
|
||||
|
||||
for (i = 0; i < [foldersWithChanges count]; i++)
|
||||
{
|
||||
// A bit tricky here because we must call stringByEscapingURL on mail folders, but not on GCS ones.
|
||||
// We do the same thing in -processFolderSync
|
||||
collectionId = [foldersWithChanges objectAtIndex: i];
|
||||
|
||||
if ([collectionId hasPrefix: @"mail/"])
|
||||
collectionId = [collectionId stringByEscapingURL];
|
||||
|
||||
[s appendFormat: @"<Folder>%@</Folder>", collectionId];
|
||||
}
|
||||
|
||||
[s appendString: @"</Folders>"];
|
||||
}
|
||||
|
||||
if (status == 5)
|
||||
{
|
||||
@@ -1686,9 +1939,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
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.
|
||||
if (![value count])
|
||||
if ([value count])
|
||||
{
|
||||
[self processSendMail: theDocumentElement
|
||||
inResponse: theResponse];
|
||||
@@ -1708,11 +1961,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
NSData *data;
|
||||
|
||||
NGMimeMessageGenerator *generator;
|
||||
NGMimeBodyPart *bodyPart;
|
||||
NGMimeBodyPart *bodyPart;
|
||||
NGMutableHashMap *map;
|
||||
NGMimeFileData *fdata;
|
||||
NSException *error;
|
||||
id body;
|
||||
|
||||
id body, bodyFromSmartForward;
|
||||
|
||||
userFolder = [[context activeUser] homeFolderInContext: context];
|
||||
accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
|
||||
@@ -1728,10 +1982,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
parser = [[NGMimeMessageParser alloc] init];
|
||||
data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding];
|
||||
messageFromSmartForward = [parser parsePartFromData: data];
|
||||
|
||||
RELEASE(parser);
|
||||
|
||||
|
||||
// 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
|
||||
// "smart forwarded" message.
|
||||
@@ -1741,11 +1993,39 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] 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 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 setBody: [messageFromSmartForward body]];
|
||||
[bodyPart setBody: bodyFromSmartForward];
|
||||
[body addBodyPart: bodyPart];
|
||||
|
||||
// Second part
|
||||
@@ -1826,6 +2106,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
deviceId = [[theRequest uri] deviceId];
|
||||
[context setObject: deviceId forKey: @"DeviceId"];
|
||||
[context setObject: [[theRequest uri] deviceType] forKey: @"DeviceType"];
|
||||
[context setObject: [[theRequest uri] attachmentName] forKey: @"AttachmentName"];
|
||||
|
||||
|
||||
cmdName = [[theRequest uri] command];
|
||||
|
||||
@@ -1869,7 +2151,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
{
|
||||
// We check if it's a Ping command with no body.
|
||||
// See http://msdn.microsoft.com/en-us/library/ee200913(v=exchg.80).aspx for details
|
||||
if ([cmdName caseInsensitiveCompare: @"Ping"] != NSOrderedSame)
|
||||
if ([cmdName caseInsensitiveCompare: @"Ping"] != NSOrderedSame && [cmdName caseInsensitiveCompare: @"GetAttachment"] != NSOrderedSame)
|
||||
return [NSException exceptionWithHTTPStatus: 500];
|
||||
}
|
||||
|
||||
@@ -1923,9 +2205,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
if ([parts count] == 5)
|
||||
{
|
||||
/* If "OCSFolderInfoURL" is properly configured, we must have 5
|
||||
parts in this url. */
|
||||
parts in this url. We strip the '-' character in case we have
|
||||
this in the domain part - like foo@bar-zot.com */
|
||||
ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@",
|
||||
[[user loginInDomain] asCSSIdentifier]];
|
||||
[[[user loginInDomain] asCSSIdentifier]
|
||||
stringByReplacingOccurrencesOfString: @"-"
|
||||
withString: @"_"]];
|
||||
[parts replaceObjectAtIndex: 4 withObject: ocFSTableName];
|
||||
folderTableURL
|
||||
= [NSURL URLWithString: [parts componentsJoinedByString: @"/"]];
|
||||
|
||||
@@ -238,6 +238,8 @@ struct GlobalObjectId {
|
||||
|
||||
if (key)
|
||||
{
|
||||
NSString *s, *charset;
|
||||
|
||||
d = [[self fetchPlainTextParts] objectForKey: key];
|
||||
|
||||
encoding = [[self lookupInfoForBodyPart: key] objectForKey: @"encoding"];
|
||||
@@ -246,6 +248,14 @@ struct GlobalObjectId {
|
||||
d = [d dataByDecodingBase64];
|
||||
else if ([encoding caseInsensitiveCompare: @"quoted-printable"] == NSOrderedSame)
|
||||
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;
|
||||
@@ -296,13 +306,13 @@ struct GlobalObjectId {
|
||||
if ([body isKindOfClass: [NSData class]])
|
||||
{
|
||||
NSString *charset;
|
||||
int encoding;
|
||||
|
||||
|
||||
charset = [[thePart contentType] valueOfParameter: @"charset"];
|
||||
encoding = [NGMimeType stringEncodingForCharset: charset];
|
||||
|
||||
if (![charset length])
|
||||
charset = @"us-ascii";
|
||||
|
||||
s = [[NSString alloc] initWithData: body encoding: encoding];
|
||||
AUTORELEASE(s);
|
||||
s = [NSString stringWithData: body usingEncodingNamed: charset];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -396,6 +406,13 @@ struct GlobalObjectId {
|
||||
{
|
||||
if ([type isEqualToString: @"text"])
|
||||
{
|
||||
NSString *s, *charset;
|
||||
|
||||
charset = [[[self lookupInfoForBodyPart: @""] objectForKey: @"parameterList"] objectForKey: @"charset"];
|
||||
|
||||
if (![charset length])
|
||||
charset = @"us-ascii";
|
||||
|
||||
d = [[self fetchPlainTextParts] objectForKey: @""];
|
||||
|
||||
// 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)
|
||||
d = [d dataByDecodingQuotedPrintableTransferEncoding];
|
||||
|
||||
s = [NSString stringWithData: d usingEncodingNamed: charset];
|
||||
|
||||
// Check if we must convert html->plain
|
||||
if (theType == 1 && [subtype isEqualToString: @"html"])
|
||||
{
|
||||
NSString *s;
|
||||
|
||||
s = [[NSString alloc] initWithData: d encoding: NSUTF8StringEncoding];
|
||||
AUTORELEASE(s);
|
||||
|
||||
s = [s htmlToText];
|
||||
d = [s dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
d = [s dataUsingEncoding: NSUTF8StringEncoding];
|
||||
}
|
||||
else if ([type isEqualToString: @"multipart"])
|
||||
{
|
||||
@@ -761,7 +776,7 @@ struct GlobalObjectId {
|
||||
|
||||
// Flags
|
||||
[s appendString: @"<Flag xmlns=\"Email:\">"];
|
||||
[s appendFormat: @"<FlagStatus>%d</FlagStatus>", 0];
|
||||
[s appendFormat: @"<FlagStatus>%d</FlagStatus>", ([self flagged] ? 2 : 0)];
|
||||
[s appendString: @"</Flag>"];
|
||||
|
||||
// FIXME - support these in the future
|
||||
@@ -799,10 +814,21 @@ struct GlobalObjectId {
|
||||
|
||||
if ((o = [theValues objectForKey: @"Flag"]))
|
||||
{
|
||||
o = [o objectForKey: @"FlagStatus"];
|
||||
|
||||
if ([o intValue])
|
||||
[self addFlags: @"\\Flagged"];
|
||||
// We must handle empty flags -> {Flag = ""; } - some ActiveSync clients, like the HTC Desire
|
||||
// will send an empty Flag message when "unflagging" a mail.
|
||||
if (([o isKindOfClass: [NSMutableDictionary class]]))
|
||||
{
|
||||
if ((o = [o objectForKey: @"FlagStatus"]))
|
||||
{
|
||||
// 0 = The flag is cleared.
|
||||
// 1 = The status is set to complete.
|
||||
// 2 = The status is set to active.
|
||||
if (([o isEqualToString: @"2"]))
|
||||
[self addFlags: @"\\Flagged"];
|
||||
else
|
||||
[self removeFlags: @"\\Flagged"];
|
||||
}
|
||||
}
|
||||
else
|
||||
[self removeFlags: @"\\Flagged"];
|
||||
}
|
||||
|
||||
@@ -1,3 +1,454 @@
|
||||
commit 5dd2088d42388b397e8cc3fde35455b2455f7f3e
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Wed Jul 2 11:32:06 2014 -0400
|
||||
|
||||
Updated for the release
|
||||
|
||||
M NEWS
|
||||
M Version
|
||||
|
||||
commit 9c3c2cb92657809945eeaf25310039173efb58b0
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Wed Jul 2 11:23:57 2014 -0400
|
||||
|
||||
Updated the doc for the release
|
||||
|
||||
M Documentation/SOGo Installation Guide.odt
|
||||
M Documentation/SOGo Mozilla Thunderbird Configuration.odt
|
||||
M Documentation/SOGo Native Microsoft Outlook Configuration.odt
|
||||
|
||||
commit 55422b5f037b1c410c25a685afa6d3077c3fce21
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Wed Jul 2 10:11:13 2014 -0400
|
||||
|
||||
Fixed typo
|
||||
|
||||
M NEWS
|
||||
|
||||
commit 899a1eb026503165436a333110fa6eb73522c5a6
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Wed Jul 2 08:39:01 2014 -0400
|
||||
|
||||
Updated NEWS file
|
||||
|
||||
M NEWS
|
||||
|
||||
commit 660f39a980513bf9bcff14bdc8d74dd092c50717
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Mon Jun 30 14:13:52 2014 -0400
|
||||
|
||||
delete notice and put grey background for multi-columns view
|
||||
|
||||
M UI/Scheduler/English.lproj/Localizable.strings
|
||||
M UI/WebServerResources/SchedulerUI.js
|
||||
|
||||
commit 8335c388308d381afc8b4b544f0b9b88e2140c25
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Mon Jun 30 13:57:10 2014 -0400
|
||||
|
||||
fix infinite loop bug
|
||||
|
||||
M UI/WebServerResources/SchedulerUI.js
|
||||
|
||||
commit 0fb7135335810a31c8936a3e58680f93c550b0c2
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Mon Jun 30 13:36:12 2014 -0400
|
||||
|
||||
bugfixes
|
||||
|
||||
M UI/Scheduler/UIxCalListingActions.m
|
||||
M UI/WebServerResources/SchedulerUI.js
|
||||
M UI/WebServerResources/UIxCalViewPrint.js
|
||||
A UI/WebServerResources/error.png
|
||||
A UI/WebServerResources/notice.png
|
||||
A UI/WebServerResources/success.png
|
||||
A UI/WebServerResources/warning.png
|
||||
|
||||
commit 6b58e034f3db20f3433eccfd425f0e57349aeced
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Mon Jun 30 10:42:07 2014 -0400
|
||||
|
||||
printView display the parentView by default
|
||||
|
||||
M UI/Scheduler/UIxCalViewPrint.h
|
||||
M UI/Scheduler/UIxCalViewPrint.m
|
||||
M UI/Templates/SchedulerUI/UIxCalViewPrint.wox
|
||||
M UI/WebServerResources/UIxCalViewPrint.js
|
||||
|
||||
commit ff015afde8700b0a4163c89ce4b9cdca5f6261e5
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jun 30 10:35:55 2014 -0400
|
||||
|
||||
Fixed indent
|
||||
|
||||
M UI/PreferencesUI/UIxPreferences.m
|
||||
|
||||
commit 6da03bc75ca64a79785001897eb629b6250b2cb8
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Sat Jun 28 08:31:07 2014 -0400
|
||||
|
||||
Cleaned up code for older gcc
|
||||
|
||||
M UI/Scheduler/UIxCalListingActions.m
|
||||
|
||||
commit 2b119c63e0bd0fa96361369152a5aface05ffe32
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Fri Jun 27 17:21:18 2014 -0400
|
||||
|
||||
Applied comments
|
||||
|
||||
M UI/Scheduler/UIxCalDayTable.m
|
||||
M UI/Scheduler/UIxCalListingActions.m
|
||||
|
||||
commit 5b61df8ca4035d3bd66bb024a1f5b62cd6054a84
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Fri Jun 27 17:00:32 2014 -0400
|
||||
|
||||
activeTasks updates dynamically
|
||||
|
||||
M UI/Scheduler/UIxCalListingActions.m
|
||||
M UI/Scheduler/product.plist
|
||||
M UI/WebServerResources/SchedulerUI.js
|
||||
|
||||
commit 5b9c7e07d1d6b6eba1901d3fa40ee22cbbf73ead
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Thu Jun 26 21:22:12 2014 -0400
|
||||
|
||||
text wraping and text alignment in list view
|
||||
|
||||
M UI/WebServerResources/UIxCalViewPrint.css
|
||||
M UI/WebServerResources/generic.css
|
||||
|
||||
commit 7a69bc46eb5c2300e93d4b85c6954638a4271132
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Thu Jun 26 16:42:05 2014 -0400
|
||||
|
||||
applied comments
|
||||
|
||||
M SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
M UI/Scheduler/UIxCalendarSelector.m
|
||||
M UI/WebServerResources/SchedulerUI.js
|
||||
|
||||
commit 302e40a1c3c1f13578499d82e40c75e50beff79e
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Thu Jun 26 16:08:47 2014 -0400
|
||||
|
||||
bugFix#2823
|
||||
|
||||
M SoObjects/Mailer/SOGoDraftObject.m
|
||||
M SoObjects/SOGo/SOGoParentFolder.m
|
||||
M UI/PreferencesUI/UIxPreferences.m
|
||||
M UI/Templates/PreferencesUI/UIxPreferences.wox
|
||||
M UI/WebServerResources/UIxPreferences.js
|
||||
|
||||
commit b6be4e6e2fb8e00c8f0b29e4f493936d0fcfbb2e
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Thu Jun 26 11:01:16 2014 -0400
|
||||
|
||||
Applied patch from bug #2810
|
||||
|
||||
M ActiveSync/SOGoMailObject+ActiveSync.m
|
||||
M NEWS
|
||||
|
||||
commit dabd8d3282c1e1f4a4d3622637c7952e1e295cb5
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Thu Jun 26 10:05:48 2014 -0400
|
||||
|
||||
Fix list view printing with multiColumns view
|
||||
|
||||
M UI/WebServerResources/UIxCalViewPrint.js
|
||||
|
||||
commit d7d458a934b407bea6aa7e1818e71b5d544cf705
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Wed Jun 25 17:39:48 2014 -0400
|
||||
|
||||
multiColumns print view + bugFixes
|
||||
|
||||
M UI/Scheduler/English.lproj/Localizable.strings
|
||||
M UI/Scheduler/UIxCalViewPrint.m
|
||||
M UI/Templates/SchedulerUI/UIxCalViewPrint.wox
|
||||
M UI/WebServerResources/SchedulerUI.css
|
||||
M UI/WebServerResources/SchedulerUI.js
|
||||
M UI/WebServerResources/UIxCalViewPrint.css
|
||||
M UI/WebServerResources/UIxCalViewPrint.js
|
||||
|
||||
commit 5419f411e5bae58cc17ab22b3b95a88efab74252
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Wed Jun 25 15:05:25 2014 -0400
|
||||
|
||||
Fix for bug #2734
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
M NEWS
|
||||
|
||||
commit 22f5ed62fef337f9fa2f84318e3c24962ee3c207
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Wed Jun 25 10:14:28 2014 -0400
|
||||
|
||||
Fixed tests regarding default permissions on CalDAV collections
|
||||
|
||||
M Tests/Integration/test-davacl.py
|
||||
|
||||
commit 3523dc489f00c147279217faab9ad12504ca7340
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jun 23 12:19:02 2014 -0400
|
||||
|
||||
Fix for bug #2825
|
||||
|
||||
M SoObjects/SOGo/GCSSpecialQueries+SOGoCacheObject.m
|
||||
|
||||
commit 5b3de04fc19daef85749fe34b969724f78a6b7ab
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Fri Jun 20 13:27:04 2014 -0400
|
||||
|
||||
Fix error from the last commit
|
||||
|
||||
M UI/Scheduler/UIxCalDayTable.m
|
||||
|
||||
commit eb3cc8916df223dae773243dcbcf65269e8b0451
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Fri Jun 20 13:16:55 2014 -0400
|
||||
|
||||
The multiColumns view
|
||||
|
||||
M UI/Scheduler/English.lproj/Localizable.strings
|
||||
M UI/Scheduler/Toolbars/SOGoAppointmentFolders.toolbar
|
||||
M UI/Scheduler/UIxCalDayTable.h
|
||||
M UI/Scheduler/UIxCalDayTable.m
|
||||
M UI/Scheduler/UIxCalListingActions.h
|
||||
M UI/Scheduler/UIxCalListingActions.m
|
||||
M UI/Scheduler/UIxCalMulticolumnDayView.m
|
||||
M UI/Scheduler/UIxCalendarSelector.m
|
||||
M UI/Templates/SchedulerUI/UIxCalDayTable.wox
|
||||
M UI/Templates/SchedulerUI/UIxCalDayView.wox
|
||||
M UI/Templates/SchedulerUI/UIxCalMulticolumnDayView.wox
|
||||
M UI/Templates/SchedulerUI/UIxCalWeekView.wox
|
||||
M UI/WebServerResources/SchedulerUI.css
|
||||
M UI/WebServerResources/SchedulerUI.js
|
||||
|
||||
commit 9fa0b42e19c76184d4195a1530039dcad5cb5a80
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Fri Jun 20 11:25:02 2014 -0400
|
||||
|
||||
Ignore SCHEDULE-AGENT for Thunderbird/Lightning
|
||||
|
||||
M NEWS
|
||||
M SoObjects/Appointments/SOGoAppointmentObject.m
|
||||
|
||||
commit e048346cb715d03fe6bd32d837b96107fdb6d44b
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Thu Jun 19 16:08:15 2014 -0400
|
||||
|
||||
Include, in our private NS, all ACLs defined on a calendar collectiob
|
||||
|
||||
M SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
|
||||
commit 333b561a5932acbe66d5b62c8963dd166b9de7f2
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jun 16 15:30:26 2014 -0400
|
||||
|
||||
Applied patch from bug #2736
|
||||
|
||||
M SOPE/NGCards/NGCardsSaxHandler.m
|
||||
M SOPE/NGCards/samples/vcf2xml.m
|
||||
M SoObjects/Mailer/NSString+Mail.m
|
||||
M UI/MailPartViewers/UIxMailPartHTMLViewer.m
|
||||
|
||||
commit 50c0b49a6fe210b438d2e5a7abac7219bdb15b8f
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jun 16 11:00:39 2014 -0400
|
||||
|
||||
Fix for bug #2813
|
||||
|
||||
M NEWS
|
||||
M SoObjects/SOGo/SOGoGCSFolder.m
|
||||
|
||||
commit 8202d0588fb8e1774e502941fc01223fb9005334
|
||||
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
Date: Mon Jun 16 10:20:02 2014 -0400
|
||||
|
||||
Add documentation for SOGoSubscriptionFolderFormat
|
||||
|
||||
Fixes #2799
|
||||
|
||||
M Documentation/SOGo Installation Guide.odt
|
||||
|
||||
commit d9b578b869da27b876f019bc10acf72839220fd3
|
||||
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
Date: Mon Jun 16 09:52:29 2014 -0400
|
||||
|
||||
Wait 20 seconds before deleting temporary forms
|
||||
|
||||
Those temporary forms are created to download attachments.
|
||||
Fixes #2811
|
||||
|
||||
M NEWS
|
||||
M UI/WebServerResources/MailerUI.js
|
||||
|
||||
commit 5bb64916c6fc96bf20bc258a075c33413784ca01
|
||||
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
Date: Thu Jun 12 09:09:08 2014 -0400
|
||||
|
||||
Fix regression when attaching files to a reply
|
||||
|
||||
M NEWS
|
||||
M UI/MailerUI/UIxMailEditor.m
|
||||
|
||||
commit e7ddfb0546fcb70f1ac3401375656936ea00c380
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Wed Jun 11 12:55:51 2014 -0400
|
||||
|
||||
Fix for bug #2805
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
M NEWS
|
||||
|
||||
commit 0e182f84360aa35c87d39320cab08d7eaeb55d90
|
||||
Author: Alexandre Cloutier <alexandre.cl07@gmail.com>
|
||||
Date: Wed Jun 11 11:25:17 2014 -0400
|
||||
|
||||
Update SOGoAppointmentFolder.m
|
||||
|
||||
Fix the status filtering from 2 to 1
|
||||
|
||||
M SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
|
||||
commit de2ee2322af740251ac82ca06deb1f7585637024
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Wed Jun 11 10:24:04 2014 -0400
|
||||
|
||||
Change the NSString with NSNumber and add some JavaScript
|
||||
|
||||
M SoObjects/Appointments/SOGoAppointmentFolder.h
|
||||
M SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
M UI/Scheduler/UIxCalendarSelector.m
|
||||
M UI/Templates/SchedulerUI/UIxCalendarSelector.wox
|
||||
M UI/WebServerResources/SchedulerUI.js
|
||||
|
||||
commit ce07695a5597964354404557c9246eee5325abb2
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Tue Jun 10 16:35:36 2014 -0400
|
||||
|
||||
Make sure to keep the / path prefix
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
|
||||
commit 43ac57b671ecf4f0d81daf3e40fab856a5d3b52a
|
||||
Author: Alexandre Cloutier <acloutier@inverse.ca>
|
||||
Date: Tue Jun 10 16:18:12 2014 -0400
|
||||
|
||||
display the number of active tasks in calendar view
|
||||
|
||||
M SoObjects/Appointments/SOGoAppointmentFolder.h
|
||||
M SoObjects/Appointments/SOGoAppointmentFolder.m
|
||||
M UI/Scheduler/UIxCalendarSelector.m
|
||||
M UI/Templates/SchedulerUI/UIxCalendarSelector.wox
|
||||
|
||||
commit 7f7e3cb06a6b8789a1cd1667fb62418fad02aac1
|
||||
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
Date: Tue Jun 10 14:48:25 2014 -0400
|
||||
|
||||
Fix folder name of new subscriptions
|
||||
|
||||
Fixes #2801
|
||||
|
||||
M NEWS
|
||||
M UI/MainUI/SOGoUserHomePage.m
|
||||
M UI/WebServerResources/UIxContactsUserFolders.js
|
||||
|
||||
commit 6b3a3f858baf2b73a7910a2039103f2976ebdc7c
|
||||
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
Date: Mon Jun 9 22:07:44 2014 -0400
|
||||
|
||||
Update prototype.js to version 1.7.2
|
||||
|
||||
M UI/WebServerResources/prototype.js
|
||||
|
||||
commit 0133bb3b6bf13c81350b791be57c01d83356cba7
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Tue Jun 10 13:07:14 2014 -0400
|
||||
|
||||
Correctly update the cache on folder update/delete ops.
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
|
||||
commit 91e3e19e40b843063cda21b84e6fcc072da2ad49
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Tue Jun 10 11:04:27 2014 -0400
|
||||
|
||||
Added the Ping ActiveSync command
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher+Sync.m
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
M NEWS
|
||||
|
||||
commit 19a95a1818e34755bffd7c5f9dbd35e214123cac
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Tue Jun 10 08:58:44 2014 -0400
|
||||
|
||||
Applied fix from #2809
|
||||
|
||||
M NEWS
|
||||
M UI/MailPartViewers/UIxMailPartHTMLViewer.m
|
||||
|
||||
commit 1292c191eca61027b9856b04840105fde588d42c
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Tue Jun 10 08:46:35 2014 -0400
|
||||
|
||||
Make sure we don't have '-' in table names.
|
||||
|
||||
M OpenChange/MAPIStoreUserContext.m
|
||||
|
||||
commit 77dad178d11b36da07660a9b5f0dd8c7a74cad50
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Tue Jun 10 08:45:35 2014 -0400
|
||||
|
||||
Make sure we don't have '-' in table names.
|
||||
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
|
||||
commit f48839bf55917cb3a9756134930206b7b15d0279
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jun 9 09:34:00 2014 -0400
|
||||
|
||||
Added (slightly modified) patch from #2806
|
||||
|
||||
M ActiveSync/SOGoMailObject+ActiveSync.m
|
||||
M NEWS
|
||||
M SoObjects/Mailer/SOGoMailObject.h
|
||||
M SoObjects/Mailer/SOGoMailObject.m
|
||||
|
||||
commit 0f04be2bcb003ff0b0c1099c524dffa3b40d0181
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jun 9 09:25:06 2014 -0400
|
||||
|
||||
Added patch from #2808
|
||||
|
||||
M ActiveSync/NSString+ActiveSync.h
|
||||
M ActiveSync/NSString+ActiveSync.m
|
||||
M ActiveSync/SOGoActiveSyncDispatcher.m
|
||||
M NEWS
|
||||
|
||||
commit 3f8bbe8c6012e60334002affded0c94151db20e3
|
||||
Author: Ludovic Marcotte <lmarcotte@inverse.ca>
|
||||
Date: Mon Jun 9 09:14:30 2014 -0400
|
||||
|
||||
Applied patch from #2807
|
||||
|
||||
M ActiveSync/NGVCard+ActiveSync.m
|
||||
M NEWS
|
||||
M SOPE/NGCards/NGVCard.h
|
||||
M SOPE/NGCards/NGVCard.m
|
||||
|
||||
commit 6b6ec8a2ae0f453dd0a127d8cf37082c80449c94
|
||||
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
Date: Thu Jun 5 13:35:17 2014 -0400
|
||||
|
||||
Update ChangeLog
|
||||
|
||||
M ChangeLog
|
||||
|
||||
commit ad30df26af2c95f91ad42fd02a3b45ada115502b
|
||||
Author: Francis Lachapelle <flachapelle@inverse.ca>
|
||||
Date: Thu Jun 5 13:34:40 2014 -0400
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +1,28 @@
|
||||
2.2.6 (2014-07-02)
|
||||
------------------
|
||||
|
||||
New features
|
||||
- add new 'multi-columns' calendar view (#1948)
|
||||
|
||||
Enhancements
|
||||
- contacts photos are now synchronized using ActiveSync (#2807)
|
||||
- implemented the GetAttachment ActiveSync command (#2808)
|
||||
- implemented the Ping ActiveSync command
|
||||
- added "soft deletes" support for ActiveSync (#2734)
|
||||
- now display the active tasks next to calendar names (#2760)
|
||||
|
||||
Bug fixes
|
||||
- better handling of empty "Flag" messages over ActiveSync (#2806)
|
||||
- fixed Chinese charset handling (#2809)
|
||||
- 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/Lightning 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)
|
||||
------------------
|
||||
|
||||
|
||||
@@ -294,9 +294,12 @@ static NSMapTable *contextsTable = nil;
|
||||
if ([parts count] == 5)
|
||||
{
|
||||
/* If "OCSFolderInfoURL" is properly configured, we must have 5
|
||||
parts in this url. */
|
||||
parts in this url. We strip the '-' character in case we have
|
||||
this in the domain part - like foo@bar-zot.com */
|
||||
ocFSTableName = [NSString stringWithFormat: @"sogo_cache_folder_%@",
|
||||
[username asCSSIdentifier]];
|
||||
[[[user loginInDomain] asCSSIdentifier]
|
||||
stringByReplacingOccurrencesOfString: @"-"
|
||||
withString: @"_"]];
|
||||
[parts replaceObjectAtIndex: 4 withObject: ocFSTableName];
|
||||
folderTableURL
|
||||
= [NSURL URLWithString: [parts componentsJoinedByString: @"/"]];
|
||||
|
||||
@@ -232,7 +232,7 @@ static NSArray *privilegedTagNames = nil;
|
||||
}
|
||||
|
||||
- (void) characters: (unichar *) _chars
|
||||
length: (int) _len
|
||||
length: (NSUInteger) _len
|
||||
{
|
||||
if (_len && _chars)
|
||||
{
|
||||
|
||||
@@ -126,6 +126,9 @@ typedef enum
|
||||
|
||||
- (void) setCategories: (NSArray *) newCategories;
|
||||
- (NSArray *) categories;
|
||||
- (NSString *) photo;
|
||||
- (void) setPhoto: (NSString *) _value;
|
||||
|
||||
|
||||
// - (void) setN: (NGVCardName *) _v;
|
||||
// - (NGVCardName *) n;
|
||||
|
||||
@@ -288,6 +288,25 @@
|
||||
return [self uniqueChildWithTag: @"n"];
|
||||
}
|
||||
|
||||
- (NSString *) photo
|
||||
{
|
||||
NGVCardPhoto *photo;
|
||||
|
||||
photo = (NGVCardPhoto *)[self uniqueChildWithTag: @"photo"];
|
||||
|
||||
return [[photo valuesAtIndex: 0 forKey: @""] objectAtIndex: 0];
|
||||
}
|
||||
|
||||
- (void) setPhoto: (NSString *) _value
|
||||
{
|
||||
CardElement *photo;
|
||||
photo = [self uniqueChildWithTag: @"photo"];
|
||||
[photo setValue: 0 ofAttribute: @"encoding" to: @"BASE64"];
|
||||
|
||||
[photo setSingleValue: _value forKey: @""];
|
||||
}
|
||||
|
||||
|
||||
- (void) setOrg: (NSString *) anOrg
|
||||
units: (NSArray *) someUnits
|
||||
{
|
||||
|
||||
@@ -217,10 +217,10 @@
|
||||
printf("</%s>\n", [_localName cString]);
|
||||
}
|
||||
|
||||
- (void)characters:(unichar *)_chars length:(int)_len {
|
||||
- (void)characters:(unichar *)_chars length:(NSUInteger)_len {
|
||||
NSString *str;
|
||||
id tmp;
|
||||
unsigned i, len;
|
||||
NSUInteger i, len;
|
||||
|
||||
if (_len == 0) {
|
||||
[self indent];
|
||||
@@ -228,7 +228,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < (unsigned)_len; i++) {
|
||||
for (i = 0; i < _len; i++) {
|
||||
if (_chars[i] > 255) {
|
||||
NSLog(@"detected large char: o%04o d%03i h%04X",
|
||||
_chars[i], _chars[i], _chars[i]);
|
||||
@@ -246,7 +246,7 @@
|
||||
[self indent];
|
||||
printf("\"%s\"\n", [str cString]);
|
||||
}
|
||||
- (void)ignorableWhitespace:(unichar *)_chars length:(int)_len {
|
||||
- (void)ignorableWhitespace:(unichar *)_chars length:(NSUInteger)_len {
|
||||
NSString *data;
|
||||
id tmp;
|
||||
|
||||
|
||||
@@ -80,6 +80,8 @@ typedef enum {
|
||||
|
||||
- (NSArray *) calendarUIDs;
|
||||
|
||||
- (NSNumber *) activeTasks;
|
||||
|
||||
/* vevent UID handling */
|
||||
|
||||
- (NSString *) resourceNameForEventUID: (NSString *) _uid;
|
||||
|
||||
@@ -230,6 +230,8 @@ static iCalEvent *iCalEventK = nil;
|
||||
abstract: NO
|
||||
withEquivalent: SoPerm_AddDocumentsImagesAndFiles
|
||||
asChildOf: davElement (@"write", XMLNS_WEBDAV)];
|
||||
|
||||
/* read-acl and write-acl are defined in RFC3744 */
|
||||
[aclManager registerDAVPermission: davElement (@"admin", nsI)
|
||||
abstract: YES
|
||||
withEquivalent: nil
|
||||
@@ -242,6 +244,72 @@ static iCalEvent *iCalEventK = nil;
|
||||
abstract: YES
|
||||
withEquivalent: SoPerm_ChangePermissions
|
||||
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;
|
||||
@@ -522,7 +590,6 @@ static iCalEvent *iCalEventK = nil;
|
||||
{
|
||||
/* this is used for group calendars (this folder just returns itself) */
|
||||
NSString *s;
|
||||
|
||||
s = [[self container] nameInContainer];
|
||||
// [self logWithFormat:@"CAL UID: %@", s];
|
||||
return [s isNotNull] ? [NSArray arrayWithObjects:&s count:1] : nil;
|
||||
@@ -3270,4 +3337,25 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
|
||||
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 */
|
||||
|
||||
@@ -1428,9 +1428,11 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
//
|
||||
- (BOOL) _shouldScheduleEvent: (iCalPerson *) theOrganizer
|
||||
{
|
||||
NSArray *userAgents;
|
||||
NSString *v;
|
||||
BOOL b;
|
||||
|
||||
int i;
|
||||
|
||||
b = YES;
|
||||
|
||||
if (theOrganizer && (v = [theOrganizer value: 0 ofAttribute: @"SCHEDULE-AGENT"]))
|
||||
@@ -1440,6 +1442,27 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -430,7 +430,7 @@
|
||||
}
|
||||
|
||||
- (void) ignorableWhitespace: (unichar *) whitespaces
|
||||
length: (int) length
|
||||
length: (NSUInteger) length
|
||||
{
|
||||
showWhoWeAre();
|
||||
}
|
||||
|
||||
@@ -1761,26 +1761,26 @@ static NSString *userAgent = nil;
|
||||
{
|
||||
SOGoUserDefaults *ud;
|
||||
ud = [[context activeUser] userDefaults];
|
||||
|
||||
|
||||
if ([ud mailAddOutgoingAddresses])
|
||||
{
|
||||
Class contactGCSEntry;
|
||||
SOGoContactFolders *contactFolders;
|
||||
NGMailAddressParser *parser;
|
||||
id parsedRecipient;
|
||||
SOGoContactFolder *folder;
|
||||
SOGoContactGCSEntry *newContact;
|
||||
NGVCard *card;
|
||||
Class contactGCSEntry;
|
||||
NGMailAddressParser *parser;
|
||||
NSArray *matchingContacts;
|
||||
NSMutableArray *recipients;
|
||||
NSString *recipient, *emailAddress, *addressBook, *uid;
|
||||
NSArray *matchingContacts;
|
||||
id parsedRecipient;
|
||||
int i;
|
||||
|
||||
|
||||
// Get all the addressbooks
|
||||
contactFolders = [[[context activeUser] homeFolderInContext: context]
|
||||
lookupName: @"Contacts"
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
lookupName: @"Contacts"
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
// Get all the recipients from the current email
|
||||
recipients = [self allRecipients];
|
||||
for (i = 0; i < [recipients count]; i++)
|
||||
@@ -1790,7 +1790,7 @@ static NSString *userAgent = nil;
|
||||
parser = [NGMailAddressParser mailAddressParserWithString: recipient];
|
||||
parsedRecipient = [parser parse];
|
||||
emailAddress = [parsedRecipient address];
|
||||
|
||||
|
||||
matchingContacts = [contactFolders allContactsFromFilter: emailAddress
|
||||
excludeGroups: YES
|
||||
excludeLists: YES];
|
||||
|
||||
@@ -114,6 +114,7 @@ NSArray *SOGoMailCoreInfoKeys;
|
||||
- (NSException *) removeFlags:(id)_f;
|
||||
|
||||
- (BOOL) isNewMail; /* \Recent */
|
||||
- (BOOL) flagged; /* \Flagged */
|
||||
- (BOOL) read; /* \Unseen */
|
||||
- (BOOL) replied; /* \Answered */
|
||||
- (BOOL) forwarded; /* $forwarded */
|
||||
|
||||
@@ -1464,6 +1464,11 @@ static BOOL debugSoParts = NO;
|
||||
return [self _hasFlag: @"seen"];
|
||||
}
|
||||
|
||||
- (BOOL) flagged
|
||||
{
|
||||
return [self _hasFlag: @"flagged"];
|
||||
}
|
||||
|
||||
- (BOOL) replied
|
||||
{
|
||||
return [self _hasFlag: @"answered"];
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
= (@"CREATE TABLE %@ ("
|
||||
@" c_path VARCHAR(255) PRIMARY KEY,"
|
||||
@" c_parent_path VARCHAR(255),"
|
||||
@" c_type TINYINT NOT NULL,"
|
||||
@" c_type TINYINT UNSIGNED NOT NULL,"
|
||||
@" c_creationdate INT NOT NULL,"
|
||||
@" c_lastmodified INT NOT NULL,"
|
||||
@" c_version INT NOT NULL DEFAULT 0,"
|
||||
|
||||
@@ -329,17 +329,24 @@ static NSArray *childRecordFields = nil;
|
||||
fc = [cm acquireOpenChannelForURL: folderLocation];
|
||||
if (fc)
|
||||
{
|
||||
sql
|
||||
= [NSString stringWithFormat: (@"SELECT c_foldername FROM %@"
|
||||
@" WHERE c_path = '%@'"),
|
||||
[folderLocation gcsTableName], ocsPath];
|
||||
[fc evaluateExpressionX: sql];
|
||||
attrs = [fc describeResults: NO];
|
||||
row = [fc fetchAttributes: attrs withZone: NULL];
|
||||
if (row)
|
||||
[self _setDisplayNameFromRow: row];
|
||||
[fc cancelFetch];
|
||||
[cm releaseChannel: fc];
|
||||
// We use an exception handler here in case the database is down when
|
||||
// performing the query. This could have unexpected results.
|
||||
NS_DURING
|
||||
{
|
||||
sql
|
||||
= [NSString stringWithFormat: (@"SELECT c_foldername FROM %@"
|
||||
@" WHERE c_path = '%@'"),
|
||||
[folderLocation gcsTableName], ocsPath];
|
||||
[fc evaluateExpressionX: sql];
|
||||
attrs = [fc describeResults: NO];
|
||||
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;
|
||||
SOGoGCSFolder *folder;
|
||||
SOGoUser *folderOwner;
|
||||
|
||||
SOGoUserDefaults *ud;
|
||||
|
||||
roles = [[context activeUser] rolesForObject: self inContext: context];
|
||||
folderOwner = [SOGoUser userWithLogin: [self ownerInContext: context]];
|
||||
|
||||
|
||||
|
||||
// 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
|
||||
// in order to create its database tables.
|
||||
// FolderType is an enum where 0 = Personal and 1 = collected
|
||||
if ([roles containsObject: SoRole_Owner] ||
|
||||
(folderOwner && [folderOwner isResource]))
|
||||
{
|
||||
if (folderType == SOGoPersonalFolder)
|
||||
{
|
||||
if (folderType == SOGoPersonalFolder)
|
||||
{
|
||||
folderName = @"personal";
|
||||
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]];
|
||||
}
|
||||
folderName = @"personal";
|
||||
folder = [subFolderClass objectWithName: folderName inContainer: self];
|
||||
[folder setDisplayName: [self defaultFolderName]];
|
||||
[folder setOCSPath: [NSString stringWithFormat: @"%@/%@", OCSPath, folderName]];
|
||||
|
||||
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
|
||||
|
||||
@@ -401,7 +401,17 @@ class DAVCalendarAclTest(DAVAclTest):
|
||||
else:
|
||||
expStatus = 207
|
||||
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):
|
||||
icsClass = self.classToICSClass[event_class].lower()
|
||||
|
||||
@@ -86,6 +86,9 @@ _xmlCharsetForCharset (NSString *charset)
|
||||
{ @"windows-1251", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger windows-1251 -> utf8 conversion
|
||||
{ @"windows-1257", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger windows-1257 -> utf8 conversion
|
||||
{ @"gb2312", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger gb2312 -> utf8 conversion
|
||||
{ @"gbk", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger gb2312 -> utf8 conversion
|
||||
{ @"gb18030", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger gb2312 -> utf8 conversion
|
||||
{ @"big5", XML_CHAR_ENCODING_ERROR}, // unsupported, will trigger gb2312 -> utf8 conversion
|
||||
{ @"euc-jp", XML_CHAR_ENCODING_EUC_JP}};
|
||||
unsigned count;
|
||||
xmlCharEncoding encoding;
|
||||
@@ -416,9 +419,9 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||
}
|
||||
|
||||
- (void) _appendStyle: (unichar *) _chars
|
||||
length: (int) _len
|
||||
length: (NSUInteger) _len
|
||||
{
|
||||
unsigned int count, length;
|
||||
NSUInteger count, length;
|
||||
unichar *start, *currentChar;
|
||||
|
||||
start = _chars;
|
||||
@@ -685,7 +688,7 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||
}
|
||||
|
||||
- (void) characters: (unichar *) _chars
|
||||
length: (int) _len
|
||||
length: (NSUInteger) _len
|
||||
{
|
||||
showWhoWeAre();
|
||||
if (!ignoredContent)
|
||||
@@ -709,7 +712,7 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||
}
|
||||
|
||||
- (void) ignorableWhitespace: (unichar *) _chars
|
||||
length: (int) _len
|
||||
length: (NSUInteger) _len
|
||||
{
|
||||
showWhoWeAre();
|
||||
}
|
||||
@@ -732,7 +735,7 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||
|
||||
/* SaxLexicalHandler */
|
||||
- (void) comment: (unichar *) _chars
|
||||
length: (int) _len
|
||||
length: (NSUInteger) _len
|
||||
{
|
||||
showWhoWeAre();
|
||||
if (inStyle)
|
||||
|
||||
@@ -644,8 +644,9 @@ static NSArray *infoKeys = nil;
|
||||
if (!attachmentAttrs || ![co imap4URL])
|
||||
{
|
||||
[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];
|
||||
a = [mail fetchFileAttachmentKeys];
|
||||
ASSIGN (attachmentAttrs, a);
|
||||
|
||||
@@ -364,8 +364,7 @@
|
||||
|
||||
// We sort our array - this is pretty useful for the Web
|
||||
// interface of SOGo.
|
||||
allUsers = [users
|
||||
sortedArrayUsingSelector: @selector (caseInsensitiveDisplayNameCompare:)];
|
||||
allUsers = [users sortedArrayUsingSelector: @selector (caseInsensitiveDisplayNameCompare:)];
|
||||
|
||||
max = [allUsers count];
|
||||
jsonResponse = [NSMutableArray arrayWithCapacity: max];
|
||||
@@ -462,9 +461,8 @@
|
||||
|
||||
folderType = [self queryParameterForKey: @"type"];
|
||||
userFolder = [self clientObject];
|
||||
folders
|
||||
= [userFolder foldersOfType: folderType
|
||||
forUID: [userFolder ownerInContext: context]];
|
||||
folders = [userFolder foldersOfType: folderType
|
||||
forUID: [userFolder ownerInContext: context]];
|
||||
result = [self _foldersResponseForResults: folders];
|
||||
|
||||
return result;
|
||||
|
||||
@@ -74,7 +74,7 @@ static NSArray *reminderValues = nil;
|
||||
if (!reminderItems && !reminderValues)
|
||||
{
|
||||
reminderItems = [NSArray arrayWithObjects:
|
||||
@"5_MINUTES_BEFORE",
|
||||
@"5_MINUTES_BEFORE",
|
||||
@"10_MINUTES_BEFORE",
|
||||
@"15_MINUTES_BEFORE",
|
||||
@"30_MINUTES_BEFORE",
|
||||
@@ -90,7 +90,7 @@ static NSArray *reminderValues = nil;
|
||||
@"1_WEEK_BEFORE",
|
||||
nil];
|
||||
reminderValues = [NSArray arrayWithObjects:
|
||||
@"-PT5M",
|
||||
@"-PT5M",
|
||||
@"-PT10M",
|
||||
@"-PT15M",
|
||||
@"-PT30M",
|
||||
@@ -699,49 +699,53 @@ static NSArray *reminderValues = nil;
|
||||
/* We want all the SourceIDS */
|
||||
NSMutableArray *folders, *availableAddressBooksID, *availableAddressBooksName;
|
||||
SOGoParentFolder *contactFolders;
|
||||
|
||||
|
||||
int i, count;
|
||||
BOOL collectedAlreadyExist;
|
||||
|
||||
|
||||
contactFolders = [[[context activeUser] homeFolderInContext: context]
|
||||
lookupName: @"Contacts"
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
lookupName: @"Contacts"
|
||||
inContext: context
|
||||
acquire: NO];
|
||||
folders = [NSMutableArray arrayWithArray: [contactFolders subFolders]];
|
||||
count = [folders count]-1;
|
||||
|
||||
|
||||
// Inside this loop we remove all the public or shared addressbooks
|
||||
for (; count >= 0; count--)
|
||||
{
|
||||
if (![[folders objectAtIndex: count] isKindOfClass: [SOGoContactGCSFolder class]])
|
||||
[folders removeObjectAtIndex: count];
|
||||
}
|
||||
|
||||
{
|
||||
if (![[folders objectAtIndex: count] isKindOfClass: [SOGoContactGCSFolder class]])
|
||||
[folders removeObjectAtIndex: count];
|
||||
}
|
||||
|
||||
// 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]];
|
||||
availableAddressBooksName = [NSMutableArray arrayWithCapacity: [folders count]];
|
||||
count = [folders count]-1;
|
||||
collectedAlreadyExist = NO;
|
||||
|
||||
for (i = 0; i <= count ; i++) {
|
||||
[availableAddressBooksID addObject:[[folders objectAtIndex:i] realNameInContainer]];
|
||||
[availableAddressBooksName addObject:[[folders objectAtIndex:i] displayName]];
|
||||
|
||||
if ([[availableAddressBooksID objectAtIndex:i] isEqualToString: @"collected"])
|
||||
collectedAlreadyExist = YES;
|
||||
}
|
||||
|
||||
for (i = 0; i <= count ; i++)
|
||||
{
|
||||
[availableAddressBooksID addObject:[[folders objectAtIndex:i] realNameInContainer]];
|
||||
[availableAddressBooksName addObject:[[folders objectAtIndex:i] displayName]];
|
||||
|
||||
if ([[availableAddressBooksID objectAtIndex:i] isEqualToString: @"collected"])
|
||||
collectedAlreadyExist = YES;
|
||||
}
|
||||
// Create the dictionary for the next function : itemAddressBookText.
|
||||
if (!addressBooksIDWithDisplayName)
|
||||
addressBooksIDWithDisplayName = [[NSMutableDictionary alloc] initWithObjects:availableAddressBooksName
|
||||
forKeys:availableAddressBooksID];
|
||||
if (!collectedAlreadyExist)
|
||||
{
|
||||
addressBooksIDWithDisplayName = [[NSMutableDictionary alloc] initWithObjects:availableAddressBooksName
|
||||
forKeys:availableAddressBooksID];
|
||||
}
|
||||
if (!collectedAlreadyExist)
|
||||
{
|
||||
[availableAddressBooksID addObject: @"collected"];
|
||||
[addressBooksIDWithDisplayName setObject: [self labelForKey: @"Collected Address Book"] forKey: @"collected"];
|
||||
[addressBooksIDWithDisplayName setObject: [self labelForKey: @"Collected Address Book"] forKey: @"collected"];
|
||||
}
|
||||
|
||||
|
||||
return availableAddressBooksID;
|
||||
}
|
||||
|
||||
- (NSString *) itemAddressBookText
|
||||
{
|
||||
return [addressBooksIDWithDisplayName objectForKey: item];
|
||||
@@ -929,7 +933,7 @@ static NSArray *reminderValues = nil;
|
||||
capabilities = [[self sieveClient] capabilities];
|
||||
else
|
||||
capabilities = [NSArray array];
|
||||
[capabilities retain];
|
||||
[capabilities retain];
|
||||
}
|
||||
|
||||
return [capabilities jsonRepresentation];
|
||||
@@ -1220,12 +1224,12 @@ static NSArray *reminderValues = nil;
|
||||
SOGoSieveManager *manager;
|
||||
|
||||
if (!client)
|
||||
{
|
||||
folder = [[self clientObject] mailAccountsFolder: @"Mail" inContext: context];
|
||||
account = [folder lookupName: @"0" inContext: context acquire: NO];
|
||||
manager = [SOGoSieveManager sieveManagerForUser: [context activeUser]];
|
||||
client = [[manager clientForAccount: account] retain];
|
||||
}
|
||||
{
|
||||
folder = [[self clientObject] mailAccountsFolder: @"Mail" inContext: context];
|
||||
account = [folder lookupName: @"0" inContext: context acquire: NO];
|
||||
manager = [SOGoSieveManager sieveManagerForUser: [context activeUser]];
|
||||
client = [[manager clientForAccount: account] retain];
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
@@ -1273,9 +1277,9 @@ static NSArray *reminderValues = nil;
|
||||
results = [self responseWithStatus: 502
|
||||
andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: @"Connection error", @"textStatus", nil]];
|
||||
}
|
||||
else
|
||||
results = [self responseWithStatus: 503
|
||||
andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: @"Service temporarily unavailable", @"textStatus", nil]];
|
||||
else
|
||||
results = [self responseWithStatus: 503
|
||||
andJSONRepresentation: [NSDictionary dictionaryWithObjectsAndKeys: @"Service temporarily unavailable", @"textStatus", nil]];
|
||||
}
|
||||
else
|
||||
results = self;
|
||||
|
||||
@@ -155,6 +155,7 @@
|
||||
"What to Print" = "What to Print";
|
||||
"Options" = "Options";
|
||||
"Tasks with no due date" = "Tasks with no due date";
|
||||
"Display working hours only" = "Display working hours only";
|
||||
"Completed tasks" = "Completed Tasks";
|
||||
|
||||
/* Appointments */
|
||||
@@ -313,6 +314,7 @@
|
||||
|
||||
"Repeat" = "Repeat";
|
||||
"Daily" = "Daily";
|
||||
"Multi-Columns" = "Multi-Columns";
|
||||
"Weekly" = "Weekly";
|
||||
"Monthly" = "Monthly";
|
||||
"Yearly" = "Yearly";
|
||||
@@ -542,6 +544,8 @@ vtodo_class2 = "(Confidential task)";
|
||||
"tagWasRemoved" = "If you remove this calendar from synchronization, you'll need to reload the data on your mobile device.\nContinue?";
|
||||
"DestinationCalendarError" = "The source and destination calendars are the same. Please try to copy to a different calendar.";
|
||||
"EventCopyError" = "The copy failed. Please try to copy to a difference calendar.";
|
||||
"Please select at least one calendar" = "Please select at least one calendar";
|
||||
|
||||
|
||||
"Open Task..." = "Open Task...";
|
||||
"Mark Completed" = "Mark Completed";
|
||||
|
||||
@@ -21,12 +21,11 @@
|
||||
onclick = "return onDayOverview();";
|
||||
image = "day-view.png";
|
||||
tooltip = "Switch to day view"; },
|
||||
/* disabled until we fix the view */
|
||||
/* { link = "#";
|
||||
label="Multicolumn Day View";
|
||||
onclick = "return onMulticolumnDayOverview();";
|
||||
image = "day-view-multicolumn.png";
|
||||
tooltip = ""; }, */
|
||||
{ link = "#";
|
||||
label="Multi-Columns View";
|
||||
onclick = "return onMulticolumnDayOverview();";
|
||||
image = "day-view-multicolumn.png";
|
||||
tooltip = "Switch to multi-columns day view"; },
|
||||
{ link = "#";
|
||||
label="Week View";
|
||||
onclick = "return onWeekOverview();";
|
||||
|
||||
@@ -35,27 +35,27 @@
|
||||
|
||||
@interface UIxCalDayTable : UIxCalView
|
||||
{
|
||||
unsigned int numberOfDays;
|
||||
NSCalendarDate *startDate;
|
||||
NSCalendarDate *currentTableDay;
|
||||
NSString *currentTableHour;
|
||||
NSMutableArray *daysToDisplay;
|
||||
NSMutableArray *hoursToDisplay;
|
||||
NSArray *weekDays;
|
||||
SOGoDateFormatter *dateFormatter;
|
||||
NSString *timeFormat;
|
||||
NSArray *weekDays;
|
||||
NSString *currentView, *timeFormat, *currentTableHour;
|
||||
NSCalendarDate *startDate, *currentTableDay;
|
||||
NSMutableArray *daysToDisplay, *calendarsToDisplay, *currentCalendar, *hoursToDisplay;
|
||||
unsigned int numberOfDays;
|
||||
}
|
||||
|
||||
- (void) setNumberOfDays: (NSNumber *) aNumber;
|
||||
- (NSNumber *) numberOfDays;
|
||||
- (NSString *) currentView;
|
||||
|
||||
- (void) setStartDate: (NSCalendarDate *) aStartDate;
|
||||
- (NSCalendarDate *) startDate;
|
||||
- (NSCalendarDate *) endDate;
|
||||
|
||||
- (NSArray *) daysToDisplay;
|
||||
- (NSArray *) calendarsToDisplay;
|
||||
- (void) setCurrentTableDay: (NSCalendarDate *) aTableDay;
|
||||
- (NSCalendarDate *) currentTableDay;
|
||||
- (NSMutableArray *) currentCalendar;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
+127
-17
@@ -55,9 +55,12 @@
|
||||
ASSIGN (timeFormat, [ud timeFormat]);
|
||||
|
||||
daysToDisplay = nil;
|
||||
calendarsToDisplay = nil;
|
||||
hoursToDisplay = nil;
|
||||
numberOfDays = 1;
|
||||
startDate = nil;
|
||||
currentView = nil;
|
||||
currentCalendar = nil;
|
||||
currentTableDay = nil;
|
||||
currentTableHour = nil;
|
||||
weekDays = [locale objectForKey: NSShortWeekDayNameArray];
|
||||
@@ -75,6 +78,8 @@
|
||||
// [allAppointments release];
|
||||
[weekDays release];
|
||||
[daysToDisplay release];
|
||||
[calendarsToDisplay release];
|
||||
[currentView release];
|
||||
[hoursToDisplay release];
|
||||
[dateFormatter release];
|
||||
[timeFormat release];
|
||||
@@ -119,6 +124,16 @@
|
||||
return [endDate endOfDay];
|
||||
}
|
||||
|
||||
- (void) setCurrentView: (NSString *) aView
|
||||
{
|
||||
ASSIGN(currentView, aView);
|
||||
}
|
||||
|
||||
- (NSString *) currentView
|
||||
{
|
||||
return currentView;
|
||||
}
|
||||
|
||||
- (NSArray *) hoursToDisplay
|
||||
{
|
||||
unsigned int currentHour, lastHour;
|
||||
@@ -148,22 +163,68 @@
|
||||
{
|
||||
NSCalendarDate *currentDate;
|
||||
int count;
|
||||
|
||||
|
||||
if (!daysToDisplay)
|
||||
{
|
||||
daysToDisplay = [NSMutableArray new];
|
||||
currentDate = [[self startDate] hour: [self dayStartHour]
|
||||
minute: 0];
|
||||
|
||||
for (count = 0; count < numberOfDays; count++)
|
||||
{
|
||||
daysToDisplay = [NSMutableArray new];
|
||||
currentDate = [[self startDate] hour: [self dayStartHour]
|
||||
minute: 0];
|
||||
for (count = 0; count < numberOfDays; count++)
|
||||
{
|
||||
[daysToDisplay addObject: currentDate];
|
||||
currentDate = [currentDate tomorrow];
|
||||
}
|
||||
[daysToDisplay addObject: currentDate];
|
||||
currentDate = [currentDate tomorrow];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return daysToDisplay;
|
||||
}
|
||||
|
||||
- (NSArray *) calendarsToDisplay
|
||||
{
|
||||
if (!calendarsToDisplay)
|
||||
{
|
||||
int max=0, i;
|
||||
NSArray *folders;
|
||||
SOGoAppointmentFolders *co;
|
||||
SOGoAppointmentFolder *folder;
|
||||
NSMutableDictionary *calendar;
|
||||
unsigned int count, foldersCount;
|
||||
NSString *folderName, *fDisplayName;
|
||||
NSNumber *isActive;
|
||||
|
||||
co = [self clientObject];
|
||||
folders = [co subFolders];
|
||||
foldersCount = [folders count];
|
||||
calendarsToDisplay = [[NSMutableArray alloc] initWithCapacity: foldersCount];
|
||||
for (count = 0; count < foldersCount; count++)
|
||||
{
|
||||
folder = [folders objectAtIndex: count];
|
||||
isActive = [NSNumber numberWithBool: [folder isActive]];
|
||||
if ([isActive intValue] != 0) {
|
||||
calendar = [NSMutableDictionary dictionary];
|
||||
folderName = [folder nameInContainer];
|
||||
fDisplayName = [folder displayName];
|
||||
if (fDisplayName == nil)
|
||||
fDisplayName = @"";
|
||||
if ([fDisplayName isEqualToString: [co defaultFolderName]])
|
||||
fDisplayName = [self labelForKey: fDisplayName];
|
||||
[calendar setObject: [NSString stringWithFormat: @"/%@", folderName]
|
||||
forKey: @"id"];
|
||||
[calendar setObject: fDisplayName forKey: @"displayName"];
|
||||
[calendar setObject: folderName forKey: @"folder"];
|
||||
[calendar setObject: [folder calendarColor] forKey: @"color"];
|
||||
[calendar setObject: isActive forKey: @"active"];
|
||||
[calendar setObject: [folder ownerInContext: context]
|
||||
forKey: @"owner"];
|
||||
[calendarsToDisplay addObject: calendar];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return calendarsToDisplay;
|
||||
}
|
||||
|
||||
- (void) setCurrentTableDay: (NSCalendarDate *) aTableDay
|
||||
{
|
||||
currentTableDay = aTableDay;
|
||||
@@ -174,6 +235,16 @@
|
||||
return currentTableDay;
|
||||
}
|
||||
|
||||
- (void) setCurrentCalendar: (NSMutableArray *) aCalendar
|
||||
{
|
||||
ASSIGN(currentCalendar, aCalendar);
|
||||
}
|
||||
|
||||
- (NSMutableArray *) currentCalendar
|
||||
{
|
||||
return currentCalendar;
|
||||
}
|
||||
|
||||
- (void) setCurrentTableHour: (NSString *) aTableHour
|
||||
{
|
||||
currentTableHour = aTableHour;
|
||||
@@ -226,6 +297,16 @@
|
||||
return [dateFormatter shortFormattedDate: currentTableDay];
|
||||
}
|
||||
|
||||
- (NSString *) labelForCalendar
|
||||
{
|
||||
return [currentCalendar objectForKey: @"displayName"];
|
||||
}
|
||||
|
||||
- (NSString *) colorForCalendar
|
||||
{
|
||||
return [currentCalendar objectForKey:@"color"];
|
||||
}
|
||||
|
||||
// - (NSDictionary *) _adjustedAppointment: (NSDictionary *) anAppointment
|
||||
// forStart: (NSCalendarDate *) start
|
||||
// andEnd: (NSCalendarDate *) end
|
||||
@@ -318,7 +399,15 @@
|
||||
|
||||
- (NSString *) daysViewClasses
|
||||
{
|
||||
return [NSString stringWithFormat: @"daysView daysViewFor%dDays", numberOfDays];
|
||||
NSString *daysView;
|
||||
|
||||
if ([currentView isEqualToString:@"multicolumndayview"])
|
||||
daysView = @"daysView daysViewForMultipleDays";
|
||||
|
||||
else
|
||||
daysView = [NSString stringWithFormat: @"daysView daysViewFor%dDays", numberOfDays];
|
||||
|
||||
return daysView;
|
||||
}
|
||||
|
||||
- (NSString *) dayClasses
|
||||
@@ -326,19 +415,24 @@
|
||||
NSMutableString *classes;
|
||||
unsigned int currentDayNbr, realDayOfWeek;
|
||||
|
||||
currentDayNbr = [daysToDisplay indexOfObject: currentTableDay];
|
||||
realDayOfWeek = [currentTableDay dayOfWeek];
|
||||
|
||||
classes = [NSMutableString string];
|
||||
[classes appendFormat: @"day day%d", currentDayNbr];
|
||||
if (numberOfDays > 1)
|
||||
if ([currentView isEqualToString:@"multicolumndayview"])
|
||||
[classes appendFormat:@"day dayColumn"];
|
||||
else {
|
||||
currentDayNbr = [daysToDisplay indexOfObject: currentTableDay];
|
||||
realDayOfWeek = [currentTableDay dayOfWeek];
|
||||
|
||||
[classes appendFormat: @"day day%d", currentDayNbr];
|
||||
|
||||
if (numberOfDays > 1)
|
||||
{
|
||||
if (realDayOfWeek == 0 || realDayOfWeek == 6)
|
||||
[classes appendString: @" weekEndDay"];
|
||||
if ([currentTableDay isToday])
|
||||
[classes appendString: @" dayOfToday"];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
@@ -358,4 +452,20 @@
|
||||
return cellClass;
|
||||
}
|
||||
|
||||
- (BOOL) isMultiColumnView
|
||||
{
|
||||
if ([currentView isEqualToString:@"multicolumndayview"])
|
||||
return YES;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL) isNotMultiColumnView
|
||||
{
|
||||
if ([currentView isEqualToString:@"dayview"] || [currentView isEqualToString:@"weekview"])
|
||||
return YES;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
NSString *value;
|
||||
NSString *criteria;
|
||||
NSString *userLogin;
|
||||
NSString *currentView;
|
||||
BOOL dayBasedView;
|
||||
WORequest *request;
|
||||
SOGoDateFormatter *dateFormatter;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
/* UIxCalListingActions.m - this file is part of SOGo
|
||||
*
|
||||
* Copyright (C) 2006-2011 Inverse inc.
|
||||
*
|
||||
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
* Francis Lachapelle <flachapelle@inverse.ca>
|
||||
* Copyright (C) 2006-2014 Inverse inc.
|
||||
*
|
||||
* 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
|
||||
@@ -78,6 +75,8 @@ static NSArray *tasksFields = nil;
|
||||
#define maxBlocks (offsetBlocks * 2) // maximum number of blocks to search
|
||||
// for a free slot (10 days)
|
||||
|
||||
@class SOGoAppointment;
|
||||
|
||||
@implementation UIxCalListingActions
|
||||
|
||||
+ (void) initialize
|
||||
@@ -121,6 +120,7 @@ static NSArray *tasksFields = nil;
|
||||
ASSIGN (dateFormatter, [user dateFormatterInContext: context]);
|
||||
ASSIGN (userTimeZone, [[user userDefaults] timeZone]);
|
||||
dayBasedView = NO;
|
||||
currentView = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -239,6 +239,7 @@ static NSArray *tasksFields = nil;
|
||||
endDate = nil;
|
||||
|
||||
param = [request formValueForKey: @"view"];
|
||||
currentView = param;
|
||||
dayBasedView = ![param isEqualToString: @"monthview"];
|
||||
}
|
||||
}
|
||||
@@ -480,7 +481,7 @@ static NSArray *tasksFields = nil;
|
||||
return infos;
|
||||
}
|
||||
|
||||
- (WOResponse *) _responseWithData: (NSArray *) data
|
||||
- (WOResponse *) _responseWithData: (id) data
|
||||
{
|
||||
WOResponse *response;
|
||||
|
||||
@@ -1044,47 +1045,113 @@ _computeBlocksPosition (NSArray *blocks)
|
||||
/* ... _computeBlocksMultiplier() ... */
|
||||
}
|
||||
|
||||
- (NSArray *) _selectedCalendars
|
||||
{
|
||||
SOGoAppointmentFolders *co;
|
||||
SOGoAppointmentFolder *folder;
|
||||
NSMutableArray *selectedCalendars;
|
||||
NSArray *folders;
|
||||
NSString *fUID;
|
||||
NSNumber *isActive;
|
||||
unsigned int count, foldersCount;
|
||||
int max=0, i;
|
||||
|
||||
co = [self clientObject];
|
||||
folders = [co subFolders];
|
||||
foldersCount = [folders count];
|
||||
selectedCalendars = [[NSMutableArray alloc] initWithCapacity: foldersCount];
|
||||
for (count = 0; count < foldersCount; count++)
|
||||
{
|
||||
folder = [folders objectAtIndex: count];
|
||||
isActive = [NSNumber numberWithBool: [folder isActive]];
|
||||
if ([isActive intValue] != 0) {
|
||||
fUID = [folder nameInContainer];
|
||||
[selectedCalendars addObject: fUID];
|
||||
}
|
||||
}
|
||||
return selectedCalendars;
|
||||
}
|
||||
|
||||
- (WOResponse *) eventsBlocksAction
|
||||
{
|
||||
int count, max;
|
||||
NSArray *events, *event, *eventsBlocks;
|
||||
NSMutableArray *allDayBlocks, *blocks, *currentDay;
|
||||
NSMutableArray *allDayBlocks, *blocks, *currentDay, *calendars, *eventsByCalendars, *eventsForCalendar;
|
||||
NSNumber *eventNbr;
|
||||
BOOL isAllDay;
|
||||
int i, j;
|
||||
|
||||
[self _setupContext];
|
||||
|
||||
[self _prepareEventBlocks: &blocks withAllDays: &allDayBlocks];
|
||||
events = [self _fetchFields: eventsFields
|
||||
forComponentOfType: @"vevent"];
|
||||
eventsBlocks = [NSArray arrayWithObjects: events, allDayBlocks, blocks, nil];
|
||||
max = [events count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
event = [events objectAtIndex: count];
|
||||
// NSLog(@"***[UIxCalListingActions eventsBlocksAction] %i = %@ : %@ / %@ / %@", count,
|
||||
// [event objectAtIndex: eventTitleIndex],
|
||||
// [event objectAtIndex: eventStartDateIndex],
|
||||
// [event objectAtIndex: eventEndDateIndex],
|
||||
// [event objectAtIndex: eventRecurrenceIdIndex]);
|
||||
eventNbr = [NSNumber numberWithUnsignedInt: count];
|
||||
isAllDay = [[event objectAtIndex: eventIsAllDayIndex] boolValue];
|
||||
if (dayBasedView && isAllDay)
|
||||
[self _fillBlocks: allDayBlocks withEvent: event withNumber: eventNbr];
|
||||
else
|
||||
[self _fillBlocks: blocks withEvent: event withNumber: eventNbr];
|
||||
}
|
||||
events = [self _fetchFields: eventsFields forComponentOfType: @"vevent"];
|
||||
|
||||
max = [blocks count];
|
||||
for (count = 0; count < max; count++)
|
||||
if ([currentView isEqualToString: @"multicolumndayview"])
|
||||
{
|
||||
currentDay = [blocks objectAtIndex: count];
|
||||
[currentDay sortUsingSelector: @selector (compareEventByStart:)];
|
||||
[self _addBlocksWidth: currentDay];
|
||||
calendars = [self _selectedCalendars];
|
||||
eventsByCalendars = [NSMutableArray arrayWithCapacity:[calendars count]];
|
||||
for (i = 0; i < [calendars count]; i++) // For each calendar
|
||||
{
|
||||
eventsForCalendar = [NSMutableArray array];
|
||||
[self _prepareEventBlocks: &blocks withAllDays: &allDayBlocks];
|
||||
for (j = 0; j < [events count]; j++) {
|
||||
if ([[[events objectAtIndex:j] objectAtIndex:1] isEqualToString:[calendars objectAtIndex:i]]) {
|
||||
[eventsForCalendar addObject: [events objectAtIndex:j]];
|
||||
}
|
||||
}
|
||||
eventsBlocks = [NSArray arrayWithObjects:eventsForCalendar, allDayBlocks, blocks, nil];
|
||||
max = [eventsForCalendar count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
event = [eventsForCalendar objectAtIndex: count];
|
||||
eventNbr = [NSNumber numberWithUnsignedInt: count];
|
||||
isAllDay = [[event objectAtIndex: eventIsAllDayIndex] boolValue];
|
||||
if (dayBasedView && isAllDay)
|
||||
[self _fillBlocks: allDayBlocks withEvent: event withNumber: eventNbr];
|
||||
else
|
||||
[self _fillBlocks: blocks withEvent: event withNumber: eventNbr];
|
||||
}
|
||||
max = [blocks count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentDay = [blocks objectAtIndex: count];
|
||||
[currentDay sortUsingSelector: @selector (compareEventByStart:)];
|
||||
[self _addBlocksWidth: currentDay];
|
||||
}
|
||||
|
||||
[eventsByCalendars insertObject:eventsBlocks atIndex:i];
|
||||
}
|
||||
return [self _responseWithData: eventsByCalendars];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self _prepareEventBlocks: &blocks withAllDays: &allDayBlocks];
|
||||
eventsBlocks = [NSArray arrayWithObjects: events, allDayBlocks, blocks, nil];
|
||||
max = [events count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
event = [events objectAtIndex: count];
|
||||
// NSLog(@"***[UIxCalListingActions eventsBlocksAction] %i = %@ : %@ / %@ / %@", count,
|
||||
// [event objectAtIndex: eventTitleIndex],
|
||||
// [event objectAtIndex: eventStartDateIndex],
|
||||
// [event objectAtIndex: eventEndDateIndex],
|
||||
// [event objectAtIndex: eventRecurrenceIdIndex]);
|
||||
eventNbr = [NSNumber numberWithUnsignedInt: count];
|
||||
isAllDay = [[event objectAtIndex: eventIsAllDayIndex] boolValue];
|
||||
if (dayBasedView && isAllDay)
|
||||
[self _fillBlocks: allDayBlocks withEvent: event withNumber: eventNbr];
|
||||
else
|
||||
[self _fillBlocks: blocks withEvent: event withNumber: eventNbr];
|
||||
}
|
||||
|
||||
max = [blocks count];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
currentDay = [blocks objectAtIndex: count];
|
||||
[currentDay sortUsingSelector: @selector (compareEventByStart:)];
|
||||
[self _addBlocksWidth: currentDay];
|
||||
}
|
||||
return [self _responseWithData: eventsBlocks];
|
||||
}
|
||||
|
||||
return [self _responseWithData: eventsBlocks];
|
||||
// timeIntervalSinceDate:
|
||||
}
|
||||
|
||||
- (NSString *) _getStatusClassForStatusCode: (int) statusCode
|
||||
@@ -1208,4 +1275,28 @@ _computeBlocksPosition (NSArray *blocks)
|
||||
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
|
||||
|
||||
@@ -39,25 +39,25 @@
|
||||
- (id) init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
// allAppointments = nil;
|
||||
subscriptionUsers = nil;
|
||||
hoursToDisplay = nil;
|
||||
currentTableUser = nil;
|
||||
currentTableHour = nil;
|
||||
// dateFormatter = [[SOGoDateFormatter alloc]
|
||||
// initWithLocale: [self locale]];
|
||||
}
|
||||
|
||||
{
|
||||
// allAppointments = nil;
|
||||
subscriptionUsers = nil;
|
||||
hoursToDisplay = nil;
|
||||
currentTableUser = nil;
|
||||
currentTableHour = nil;
|
||||
// dateFormatter = [[SOGoDateFormatter alloc]
|
||||
// initWithLocale: [self locale]];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
// [allAppointments release];
|
||||
// [allAppointments release];
|
||||
[subscriptionUsers release];
|
||||
[hoursToDisplay release];
|
||||
// [dateFormatter release];
|
||||
// [dateFormatter release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@@ -188,16 +188,6 @@
|
||||
|
||||
/* fetching */
|
||||
|
||||
// - (NSCalendarDate *) startDate
|
||||
// {
|
||||
// return [[self selectedDate] beginOfDay];
|
||||
// }
|
||||
|
||||
// - (NSCalendarDate *) endDate
|
||||
// {
|
||||
// return [[self selectedDate] endOfDay];
|
||||
// }
|
||||
|
||||
// - (NSArray *) appointmentsForCurrentUser
|
||||
// {
|
||||
// NSMutableArray *filteredAppointments;
|
||||
@@ -292,4 +282,11 @@
|
||||
* (100.0 / [users count]))];
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) defaultAction
|
||||
{
|
||||
[super setCurrentView: @"multicolumndayview"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
@interface UIxCalViewPrint : UIxComponent
|
||||
{
|
||||
id item;
|
||||
SOGoUserSettings *us;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSArray.h>
|
||||
|
||||
#import <SOGo/SOGoUserSettings.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
|
||||
#import <UIxCalViewPrint.h>
|
||||
|
||||
static NSArray *layoutItems = nil;
|
||||
@@ -32,21 +35,22 @@ static NSArray *layoutItems = nil;
|
||||
{
|
||||
if (!layoutItems)
|
||||
{
|
||||
layoutItems = [NSArray arrayWithObjects: @"LIST", @"Daily", @"Weekly", nil];
|
||||
layoutItems = [NSArray arrayWithObjects: @"LIST", @"Daily", @"Multi-Columns", @"Weekly", nil];
|
||||
[layoutItems retain];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
item = nil;
|
||||
return [super init];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[item release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/****************************************************************/
|
||||
@@ -72,10 +76,29 @@ static NSArray *layoutItems = nil;
|
||||
return [self labelForKey: [NSString stringWithFormat: item]];
|
||||
}
|
||||
|
||||
- (NSString *) layoutSelectedByUser
|
||||
- (NSString *) parentPrintLayout
|
||||
{
|
||||
return nil;
|
||||
// The objective here is to return the parent view layout and select the print layout corresponding. Default print view: list view
|
||||
SOGoUser *activeUser;
|
||||
NSString *parentView;
|
||||
|
||||
activeUser = [context activeUser];
|
||||
us = [activeUser userSettings];
|
||||
parentView = [[us objectForKey:@"Calendar"] objectForKey:@"View" ];
|
||||
|
||||
if ([parentView isEqualToString:@"dayview"])
|
||||
return @"Daily";
|
||||
|
||||
else if ([parentView isEqualToString:@"weekview"])
|
||||
return @"Weekly";
|
||||
|
||||
else if ([parentView isEqualToString:@"multicolumndayview"])
|
||||
return @"Multi-Columns";
|
||||
|
||||
else
|
||||
return @"LIST";
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/* */
|
||||
|
||||
|
||||
@@ -93,37 +93,39 @@ _intValueFromHex (NSString *hexString)
|
||||
NSMutableDictionary *calendar;
|
||||
unsigned int count, max;
|
||||
NSString *folderName, *fDisplayName;
|
||||
NSNumber *isActive;
|
||||
|
||||
NSNumber *isActive, *fActiveTasks;
|
||||
|
||||
if (!calendars)
|
||||
{
|
||||
co = [self clientObject];
|
||||
folders = [co subFolders];
|
||||
max = [folders count];
|
||||
calendars = [[NSMutableArray alloc] initWithCapacity: max];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
co = [self clientObject];
|
||||
folders = [co subFolders];
|
||||
max = [folders count];
|
||||
calendars = [[NSMutableArray alloc] initWithCapacity: max];
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
folder = [folders objectAtIndex: count];
|
||||
calendar = [NSMutableDictionary dictionary];
|
||||
folderName = [folder nameInContainer];
|
||||
fDisplayName = [folder displayName];
|
||||
if (fDisplayName == nil)
|
||||
fDisplayName = @"";
|
||||
if ([fDisplayName isEqualToString: [co defaultFolderName]])
|
||||
fDisplayName = [self labelForKey: fDisplayName];
|
||||
[calendar setObject: [NSString stringWithFormat: @"/%@", folderName]
|
||||
forKey: @"id"];
|
||||
[calendar setObject: fDisplayName forKey: @"displayName"];
|
||||
[calendar setObject: folderName forKey: @"folder"];
|
||||
[calendar setObject: [folder calendarColor] forKey: @"color"];
|
||||
isActive = [NSNumber numberWithBool: [folder isActive]];
|
||||
[calendar setObject: isActive forKey: @"active"];
|
||||
[calendar setObject: [folder ownerInContext: context]
|
||||
forKey: @"owner"];
|
||||
[calendars addObject: calendar];
|
||||
}
|
||||
folder = [folders objectAtIndex: count];
|
||||
calendar = [NSMutableDictionary dictionary];
|
||||
folderName = [folder nameInContainer];
|
||||
fDisplayName = [folder displayName];
|
||||
if (fDisplayName == nil)
|
||||
fDisplayName = @"";
|
||||
if ([fDisplayName isEqualToString: [co defaultFolderName]])
|
||||
fDisplayName = [self labelForKey: fDisplayName];
|
||||
[calendar setObject: [NSString stringWithFormat: @"/%@", folderName]
|
||||
forKey: @"id"];
|
||||
[calendar setObject: fDisplayName forKey: @"displayName"];
|
||||
[calendar setObject: folderName forKey: @"folder"];
|
||||
[calendar setObject: [folder calendarColor] forKey: @"color"];
|
||||
isActive = [NSNumber numberWithBool: [folder isActive]];
|
||||
[calendar setObject: isActive forKey: @"active"];
|
||||
[calendar setObject: [folder ownerInContext: context]
|
||||
forKey: @"owner"];
|
||||
fActiveTasks = [folder activeTasks];
|
||||
[calendar setObject:fActiveTasks forKey:@"activeTasks" ];
|
||||
[calendars addObject: calendar];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return calendars;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,11 @@
|
||||
actionClass = "UIxCalListingActions";
|
||||
actionName = "tasksList";
|
||||
};
|
||||
activeTasks = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxCalListingActions";
|
||||
actionName = "activeTasks";
|
||||
};
|
||||
dayview = {
|
||||
protectedBy = "View";
|
||||
pageName = "UIxCalDayView";
|
||||
|
||||
@@ -299,7 +299,8 @@
|
||||
<dd><input type="checkbox"
|
||||
const:name="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:popup list="addressBookList" item="item"
|
||||
const:id="addressBookList"
|
||||
|
||||
@@ -7,51 +7,105 @@
|
||||
xmlns:rsrc="OGo:url"
|
||||
xmlns:label="OGo:label">
|
||||
<div id="calendarHeader" var:class="daysViewClasses">
|
||||
<div class="dayLabels"
|
||||
><var:foreach list="daysToDisplay" item="currentTableDay"
|
||||
><div var:class="dayClasses"
|
||||
><var:string value="labelForDate"/><br
|
||||
/><span class="dayOfWeek"><var:string value="labelForDay"/></span
|
||||
></div
|
||||
></var:foreach>
|
||||
</div>
|
||||
<div class="days"
|
||||
><var:foreach list="daysToDisplay" item="currentTableDay"
|
||||
><div var:class="dayClasses"
|
||||
var:day="currentTableDay.shortDateString"
|
||||
var:id="currentAllDayId"
|
||||
hour="allday"
|
||||
><!-- space --></div
|
||||
></var:foreach
|
||||
></div>
|
||||
|
||||
<!-- MultiColumnView -->
|
||||
<!-- Display the name of the calendar -->
|
||||
<var:if condition="isMultiColumnView">
|
||||
<div class="calendarLabels">
|
||||
<var:foreach list="calendarsToDisplay" item="currentCalendar">
|
||||
<div class="calendarsToDisplay">
|
||||
<var:string value="labelForCalendar"/>
|
||||
</div>
|
||||
</var:foreach>
|
||||
</div>
|
||||
|
||||
<!-- Display today abreviation and date -->
|
||||
<div class="dayLabels">
|
||||
<var:foreach list="daysToDisplay" item="currentTableDay">
|
||||
<div var:class="dayClasses">
|
||||
<var:string value="labelForDate"/><br/>
|
||||
<span class="dayOfWeek"><var:string value="labelForDay"/></span>
|
||||
</div>
|
||||
</var:foreach>
|
||||
</div>
|
||||
|
||||
<!-- All day cells -->
|
||||
<div class="days">
|
||||
<var:foreach list="calendarsToDisplay" item="currentCalendar">
|
||||
<var:foreach list="daysToDisplay" item="currentTableDay">
|
||||
<div var:class="dayClasses" var:day="currentTableDay.shortDateString" var:id="currentAllDayId" hour="allday"><!-- space --></div>
|
||||
</var:foreach>
|
||||
</var:foreach>
|
||||
</div>
|
||||
</var:if>
|
||||
|
||||
<!-- DayView or WeekView -->
|
||||
<var:if condition="isNotMultiColumnView">
|
||||
<!-- Display today abreviation and date -->
|
||||
<div class="dayLabels">
|
||||
<var:foreach list="daysToDisplay" item="currentTableDay">
|
||||
<div var:class="dayClasses">
|
||||
<var:string value="labelForDate"/><br/>
|
||||
<span class="dayOfWeek"><var:string value="labelForDay"/></span>
|
||||
</div>
|
||||
</var:foreach>
|
||||
</div>
|
||||
|
||||
<!-- All day cells -->
|
||||
<div class="days">
|
||||
<var:foreach list="daysToDisplay" item="currentTableDay">
|
||||
<div var:class="dayClasses" var:day="currentTableDay.shortDateString" var:id="currentAllDayId" hour="allday"><!-- space --></div>
|
||||
</var:foreach>
|
||||
</div>
|
||||
</var:if>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- The hours cells -->
|
||||
<div id="daysView" var:class="daysViewClasses">
|
||||
<div class="hours"
|
||||
><var:foreach list="hoursToDisplay" item="currentTableHour"
|
||||
><div class="hour" var:id="currentHourId"
|
||||
><var:string value="currentTableHour"/></div
|
||||
></var:foreach>
|
||||
</div>
|
||||
|
||||
<div class="days">
|
||||
<var:foreach list="daysToDisplay" item="currentTableDay"
|
||||
><div var:class="dayClasses"
|
||||
var:id="currentDayId"
|
||||
var:day-number="currentDayNumber"
|
||||
var:day="currentTableDay.shortDateString">
|
||||
<div class="hourCells">
|
||||
<var:foreach list="hoursToDisplay" item="currentTableHour"
|
||||
><div var:class="clickableHourCellClass"
|
||||
var:day="currentTableDay.shortDateString"
|
||||
var:hour="currentAppointmentHour">
|
||||
<span class="minutes15"><!-- space --></span>
|
||||
<span class="minutes30"><!-- space --></span>
|
||||
<span class="minutes45"><!-- space --></span>
|
||||
</div></var:foreach></div>
|
||||
<div class="events"><!-- space --></div>
|
||||
<div class="hours">
|
||||
<var:foreach list="hoursToDisplay" item="currentTableHour">
|
||||
<div class="hour" var:id="currentHourId">
|
||||
<var:string value="currentTableHour"/>
|
||||
</div>
|
||||
</var:foreach>
|
||||
</div>
|
||||
<div class="days">
|
||||
<!-- MultiColumnView -->
|
||||
<var:if condition="isMultiColumnView">
|
||||
<var:foreach list="calendarsToDisplay" item="currentCalendar">
|
||||
<var:foreach list="daysToDisplay" item="currentTableDay">
|
||||
<div var:class="dayClasses" var:id="currentDayId" var:day-number="currentDayNumber" var:day="currentTableDay.shortDateString">
|
||||
<div class="hourCells">
|
||||
<var:foreach list="hoursToDisplay" item="currentTableHour">
|
||||
<div var:class="clickableHourCellClass" var:day="currentTableDay.shortDateString" var:hour="currentAppointmentHour">
|
||||
<span class="minutes15"><!-- space --></span>
|
||||
<span class="minutes30"><!-- space --></span>
|
||||
<span class="minutes45"><!-- space --></span>
|
||||
</div>
|
||||
</var:foreach>
|
||||
</div>
|
||||
<div class="events"><!-- space --></div>
|
||||
</div>
|
||||
</var:foreach>
|
||||
</var:foreach>
|
||||
</var:if>
|
||||
<var:if condition="isNotMultiColumnView">
|
||||
<var:foreach list="daysToDisplay" item="currentTableDay">
|
||||
<div var:class="dayClasses" var:id="currentDayId" var:day-number="currentDayNumber" var:day="currentTableDay.shortDateString">
|
||||
<div class="hourCells">
|
||||
<var:foreach list="hoursToDisplay" item="currentTableHour">
|
||||
<div var:class="clickableHourCellClass" var:day="currentTableDay.shortDateString" var:hour="currentAppointmentHour">
|
||||
<span class="minutes15"><!-- space --></span>
|
||||
<span class="minutes30"><!-- space --></span>
|
||||
<span class="minutes45"><!-- space --></span>
|
||||
</div>
|
||||
</var:foreach>
|
||||
</div>
|
||||
<div class="events"><!-- space --></div>
|
||||
</div>
|
||||
</var:foreach>
|
||||
</var:if>
|
||||
</div>
|
||||
</div>
|
||||
</container>
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
className="UIxCalDayTable"
|
||||
startDate="startDate"
|
||||
const:CSSClass="dayOverview"
|
||||
const:numberOfDays="1" />
|
||||
const:numberOfDays="1"
|
||||
const:currentView="dayview"/>
|
||||
</div>
|
||||
</container>
|
||||
|
||||
@@ -5,40 +5,33 @@
|
||||
xmlns:const="http://www.skyrix.com/od/constant"
|
||||
xmlns:rsrc="OGo:url"
|
||||
xmlns:label="OGo:label">
|
||||
<a href="#"
|
||||
id="leftNavigationArrow"
|
||||
var:date="prevDayQueryParameters.day"
|
||||
onclick="return onCalendarGotoDay(this);"
|
||||
><img rsrc:src="arrow-left.png"/></a>
|
||||
|
||||
<div class="menu" id="currentViewMenu">
|
||||
<ul>
|
||||
<li><var:string label:value="New Event..."/></li>
|
||||
<li><var:string label:value="New Task..."/></li>
|
||||
<li><!-- separator --></li>
|
||||
<li><var:string label:value="Previous Day"/></li>
|
||||
<li><var:string label:value="Next Day"/></li>
|
||||
<li><!-- separator --></li>
|
||||
<li><var:string label:value="Delete Event"/></li>
|
||||
<li><var:string label:value="Copy event to my calendar"/></li>
|
||||
<li><var:string label:value="View Raw Source"/></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Navigation header -->
|
||||
<span class="daysHeader">
|
||||
<span class="day2"><a href="#"
|
||||
var:date="dayBeforePrevDayQueryParameters.day"
|
||||
onclick="return onCalendarGotoDay(this);"
|
||||
><var:string value="dayBeforeYesterdayName"
|
||||
/></a></span
|
||||
><span class="day1"><a href="#"
|
||||
var:date="prevDayQueryParameters.day"
|
||||
onclick="return onCalendarGotoDay(this);"
|
||||
><var:string value="yesterdayName"
|
||||
/></a></span
|
||||
><span class="day0"><var:string value="currentDayName" /></span
|
||||
><span class="day1"><a href="#"
|
||||
var:date="nextDayQueryParameters.day"
|
||||
onclick="return onCalendarGotoDay(this);"
|
||||
><var:string value="tomorrowName"
|
||||
/></a></span
|
||||
><span class="day2"><a href="#"
|
||||
var:date="dayAfterNextDayQueryParameters.day"
|
||||
onclick="return onCalendarGotoDay(this);"
|
||||
><var:string value="dayAfterTomorrowName"
|
||||
/></a></span
|
||||
></span>
|
||||
<a href="#"
|
||||
id="rightNavigationArrow"
|
||||
var:date="nextDayQueryParameters.day"
|
||||
onclick="return onCalendarGotoDay(this);"
|
||||
><img rsrc:src="arrow-right.png"/></a>
|
||||
<span class="day1">
|
||||
<a href="#" id="leftNavigationArrow" var:date="prevDayQueryParameters.day" onclick="return onCalendarGotoDay(this);">
|
||||
<img rsrc:src="arrow-left.png"/></a></span>
|
||||
<span class="day0"><var:string value="currentDayName" /></span>
|
||||
<span class="day1">
|
||||
<a href="#" id="rightNavigationArrow" var:date="nextDayQueryParameters.day" onclick="return onCalendarGotoDay(this);">
|
||||
<img rsrc:src="arrow-right.png"/></a></span>
|
||||
<a href="#" id="listCollapse"><img var:class="collapseBtnClass" rsrc:src="collapse.png"/></a>
|
||||
</span>
|
||||
|
||||
<!-- TODO : Move this inside the schedulerUI.css -->
|
||||
<style type="text/css">
|
||||
DIV.day
|
||||
{
|
||||
@@ -51,69 +44,13 @@
|
||||
}
|
||||
</var:foreach>
|
||||
</style>
|
||||
|
||||
|
||||
<!-- Display the calendar header and the table view -->
|
||||
<div id="calendarContent">
|
||||
<div id="daysView" class="daysView multicolumnDayView">
|
||||
<div class="hours">
|
||||
<var:foreach list="hoursToDisplay" item="currentTableHour"
|
||||
><div class="hour"><var:string value="currentTableHour" />:00</div
|
||||
></var:foreach>
|
||||
</div>
|
||||
<div class="hourLines">
|
||||
<div class="hourLine hourLine0"><!-- space --></div
|
||||
><div class="hourLine hourLine1"><!-- space --></div
|
||||
><div class="hourLine hourLine2"><!-- space --></div
|
||||
><div class="hourLine hourLine3"><!-- space --></div
|
||||
><div class="hourLine hourLine4"><!-- space --></div
|
||||
><div class="hourLine hourLine5"><!-- space --></div
|
||||
><div class="hourLine hourLine6"><!-- space --></div
|
||||
><div class="hourLine hourLine7"><!-- space --></div
|
||||
><div class="hourLine hourLine8"><!-- space --></div
|
||||
><div class="hourLine hourLine9"><!-- space --></div
|
||||
><div class="hourLine hourLine10"><!-- space --></div
|
||||
><div class="hourLine hourLine11"><!-- space --></div
|
||||
><div class="hourLine hourLine12"><!-- space --></div
|
||||
><div class="hourLine hourLine13"><!-- space --></div
|
||||
><div class="hourLine hourLine14"><!-- space --></div
|
||||
><div class="hourLine hourLine15"><!-- space --></div
|
||||
><div class="hourLine hourLine16"><!-- space --></div
|
||||
><div class="hourLine hourLine17"><!-- space --></div
|
||||
><div class="hourLine hourLine18"><!-- space --></div
|
||||
><div class="hourLine hourLine19"><!-- space --></div
|
||||
><div class="hourLine hourLine20"><!-- space --></div
|
||||
><div class="hourLine hourLine21"><!-- space --></div
|
||||
><div class="hourLine hourLine22"><!-- space --></div
|
||||
><div class="hourLine hourLine23"><!-- space --></div>
|
||||
</div>
|
||||
|
||||
<div class="days" var:day="selectedDate.shortDateString">
|
||||
<var:foreach list="subscriptionUsers" item="currentTableUser"
|
||||
><div var:class="currentUserClasses"
|
||||
var:user="currentTableUser"
|
||||
><div class="header"><var:string value="currentTableUser" /></div>
|
||||
<div class="appointments">
|
||||
<var:foreach list="hoursToDisplay" item="currentTableHour"
|
||||
><div var:class="clickableHourCellClass"
|
||||
var:hour="currentAppointmentHour">
|
||||
</div></var:foreach>
|
||||
<var:foreach list="appointmentsForCurrentUser" item="currentAppointment"
|
||||
><var:component className="UIxCalInlineAptView"
|
||||
dayStartHour="dayStartHour"
|
||||
dayEndHour="dayEndHour"
|
||||
appointment="currentAppointment"
|
||||
formatter="aptFormatter"
|
||||
tooltipFormatter="aptTooltipFormatter"
|
||||
url="appointmentViewURL"
|
||||
const:style="dayoverview"
|
||||
queryDictionary="currentDateQueryParameters"
|
||||
referenceDate="selectedDate"
|
||||
canAccess="canAccessApt"
|
||||
/></var:foreach
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</var:foreach>
|
||||
</div>
|
||||
</div>
|
||||
<var:component
|
||||
className="UIxCalDayTable"
|
||||
startDate="startDate"
|
||||
const:CSSClass="dayOverview"
|
||||
const:currentView="multicolumndayview"/>
|
||||
</div>
|
||||
</container>
|
||||
</container>
|
||||
@@ -25,10 +25,10 @@
|
||||
<td>
|
||||
<var:popup list="printLayoutList" item="item"
|
||||
const:id="printLayoutList"
|
||||
string="itemPrintLayoutText" /></td>
|
||||
string="itemPrintLayoutText" selection="parentPrintLayout" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<!-- What to print -->
|
||||
<span class="caption"><var:string label:value="What to Print" /></span>
|
||||
<table class="frame">
|
||||
@@ -81,6 +81,16 @@
|
||||
<!-- Options -->
|
||||
<span class="caption"><var:string label:value="Options" /></span>
|
||||
<table class="frame">
|
||||
<tr>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="printWorkingHours" id="printHours" checked="true" onChange="onPrintWorkingHoursCheck(this);" />
|
||||
<var:string label:value="Display working hours only" /></label></td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label>
|
||||
<hr /></label></td></tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<label>
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
className="UIxCalDayTable"
|
||||
startDate="startDate"
|
||||
const:CSSClass="weekOverview"
|
||||
const:numberOfDays="7" />
|
||||
const:numberOfDays="7"
|
||||
const:currentView="weekview"/>
|
||||
</div>
|
||||
</container>
|
||||
|
||||
@@ -38,9 +38,10 @@ div.colorBox.calendarFolder<var:string value="currentCalendar.folder" />
|
||||
<var:foreach list="calendars" item="currentCalendar"
|
||||
><li class="denied" var:id="currentCalendar.id" var:owner="currentCalendar.owner" >
|
||||
<input type="checkbox" class="checkBox" const:disabled="disabled" var:checked="currentCalendar.active" />
|
||||
<div var:class="currentCalendarClass"><entity name="nbsp"/></div
|
||||
><var:string value="currentCalendar.displayName"
|
||||
/></li>
|
||||
<div var:class="currentCalendarClass"><entity name="nbsp"/></div>
|
||||
<var:string value="currentCalendar.displayName" />
|
||||
<span id="activeTasks"><var:string value="currentCalendar.activeTasks"/></span>
|
||||
</li>
|
||||
</var:foreach>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1749,7 +1749,7 @@ function download(url) {
|
||||
setTimeout(function () {
|
||||
form.remove();
|
||||
div.remove();
|
||||
}, 2000);
|
||||
}, 20000);
|
||||
}
|
||||
|
||||
function saveAttachment(event) {
|
||||
|
||||
@@ -392,7 +392,7 @@ DIV#daysView
|
||||
left: 0px; }
|
||||
|
||||
DIV#daysView
|
||||
{ top: 120px;
|
||||
{ top: 97px;
|
||||
bottom: 0px;
|
||||
border-top: 1px solid #ccc;
|
||||
overflow: auto;
|
||||
@@ -400,11 +400,12 @@ DIV#daysView
|
||||
right: 0px;}
|
||||
|
||||
DIV#calendarHeader
|
||||
{ top: 50px;
|
||||
{ top: 25px;
|
||||
border: 0px;
|
||||
height: 100px;
|
||||
height: 85px;
|
||||
right: 0px; }
|
||||
|
||||
DIV#calendarHeader DIV.calendarLabels,
|
||||
DIV#calendarHeader DIV.dayLabels,
|
||||
DIV#calendarHeader DIV.days
|
||||
{ position: absolute;
|
||||
@@ -414,8 +415,16 @@ DIV#calendarHeader DIV.days
|
||||
/* 'right' is computed from JS code when daysView is first drawn */
|
||||
overflow: hidden; }
|
||||
|
||||
DIV#calendarHeader DIV.calendarLabels
|
||||
{ top: 11px;
|
||||
height: 20px;
|
||||
border-left: 1px solid #ccc;
|
||||
text-align: center;
|
||||
font-style:italic;
|
||||
font-size:150%;
|
||||
}
|
||||
DIV#calendarHeader DIV.dayLabels
|
||||
{ top: 0px;
|
||||
{ top: 32px;
|
||||
height: 35px; }
|
||||
|
||||
DIV#calendarHeader DIV.dayLabels DIV.day
|
||||
@@ -432,7 +441,11 @@ DIV#calendarHeader DIV.dayLabels
|
||||
DIV#calendarHeader DIV.days
|
||||
{ cursor: pointer;
|
||||
bottom: 0px;
|
||||
top: 35px; }
|
||||
top: 67px;
|
||||
z-index:0;
|
||||
border-bottom:1px solid #ccc;
|
||||
background:white;
|
||||
}
|
||||
|
||||
DIV#calendarHeader DIV.day,
|
||||
DIV#daysView DIV.day
|
||||
@@ -530,7 +543,8 @@ DIV.monthView DIV.dayHeader
|
||||
text-align: right;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
-moz-user-select: none; }
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
DIV#monthDaysView DIV.selectedDay DIV.dayHeader
|
||||
{ font-weight: bold; }
|
||||
@@ -540,7 +554,8 @@ DIV.monthView DIV.days
|
||||
cursor: pointer;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px; }
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
DIV.monthView DIV.day
|
||||
{ position: absolute;
|
||||
@@ -1279,13 +1294,14 @@ DIV#daysView DIV.event.lasts96
|
||||
DIV.event > DIV.eventInside
|
||||
{ position: absolute;
|
||||
overflow: hidden;
|
||||
padding: 1px;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
left: 2px;
|
||||
right: 1px;
|
||||
-webkit-border-radius: 2px;
|
||||
border-radius: 2px; }
|
||||
|
||||
DIV.eventInside {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
DIV.eventInside SPAN.location
|
||||
{ font-size: smaller; }
|
||||
@@ -1471,7 +1487,7 @@ DIV.event.draggable:hover DIV.rightDragGrip
|
||||
#ghostEndHour
|
||||
{ bottom: -14px; }
|
||||
|
||||
DIV#nowLineDisplay
|
||||
DIV.nowLineDisplay
|
||||
{ position: relative;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
@@ -1486,3 +1502,38 @@ DIV#freeBusyReplicas DIV
|
||||
{ display: inline-block; height: 2em; }
|
||||
TD.attendees A.button
|
||||
{ float: left; left: 19px; margin-top: 5px; }
|
||||
|
||||
DIV#calendarContent .alert-box {
|
||||
color:#555;
|
||||
border-radius:10px;
|
||||
font-family:Tahoma,Geneva,Arial,sans-serif;font-size:11px;
|
||||
padding:10px 10px 10px 36px;
|
||||
margin:10px;
|
||||
}
|
||||
|
||||
DIV#calendarContent .alert-box span {
|
||||
font-weight:bold;
|
||||
text-transform:uppercase;
|
||||
}
|
||||
|
||||
.error {
|
||||
background:#ffecec url('error.png') no-repeat 10px 50%;
|
||||
border:1px solid #f5aca6;
|
||||
}
|
||||
.success {
|
||||
background:#e9ffd9 url('success.png') no-repeat 10px 50%;
|
||||
border:1px solid #a6ca8a;
|
||||
}
|
||||
.warning {
|
||||
background:#fff8c4 url('warning.png') no-repeat 10px 50%;
|
||||
border:1px solid #f2c779;
|
||||
}
|
||||
.notice {
|
||||
background:#e3f7fc url('notice.png') no-repeat 10px 50%;
|
||||
border:1px solid #8ed9f6;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -34,15 +34,32 @@ var categoriesStyleSheet = null;
|
||||
var clipboard = null;
|
||||
var eventsToCopy = [];
|
||||
|
||||
|
||||
// This should probably go in the generic.js
|
||||
function printView() {
|
||||
var flag = false;
|
||||
var calendarsList = $("calendarList").children;
|
||||
for (var i = 0; i < calendarsList.length; i++) {
|
||||
if (calendarsList[i].down("input").checked) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag)
|
||||
window.open("printView","","width=660,height=470");
|
||||
|
||||
else
|
||||
showAlertDialog("Please select at least one calendar");
|
||||
|
||||
window.open("printView","","width=660,height=470");
|
||||
|
||||
return false; /* stop following the link */
|
||||
return false;
|
||||
}
|
||||
|
||||
function newEvent(type, day, hour, duration) {
|
||||
var folder = null;
|
||||
/* if (currentView == "multicolumndayview") {
|
||||
Need to find where the click register is saved
|
||||
}*/
|
||||
if (UserDefaults['SOGoDefaultCalendar'] == 'personal')
|
||||
folder = $("calendarList").down("li");
|
||||
else if (UserDefaults['SOGoDefaultCalendar'] == 'first') {
|
||||
@@ -400,13 +417,14 @@ function deleteEvent() {
|
||||
}
|
||||
|
||||
function deleteEventFromListConfirm() {
|
||||
if (document.deleteEventAjaxRequest) {
|
||||
document.deleteEventAjaxRequest.aborted = true;
|
||||
document.deleteEventAjaxRequest.abort();
|
||||
}
|
||||
|
||||
_batchDeleteEvents();
|
||||
disposeDialog();
|
||||
if (document.deleteEventAjaxRequest) {
|
||||
document.deleteEventAjaxRequest.aborted = true;
|
||||
document.deleteEventAjaxRequest.abort();
|
||||
}
|
||||
|
||||
_batchDeleteEvents();
|
||||
refreshTasks();
|
||||
disposeDialog();
|
||||
}
|
||||
|
||||
function deleteEventFromViewConfirm() {
|
||||
@@ -911,8 +929,8 @@ function onDaySelect(node) {
|
||||
&& day != currentDay);
|
||||
|
||||
var td = $(node).getParentWithTagName("td");
|
||||
var table = $(td).getParentWithTagName("table");
|
||||
|
||||
|
||||
// var table = $(td).getParentWithTagName("table");
|
||||
// log ("table.selected: " + table.selected);
|
||||
|
||||
if (document.selectedDate)
|
||||
@@ -1085,142 +1103,166 @@ function eventsListCallback(http) {
|
||||
log ("eventsListCallback 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");
|
||||
function activeTasksCallback(http) {
|
||||
if (http.readyState == 4 && http.status == 200) {
|
||||
if (http.responseText.length > 0) {
|
||||
document.activeTasksAjaxRequest = null;
|
||||
var data = http.responseText.evalJSON(true);
|
||||
var list = $("calendarList");
|
||||
|
||||
var items = list.childNodesWithTag("li");
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var id = items[i].getAttribute("id").substr(1);
|
||||
var number = data[id];
|
||||
var input = items[i].childNodesWithTag("input")[0];
|
||||
var activeTasks = items[i].childNodesWithTag("span")[0];
|
||||
if (number == "0") {
|
||||
activeTasks.innerHTML = "";
|
||||
}
|
||||
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 */
|
||||
@@ -1589,7 +1631,7 @@ function refreshCalendarEvents(scrollEvent) {
|
||||
if (!currentDay)
|
||||
currentDay = todayDate.getDayString();
|
||||
|
||||
if (currentView == "dayview") {
|
||||
if (currentView == "dayview" || currentView == "multicolumndayview") {
|
||||
sd = currentDay;
|
||||
ed = sd;
|
||||
}
|
||||
@@ -1627,28 +1669,54 @@ function refreshCalendarEvents(scrollEvent) {
|
||||
"scrollEvent": scrollEvent});
|
||||
}
|
||||
|
||||
function _parseEvents(list) {
|
||||
function _parseEvents(list, calendars) {
|
||||
var newCalendarEvents = {};
|
||||
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
if (currentView == "multicolumndayview") {
|
||||
var list;
|
||||
for (var i = 0; i < calendars.length; i++) {
|
||||
list = calendars[i][0];
|
||||
for (var j = 0; j < list.length; j++) {
|
||||
var event = list[j];
|
||||
var cname = event[0];
|
||||
var calendar = event[1];
|
||||
// log("parsed cname: " + cname + "; calendar: " + calendar);
|
||||
var calendarDict = newCalendarEvents[calendar];
|
||||
if (!calendarDict) {
|
||||
calendarDict = {};
|
||||
newCalendarEvents[calendar] = calendarDict;
|
||||
}
|
||||
var occurences = calendarDict[cname];
|
||||
if (!occurences) {
|
||||
occurences = [];
|
||||
calendarDict[cname] = occurences;
|
||||
}
|
||||
event.blocks = [];
|
||||
occurences.push(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var event = list[i];
|
||||
var cname = event[0];
|
||||
var calendar = event[1];
|
||||
// log("parsed cname: " + cname + "; calendar: " + calendar);
|
||||
var calendarDict = newCalendarEvents[calendar];
|
||||
if (!calendarDict) {
|
||||
calendarDict = {};
|
||||
newCalendarEvents[calendar] = calendarDict;
|
||||
calendarDict = {};
|
||||
newCalendarEvents[calendar] = calendarDict;
|
||||
}
|
||||
var occurences = calendarDict[cname];
|
||||
if (!occurences) {
|
||||
occurences = [];
|
||||
calendarDict[cname] = occurences;
|
||||
occurences = [];
|
||||
calendarDict[cname] = occurences;
|
||||
}
|
||||
event.blocks = [];
|
||||
occurences.push(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return newCalendarEvents;
|
||||
}
|
||||
|
||||
@@ -1680,15 +1748,28 @@ function refreshCalendarEventsCallback(http) {
|
||||
if (http.readyState == 4
|
||||
&& http.status == 200) {
|
||||
if (http.responseText.length > 0) {
|
||||
var eventsBlocks = http.responseText.evalJSON(true);
|
||||
calendarEvents = _parseEvents(eventsBlocks[0]);
|
||||
if (currentView == "monthview")
|
||||
_drawMonthCalendarEvents(eventsBlocks[2], eventsBlocks[0]);
|
||||
else {
|
||||
_drawCalendarAllDayEvents(eventsBlocks[1], eventsBlocks[0]);
|
||||
_drawCalendarEvents(eventsBlocks[2], eventsBlocks[0]);
|
||||
}
|
||||
_setupEventsDragAndDrop(eventsBlocks[0]);
|
||||
var eventsBlocks = http.responseText.evalJSON(true);
|
||||
|
||||
if (currentView == "multicolumndayview") {
|
||||
calendarEvents = _parseEvents(null, eventsBlocks);
|
||||
_drawCalendarAllDayEvents(null, null, eventsBlocks);
|
||||
_drawCalendarEvents(null, null, eventsBlocks);
|
||||
for (var i = 0 ; i < eventsBlocks.length; i++) {
|
||||
_setupEventsDragAndDrop(eventsBlocks[i][0]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
calendarEvents = _parseEvents(eventsBlocks[0], null);
|
||||
allDayEventsList = eventsBlocks[1];
|
||||
eventsList = eventsBlocks[2];
|
||||
if (currentView == "monthview")
|
||||
_drawMonthCalendarEvents(eventsList, eventsBlocks[0], null);
|
||||
else {
|
||||
_drawCalendarAllDayEvents(allDayEventsList, eventsBlocks[0], null);
|
||||
_drawCalendarEvents(eventsList, eventsBlocks[0], null);
|
||||
}
|
||||
_setupEventsDragAndDrop(eventsBlocks[0]);
|
||||
}
|
||||
resetCategoriesStyles();
|
||||
onWindowResize(null);
|
||||
}
|
||||
@@ -1851,20 +1932,37 @@ function newBaseEventDIV(eventRep, event, eventText) {
|
||||
return eventCell;
|
||||
}
|
||||
|
||||
function _drawCalendarAllDayEvents(events, eventsData) {
|
||||
function _drawCalendarAllDayEvents(events, eventsData, columnsData) {
|
||||
var headerView = $("calendarHeader");
|
||||
var subdivs = headerView.childNodesWithTag("div");
|
||||
var days = subdivs[1].childNodesWithTag("div");
|
||||
for (var i = 0; i < days.length; i++) {
|
||||
|
||||
if (currentView == "multicolumndayview"){
|
||||
var days = subdivs[2].childNodesWithTag("div");
|
||||
for (var i = 0; i < days.length; i++) {
|
||||
var parentDiv = days[i];
|
||||
var calendar = columnsData[i];
|
||||
var calendarAllDayEvents = calendar[1][0];
|
||||
var calendarAllDayEventsData = calendar[0];
|
||||
for (var j = 0; j < calendarAllDayEvents.length; j++) {
|
||||
var eventRep = calendarAllDayEvents[j];
|
||||
var nbr = eventRep.nbr;
|
||||
var eventCell = newAllDayEventDIV(eventRep, calendarAllDayEventsData[nbr]);
|
||||
parentDiv.appendChild(eventCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
var days = subdivs[1].childNodesWithTag("div");
|
||||
for (var i = 0; i < days.length; i++) {
|
||||
var parentDiv = days[i];
|
||||
for (var j = 0; j < events[i].length; j++) {
|
||||
var eventRep = events[i][j];
|
||||
var nbr = eventRep.nbr;
|
||||
var eventCell = newAllDayEventDIV(eventRep, eventsData[nbr]);
|
||||
parentDiv.appendChild(eventCell);
|
||||
var eventRep = events[i][j];
|
||||
var nbr = eventRep.nbr;
|
||||
var eventCell = newAllDayEventDIV(eventRep, eventsData[nbr]);
|
||||
parentDiv.appendChild(eventCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resizeCalendarHeaderDIV();
|
||||
}
|
||||
|
||||
@@ -1876,15 +1974,17 @@ function resizeCalendarHeaderDIV() {
|
||||
var daysView = $("daysView");
|
||||
if (headerView && daysView) {
|
||||
/* consts */
|
||||
var headerViewBaseHeight = 70;
|
||||
var daysViewBaseTop = 120;
|
||||
var maxDelta = 80;
|
||||
var headerViewBaseHeight = 90;
|
||||
var daysViewBaseTop = 95;
|
||||
/* /consts */
|
||||
|
||||
var maxEventPerDay = 0;
|
||||
|
||||
var subdivs = headerView.childNodesWithTag("div");
|
||||
var days = subdivs[1].childNodesWithTag("div");
|
||||
if (currentView == "multicolumndayview")
|
||||
var days = subdivs[2].childNodesWithTag("div");
|
||||
else
|
||||
var days = subdivs[1].childNodesWithTag("div");
|
||||
for (var i = 0; i < days.length; i++) {
|
||||
var parentDiv = days[i];
|
||||
var divs = parentDiv.childNodesWithTag("div");
|
||||
@@ -1892,16 +1992,31 @@ function resizeCalendarHeaderDIV() {
|
||||
maxEventPerDay = divs.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxEventPerDay > 2) {
|
||||
var delta = ((maxEventPerDay - 2) * 22) + 10;
|
||||
if (delta > maxDelta) {
|
||||
delta = maxDelta;
|
||||
}
|
||||
daysView.style.top = String(delta + daysViewBaseTop) + "px";
|
||||
headerView.style.height = String(delta + headerViewBaseHeight) + "px";
|
||||
if (maxEventPerDay > 0 && maxEventPerDay <= 4) {
|
||||
var moveDaysView = (maxEventPerDay * 19.5);
|
||||
var expendHeaderHeight = ((maxEventPerDay - 1) * 20);
|
||||
var deltaMax = 60;
|
||||
if (expendHeaderHeight > deltaMax){
|
||||
expendHeaderHeight = deltaMax;
|
||||
moveDaysView = deltaMax;
|
||||
}
|
||||
daysView.style.top = String(daysViewBaseTop + moveDaysView) + "px";
|
||||
headerView.style.height = String(headerViewBaseHeight + expendHeaderHeight) + "px";
|
||||
}
|
||||
|
||||
else if (maxEventPerDay > 4) {
|
||||
var deltaMax = 60;
|
||||
daysView.style.top = String(daysViewBaseTop + deltaMax + 19) + "px";
|
||||
headerView.style.height = String(headerViewBaseHeight + deltaMax) + "px";
|
||||
}
|
||||
else {
|
||||
var headerDays = document.getElementsByClassName("days")[0];
|
||||
headerDays.observe("mouseenter", function(event){
|
||||
headerDays.style.zIndex = "1";
|
||||
});
|
||||
headerDays.observe("mouseleave", function(event){
|
||||
headerDays.style.zIndex = "0";
|
||||
});
|
||||
daysView.style.top = null;
|
||||
headerView.style.height = null;
|
||||
}
|
||||
@@ -1916,21 +2031,37 @@ function newAllDayEventDIV(eventRep, event) {
|
||||
return eventCell;
|
||||
}
|
||||
|
||||
function _drawCalendarEvents(events, eventsData) {
|
||||
function _drawCalendarEvents(events, eventsData, columnsData) {
|
||||
var daysView = $("daysView");
|
||||
var subdivs = daysView.childNodesWithTag("div");
|
||||
for (var i = 0; i < subdivs.length; i++) {
|
||||
var subdiv = subdivs[i];
|
||||
if (subdiv.hasClassName("days")) {
|
||||
var days = subdiv.childNodesWithTag("div");
|
||||
for (var j = 0; j < days.length; j++) {
|
||||
if (currentView == "multicolumndayview") {
|
||||
for (var j = 0; j < days.length; j++) {
|
||||
var parentDiv = days[j].childNodesWithTag("div")[0];
|
||||
var calendar = columnsData[j];
|
||||
var calendarEvents = calendar[2][0];
|
||||
var calendarEventsData = calendar[0];
|
||||
for (var k = 0; k < calendarEvents.length; k++) {
|
||||
var eventRep = calendarEvents[k];
|
||||
var nbr = eventRep.nbr;
|
||||
var eventCell = newEventDIV(eventRep, calendarEventsData[nbr]);
|
||||
parentDiv.appendChild(eventCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (var j = 0; j < days.length; j++) {
|
||||
var parentDiv = days[j].childNodesWithTag("div")[0];
|
||||
for (var k = 0; k < events[j].length; k++) {
|
||||
var eventRep = events[j][k];
|
||||
var nbr = eventRep.nbr;
|
||||
var eventCell = newEventDIV(eventRep, eventsData[nbr]);
|
||||
parentDiv.appendChild(eventCell);
|
||||
var eventRep = events[j][k];
|
||||
var nbr = eventRep.nbr;
|
||||
var eventCell = newEventDIV(eventRep, eventsData[nbr]);
|
||||
parentDiv.appendChild(eventCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2009,7 +2140,8 @@ function adjustCalendarHeaderDIV() {
|
||||
var delta = ch.clientWidth - dv.clientWidth - 1;
|
||||
var styleElement = document.createElement("style");
|
||||
styleElement.type = "text/css";
|
||||
var selectors = ["DIV#calendarHeader DIV.dayLabels",
|
||||
var selectors = ["DIV#calendarHeader DIV.calendarLabels",
|
||||
"DIV#calendarHeader DIV.dayLabels",
|
||||
"DIV#calendarHeader DIV.days"];
|
||||
var rule = ("right: " + delta + "px");
|
||||
if (styleElement.styleSheet && styleElement.styleSheet.addRule) {
|
||||
@@ -2026,6 +2158,37 @@ function adjustCalendarHeaderDIV() {
|
||||
}
|
||||
}
|
||||
|
||||
function adjustMultiColumnCalendarHeaderDIV() {
|
||||
|
||||
var ch = $("calendarHeader");
|
||||
var calendarLabels = ch.getElementsByClassName("calendarLabels")[0];
|
||||
var calendarsToDisplay = calendarLabels.getElementsByClassName("calendarsToDisplay");
|
||||
var dayLabels = ch.getElementsByClassName("dayLabels")[0].getElementsByClassName("dayColumn")[0];
|
||||
var days = ch.getElementsByClassName("days")[0].getElementsByClassName("dayColumn");
|
||||
var daysView = $("daysView").getElementsByClassName("dayColumn");
|
||||
|
||||
var nbCalendars = calendarsToDisplay.length;
|
||||
|
||||
if (nbCalendars > 0) {
|
||||
var width = 100/nbCalendars;
|
||||
var left = 0;
|
||||
var position = "absolute";
|
||||
for(var i=0; i < nbCalendars; i++){
|
||||
calendarsToDisplay[i].setStyle({ width: width + '%', left: left + '%', position: position}).show();
|
||||
days[i].setStyle({ width: width + '%', left: left + '%'}).show();
|
||||
daysView[i].setStyle({ width: width + '%', left: left + '%'}).show();
|
||||
left += width;
|
||||
}
|
||||
dayLabels.setStyle({ width: '100%'}).show();
|
||||
}
|
||||
else {
|
||||
$("calendarHeader").remove();
|
||||
$("daysView").remove();
|
||||
$("calendarContent").style.height = "100%";
|
||||
$("calendarContent").style.background = "#E7E7E7";
|
||||
}
|
||||
}
|
||||
|
||||
function calendarDisplayCallback(http) {
|
||||
var div = $("calendarView");
|
||||
var daysView = $("daysView");
|
||||
@@ -2054,6 +2217,9 @@ function calendarDisplayCallback(http) {
|
||||
currentView = http.callbackData["view"];
|
||||
if (http.callbackData["day"])
|
||||
currentDay = http.callbackData["day"];
|
||||
|
||||
if (currentView == "multicolumndayview")
|
||||
adjustMultiColumnCalendarHeaderDIV();
|
||||
|
||||
// Initialize contextual menu
|
||||
var menu = new Array(onMenuNewEventClick,
|
||||
@@ -2067,10 +2233,7 @@ function calendarDisplayCallback(http) {
|
||||
onMenuRawEvent
|
||||
);
|
||||
var observer;
|
||||
if (currentView == 'dayview') {
|
||||
observer = $("daysView");
|
||||
}
|
||||
else if (currentView == 'weekview') {
|
||||
if (currentView == 'dayview' || currentView == 'weekview' || currentView == 'multicolumndayview') {
|
||||
observer = $("daysView");
|
||||
}
|
||||
else {
|
||||
@@ -2193,22 +2356,29 @@ function _loadEventHref(href) {
|
||||
}
|
||||
|
||||
function _loadTasksHref(href) {
|
||||
if (document.tasksListAjaxRequest) {
|
||||
document.tasksListAjaxRequest.aborted = true;
|
||||
document.tasksListAjaxRequest.abort();
|
||||
}
|
||||
url = ApplicationBaseURL + "/" + href;
|
||||
|
||||
var tasksList = $("tasksList");
|
||||
var selectedIds;
|
||||
if (tasksList)
|
||||
selectedIds = tasksList.getSelectedNodesId();
|
||||
else
|
||||
selectedIds = null;
|
||||
document.tasksListAjaxRequest
|
||||
= triggerAjaxRequest(url, tasksListCallback, selectedIds);
|
||||
|
||||
return true;
|
||||
if (document.tasksListAjaxRequest) {
|
||||
document.tasksListAjaxRequest.aborted = true;
|
||||
document.tasksListAjaxRequest.abort();
|
||||
}
|
||||
if (document.activeTasksAjaxRequest) {
|
||||
document.activeTasksAjaxRequest.aborted = true;
|
||||
document.activeTasksAjaxRequest.abort();
|
||||
}
|
||||
url = ApplicationBaseURL + "/" + href;
|
||||
urlActiveTasks = ApplicationBaseURL + "/activeTasks";
|
||||
|
||||
var tasksList = $("tasksList");
|
||||
var selectedIds;
|
||||
if (tasksList)
|
||||
selectedIds = tasksList.getSelectedNodesId();
|
||||
else
|
||||
selectedIds = null;
|
||||
|
||||
document.tasksListAjaxRequest = triggerAjaxRequest(url, tasksListCallback, selectedIds);
|
||||
|
||||
document.activeTasksAjaxRequest = triggerAjaxRequest(urlActiveTasks, activeTasksCallback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function onHeaderClick(event) {
|
||||
@@ -2249,9 +2419,9 @@ function onHeaderClick(event) {
|
||||
|
||||
function refreshCurrentFolder(id) {
|
||||
if (id == 'tasks')
|
||||
refreshTasks();
|
||||
refreshTasks();
|
||||
else
|
||||
refreshEvents();
|
||||
refreshEvents();
|
||||
}
|
||||
|
||||
/* refreshes the "unifinder" list */
|
||||
@@ -2900,27 +3070,35 @@ function configureDragHandles() {
|
||||
}
|
||||
|
||||
function initCalendarSelector() {
|
||||
var selector = $("calendarSelector");
|
||||
updateCalendarStatus(); // triggers the initial events refresh
|
||||
selector.changeNotification = updateCalendarsList;
|
||||
|
||||
var list = $("calendarList");
|
||||
list.on("mousedown", onCalendarSelectionChange);
|
||||
list.on("dblclick", onCalendarModify);
|
||||
list.on("selectstart", listRowMouseDownHandler);
|
||||
list.attachMenu("calendarsMenu");
|
||||
|
||||
var items = list.childNodesWithTag("li");
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var input = items[i].childNodesWithTag("input")[0];
|
||||
$(input).observe("click", clickEventWrapper(updateCalendarStatus));
|
||||
var selector = $("calendarSelector");
|
||||
updateCalendarStatus(); // triggers the initial events refresh
|
||||
selector.changeNotification = updateCalendarsList;
|
||||
|
||||
var list = $("calendarList");
|
||||
list.on("mousedown", onCalendarSelectionChange);
|
||||
list.on("dblclick", onCalendarModify);
|
||||
list.on("selectstart", listRowMouseDownHandler);
|
||||
list.attachMenu("calendarsMenu");
|
||||
|
||||
var items = list.childNodesWithTag("li");
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var input = items[i].childNodesWithTag("input")[0];
|
||||
var activeTasks = items[i].childNodesWithTag("span")[0];
|
||||
$(input).observe("click", clickEventWrapper(updateCalendarStatus));
|
||||
if (activeTasks.textContent == "0") {
|
||||
activeTasks.innerHTML = "";
|
||||
}
|
||||
|
||||
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));
|
||||
else {
|
||||
activeTasks.innerHTML = "(" + activeTasks.innerText + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -3353,39 +3531,61 @@ function onWindowResize(event) {
|
||||
}
|
||||
|
||||
function drawNowLine() {
|
||||
var d = new Date();
|
||||
var hours = d.getHours();
|
||||
var minutes = d.getMinutes();
|
||||
|
||||
if (currentView == "dayview") {
|
||||
var today = new Date();
|
||||
var m = parseInt(today.getMonth()) + 1;
|
||||
var d = today.getDate();
|
||||
if (m < 10)
|
||||
m = "0" + m;
|
||||
if (d < 10)
|
||||
d = "0" + d;
|
||||
var day = today.getFullYear() + "" + m + "" + d;
|
||||
var targets = $$("DIV#daysView DIV.days DIV.day[day=" + day
|
||||
+ "] DIV.clickableHourCell");
|
||||
}
|
||||
else if (currentView == "weekview")
|
||||
var targets = $$("DIV#daysView DIV.days DIV.dayOfToday DIV.clickableHourCell");
|
||||
|
||||
if (targets) {
|
||||
var target = targets[hours];
|
||||
|
||||
var d = new Date();
|
||||
var hours = d.getHours();
|
||||
var minutes = d.getMinutes();
|
||||
|
||||
if (currentView == "dayview") {
|
||||
var today = new Date();
|
||||
var m = parseInt(today.getMonth()) + 1;
|
||||
var d = today.getDate();
|
||||
if (m < 10)
|
||||
m = "0" + m;
|
||||
if (d < 10)
|
||||
d = "0" + d;
|
||||
var day = today.getFullYear() + "" + m + "" + d;
|
||||
var targets = $$("DIV#daysView DIV.days DIV.day[day=" + day
|
||||
+ "] DIV.hourCells");
|
||||
}
|
||||
else if (currentView == "multicolumndayview") {
|
||||
var targets = $$("DIV#daysView DIV.hourCells");
|
||||
}
|
||||
else if (currentView == "weekview")
|
||||
var targets = $$("DIV#daysView DIV.days DIV.dayOfToday DIV.hourCells");
|
||||
|
||||
else {
|
||||
var targets = [];
|
||||
}
|
||||
|
||||
if (targets[0] != undefined) {
|
||||
if (currentView == "multicolumndayview") {
|
||||
var nbCalendars = targets.length;
|
||||
for(var i = 0; i < nbCalendars; i++){
|
||||
var target = targets[i].getElementsByClassName("clickableHourCell")[hours];
|
||||
|
||||
if (target) {
|
||||
var div = $("nowLineDisplay");
|
||||
if (!div)
|
||||
div = new Element("div", {'id': 'nowLineDisplay'});
|
||||
|
||||
div.style.top = parseInt((minutes * target.offsetHeight / 60) - 1) + "px";
|
||||
target.insertBefore(div, target.firstChild);
|
||||
|
||||
setTimeout("drawNowLine ();", 60000); // 1 min.
|
||||
var div = targets[i].getElementsByClassName("nowLineDisplay")[0];
|
||||
if (!div)
|
||||
div = new Element("div", {'class': 'nowLineDisplay'});
|
||||
|
||||
div.style.top = parseInt((minutes * target.offsetHeight / 60) - 1) + "px";
|
||||
target.insertBefore(div, target.firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
var target = targets[0].getElementsByClassName("clickableHourCell")[hours];
|
||||
if (target) {
|
||||
var div = targets[0].getElementsByClassName("nowLineDisplay")[0];
|
||||
if (!div)
|
||||
div = new Element("div", {'class': 'nowLineDisplay'});
|
||||
|
||||
div.style.top = parseInt((minutes * target.offsetHeight / 60) - 1) + "px";
|
||||
target.insertBefore(div, target.firstChild);
|
||||
}
|
||||
}
|
||||
setTimeout("drawNowLine ();", 60000); // 1 min.
|
||||
}
|
||||
}
|
||||
|
||||
function onListCollapse(event, element) {
|
||||
|
||||
@@ -120,7 +120,7 @@ DIV#rightSide {
|
||||
DIV#rightFrame TD.label {
|
||||
color: #666;
|
||||
text-align: right;
|
||||
width: 30%;
|
||||
min-width:52px;
|
||||
}
|
||||
|
||||
/********** Overriding SchedulerUI.css **********/
|
||||
@@ -128,10 +128,20 @@ DIV#rightSide {
|
||||
DIV#calendarHeader {
|
||||
position:relative;
|
||||
top:0;
|
||||
height:70px;
|
||||
height:38px;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
DIV#calendarHeader DIV.calendarLabels
|
||||
{ top: 1px;
|
||||
height: 20px;
|
||||
border-left: 1px solid #ccc;
|
||||
text-align: center;
|
||||
font-style:italic;
|
||||
font-size:150%;
|
||||
}
|
||||
|
||||
DIV#calendarHeader DIV.calendarLabels,
|
||||
DIV#calendarHeader DIV.dayLabels,
|
||||
DIV#calendarHeader DIV.days {
|
||||
position:relative;
|
||||
@@ -139,9 +149,12 @@ DIV#calendarHeader DIV.days {
|
||||
margin-left:50px;
|
||||
}
|
||||
|
||||
DIV#calendarHeader DIV.dayLabels
|
||||
{ top: 1px; }
|
||||
|
||||
DIV#calendarHeader DIV.days {
|
||||
top: 0;
|
||||
height: 33px;
|
||||
height: 1px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
@@ -170,9 +183,13 @@ DIV#daysView DIV.hour {
|
||||
|
||||
DIV.event {
|
||||
z-index:1;
|
||||
border:solid black 1px;
|
||||
background-color:white;
|
||||
background-color: white;
|
||||
border-radius:5px;
|
||||
border:1px solid black;
|
||||
|
||||
}
|
||||
DIV.eventInside {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
DIV#daysView DIV.event.starts0
|
||||
@@ -596,10 +613,15 @@ LABEL {
|
||||
position:static;
|
||||
top:5px;
|
||||
left:5px;
|
||||
|
||||
}
|
||||
|
||||
DIV#rightFrame {
|
||||
border:none;
|
||||
overflow:visible;
|
||||
}
|
||||
|
||||
BODY {
|
||||
background-color:white;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ var firstDayOfWeek = window.opener.firstDayOfWeek;
|
||||
var printCompletedTasks=1;
|
||||
var printNoDueDateTasks=1;
|
||||
var eventsBlocks;
|
||||
var currentView;
|
||||
var currentPreview;
|
||||
var currentDay = window.parentvar("currentDay");
|
||||
var sd, ed;
|
||||
|
||||
@@ -38,7 +38,7 @@ function refreshContent() {
|
||||
}
|
||||
|
||||
function updateDisplayView(data, newView) {
|
||||
newView = ((newView) ? newView : currentView);
|
||||
newView = ((newView) ? newView : currentPreview);
|
||||
var url = ApplicationBaseURL + "/" + newView;
|
||||
var day = null;
|
||||
|
||||
@@ -97,9 +97,24 @@ function previewDisplayCallback(http) {
|
||||
$("currentViewMenu").remove();
|
||||
$("listCollapse").remove();
|
||||
|
||||
// TODO : Month
|
||||
_drawAllDayEvents(eventsBlocks[1], eventsBlocks[0]);
|
||||
_drawEvents(eventsBlocks[2], eventsBlocks[0]);
|
||||
if (currentPreview == "multicolumndayview") {
|
||||
_drawCalendarAllDayEvents(null, null, eventsBlocks);
|
||||
}
|
||||
else {
|
||||
allDayEventsList = eventsBlocks[1];
|
||||
if (currentPreview == "monthview") {
|
||||
//_drawMonthCalendarEvents(eventsList, eventsBlocks[0], null);
|
||||
}
|
||||
else {
|
||||
_drawCalendarAllDayEvents(allDayEventsList, eventsBlocks[0], null);
|
||||
}
|
||||
}
|
||||
// This ensure the diplay working hours checkbox when switching views
|
||||
var printHoursCheckBox = $("printHours");
|
||||
onPrintWorkingHoursCheck(printHoursCheckBox);
|
||||
|
||||
// Add events color for each calendars
|
||||
addCalendarsColor();
|
||||
}
|
||||
else
|
||||
log ("calendarDisplayCallback Ajax error ("+ http.readyState + "/" + http.status + ")");
|
||||
@@ -107,17 +122,38 @@ function previewDisplayCallback(http) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function addCalendarsColor () {
|
||||
var allCalendars = window.parent$("calendarList");
|
||||
var allColors = window.parentvar("UserSettings")['Calendar']['FolderColors'];
|
||||
|
||||
for (var i = 0; i < allCalendars.children.length; i++) {
|
||||
if (allCalendars.children[i].down("input").checked){
|
||||
owner = allCalendars.children[i].getAttribute("owner");
|
||||
folderName = allCalendars.children[i].getAttribute("id").substr(1);
|
||||
|
||||
color = allColors[owner + ":Calendar/" + folderName];
|
||||
if (!color) {
|
||||
if(folderName.split("_")[1])
|
||||
color = allColors[owner + ":Calendar/" + folderName.split("_")[1]];
|
||||
else
|
||||
color = "#AAAAAA";
|
||||
}
|
||||
appendStyleElement(folderName, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function refreshEvents() {
|
||||
var todayDate = new Date();
|
||||
|
||||
if (!currentDay)
|
||||
currentDay = todayDate.getDayString();
|
||||
|
||||
if (currentView == "dayview") {
|
||||
if (currentPreview == "dayview" || currentPreview == "multicolumndayview") { // dayView and MultiColumns View
|
||||
sd = currentDay;
|
||||
ed = sd;
|
||||
}
|
||||
else if (currentView == "weekview") {
|
||||
else if (currentPreview == "weekview") { // WeekView
|
||||
var startDate;
|
||||
startDate = currentDay.asDate();
|
||||
startDate = startDate.beginOfWeek();
|
||||
@@ -144,7 +180,7 @@ function refreshEvents() {
|
||||
document.refreshEventsAjaxRequest.abort();
|
||||
}
|
||||
var url = (ApplicationBaseURL + "/eventsblocks?sd=" + sd + "&ed=" + ed
|
||||
+ "&view=" + currentView);
|
||||
+ "&view=" + currentPreview);
|
||||
|
||||
document.refreshEventsAjaxRequest
|
||||
= triggerAjaxRequest(url, refreshEventsCallback,
|
||||
@@ -157,9 +193,13 @@ function refreshTasks(){
|
||||
document.tasksListAjaxRequest.abort();
|
||||
}
|
||||
|
||||
var taskListFilter = window.parentvar("taskListFilter");
|
||||
url = window.parentvar("ApplicationBaseURL") + "/" + "taskslist?show-completed=" + printCompletedTasks
|
||||
+ "&asc=" + sorting["task-ascending"]
|
||||
+ "&sort=" + sorting["task-attribute"];
|
||||
+ "&sort=" + sorting["task-attribute"]
|
||||
+ "&filterpopup=" + taskListFilter;
|
||||
|
||||
// TODO : Is that really necessary ?
|
||||
var tasksList = window.parent$("tasksList");
|
||||
var selectedIds;
|
||||
if (tasksList)
|
||||
@@ -176,11 +216,13 @@ function refreshEventsCallback(http) {
|
||||
if (http.responseText.length > 0) {
|
||||
eventsBlocks = http.responseText.evalJSON(true);
|
||||
$("rightFrameEvents").innerHTML = "";
|
||||
if ($("printLayoutList").value == "0")
|
||||
if ($("printLayoutList").value == "0" && eventsBlocks.length > 0) {
|
||||
_drawEventsCells(eventsBlocks);
|
||||
else {
|
||||
updateDisplayView(null, currentView);
|
||||
}
|
||||
else {
|
||||
updateDisplayView(null, currentPreview);
|
||||
}
|
||||
adjustFrames();
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -193,8 +235,10 @@ function refreshTasksListCallback(http) {
|
||||
if (http.responseText.length > 0) {
|
||||
var tasksBlocks = http.responseText.evalJSON(true);
|
||||
$("rightFrameTasks").innerHTML = "";
|
||||
var layout = $("printLayoutList").value;
|
||||
_drawTasksCells(tasksBlocks);
|
||||
if (tasksBlocks.length > 0) {
|
||||
_drawTasksCells(tasksBlocks);
|
||||
adjustFrames();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -204,10 +248,21 @@ function refreshTasksListCallback(http) {
|
||||
function _drawEventsCells(eventsBlocks) {
|
||||
var events = _("Events");
|
||||
$("rightFrameEvents").insert("<h3>"+events+"</h3>");
|
||||
for(var i=0; i < eventsBlocks[0].length; i++)
|
||||
{
|
||||
var event = _parseEvent(eventsBlocks[0][i]);
|
||||
$("rightFrameEvents").insert(event);
|
||||
if (currentPreview == "multicolumndayview") {
|
||||
for(var i=0; i < eventsBlocks.length; i++) // calendars
|
||||
{
|
||||
for (var j = 0; j < eventsBlocks[i][0].length; j++) {
|
||||
var event = _parseEvent(eventsBlocks[i][0][j]);
|
||||
$("rightFrameEvents").insert(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(var i=0; i < eventsBlocks[0].length; i++)
|
||||
{
|
||||
var event = _parseEvent(eventsBlocks[0][i]);
|
||||
$("rightFrameEvents").insert(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,44 +280,113 @@ function _drawTasksCells(tasksBlocks) {
|
||||
|
||||
// TODO : Maybe use the drawfunction from the schedulerUI.js
|
||||
|
||||
function _drawEvents(events, eventsData) {
|
||||
function _drawCalendarEvents(events, eventsData, columnsData) {
|
||||
var daysView = $("daysView");
|
||||
var subdivs = daysView.childNodesWithTag("div");
|
||||
var printHoursCheckBox = $("printHours");
|
||||
for (var i = 0; i < subdivs.length; i++) {
|
||||
var subdiv = subdivs[i];
|
||||
if (subdiv.hasClassName("days")) {
|
||||
var days = subdiv.childNodesWithTag("div");
|
||||
for (var j = 0; j < days.length; j++) {
|
||||
var parentDiv = days[j].childNodesWithTag("div")[0];
|
||||
for (var k = 0; k < events[j].length; k++) {
|
||||
var eventRep = events[j][k];
|
||||
var nbr = eventRep.nbr;
|
||||
var eventCell = newEventDIV(eventRep, eventsData[nbr]);
|
||||
parentDiv.appendChild(eventCell);
|
||||
if (currentPreview == "multicolumndayview") {
|
||||
for (var j = 0; j < days.length; j++) {
|
||||
var parentDiv = days[j].childNodesWithTag("div")[0];
|
||||
var calendar = columnsData[j];
|
||||
var calendarEvents = calendar[2][0];
|
||||
var calendarEventsData = calendar[0];
|
||||
if (parentDiv.getElementsByClassName("event").length > 0) {
|
||||
var oldEvents = parentDiv.getElementsByClassName("event");
|
||||
var length = oldEvents.length - 1;
|
||||
for (var x = length; x >= 0; x--){
|
||||
oldEvents[x].remove();
|
||||
}
|
||||
}
|
||||
for (var k = 0; k < calendarEvents.length; k++) {
|
||||
var eventRep = calendarEvents[k];
|
||||
var nbr = eventRep.nbr;
|
||||
|
||||
if (printHoursCheckBox.checked) {
|
||||
var offset = _computeOffset(parentDiv);
|
||||
if ((eventRep.start - offset[0]) > 0 && (eventRep.start - offset[0]) < offset[1]) {
|
||||
var eventCell = newEventDIV(eventRep, calendarEventsData[nbr], offset[0]);
|
||||
parentDiv.appendChild(eventCell);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var eventCell = newEventDIV(eventRep, calendarEventsData[nbr], null);
|
||||
parentDiv.appendChild(eventCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (var j = 0; j < days.length; j++) {
|
||||
var parentDiv = days[j].childNodesWithTag("div")[0];
|
||||
if (parentDiv.getElementsByClassName("event").length > 0) {
|
||||
var oldEvents = parentDiv.getElementsByClassName("event");
|
||||
var length = oldEvents.length - 1;
|
||||
for (var x = length; x >= 0; x--){
|
||||
oldEvents[x].remove();
|
||||
}
|
||||
}
|
||||
for (var k = 0; k < events[j].length; k++) {
|
||||
var eventRep = events[j][k];
|
||||
var nbr = eventRep.nbr;
|
||||
if (printHoursCheckBox.checked) {
|
||||
var offset = _computeOffset(parentDiv);
|
||||
if ((eventRep.start - offset[0]) > 0 && (eventRep.start - offset[0]) < offset[1]) {
|
||||
var eventCell = newEventDIV(eventRep, eventsData[nbr], offset[0]);
|
||||
parentDiv.appendChild(eventCell);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var eventCell = newEventDIV(eventRep, eventsData[nbr], null);
|
||||
parentDiv.appendChild(eventCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _drawAllDayEvents(events, eventsData) {
|
||||
function _drawCalendarAllDayEvents(events, eventsData, columnsData) {
|
||||
var headerView = $("calendarHeader");
|
||||
var subdivs = headerView.childNodesWithTag("div");
|
||||
var days = subdivs[1].childNodesWithTag("div");
|
||||
for (var i = 0; i < days.length; i++) {
|
||||
var parentDiv = days[i];
|
||||
for (var j = 0; j < events[i].length; j++) {
|
||||
var eventRep = events[i][j];
|
||||
var nbr = eventRep.nbr;
|
||||
var eventCell = newAllDayEventDIV(eventRep, eventsData[nbr]);
|
||||
parentDiv.appendChild(eventCell);
|
||||
|
||||
if (currentPreview == "multicolumndayview"){
|
||||
var days = subdivs[2].childNodesWithTag("div");
|
||||
for (var i = 0; i < days.length; i++) {
|
||||
var parentDiv = days[i];
|
||||
var calendar = columnsData[i];
|
||||
var calendarAllDayEvents = calendar[1][0];
|
||||
var calendarAllDayEventsData = calendar[0];
|
||||
for (var j = 0; j < calendarAllDayEvents.length; j++) {
|
||||
var eventRep = calendarAllDayEvents[j];
|
||||
var nbr = eventRep.nbr;
|
||||
var eventCell = newAllDayEventDIV(eventRep, calendarAllDayEventsData[nbr]);
|
||||
parentDiv.appendChild(eventCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
var days = subdivs[1].childNodesWithTag("div");
|
||||
for (var i = 0; i < days.length; i++) {
|
||||
var parentDiv = days[i];
|
||||
for (var j = 0; j < events[i].length; j++) {
|
||||
var eventRep = events[i][j];
|
||||
var nbr = eventRep.nbr;
|
||||
var eventCell = newAllDayEventDIV(eventRep, eventsData[nbr]);
|
||||
parentDiv.appendChild(eventCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
adjustPreviewHeader();
|
||||
}
|
||||
|
||||
// todo : month
|
||||
|
||||
function newEventDIV(eventRep, event) {
|
||||
function newEventDIV(eventRep, event, offset) {
|
||||
var eventCell = newBaseEventDIV(eventRep, event, event[4]);
|
||||
|
||||
var pc = 100 / eventRep.siblings;
|
||||
@@ -270,7 +394,12 @@ function newEventDIV(eventRep, event) {
|
||||
eventCell.style.left = left + "%";
|
||||
var right = Math.floor(100 - (eventRep.position + 1) * pc);
|
||||
eventCell.style.right = right + "%";
|
||||
eventCell.addClassName("starts" + eventRep.start);
|
||||
if (offset != null) {
|
||||
eventCell.addClassName("starts" + (eventRep.start - offset));
|
||||
}
|
||||
else {
|
||||
eventCell.addClassName("starts" + eventRep.start);
|
||||
}
|
||||
eventCell.addClassName("lasts" + eventRep.length);
|
||||
|
||||
if (event[7]) {
|
||||
@@ -376,6 +505,17 @@ function newBaseEventDIV(eventRep, event, eventText) {
|
||||
return eventCell;
|
||||
}
|
||||
|
||||
function appendStyleElement(folderPath, color) {
|
||||
if (document.styleSheets) {
|
||||
var fgColor = getContrastingTextColor(color);
|
||||
var styleElement = document.styleSheets[3];
|
||||
|
||||
styleElement.insertRule(".calendarFolder" + folderPath +
|
||||
"{background-color: " + color + " !important;" +
|
||||
" color: " + fgColor + " !important;}", styleElement.cssRules.length);
|
||||
}
|
||||
}
|
||||
|
||||
function _parseEvent(event) {
|
||||
// Localized strings :
|
||||
var start = _("Start:");
|
||||
@@ -426,6 +566,29 @@ function _parseTask(task) {
|
||||
return parsedTask;
|
||||
}
|
||||
|
||||
function _computeOffset(hoursCells) {
|
||||
var outOfDayCells = hoursCells.getElementsByClassName("outOfDay");
|
||||
var count = 1;
|
||||
var offset = [];
|
||||
var buffer;
|
||||
var j = 1;
|
||||
for (var i = 0; i < outOfDayCells.length; i++) {
|
||||
hourCell1 = parseInt(outOfDayCells[i].getAttribute("hour")) + 100;
|
||||
hourCell2 = parseInt(outOfDayCells[j].getAttribute("hour"));
|
||||
if (hourCell1 == hourCell2) {
|
||||
count += 1;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
j ++;
|
||||
}
|
||||
offset.push(count * 4);
|
||||
offset.push((hourCell2 / 100 * 4) - (count * 4));
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/************************************** Preview Navigation *****************************************/
|
||||
|
||||
function onCalendarGotoDay(node) {
|
||||
@@ -481,74 +644,157 @@ function dateSelectorCallback(http) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*********************** Input Field, Checkboxes, Radio and listMenu *********************************/
|
||||
/*********************** Input Field, listMenu, Checkboxes and Radio *********************************/
|
||||
|
||||
function onInputTitleChange(event){
|
||||
var inputFieldTitle = $("inputFieldTitle").value;
|
||||
if (inputFieldTitle)
|
||||
document.getElementById("rightFrameTitle").innerHTML = inputFieldTitle + "<br />";
|
||||
else
|
||||
document.getElementById("rightFrameTitle").innerHTML = inputFieldTitle;
|
||||
document.getElementById("rightFrameTitle").remove();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function onPrintLayoutListChange() {
|
||||
var selectedLayout = $("printLayoutList").value;
|
||||
var parentView = window.parentvar("currentView");
|
||||
var selectedLayout = $("printLayoutList").value;
|
||||
document.getElementById("printHours").disabled = (selectedLayout == 0);
|
||||
switch(selectedLayout) {
|
||||
case "0": // List view
|
||||
window.resizeTo(700,500);
|
||||
currentView = parentView;
|
||||
ajustFrames();
|
||||
currentPreview = parentView;
|
||||
break;
|
||||
|
||||
case "1": // Day view
|
||||
window.resizeTo(1010,500);
|
||||
currentView = "dayview";
|
||||
ajustFrames(currentView);
|
||||
currentPreview = "dayview";
|
||||
break;
|
||||
|
||||
case "2": // Week view
|
||||
|
||||
case "2": // Multi-columns view
|
||||
window.resizeTo(1010,500);
|
||||
currentView = "weekview";
|
||||
ajustFrames(currentView);
|
||||
currentPreview = "multicolumndayview";
|
||||
break;
|
||||
|
||||
//todo : month
|
||||
case "3": // Week view
|
||||
window.resizeTo(1010,500);
|
||||
currentPreview = "weekview";
|
||||
break;
|
||||
|
||||
/*case "4": // Month view
|
||||
window.resizeTo(1010,500);
|
||||
currentPreview = "monthview";
|
||||
break;*/
|
||||
}
|
||||
|
||||
refreshContent();
|
||||
return false;
|
||||
}
|
||||
|
||||
function ajustFrames(view) {
|
||||
if (view == "dayview" || view == "weekview") {
|
||||
function adjustPreviewHeader() {
|
||||
// 1 - Check if there is any allDay Events. If not reduce the space taken
|
||||
var selectedLayout = $("printLayoutList").value;
|
||||
if (selectedLayout != 0) {
|
||||
var calendarHeader = $("calendarHeader");
|
||||
var allDayDisplay = $("calendarHeader").getElementsByClassName("days");
|
||||
var allDayEvents = $("calendarHeader").getElementsByClassName("eventInside");
|
||||
var eventHeight = 22;
|
||||
var headerHeight = 38;
|
||||
if (selectedLayout == 1) { // Since there is only one column in day view
|
||||
height = allDayEvents.length * eventHeight;
|
||||
}
|
||||
else { // Applies only on week view and multi-columns view
|
||||
var nbEventsMax = 0
|
||||
var eventClass = $("calendarHeader").getElementsByClassName("event");
|
||||
for (var i = 0; i < allDayDisplay[0].childNodes.length; i++) {
|
||||
if (allDayDisplay[0].childNodes[i].firstChild != null) {
|
||||
count = allDayDisplay[0].childNodes[i].getElementsByClassName("event").length;
|
||||
if (count > nbEventsMax) {
|
||||
nbEventsMax = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
height = nbEventsMax * eventHeight;
|
||||
if (selectedLayout == 2) {
|
||||
headerHeight = 58;
|
||||
adjustMultiColumnCalendarHeaderDIV();
|
||||
}
|
||||
}
|
||||
calendarHeader.style.height = (height + headerHeight) + "px";
|
||||
allDayDisplay[0].style.height = height + "px";
|
||||
}
|
||||
}
|
||||
|
||||
function adjustMultiColumnCalendarHeaderDIV() {
|
||||
var ch = $("calendarHeader");
|
||||
var calendarLabels = ch.getElementsByClassName("calendarLabels")[0];
|
||||
var calendarsToDisplay = calendarLabels.getElementsByClassName("calendarsToDisplay");
|
||||
var dayLabels = ch.getElementsByClassName("dayLabels")[0].getElementsByClassName("dayColumn")[0];
|
||||
var days = ch.getElementsByClassName("days")[0].getElementsByClassName("dayColumn");
|
||||
var daysView = $("daysView").getElementsByClassName("dayColumn");
|
||||
|
||||
var nbCalendars = calendarsToDisplay.length;
|
||||
|
||||
if (nbCalendars > 0) {
|
||||
var width = 100/nbCalendars;
|
||||
var left = 0;
|
||||
var position = "absolute";
|
||||
for(var i=0; i < nbCalendars; i++){
|
||||
calendarsToDisplay[i].setStyle({ width: width + '%', left: left + '%', position: position}).show();
|
||||
days[i].setStyle({ width: width + '%', left: left + '%'}).show();
|
||||
daysView[i].setStyle({ width: width + '%', left: left + '%'}).show();
|
||||
left += width;
|
||||
}
|
||||
dayLabels.setStyle({ width: '100%'}).show();
|
||||
}
|
||||
else {
|
||||
$("calendarHeader").remove();
|
||||
$("daysView").remove();
|
||||
var htmlText = "<div class='alert-box notice'><span>" + _("notice:") + "</span>"+_("Please go ahead and select calendars")+"</div>";
|
||||
$("calendarContent").innerHTML = htmlText;
|
||||
}
|
||||
}
|
||||
|
||||
function adjustFrames() {
|
||||
var view = $("printLayoutList").value;
|
||||
if (view == 0) {
|
||||
var eventsCheckBox = $("printEvents");
|
||||
var tasksCheckBox = $("printTasks");
|
||||
onEventsCheck(eventsCheckBox);
|
||||
onTasksCheck(tasksCheckBox);
|
||||
document.getElementById("rightFrameTasks").style.pageBreakBefore = 'auto';
|
||||
document.getElementById("rightFrameTasks").style.pageBreakInside = 'auto';
|
||||
}
|
||||
else {
|
||||
document.getElementById("rightFrameEvents").style.width = '100%';
|
||||
document.getElementById("rightFrameTasks").style.width = '100%';
|
||||
document.getElementById("rightFrameTasks").style.pageBreakBefore = 'always';
|
||||
document.getElementById("rightFrameTasks").style.pageBreakInside = 'avoid';
|
||||
|
||||
}
|
||||
else {
|
||||
document.getElementById("rightFrameEvents").style.width = '49.5%';
|
||||
document.getElementById("rightFrameTasks").style.width = '49.5%';
|
||||
document.getElementById("rightFrameTasks").style.pageBreakBefore = 'auto';
|
||||
document.getElementById("rightFrameTasks").style.pageBreakInside = 'auto';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function onEventsCheck(checkBox) {
|
||||
if(checkBox.checked){
|
||||
document.getElementById("rightFrameEvents").style.display = 'block';
|
||||
var printOptions = document.getElementById("printHours");
|
||||
var selectedLayout = $("printLayoutList").value;
|
||||
if (!checkBox.checked || selectedLayout == 0)
|
||||
printOptions.disabled = true;
|
||||
else
|
||||
printOptions.disabled = false;
|
||||
|
||||
var events = $("rightFrameEvents").childNodesWithTag("DIV");
|
||||
if(checkBox.checked && events.length > 0){
|
||||
$("rightFrameEvents").style.display = 'block';
|
||||
if ($("printLayoutList").value == 0){
|
||||
document.getElementById("rightFrameTasks").style.width = '49.5%';
|
||||
$("rightFrameTasks").style.width = '49.5%';
|
||||
}
|
||||
}
|
||||
else {
|
||||
document.getElementById("rightFrameEvents").style.display = 'none';
|
||||
$("rightFrameEvents").style.display = 'none';
|
||||
if ($("printLayoutList").value == 0){
|
||||
document.getElementById("rightFrameTasks").style.width = '100%';
|
||||
$("rightFrameTasks").style.width = '100%';
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function onTasksCheck(checkBox) {
|
||||
@@ -556,18 +802,58 @@ function onTasksCheck(checkBox) {
|
||||
for (var i = 0; i < printOptions.length; i++)
|
||||
printOptions[i].disabled = !checkBox.checked;
|
||||
|
||||
if(checkBox.checked) {
|
||||
document.getElementById("rightFrameTasks").style.display = 'block';
|
||||
var tasks = $("rightFrameTasks").childNodesWithTag("DIV");
|
||||
if(checkBox.checked && tasks.length > 0) {
|
||||
$("rightFrameTasks").style.display = 'block';
|
||||
if ($("printLayoutList").value == 0){
|
||||
document.getElementById("rightFrameEvents").style.width = '49.5%';
|
||||
$("rightFrameEvents").style.width = '49.5%';
|
||||
}
|
||||
}
|
||||
else {
|
||||
document.getElementById("rightFrameTasks").style.display = 'none';
|
||||
$("rightFrameTasks").style.display = 'none';
|
||||
if ($("printLayoutList").value == 0){
|
||||
document.getElementById("rightFrameEvents").style.width = '100%';
|
||||
$("rightFrameEvents").style.width = '100%';
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function onPrintWorkingHoursCheck(checkBox) {
|
||||
var isCheked = checkBox.checked;
|
||||
var outOfDayCells = $$("DIV#daysView .outOfDay");
|
||||
var hours = $$("DIV#daysView .hour");
|
||||
var hoursOutOfDay = [];
|
||||
for (var i = 0; i < outOfDayCells.length; i++) {
|
||||
var buffer = outOfDayCells[i].getAttribute("hour").substr(0,1);
|
||||
if (buffer != "0") {
|
||||
buffer += outOfDayCells[i].getAttribute("hour").substr(1,1);
|
||||
}
|
||||
else {
|
||||
buffer = outOfDayCells[i].getAttribute("hour").substr(1,1);
|
||||
}
|
||||
if(isCheked) {
|
||||
outOfDayCells[i].hide();
|
||||
hours[buffer].hide();
|
||||
}
|
||||
else {
|
||||
outOfDayCells[i].show();
|
||||
hours[buffer].show();
|
||||
}
|
||||
}
|
||||
|
||||
if (currentPreview == "multicolumndayview") {
|
||||
_drawCalendarEvents(null, null, eventsBlocks);
|
||||
}
|
||||
else {
|
||||
eventsList = eventsBlocks[2];
|
||||
if (currentPreview == "monthview") {
|
||||
//_drawMonthCalendarEvents(eventsList, eventsBlocks[0], null);
|
||||
}
|
||||
else {
|
||||
_drawCalendarEvents(eventsList, eventsBlocks[0], null);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*function onPrintDateCheck() {
|
||||
@@ -580,12 +866,12 @@ function onTasksCheck(checkBox) {
|
||||
|
||||
function onPrintCompletedTasksCheck(checkBox) {
|
||||
printCompletedTasks = (checkBox.checked ? 1 : 0);
|
||||
refreshTasks();
|
||||
refreshContent();
|
||||
}
|
||||
|
||||
function onPrintNoDueDateTasksCheck(checkBox) {
|
||||
printNoDueDateTasks = (checkBox.checked ? 1 : 0);
|
||||
refreshTasks();
|
||||
refreshContent();
|
||||
}
|
||||
|
||||
/************** Date picker functions *************
|
||||
@@ -633,7 +919,6 @@ function onPrintClick(event) {
|
||||
/**************************** Initialization *******************************************/
|
||||
|
||||
function init() {
|
||||
|
||||
initializePrintSettings();
|
||||
//initializeWhatToPrint();
|
||||
//initializeOptions();
|
||||
@@ -661,6 +946,6 @@ function initializePrintSettings() {
|
||||
}*/
|
||||
|
||||
/*function initializeOptions() {
|
||||
}*/
|
||||
}*/
|
||||
|
||||
document.observe("dom:loaded", init);
|
||||
|
||||
@@ -164,9 +164,9 @@ function foldersSearchCallback(http) {
|
||||
|
||||
dd.innerHTML = '';
|
||||
for (var i = 1; i < folders.length - 1; i++)
|
||||
dd.appendChild (addFolderBranchToTree(d, user, folders[i], nodeId, i, false));
|
||||
dd.appendChild (addFolderBranchToTree(d, user, folders[folders.length-1], nodeId,
|
||||
(folders.length - 1), true));
|
||||
dd.appendChild(addFolderBranchToTree(d, user, folders[i], nodeId, i, false));
|
||||
dd.appendChild(addFolderBranchToTree(d, user, folders[folders.length-1], nodeId,
|
||||
(folders.length - 1), true));
|
||||
//dd.update(str);
|
||||
for (var i = 1; i < folders.length; i++) {
|
||||
var sd = $("sd" + (nodeId + i));
|
||||
@@ -193,13 +193,8 @@ function addFolderBranchToTree(tree, user, folder, nodeId, subId, isLast) {
|
||||
icon += 'calendar-folder-16x16.png';
|
||||
var folderId = user + ":" + folderInfos[1].substr(1);
|
||||
|
||||
// name has the format "Foldername (Firstname Lastname <email>)"
|
||||
// We sanitize the value to avoid XSS issues
|
||||
var name = folderInfos[0].escapeHTML();
|
||||
|
||||
var pos = name.lastIndexOf(' (');
|
||||
if (pos > -1)
|
||||
name = name.substring(0, pos); // strip the part with fullname and email
|
||||
var name = folderInfos[0].escapeHTML();
|
||||
var node = new dTreeNode(subId, nodeId, name, 0, '#', folderId,
|
||||
folderInfos[2] + '-folder', '', '', icon, icon);
|
||||
node._ls = isLast;
|
||||
@@ -223,25 +218,19 @@ function onConfirmFolderSelection(event) {
|
||||
var node = topNode.selectedEntry.parentNode;
|
||||
var folder = node.getAttribute("dataname");
|
||||
var type = node.getAttribute("datatype");
|
||||
|
||||
var folderName;
|
||||
if (window.opener.userFolderType == "user") {
|
||||
var span = $(topNode.selectedEntry).down("SPAN.nodeName");
|
||||
var email = (span.innerHTML
|
||||
.replace("<", "<", "g")
|
||||
.replace(">", ">", "g"));
|
||||
folderName = email.replace(/>,.*$/, ">", "g");
|
||||
var resource = $(topNode.selectedEntry).down("SPAN.nodeName");
|
||||
var description = (resource.innerHTML
|
||||
.replace("<", "<", "g")
|
||||
.replace(">", ">", "g"));
|
||||
folderName = description.replace(/>,.*$/, ">", "g");
|
||||
}
|
||||
else {
|
||||
var resource = $(topNode.selectedEntry).down("SPAN.nodeName");
|
||||
var user = $(node.parentNode.previousSibling).down("SPAN.nodeName");
|
||||
var email = (user.innerHTML
|
||||
.replace("<", "<", "g")
|
||||
.replace(">", ">", "g"));
|
||||
folderName = resource.innerHTML;
|
||||
folderName = folderName.replace(/>,.*(\))?$/, ">)$1", "g");
|
||||
}
|
||||
|
||||
var data = { folderName: folderName, folder: folder, type: type, window: window };
|
||||
if (parent$(accessToSubscribedFolder(folder)))
|
||||
window.alert(_("You have already subscribed to that folder!"));
|
||||
|
||||
@@ -291,6 +291,7 @@ function initPreferences() {
|
||||
$("vacationEndDate_date").disable();
|
||||
});
|
||||
}
|
||||
onAddOutgoingAddressesCheck();
|
||||
}
|
||||
|
||||
function initSieveFilters() {
|
||||
@@ -1172,8 +1173,15 @@ function serializeContactsCategories() {
|
||||
}
|
||||
|
||||
/* / contact categories */
|
||||
|
||||
|
||||
|
||||
function onAddOutgoingAddressesCheck(checkBox) {
|
||||
if (!checkBox) {
|
||||
checkBox = $("addOutgoingAddresses");
|
||||
}
|
||||
$("addressBookList").disabled = !checkBox.checked;
|
||||
|
||||
}
|
||||
|
||||
function onReplyPlacementListChange() {
|
||||
if ($("replyPlacementList").value == 0) {
|
||||
// Reply placement is above quote, signature can be place before of after quote
|
||||
|
||||
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 712 B |
@@ -28,7 +28,8 @@ TABLE, DIV, IMG
|
||||
font-size: inherit;
|
||||
border: 0px;
|
||||
margin: 0px;
|
||||
padding: 0px; }
|
||||
padding: 0px;
|
||||
word-break:break-all; }
|
||||
|
||||
FIELDSET
|
||||
{ border: 1px solid #aaa;
|
||||
|
||||
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 476 B |
Vendored
+1648
-1205
File diff suppressed because it is too large
Load Diff
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 704 B |
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 483 B |
Reference in New Issue
Block a user