diff --git a/.gitignore b/.gitignore
index 04a616227..0214a5472 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
*/*/obj/
*/obj/
.scss-lint-config.yml_
+ActiveSync/ActiveSync.SOGo
Documentation/*.docbook
Documentation/*.pdf
SoObjects/SOGo/SOGo.framework/
@@ -20,11 +21,8 @@ UI/WebServerResources/bower_components/
UI/WebServerResources/css/
UI/WebServerResources/css/styles.css
UI/WebServerResources/css/styles.css.map
-UI/WebServerResources/js/Common.js*
-UI/WebServerResources/js/Contacts.js*
-UI/WebServerResources/js/Mailer.js*
-UI/WebServerResources/js/Preferences.js*
-UI/WebServerResources/js/Scheduler.js*
+UI/WebServerResources/js/*.js
+UI/WebServerResources/js/*.js.map
UI/WebServerResources/js/vendor/
UI/WebServerResources/node_modules/
UI/WebServerResources/scss/.sass-cache/
diff --git a/ActiveSync/GNUmakefile.preamble b/ActiveSync/GNUmakefile.preamble
index c302ad8be..545cdf46b 100644
--- a/ActiveSync/GNUmakefile.preamble
+++ b/ActiveSync/GNUmakefile.preamble
@@ -1 +1,4 @@
-# compilation settings
+ifeq ($(HAS_LIBRARY_ssl),yes)
+ADDITIONAL_CPPFLAGS += -DHAVE_OPENSSL=1
+BUNDLE_LIBS += -lcrypto
+endif
diff --git a/ActiveSync/NGDOMElement+ActiveSync.m b/ActiveSync/NGDOMElement+ActiveSync.m
index e9294e573..61a6015d0 100644
--- a/ActiveSync/NGDOMElement+ActiveSync.m
+++ b/ActiveSync/NGDOMElement+ActiveSync.m
@@ -101,7 +101,7 @@ static NSArray *asElementArray = nil;
int i, count;
if (!asElementArray)
- asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", @"Category", nil];
+ asElementArray = [[NSArray alloc] initWithObjects: @"Attendee", @"Category", @"Exception", nil];
data = [NSMutableDictionary dictionary];
diff --git a/ActiveSync/NGVCard+ActiveSync.m b/ActiveSync/NGVCard+ActiveSync.m
index eb62b9722..45fb93142 100644
--- a/ActiveSync/NGVCard+ActiveSync.m
+++ b/ActiveSync/NGVCard+ActiveSync.m
@@ -48,11 +48,42 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@implementation NGVCard (ActiveSync)
+//
+// This function is called for each elements which can be ghosted according to specs.
+// https://msdn.microsoft.com/en-us/library/gg650908%28v=exchg.80%29.aspx
+//
+- (BOOL) _isGhosted: (NSString *) element
+ inContext: (WOContext *) context
+{
+ NSArray *supportedElements;
+
+ supportedElements = [context objectForKey: @"SupportedElements"];
+
+ // If the client does not include a Supported element in the initial Sync command request for
+ // a folder, then all of the elements that can be ghosted are considered not ghosted.
+ if (!supportedElements)
+ return NO;
+
+ // If the client includes an empty Supported element in the initial Sync command request for
+ // a folder, then all elements that can be ghosted are considered ghosted.
+ if (![supportedElements count])
+ return YES;
+
+ // If the client includes a Supported element that contains child elements in the initial
+ // Sync command request for a folder, then each child element of that Supported element is
+ // considered not ghosted. All elements that can be ghosted that are not included as child
+ // elements of the Supported element are considered ghosted.
+ if (!([supportedElements indexOfObject: element] == NSNotFound))
+ return YES;
+
+ return NO;
+}
+
- (NSString *) activeSyncRepresentationInContext: (WOContext *) context
{
NSArray *emails, *addresses, *categories, *elements;
CardElement *n, *homeAdr, *workAdr;
- NSMutableString *s;
+ NSMutableString *s, *a;
NSString *url;
id o;
@@ -63,7 +94,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ((o = [n flattenedValueAtIndex: 0 forKey: @""]))
[s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]];
-
+
if ((o = [n flattenedValueAtIndex: 1 forKey: @""]))
[s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]];
@@ -146,16 +177,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([addresses count])
{
homeAdr = [addresses objectAtIndex: 0];
+ a = [NSMutableString string];
if ((o = [homeAdr flattenedValueAtIndex: 2 forKey: @""]))
- [s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]];
+ [a appendString: o];
+
+ if ((o = [homeAdr flattenedValueAtIndex: 1 forKey: @""]) && [o length])
+ [a appendFormat: @"\n%@", o];
+ [s appendFormat: @"%@", [a activeSyncRepresentationInContext: context]];
+
if ((o = [homeAdr flattenedValueAtIndex: 3 forKey: @""]))
[s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]];
if ((o = [homeAdr flattenedValueAtIndex: 4 forKey: @""]))
[s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]];
-
+
if ((o = [homeAdr flattenedValueAtIndex: 5 forKey: @""]))
[s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]];
@@ -171,9 +208,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([addresses count])
{
workAdr = [addresses objectAtIndex: 0];
+ a = [NSMutableString string];
if ((o = [workAdr flattenedValueAtIndex: 2 forKey: @""]))
- [s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]];
+ [a appendString: o];
+
+ if ((o = [workAdr flattenedValueAtIndex: 1 forKey: @""]) && [o length])
+ [a appendFormat: @"\n%@", o];
+
+ [s appendFormat: @"%@", [a activeSyncRepresentationInContext: context]];
if ((o = [workAdr flattenedValueAtIndex: 3 forKey: @""]))
[s appendFormat: @"%@", [o activeSyncRepresentationInContext: context]];
@@ -217,6 +260,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
inContext: (WOContext *) context
{
CardElement *element;
+ NSMutableArray *addressLines;
id o;
// Contact's note
@@ -226,6 +270,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Categories
if ((o = [theValues objectForKey: @"Categories"]) && [o length])
[self setCategories: o];
+ else
+ [[self children] removeObjectsInArray: [self childrenWithTag: @"Categories"]];
// Birthday
if ((o = [theValues objectForKey: @"Birthday"]))
@@ -233,6 +279,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
o = [o calendarDate];
[self setBday: [o descriptionWithCalendarFormat: @"%Y-%m-%d" timeZone: nil locale: nil]];
}
+ else if (![self _isGhosted: @"Birthday" inContext: context])
+ {
+ [self setBday: @""];
+ }
+
//
// Business address information
@@ -244,18 +295,48 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// BusinessCountry
//
element = [self elementWithTag: @"adr" ofType: @"work"];
- [element setSingleValue: @""
- atIndex: 1 forKey: @""];
- [element setSingleValue: [theValues objectForKey: @"BusinessStreet"]
- atIndex: 2 forKey: @""];
- [element setSingleValue: [theValues objectForKey: @"BusinessCity"]
- atIndex: 3 forKey: @""];
- [element setSingleValue: [theValues objectForKey: @"BusinessState"]
- atIndex: 4 forKey: @""];
- [element setSingleValue: [theValues objectForKey: @"BusinessPostalCode"]
- atIndex: 5 forKey: @""];
- [element setSingleValue: [theValues objectForKey: @"BusinessCountry"]
- atIndex: 6 forKey: @""];
+
+ if ((o = [theValues objectForKey: @"BusinessStreet"]) || ![self _isGhosted: @"BusinessStreet" inContext: context])
+ {
+ addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]];
+
+ [element setSingleValue: @""
+ atIndex: 1 forKey: @""];
+ [element setSingleValue: [addressLines count] ? [addressLines objectAtIndex: 0] : @""
+ atIndex: 2 forKey: @""];
+
+ // Extended address line. If there are more than 2 address lines we add them to the extended address line.
+ if ([addressLines count] > 1)
+ {
+ [addressLines removeObjectAtIndex: 0];
+ [element setSingleValue: [addressLines componentsJoinedByString: @" "]
+ atIndex: 1 forKey: @""];
+ }
+ }
+
+ if ((o = [theValues objectForKey: @"BusinessCity"]) || ![self _isGhosted: @"BusinessCity" inContext: context])
+ {
+ [element setSingleValue: [theValues objectForKey: @"BusinessCity"]
+ atIndex: 3 forKey: @""];
+ }
+
+ if ((o = [theValues objectForKey: @"BusinessState"]) || ![self _isGhosted: @"BusinessState" inContext: context])
+ {
+ [element setSingleValue: [theValues objectForKey: @"BusinessState"]
+ atIndex: 4 forKey: @""];
+ }
+
+ if ((o = [theValues objectForKey: @"BusinessPostalCode"]) || ![self _isGhosted: @"BusinessPostalCode" inContext: context])
+ {
+ [element setSingleValue: [theValues objectForKey: @"BusinessPostalCode"]
+ atIndex: 5 forKey: @""];
+ }
+
+ if ((o = [theValues objectForKey: @"BusinessCountry"]) || ![self _isGhosted: @"BusinessCountry" inContext: context])
+ {
+ [element setSingleValue: [theValues objectForKey: @"BusinessCountry"]
+ atIndex: 6 forKey: @""];
+ }
//
// Home address information
@@ -267,35 +348,69 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// HomeCountry
//
element = [self elementWithTag: @"adr" ofType: @"home"];
- [element setSingleValue: @""
- atIndex: 1 forKey: @""];
- [element setSingleValue: [theValues objectForKey: @"HomeStreet"]
- atIndex: 2 forKey: @""];
- [element setSingleValue: [theValues objectForKey: @"HomeCity"]
- atIndex: 3 forKey: @""];
- [element setSingleValue: [theValues objectForKey: @"HomeState"]
- atIndex: 4 forKey: @""];
- [element setSingleValue: [theValues objectForKey: @"HomePostalCode"]
- atIndex: 5 forKey: @""];
- [element setSingleValue: [theValues objectForKey: @"HomeCountry"]
- atIndex: 6 forKey: @""];
+
+ if ((o = [theValues objectForKey: @"HomeStreet"]) || ![self _isGhosted: @"HomeStreet" inContext: context])
+ {
+ addressLines = [NSMutableArray arrayWithArray: [o componentsSeparatedByString: @"\n"]];
+
+ [element setSingleValue: @""
+ atIndex: 1 forKey: @""];
+ [element setSingleValue: [addressLines count] ? [addressLines objectAtIndex: 0] : @""
+ atIndex: 2 forKey: @""];
+
+ // Extended address line. If there are more then 2 address lines we add them to the extended address line.
+ if ([addressLines count] > 1)
+ {
+ [addressLines removeObjectAtIndex: 0];
+ [element setSingleValue: [addressLines componentsJoinedByString: @" "]
+ atIndex: 1 forKey: @""];
+ }
+ }
+
+ if ((o = [theValues objectForKey: @"HomeCity"]) || ![self _isGhosted: @"HomeCity" inContext: context])
+ {
+ [element setSingleValue: [theValues objectForKey: @"HomeCity"]
+ atIndex: 3 forKey: @""];
+ }
+
+ if ((o = [theValues objectForKey: @"HomeState"]) || ![self _isGhosted: @"HomeState" inContext: context])
+ {
+ [element setSingleValue: [theValues objectForKey: @"HomeState"]
+ atIndex: 4 forKey: @""];
+ }
+
+ if ((o = [theValues objectForKey: @"HomePostalCode"]) || ![self _isGhosted: @"HomePostalCode" inContext: context])
+ {
+ [element setSingleValue: [theValues objectForKey: @"HomePostalCode"]
+ atIndex: 5 forKey: @""];
+ }
+
+ if ((o = [theValues objectForKey: @"HomeCountry"]) || ![self _isGhosted: @"HomeCountry" inContext: context])
+ {
+ [element setSingleValue: [theValues objectForKey: @"HomeCountry"]
+ atIndex: 6 forKey: @""];
+ }
// Company's name
if ((o = [theValues objectForKey: @"CompanyName"]))
[self setOrg: o units: nil];
+ else if (![self _isGhosted: @"CompanyName" inContext: context])
+ [self setOrg: @"" units: nil];
// Department
if ((o = [theValues objectForKey: @"Department"]))
[self setOrg: nil units: [NSArray arrayWithObjects:o,nil]];
+ else if (![self _isGhosted: @"Department" inContext: context])
+ [self setOrg: nil units: [NSArray arrayWithObjects:@"",nil]];
// Email addresses
- if ((o = [theValues objectForKey: @"Email1Address"]))
+ if ((o = [theValues objectForKey: @"Email1Address"]) || ![self _isGhosted: @"Email1Address" inContext: context])
{
element = [self elementWithTag: @"email" ofType: @"work"];
[element setSingleValue: [o pureEMailAddress] forKey: @""];
}
- if ((o = [theValues objectForKey: @"Email2Address"]))
+ if ((o = [theValues objectForKey: @"Email2Address"]) || ![self _isGhosted: @"Email2Address" inContext: context])
{
element = [self elementWithTag: @"email" ofType: @"home"];
[element setSingleValue: [o pureEMailAddress] forKey: @""];
@@ -303,7 +418,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// SOGo currently only supports 2 email addresses ... but AS clients might send 3
// FIXME: revise this when the GUI revamp is done in SOGo
- if ((o = [theValues objectForKey: @"Email3Address"]))
+ if ((o = [theValues objectForKey: @"Email3Address"]) || ![self _isGhosted: @"Email3Address" inContext: context])
{
element = [self elementWithTag: @"email" ofType: @"three"];
[element setSingleValue: [o pureEMailAddress] forKey: @""];
@@ -313,45 +428,62 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// MiddleName
// Suffix (II)
// Title (Mr.)
- [self setFn: [theValues objectForKey: @"FileAs"]];
+ if ((o = [theValues objectForKey: @"FileAs"]) || ![self _isGhosted: @"FileAs" inContext: context])
+ [self setFn: [theValues objectForKey: @"FileAs"]];
[self setNWithFamily: [theValues objectForKey: @"LastName"]
given: [theValues objectForKey: @"FirstName"]
additional: nil prefixes: nil suffixes: nil];
// IM information
- [[self uniqueChildWithTag: @"x-aim"]
- setSingleValue: [theValues objectForKey: @"IMAddress"]
- forKey: @""];
+ if ((o = [theValues objectForKey: @"IMAddress"]) || ![self _isGhosted: @"IMAddress" inContext: context])
+ [[self uniqueChildWithTag: @"x-aim"]
+ setSingleValue: [theValues objectForKey: @"IMAddress"]
+ forKey: @""];
//
// Phone numbrrs
//
- element = [self elementWithTag: @"tel" ofType: @"work"];
- [element setSingleValue: [theValues objectForKey: @"BusinessPhoneNumber"] forKey: @""];
+ if ((o = [theValues objectForKey: @"BusinessPhoneNumber"]) || ![self _isGhosted: @"BusinessPhoneNumber" inContext: context])
+ {
+ element = [self elementWithTag: @"tel" ofType: @"work"];
+ [element setSingleValue: [theValues objectForKey: @"BusinessPhoneNumber"] forKey: @""];
+ }
- element = [self elementWithTag: @"tel" ofType: @"home"];
- [element setSingleValue: [theValues objectForKey: @"HomePhoneNumber"] forKey: @""];
+ if ((o = [theValues objectForKey: @"HomePhoneNumber"]) || ![self _isGhosted: @"HomePhoneNumber" inContext: context])
+ {
+ element = [self elementWithTag: @"tel" ofType: @"home"];
+ [element setSingleValue: [theValues objectForKey: @"HomePhoneNumber"] forKey: @""];
+ }
- element = [self elementWithTag: @"tel" ofType: @"cell"];
- [element setSingleValue: [theValues objectForKey: @"MobilePhoneNumber"] forKey: @""];
+ if ((o = [theValues objectForKey: @"MobilePhoneNumber"]) || ![self _isGhosted: @"MobilePhoneNumber" inContext: context])
+ {
+ element = [self elementWithTag: @"tel" ofType: @"cell"];
+ [element setSingleValue: [theValues objectForKey: @"MobilePhoneNumber"] forKey: @""];
+ }
- element = [self elementWithTag: @"tel" ofType: @"fax"];
- [element setSingleValue: [theValues objectForKey: @"BusinessFaxNumber"] forKey: @""];
+ if ((o = [theValues objectForKey: @"BusinessFaxNumber"]) || ![self _isGhosted: @"BusinessFaxNumber" inContext: context])
+ {
+ element = [self elementWithTag: @"tel" ofType: @"fax"];
+ [element setSingleValue: [theValues objectForKey: @"BusinessFaxNumber"] forKey: @""];
+ }
- element = [self elementWithTag: @"tel" ofType: @"pager"];
- [element setSingleValue: [theValues objectForKey: @"PagerNumber"] forKey: @""];
+ if ((o = [theValues objectForKey: @"PagerNumber"]) || ![self _isGhosted: @"PagerNumber" inContext: context])
+ {
+ element = [self elementWithTag: @"tel" ofType: @"pager"];
+ [element setSingleValue: [theValues objectForKey: @"PagerNumber"] forKey: @""];
+ }
// Job's title
- if ((o = [theValues objectForKey: @"JobTitle"]))
+ if ((o = [theValues objectForKey: @"JobTitle"]) || ![self _isGhosted: @"JobTitle" inContext: context])
[self setTitle: o];
// WebPage (work)
- if ((o = [theValues objectForKey: @"WebPage"]))
+ if ((o = [theValues objectForKey: @"WebPage"]) || ![self _isGhosted: @"WebPage" inContext: context])
[[self elementWithTag: @"url" ofType: @"work"]
setSingleValue: o forKey: @""];
- if ((o = [theValues objectForKey: @"NickName"]))
+ if ((o = [theValues objectForKey: @"NickName"]) || ![self _isGhosted: @"NickName" inContext: context])
[self setNickname: o];
if ((o = [theValues objectForKey: @"Picture"]))
diff --git a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m
index 950ee4769..836d9ee79 100644
--- a/ActiveSync/SOGoActiveSyncDispatcher+Sync.m
+++ b/ActiveSync/SOGoActiveSyncDispatcher+Sync.m
@@ -112,13 +112,73 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@implementation SOGoActiveSyncDispatcher (Sync)
+- (void) _setOrUnsetSyncRequest: (BOOL) set
+ collections: (NSArray *) collections
+{
+ SOGoCacheGCSObject *o;
+ NSNumber *processIdentifier;
+ NSString *key;
+ int i;
+
+ processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]];
+
+ o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO];
+ [o setObjectType: ActiveSyncGlobalCacheObject];
+ [o setTableUrl: [self folderTableURL]];
+ [o reloadIfNeeded];
+
+ if (set)
+ {
+ RELEASE(syncRequest);
+ syncRequest = [NSNumber numberWithUnsignedInt: [[NSCalendarDate date] timeIntervalSince1970]];
+ RETAIN(syncRequest);
+
+ [[o properties] setObject: syncRequest forKey: @"SyncRequest"];
+
+ for (i = 0; i < [collections count]; i++)
+ {
+ key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[[collections objectAtIndex: i] getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]];
+ [[o properties] setObject: processIdentifier forKey: key];
+ }
+ }
+ else
+ {
+ [[o properties] removeObjectForKey: @"SyncRequest"];
+ for (i = 0; i < [collections count]; i++)
+ {
+ key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[[collections objectAtIndex: i] getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]];
+ [[o properties] removeObjectForKey: key];
+ }
+ }
+
+ [o save];
+}
+
- (void) _setFolderMetadata: (NSDictionary *) theFolderMetadata
forKey: (NSString *) theFolderKey
{
+ NSNumber *processIdentifier, *processIdentifierInCache;
SOGoCacheGCSObject *o;
NSDictionary *values;
NSString *key;
+ if ([theFolderKey hasPrefix: @"folder"])
+ key = [NSString stringWithFormat: @"SyncRequest+mail/%@", [theFolderKey substringFromIndex: 6]];
+ else
+ key = [NSString stringWithFormat: @"SyncRequest+%@", theFolderKey];
+
+ processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]];
+ processIdentifierInCache = [[self globalMetadataForDevice] objectForKey: key];
+
+ // Don't update the cache if another request is processing the same collection.
+ if (!([processIdentifierInCache isEqual: processIdentifier]))
+ {
+ if (debugOn)
+ [self logWithFormat: @"EAS - We lost our lock - discard folder cache update %@ %@ <> %@", key, processIdentifierInCache, processIdentifier];
+
+ return;
+ }
+
key = [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theFolderKey];
values = [theFolderMetadata copy];
@@ -132,7 +192,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[[o properties] removeObjectForKey: @"DateCache"];
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
+ [[o properties] removeObjectForKey: @"SupportedElements"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
+ [[o properties] removeObjectForKey: @"InitialLoadSequence"];
[[o properties] addEntriesFromDictionary: values];
[o save];
@@ -159,7 +221,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSString *nameInCache;
if (theFolderType == ActiveSyncMailFolder)
- nameInCache= [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]];
+ nameInCache = [imapFolderGUIDS objectForKey: [theCollection nameInContainer]];
else
{
NSString *component_name;
@@ -170,7 +232,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
component_name = @"vtodo";
- nameInCache= [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]];
+ nameInCache = [NSString stringWithFormat: @"%@/%@", component_name, [theCollection nameInContainer]];
}
return nameInCache;
@@ -403,8 +465,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[o takeActiveSyncValues: allChanges inContext: context];
[sogoObject saveComponent: o];
- [syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
-
+ if ([syncCache objectForKey: serverId])
+ [syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
}
break;
case ActiveSyncEventFolder:
@@ -414,27 +476,27 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[o takeActiveSyncValues: allChanges inContext: context];
[sogoObject saveComponent: o];
- [syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
-
+ if ([syncCache objectForKey: serverId])
+ [syncCache setObject: [NSString stringWithFormat:@"%f", [[sogoObject lastModified] timeIntervalSince1970]] forKey: serverId];
}
break;
case ActiveSyncMailFolder:
default:
{
NSDictionary *result;
- NSString *modseq;
+ NSNumber *modseq;
[sogoObject takeActiveSyncValues: allChanges inContext: context];
result = [sogoObject fetchParts: [NSArray arrayWithObject: @"MODSEQ"]];
modseq = [[[result objectForKey: @"RawResponse"] objectForKey: @"fetch"] objectForKey: @"modseq"];
- if (modseq)
- [syncCache setObject: modseq forKey: serverId];
+ if (modseq && [syncCache objectForKey: serverId])
+ [syncCache setObject: [modseq stringValue] forKey: serverId];
}
}
- [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
+ [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
[theBuffer appendString: @""];
@@ -577,36 +639,44 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
withFilterType: (NSCalendarDate *) theFilterType
inBuffer: (NSMutableString *) theBuffer
lastServerKey: (NSString **) theLastServerKey
-
+ defaultInterval: (unsigned int) theDefaultInterval
{
NSMutableDictionary *folderMetadata, *dateCache, *syncCache;
NSString *davCollectionTagToStore;
NSAutoreleasePool *pool;
NSMutableString *s;
- BOOL more_available;
+ BOOL cleanup_needed, more_available;
int i, max;
s = [NSMutableString string];
-
- more_available = NO;
+ cleanup_needed = more_available = NO;
folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: theCollection withType: theFolderType]];
- // If this is a new sync operation, DateCache and SyncCache needs to be deleted
+ // If this is a new sync operation, DateCache and SyncCache need to be deleted
if ([theSyncKey isEqualToString: @"-1"])
{
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"SyncCache"];
[folderMetadata setObject: [NSMutableDictionary dictionary] forKey: @"DateCache"];
}
+ else if ([folderMetadata objectForKey: @"SyncKey"] && !([theSyncKey isEqualToString: [folderMetadata objectForKey: @"SyncKey"]]))
+ {
+ // The syncKey received from the client doesn't match the syncKey we have in cache - client might have missed a response.
+ // We need to cleanup this mess.
+ [self logWithFormat: @"Cache cleanup needed for device %@ - user: %@ syncKey: %@ cache: %@", [context objectForKey: @"DeviceId"], [[context activeUser] login], theSyncKey, [folderMetadata objectForKey: @"SyncKey"]];
+ cleanup_needed = YES;
+ }
syncCache = [folderMetadata objectForKey: @"SyncCache"];
dateCache = [folderMetadata objectForKey: @"DateCache"];
if ((theFolderType == ActiveSyncMailFolder || theFolderType == ActiveSyncEventFolder || theFolderType == ActiveSyncTaskFolder) &&
- !([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
- !([theSyncKey isEqualToString: @"-1"]) && // new sync operation
- theFilterType)
+ (cleanup_needed ||
+ ( !([folderMetadata objectForKey: @"MoreAvailable"]) && // previous sync operation reached the windowSize or maximumSyncReponseSize
+ !([folderMetadata objectForKey: @"InitialLoadSequence"]))) &&
+ theFilterType
+ )
{
NSArray *allKeys;
NSString *key;
@@ -622,14 +692,36 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if ([[dateCache objectForKey:key] compare: theFilterType] == NSOrderedAscending)
{
- [s appendString: @""];
- [s appendFormat: @"%@", key];
- [s appendString: @""];
+ if ([syncCache objectForKey:key])
+ {
+ if (debugOn)
+ [self logWithFormat: @"EAS - SoftDelete %@", key];
- [syncCache removeObjectForKey: key];
- [dateCache removeObjectForKey: key];
+ [s appendString: @""];
+ [s appendFormat: @"%@", key];
+ [s appendString: @""];
+
+ [syncCache removeObjectForKey: key];
+ //[dateCache removeObjectForKey: key];
- softdelete_count++;
+ softdelete_count++;
+ }
+ else if (cleanup_needed)
+ {
+ if (debugOn)
+ [self logWithFormat: @"EAS - SoftDelete cleanup %@", key];
+
+ // With this we make sure that a SoftDelete is set again on next sync.
+ [syncCache setObject: @"0" forKey: key];
+ }
+ else
+ {
+ if (debugOn)
+ [self logWithFormat: @"EAS - SoftDelete final delete %@", key];
+
+ // Now we are save to remove the dateCache entry.
+ [dateCache removeObjectForKey: key];
+ }
}
if (softdelete_count >= theWindowSize || (theMaxSyncResponseSize > 0 && [s length] >= theMaxSyncResponseSize))
@@ -656,8 +748,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
if ([theSyncKey isEqualToString: [theCollection davCollectionTag]] && !([s length]))
return;
-
- more_available = NO;
davCollectionTagToStore = [theCollection davCollectionTag];
@@ -673,7 +763,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSDictionary *component;
NSArray *allComponents;
- BOOL updated;
+ BOOL updated, initialLoadInProgress;
int deleted, return_count;
if (theFolderType == ActiveSyncContactFolder)
@@ -683,13 +773,76 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
component_name = @"vtodo";
- allComponents = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
- allComponents = [allComponents sortedArrayUsingDescriptors: [NSArray arrayWithObjects: [[NSSortDescriptor alloc] initWithKey: @"c_lastmodified" ascending:YES], nil]];
+ initialLoadInProgress = NO;
+
+ if ([theSyncKey isEqualToString: @"-1"])
+ [folderMetadata setObject: davCollectionTagToStore forKey: @"InitialLoadSequence"];
+
+ if ([folderMetadata objectForKey: @"InitialLoadSequence"])
+ {
+ if ([theSyncKey intValue] < [[folderMetadata objectForKey: @"InitialLoadSequence"] intValue])
+ initialLoadInProgress = YES;
+ else
+ [folderMetadata removeObjectForKey: @"InitialLoadSequence"];
+ }
+
+ allComponents = [theCollection syncTokenFieldsWithProperties: nil
+ matchingSyncToken: theSyncKey
+ fromDate: theFilterType
+ initialLoad: initialLoadInProgress];
+ allComponents = [allComponents sortedArrayUsingDescriptors: [NSArray arrayWithObject: [[[NSSortDescriptor alloc] initWithKey: @"c_lastmodified" ascending: YES] autorelease]]];
// Check for the WindowSize
max = [allComponents count];
+ //
+ // Cleanup the mess
+ //
+ if (cleanup_needed)
+ {
+
+ for (i = 0; i < max; i++)
+ {
+ component = [allComponents objectAtIndex: i];
+ deleted = [[component objectForKey: @"c_deleted"] intValue];
+
+ if (!deleted && ![[component objectForKey: @"c_component"] isEqualToString: component_name])
+ continue;
+
+ uid = [[component objectForKey: @"c_name"] sanitizedServerIdWithType: theFolderType];
+
+ if (deleted)
+ {
+ if (debugOn)
+ [self logWithFormat: @"EAS - Cache cleanup: DELETE %@", uid];
+
+ // For deletes we have to recreate a cache entry to make sure the delete is sent again.
+ [syncCache setObject: @"0" forKey: uid];
+ }
+ else
+ {
+ if ([syncCache objectForKey: uid] && [[component objectForKey: @"c_creationdate"] intValue] > [theSyncKey intValue])
+ {
+ if (debugOn)
+ [self logWithFormat: @"EAS - Cache cleanup: ADD %@", uid];
+
+ // Cleanup the cache to make sure the add is sent again.
+ [syncCache removeObjectForKey: uid];
+ [dateCache removeObjectForKey: uid];
+ }
+ else
+ {
+ if (debugOn)
+ [self logWithFormat: @"EAS - Cache cleanup: CHANGE %@", uid];
+
+ // Update cache entry to make sure the change is sent again.
+ [syncCache setObject: @"0" forKey: uid];
+ }
+ }
+ }
+ }
+
return_count = 0;
for (i = 0; i < max; i++)
@@ -847,12 +1000,38 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSMutableArray *allCacheObjects, *sortedBySequence;
SOGoMailObject *mailObject;
- NSArray *allMessages;
+ NSArray *allMessages, *a;
+ NSString *firstUIDAdded;
- int j, k, return_count;
- BOOL found_in_cache;
+ int j, k, return_count, highestmodseq;
+ BOOL found_in_cache, initialLoadInProgress;
- allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
+ initialLoadInProgress = NO;
+ found_in_cache = NO;
+ firstUIDAdded = nil;
+
+ if ([theSyncKey isEqualToString: @"-1"])
+ {
+ highestmodseq = 0;
+
+ a = [[theCollection davCollectionTag] componentsSeparatedByString: @"-"];
+ [folderMetadata setObject: [a objectAtIndex: 1] forKey: @"InitialLoadSequence"];
+ }
+ else
+ {
+ a = [theSyncKey componentsSeparatedByString: @"-"];
+ highestmodseq = [[a objectAtIndex: 1] intValue];
+ }
+
+ if ([folderMetadata objectForKey: @"InitialLoadSequence"])
+ {
+ if (highestmodseq < [[folderMetadata objectForKey: @"InitialLoadSequence"] intValue])
+ initialLoadInProgress = YES;
+ else
+ [folderMetadata removeObjectForKey: @"InitialLoadSequence"];
+ }
+
+ allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType initialLoad: initialLoadInProgress];
max = [allMessages count];
allCacheObjects = [NSMutableArray array];
@@ -860,7 +1039,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
for (i = 0; i < max; i++)
{
[allCacheObjects addObject: [SOGoSyncCacheObject syncCacheObjectWithUID: [[[allMessages objectAtIndex: i] allKeys] lastObject]
- sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
+ sequence: [[[allMessages objectAtIndex: i] allValues] lastObject]]];
}
sortedBySequence = [[NSMutableArray alloc] initWithDictionary: syncCache];
@@ -869,15 +1048,82 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[allCacheObjects sortUsingSelector: @selector(compareSequence:)];
- //NSLog(@"sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]);
- //NSLog(@"allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]);
+ if (debugOn)
+ {
+ [self logWithFormat: @"EAS - sortedBySequence (%d) - lastObject: %@", [sortedBySequence count], [sortedBySequence lastObject]];
+ [self logWithFormat: @"EAS - allCacheObjects (%d) - lastObject: %@", [allCacheObjects count], [allCacheObjects lastObject]];
+ }
lastCacheObject = [sortedBySequence lastObject];
+
+ //
+ // Cleanup the mess
+ //
+ if (cleanup_needed)
+ {
+ NSMutableArray *sortedByUID;
+ int uidnextFromCache;
+
+ sortedByUID = [[NSMutableArray alloc] initWithDictionary: syncCache];
+ [sortedByUID sortUsingSelector: @selector(compareUID:)];
+
+ // Get the uid from SyncKey in cache. The uid is the first uid added to cache by the last sync request.
+ a = [[folderMetadata objectForKey: @"SyncKey"] componentsSeparatedByString: @"-"];
+ uidnextFromCache = [[a objectAtIndex: 0] intValue];
+
+ if (debugOn)
+ [self logWithFormat: @"EAS - Cache cleanup: from uid: %d to uid: %d", uidnextFromCache, [[[sortedByUID lastObject] uid] intValue]];
+
+ // Remove all entries from cache beginning with the first uid added by the last sync request.
+ for (j = uidnextFromCache; j <= [[[sortedByUID lastObject] uid] intValue]; j++)
+ {
+ if (debugOn)
+ [self logWithFormat: @"EAS - Cache cleanup: ADD %d", j];
+
+ [syncCache removeObjectForKey: [NSString stringWithFormat:@"%d", j]];
+ [dateCache removeObjectForKey: [NSString stringWithFormat:@"%d", j]];
+ }
+
+ RELEASE(sortedByUID);
+
+ for (j = 0; j < [allCacheObjects count]; j++)
+ {
+ // Update the modseq in cache, sence othersie, it would be identical to the modseq from server
+ //and we would skip the cache when generating the response.
+ if ([syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]] && ![[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]])
+ {
+ if (debugOn)
+ [self logWithFormat: @"EAS - Cache cleanup: CHANGE %@", [[allCacheObjects objectAtIndex: j] uid]];
+
+ [syncCache setObject: @"0" forKey:[[allCacheObjects objectAtIndex: j] uid]];
+ }
+ else if ([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]])
+ {
+ if (debugOn)
+ [self logWithFormat: @"EAS - Cache cleanup: DELETE %@", [[allCacheObjects objectAtIndex: j] uid]];
+
+ // For deletes we have to recreate a cache entry to have the included in the response.
+ [syncCache setObject: @"0" forKey:[[allCacheObjects objectAtIndex: j] uid]];
+ }
+ }
+ }
- if ([folderMetadata objectForKey: @"MoreAvailable"] && lastCacheObject)
+ if (!cleanup_needed &&
+ [folderMetadata objectForKey: @"MoreAvailable"] &&
+ lastCacheObject &&
+ !([[lastCacheObject sequence] isEqual: @"0"])) // Sequence 0 is set during cache cleanup.
{
for (j = 0; j < [allCacheObjects count]; j++)
{
+ if (([[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]] && [syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]]) ||
+ (![[[allCacheObjects objectAtIndex: j] sequence] isEqual: [NSNull null]] && ![syncCache objectForKey: [[allCacheObjects objectAtIndex: j] uid]]))
+ {
+ // We need to continue with adds or deletes from here.
+ found_in_cache = YES;
+ j--;
+ break;
+ }
+
if ([[lastCacheObject uid] isEqual: [[allCacheObjects objectAtIndex: j] uid]])
{
// Found out where we're at, let's continue from there...
@@ -892,12 +1138,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (found_in_cache)
k = j+1;
else
- {
- k = 0;
- j = 0;
- }
-
- //NSLog(@"found in cache: %d k = %d", found_in_cache, k);
+ j = k = 0;
+
+ if (debugOn)
+ [self logWithFormat: @"EAS - found in cache: %d k = %d", found_in_cache, k];
return_count = 0;
@@ -911,20 +1155,35 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSString *lastSequence;
more_available = YES;
- lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? @"1" : [aCacheObject sequence]);
- *theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", [aCacheObject uid], lastSequence];
- //NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey);
+ if (!firstUIDAdded)
+ {
+ a = [davCollectionTagToStore componentsSeparatedByString: @"-"];
+ firstUIDAdded = [a objectAtIndex: 0];
+ RETAIN(firstUIDAdded);
+ }
+ lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? [NSString stringWithFormat:@"%d", highestmodseq] : [aCacheObject sequence]);
+ *theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", firstUIDAdded, lastSequence];
+
+ if (debugOn)
+ [self logWithFormat: @"EAS - Reached windowSize - lastUID will be: %@", *theLastServerKey];
+
DESTROY(pool);
break;
}
aCacheObject = [allCacheObjects objectAtIndex: k];
- // If found in cache, it's either a Change or a Delete
+ if (debugOn)
+ [self logWithFormat: @"EAS - Dealing with cacheObject: %@", aCacheObject];
+
+ // If found in cache, it's either a Change or a Delete operation.
if ([syncCache objectForKey: [aCacheObject uid]])
{
if ([[aCacheObject sequence] isEqual: [NSNull null]])
{
+ if (debugOn)
+ [self logWithFormat: @"EAS - DELETE!"];
+
// Deleted
[s appendString: @""];
[s appendFormat: @"%@", [aCacheObject uid]];
@@ -942,9 +1201,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
mailObject = [theCollection lookupName: [aCacheObject uid]
inContext: context
acquire: 0];
-
+
if (![[aCacheObject sequence] isEqual: [syncCache objectForKey: [aCacheObject uid]]])
{
+ if (debugOn)
+ [self logWithFormat: @"EAS - CHANGE!"];
+
[s appendString: @""];
[s appendFormat: @"%@", [aCacheObject uid]];
[s appendString: @""];
@@ -960,6 +1222,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
else
{
+ if (debugOn)
+ [self logWithFormat: @"EAS - ADD!"];
+
// Added
if (![[aCacheObject sequence] isEqual: [NSNull null]])
{
@@ -992,11 +1257,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[syncCache setObject: [aCacheObject sequence] forKey: [aCacheObject uid]];
[dateCache setObject: [NSCalendarDate date] forKey: [aCacheObject uid]];
+
+ // Save the frist UID we add. We will use it for the synckey late.
+ if (!firstUIDAdded)
+ {
+ firstUIDAdded = [aCacheObject uid];
+ RETAIN(firstUIDAdded);
+ if (debugOn)
+ [self logWithFormat: @"EAS - first uid added %@", firstUIDAdded];
+ }
+
return_count++;
}
else
{
- //NSLog(@"skipping old deleted UID: %@", [aCacheObject uid]);
+ if (debugOn)
+ [self logWithFormat: @"EAS - skipping old deleted UID: %@", [aCacheObject uid]];
}
}
@@ -1005,16 +1281,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (more_available)
{
- [folderMetadata setObject: [NSNumber numberWithBool: YES] forKey: @"MoreAvailable"];
+ [folderMetadata setObject: [NSNumber numberWithInt: YES] forKey: @"MoreAvailable"];
[folderMetadata setObject: *theLastServerKey forKey: @"SyncKey"];
}
else
{
[folderMetadata removeObjectForKey: @"MoreAvailable"];
- [folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
+
+ if (firstUIDAdded)
+ {
+ a = [davCollectionTagToStore componentsSeparatedByString: @"-"];
+ [folderMetadata setObject: [[NSString alloc] initWithFormat: @"%@-%@", firstUIDAdded, [a objectAtIndex: 1]] forKey: @"SyncKey"];
+ RELEASE(firstUIDAdded);
+ }
+ else
+ [folderMetadata setObject: davCollectionTagToStore forKey: @"SyncKey"];
}
[self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: theCollection withType: theFolderType]];
+
RELEASE(*theLastServerKey);
} // default:
@@ -1113,15 +1398,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
changeDetected: (BOOL *) changeDetected
maxSyncResponseSize: (int) theMaxSyncResponseSize
{
- NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache;
- SOGoMicrosoftActiveSyncFolderType folderType;
- id collection, value;
-
- NSMutableString *changeBuffer, *commandsBuffer;
- BOOL getChanges, first_sync;
- unsigned int windowSize, v, status;
+ NSString *collectionId, *realCollectionId, *syncKey, *davCollectionTag, *bodyPreferenceType, *mimeSupport, *lastServerKey, *syncKeyInCache, *folderKey;
NSMutableDictionary *folderMetadata, *folderOptions;
-
+ NSMutableArray *supportedElements, *supportedElementNames;
+ NSMutableString *changeBuffer, *commandsBuffer;
+ id collection, value;
+
+ SOGoMicrosoftActiveSyncFolderType folderType;
+ unsigned int windowSize, v, status, i;
+ BOOL getChanges, first_sync;
+
changeBuffer = [NSMutableString string];
commandsBuffer = [NSMutableString string];
@@ -1144,7 +1430,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//[theBuffer appendString: @""];
return;
}
-
+
+ //
+ // First check if we have any concurrent Sync requests going on for this device.
+ // If we do and we are still within our maximumSyncInterval, we let our EAS
+ // device know to retry.
+ //
+ folderKey = [self _getNameInCache: collection withType: folderType];
+ folderMetadata = [self _folderMetadataForKey: folderKey];
+
// We check for a window size, default to 100 if not specfied or out of bounds
windowSize = [[[(id)[theDocumentElement getElementsByTagName: @"WindowSize"] lastObject] textValue] intValue];
@@ -1169,13 +1463,35 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
first_sync = NO;
- folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
-
if ([syncKey isEqualToString: @"0"])
{
davCollectionTag = @"-1";
first_sync = YES;
*changeDetected = YES;
+
+ supportedElementNames = [[[NSMutableArray alloc] init] autorelease];
+ value = [theDocumentElement getElementsByTagName: @"Supported"];
+
+ if ([value count])
+ {
+ supportedElements = (id)[[value lastObject] childNodes];
+
+ if ([supportedElements count])
+ {
+ for (i = 0; i < [supportedElements count]; i++)
+ {
+ if ([[supportedElements objectAtIndex: i] nodeType] == DOM_ELEMENT_NODE)
+ [supportedElementNames addObject: [[supportedElements objectAtIndex: i] tagName]];
+ }
+ }
+
+ [folderMetadata setObject: supportedElementNames forKey: @"SupportedElements"];
+
+ [self _setFolderMetadata: folderMetadata forKey: folderKey];
+
+ if (debugOn)
+ [self logWithFormat: @"EAS - %d %@: supportedElements saved: %@", [supportedElements count], [collection nameInContainer], supportedElementNames];
+ }
}
else if ((![syncKey isEqualToString: @"-1"]) && !([folderMetadata objectForKey: @"SyncCache"]))
{
@@ -1200,6 +1516,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// By default, send MIME mails. See #3146 for details.
if (!bodyPreferenceType)
bodyPreferenceType = @"4";
+
+ mimeSupport = [[folderMetadata objectForKey: @"FolderOptions"] objectForKey: @"MIMESupport"];
+
+ if (!mimeSupport)
+ mimeSupport = @"1";
}
else
{
@@ -1225,11 +1546,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
folderOptions = [[NSDictionary alloc] initWithObjectsAndKeys: mimeSupport, @"MIMESupport", bodyPreferenceType, @"BodyPreferenceType", nil];
[folderMetadata setObject: folderOptions forKey: @"FolderOptions"];
- [self _setFolderMetadata: folderMetadata forKey: [self _getNameInCache: collection withType: folderType]];
+ [self _setFolderMetadata: folderMetadata forKey: folderKey];
}
}
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
+ [context setObject: mimeSupport forKey: @"MIMESupport"];
+ [context setObject: [folderMetadata objectForKey: @"SupportedElements"] forKey: @"SupportedElements"];
//
// We process the commands from the request
@@ -1268,10 +1591,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
withFolderType: folderType
withFilterType: [NSCalendarDate dateFromFilterType: [[(id)[theDocumentElement getElementsByTagName: @"FilterType"] lastObject] textValue]]
inBuffer: changeBuffer
- lastServerKey: &lastServerKey];
+ lastServerKey: &lastServerKey
+ defaultInterval: [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncInterval]];
}
- folderMetadata = [self _folderMetadataForKey: [self _getNameInCache: collection withType: folderType]];
+ folderMetadata = [self _folderMetadataForKey: folderKey];
// If we got any changes or if we have applied any commands
// let's regenerate our SyncKey based on the collection tag.
@@ -1430,19 +1754,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SOGoSystemDefaults *defaults;
id aCollection;
NSMutableString *output, *s;
+ NSMutableDictionary *globalMetadata;
+ NSNumber *syncRequestInCache, *processIdentifier;
+ NSString *key;
NSArray *allCollections;
NSData *d;
- int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize;
+ int i, j, defaultInterval, heartbeatInterval, internalInterval, maxSyncResponseSize, total_sleep;
BOOL changeDetected;
- changeDetected = NO;
-
- maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
-
// We initialize our output buffer
output = [[NSMutableString alloc] init];
+ defaults = [SOGoSystemDefaults sharedSystemDefaults];
+ defaultInterval = [defaults maximumSyncInterval];
+ processIdentifier = [NSNumber numberWithInt: [[NSProcessInfo processInfo] processIdentifier]];
+
+ allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"];
+
[output appendString: @""];
[output appendString: @""];
[output appendString: @""];
@@ -1457,12 +1786,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[output appendString: @""];
d = [[output dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
[theResponse setContent: d];
+ RELEASE(output);
return;
}
+
+ // Let other requests know about the collections we are dealing with.
+ [self _setOrUnsetSyncRequest: YES collections: allCollections];
- defaults = [SOGoSystemDefaults sharedSystemDefaults];
+ changeDetected = NO;
+ maxSyncResponseSize = [[SOGoSystemDefaults sharedSystemDefaults] maximumSyncResponseSize];
heartbeatInterval = [[[(id)[theDocumentElement getElementsByTagName: @"HeartbeatInterval"] lastObject] textValue] intValue];
- defaultInterval = [defaults maximumSyncInterval];
internalInterval = [defaults internalSyncInterval];
// If the request doesn't contain "HeartbeatInterval" there is no reason to delay the response.
@@ -1472,17 +1805,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// We check to see if our heartbeat interval falls into the supported ranges.
if (heartbeatInterval > defaultInterval || heartbeatInterval < 1)
{
+ int limit;
// Interval is too long, inform the client.
heartbeatInterval = defaultInterval;
- // Outlook doesn't like this...
- //[output appendFormat: @"%d", defaultInterval];
+ // When Status = 14, the Wait interval is specified in minutes while
+ // defaultInterval is specifed in seconds. Adjust accordinlgy.
+ limit = defaultInterval/60;
+ if (limit < 1) limit = 1;
+ if (limit > 59) limit = 59;
+ //[output appendFormat: @"%d", limit];
//[output appendFormat: @"%d", 14];
}
[output appendString: @""];
-
- allCollections = (id)[theDocumentElement getElementsByTagName: @"Collection"];
// We enter our loop detection change
for (i = 0; i < (heartbeatInterval/internalInterval); i++)
@@ -1492,25 +1828,62 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
for (j = 0; j < [allCollections count]; j++)
{
aCollection = [allCollections objectAtIndex: j];
-
+
[self processSyncCollection: aCollection
inBuffer: s
changeDetected: &changeDetected
maxSyncResponseSize: maxSyncResponseSize];
- if (maxSyncResponseSize > 0 && [s length] >= maxSyncResponseSize)
+ // Don't return a response if another Sync is waiting.
+ globalMetadata = [self globalMetadataForDevice];
+ key = [NSString stringWithFormat: @"SyncRequest+%@", [[[(id)[aCollection getElementsByTagName: @"CollectionId"] lastObject] textValue] stringByUnescapingURL]];
+
+ if (!([[globalMetadata objectForKey: key] isEqual: processIdentifier]))
+ {
+ if (debugOn)
+ [self logWithFormat: @"EAS - Discard response %@", [self globalMetadataForDevice]];
+
+ [theResponse setStatus: 503];
+
+ RELEASE(output);
+ return;
+ }
+
+ if ((maxSyncResponseSize > 0 && [s length] >= maxSyncResponseSize))
break;
}
if (changeDetected)
{
- [self logWithFormat: @"Change detected, we push the content."];
+ [self logWithFormat: @"Change detected during Sync, we push the content."];
break;
}
else if (heartbeatInterval > 1)
{
- [self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
- sleep(internalInterval);
+ total_sleep = 0;
+
+ while (total_sleep < internalInterval)
+ {
+ // We check if we must break the current synchronization since an other Sync
+ // has just arrived.
+ syncRequestInCache = [[self globalMetadataForDevice] objectForKey: @"SyncRequest"];
+ if (!([syncRequest isEqualToNumber: syncRequestInCache]))
+ {
+ if (debugOn)
+ [self logWithFormat: @"EAS - Heartbeat stopped %@", [self globalMetadataForDevice]];
+
+ // Make sure we end the heardbeat-loop.
+ heartbeatInterval = internalInterval = 1;
+
+ break;
+ }
+ else
+ {
+ [self logWithFormat: @"Sleeping %d seconds while detecting changes in Sync...", internalInterval-total_sleep];
+ sleep(5);
+ total_sleep += 5;
+ }
+ }
}
else
{
@@ -1518,8 +1891,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
}
-
- // Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0 oterwise send an empty response.
+ //
+ // Only send a response if there are changes or MS-ASProtocolVersion is either 2.5 or 12.0,
+ // otherwise send an empty response.
+ //
if (changeDetected || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"2.5"] || [[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"12.0"])
{
// We always return the last generated response.
diff --git a/ActiveSync/SOGoActiveSyncDispatcher.h b/ActiveSync/SOGoActiveSyncDispatcher.h
index d72073ad1..76b4acbe7 100644
--- a/ActiveSync/SOGoActiveSyncDispatcher.h
+++ b/ActiveSync/SOGoActiveSyncDispatcher.h
@@ -1,6 +1,6 @@
/*
-Copyright (c) 2014, Inverse inc.
+Copyright (c) 2014-2015, Inverse inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -31,15 +31,25 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SOGoActiveSyncConstants.h"
+@class NSCalendarDate;
@class NSException;
+@class NSMutableDictionary;
@class NSURL;
+@class NSNumber;
@interface SOGoActiveSyncDispatcher : NSObject
{
NSURL *folderTableURL;
+ NSDictionary *imapFolderGUIDS;
id context;
+
+ NSNumber *syncRequest;
+
+ BOOL debugOn;
}
+- (NSMutableDictionary *) globalMetadataForDevice;
+
- (id) collectionFromId: (NSString *) theCollectionId
type: (SOGoMicrosoftActiveSyncFolderType) theFolderType;
diff --git a/ActiveSync/SOGoActiveSyncDispatcher.m b/ActiveSync/SOGoActiveSyncDispatcher.m
index 9be2ebe62..eae24fcf3 100644
--- a/ActiveSync/SOGoActiveSyncDispatcher.m
+++ b/ActiveSync/SOGoActiveSyncDispatcher.m
@@ -30,8 +30,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "SOGoActiveSyncDispatcher.h"
#import
+#import
#import
#import
+#if GNUSTEP_BASE_MINOR_VERSION >= 21
+#import
+#endif
#import
#import
#import
@@ -134,6 +138,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include
+#ifdef HAVE_OPENSSL
+#include
+#include
+#include
+#endif
+
@interface SOGoActiveSyncDispatcher (Sync)
- (NSMutableDictionary *) _folderMetadataForKey: (NSString *) theFolderKey;
@@ -143,20 +153,22 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@implementation SOGoActiveSyncDispatcher
-static BOOL debugOn = NO;
-
- (id) init
{
[super init];
debugOn = [[SOGoSystemDefaults sharedSystemDefaults] easDebugEnabled];
folderTableURL = nil;
+ imapFolderGUIDS = nil;
+ syncRequest = nil;
return self;
}
- (void) dealloc
{
RELEASE(folderTableURL);
+ RELEASE(imapFolderGUIDS);
+ RELEASE(syncRequest);
[super dealloc];
}
@@ -169,16 +181,16 @@ static BOOL debugOn = NO;
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
- [[o properties] removeAllObjects];
- [[o properties] addEntriesFromDictionary: [NSDictionary dictionaryWithObject: theSyncKey forKey: @"FolderSyncKey"]];
+ [[o properties] setObject: theSyncKey
+ forKey: @"FolderSyncKey"];
[o save];
}
-- (NSMutableDictionary *) _globalMetadataForDevice
+- (NSMutableDictionary *) globalMetadataForDevice
{
SOGoCacheGCSObject *o;
- o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil];
+ o = [SOGoCacheGCSObject objectWithName: [context objectForKey: @"DeviceId"] inContainer: nil useCache: NO];
[o setObjectType: ActiveSyncGlobalCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
@@ -202,7 +214,7 @@ static BOOL debugOn = NO;
if (theFilter)
{
o = [SOGoCacheGCSObject objectWithName: [NSString stringWithFormat: @"%@+%@", [context objectForKey: @"DeviceId"], theCollectionId] inContainer: nil];
- [o setObjectType: ActiveSyncGlobalCacheObject];
+ [o setObjectType: ActiveSyncFolderCacheObject];
[o setTableUrl: [self folderTableURL]];
[o reloadIfNeeded];
@@ -229,23 +241,27 @@ static BOOL debugOn = NO;
SOGoMailAccounts *accountsFolder;
SOGoMailAccount *accountFolder;
SOGoUserFolder *userFolder;
- NSDictionary *imapGUIDs;
- userFolder = [[context activeUser] homeFolderInContext: context];
- accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
- accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
-
- // Get the GUID of the IMAP folder
- imapGUIDs = [accountFolder imapFolderGUIDs];
-
- //return [[imapGUIDs allKeysForObject: theIdToTranslate] objectAtIndex: 0];
- return [[[imapGUIDs allKeysForObject: [NSString stringWithFormat: @"folder%@", theIdToTranslate]] objectAtIndex: 0] substringFromIndex: 6] ;
+ if (!imapFolderGUIDS)
+ {
+ userFolder = [[context activeUser] homeFolderInContext: context];
+ accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO];
+ accountFolder = [accountsFolder lookupName: @"0" inContext: context acquire: NO];
+
+ // Get the GUID of the IMAP folder
+ imapFolderGUIDS = [accountFolder imapFolderGUIDs];
+ [imapFolderGUIDS retain];
+
+ }
+
+ return [[[imapFolderGUIDS allKeysForObject: [NSString stringWithFormat: @"folder%@", theIdToTranslate]] objectAtIndex: 0] substringFromIndex: 6] ;
}
return theIdToTranslate;
}
+
//
//
//
@@ -691,26 +707,25 @@ static BOOL debugOn = NO;
- (void) processFolderSync: (id ) theDocumentElement
inResponse: (WOResponse *) theResponse
{
- NSString *key, *cKey, *nkey, *name, *serverId, *parentId, *nameInCache, *personalFolderName, *syncKey, *folderType;
+ NSString *key, *cKey, *nkey, *name, *serverId, *parentId, *nameInCache, *personalFolderName, *syncKey, *folderType, *operation;
+ NSMutableDictionary *cachedGUIDs, *metadata;
+ NSMutableArray *folders, *processedFolders;
NSDictionary *folderMetadata, *imapGUIDs;
NSArray *allFoldersMetadata, *allKeys;
- NSMutableDictionary *cachedGUIDs, *metadata;
SOGoMailAccounts *accountsFolder;
SOGoMailAccount *accountFolder;
NSMutableString *s, *commands;
SOGoUserFolder *userFolder;
- NSMutableArray *folders, *processedFolders;
SoSecurityManager *sm;
SOGoCacheGCSObject *o;
id currentFolder;
NSData *d;
int status, command_count, i, type, fi, count;
-
BOOL first_sync;
sm = [SoSecurityManager sharedSecurityManager];
- metadata = [self _globalMetadataForDevice];
+ metadata = [self globalMetadataForDevice];
syncKey = [[(id)[theDocumentElement getElementsByTagName: @"SyncKey"] lastObject] textValue];
s = [NSMutableString string];
@@ -824,32 +839,38 @@ static BOOL debugOn = NO;
}
else
{
- if ([cKey rangeOfString: @"vevent" options: NSCaseInsensitiveSearch].location != NSNotFound ||
- [cKey rangeOfString: @"vtodo" options: NSCaseInsensitiveSearch].location != NSNotFound)
- folderType = @"Calendar";
- else
- folderType = @"Contacts";
+ if ([cKey rangeOfString: @"vevent" options: NSCaseInsensitiveSearch].location != NSNotFound ||
+ [cKey rangeOfString: @"vtodo" options: NSCaseInsensitiveSearch].location != NSNotFound)
+ folderType = @"Calendar";
+ else
+ folderType = @"Contacts";
- if ([ cKey rangeOfString: @"/"].location != NSNotFound)
- currentFolder = [[[[context activeUser] homeFolderInContext: context] lookupName: folderType inContext: context acquire: NO]
+ if ([ cKey rangeOfString: @"/"].location != NSNotFound)
+ currentFolder = [[[[context activeUser] homeFolderInContext: context] lookupName: folderType inContext: context acquire: NO]
lookupName: [cKey substringFromIndex: [cKey rangeOfString: @"/"].location+1] inContext: context acquire: NO];
- // remove the folder from device if it doesn't exists or it has not the proper permissions
- if (!currentFolder ||
- [sm validatePermission: SoPerm_DeleteObjects
- onObject: currentFolder
- inContext: context] ||
- [sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
- onObject: currentFolder
- inContext: context])
- {
- [commands appendFormat: @"%@", [cKey stringByEscapingURL] ];
- command_count++;
- [o destroy];
- }
- }
- }
- }
+ // We skip personal GCS folders - we always want to synchronize these
+ if ([currentFolder isKindOfClass: [SOGoGCSFolder class]] &&
+ [[currentFolder nameInContainer] isEqualToString: @"personal"])
+ continue;
+
+ // Remove the folder from device if it doesn't exist, we don't want to sync it, or it doesn't have the proper permissions
+ if (!currentFolder ||
+ ![currentFolder synchronize] ||
+ [sm validatePermission: SoPerm_DeleteObjects
+ onObject: currentFolder
+ inContext: context] ||
+ [sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
+ onObject: currentFolder
+ inContext: context])
+ {
+ [commands appendFormat: @"%@", [cKey stringByEscapingURL] ];
+ command_count++;
+ [o destroy];
+ }
+ }
+ }
+ }
// Handle addition and changes
for (i = 0; i < [allFoldersMetadata count]; i++)
@@ -937,7 +958,9 @@ static BOOL debugOn = NO;
[[o properties] removeObjectForKey: @"DateCache"];
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
+ [[o properties] removeObjectForKey: @"SupportedElements"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
+ [[o properties] removeObjectForKey: @"InitialLoadSequence"];
[o save];
command_count++;
@@ -946,17 +969,29 @@ static BOOL debugOn = NO;
personalFolderName = [[[context activeUser] personalCalendarFolderInContext: context] nameInContainer];
folders = [[[[[context activeUser] homeFolderInContext: context] lookupName: @"Calendar" inContext: context acquire: NO] subFolders] mutableCopy];
+ [folders autorelease];
+
[folders addObjectsFromArray: [[[[context activeUser] homeFolderInContext: context] lookupName: @"Contacts" inContext: context acquire: NO] subFolders]];
- // Inside this loop we remove all the folder without write/delete permissions
+ // We remove all the folders that aren't GCS-ones, that we don't want to synchronize and
+ // the ones without write/delete permissions.
count = [folders count]-1;
for (; count >= 0; count--)
{
- if ([sm validatePermission: SoPerm_DeleteObjects
- onObject: [folders objectAtIndex: count]
+ currentFolder = [folders objectAtIndex: count];
+
+ // We skip personal GCS folders - we always want to synchronize these
+ if ([currentFolder isKindOfClass: [SOGoGCSFolder class]] &&
+ [[currentFolder nameInContainer] isEqualToString: @"personal"])
+ continue;
+
+ if (![currentFolder isKindOfClass: [SOGoGCSFolder class]] ||
+ ![currentFolder synchronize] ||
+ [sm validatePermission: SoPerm_DeleteObjects
+ onObject: currentFolder
inContext: context] ||
[sm validatePermission: SoPerm_AddDocumentsImagesAndFiles
- onObject: [folders objectAtIndex: count]
+ onObject: currentFolder
inContext: context])
{
[folders removeObjectAtIndex: count];
@@ -964,7 +999,6 @@ static BOOL debugOn = NO;
}
count = [folders count]-1;
- NSString *operation;
for (fi = 0; fi <= count ; fi++)
{
@@ -1022,7 +1056,9 @@ static BOOL debugOn = NO;
[[o properties] removeObjectForKey: @"DateCache"];
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
+ [[o properties] removeObjectForKey: @"SupportedElements"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
+ [[o properties] removeObjectForKey: @"InitialLoadSequence"];
}
[o save];
@@ -1045,7 +1081,9 @@ static BOOL debugOn = NO;
[[o properties] removeObjectForKey: @"DateCache"];
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
+ [[o properties] removeObjectForKey: @"SupportedElements"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
+ [[o properties] removeObjectForKey: @"InitialLoadSequence"];
}
[o save];
@@ -1204,7 +1242,7 @@ static BOOL debugOn = NO;
filter = [NSCalendarDate dateFromFilterType: [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"FilterType"] lastObject] textValue]];
syncKey = [[(id)[[allCollections objectAtIndex: j] getElementsByTagName: @"SyncKey"] lastObject] textValue];
- allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter];
+ allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter initialLoad: NO];
count = [allMessages count];
@@ -1254,7 +1292,7 @@ static BOOL debugOn = NO;
- (void) processItemOperations: (id ) theDocumentElement
inResponse: (WOResponse *) theResponse
{
- NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *collectionId;
+ NSString *fileReference, *realCollectionId, *serverId, *bodyPreferenceType, *mimeSupport, *collectionId;
NSMutableString *s;
NSArray *fetchRequests;
id aFetch;
@@ -1367,6 +1405,8 @@ static BOOL debugOn = NO;
serverId = [[(id)[theDocumentElement getElementsByTagName: @"ServerId"] lastObject] textValue];
bodyPreferenceType = [[(id)[[(id)[theDocumentElement getElementsByTagName: @"BodyPreference"] lastObject] getElementsByTagName: @"Type"] lastObject] textValue];
[context setObject: bodyPreferenceType forKey: @"BodyPreferenceType"];
+ mimeSupport = [[(id)[theDocumentElement getElementsByTagName: @"MIMESupport"] lastObject] textValue];
+ [context setObject: mimeSupport forKey: @"MIMESupport"];
currentCollection = [self collectionFromId: realCollectionId type: folderType];
@@ -2024,13 +2064,13 @@ static BOOL debugOn = NO;
if ([foldersWithChanges count])
{
- [self logWithFormat: @"Change detected, we push the content."];
+ [self logWithFormat: @"Change detected using Ping, we let the EAS client know to send a Sync."];
status = 2;
break;
}
else
{
- [self logWithFormat: @"Sleeping %d seconds while detecting changes...", internalInterval];
+ [self logWithFormat: @"Sleeping %d seconds while detecting changes in Ping...", internalInterval];
sleep(internalInterval);
}
}
@@ -2094,6 +2134,123 @@ static BOOL debugOn = NO;
[theResponse setContent: d];
}
+//
+//
+//
+#ifdef HAVE_OPENSSL
+- (unsigned int) validateCert: (NSString *) theCert
+{
+ NSData *d;
+
+ const unsigned char *data;
+ X509_STORE_CTX *ctx;
+ X509_LOOKUP *lookup;
+ X509_STORE *store;
+ X509 *cert;
+
+ BOOL success;
+ size_t len;
+ int rc;
+
+ success = NO;
+
+ d = [theCert dataByDecodingBase64];
+ data = (unsigned char *)[d bytes];
+ len = [d length];
+
+ cert = d2i_X509(NULL, &data, len);
+ if (!cert)
+ {
+ [self logWithFormat: @"EAS - validateCert failed for device %@: d2i_X509 failed", [context objectForKey: @"DeviceId"]];
+ return 17;
+ }
+
+ store = X509_STORE_new();
+ OpenSSL_add_all_algorithms();
+
+ if (store)
+ {
+ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+ if (lookup)
+ {
+ X509_LOOKUP_load_file(lookup, NULL, X509_FILETYPE_DEFAULT);
+ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
+ if (lookup)
+ {
+ X509_LOOKUP_add_dir(lookup, NULL, X509_FILETYPE_DEFAULT);
+ ERR_clear_error();
+ success = YES;
+ }
+ }
+ }
+
+ if (!success)
+ {
+ if (store)
+ {
+ X509_STORE_free(store);
+ store = NULL;
+ }
+ }
+
+ ctx = X509_STORE_CTX_new();
+ if (!ctx)
+ {
+ [self logWithFormat: @"EAS - validateCert failed for device %@: X509_STORE_CTX_new failed", [context objectForKey: @"DeviceId"]];
+ return 17;
+ }
+
+ if (X509_STORE_CTX_init(ctx, store, cert, NULL) != 1)
+ {
+ [self logWithFormat: @"EAS - validateCert failed for device %@: X509_STORE_CTX_init failed", [context objectForKey: @"DeviceId"]];
+ X509_STORE_CTX_free(ctx);
+ return 17;
+ }
+
+ rc = X509_verify_cert(ctx);
+ X509_STORE_CTX_free(ctx);
+ X509_free(cert);
+
+ if (rc)
+ {
+ return 1;
+ }
+ else
+ {
+ [self logWithFormat: @"EAS - validateCert failed for device %@: err=%d", [context objectForKey: @"DeviceId"], X509_STORE_CTX_get_error(ctx)];
+ return 17;
+ }
+}
+#else
+- (unsigned int) validateCert: (NSString *) theCert
+{
+ return 17;
+}
+#endif
+
+- (void) processValidateCert: (id ) theDocumentElement
+ inResponse: (WOResponse *) theResponse
+{
+ NSMutableString *s;
+ NSString *cert;
+ NSData *d;
+
+ cert = [[(id)[theDocumentElement getElementsByTagName: @"Certificate"] lastObject] textValue];
+
+ s = [NSMutableString string];
+ [s appendString: @""];
+ [s appendString: @""];
+ [s appendString: @""];
+ [s appendString: @"1"];
+ [s appendFormat: @"%d", [self validateCert: cert]];
+ [s appendString: @""];
+
+ d = [[s dataUsingEncoding: NSUTF8StringEncoding] xml2wbxml];
+
+ [theResponse setContent: d];
+}
+
+
//
//
//
@@ -2410,39 +2567,100 @@ static BOOL debugOn = NO;
NGMimeMessageParser *parser;
NGMimeMessage *message;
NSException *error;
- NSData *data;
- NGMutableHashMap *map;
- NGMimeMessage *messageToSend;
- NGMimeMessageGenerator *generator;
+ NSMutableData *data;
+ NSData *new_from_header;
NSDictionary *identity;
NSString *fullName, *email;
+
+ const char *bytes;
+ int i, e, len;
+ BOOL found_header;
// We get the mail's data
- data = [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding];
+ data = [NSMutableData dataWithData: [[[[(id)[theDocumentElement getElementsByTagName: @"MIME"] lastObject] textValue] stringByDecodingBase64] dataUsingEncoding: NSUTF8StringEncoding]];
// We extract the recipients
parser = [[NGMimeMessageParser alloc] init];
message = [parser parsePartFromData: data];
RELEASE(parser);
- map = [NGHashMap hashMapWithDictionary: [message headers]];
-
identity = [[context activeUser] primaryIdentity];
fullName = [identity objectForKey: @"fullName"];
email = [identity objectForKey: @"email"];
+
if ([fullName length])
- [map setObject: [NSString stringWithFormat: @"%@ <%@>", fullName, email] forKey: @"from"];
+ new_from_header = [[NSString stringWithFormat: @"From: %@ <%@>\r\n", [fullName asQPSubjectString: @"utf-8"], email] dataUsingEncoding:NSUTF8StringEncoding];
else
- [map setObject: email forKey: @"from"];
+ new_from_header = [[NSString stringWithFormat: @"From: %@\r\n", email] dataUsingEncoding:NSUTF8StringEncoding];
- messageToSend = [[[NGMimeMessage alloc] initWithHeader: map] autorelease];
+ bytes = [data bytes];
+ len = [data length];
+ i = 0;
+ found_header = NO;
- [messageToSend setBody: [message body]];
+ // Search for the from-header
+ while (i < len)
+ {
+ if (i == 0 &&
+ (*bytes == 'f' || *bytes == 'F') &&
+ (*(bytes+1) == 'r' || *(bytes+1) == 'R') &&
+ (*(bytes+2) == 'o' || *(bytes+2) == 'O') &&
+ (*(bytes+3) == 'm' || *(bytes+3) == 'M') &&
+ (*(bytes+4) == ':'))
+ {
+ found_header = YES;
+ break;
+ }
+
+ if (((*bytes == '\r') && (*(bytes+1) == '\n')) &&
+ (*(bytes+2) == 'f' || *(bytes+2) == 'F') &&
+ (*(bytes+3) == 'r' || *(bytes+3) == 'R') &&
+ (*(bytes+4) == 'o' || *(bytes+4) == 'O') &&
+ (*(bytes+5) == 'm' || *(bytes+5) == 'M') &&
+ (*(bytes+6) == ':'))
+ {
+ found_header = YES;
+ i = i + 2; // \r\n
+ bytes = bytes + 2;
+ break;
+ }
+
+ bytes++;
+ i++;
+ }
+
+ // We search for the first \r\n AFTER the From: header to get the length of the string to replace.
+ e = i;
+ while (e < len)
+ {
+ if ((*bytes == '\r') && (*(bytes+1) == '\n'))
+ {
+ e = e + 2;
+ break;
+ }
+
+ bytes++;
+ e++;
+ }
+
+ // Update/Add the From header in the MIMEBody of the SendMail request.
+ // Any other way to modify the mail body would break s/mime emails.
+ if (found_header)
+ {
+ // Change the From header
+ [data replaceBytesInRange: NSMakeRange(i, (NSUInteger)(e-i))
+ withBytes: [new_from_header bytes]
+ length: [new_from_header length]];
+ }
+ else
+ {
+ // Add a From header
+ [data replaceBytesInRange: NSMakeRange(0, 0)
+ withBytes: [new_from_header bytes]
+ length: [new_from_header length]];
+ }
- generator = [[[NGMimeMessageGenerator alloc] init] autorelease];
- data = [generator generateMimeFromPart: messageToSend];
-
error = [self _sendMail: data
recipients: [message allRecipients]
saveInSentItems: ([(id)[theDocumentElement getElementsByTagName: @"SaveInSentItems"] count] ? YES : NO)];
@@ -2619,6 +2837,11 @@ static BOOL debugOn = NO;
[map setObject: [mailObject messageId] forKey: @"in-reply-to"];
references = [[[[[mailObject mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] mutableCopy] autorelease];
+
+ // If there is no References: header, initialize it with In-Reply-To.
+ if ([mailObject inReplyTo] && ![references count])
+ references = [NSMutableArray arrayWithObject: [mailObject inReplyTo]];
+
if ([references count] > 0)
{
// If there are more than ten identifiers listed, we eliminate the second one.
@@ -2934,9 +3157,25 @@ static BOOL debugOn = NO;
options: NSCaseInsensitiveSearch].location == NSNotFound)
{
NSString *value;
-
- value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z" timeZone: [NSTimeZone timeZoneWithName: @"GMT"] locale: nil];
- s = [NSString stringWithFormat: @"Date: %@\n%@", value, [theRequest contentAsString]];
+#if GNUSTEP_BASE_MINOR_VERSION < 21
+ value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z"
+ timeZone: [NSTimeZone timeZoneWithName: @"GMT"]
+ locale: nil];
+#else
+ value = [[NSDate date] descriptionWithCalendarFormat: @"%a, %d %b %Y %H:%M:%S %z"
+ timeZone: [NSTimeZone timeZoneWithName: @"GMT"]
+ locale: [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSArray arrayWithObjects: @"Jan", @"Feb", @"Mar", @"Apr",
+ @"May", @"Jun", @"Jul", @"Aug",
+ @"Sep", @"Oct", @"Nov", @"Dec", nil],
+ @"NSShortMonthNameArray",
+ [NSArray arrayWithObjects: @"Sun", @"Mon", @"Tue", @"Wed", @"Thu",
+ @"Fri", @"Sat", nil],
+ @"NSShortWeekDayNameArray",
+ nil]];
+
+#endif
+ s = [NSString stringWithFormat: @"Date: %@\r\n%@", value, [theRequest contentAsString]];
}
else
{
@@ -3026,8 +3265,7 @@ static BOOL debugOn = NO;
return nil;
urlString = [[user domainDefaults] folderInfoURL];
- parts = [[urlString componentsSeparatedByString: @"/"]
- mutableCopy];
+ parts = [[urlString componentsSeparatedByString: @"/"] mutableCopy];
[parts autorelease];
if ([parts count] == 5)
{
diff --git a/ActiveSync/SOGoMailObject+ActiveSync.m b/ActiveSync/SOGoMailObject+ActiveSync.m
index 6b2e1b03a..434e6b9c9 100644
--- a/ActiveSync/SOGoMailObject+ActiveSync.m
+++ b/ActiveSync/SOGoMailObject+ActiveSync.m
@@ -472,18 +472,46 @@ struct GlobalObjectId {
return d;
}
+//
+//
+//
+- (BOOL) _sanitinizeNeeded: (NSArray *) theParts
+{
+ NSString *encoding, *charset;
+ int i;
+
+ for (i = 0; i < [theParts count]; i++)
+ {
+ encoding = [[theParts objectAtIndex: i ] objectForKey: @"encoding"];
+ charset = [[[theParts objectAtIndex: i ] objectForKey: @"parameterList"] objectForKey: @"charset"];
+ if (encoding && [encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame &&
+ charset && ![charset caseInsensitiveCompare: @"utf-8"] == NSOrderedSame
+ && ![charset caseInsensitiveCompare: @"us-ascii"] == NSOrderedSame)
+ {
+ return YES;
+ }
+
+ if ([self _sanitinizeNeeded: [[theParts objectAtIndex: i ] objectForKey: @"parts"]])
+ return YES;
+ }
+
+ return NO;
+}
+
//
//
//
- (NSData *) _preferredBodyDataUsingType: (int) theType
+ mimeSupport: (int) theMimeSupport
nativeType: (int *) theNativeType
{
NSString *type, *subtype, *encoding;
NSData *d;
+ BOOL isSMIME, sanitinizeNeeded;
type = [[[self bodyStructure] valueForKey: @"type"] lowercaseString];
subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString];
-
+ isSMIME = NO;
d = nil;
// We determine the native type
@@ -494,8 +522,14 @@ struct GlobalObjectId {
else if ([type isEqualToString: @"multipart"])
*theNativeType = 4;
+ if (([subtype isEqualToString: @"signed"] || [subtype isEqualToString: @"pkcs7-mime"] ) && theMimeSupport > 0)
+ {
+ *theNativeType = 4;
+ isSMIME = YES;
+ }
+
// We get the right part based on the preference
- if (theType == 1 || theType == 2)
+ if ((theType == 1 || theType == 2) && !isSMIME)
{
if ([type isEqualToString: @"text"] && ![subtype isEqualToString: @"calendar"])
{
@@ -536,12 +570,12 @@ struct GlobalObjectId {
d = [self _preferredBodyDataInMultipartUsingType: theType nativeTypeFound: theNativeType];
}
}
- else if (theType == 4)
+ else if (theType == 4 || isSMIME)
{
- // We sanitize the content *ONLY* for Outlook clients and if the content-transfer-encoding is 8bit. Outlook has strange issues
- // with quoted-printable/base64 encoded text parts. It just doesn't decode them.
- encoding = [[self lookupInfoForBodyPart: @""] objectForKey: @"encoding"];
- if ([[context objectForKey: @"DeviceType"] isEqualToString: @"WindowsOutlook15"] || ([encoding caseInsensitiveCompare: @"8bit"] == NSOrderedSame))
+ // We sanitize the content if the content-transfer-encoding is 8bit and charset is not utf-8 or us-ascii.
+ sanitinizeNeeded = [self _sanitinizeNeeded: [NSArray arrayWithObject: [self bodyStructure]]];
+
+ if (sanitinizeNeeded && !isSMIME)
d = [self _sanitizedMIMEMessage];
else
d = [self content];
@@ -656,16 +690,18 @@ struct GlobalObjectId {
{
NSData *d, *globalObjId;
NSArray *attachmentKeys;
- NSMutableString *s;
-
- uint32_t v;
- NSString *p;
-
- id value;
-
iCalCalendar *calendar;
+ NSString *p, *subtype;
+ NSMutableString *s;
+ id value;
- int preferredBodyType, nativeBodyType;
+ int preferredBodyType, mimeSupport, nativeBodyType;
+ uint32_t v;
+
+ subtype = [[[self bodyStructure] valueForKey: @"subtype"] lowercaseString];
+
+ preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
+ mimeSupport = [[context objectForKey: @"MIMESupport"] intValue];
s = [NSMutableString string];
@@ -862,7 +898,12 @@ struct GlobalObjectId {
else
{
// MesssageClass and ContentClass
- [s appendFormat: @"%@", @"IPM.Note"];
+ if ([subtype isEqualToString: @"signed"])
+ [s appendFormat: @"%@", @"IPM.Note.SMIME.MultipartSigned"];
+ else if ([subtype isEqualToString: @"pkcs7-mime"])
+ [s appendFormat: @"%@", @"IPM.Note.SMIME"];
+ else
+ [s appendFormat: @"%@", @"IPM.Note"];
[s appendFormat: @"%@", @"urn:content-classes:message"];
}
@@ -876,10 +917,8 @@ struct GlobalObjectId {
[s appendFormat: @"%@", @"65001"];
// Body - namespace 17
- preferredBodyType = [[context objectForKey: @"BodyPreferenceType"] intValue];
-
nativeBodyType = 1;
- d = [self _preferredBodyDataUsingType: preferredBodyType nativeType: &nativeBodyType];
+ d = [self _preferredBodyDataUsingType: preferredBodyType mimeSupport: mimeSupport nativeType: &nativeBodyType];
if (calendar && !d)
{
@@ -981,9 +1020,12 @@ struct GlobalObjectId {
{
[s appendString: @""];
- // Set the correct type if client requested text/html but we got text/plain
+ // Set the correct type if client requested text/html but we got text/plain.
+ // For s/mime mails type is always 4 if mimeSupport is 1 or 2.
if (preferredBodyType == 2 && nativeBodyType == 1)
[s appendString: @"1"];
+ else if (([subtype isEqualToString: @"signed"] || [subtype isEqualToString: @"pkcs7-mime"] ) && mimeSupport > 0)
+ [s appendString: @"4"];
else
[s appendFormat: @"%d", preferredBodyType];
@@ -1001,7 +1043,8 @@ struct GlobalObjectId {
// Attachments -namespace 16
attachmentKeys = [self fetchFileAttachmentKeys];
- if ([attachmentKeys count])
+
+ if ([attachmentKeys count] && !([subtype isEqualToString: @"signed"]))
{
int i;
@@ -1071,9 +1114,15 @@ struct GlobalObjectId {
if ([[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.0"] ||
[[[context request] headerForKey: @"MS-ASProtocolVersion"] isEqualToString: @"14.1"])
{
+ id value;
NSString *reference;
- reference = [[[[self mailHeaders] objectForKey: @"references"] componentsSeparatedByString: @" "] objectAtIndex: 0];
+ value = [[self mailHeaders] objectForKey: @"references"];
+
+ if ([value isKindOfClass: [NSArray class]])
+ reference = [[[value objectAtIndex: 0] componentsSeparatedByString: @" "] objectAtIndex: 0];
+ else
+ reference = [[value componentsSeparatedByString: @" "] objectAtIndex: 0];
if ([reference length] > 0)
[s appendFormat: @"%@", [[reference dataUsingEncoding: NSUTF8StringEncoding] activeSyncRepresentationInContext: context]];
diff --git a/ActiveSync/SOGoSyncCacheObject.h b/ActiveSync/SOGoSyncCacheObject.h
index c6ba61068..a86d1067b 100644
--- a/ActiveSync/SOGoSyncCacheObject.h
+++ b/ActiveSync/SOGoSyncCacheObject.h
@@ -37,6 +37,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@interface SOGoSyncCacheObject : NSObject
{
+ @public
id _uid;
id _sequence;
}
diff --git a/ActiveSync/SOGoSyncCacheObject.m b/ActiveSync/SOGoSyncCacheObject.m
index 63c545b34..79761a343 100644
--- a/ActiveSync/SOGoSyncCacheObject.m
+++ b/ActiveSync/SOGoSyncCacheObject.m
@@ -32,9 +32,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import
#import
+#import
+
+static Class NSNullK;
@implementation SOGoSyncCacheObject
++ (void) initialize
+{
+ NSNullK = [NSNull class];
+}
+
- (id) init
{
if ((self = [super init]))
@@ -46,14 +54,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
return self;
}
-+ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence;
++ (id) syncCacheObjectWithUID: (id) theUID sequence: (id) theSequence
{
id o;
o = [[self alloc] init];
-
- [o setUID: theUID];
- [o setSequence: theSequence];
+
+ [o setUID: [NSNumber numberWithInt: [theUID intValue]]];
+ [o setSequence: ([theSequence isKindOfClass: NSNullK] ? theSequence : [NSNumber numberWithInt: [theSequence intValue]])];
return [o autorelease];
}
@@ -67,7 +75,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (id) uid
{
- return _uid;
+ return [_uid description];
}
- (void) setUID: (id) theUID
@@ -77,7 +85,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (id) sequence
{
- return _sequence;
+ return ([_sequence isKindOfClass: NSNullK] ? _sequence : [_sequence description]);
}
- (void) setSequence: (id) theSequence
@@ -88,7 +96,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (NSComparisonResult) compareUID: (SOGoSyncCacheObject *) theSyncCacheObject
{
- return [[self uid] compare: [theSyncCacheObject uid]];
+ return [self->_uid compare: theSyncCacheObject->_uid];
}
//
@@ -97,21 +105,21 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
- (NSComparisonResult) compareSequence: (SOGoSyncCacheObject *) theSyncCacheObject
{
- if ([[self sequence] isEqual: [NSNull null]] &&
- [[theSyncCacheObject sequence] isEqual: [NSNull null]])
+ if ([self->_sequence isEqual: [NSNull null]] &&
+ [theSyncCacheObject->_sequence isEqual: [NSNull null]])
return [self compareUID: theSyncCacheObject];
- if (![[self sequence] isEqual: [NSNull null]] && [[theSyncCacheObject sequence] isEqual: [NSNull null]])
+ if (![self->_sequence isEqual: [NSNull null]] && [theSyncCacheObject->_sequence isEqual: [NSNull null]])
return NSOrderedDescending;
- if ([[self sequence] isEqual: [NSNull null]] && ![[theSyncCacheObject sequence] isEqual: [NSNull null]])
+ if ([self->_sequence isEqual: [NSNull null]] && ![theSyncCacheObject->_sequence isEqual: [NSNull null]])
return NSOrderedAscending;
// Must check this here, to avoid comparing NSNull objects
- if ([[self sequence] compare: [theSyncCacheObject sequence]] == NSOrderedSame)
+ if ([self->_sequence compare: theSyncCacheObject->_sequence] == NSOrderedSame)
return [self compareUID: theSyncCacheObject];
- return [[self sequence] compare: [theSyncCacheObject sequence]];
+ return [self->_sequence compare: theSyncCacheObject->_sequence];
}
- (NSString *) description
@@ -120,3 +128,4 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
}
@end
+
diff --git a/ActiveSync/iCalAlarm+ActiveSync.m b/ActiveSync/iCalAlarm+ActiveSync.m
index 33e6dbb73..62a3ad146 100644
--- a/ActiveSync/iCalAlarm+ActiveSync.m
+++ b/ActiveSync/iCalAlarm+ActiveSync.m
@@ -58,7 +58,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// don't send negative reminder - not supported
if (delta > 0)
- [s appendFormat: @"%d", delta];
+ [s appendFormat: @"%d", (int)delta];
}
return s;
diff --git a/ActiveSync/iCalEvent+ActiveSync.m b/ActiveSync/iCalEvent+ActiveSync.m
index 2156b1eac..463d79faa 100644
--- a/ActiveSync/iCalEvent+ActiveSync.m
+++ b/ActiveSync/iCalEvent+ActiveSync.m
@@ -38,6 +38,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import
#import
+#import
+#import
#import
#import
#import
@@ -45,11 +47,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import
#import
#import
+#import
+#import
+#import
#import
#import
+#import
#import
+#import
#include "iCalAlarm+ActiveSync.h"
#include "iCalRecurrenceRule+ActiveSync.h"
@@ -98,10 +105,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else if ([self created])
[s appendFormat: @"%@", [[self created] activeSyncRepresentationWithoutSeparatorsInContext: context]];
+ // Timezone
+ tz = [(iCalDateTime *)[self firstChildWithTag: @"dtstart"] timeZone];
+
// StartTime -- http://msdn.microsoft.com/en-us/library/ee157132(v=exchg.80).aspx
if ([self startDate])
{
- if ([self isAllDay])
+ if ([self isAllDay] && !tz)
[s appendFormat: @"%@",
[[[self startDate] dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
@@ -114,7 +124,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// EndTime -- http://msdn.microsoft.com/en-us/library/ee157945(v=exchg.80).aspx
if ([self endDate])
{
- if ([self isAllDay])
+ if ([self isAllDay] && !tz)
[s appendFormat: @"%@",
[[[self endDate] dateByAddingYears: 0 months: 0 days: 0
hours: 0 minutes: 0
@@ -124,9 +134,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[s appendFormat: @"%@", [[self endDate] activeSyncRepresentationWithoutSeparatorsInContext: context]];
}
- // Timezone
- tz = [(iCalDateTime *)[self firstChildWithTag: @"dtstart"] timeZone];
-
if (!tz)
tz = [iCalTimeZone timeZoneForName: [userTimeZone name]];
@@ -226,9 +233,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//[s appendFormat: @"%d", v];
// UID -- http://msdn.microsoft.com/en-us/library/ee159919(v=exchg.80).aspx
- if ([[self uid] length])
+ if (![self recurrenceId] && [[self uid] length])
[s appendFormat: @"%@", [self uid]];
-
+
// Sensitivity
if ([[self accessClass] isEqualToString: @"PRIVATE"])
v = 2;
@@ -251,7 +258,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[s appendFormat: @""];
}
-
// Reminder -- http://msdn.microsoft.com/en-us/library/ee219691(v=exchg.80).aspx
// TODO: improve this to handle more alarm types
if ([self hasAlarms])
@@ -265,7 +271,74 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Recurrence rules
if ([self isRecurrent])
{
+ NSMutableArray *components, *exdates;
+ iCalEvent *current_component;
+ NSString *recurrence_id;
+
+ unsigned int count, max, i;
+
[s appendString: [[[self recurrenceRules] lastObject] activeSyncRepresentationInContext: context]];
+
+ components = [NSMutableArray arrayWithArray: [[self parent] events]];
+ max = [components count];
+
+ if (max > 1 || [self hasExceptionDates])
+ {
+ exdates = [NSMutableArray arrayWithArray: [self exceptionDates]];
+
+ [s appendString: @""];
+
+ for (count = 1; count < max; count++)
+ {
+ current_component = [components objectAtIndex: count] ;
+
+ if ([self isAllDay])
+ {
+ recurrence_id = [NSString stringWithFormat: @"%@",
+ [[[current_component recurrenceId] dateByAddingYears: 0 months: 0 days: 0
+ hours: 0 minutes: 0
+ seconds: -[userTimeZone secondsFromGMTForDate:
+ [current_component recurrenceId]]]
+ activeSyncRepresentationWithoutSeparatorsInContext: context]];
+ }
+ else
+ {
+ recurrence_id = [NSString stringWithFormat: @"%@", [[current_component recurrenceId]
+ activeSyncRepresentationWithoutSeparatorsInContext: context]];
+ }
+
+ [s appendString: @""];
+ [s appendFormat: @"%@", recurrence_id];
+ [s appendFormat: @"%@", [current_component activeSyncRepresentationInContext: context]];
+ [s appendString: @""];
+ }
+
+ for (i = 0; i < [exdates count]; i++)
+ {
+ [s appendString: @""];
+ [s appendString: @"1"];
+
+ if ([self isAllDay])
+ {
+ recurrence_id = [NSString stringWithFormat: @"%@",
+ [[[[exdates objectAtIndex: i] asCalendarDate] dateByAddingYears: 0 months: 0 days: 0
+ hours: 0 minutes: 0
+ seconds: -[userTimeZone secondsFromGMTForDate:
+ [[exdates objectAtIndex: i] asCalendarDate]]]
+ activeSyncRepresentationWithoutSeparatorsInContext: context]];
+ }
+ else
+ {
+ recurrence_id = [NSString stringWithFormat: @"%@", [[[exdates objectAtIndex: i] asCalendarDate]
+ activeSyncRepresentationWithoutSeparatorsInContext: context]];
+ }
+
+ [s appendFormat: @"%@", recurrence_id];
+ [s appendString: @""];
+ }
+
+ [s appendString: @""];
+ }
}
// Comment
@@ -285,13 +358,14 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
[s appendString: @""];
[s appendFormat: @"%d", 1];
- [s appendFormat: @"%d", [o length]];
+ [s appendFormat: @"%d", (int)[o length]];
[s appendFormat: @"%@", o];
[s appendString: @""];
}
}
- [s appendFormat: @"%d", 1];
+ if (![self recurrenceId])
+ [s appendFormat: @"%d", 1];
return s;
}
@@ -337,13 +411,19 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- (void) takeActiveSyncValues: (NSDictionary *) theValues
inContext: (WOContext *) context
{
- iCalDateTime *start, *end;
+ iCalDateTime *start, *end; //, *oldstart;
+ NSCalendarDate *oldstart;
NSTimeZone *userTimeZone;
iCalTimeZone *tz;
id o;
+ int deltasecs;
BOOL isAllDay;
-
+
+ NSMutableArray *occurences;
+
+ occurences = [NSMutableArray arrayWithArray: [[self parent] events]];
+
if ((o = [theValues objectForKey: @"UID"]))
[self setUid: o];
@@ -356,6 +436,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
isAllDay = YES;
}
+ else if ([occurences count] && !([theValues objectForKey: @"AllDayEvent"]))
+ {
+ // If the occurence has no AllDay tag use it from master.
+ isAllDay = [[occurences objectAtIndex: 0] isAllDay];
+ }
//
// 0- free, 1- tentative, 2- busy and 3- out of office
@@ -422,6 +507,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
o = [o calendarDate];
start = (iCalDateTime *) [self uniqueChildWithTag: @"dtstart"];
+ oldstart = [start dateTime];
[start setTimeZone: tz];
if (isAllDay)
@@ -433,6 +519,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
[start setDateTime: o];
}
+
+ // Calculate delta if start date has been changed.
+ if (oldstart)
+ deltasecs = [[start dateTime ] timeIntervalSinceDate: oldstart] * -1;
+ else
+ deltasecs = 0;
}
if ((o = [theValues objectForKey: @"EndTime"]))
@@ -491,6 +583,177 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
RELEASE(rule);
[rule takeActiveSyncValues: o inContext: context];
+
+ // Exceptions
+ if ((o = [theValues objectForKey: @"Exceptions"]) && [o isKindOfClass: [NSArray class]])
+ {
+ NSCalendarDate *recurrenceId, *adjustedRecurrenceId, *currentId;
+ NSMutableArray *exdates, *exceptionstouched;
+ iCalEvent *currentOccurence;
+ NSDictionary *exception;
+
+ unsigned int i, count, max;
+
+ [self removeAllExceptionDates];
+
+ exdates = [NSMutableArray array];
+ exceptionstouched = [NSMutableArray array];
+
+ for (i = 0; i < [o count]; i++)
+ {
+ exception = [o objectAtIndex: i];
+
+ if (![[exception objectForKey: @"Exception_Deleted"] intValue])
+ {
+ recurrenceId = [[exception objectForKey: @"Exception_StartTime"] asCalendarDate];
+
+ if (isAllDay)
+ recurrenceId = [recurrenceId dateByAddingYears: 0 months: 0 days: 0
+ hours: 0 minutes: 0
+ seconds: [userTimeZone secondsFromGMTForDate:recurrenceId]];
+
+ // When moving the calendar entry (i.e. changing the startDate) iOS and Android sometimes send a value for
+ // Exception_StartTime which is not what we expect.
+ // With this we make sure the recurrenceId is always correct.
+ //
+ // iOS problem - If the master is a no-allday-event but the exception is, an invalid Exception_StartTime is in the request.
+ // e.g. it sends 20150409T040000Z instead of 20150409T060000Z;
+ // This might be a special case since iPhone doesn't allow an allday-exception on a non-allday-event.
+ if (!([[start dateTime] compare: [[start dateTime] hour:[recurrenceId hourOfDay] minute:[recurrenceId minuteOfHour]]] == NSOrderedSame))
+ recurrenceId = [recurrenceId hour:[[start dateTime] hourOfDay] minute:[[start dateTime] minuteOfHour]];
+
+ // We need to store the recurrenceIds and exception dates adjusted by deltasecs.
+ // This ensures that the adjustment in SOGoCalendarComponent->updateComponent->_updateRecurrenceIDsWithEvent gives the correct dates.
+ adjustedRecurrenceId = [recurrenceId dateByAddingYears: 0 months: 0 days: 0
+ hours: 0 minutes: 0 seconds: deltasecs];
+
+ // search for an existing occurence and update it
+ max = [occurences count];
+ currentOccurence = nil;
+ count = 1;
+ while (count < max)
+ {
+ currentOccurence = [occurences objectAtIndex: count] ;
+ currentId = [currentOccurence recurrenceId];
+
+ if ([currentId compare: adjustedRecurrenceId] == NSOrderedSame)
+ {
+ [exceptionstouched addObject: adjustedRecurrenceId];
+ [currentOccurence takeActiveSyncValues: exception inContext: context];
+
+ break;
+ }
+
+ count++;
+ currentOccurence = nil;
+ }
+
+ // Create a new occurence if we found none to update.
+ if (!currentOccurence)
+ {
+ iCalDateTime *recid;
+
+ currentOccurence = [self mutableCopy];
+ [currentOccurence removeAllRecurrenceRules];
+ [currentOccurence removeAllExceptionRules];
+ [currentOccurence removeAllExceptionDates];
+
+ [[self parent] addToEvents: currentOccurence];
+ [currentOccurence takeActiveSyncValues: exception inContext: context];
+
+ recid = (iCalDateTime *)[currentOccurence uniqueChildWithTag: @"recurrence-id"];
+ if (isAllDay)
+ [recid setDate: adjustedRecurrenceId];
+ else
+ [recid setDateTime: adjustedRecurrenceId];
+
+ [exceptionstouched addObject: [recid dateTime]];
+ }
+ }
+ else if ([[exception objectForKey: @"Exception_Deleted"] intValue])
+ {
+ recurrenceId = [[exception objectForKey: @"Exception_StartTime"] asCalendarDate];
+
+ if (isAllDay)
+ recurrenceId = [recurrenceId dateByAddingYears: 0 months: 0
+ days: 0 hours: 0 minutes: 0
+ seconds: [userTimeZone secondsFromGMTForDate:recurrenceId]];
+
+ // We add only valid exception dates.
+ if ([self doesOccurOnDate: recurrenceId] &&
+ ([[start dateTime] compare: [[start dateTime] hour:[recurrenceId hourOfDay]
+ minute:[recurrenceId minuteOfHour]]] == NSOrderedSame))
+ {
+ // We need to store the recurrenceIds and exception dates adjusted by deltasecs.
+ // This ensures that the adjustment in SOGoCalendarComponent->updateComponent->_updateRecurrenceIDsWithEvent gives the correct dates.
+ [exdates addObject: [recurrenceId dateByAddingYears: 0 months: 0 days: 0 hours: 0 minutes: 0 seconds: deltasecs]];
+ }
+ }
+ }
+
+ // Update exception dates in master event.
+ max = [exdates count];
+ if (max > 0)
+ {
+ for (count = 0; count < max; count++)
+ {
+ if (([exceptionstouched indexOfObject: [exdates objectAtIndex: count]] == NSNotFound))
+ [self addToExceptionDates: [exdates objectAtIndex: count]];
+ }
+ }
+
+ // Remove all exceptions included in the request.
+ max = [occurences count];
+ count = 1; // skip the master event
+ while (count < max)
+ {
+ currentOccurence = [occurences objectAtIndex: count] ;
+ currentId = [currentOccurence recurrenceId];
+
+ // Delete all occurences which are not touched (modified/added).
+ if (([exceptionstouched indexOfObject: currentId] == NSNotFound))
+ [[[self parent] children] removeObject: currentOccurence];
+
+ count++;
+ }
+ }
+ else if ([self isRecurrent])
+ {
+ // We have no exceptions in request but there is a recurrence rule.
+ // Remove all excpetions and exception dates.
+ iCalEvent *currentOccurence;
+ unsigned int count, max;
+
+ [self removeAllExceptionDates];
+ max = [occurences count];
+ count = 1; // skip the master event
+ while (count < max)
+ {
+ currentOccurence = [occurences objectAtIndex: count];
+ [[[self parent] children] removeObject: currentOccurence];
+ count++;
+ }
+ }
+ }
+ else if ([self isRecurrent])
+ {
+ // We have no recurrence rule in request.
+ // Remove all exceptions, exception dates and recurrence rules.
+ iCalEvent *currentOccurence;
+ unsigned int count, max;
+
+ [self removeAllRecurrenceRules];
+ [self removeAllExceptionRules];
+ [self removeAllExceptionDates];
+
+ max = [occurences count];
+ count = 1; // skip the master event
+ while (count < max)
+ {
+ currentOccurence = [occurences objectAtIndex: count];
+ [[[self parent] children] removeObject: currentOccurence];
+ count++;
+ }
}
// Organizer - we don't touch the value unless we're the organizer
@@ -498,7 +761,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
([self userIsOrganizer: [context activeUser]] || [[context activeUser] hasEmail: o]))
{
iCalPerson *person;
-
+
person = [iCalPerson elementWithTag: @"organizer"];
[person setEmail: o];
[person setCn: [theValues objectForKey: @"Organizer_Name"]];
diff --git a/ActiveSync/iCalRecurrenceRule+ActiveSync.m b/ActiveSync/iCalRecurrenceRule+ActiveSync.m
index a9060fe27..225bbd90a 100644
--- a/ActiveSync/iCalRecurrenceRule+ActiveSync.m
+++ b/ActiveSync/iCalRecurrenceRule+ActiveSync.m
@@ -140,7 +140,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Simple reccurrence rule of type "Monthly"
type = 2;
[s appendFormat: @"%d",
- [[[self parent] startDate] dayOfMonth]];
+ (int)[[[self parent] startDate] dayOfMonth]];
}
}
else if ([self frequency] == iCalRecurrenceFrequenceYearly)
diff --git a/ActiveSync/iCalToDo+ActiveSync.m b/ActiveSync/iCalToDo+ActiveSync.m
index e9417752c..94e0f9756 100644
--- a/ActiveSync/iCalToDo+ActiveSync.m
+++ b/ActiveSync/iCalToDo+ActiveSync.m
@@ -140,7 +140,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
[s appendString: @""];
[s appendFormat: @"%d", 1];
- [s appendFormat: @"%d", [o length]];
+ [s appendFormat: @"%d", (int)[o length]];
[s appendFormat: @"%@", o];
[s appendString: @""];
}
diff --git a/ChangeLog b/ChangeLog
index 845239066..9ff78fafc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,2958 @@
-commit 5c017c19fc34acfa3c49559d3f0ffaf15b47c371
+commit 233b9c0613e630273808d15c324b99a6ef5039b4
+Author: Ludovic Marcotte
+Date: Mon Aug 31 11:08:41 2015 -0400
+
+ (js/css) updates for the new packages
+
+M UI/WebServerResources/css/styles.css
+M UI/WebServerResources/css/styles.css.map
+M UI/WebServerResources/js/Mailer.services.js
+M UI/WebServerResources/js/Mailer.services.js.map
+M UI/WebServerResources/js/Scheduler.services.js
+M UI/WebServerResources/js/Scheduler.services.js.map
+M UI/WebServerResources/js/vendor/angular-animate.js
+M UI/WebServerResources/js/vendor/angular-animate.min.js
+M UI/WebServerResources/js/vendor/angular-animate.min.js.map
+M UI/WebServerResources/js/vendor/angular-aria.js
+M UI/WebServerResources/js/vendor/angular-aria.min.js
+M UI/WebServerResources/js/vendor/angular-file-upload.min.js
+M UI/WebServerResources/js/vendor/angular-material.js
+M UI/WebServerResources/js/vendor/angular-material.min.js
+M UI/WebServerResources/js/vendor/angular-sanitize.js
+M UI/WebServerResources/js/vendor/angular-sanitize.min.js
+M UI/WebServerResources/js/vendor/angular.js
+M UI/WebServerResources/js/vendor/angular.min.js
+M UI/WebServerResources/js/vendor/angular.min.js.map
+
+commit 89206f37054229a48ee6e33bad5177593c6ceda1
+Author: Francis Lachapelle
+Date: Mon Aug 31 09:51:08 2015 -0400
+
+ (css) Fix for latest md changes
+
+M UI/WebServerResources/scss/components/progressCircular/progress-circular.scss
+
+commit a87daf282c311150a1b32db3696b44a80b9c4b16
+Author: Ludovic Marcotte
+Date: Sat Aug 29 16:30:07 2015 -0400
+
+ (fix) workaround for Firefox with menus which include md-checkbox
+
+ See https://github.com/angular/material/issues/4212 for details
+ on the Angular Material bug.
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/Templates/MailerUI/UIxMailEditor.wox
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+
+commit fb4889a15e03bdbadbc30f579ba8f26342a1cbfd
+Author: Ludovic Marcotte
+Date: Fri Aug 28 20:11:21 2015 -0400
+
+ (fix) small code refactoring
+
+M UI/MailerUI/UIxMailListActions.m
+M UI/MailerUI/UIxMailMainFrame.m
+
+commit 3fdabf867180449cea525db052013d967cdc4286
+Author: Ludovic Marcotte
+Date: Fri Aug 28 15:50:46 2015 -0400
+
+ (fix) properly update flags when message was answered/forwarded
+
+M UI/WebServerResources/js/Mailer/Message.service.js
+
+commit 66c52f22dd86dc63e955e83ca92291902271c7c2
+Author: Ludovic Marcotte
+Date: Fri Aug 28 15:25:43 2015 -0400
+
+ (fix) added timepicker directive
+
+A UI/WebServerResources/js/Common/sgTimepicker.directive.js
+
+commit 8f7334f11d15fd56cbf7638c4eae9c3a9cc8c5cd
+Author: Francis Lachapelle
+Date: Fri Aug 28 15:15:40 2015 -0400
+
+ (js) Simplify Account and Message services
+
+M UI/WebServerResources/js/Mailer/Account.service.js
+M UI/WebServerResources/js/Mailer/Message.service.js
+
+commit 624833eca9c83f6743d309d6269fae4a26a412e6
+Author: Ludovic Marcotte
+Date: Fri Aug 28 14:09:20 2015 -0400
+
+ (fix) updates for new packages
+
+M UI/WebServerResources/css/styles.css
+M UI/WebServerResources/css/styles.css.map
+M UI/WebServerResources/js/Common.js
+M UI/WebServerResources/js/Common.js.map
+M UI/WebServerResources/js/Contacts.js
+M UI/WebServerResources/js/Contacts.js.map
+M UI/WebServerResources/js/Contacts.services.js
+M UI/WebServerResources/js/Contacts.services.js.map
+M UI/WebServerResources/js/Mailer.js
+M UI/WebServerResources/js/Mailer.js.map
+M UI/WebServerResources/js/Mailer.services.js
+M UI/WebServerResources/js/Mailer.services.js.map
+M UI/WebServerResources/js/Scheduler.services.js
+M UI/WebServerResources/js/Scheduler.services.js.map
+M UI/WebServerResources/js/vendor/angular-material.js
+M UI/WebServerResources/js/vendor/angular-material.min.js
+
+commit 431c53b84d822d1be7cc31473274ec369697f45a
+Author: Ludovic Marcotte
+Date: Fri Aug 28 13:59:41 2015 -0400
+
+ (feat) new time picker component + enabled it in the calendar module
+
+M UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox
+M UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
+M UI/WebServerResources/js/Scheduler/Component.service.js
+A UI/WebServerResources/scss/components/timepicker/timepicker-default-theme.css
+A UI/WebServerResources/scss/components/timepicker/timepicker.scss
+M UI/WebServerResources/scss/styles.scss
+
+commit c9d9f7b47a6101de8f11762a0620401576f5b4ed
+Author: Francis Lachapelle
+Date: Thu Aug 27 15:55:56 2015 -0400
+
+ (js) Add caching of cards
+
+M UI/WebServerResources/js/Contacts/AddressBook.service.js
+M UI/WebServerResources/js/Contacts/Card.service.js
+
+commit 1667f8879ddd325fe08c9292f587716cf5f619ac
+Author: Francis Lachapelle
+Date: Thu Aug 27 14:09:43 2015 -0400
+
+ (js) New progress indicator in AddressBook module
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/WebServerResources/js/Contacts/AddressBook.service.js
+
+commit 3611c4021dff18b4189eaa4f5a7b1e95e677dae7
+Author: Francis Lachapelle
+Date: Thu Aug 27 13:37:47 2015 -0400
+
+ Improve view detail (messages, cards)
+
+M UI/Contacts/English.lproj/Localizable.strings
+M UI/MailerUI/English.lproj/Localizable.strings
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/Templates/MailerUI/UIxMailFolderTemplate.wox
+M UI/WebServerResources/js/Contacts/CardController.js
+M UI/WebServerResources/scss/views/MailerUI.scss
+M UI/WebServerResources/scss/views/_view.scss
+
+commit bba0d6f44d7515343c8513b293608d09e1f1dc1f
+Author: Luc Charland
+Date: Thu Aug 27 11:27:23 2015 -0400
+
+ make the tests more robust when data is invalid in DB/Webdav
+
+M Tests/Integration/carddav.py
+M Tests/Integration/test-carddav.py
+
+commit bf390f17dd6c6e8c6165bd4f675f422d3f0c0fc9
+Author: Luc Charland
+Date: Wed Aug 26 15:07:11 2015 -0400
+
+ fixed when strings not initialized in prefs
+
+M Tests/Integration/preferences.py
+M Tests/Integration/test-prevent-invitations.py
+
+commit 834aba85a67dbab0a7a87b7caf4d4ff539db0917
+Author: Ludovic Marcotte
+Date: Thu Aug 27 11:47:51 2015 -0400
+
+ (fix) cleanups
+
+M UI/MailPartViewers/UIxMailPartTextViewer.h
+M UI/MailPartViewers/UIxMailPartTextViewer.m
+
+commit d878c69c15cc2f6ff2995558c5961538b799aecb
+Author: Ludovic Marcotte
+Date: Thu Aug 27 11:01:23 2015 -0400
+
+ (feat) can now create mails from address book module
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/Templates/ContactsUI/UIxContactViewTemplate.wox
+M UI/WebServerResources/Gruntfile.js
+M UI/WebServerResources/js/Contacts/AddressBookController.js
+M UI/WebServerResources/js/Contacts/Card.service.js
+M UI/WebServerResources/js/Contacts/Contacts.app.js
+M UI/WebServerResources/js/Mailer/Account.service.js
+
+commit d9d59c6b52382769d7793883da50782bfb10cdbc
+Author: Francis Lachapelle
+Date: Wed Aug 26 21:22:39 2015 -0400
+
+ (fix) Force a single mail composer dialog
+
+M UI/WebServerResources/js/Mailer/MailboxController.js
+M UI/WebServerResources/js/Mailer/MessageController.js
+
+commit 69000a0929ffe4dc640c904abd36f6ebb4322f4b
+Author: Francis Lachapelle
+Date: Wed Aug 26 17:28:03 2015 -0400
+
+ Review animations of view detail (messages, cards)
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/Templates/ContactsUI/UIxContactViewTemplate.wox
+M UI/Templates/MailerUI/UIxMailFolderTemplate.wox
+M UI/Templates/MailerUI/UIxMailViewTemplate.wox
+M UI/WebServerResources/js/Contacts/CardController.js
+M UI/WebServerResources/js/Mailer/MessageController.js
+M UI/WebServerResources/scss/views/_view.scss
+
+commit 876d9cfa213cbf26c72bbac1ba1cb1d509b386bb
+Author: Ludovic Marcotte
+Date: Wed Aug 26 11:56:47 2015 -0400
+
+ (fix) make messages loading very fast
+
+ Avoid calling angular.extend() loading when unwrapping the mailbox
+ and never call again Message.id directly, but rather use Message.$absolutePath()
+ which does initialization and caching
+
+M UI/WebServerResources/js/Mailer/Mailbox.service.js
+M UI/WebServerResources/js/Mailer/Message.service.js
+
+commit 117e6b6bc1484a223df8051ccd36cb6c5e3b3bef
+Author: Luc Charland
+Date: Wed Aug 26 11:26:38 2015 -0400
+
+ added the multiple phone test
+
+M Tests/Integration/test-carddav.py
+
+commit 01e2256483c72c75bc3f8f6d972a7b82401a0fb7
+Author: Francis Lachapelle
+Date: Wed Aug 26 09:48:47 2015 -0400
+
+ (css) Improve autocompletion menu
+
+M UI/WebServerResources/scss/components/autocomplete/autocomplete.scss
+M UI/WebServerResources/scss/components/chips/chips.scss
+
+commit 49e7cf0994d1df4331016188eba9e40e5ed02dab
+Author: Francis Lachapelle
+Date: Wed Aug 26 09:40:47 2015 -0400
+
+ (css) Adapt fullscreen dialogs to new md layout
+
+M UI/WebServerResources/scss/components/dialog/dialog.scss
+
+commit b3d6973fb15d0b7138b4f7ad8483b3f152ef1280
+Author: Ludovic Marcotte
+Date: Wed Aug 26 09:31:08 2015 -0400
+
+ (fix) small optimization
+
+M SoObjects/SOGo/NSArray+Utilities.h
+M SoObjects/SOGo/NSArray+Utilities.m
+M UI/MailerUI/UIxMailListActions.m
+
+commit aae522259f26a980d7fea18b9070ffd8d9d00a68
+Author: Ludovic Marcotte
+Date: Wed Aug 26 08:28:19 2015 -0400
+
+ (fix) expose the json accounts call
+
+M UI/MailerUI/product.plist
+
+commit 68ef8784a0b0bd16df296cc64c09bb0523c4a8a4
+Author: Ludovic Marcotte
+Date: Wed Aug 26 08:27:45 2015 -0400
+
+ (fix) added json call to get all mail accounts
+
+M UI/MailerUI/UIxMailMainFrame.m
+
+commit da821ea6c78372364af9d63798110fb742e3b1bc
+Author: Francis Lachapelle
+Date: Tue Aug 25 22:17:57 2015 -0400
+
+ (js) Improve recipient auto-completion
+
+M UI/Templates/MailerUI/UIxMailEditor.wox
+M UI/WebServerResources/js/Mailer/MessageEditorController.js
+M UI/WebServerResources/scss/components/chips/chips.scss
+
+commit 4dee99e0ba71e203c12d35bed6acc0ee3c62e4bd
+Author: Ludovic Marcotte
+Date: Tue Aug 25 20:47:12 2015 -0400
+
+ (fix) improved padding usage to better align icons
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+M UI/Templates/UIxTopnavToolbarTemplate.wox
+
+commit eea08d083daa8b73f034a0df49d12774fefe52b8
+Author: Francis Lachapelle
+Date: Tue Aug 25 14:53:43 2015 -0400
+
+ (html) Improve contacts list
+
+M UI/Contacts/English.lproj/Localizable.strings
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+
+commit 8055acfd4aba4268a4451106bc7f7e585d107648
+Author: Francis Lachapelle
+Date: Tue Aug 25 14:20:20 2015 -0400
+
+ (fix) Restore previous sort in Contacts module
+
+M UI/WebServerResources/js/Contacts/AddressBook.service.js
+
+commit 1fc947d8c818f7e89f7eda4703ce1af1515e1e06
+Author: Francis Lachapelle
+Date: Tue Aug 25 13:46:33 2015 -0400
+
+ (fix) Define CSS styles for ALL calendars
+
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+
+commit 7b3a238fb9a25857ef05cda3de2df1ae3a4a4f20
+Author: Francis Lachapelle
+Date: Tue Aug 25 12:20:46 2015 -0400
+
+ (js) New progress indicator in Mail module
+
+M UI/Templates/MailerUI/UIxMailFolderTemplate.wox
+M UI/WebServerResources/js/Mailer/Mailbox.service.js
+M UI/WebServerResources/scss/components/progressCircular/_extends.scss
+M UI/WebServerResources/scss/components/progressCircular/progress-circular.scss
+M UI/WebServerResources/scss/styles.scss
+
+commit 99d121fd3e9ed085062abf02ce336f3563a55688
+Author: Ludovic Marcotte
+Date: Tue Aug 25 11:46:14 2015 -0400
+
+ (fix) updates for new packages
+
+M UI/WebServerResources/css/styles.css
+M UI/WebServerResources/css/styles.css.map
+M UI/WebServerResources/js/Common.js
+M UI/WebServerResources/js/Common.js.map
+M UI/WebServerResources/js/Contacts.js
+M UI/WebServerResources/js/Contacts.js.map
+M UI/WebServerResources/js/Mailer.js
+M UI/WebServerResources/js/Mailer.js.map
+M UI/WebServerResources/js/Mailer.services.js
+M UI/WebServerResources/js/Mailer.services.js.map
+M UI/WebServerResources/js/Scheduler.services.js
+M UI/WebServerResources/js/Scheduler.services.js.map
+M UI/WebServerResources/js/vendor/angular-animate.js
+M UI/WebServerResources/js/vendor/angular-animate.min.js
+M UI/WebServerResources/js/vendor/angular-animate.min.js.map
+M UI/WebServerResources/js/vendor/angular-material.js
+M UI/WebServerResources/js/vendor/angular-material.min.js
+M UI/WebServerResources/js/vendor/angular-sanitize.js
+M UI/WebServerResources/js/vendor/angular-sanitize.min.js
+M UI/WebServerResources/js/vendor/angular-sanitize.min.js.map
+M UI/WebServerResources/js/vendor/angular.js
+M UI/WebServerResources/js/vendor/angular.min.js
+M UI/WebServerResources/js/vendor/angular.min.js.map
+
+commit eab3ee111a65f75e95e1ea46e7bf7135b3949d52
+Author: Ludovic Marcotte
+Date: Tue Aug 25 11:45:17 2015 -0400
+
+ (feat) now use md-on-demand for (hopefully) faster mailbox loading
+
+M UI/Templates/MailerUI/UIxMailFolderTemplate.wox
+M UI/WebServerResources/js/Mailer/Mailbox.service.js
+
+commit eeb6be1f0871130b9d12b90a7771135e3ea6c780
+Author: Francis Lachapelle
+Date: Tue Aug 25 10:24:34 2015 -0400
+
+ (css) Match new ng-material files names
+
+M UI/WebServerResources/scss/components/bottomSheet/_extends.scss
+A UI/WebServerResources/scss/components/bottomSheet/bottom-sheet.scss
+D UI/WebServerResources/scss/components/bottomSheet/bottomSheet.scss
+M UI/WebServerResources/scss/components/gridList/_extends.scss
+A UI/WebServerResources/scss/components/gridList/grid-list.scss
+D UI/WebServerResources/scss/components/gridList/gridList.scss
+A UI/WebServerResources/scss/components/progressCircular/progress-circular.scss
+D UI/WebServerResources/scss/components/progressCircular/progressCircular.scss
+M UI/WebServerResources/scss/components/progressLinear/_extends.scss
+A UI/WebServerResources/scss/components/progressLinear/progress-linear.scss
+D UI/WebServerResources/scss/components/progressLinear/progressLinear.scss
+M UI/WebServerResources/scss/components/virtualRepeat/_extends.scss
+A UI/WebServerResources/scss/components/virtualRepeat/virtual-repeat.scss
+D UI/WebServerResources/scss/components/virtualRepeat/virtualRepeat.scss
+M UI/WebServerResources/scss/styles.scss
+M UI/WebServerResources/scss/views/_view.scss
+
+commit 29108bbb62c6104d72819e173ad208cdf97df50f
+Author: Francis Lachapelle
+Date: Tue Aug 25 06:34:34 2015 -0400
+
+ (html) Localization
+
+M UI/Templates/ContactsUI/UIxContactViewTemplate.wox
+
+commit 9c6884bd7d2e09f163e369b717f8da4550a9071f
+Author: Ludovic Marcotte
+Date: Mon Aug 24 20:04:56 2015 -0400
+
+ (feat) add single export/save as actions
+
+M UI/Templates/ContactsUI/UIxContactViewTemplate.wox
+M UI/Templates/MailerUI/UIxMailViewTemplate.wox
+M UI/WebServerResources/js/Contacts/CardController.js
+M UI/WebServerResources/js/Mailer/MessageController.js
+
+commit 0aac4897fa521881186bf2eeda93c147a4307dd9
+Author: Francis Lachapelle
+Date: Mon Aug 24 17:28:01 2015 -0400
+
+ (fix) Set virtual item size in contacts list
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+
+commit 2a99d1fd8072284982a9ef92143f16af5e9d5487
+Author: Ludovic Marcotte
+Date: Mon Aug 24 16:17:44 2015 -0400
+
+ (fix) reset search filter when switching address books
+
+M UI/WebServerResources/js/Contacts/AddressBooksController.js
+
+commit ba148eec5bee94f07c162f16abf2964051d740e6
+Author: Francis Lachapelle
+Date: Mon Aug 24 14:54:25 2015 -0400
+
+ (js) Fix error in Component.prototype.$reset
+
+M UI/WebServerResources/js/Scheduler/Component.service.js
+
+commit 49c4884cc9ac8d11be381163df594f65f21b2b6e
+Author: Francis Lachapelle
+Date: Mon Aug 24 14:40:20 2015 -0400
+
+ (html) Improve grids layout in recurrence editor
+
+M UI/Templates/SchedulerUI/UIxRecurrenceEditor.wox
+
+commit e5d6ee93c3b4f17be4925dbb696038ac043b271e
+Author: Francis Lachapelle
+Date: Mon Aug 24 11:32:00 2015 -0400
+
+ Update Angular to version 1.4.x
+
+M UI/WebServerResources/bower.json
+
+commit 40eff05d4e61e645ddd253583c1287e6d90aa7ff
+Author: Ludovic Marcotte
+Date: Mon Aug 24 11:26:00 2015 -0400
+
+ (feat) added toolbar to account editing dialog
+
+M UI/Templates/PreferencesUI/UIxAccountEditor.wox
+
+commit be031fe2c99981caeed3d9904f906920af305431
+Author: Francis Lachapelle
+Date: Mon Aug 24 11:24:51 2015 -0400
+
+ (html) Restore animation when opening a card
+
+M UI/Templates/ContactsUI/UIxContactViewTemplate.wox
+
+commit 0fe141928944f420ca29b003c8d8f8de091f56ac
+Author: Francis Lachapelle
+Date: Mon Aug 24 11:23:34 2015 -0400
+
+ (html) Enlarge folder links dialog width
+
+M UI/Templates/ContactsUI/UIxContactFolderLinksTemplate.wox
+M UI/Templates/SchedulerUI/UIxCalendarFolderLinksTemplate.wox
+
+commit 9db633cada44eecea42362029310b4beb2507fe1
+Author: Francis Lachapelle
+Date: Mon Aug 24 11:20:35 2015 -0400
+
+ (html) Replace md-layout-fill by layout-fill
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+M UI/Templates/MainUI/SOGoRootPage.wox
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+
+commit 8a2d7d56e01908976ca7f30c736c6f754efe7702
+Author: Ludovic Marcotte
+Date: Mon Aug 24 11:20:23 2015 -0400
+
+ (feat) added toolbar to filter editor dialog
+
+M UI/Templates/PreferencesUI/UIxFilterEditor.wox
+
+commit 0c17e745f198d7ef64a1800d916e5a6e4f204e37
+Author: Francis Lachapelle
+Date: Mon Aug 24 11:11:23 2015 -0400
+
+ (html) Localization
+
+M UI/Templates/UIxTopnavToolbarTemplate.wox
+
+commit 18bc75e50b4fb7db57872e18e5073064164ab455
+Author: Ludovic Marcotte
+Date: Mon Aug 24 08:49:31 2015 -0400
+
+ (fix) adjust import path due to md changes
+
+M UI/WebServerResources/scss/core/layout.scss
+
+commit 68ad3c104581e3c92f7749cd235db95004ead61c
+Author: Ludovic Marcotte
+Date: Sat Aug 22 07:51:13 2015 -0400
+
+ (fix) localize 'me'
+
+M UI/WebServerResources/js/Mailer/Message.service.js
+
+commit 85c8aebbd4f8a54525451e0dea4d4215530625c7
+Author: Ludovic Marcotte
+Date: Fri Aug 21 20:56:21 2015 -0400
+
+ (feat) initial support for contact import in the address book module
+
+M UI/Contacts/UIxContactFolderActions.m
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/WebServerResources/js/Contacts/AddressBooksController.js
+M UI/WebServerResources/js/Contacts/Contacts.app.js
+
+commit 0a8834193f9fc509766fc4fd173b633db4746e6b
+Author: Ludovic Marcotte
+Date: Fri Aug 21 13:40:07 2015 -0400
+
+ (fix) updates for new packages
+
+M UI/WebServerResources/css/styles.css
+M UI/WebServerResources/css/styles.css.map
+M UI/WebServerResources/js/Contacts.js
+M UI/WebServerResources/js/Contacts.js.map
+M UI/WebServerResources/js/Contacts.services.js
+M UI/WebServerResources/js/Contacts.services.js.map
+M UI/WebServerResources/js/Mailer.services.js
+M UI/WebServerResources/js/Mailer.services.js.map
+M UI/WebServerResources/js/Preferences.js
+M UI/WebServerResources/js/Preferences.js.map
+M UI/WebServerResources/js/Scheduler.js
+M UI/WebServerResources/js/Scheduler.js.map
+M UI/WebServerResources/js/Scheduler.services.js
+M UI/WebServerResources/js/Scheduler.services.js.map
+M UI/WebServerResources/js/vendor/angular-animate.js
+M UI/WebServerResources/js/vendor/angular-animate.min.js
+M UI/WebServerResources/js/vendor/angular-animate.min.js.map
+M UI/WebServerResources/js/vendor/angular-material.js
+M UI/WebServerResources/js/vendor/angular-material.min.js
+M UI/WebServerResources/js/vendor/angular-sanitize.js
+M UI/WebServerResources/js/vendor/angular-sanitize.min.js
+M UI/WebServerResources/js/vendor/angular.js
+M UI/WebServerResources/js/vendor/angular.min.js
+M UI/WebServerResources/js/vendor/angular.min.js.map
+
+commit 91e4c2cefc10cd3c72c2b46229e18250128917ea
+Author: Ludovic Marcotte
+Date: Fri Aug 21 13:37:18 2015 -0400
+
+ (feat) improved the mailviewer with regards to sender + recipients
+
+M UI/Templates/MailerUI/UIxMailViewTemplate.wox
+M UI/WebServerResources/js/Mailer/Message.service.js
+
+commit b80d8dbb06ef98b4b65cc7cf39b4f54423dcf0ba
+Author: Ludovic Marcotte
+Date: Fri Aug 21 10:30:47 2015 -0400
+
+ (fix) Session fix when SOGoEnableDomainBasedUID is enabled but logins are domain-less
+
+M NEWS
+M SoObjects/SOGo/SOGoUserManager.m
+M UI/MainUI/SOGoRootPage.m
+
+commit 525d1ad80f7e784b32f99cab1e01117b476df1e6
+Author: Ludovic Marcotte
+Date: Thu Aug 20 22:38:01 2015 -0400
+
+ (fix) also return all calendars in findAll
+
+M UI/WebServerResources/js/Scheduler/Calendar.service.js
+
+commit 332dd0d96d5c58ff9cf1b96cd32f07009f0277da
+Author: Ludovic Marcotte
+Date: Thu Aug 20 22:37:27 2015 -0400
+
+ (fix) small optimization
+
+M UI/Contacts/UIxContactFoldersView.m
+
+commit a45000d15d239f089dd23ba8b91b25910c956ce8
+Author: Ludovic Marcotte
+Date: Thu Aug 20 19:42:24 2015 -0400
+
+ (fix) show delete option only where objectEraser is on
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+
+commit 3301bdb0550a0cfeaa6fa983ffaefc2b2ad11f07
+Author: Ludovic Marcotte
+Date: Thu Aug 20 19:37:30 2015 -0400
+
+ (fix) return the complete list of addressbooks
+
+ This fixes many bugs / side effects, especially with ACLs
+
+M UI/WebServerResources/js/Contacts/AddressBook.service.js
+
+commit 2f82b5cfd3430d7560a0d41d1076c86ab7037408
+Author: Francis Lachapelle
+Date: Thu Aug 20 16:39:34 2015 -0400
+
+ (js) Localization of toast message
+
+M UI/PreferencesUI/English.lproj/Localizable.strings
+M UI/WebServerResources/js/Preferences/PreferencesController.js
+
+commit e3b64e035ddf15a86488007cfedb90b9656a208e
+Author: Francis Lachapelle
+Date: Tue Aug 18 21:25:31 2015 -0400
+
+ (css) New md-fab-overlap-bottom class
+
+M UI/WebServerResources/scss/components/button/button.scss
+
+commit 1470258b5ce7bb35cf9761bacf69b1582cfec97f
+Author: Francis Lachapelle
+Date: Tue Aug 18 21:20:34 2015 -0400
+
+ (js) Simplify Account.prototype.$newMailbox
+
+M UI/WebServerResources/js/Mailer/Account.service.js
+
+commit 83fee27ca836fbe823c02a772eccc2751bdba967
+Author: Francis Lachapelle
+Date: Tue Aug 18 21:16:05 2015 -0400
+
+ (js) Simplify Mailbox.prototype.$emptyTrash
+
+M UI/WebServerResources/js/Mailer/Mailbox.service.js
+
+commit e5b0596bc9b14fc491332bb9c8fadf03dbbf73c7
+Author: Ludovic Marcotte
+Date: Thu Aug 20 15:47:41 2015 -0400
+
+ (fix) don't convert back the birthday date object on non-deep omits
+
+M UI/WebServerResources/js/Contacts/Card.service.js
+
+commit 26e8276b08c8cad37f7a6560e7c15a17e405bb4b
+Author: Ludovic Marcotte
+Date: Thu Aug 20 15:47:11 2015 -0400
+
+ (feat) improved ACLs handling for address book
+
+M UI/Contacts/UIxContactFoldersView.m
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/Templates/ContactsUI/UIxContactViewTemplate.wox
+
+commit 078ba95f8efd1a119b6d33b489b14e4e4fea3076
+Author: Ludovic Marcotte
+Date: Thu Aug 20 14:53:43 2015 -0400
+
+ (feat) show small toast when preferences are saved to give user's feedback
+
+M UI/WebServerResources/js/Preferences/PreferencesController.js
+
+commit 65525dd33bc541caf1eb8a7c9b6419ffa085a783
+Author: Ludovic Marcotte
+Date: Thu Aug 20 10:29:47 2015 -0400
+
+ (feat) added date picker in contact editor + now handle correctly birthday
+
+M UI/Contacts/UIxContactEditor.m
+M UI/Contacts/UIxContactView.m
+M UI/Templates/ContactsUI/UIxContactEditorTemplate.wox
+M UI/Templates/ContactsUI/UIxContactViewTemplate.wox
+M UI/WebServerResources/js/Contacts/Card.service.js
+M UI/WebServerResources/js/Contacts/CardController.js
+
+commit da81f7bc589ce3666d73c98b7c92efd2680518e0
+Author: Ludovic Marcotte
+Date: Wed Aug 19 20:45:07 2015 -0400
+
+ (fix) set background to transparent by default
+
+M UI/WebServerResources/scss/components/datepicker/datePicker.scss
+
+commit 8fcd594be615e45d00ff7d3fa044cac5a78ace9f
+Author: Ludovic Marcotte
+Date: Wed Aug 19 19:38:42 2015 -0400
+
+ (fix) removed unused code
+
+M UI/Contacts/UIxContactFoldersView.m
+
+commit 548f056b9cfbce214fab2a969f137fb680ec655f
+Author: Ludovic Marcotte
+Date: Wed Aug 19 11:23:03 2015 -0400
+
+ (fix) small formatting nit
+
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+
+commit dffdbf318c0a1605cee4a7d750a0ff54da5afbb2
+Author: Ludovic Marcotte
+Date: Wed Aug 19 11:22:17 2015 -0400
+
+ (feat) new 'viewable' property on events/tasks for full viewers
+
+M UI/Scheduler/UIxCalListingActions.m
+M UI/Templates/SchedulerUI/UIxTaskViewTemplate.wox
+M UI/WebServerResources/js/Scheduler/CalendarListController.js
+
+commit 161bcfac875b73d8a0301b67dc8446c106663058
+Author: Ludovic Marcotte
+Date: Wed Aug 19 10:39:10 2015 -0400
+
+ (fix) proper loading of transalations
+
+M SoObjects/SOGo/NSObject+Utilities.h
+M SoObjects/SOGo/NSObject+Utilities.m
+M UI/MailerUI/UIxMailFolderActions.m
+M UI/MailerUI/UIxMailListActions.m
+M UI/Scheduler/UIxCalListingActions.m
+
+commit c6f17cd45cff78a378da62c15c074c6bd895dc44
+Author: Ludovic Marcotte
+Date: Wed Aug 19 08:47:33 2015 -0400
+
+ (feat) make use of the sieve capabilities
+
+M UI/PreferencesUI/UIxPreferences.m
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+M UI/WebServerResources/js/Preferences/FiltersDialogController.js
+M UI/WebServerResources/js/Preferences/PreferencesController.js
+
+commit 38713a6c675f717ef58fcc94cfed3752731e0d1a
+Author: Ludovic Marcotte
+Date: Tue Aug 18 16:21:47 2015 -0400
+
+ (feat) handle multiple home/work phones, emails and urls
+
+M SoObjects/Contacts/NGVCard+SOGo.h
+M SoObjects/Contacts/NGVCard+SOGo.m
+M UI/Contacts/UIxContactEditor.m
+
+commit 8cef98eba1fefeacf1e0959d3007eba0dbffbd21
+Author: Ludovic Marcotte
+Date: Tue Aug 18 15:32:44 2015 -0400
+
+ (fix) add flex/flex-sm attributes
+
+M UI/Templates/PreferencesUI/UIxAccountEditor.wox
+M UI/Templates/PreferencesUI/UIxFilterEditor.wox
+
+commit 2144d3122af4acdb0d6484069e4022610d3236e2
+Author: Ludovic Marcotte
+Date: Tue Aug 18 15:25:51 2015 -0400
+
+ (feat) added support back for SOGoForwardConstraints
+
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+M UI/WebServerResources/js/Preferences/PreferencesController.js
+
+commit 9d272ae0632cb190b708f633a847a05fa7d342d8
+Author: Ludovic Marcotte
+Date: Tue Aug 18 14:57:29 2015 -0400
+
+ (feat) added the 'add default email addresses' feature in vacation module
+
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+M UI/WebServerResources/js/Preferences/PreferencesController.js
+
+commit fcd0140ffd7ac460932de91888f40e4d365fc09f
+Author: Ludovic Marcotte
+Date: Tue Aug 18 14:52:47 2015 -0400
+
+ Revert "(fix) by default, open links in new tab/window from html mails"
+
+ This reverts commit 869b3c4cd1625af5bd3f21b7443c5999c8ab01fe.
+
+M UI/Templates/MailPartViewers/UIxMailPartHTMLViewer.wox
+
+commit d783782af7d6becb3edb845ace7fe55da685c37a
+Author: Ludovic Marcotte
+Date: Tue Aug 18 11:31:42 2015 -0400
+
+ (fix) adding basic flags
+
+M UI/Templates/PreferencesUI/UIxFilterEditor.wox
+
+commit 869b3c4cd1625af5bd3f21b7443c5999c8ab01fe
+Author: Ludovic Marcotte
+Date: Tue Aug 18 11:13:27 2015 -0400
+
+ (fix) by default, open links in new tab/window from html mails
+
+M UI/Templates/MailPartViewers/UIxMailPartHTMLViewer.wox
+
+commit 2da29373913fcd7cb8c40532e6f9fd918ae092f3
+Author: Ludovic Marcotte
+Date: Tue Aug 18 11:01:59 2015 -0400
+
+ (fix) formatting improvements
+
+M UI/Templates/MailPartViewers/UIxMailPartHTMLViewer.wox
+
+commit 5034d6655a751d4b92856eef6cd3dd1eacfe1902
+Author: Ludovic Marcotte
+Date: Tue Aug 18 10:10:37 2015 -0400
+
+ (fix) correctly refresh the mailboxes list when empty'ing the trash that contains subfolders
+
+M UI/WebServerResources/js/Mailer/Mailbox.service.js
+
+commit d1446f7dc109270d86fdd24c4534454e7f25d88d
+Author: Ludovic Marcotte
+Date: Tue Aug 18 09:57:07 2015 -0400
+
+ (fix) broken top level mailbox creation
+
+M UI/WebServerResources/js/Mailer/Account.service.js
+
+commit c5a219add8d05a8a2e747363a510bcb228994dc2
+Author: Ludovic Marcotte
+Date: Tue Aug 18 09:27:33 2015 -0400
+
+ (fix) wrong check for web calendars
+
+M UI/Scheduler/UIxComponentEditor.m
+
+commit bc2c839fbce17887c1bd9715c73428f3d872e755
+Author: Ludovic Marcotte
+Date: Tue Aug 18 08:39:20 2015 -0400
+
+ Update for the new packaging
+
+M UI/WebServerResources/css/styles.css
+M UI/WebServerResources/css/styles.css.map
+M UI/WebServerResources/js/Common.js
+M UI/WebServerResources/js/Common.js.map
+M UI/WebServerResources/js/Contacts.js
+M UI/WebServerResources/js/Contacts.js.map
+M UI/WebServerResources/js/Contacts.services.js
+M UI/WebServerResources/js/Contacts.services.js.map
+M UI/WebServerResources/js/Preferences.services.js
+M UI/WebServerResources/js/Preferences.services.js.map
+M UI/WebServerResources/js/Scheduler.js
+M UI/WebServerResources/js/Scheduler.js.map
+M UI/WebServerResources/js/Scheduler.services.js
+M UI/WebServerResources/js/Scheduler.services.js.map
+M UI/WebServerResources/js/vendor/angular-aria.js
+M UI/WebServerResources/js/vendor/angular-aria.min.js
+M UI/WebServerResources/js/vendor/angular-material.js
+M UI/WebServerResources/js/vendor/angular-material.min.js
+
+commit 20bf66d9311c7da05e5d2dff4f5fd6800cf023ea
+Author: Ludovic Marcotte
+Date: Mon Aug 17 16:21:10 2015 -0400
+
+ (feat) handle links to addressbooks like we do for calendars
+
+ also included missing template commit for calendar
+
+M SoObjects/Contacts/SOGoContactGCSFolder.h
+M SoObjects/Contacts/SOGoContactGCSFolder.m
+M UI/Contacts/UIxContactFolderLinksTemplate.h
+M UI/Contacts/UIxContactFolderLinksTemplate.m
+M UI/Contacts/UIxContactFoldersView.m
+M UI/Templates/ContactsUI/UIxContactFolderLinksTemplate.wox
+M UI/Templates/SchedulerUI/UIxCalendarFolderLinksTemplate.wox
+M UI/WebServerResources/js/Contacts/AddressBooksController.js
+
+commit 864a7f8028369143fbd05b1fbf726bed1c7b18c2
+Author: Francis Lachapelle
+Date: Fri Aug 14 02:31:27 2015 -0400
+
+ (html) Improve Preferences module
+
+M UI/Templates/PreferencesUI/UIxFilterEditor.wox
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+
+commit 7b5541b5d1b1c6b9275c734762e4598555b79ddd
+Author: Ludovic Marcotte
+Date: Mon Aug 17 13:44:21 2015 -0400
+
+ (fix) reuse the calendar urls from the model
+
+M UI/Scheduler/UIxCalendarFolderLinksTemplate.h
+M UI/Scheduler/UIxCalendarFolderLinksTemplate.m
+M UI/Scheduler/product.plist
+M UI/WebServerResources/js/Scheduler/CalendarsController.js
+
+commit 744cba4422078f4a2e094a97b00c432977208661
+Author: Ludovic Marcotte
+Date: Mon Aug 17 10:10:57 2015 -0400
+
+ (feat) integrated md-datepicker and enabled it in the Preferences module (Vacation)
+
+ The date picker suffers from this bug: https://github.com/angular/material/issues/4192
+
+ Which makes it not that usable right now.
+
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+M UI/WebServerResources/js/Preferences/Preferences.service.js
+A UI/WebServerResources/scss/components/datepicker/_extends-calendar.scss
+A UI/WebServerResources/scss/components/datepicker/_extends-datePicker.scss
+A UI/WebServerResources/scss/components/datepicker/calendar.scss
+A UI/WebServerResources/scss/components/datepicker/datePicker.scss
+M UI/WebServerResources/scss/components/menu/menu.scss
+M UI/WebServerResources/scss/core/variables.scss
+M UI/WebServerResources/scss/styles.scss
+
+commit ded467e0a2bfab88981308286390bcb8e2049418
+Author: Ludovic Marcotte
+Date: Mon Aug 17 09:55:14 2015 -0400
+
+ (fix) add empty settings hashes to prever JS exceptions
+
+M UI/PreferencesUI/UIxJSONPreferences.m
+
+commit a718b87d9194940898354dd29d650e10049e062f
+Author: Ludovic Marcotte
+Date: Sun Aug 16 21:42:53 2015 -0400
+
+ (fix) minor gui improvement to links templates
+
+M UI/Templates/ContactsUI/UIxContactFolderLinksTemplate.wox
+M UI/Templates/SchedulerUI/UIxCalendarFolderLinksTemplate.wox
+
+commit 6e2e71b2026541d03a6f791645ac1287558a2297
+Author: Ludovic Marcotte
+Date: Sat Aug 15 17:25:04 2015 -0400
+
+ (fix) correctly set flex attribut on sm devices
+
+M UI/Templates/ContactsUI/UIxContactFolderLinksTemplate.wox
+M UI/Templates/SchedulerUI/UIxCalendarFolderLinksTemplate.wox
+M UI/Templates/SchedulerUI/UIxCalendarProperties.wox
+
+commit 04e63e5edf182985f85c6672b094a85ba1f97364
+Author: Ludovic Marcotte
+Date: Sat Aug 15 17:08:15 2015 -0400
+
+ (fix) add minimal constraints when creating events or tasks
+
+M UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox
+M UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
+
+commit f1a50bcb74a9c580116614d95bdd139d67a0b5cf
+Author: Ludovic Marcotte
+Date: Fri Aug 14 16:31:29 2015 -0400
+
+ (feat) added refresh support to calendar
+
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+
+commit 0672b96b8b1a33d9b3952279591c72c9b2e874fc
+Author: Ludovic Marcotte
+Date: Fri Aug 14 15:51:17 2015 -0400
+
+ (fix) show the alarm name as a floating label for now
+
+ This might change as the text is so tiny!
+
+M UI/WebServerResources/js/Common/Alarm.service.js
+
+commit 93fa74d2793064086bfd348cc9efe638206341ee
+Author: Ludovic Marcotte
+Date: Fri Aug 14 15:22:59 2015 -0400
+
+ (feat) save/restore sorting state for address books
+
+ The initial loading doesn't work yet. Will work on this later.
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/WebServerResources/js/Contacts/AddressBook.service.js
+M UI/WebServerResources/js/Contacts/AddressBookController.js
+
+commit ffa4079fa8a412bc5f706fd3955bb4082a3ab4df
+Author: Ludovic Marcotte
+Date: Fri Aug 14 15:11:07 2015 -0400
+
+ (fix) b0rk3n sort saving regarding the asc/desc state
+
+M UI/Contacts/UIxContactsListActions.m
+M UI/Scheduler/UIxCalListingActions.m
+
+commit 8f75bdce2d97ce850c8ecf00b53115d5caefe6c9
+Author: Luc Charland
+Date: Fri Aug 14 12:46:37 2015 -0400
+
+ Use Json for testing webcalendar
+
+M Tests/Integration/test-ui-posts.py
+
+commit 4f53ea70e152018a39e407b3fb63003e621df24f
+Author: Luc Charland
+Date: Fri Aug 14 12:46:03 2015 -0400
+
+ Commented out testing two phones same type until fixed
+
+M Tests/Integration/test-carddav.py
+
+commit b72a9383a49a0273c61ad7bbe9782a0e050c1844
+Author: Ludovic Marcotte
+Date: Fri Aug 14 12:14:15 2015 -0400
+
+ (feat) major rework of the sidenav and top toolbars
+
+M UI/Common/UIxPageFrame.m
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+M UI/Templates/UIxPageFrame.wox
+M UI/Templates/UIxSidenavToolbarTemplate.wox
+M UI/Templates/UIxTopnavToolbarTemplate.wox
+M UI/WebServerResources/js/Common/Common.app.js
+M UI/WebServerResources/js/Common/navController.js
+
+commit 02daebe36be620611a26bef5f8f662fb11dce60d
+Author: Ludovic Marcotte
+Date: Wed Aug 12 18:31:06 2015 -0400
+
+ (fix) also handle tasks deletion correctly
+
+M UI/WebServerResources/js/Scheduler/Calendar.service.js
+
+commit 336e4c93e9ab155d591c886fe091d28691e82740
+Author: Ludovic Marcotte
+Date: Wed Aug 12 18:25:15 2015 -0400
+
+ (fix) added temporary fix for file inputs on firefox
+
+M UI/Templates/MailerUI/UIxMailEditor.wox
+
+commit 47252affba52c706ec2d7bd1e98fbf001c33ef91
+Author: Ludovic Marcotte
+Date: Wed Aug 12 16:11:38 2015 -0400
+
+ (feat) initial selection + ops in calendar module
+
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+M UI/WebServerResources/js/Scheduler/Calendar.service.js
+M UI/WebServerResources/js/Scheduler/CalendarListController.js
+M UI/WebServerResources/js/Scheduler/Component.service.js
+
+commit 8fb5c1d09fe2119b597259f93ded116c55b85df9
+Author: Luc Charland
+Date: Wed Aug 12 14:34:32 2015 -0400
+
+ Added sort saving to Contacts
+
+M UI/Contacts/UIxContactsListActions.m
+
+commit 54334b294e7aecc7a3c3950c74b37d708374f55e
+Author: Ludovic Marcotte
+Date: Wed Aug 12 10:31:06 2015 -0400
+
+ (fix) small fix in the template
+
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+
+commit c51bed258080303937bae494f665b04c9d2d8911
+Author: Ludovic Marcotte
+Date: Wed Aug 12 09:43:55 2015 -0400
+
+ (fix) updated js/css/map files for packaging
+
+M UI/WebServerResources/angular-material
+M UI/WebServerResources/css/styles.css
+M UI/WebServerResources/css/styles.css.map
+M UI/WebServerResources/js/Common.js
+M UI/WebServerResources/js/Common.js.map
+M UI/WebServerResources/js/Common/Acl.service.js
+M UI/WebServerResources/js/Contacts.js
+M UI/WebServerResources/js/Contacts.js.map
+M UI/WebServerResources/js/Contacts.services.js
+M UI/WebServerResources/js/Contacts.services.js.map
+M UI/WebServerResources/js/Mailer.js
+M UI/WebServerResources/js/Mailer.js.map
+M UI/WebServerResources/js/Mailer.services.js
+M UI/WebServerResources/js/Mailer.services.js.map
+M UI/WebServerResources/js/Main.js
+M UI/WebServerResources/js/Main.js.map
+M UI/WebServerResources/js/Scheduler.js
+M UI/WebServerResources/js/Scheduler.js.map
+M UI/WebServerResources/js/Scheduler.services.js
+M UI/WebServerResources/js/Scheduler.services.js.map
+M UI/WebServerResources/js/vendor/angular-file-upload.min.js
+M UI/WebServerResources/js/vendor/angular-material.js
+M UI/WebServerResources/js/vendor/angular-material.min.js
+
+commit 935df2bd002b62ec0e8c32a769c95a5aac778c83
+Author: Ludovic Marcotte
+Date: Tue Aug 11 21:16:16 2015 -0400
+
+ (fix) wrong label name
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+
+commit 85a843bc1edd32472fe6bf9563b418e60789fb61
+Author: Ludovic Marcotte
+Date: Tue Aug 11 20:58:39 2015 -0400
+
+ (fix) now show the event/task list instead of the day/week/month views on sm devices
+
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+
+commit 723372dddb46cda3f722696a376cd8064fb6313b
+Author: Ludovic Marcotte
+Date: Tue Aug 11 15:05:31 2015 -0400
+
+ (fix) when batch deleting, update the unseen count accordingly
+
+M UI/WebServerResources/js/Mailer/MailboxController.js
+
+commit 9362513feed6d4747f740c7863c9c82917364c77
+Author: Ludovic Marcotte
+Date: Tue Aug 11 14:48:45 2015 -0400
+
+ (fix) moved the mailbox cleanup after expunge in the service
+
+M UI/WebServerResources/js/Mailer/Mailbox.service.js
+M UI/WebServerResources/js/Mailer/MailboxesController.js
+
+commit 2e3057c2cea8d727625f1a13d33e22359adde2ff
+Author: Ludovic Marcotte
+Date: Tue Aug 11 13:40:38 2015 -0400
+
+ (fix) calendar views now honor "breakpoints"
+
+ We also now use again the default top nav toolbar and we
+ hide the fab button on sm devices. This of course needs a bit
+ more work but we are getting there!
+
+M UI/Templates/SchedulerUI/UIxCalDayView.wox
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+M UI/Templates/SchedulerUI/UIxCalMonthView.wox
+M UI/Templates/SchedulerUI/UIxCalWeekView.wox
+
+commit 456649a38a167eff8128ffab38d32d4029a44f14
+Author: Ludovic Marcotte
+Date: Tue Aug 11 13:38:28 2015 -0400
+
+ (fix) allow fullsreen editor on sm devices
+
+M UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox
+M UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
+
+commit 86818d4f64cc8ae853bb5f6bb56237d0adb4603a
+Author: Ludovic Marcotte
+Date: Tue Aug 11 13:10:11 2015 -0400
+
+ (fix) fixed scrolling during contact edition
+
+M UI/Templates/ContactsUI/UIxContactEditorTemplate.wox
+
+commit b59287d6df86d5fa1eb71a9cb562c6b1ea7fa284
+Author: Ludovic Marcotte
+Date: Tue Aug 11 11:18:41 2015 -0400
+
+ (fix) removed unused code
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+
+commit bdc8dfb2b458d70592ff3462321482b3620afb20
+Author: Ludovic Marcotte
+Date: Tue Aug 11 09:38:53 2015 -0400
+
+ (fix) removed unused code
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+
+commit 2825285b72388fcabea69f2d73913e602c51868c
+Author: Ludovic Marcotte
+Date: Tue Aug 11 08:45:05 2015 -0400
+
+ (fix) author cleanup
+
+M UI/Templates/UIxPageFrame.wox
+
+commit 314dcfd30ee4e3a5c1802e595fc671f282b3f45a
+Author: Ludovic Marcotte
+Date: Mon Aug 10 23:48:59 2015 -0400
+
+ (fix) properly cleanup the Trash folder after empty'ing
+
+M UI/WebServerResources/js/Mailer/MailboxesController.js
+
+commit d28fccf71e21ca850731c28530aa8b3b959b533b
+Author: Ludovic Marcotte
+Date: Mon Aug 10 23:14:40 2015 -0400
+
+ (fix) open attachment in new window
+
+M UI/Templates/MailPartViewers/UIxMailPartLinkViewer.wox
+
+commit ff632ec3e88ba229ef749190499c34659d3ce46c
+Author: Ludovic Marcotte
+Date: Mon Aug 10 22:02:43 2015 -0400
+
+ (fix) leave some room for the message date
+
+M UI/WebServerResources/scss/views/MailerUI.scss
+
+commit b7af83f7e335688d5b75be460655a13f9638259f
+Author: Ludovic Marcotte
+Date: Mon Aug 10 20:47:58 2015 -0400
+
+ (feat) pimped the login window
+
+M UI/Templates/MainUI/SOGoRootPage.wox
+M UI/WebServerResources/scss/views/LoginUI.scss
+
+commit d2aeee39eff73aceb9a6ddcdea1c59cb9599ef64
+Author: Ludovic Marcotte
+Date: Mon Aug 10 14:13:25 2015 -0400
+
+ (feat) now able to rename folders w/o double-tap
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+
+commit ecc0c453d7e9348445cc21659d32cfb57ecf54ac
+Author: Ludovic Marcotte
+Date: Mon Aug 10 13:27:35 2015 -0400
+
+ (fix) pimped the priority editor
+
+M UI/Templates/MailerUI/UIxMailEditor.wox
+
+commit 420dece1e75e292b05660b002fef0edecab2f8f8
+Author: Ludovic Marcotte
+Date: Mon Aug 10 13:10:45 2015 -0400
+
+ (feat) flag selected messages and mark selected messages as unread
+
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+M UI/WebServerResources/js/Mailer/Mailbox.service.js
+M UI/WebServerResources/js/Mailer/MailboxController.js
+
+commit c738a2dc70a1f8911d804961452953cfc042d36d
+Author: Ludovic Marcotte
+Date: Mon Aug 10 11:02:23 2015 -0400
+
+ (fix) wrong use of this which was breaking ACL module
+
+M UI/WebServerResources/js/Common/User.service.js
+
+commit e41e17c5d951f78a3065072edb41a46eae6ecf89
+Author: Ludovic Marcotte
+Date: Mon Aug 10 09:54:14 2015 -0400
+
+ (fix) use proper var:string value
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+
+commit 5741d7f95ce13ba78b7228441f804a0333e845ec
+Author: Ludovic Marcotte
+Date: Mon Aug 10 09:48:47 2015 -0400
+
+ (feat) can now copy selected cards
+
+M UI/Common/UIxFolderActions.m
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/WebServerResources/js/Contacts/AddressBook.service.js
+M UI/WebServerResources/js/Contacts/AddressBookController.js
+
+commit e13f7a267bc96ebc501b2c1b1aa88b882a8e9b77
+Author: Ludovic Marcotte
+Date: Sun Aug 9 20:21:09 2015 -0400
+
+ (feat) show email priority
+
+M UI/Templates/MailerUI/UIxMailFolderTemplate.wox
+
+commit dd7b929f17341a5cf63519b05874a87fba5c15f5
+Author: Ludovic Marcotte
+Date: Sun Aug 9 10:36:54 2015 -0400
+
+ (feat) compose mail with clicked email address
+
+M UI/Templates/MailerUI/UIxMailViewTemplate.wox
+M UI/WebServerResources/js/Mailer/Account.service.js
+M UI/WebServerResources/js/Mailer/MailboxController.js
+M UI/WebServerResources/js/Mailer/MessageController.js
+M UI/WebServerResources/js/Mailer/MessageEditorController.js
+
+commit ad9b969d6d21922ab6a9560177597d11361a9257
+Author: Ludovic Marcotte
+Date: Fri Aug 7 21:14:17 2015 -0400
+
+ (fix) avoid displaying the chips line
+
+M UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox
+
+commit 704307e6d6d2d11172684e7788d7ff319716caad
+Author: Ludovic Marcotte
+Date: Fri Aug 7 20:38:26 2015 -0400
+
+ (feat) pimped the mail part viewers
+
+M UI/Templates/MailPartViewers/UIxMailPartImageViewer.wox
+M UI/Templates/MailPartViewers/UIxMailPartLinkViewer.wox
+M UI/Templates/MailerUI/UIxMailViewTemplate.wox
+M UI/WebServerResources/js/Mailer/Message.service.js
+M UI/WebServerResources/scss/views/MailerUI.scss
+
+commit ce8a4b6d8a9104dd41fb319134e70005beb7b273
+Author: Francis Lachapelle
+Date: Fri Aug 7 15:32:00 2015 -0400
+
+ (js) Make events clickable in month view
+
+M UI/Templates/SchedulerUI/UIxCalMonthView.wox
+M UI/WebServerResources/js/Scheduler/sgCalendarDayBlock.directive.js
+M UI/WebServerResources/js/Scheduler/sgCalendarDayTable.directive.js
+M UI/WebServerResources/js/Scheduler/sgCalendarMonthDay.directive.js
+M UI/WebServerResources/js/Scheduler/sgCalendarMonthEvent.directive.js
+M UI/WebServerResources/scss/views/SchedulerUI.scss
+
+commit 8ab591a1b645cfd013f3899cc61903d61e71162b
+Author: Francis Lachapelle
+Date: Fri Aug 7 14:25:57 2015 -0400
+
+ (html) Improve task viewer
+
+M UI/Templates/SchedulerUI/UIxTaskViewTemplate.wox
+
+commit 4434547a18b6fb6527a3a9d92cbb7051c00cff11
+Author: Francis Lachapelle
+Date: Fri Aug 7 14:25:14 2015 -0400
+
+ (css) Don't highlight disabled list items
+
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+M UI/WebServerResources/scss/components/list/list.scss
+
+commit 29d86ad478fee8fc55b22a05b797124aa972ddf9
+Author: Francis Lachapelle
+Date: Fri Aug 7 14:13:27 2015 -0400
+
+ (js) Respect user's settings in Calendar module
+
+M UI/Scheduler/UIxCalMainView.m
+M UI/WebServerResources/js/Scheduler/CalendarListController.js
+M UI/WebServerResources/js/Scheduler/Component.service.js
+
+commit 15a5ab0b12862ad1c5b4a995d06db310a977f4b2
+Author: Ludovic Marcotte
+Date: Fri Aug 7 13:46:01 2015 -0400
+
+ (fix) added tooltip for consistency
+
+M UI/Templates/MailPartViewers/UIxMailPartImageViewer.wox
+
+commit 3b5013555de7b3651c90dc0a2bfdf61a9e2a7d9f
+Author: Francis Lachapelle
+Date: Fri Aug 7 11:22:24 2015 -0400
+
+ (html) Improve/fix appointment/task viewer/editor
+
+M UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox
+M UI/Templates/SchedulerUI/UIxAppointmentViewTemplate.wox
+M UI/Templates/SchedulerUI/UIxReminderEditor.wox
+M UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
+M UI/Templates/SchedulerUI/UIxTaskViewTemplate.wox
+
+commit 655a499cd461f240f36d257f712585f931767993
+Author: Francis Lachapelle
+Date: Fri Aug 7 11:18:40 2015 -0400
+
+ (css) Fix subheader margin
+
+M UI/WebServerResources/scss/components/subheader/subheader.scss
+
+commit 52559087b32e86acb6b0fa2e4a9716459c932d6c
+Author: Francis Lachapelle
+Date: Fri Aug 7 11:17:09 2015 -0400
+
+ (css) Merge autoScrollList.scss with list.scss
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/Templates/MailerUI/UIxMailFolderTemplate.wox
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+D UI/WebServerResources/scss/components/autoScrollList/autoScrollList.scss
+M UI/WebServerResources/scss/components/list/list.scss
+M UI/WebServerResources/scss/styles.scss
+
+commit fb7d5b628ffded1541d426599098aa2f2eee47f1
+Author: Francis Lachapelle
+Date: Fri Aug 7 11:04:04 2015 -0400
+
+ (js) Make use of 'controllerAs' in MainUI module
+
+M UI/MainUI/English.lproj/Localizable.strings
+M UI/Templates/MainUI/SOGoRootPage.wox
+M UI/WebServerResources/js/Common/Authentication.service.js
+M UI/WebServerResources/js/Main/Main.app.js
+
+commit 6e82a7ba7b49fbdce28a339d83bc23f00d119fb6
+Author: Francis Lachapelle
+Date: Fri Aug 7 11:02:34 2015 -0400
+
+ (js) Indentation
+
+M UI/WebServerResources/js/Common/AclController.js
+
+commit fbcaf254284f22c31b391730dbf91f3a877630cc
+Author: Ludovic Marcotte
+Date: Fri Aug 7 10:46:45 2015 -0400
+
+ Bumped to v3.0.0
+
+M Version
+
+commit a17e59af58bb04bbc45b9552a249b03471a49ba3
+Author: Ludovic Marcotte
+Date: Fri Aug 7 10:37:36 2015 -0400
+
+ (feat) added v3 js/css/map files for packaging
+
+A UI/WebServerResources/css/styles.css
+A UI/WebServerResources/css/styles.css.map
+A UI/WebServerResources/js/Common.js
+A UI/WebServerResources/js/Common.js.map
+A UI/WebServerResources/js/Contacts.js
+A UI/WebServerResources/js/Contacts.js.map
+A UI/WebServerResources/js/Contacts.services.js
+A UI/WebServerResources/js/Contacts.services.js.map
+A UI/WebServerResources/js/Mailer.js
+A UI/WebServerResources/js/Mailer.js.map
+A UI/WebServerResources/js/Mailer.services.js
+A UI/WebServerResources/js/Mailer.services.js.map
+A UI/WebServerResources/js/Main.js
+A UI/WebServerResources/js/Main.js.map
+A UI/WebServerResources/js/Preferences.js
+A UI/WebServerResources/js/Preferences.js.map
+A UI/WebServerResources/js/Preferences.services.js
+A UI/WebServerResources/js/Preferences.services.js.map
+A UI/WebServerResources/js/Scheduler.js
+A UI/WebServerResources/js/Scheduler.js.map
+A UI/WebServerResources/js/Scheduler.services.js
+A UI/WebServerResources/js/Scheduler.services.js.map
+A UI/WebServerResources/js/vendor/angular-animate.js
+A UI/WebServerResources/js/vendor/angular-animate.min.js
+A UI/WebServerResources/js/vendor/angular-animate.min.js.map
+A UI/WebServerResources/js/vendor/angular-aria.js
+A UI/WebServerResources/js/vendor/angular-aria.min.js
+A UI/WebServerResources/js/vendor/angular-aria.min.js.map
+A UI/WebServerResources/js/vendor/angular-file-upload.min.js
+A UI/WebServerResources/js/vendor/angular-material.js
+A UI/WebServerResources/js/vendor/angular-material.min.js
+A UI/WebServerResources/js/vendor/angular-sanitize.js
+A UI/WebServerResources/js/vendor/angular-sanitize.min.js
+A UI/WebServerResources/js/vendor/angular-sanitize.min.js.map
+A UI/WebServerResources/js/vendor/angular-ui-router.js
+A UI/WebServerResources/js/vendor/angular-ui-router.min.js
+A UI/WebServerResources/js/vendor/angular.js
+A UI/WebServerResources/js/vendor/angular.min.js
+A UI/WebServerResources/js/vendor/angular.min.js.map
+A UI/WebServerResources/js/vendor/lodash.js
+A UI/WebServerResources/js/vendor/lodash.min.js
+
+commit b966fd3e308d3836d0396ca5b5eb1fdb5b52e93e
+Author: Francis Lachapelle
+Date: Fri Aug 7 09:56:47 2015 -0400
+
+ (js) Move *.app.js files to their directories
+
+M UI/Templates/MainUI/SOGoRootPage.wox
+M UI/WebServerResources/Gruntfile.js
+D UI/WebServerResources/js/Contacts.app.js
+A UI/WebServerResources/js/Contacts/Contacts.app.js
+D UI/WebServerResources/js/Mailer.app.js
+A UI/WebServerResources/js/Mailer/Mailer.app.js
+D UI/WebServerResources/js/Main.app.js
+A UI/WebServerResources/js/Main/Main.app.js
+D UI/WebServerResources/js/Preferences.app.js
+A UI/WebServerResources/js/Preferences/Preferences.app.js
+D UI/WebServerResources/js/Scheduler.app.js
+A UI/WebServerResources/js/Scheduler/Scheduler.app.js
+
+commit caa50d25cb9fc41a4fb308ca4cb6bd57d97c977c
+Author: Ludovic Marcotte
+Date: Fri Aug 7 09:49:51 2015 -0400
+
+ (fix) removed broken parts from the template
+
+M UI/Templates/MailerUI/UIxMailEditor.wox
+
+commit 636e5c375ecc859cb2e66904dbe942bcac53de3b
+Author: Francis Lachapelle
+Date: Fri Aug 7 09:35:44 2015 -0400
+
+ (js) Merge utils.js to Common.js
+
+M UI/Templates/UIxPageFrame.wox
+M UI/WebServerResources/Gruntfile.js
+M UI/WebServerResources/js/Common/utils.js
+
+commit f6ea2229f8c61698391b0d438bcd68661238d766
+Author: Ludovic Marcotte
+Date: Fri Aug 7 09:15:38 2015 -0400
+
+ (fix) reworked the targets to not always generate static files
+
+M UI/WebServerResources/GNUmakefile
+
+commit 063e973ef3709c20825ad378f9a76169f6788dd2
+Author: Francis Lachapelle
+Date: Thu Aug 6 16:24:14 2015 -0400
+
+ (css) Cleanup variables already in material
+
+M UI/WebServerResources/scss/components/list/list.scss
+
+commit eb543ce14f76711f7c17ed45c9784665cb63dc90
+Author: Francis Lachapelle
+Date: Thu Aug 6 16:19:42 2015 -0400
+
+ (js) Allow to change the labels of Dialog.confirm
+
+M UI/WebServerResources/js/Common/Dialog.service.js
+M UI/WebServerResources/js/Contacts/CardController.js
+
+commit 9465fbad4b45508ec829d9988a52e0db16140100
+Author: Francis Lachapelle
+Date: Thu Aug 6 16:18:39 2015 -0400
+
+ (obj-c) Indentation
+
+M SoObjects/Appointments/SOGoAppointmentObject.m
+
+commit 560feb45d93ca285f746b5732379a66f182d3f16
+Author: Ludovic Marcotte
+Date: Thu Aug 6 16:18:53 2015 -0400
+
+ (fix) massively pimped the email composer
+
+M UI/Templates/MailerUI/UIxMailEditor.wox
+
+commit b5ca697b43ed07d384d9225517e1b7567dcfa7d4
+Author: Ludovic Marcotte
+Date: Thu Aug 6 16:18:10 2015 -0400
+
+ (fix) imprived the gravatar directive to extract email address between < >
+
+M UI/WebServerResources/js/Common/Gravatar.service.js
+
+commit 33d0f30d97a7f038d2eb90553455ba79fb35cb75
+Author: Ludovic Marcotte
+Date: Thu Aug 6 16:17:31 2015 -0400
+
+ (fix) avoid closing the email composition window by error
+
+M UI/WebServerResources/js/Mailer/MailboxController.js
+
+commit 0d537ab27b2f36c4916548a7aa6828974be2a031
+Author: Francis Lachapelle
+Date: Thu Aug 6 16:15:12 2015 -0400
+
+ Return recurrence rules of occurrence
+
+M SoObjects/Appointments/iCalRepeatableEntityObject+SOGo.m
+
+commit 2239286697e3b116d19c7299e0f297ca02668835
+Author: Francis Lachapelle
+Date: Thu Aug 6 16:13:10 2015 -0400
+
+ (js) Use CK Editor in mail account editor (Prefs)
+
+M UI/Templates/PreferencesUI/UIxAccountEditor.wox
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+M UI/WebServerResources/js/Preferences.app.js
+M UI/WebServerResources/js/Preferences/AccountDialogController.js
+M UI/WebServerResources/js/Preferences/PreferencesController.js
+
+commit d0d79728d2a58c0698291771a131b60e3a67d1ff
+Author: Francis Lachapelle
+Date: Thu Aug 6 16:09:49 2015 -0400
+
+ (js) Set locale to CKEditor in Mail editor
+
+M UI/Templates/MailerUI/UIxMailEditor.wox
+M UI/WebServerResources/js/Mailer/MessageEditorController.js
+
+commit e6c9289784da44ef2181cbe4c17f877b4369fc65
+Author: Francis Lachapelle
+Date: Thu Aug 6 16:07:46 2015 -0400
+
+ Add LocaleCode to JSON defaults
+
+M UI/PreferencesUI/UIxJSONPreferences.m
+
+commit ae017b50bcb88d52cb151b797c05cea38e7892f7
+Author: Francis Lachapelle
+Date: Thu Aug 6 16:04:39 2015 -0400
+
+ (js) Improve ckEditor directive
+
+ The directive will now consider the following attributes:
+
+ - ck-options: a JSON object of options
+ - ck-locale: the locale code to be used as the language and the speller
+ language
+
+M UI/WebServerResources/js/vendor/ckeditor/ck.js
+
+commit 42eb9e51b45dc78bc6a912ca99d397bc4a09c350
+Author: Luc Charland
+Date: Thu Aug 6 14:21:38 2015 -0400
+
+ Reworked saving Events filter and added Tasks filter
+
+M UI/Scheduler/UIxCalListingActions.m
+
+commit 7bb551a4ad50bd9ddab71aba28e1a9994e132144
+Author: Luc Charland
+Date: Thu Aug 6 12:38:26 2015 -0400
+
+ Added saving of sort order for Events and Tasks
+
+M UI/Scheduler/UIxCalListingActions.m
+
+commit 31b959a7dc7096bb9f50d2116ac261da3b084de8
+Author: Luc Charland
+Date: Thu Aug 6 12:16:17 2015 -0400
+
+ Added saving of Calendar Sort to jsonSettings
+
+M UI/Scheduler/UIxCalListingActions.m
+
+commit 2a132b4e8f2917507219aba5273bce6b74593c76
+Author: Francis Lachapelle
+Date: Thu Aug 6 11:48:42 2015 -0400
+
+ (css) Cleanup directives already in material
+
+M UI/WebServerResources/scss/components/divider/divider.scss
+M UI/WebServerResources/scss/core/layout.scss
+M UI/WebServerResources/scss/core/mixins.scss
+M UI/WebServerResources/scss/core/typography.scss
+
+commit ff1a1e6f595607df72f8f66de1bf4c3784eab02e
+Author: Francis Lachapelle
+Date: Thu Aug 6 11:26:26 2015 -0400
+
+ (html) Cleanup UIxContactFoldersView.wox
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+
+commit a4a75607fac93e989f096688943561ae4b6261ba
+Author: Francis Lachapelle
+Date: Thu Aug 6 11:22:23 2015 -0400
+
+ (js,html) Improve sgSubscribe directive
+
+M UI/Contacts/English.lproj/Localizable.strings
+M UI/Templates/ContactsUI/UIxContactsUserFolders.wox
+M UI/WebServerResources/js/Common/sgSubscribe.directive.js
+
+commit 6641bbded2b0743c0426e8cc09ad9d8e820cefe4
+Author: Francis Lachapelle
+Date: Thu Aug 6 11:18:53 2015 -0400
+
+ (js) Improve User.$filter function
+
+M UI/WebServerResources/js/Common/User.service.js
+
+commit 78975b9e6848d68c8377f01f290d9812fca5e5a9
+Author: Ludovic Marcotte
+Date: Thu Aug 6 09:36:43 2015 -0400
+
+ (fix) add proper class to avoid displaying the chips' line
+
+M UI/Templates/MailerUI/UIxMailViewTemplate.wox
+
+commit 2f122f12b6bc9b0ad0cc0969cc5ba0f5eef23a8b
+Author: Ludovic Marcotte
+Date: Wed Aug 5 20:23:11 2015 -0400
+
+ (fix) don't show a dialog on top of a dialog for acl confirmation
+
+M UI/Templates/UIxAclEditor.wox
+M UI/WebServerResources/js/Common/AclController.js
+
+commit 5e19a889c2bed84f5fa3e55fe0dc37fb5bb18e65
+Author: Francis Lachapelle
+Date: Wed Aug 5 16:44:25 2015 -0400
+
+ (feat) Handle invitations in appointment viewer
+
+M UI/Scheduler/English.lproj/Localizable.strings
+M UI/Scheduler/UIxAppointmentEditor.m
+M UI/Scheduler/UIxCalMainView.m
+M UI/Scheduler/UIxComponentEditor.h
+M UI/Scheduler/UIxComponentEditor.m
+M UI/Templates/SchedulerUI/UIxAppointmentViewTemplate.wox
+M UI/WebServerResources/js/Common/User.service.js
+M UI/WebServerResources/js/Contacts/AddressBook.service.js
+M UI/WebServerResources/js/Contacts/Card.service.js
+M UI/WebServerResources/js/Scheduler/CalendarListController.js
+M UI/WebServerResources/js/Scheduler/Component.service.js
+M UI/WebServerResources/js/Scheduler/ComponentController.js
+M UI/WebServerResources/scss/components/content/content.scss
+M UI/WebServerResources/scss/components/list/list.scss
+M UI/WebServerResources/scss/core/base_styles/_base_style.scss
+
+commit 87aec2fc01b0c05c73b1761135b103bb2d80ce14
+Author: Ludovic Marcotte
+Date: Wed Aug 5 16:02:57 2015 -0400
+
+ (feat) added batch export option to cards
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/Templates/ContactsUI/UIxContactViewTemplate.wox
+M UI/WebServerResources/js/Contacts/AddressBookController.js
+
+commit 5327df4c7adf01c002facf5dd3d4f9a327fa8480
+Author: Ludovic Marcotte
+Date: Wed Aug 5 15:04:47 2015 -0400
+
+ (feat) added select all feature to contacts module
+
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/WebServerResources/js/Contacts/AddressBookController.js
+
+commit a220ee67b786d2ebb7d3bff8559b031b258df67c
+Author: Ludovic Marcotte
+Date: Wed Aug 5 14:39:58 2015 -0400
+
+ (feat) reworked batch menu + added save messages option
+
+M UI/MailerUI/UIxMailFolderActions.m
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+M UI/WebServerResources/js/Mailer/MailboxController.js
+
+commit 124ca5d8cf0f72bfbfbe57969c9fedeca1749b12
+Author: Ludovic Marcotte
+Date: Wed Aug 5 11:08:36 2015 -0400
+
+ (fix) escape the HTML content from the raw source before returning it
+
+M UI/MailerUI/UIxMailSourceView.m
+
+commit 9dac8e55ff70be7f5f55dfdd7d75e804c2a1f2af
+Author: Francis Lachapelle
+Date: Wed Aug 5 10:14:32 2015 -0400
+
+ (js) Avoid accidentally closing the composer
+
+M UI/WebServerResources/js/Mailer/MessageController.js
+
+commit 4539fd4c5ed4e4813c06cbc75580116032948fa3
+Author: Francis Lachapelle
+Date: Wed Aug 5 10:12:51 2015 -0400
+
+ (html) Internationalized labels in message viewer
+
+M UI/Templates/MailerUI/UIxMailViewTemplate.wox
+
+commit 923ab97309755bbc95ee7e8a60b3bc47e1543f86
+Author: Luc Charland
+Date: Wed Aug 5 10:05:21 2015 -0400
+
+ Added tooltips for saving/view attachments
+
+M UI/MailPartViewers/English.lproj/Localizable.strings
+M UI/Templates/MailPartViewers/UIxMailPartLinkViewer.wox
+
+commit c56644f0e57f270a45f9e7ce20fbcc1f6035fa39
+Author: Luc Charland
+Date: Wed Aug 5 09:39:06 2015 -0400
+
+ Added tooltips to Contacts
+
+M UI/Templates/ContactsUI/UIxContactViewTemplate.wox
+
+commit da119ef3ad48f2229de933de30018022b6eb0803
+Author: Ludovic Marcotte
+Date: Tue Aug 4 20:32:43 2015 -0400
+
+ (feat) raw source support in contact module
+
+M UI/Templates/ContactsUI/UIxContactViewTemplate.wox
+M UI/WebServerResources/js/Contacts/CardController.js
+
+commit 6e114b84be14675549d45957f3777122a4102cae
+Author: Ludovic Marcotte
+Date: Tue Aug 4 16:51:33 2015 -0400
+
+ (feat) reorganized menu + added view raw source feature
+
+M UI/MailerUI/UIxMailSourceView.h
+M UI/MailerUI/UIxMailSourceView.m
+M UI/Templates/MailerUI/UIxMailViewTemplate.wox
+M UI/WebServerResources/js/Mailer/MessageController.js
+
+commit dff7b59036461b2615592cc5a174fa1b4487f4ef
+Author: Ludovic Marcotte
+Date: Tue Aug 4 16:39:02 2015 -0400
+
+ (fix) wrong tagging for dialog content
+
+M UI/WebServerResources/js/Contacts/AddressBookController.js
+
+commit 7f43f894b0629d764bf802f19aee2232d8d83376
+Author: Ludovic Marcotte
+Date: Tue Aug 4 16:08:17 2015 -0400
+
+ (fix) update the unseenCount when reading messages
+
+M UI/WebServerResources/js/Mailer/Message.service.js
+
+commit a27f106a4ada9fdc7101579a9de355607e3f54cb
+Author: Ludovic Marcotte
+Date: Tue Aug 4 13:52:31 2015 -0400
+
+ (fix) pimped ical viewer
+
+M UI/MailPartViewers/UIxMailPartICalViewer.h
+M UI/MailPartViewers/UIxMailPartICalViewer.m
+M UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox
+M UI/WebServerResources/js/Mailer/Message.service.js
+M UI/WebServerResources/js/Mailer/sgIMIP.directive.js
+M UI/WebServerResources/scss/components/icon/icon.scss
+
+commit 95a360dd36e3b7111530d50cffc3c9935cd2933d
+Author: Francis Lachapelle
+Date: Tue Aug 4 13:45:17 2015 -0400
+
+ (fix) Variable test in Component service
+
+M UI/WebServerResources/js/Scheduler/Component.service.js
+
+commit 09224f587f447fd95251aa7d11a9ac608f43b985
+Author: Luc Charland
+Date: Tue Aug 4 12:52:25 2015 -0400
+
+ Added a few tooltips for the mail view
+
+M UI/Templates/MailerUI/UIxMailViewTemplate.wox
+
+commit ff1b9e7ed1349550dc6e22b7aae9c0d324d6dc07
+Author: Francis Lachapelle
+Date: Tue Aug 4 10:56:55 2015 -0400
+
+ (fix) Draft initialization
+
+M UI/WebServerResources/js/Mailer/Message.service.js
+M UI/WebServerResources/js/Mailer/MessageController.js
+
+commit 6d260c58c7ad65dc9c1013c2ebef039d262cdfaa
+Author: Francis Lachapelle
+Date: Tue Aug 4 10:37:07 2015 -0400
+
+ (js) Update Gruntfile.js for changes in vendor js
+
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+M UI/WebServerResources/Gruntfile.js
+
+commit ba69bd4c82e9f256eed409b05d99f347c44d19cb
+Author: Francis Lachapelle
+Date: Tue Aug 4 07:06:42 2015 -0400
+
+ (css) Improve color picker inside md-avatar
+
+M UI/WebServerResources/scss/components/button/button.scss
+
+commit 2a45a2133b1f27b89ab00107098e313d28cd88e5
+Author: Francis Lachapelle
+Date: Mon Aug 3 16:34:44 2015 -0400
+
+ (fix) Padding of mail account delegation dialog
+
+M UI/Templates/MailerUI/UIxMailUserDelegationEditor.wox
+
+commit 57fbc453a63296ebe8d02009e7e543e029976a52
+Author: Francis Lachapelle
+Date: Mon Aug 3 16:32:59 2015 -0400
+
+ (html) Add avatars to ACL editor
+
+M UI/Templates/UIxAclEditor.wox
+
+commit f17b3638d79e9c56d9477585a18363328b1c5381
+Author: Francis Lachapelle
+Date: Mon Aug 3 16:20:24 2015 -0400
+
+ (js) Fix usage of Settings.activeUser
+
+M UI/WebServerResources/js/Scheduler/Calendar.service.js
+
+commit 67517e0b2832a2a0ed45202dfd5027d43b307ff4
+Author: extrafu
+Date: Mon Aug 3 20:40:59 2015 -0400
+
+ Update README.md
+
+M README.md
+
+commit d2b2cb186e358ffce254c66adc8d895a6c471cd7
+Author: Francis Lachapelle
+Date: Mon Aug 3 16:18:23 2015 -0400
+
+ (js) Fix usage of Settings.activeUser
+
+M UI/WebServerResources/js/Contacts/AddressBook.service.js
+M UI/WebServerResources/js/Preferences/Preferences.service.js
+
+commit ca8b64bd889def913100689581319208222bff4e
+Author: Francis Lachapelle
+Date: Mon Aug 3 16:17:02 2015 -0400
+
+ (js) Fix error when deleting an ACL user
+
+M UI/WebServerResources/js/Common/AclController.js
+
+commit 732b8c9d8a2003ae3a541cab710267de4797fb42
+Author: Francis Lachapelle
+Date: Mon Aug 3 16:15:59 2015 -0400
+
+ (css) Fullscreen dialogs on small screens
+
+M UI/WebServerResources/scss/components/dialog/dialog.scss
+
+commit ba6418fb936f50fa2af73e4e62fa4361e439fc1a
+Author: Francis Lachapelle
+Date: Mon Aug 3 14:44:11 2015 -0400
+
+ (html) Review the toolbar of dialog boxes
+
+M UI/Templates/SchedulerUI/UIxCalendarFolderLinksTemplate.wox
+M UI/Templates/UIxAclEditor.wox
+M UI/WebServerResources/js/Common/AclController.js
+
+commit ab1cd36c7755ce76d83b13d2ff5b881c1bf71691
+Author: Francis Lachapelle
+Date: Mon Aug 3 14:40:18 2015 -0400
+
+ Review delegation dialog in Mail app module
+
+M UI/Templates/MailerUI/UIxMailUserDelegationEditor.wox
+M UI/WebServerResources/js/Mailer/MailboxesController.js
+
+commit 9f2c5b5f506631b70ddae0fba71dd6ea4dbe9e82
+Author: Ludovic Marcotte
+Date: Mon Aug 3 12:48:29 2015 -0400
+
+ (fix) added bg icon
+
+M UI/Templates/MailerUI/UIxMailUserDelegationEditor.wox
+M UI/Templates/UIxAclEditor.wox
+
+commit b6ea1de4c060ecc9e7ed923f6cc6928c37cbd34d
+Author: Ludovic Marcotte
+Date: Mon Aug 3 12:43:21 2015 -0400
+
+ (fix) improved message editor
+
+M UI/Templates/MailerUI/UIxMailEditor.wox
+M UI/Templates/MailerUI/UIxMailFolderTemplate.wox
+M UI/WebServerResources/scss/views/MessageEditorUI.scss
+
+commit 21f214659e277d99712bc51b0b409dc228ed105d
+Author: Francis Lachapelle
+Date: Mon Aug 3 12:16:16 2015 -0400
+
+ (fix) Some compilation warnings
+
+M SoObjects/Mailer/SOGoDraftObject.m
+M SoObjects/Mailer/SOGoMailFolder.h
+M SoObjects/Mailer/SOGoMailFolder.m
+M SoObjects/Mailer/SOGoMailObject.m
+M SoObjects/SOGo/SOGoUserManager.m
+
+commit 82720d341ca202e0d0289c04f89bb3bd5431a8ee
+Author: Francis Lachapelle
+Date: Mon Aug 3 12:03:56 2015 -0400
+
+ (fix) Some compilation warnings
+
+M UI/Contacts/UIxContactView.m
+M UI/MailerUI/UIxMailActions.m
+M UI/MailerUI/UIxMailMainFrame.m
+M UI/MailerUI/UIxMailUserRightsEditor.h
+
+commit a330ff732017444c2481fe26ef1fcf663e053082
+Author: Francis Lachapelle
+Date: Mon Aug 3 11:53:54 2015 -0400
+
+ (js) Move Settings to a value in Common ng module
+
+ Also removed explicit settings and defaults that were imported in
+ various app modules. We now always use the Preferences ng service.
+
+M UI/Templates/ContactsUI/UIxContactEditorTemplate.wox
+M UI/Templates/ContactsUI/UIxContactFoldersView.wox
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+M UI/Templates/MainUI/SOGoRootPage.wox
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+M UI/WebServerResources/js/Common/Acl.service.js
+M UI/WebServerResources/js/Common/Alarm.service.js
+M UI/WebServerResources/js/Common/Common.app.js
+M UI/WebServerResources/js/Common/User.service.js
+M UI/WebServerResources/js/Common/navController.js
+M UI/WebServerResources/js/Contacts.app.js
+M UI/WebServerResources/js/Contacts/AddressBook.service.js
+M UI/WebServerResources/js/Contacts/Card.service.js
+M UI/WebServerResources/js/Mailer/Account.service.js
+M UI/WebServerResources/js/Mailer/Mailbox.service.js
+M UI/WebServerResources/js/Mailer/Message.service.js
+M UI/WebServerResources/js/Preferences.app.js
+M UI/WebServerResources/js/Preferences/Preferences.service.js
+M UI/WebServerResources/js/Scheduler/Calendar.service.js
+M UI/WebServerResources/js/Scheduler/Component.service.js
+
+commit 4cf82bd41ca195b3fca2d398946b99927e75ba72
+Author: Luc Charland
+Date: Mon Aug 3 10:56:24 2015 -0400
+
+ removed trailing space to strings
+
+M UI/MailerUI/Arabic.lproj/Localizable.strings
+M UI/MailerUI/Basque.lproj/Localizable.strings
+M UI/MailerUI/BrazilianPortuguese.lproj/Localizable.strings
+M UI/MailerUI/Catalan.lproj/Localizable.strings
+M UI/MailerUI/ChineseTaiwan.lproj/Localizable.strings
+M UI/MailerUI/Czech.lproj/Localizable.strings
+M UI/MailerUI/Danish.lproj/Localizable.strings
+M UI/MailerUI/Dutch.lproj/Localizable.strings
+M UI/MailerUI/English.lproj/Localizable.strings
+M UI/MailerUI/Finnish.lproj/Localizable.strings
+M UI/MailerUI/French.lproj/Localizable.strings
+M UI/MailerUI/German.lproj/Localizable.strings
+M UI/MailerUI/Hungarian.lproj/Localizable.strings
+M UI/MailerUI/Icelandic.lproj/Localizable.strings
+M UI/MailerUI/Italian.lproj/Localizable.strings
+M UI/MailerUI/NorwegianBokmal.lproj/Localizable.strings
+M UI/MailerUI/NorwegianNynorsk.lproj/Localizable.strings
+M UI/MailerUI/Polish.lproj/Localizable.strings
+M UI/MailerUI/Russian.lproj/Localizable.strings
+M UI/MailerUI/Slovak.lproj/Localizable.strings
+M UI/MailerUI/Slovenian.lproj/Localizable.strings
+M UI/MailerUI/SpanishArgentina.lproj/Localizable.strings
+M UI/MailerUI/SpanishSpain.lproj/Localizable.strings
+M UI/MailerUI/Swedish.lproj/Localizable.strings
+M UI/MailerUI/Ukrainian.lproj/Localizable.strings
+M UI/MailerUI/Welsh.lproj/Localizable.strings
+M UI/PreferencesUI/Arabic.lproj/Localizable.strings
+M UI/PreferencesUI/Basque.lproj/Localizable.strings
+M UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings
+M UI/PreferencesUI/Catalan.lproj/Localizable.strings
+M UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings
+M UI/PreferencesUI/Czech.lproj/Localizable.strings
+M UI/PreferencesUI/Danish.lproj/Localizable.strings
+M UI/PreferencesUI/Dutch.lproj/Localizable.strings
+M UI/PreferencesUI/English.lproj/Localizable.strings
+M UI/PreferencesUI/Finnish.lproj/Localizable.strings
+M UI/PreferencesUI/French.lproj/Localizable.strings
+M UI/PreferencesUI/German.lproj/Localizable.strings
+M UI/PreferencesUI/Hungarian.lproj/Localizable.strings
+M UI/PreferencesUI/Icelandic.lproj/Localizable.strings
+M UI/PreferencesUI/Italian.lproj/Localizable.strings
+M UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings
+M UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings
+M UI/PreferencesUI/Polish.lproj/Localizable.strings
+M UI/PreferencesUI/Russian.lproj/Localizable.strings
+M UI/PreferencesUI/Slovak.lproj/Localizable.strings
+M UI/PreferencesUI/Slovenian.lproj/Localizable.strings
+M UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings
+M UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings
+M UI/PreferencesUI/Swedish.lproj/Localizable.strings
+M UI/PreferencesUI/Ukrainian.lproj/Localizable.strings
+M UI/PreferencesUI/Welsh.lproj/Localizable.strings
+M UI/Templates/ContactsUI/UIxContactFolderLinksTemplate.wox
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+
+commit 9f202cfcdc984a2fcf8a1c4781f7280d27915b6a
+Author: Luc Charland
+Date: Mon Aug 3 10:24:15 2015 -0400
+
+ cleaned up a few comments
+
+M Scripts/verify_translations.py
+
+commit 6835cd5319d608a3cb7ab8e1246938fae45a670b
+Author: Luc Charland
+Date: Mon Aug 3 09:51:31 2015 -0400
+
+ removed double Name translation in 5 languages
+
+M UI/Scheduler/ChineseTaiwan.lproj/Localizable.strings
+M UI/Scheduler/Czech.lproj/Localizable.strings
+M UI/Scheduler/Russian.lproj/Localizable.strings
+M UI/Scheduler/Slovak.lproj/Localizable.strings
+M UI/Scheduler/Ukrainian.lproj/Localizable.strings
+
+commit 31bc88d9d3c6d0fd446c744018aa5848ac423c36
+Author: Ludovic Marcotte
+Date: Mon Aug 3 09:18:20 2015 -0400
+
+ Updated NEWS file regarding PR93
+
+ Conflicts:
+
+ NEWS
+
+M NEWS
+
+commit f515106bcc2e4f80dc000f46a574ef0920b9a8bb
+Author: Ludovic Marcotte
+Date: Mon Aug 3 09:16:55 2015 -0400
+
+ (fix) added patch from PR93
+
+M OpenChange/MAPIApplication.h
+M OpenChange/MAPIApplication.m
+M OpenChange/MAPIStoreCalendarFolder.m
+M OpenChange/MAPIStoreCalendarMessage.m
+M OpenChange/MAPIStoreContactsFolder.m
+M OpenChange/MAPIStoreContext.m
+M OpenChange/MAPIStoreDBFolder.m
+M OpenChange/MAPIStoreFolder.m
+M OpenChange/MAPIStoreGCSBaseContext.m
+M OpenChange/MAPIStoreMailContext.m
+M OpenChange/MAPIStoreMailFolder.m
+M OpenChange/MAPIStoreMailMessage.m
+M OpenChange/MAPIStoreMailVolatileMessage.m
+M OpenChange/MAPIStoreSOGo.m
+M OpenChange/MAPIStoreTasksFolder.m
+M OpenChange/MAPIStoreUserContext.h
+M OpenChange/MAPIStoreUserContext.m
+M SoObjects/Mailer/SOGoMailFolder.m
+M SoObjects/SOGo/SOGoUserManager.m
+
+commit ec1420c19b3748345a1edda617988ed6d06ecbb6
+Author: Ludovic Marcotte
+Date: Sun Aug 2 13:25:38 2015 -0400
+
+ (feat) now use mdDialog for message composition
+
+M UI/Templates/MailerUI/UIxMailEditor.wox
+M UI/Templates/MailerUI/UIxMailFolderTemplate.wox
+M UI/Templates/MailerUI/UIxMailViewTemplate.wox
+M UI/WebServerResources/js/Mailer.app.js
+M UI/WebServerResources/js/Mailer/MailboxController.js
+M UI/WebServerResources/js/Mailer/MessageController.js
+M UI/WebServerResources/js/Mailer/MessageEditorController.js
+
+commit f44af10cddb8cca5b565b8a7e63f75c998b2c5bc
+Author: Ludovic Marcotte
+Date: Fri Jul 31 15:40:00 2015 -0400
+
+ (feat) integrated the color picker in the pref module
+
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+
+commit e25305980f161b731c3d399c91a74118dfaa57b2
+Author: Ludovic Marcotte
+Date: Fri Jul 31 14:54:55 2015 -0400
+
+ (fix) improved the account delegation dialog
+
+M UI/Templates/MailerUI/UIxMailUserDelegationEditor.wox
+
+commit 0c7bdac0915e7d451fee606ec1a7a630f23bae21
+Author: Ludovic Marcotte
+Date: Fri Jul 31 14:31:38 2015 -0400
+
+ (fix) refactored code and we now return the unseenCount when fetching uids
+
+M SoObjects/Mailer/SOGoMailFolder.h
+M SoObjects/Mailer/SOGoMailFolder.m
+M UI/MailerUI/UIxMailFolderActions.m
+M UI/MailerUI/UIxMailListActions.m
+M UI/MailerUI/UIxMailMainFrame.m
+M UI/WebServerResources/js/Mailer/MailboxesController.js
+
+commit 2f8f8c78f78ee2581bb994f98bc2daddfbe9d508
+Author: Luc Charland
+Date: Fri Jul 31 13:54:03 2015 -0400
+
+ Removing colons
+
+M Scripts/verify_translations.py
+M SoObjects/Appointments/Arabic.lproj/Localizable.strings
+M SoObjects/Appointments/Basque.lproj/Localizable.strings
+M SoObjects/Appointments/BrazilianPortuguese.lproj/Localizable.strings
+M SoObjects/Appointments/Catalan.lproj/Localizable.strings
+M SoObjects/Appointments/ChineseTaiwan.lproj/Localizable.strings
+M SoObjects/Appointments/Czech.lproj/Localizable.strings
+M SoObjects/Appointments/Danish.lproj/Localizable.strings
+M SoObjects/Appointments/Dutch.lproj/Localizable.strings
+M SoObjects/Appointments/English.lproj/Localizable.strings
+M SoObjects/Appointments/Finnish.lproj/Localizable.strings
+M SoObjects/Appointments/French.lproj/Localizable.strings
+M SoObjects/Appointments/German.lproj/Localizable.strings
+M SoObjects/Appointments/Hungarian.lproj/Localizable.strings
+M SoObjects/Appointments/Icelandic.lproj/Localizable.strings
+M SoObjects/Appointments/Italian.lproj/Localizable.strings
+M SoObjects/Appointments/NorwegianBokmal.lproj/Localizable.strings
+M SoObjects/Appointments/NorwegianNynorsk.lproj/Localizable.strings
+M SoObjects/Appointments/Polish.lproj/Localizable.strings
+M SoObjects/Appointments/Russian.lproj/Localizable.strings
+M SoObjects/Appointments/Slovak.lproj/Localizable.strings
+M SoObjects/Appointments/Slovenian.lproj/Localizable.strings
+M SoObjects/Appointments/SpanishArgentina.lproj/Localizable.strings
+M SoObjects/Appointments/SpanishSpain.lproj/Localizable.strings
+M SoObjects/Appointments/Swedish.lproj/Localizable.strings
+M SoObjects/Appointments/Ukrainian.lproj/Localizable.strings
+M SoObjects/Appointments/Welsh.lproj/Localizable.strings
+M UI/Contacts/Arabic.lproj/Localizable.strings
+M UI/Contacts/Basque.lproj/Localizable.strings
+M UI/Contacts/BrazilianPortuguese.lproj/Localizable.strings
+M UI/Contacts/Catalan.lproj/Localizable.strings
+M UI/Contacts/ChineseTaiwan.lproj/Localizable.strings
+M UI/Contacts/Czech.lproj/Localizable.strings
+M UI/Contacts/Danish.lproj/Localizable.strings
+M UI/Contacts/Dutch.lproj/Localizable.strings
+M UI/Contacts/English.lproj/Localizable.strings
+M UI/Contacts/Finnish.lproj/Localizable.strings
+M UI/Contacts/French.lproj/Localizable.strings
+M UI/Contacts/German.lproj/Localizable.strings
+M UI/Contacts/Hungarian.lproj/Localizable.strings
+M UI/Contacts/Icelandic.lproj/Localizable.strings
+M UI/Contacts/Italian.lproj/Localizable.strings
+M UI/Contacts/NorwegianBokmal.lproj/Localizable.strings
+M UI/Contacts/NorwegianNynorsk.lproj/Localizable.strings
+M UI/Contacts/Polish.lproj/Localizable.strings
+M UI/Contacts/Russian.lproj/Localizable.strings
+M UI/Contacts/Slovak.lproj/Localizable.strings
+M UI/Contacts/Slovenian.lproj/Localizable.strings
+M UI/Contacts/SpanishArgentina.lproj/Localizable.strings
+M UI/Contacts/SpanishSpain.lproj/Localizable.strings
+M UI/Contacts/Swedish.lproj/Localizable.strings
+M UI/Contacts/Ukrainian.lproj/Localizable.strings
+M UI/Contacts/Welsh.lproj/Localizable.strings
+M UI/MailerUI/Arabic.lproj/Localizable.strings
+M UI/MailerUI/Basque.lproj/Localizable.strings
+M UI/MailerUI/BrazilianPortuguese.lproj/Localizable.strings
+M UI/MailerUI/Catalan.lproj/Localizable.strings
+M UI/MailerUI/ChineseTaiwan.lproj/Localizable.strings
+M UI/MailerUI/Czech.lproj/Localizable.strings
+M UI/MailerUI/Danish.lproj/Localizable.strings
+M UI/MailerUI/Dutch.lproj/Localizable.strings
+M UI/MailerUI/English.lproj/Localizable.strings
+M UI/MailerUI/Finnish.lproj/Localizable.strings
+M UI/MailerUI/French.lproj/Localizable.strings
+M UI/MailerUI/German.lproj/Localizable.strings
+M UI/MailerUI/Hungarian.lproj/Localizable.strings
+M UI/MailerUI/Icelandic.lproj/Localizable.strings
+M UI/MailerUI/Italian.lproj/Localizable.strings
+M UI/MailerUI/NorwegianBokmal.lproj/Localizable.strings
+M UI/MailerUI/NorwegianNynorsk.lproj/Localizable.strings
+M UI/MailerUI/Polish.lproj/Localizable.strings
+M UI/MailerUI/Russian.lproj/Localizable.strings
+M UI/MailerUI/Slovak.lproj/Localizable.strings
+M UI/MailerUI/Slovenian.lproj/Localizable.strings
+M UI/MailerUI/SpanishArgentina.lproj/Localizable.strings
+M UI/MailerUI/SpanishSpain.lproj/Localizable.strings
+M UI/MailerUI/Swedish.lproj/Localizable.strings
+M UI/MailerUI/Ukrainian.lproj/Localizable.strings
+M UI/MailerUI/Welsh.lproj/Localizable.strings
+M UI/MainUI/Arabic.lproj/Localizable.strings
+M UI/MainUI/Basque.lproj/Localizable.strings
+M UI/MainUI/BrazilianPortuguese.lproj/Localizable.strings
+M UI/MainUI/Catalan.lproj/Localizable.strings
+M UI/MainUI/ChineseTaiwan.lproj/Localizable.strings
+M UI/MainUI/Czech.lproj/Localizable.strings
+M UI/MainUI/Danish.lproj/Localizable.strings
+M UI/MainUI/Dutch.lproj/Localizable.strings
+M UI/MainUI/English.lproj/Localizable.strings
+M UI/MainUI/Finnish.lproj/Localizable.strings
+M UI/MainUI/French.lproj/Localizable.strings
+M UI/MainUI/German.lproj/Localizable.strings
+M UI/MainUI/Hungarian.lproj/Localizable.strings
+M UI/MainUI/Icelandic.lproj/Localizable.strings
+M UI/MainUI/Italian.lproj/Localizable.strings
+M UI/MainUI/NorwegianBokmal.lproj/Localizable.strings
+M UI/MainUI/NorwegianNynorsk.lproj/Localizable.strings
+M UI/MainUI/Polish.lproj/Localizable.strings
+M UI/MainUI/Russian.lproj/Localizable.strings
+M UI/MainUI/Slovak.lproj/Localizable.strings
+M UI/MainUI/Slovenian.lproj/Localizable.strings
+M UI/MainUI/SpanishArgentina.lproj/Localizable.strings
+M UI/MainUI/SpanishSpain.lproj/Localizable.strings
+M UI/MainUI/Swedish.lproj/Localizable.strings
+M UI/MainUI/Ukrainian.lproj/Localizable.strings
+M UI/MainUI/Welsh.lproj/Localizable.strings
+M UI/PreferencesUI/Arabic.lproj/Localizable.strings
+M UI/PreferencesUI/Basque.lproj/Localizable.strings
+M UI/PreferencesUI/BrazilianPortuguese.lproj/Localizable.strings
+M UI/PreferencesUI/Catalan.lproj/Localizable.strings
+M UI/PreferencesUI/ChineseTaiwan.lproj/Localizable.strings
+M UI/PreferencesUI/Czech.lproj/Localizable.strings
+M UI/PreferencesUI/Danish.lproj/Localizable.strings
+M UI/PreferencesUI/Dutch.lproj/Localizable.strings
+M UI/PreferencesUI/English.lproj/Localizable.strings
+M UI/PreferencesUI/Finnish.lproj/Localizable.strings
+M UI/PreferencesUI/French.lproj/Localizable.strings
+M UI/PreferencesUI/German.lproj/Localizable.strings
+M UI/PreferencesUI/Hungarian.lproj/Localizable.strings
+M UI/PreferencesUI/Icelandic.lproj/Localizable.strings
+M UI/PreferencesUI/Italian.lproj/Localizable.strings
+M UI/PreferencesUI/NorwegianBokmal.lproj/Localizable.strings
+M UI/PreferencesUI/NorwegianNynorsk.lproj/Localizable.strings
+M UI/PreferencesUI/Polish.lproj/Localizable.strings
+M UI/PreferencesUI/Russian.lproj/Localizable.strings
+M UI/PreferencesUI/Slovak.lproj/Localizable.strings
+M UI/PreferencesUI/Slovenian.lproj/Localizable.strings
+M UI/PreferencesUI/SpanishArgentina.lproj/Localizable.strings
+M UI/PreferencesUI/SpanishSpain.lproj/Localizable.strings
+M UI/PreferencesUI/Swedish.lproj/Localizable.strings
+M UI/PreferencesUI/Ukrainian.lproj/Localizable.strings
+M UI/PreferencesUI/Welsh.lproj/Localizable.strings
+M UI/Scheduler/Arabic.lproj/Localizable.strings
+M UI/Scheduler/Basque.lproj/Localizable.strings
+M UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings
+M UI/Scheduler/Catalan.lproj/Localizable.strings
+M UI/Scheduler/ChineseTaiwan.lproj/Localizable.strings
+M UI/Scheduler/Czech.lproj/Localizable.strings
+M UI/Scheduler/Danish.lproj/Localizable.strings
+M UI/Scheduler/Dutch.lproj/Localizable.strings
+M UI/Scheduler/English.lproj/Localizable.strings
+M UI/Scheduler/Finnish.lproj/Localizable.strings
+M UI/Scheduler/French.lproj/Localizable.strings
+M UI/Scheduler/German.lproj/Localizable.strings
+M UI/Scheduler/Hungarian.lproj/Localizable.strings
+M UI/Scheduler/Icelandic.lproj/Localizable.strings
+M UI/Scheduler/Italian.lproj/Localizable.strings
+M UI/Scheduler/NorwegianBokmal.lproj/Localizable.strings
+M UI/Scheduler/NorwegianNynorsk.lproj/Localizable.strings
+M UI/Scheduler/Polish.lproj/Localizable.strings
+M UI/Scheduler/Russian.lproj/Localizable.strings
+M UI/Scheduler/Slovak.lproj/Localizable.strings
+M UI/Scheduler/Slovenian.lproj/Localizable.strings
+M UI/Scheduler/SpanishArgentina.lproj/Localizable.strings
+M UI/Scheduler/SpanishSpain.lproj/Localizable.strings
+M UI/Scheduler/Swedish.lproj/Localizable.strings
+M UI/Scheduler/Ukrainian.lproj/Localizable.strings
+M UI/Scheduler/Welsh.lproj/Localizable.strings
+M UI/Templates/Appointments/SOGoAptMailReceipt.wox
+M UI/Templates/ContactsUI/UIxContactEditorTemplate.wox
+M UI/Templates/ContactsUI/UIxContactFolderLinksTemplate.wox
+M UI/Templates/ContactsUI/UIxListEditor.wox
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+M UI/Templates/MailerUI/UIxMailSearch.wox
+M UI/Templates/MainUI/SOGoRootPage.wox
+M UI/Templates/PreferencesUI/UIxAccountEditor.wox
+M UI/Templates/PreferencesUI/UIxPreferences.wox
+M UI/Templates/SchedulerUI/UIxAppointmentEditor.wox
+M UI/Templates/SchedulerUI/UIxAppointmentEditorTemplate.wox
+M UI/Templates/SchedulerUI/UIxAppointmentViewTemplate.wox
+M UI/Templates/SchedulerUI/UIxCalFilterPanel.wox
+M UI/Templates/SchedulerUI/UIxCalMainView.wox
+M UI/Templates/SchedulerUI/UIxCalViewPrint.wox
+M UI/Templates/SchedulerUI/UIxCalendarFolderLinksTemplate.wox
+M UI/Templates/SchedulerUI/UIxComponentEditor.wox
+M UI/Templates/SchedulerUI/UIxTaskEditor.wox
+M UI/Templates/SchedulerUI/UIxTaskEditorTemplate.wox
+M UI/Templates/SchedulerUI/UIxTaskViewTemplate.wox
+M UI/WebServerResources/angular-material
+
+commit 90b7ce7ec301120979e64a48aaa0ac0c6f704553
+Author: Luc Charland
+Date: Thu Jul 30 10:37:36 2015 -0400
+
+ fixed Appointments path
+
+M Scripts/verify_translations.py
+
+commit 6ecea9d70267ab54b29e8bb3b1deee44d0b8b908
+Author: Luc Charland
+Date: Thu Jul 30 10:26:43 2015 -0400
+
+ cleaned up a few comments
+
+M Scripts/verify_translations.py
+
+commit a54b8780a82db6c7401b558ee28972414907e218
+Author: Luc Charland
+Date: Thu Jul 30 10:07:18 2015 -0400
+
+ added first draft of verify_translation
+
+A Scripts/verify_translations.py
+
+commit 40d269c504309ab9e9108a799663266f8355fcbe
+Author: Ludovic Marcotte
+Date: Fri Jul 31 13:32:14 2015 -0400
+
+ (feat) unseen count support folder all accounts/mailboxes
+
+M UI/MailerUI/UIxMailFolderActions.m
+M UI/MailerUI/UIxMailMainFrame.m
+M UI/MailerUI/product.plist
+M UI/Templates/MailerUI/UIxMailMainFrame.wox
+M UI/WebServerResources/js/Mailer/Mailbox.service.js
+M UI/WebServerResources/js/Mailer/MailboxesController.js
+
+commit 1d52be415fc33a33b00da92a3ce698e164aae15f
+Author: Francis Lachapelle