Compare commits

..
Author SHA1 Message Date
Luc Charland 1dd4d870e0 Changed the keyname to EventsSortingState 2015-08-06 12:38:26 -04:00
280 changed files with 4733 additions and 105722 deletions

No files matched your search

+5 -3
View File
@@ -9,7 +9,6 @@
*/*/obj/
*/obj/
.scss-lint-config.yml_
ActiveSync/ActiveSync.SOGo
Documentation/*.docbook
Documentation/*.pdf
SoObjects/SOGo/SOGo.framework/
@@ -21,8 +20,11 @@ UI/WebServerResources/bower_components/
UI/WebServerResources/css/
UI/WebServerResources/css/styles.css
UI/WebServerResources/css/styles.css.map
UI/WebServerResources/js/*.js
UI/WebServerResources/js/*.js.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/vendor/
UI/WebServerResources/node_modules/
UI/WebServerResources/scss/.sass-cache/
+10 -50
View File
@@ -133,7 +133,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
[[o properties] addEntriesFromDictionary: values];
[o save];
@@ -160,7 +159,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSString *nameInCache;
if (theFolderType == ActiveSyncMailFolder)
nameInCache = [imapFolderGUIDS objectForKey: [theCollection nameInContainer]];
nameInCache= [[[theCollection mailAccountFolder] imapFolderGUIDs] objectForKey: [theCollection nameInContainer]];
else
{
NSString *component_name;
@@ -171,7 +170,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;
@@ -674,7 +673,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSDictionary *component;
NSArray *allComponents;
BOOL updated, initialLoadInProgress;
BOOL updated;
int deleted, return_count;
if (theFolderType == ActiveSyncContactFolder)
@@ -684,24 +683,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
else
component_name = @"vtodo";
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]]];
allComponents = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
allComponents = [allComponents sortedArrayUsingDescriptors: [NSArray arrayWithObjects: [[NSSortDescriptor alloc] initWithKey: @"c_lastmodified" ascending:YES], nil]];
// Check for the WindowSize
@@ -864,35 +847,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSMutableArray *allCacheObjects, *sortedBySequence;
SOGoMailObject *mailObject;
NSArray *allMessages, *a;
NSArray *allMessages;
int j, k, return_count, highestmodseq;
BOOL found_in_cache, initialLoadInProgress;
int j, k, return_count;
BOOL found_in_cache;
initialLoadInProgress = NO;
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];
allMessages = [theCollection syncTokenFieldsWithProperties: nil matchingSyncToken: theSyncKey fromDate: theFilterType];
max = [allMessages count];
allCacheObjects = [NSMutableArray array];
@@ -951,7 +911,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
NSString *lastSequence;
more_available = YES;
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? [NSString stringWithFormat:@"%d", highestmodseq] : [aCacheObject sequence]);
lastSequence = ([[aCacheObject sequence] isEqual: [NSNull null]] ? @"1" : [aCacheObject sequence]);
*theLastServerKey = [[NSString alloc] initWithFormat: @"%@-%@", [aCacheObject uid], lastSequence];
//NSLog(@"Reached windowSize - lastUID will be: %@", *theLastServerKey);
DESTROY(pool);
-1
View File
@@ -37,7 +37,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@interface SOGoActiveSyncDispatcher : NSObject
{
NSURL *folderTableURL;
NSDictionary *imapFolderGUIDS;
id context;
}
+11 -25
View File
@@ -151,14 +151,12 @@ static BOOL debugOn = NO;
debugOn = [[SOGoSystemDefaults sharedSystemDefaults] easDebugEnabled];
folderTableURL = nil;
imapFolderGUIDS = nil;
return self;
}
- (void) dealloc
{
RELEASE(folderTableURL);
RELEASE(imapFolderGUIDS);
[super dealloc];
}
@@ -231,27 +229,23 @@ static BOOL debugOn = NO;
SOGoMailAccounts *accountsFolder;
SOGoMailAccount *accountFolder;
SOGoUserFolder *userFolder;
NSDictionary *imapGUIDs;
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] ;
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] ;
}
return theIdToTranslate;
}
//
//
//
@@ -944,7 +938,6 @@ static BOOL debugOn = NO;
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
[o save];
command_count++;
@@ -1030,7 +1023,6 @@ static BOOL debugOn = NO;
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
}
[o save];
@@ -1054,7 +1046,6 @@ static BOOL debugOn = NO;
[[o properties] removeObjectForKey: @"MoreAvailable"];
[[o properties] removeObjectForKey: @"BodyPreferenceType"];
[[o properties] removeObjectForKey: @"SuccessfulMoveItemsOps"];
[[o properties] removeObjectForKey: @"InitialLoadSequence"];
}
[o save];
@@ -1213,7 +1204,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 initialLoad: NO];
allMessages = [currentCollection syncTokenFieldsWithProperties: nil matchingSyncToken: syncKey fromDate: filter];
count = [allMessages count];
@@ -2628,11 +2619,6 @@ 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.
+393 -13095
View File
File diff suppressed because it is too large. Load diff
@@ -233,7 +233,6 @@ Installation
This section will guide you through the installation of the native
Microsoft Outlook compatibility layer SOGo offers.
////
Red Hat Enterprise Linux v6 x86_64
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -317,7 +316,7 @@ from this guide.
On Ubuntu 12.04, the Samba init scripts need to be modified to
disable the upstart check. For more details, refer to:
https://wiki.samba.org/index.php/Samba4/InitScript
////
Debian 8 (Jessie) and Ubuntu 14.04 (Trusty Tahr)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -396,7 +395,7 @@ samba-tool user setexpiry administrator --noexpiry
----
You might consider changing the realm and domain used, to suit your
environment.
enviroment.
You might also have to
remove `/etc/samba/smb.conf` prior running this command.
@@ -442,7 +441,7 @@ Your Samba 4 configuration file should look like this:
OpenChange Configuration
~~~~~~~~~~~~~~~~~~~~~~~~
Since v2.2, OpenChange stores its metadata in MySQL so you need to have it installed.
OpenChange 2.2 stores its metadata in MySQL so you need to have it installed.
First, create the OpenChange MySQL user:
@@ -513,11 +512,9 @@ mapistore:indexing_backend = mysql://openchange-user:openchange$123@localhost/op
mapiproxy:openchangedb = mysql://openchange-user:openchange$123@localhost/openchange
----
////
On RHEL, make sure SELinux is disabled:
setenforce 0
////
Next, you can start Samba using the usual command:
@@ -548,11 +545,9 @@ This service runs as a WSGI application under apache (mod_wsgi).
While HTTPS is not required to access this service, it is strongly
recommended.
////
On RHEL-based distributions, the apache configuration required by these
services can be found in `/etc/httpd/conf.d/ocsmanager.conf` and
`/etc/httpd/conf.d/rpcproxy.conf`.
////
For Debian-based distributions, these files can be found
in `/etc/apache2/conf.d/` or `/etc/apache2/conf-available`.
@@ -568,10 +563,8 @@ The OCS Manager and RPC Proxy configuration module can be enabled using:
a2enconf ocsmanager
a2enconf rpcproxy
////
On RHEL-based distributions, make sure the `LoadModule` directive is
uncommented in `/etc/httpd/conf.d/wsgi.conf`.
////
The _reqtimeout_ apache module is known to cause problems when using the
default configuration shipped with Debian-based systems. On such
@@ -587,12 +580,10 @@ To avoid this problem, use a much higher timeout or disable the module:
a2dismod reqtimeout
You should now restart the Apache service and make sure it will start on
boot.
////
On RHEL-based distributions, do:
boot. On RHEL-based distributions, do:
chkconfig httpd on && /etc/init.d/httpd restart
////
On Debian-based distributions, do:
update-rc.d apache2 defaults && /etc/init.d/apache2 restart
@@ -822,7 +813,7 @@ the `openchange_user_cleanup` script distributed with SOGo. The script
can be found in `/usr/share/doc/sogo/` (`/usr/share/sogo-VERSION/` on
RHEL).
To reset a user, run the script as root:
`openchange_user_cleanup username`. See the usage output for additional options.
`python openchange_user_cleanup username`. See the usage output for additional options.
* The "Out of Office Assistant" will not currently work. This feature
has not been implemented.
* Creating folders below INBOX (when not normally permitted by the IMAP
+5 -20
View File
@@ -1,26 +1,12 @@
2.3.2 (2015-09-16)
2.3.x (2015-MM-DD)
------------------
Enhancements
- improved EAS speed and memory usage, avoiding many IMAP LIST commands (#3294)
- improved EAS speed during initial syncing of large mailboxes (#3293)
- updated CKEditor to version 4.5.3
Bug fixes
- fixed display of whitelisted attendees in Preferences window on Firefox (#3285)
- non-latin subfolder names are displayed correctly on Outlook (Zentyal)
- fixed several sync issues on environments with multiple users (Zentyal)
- folders from other users will no longer appear on your Outlook (Zentyal)
- use right auth in multidomain environments in contacts and calendar from Outlook (Zentyal)
- session fix when SOGoEnableDomainBasedUID is enabled but logins are domain-less
- less sync issues when setting read flag (Zentyal)
- attachments with non-latin filenames sent by Outlook are now received (Zentyal)
- support attachments from more mail clients (Zentyal)
- avoid conflicting message on saving a draft mail (Zentyal)
- less conflicting messages in Outlook while moving messages between folders (Zentyal)
- start/end shifting by 1 hour due to timezone change on last Sunday of October 2015 (#3344)
- fixed localization of calendar categories with empty profile (#3295)
- fixed options availability in contextual menu of Contacts module (#3342)
- Non-latin subfolder names are displayed correctly on Outlook (Zentyal)
- Fixed several sync issues on environments with multiple users (Zentyal)
- Folders from other users will no longer appear on your Outlook (Zentyal)
- Use right auth in multidomain environments in contacts and calendar from Outlook (Zentyal)
2.3.1 (2015-07-23)
------------------
@@ -33,7 +19,6 @@ Enhancements
- added create-folder subcommand to sogo-tool to create contact and calendar folders
- group mail addresses can be used as recipient in Outlook
- added 'ActiveSync' module constraints
- updated CKEditor to version 4.5.1
- added Slovenian translation - thanks to Jens Riecken
- added Chinese (Taiwan) translation
+1 -1
View File
@@ -671,7 +671,7 @@ static Class NSArrayK, MAPIStoreAppointmentWrapperK;
return newAttachment;
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
- (int) setReadFlag: (uint8_t) flag
{
return MAPISTORE_SUCCESS;
}
-136
View File
@@ -22,7 +22,6 @@
#import <Foundation/NSArray.h>
#import <Foundation/NSCalendarDate.h>
#import <Foundation/NSData.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
@@ -35,7 +34,6 @@
#import "MAPIStoreDBFolder.h"
#import "MAPIStoreDBMessage.h"
#import "MAPIStoreTypes.h"
#import "NSData+MAPIStore.h"
#import "NSObject+MAPIStore.h"
#import "NSString+MAPIStore.h"
@@ -106,105 +104,6 @@
return objectVersion;
}
- (void) _updatePredecessorChangeList
{
BOOL updated;
enum mapistore_error rc;
NSData *currentChangeList, *changeKey;
NSMutableArray *changeKeys;
NSMutableData *newChangeList;
NSUInteger count, len;
struct SizedXid *changes;
struct SPropValue property;
struct SRow aRow;
struct XID *currentChangeKey;
TALLOC_CTX *localMemCtx;
uint32_t nChanges;
localMemCtx = talloc_new (NULL);
if (!localMemCtx)
{
[self errorWithFormat: @"No more memory"];
return;
}
changeKey = [self getReplicaKeyFromGlobCnt: [self objectVersion]];
currentChangeList = [properties objectForKey: MAPIPropertyKey (PidTagPredecessorChangeList)];
if (!currentChangeList)
{
/* Create a new PredecessorChangeList */
len = [changeKey length];
newChangeList = [NSMutableData dataWithCapacity: len + 1];
[newChangeList appendUInt8: len];
[newChangeList appendData: changeKey];
}
else
{
/* Update current predecessor change list with new change key */
changes = [currentChangeList asSizedXidArrayInMemCtx: localMemCtx
with: &nChanges];
updated = NO;
currentChangeKey = [changeKey asXIDInMemCtx: localMemCtx];
for (count = 0; count < nChanges && !updated; count++)
{
if (GUID_equal(&changes[count].XID.NameSpaceGuid, &currentChangeKey->NameSpaceGuid))
{
NSData *globCnt, *oldGlobCnt;
oldGlobCnt = [NSData dataWithBytes: changes[count].XID.LocalId.data length: changes[count].XID.LocalId.length];
globCnt = [NSData dataWithBytes: currentChangeKey->LocalId.data length: currentChangeKey->LocalId.length];
if ([globCnt compare: oldGlobCnt] == NSOrderedDescending)
{
if ([globCnt length] != [oldGlobCnt length])
{
[self errorWithFormat: @"Cannot compare globcnt with different length: %@ and %@", globCnt, oldGlobCnt];
abort();
}
memcpy (changes[count].XID.LocalId.data, currentChangeKey->LocalId.data, currentChangeKey->LocalId.length);
updated = YES;
}
}
}
/* Serialise it */
changeKeys = [NSMutableArray array];
if (!updated)
[changeKeys addObject: changeKey];
for (count = 0; count < nChanges; count++)
{
changeKey = [NSData dataWithXID: &changes[count].XID];
[changeKeys addObject: changeKey];
}
[changeKeys sortUsingFunction: MAPIChangeKeyGUIDCompare context: localMemCtx];
newChangeList = [NSMutableData data];
len = [changeKeys count];
for (count = 0; count < len; count++)
{
changeKey = [changeKeys objectAtIndex: count];
[newChangeList appendUInt8: [changeKey length]];
[newChangeList appendData: changeKey];
}
}
if ([newChangeList length] > 0)
{
property.ulPropTag = PidTagPredecessorChangeList;
property.value.bin = *[newChangeList asBinaryInMemCtx: localMemCtx];
aRow.cValues = 1;
aRow.lpProps = &property;
rc = [self addPropertiesFromRow: &aRow];
if (rc != MAPISTORE_SUCCESS)
[self errorWithFormat: @"Impossible to add a new predecessor change list: %d", rc];
}
talloc_free (localMemCtx);
}
//
// FIXME: how this can happen?
//
@@ -267,9 +166,6 @@
[properties setObject: [NSNumber numberWithUnsignedLongLong: newVersion]
forKey: @"version"];
/* Update PredecessorChangeList accordingly */
[self _updatePredecessorChangeList];
[self logWithFormat: @"%d props in dict", [properties count]];
[sogoObject save];
@@ -313,36 +209,4 @@
return [sogoObject lastModified];
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
{
/* Modify PidTagMessageFlags from SetMessageReadFlag and
SyncImportReadStateChanges ROPs */
NSNumber *flags;
uint32_t newFlag;
flags = [properties objectForKey: MAPIPropertyKey (PR_MESSAGE_FLAGS)];
if (flags)
{
newFlag = [flags unsignedLongValue];
if (flag & SUPPRESS_RECEIPT)
newFlag |= MSGFLAG_READ;
if (flag & CLEAR_RN_PENDING)
newFlag &= ~MSGFLAG_RN_PENDING;
if (flag & CLEAR_READ_FLAG)
newFlag &= ~MSGFLAG_READ;
if (flag & CLEAR_NRN_PENDING)
newFlag &= ~MSGFLAG_NRN_PENDING;
}
else
{
newFlag = MSGFLAG_READ;
if (flag & CLEAR_READ_FLAG)
newFlag = 0x0;
}
[properties setObject: [NSNumber numberWithUnsignedLong: newFlag]
forKey: MAPIPropertyKey (PR_MESSAGE_FLAGS)];
return MAPISTORE_SUCCESS;
}
@end
-1
View File
@@ -121,7 +121,6 @@
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
wantCopy: (uint8_t) want_copy
inMemCtx: (TALLOC_CTX *) memCtx;
+8 -20
View File
@@ -642,7 +642,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
fromFolder: (MAPIStoreFolder *) sourceFolder
withMID: (uint64_t) targetMid
andChangeKey: (struct Binary_r *) targetChangeKey
andPredecessorChangeList: (struct Binary_r *) targetPredecessorChangeList
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{
@@ -670,18 +669,15 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
[sourceMsg copyToMessage: destMsg inMemCtx: memCtx];
if (targetPredecessorChangeList)
if (targetChangeKey)
{
property.ulPropTag = PidTagPredecessorChangeList;
property.value.bin = *targetPredecessorChangeList;
property.ulPropTag = PidTagChangeKey;
property.value.bin = *targetChangeKey;
aRow.cValues = 1;
aRow.lpProps = &property;
rc = [destMsg addPropertiesFromRow: &aRow];
if (rc != MAPISTORE_SUCCESS)
{
[self errorWithFormat: @"Cannot add PredecessorChangeList on move"];
goto end;
}
goto end;
}
[destMsg save: memCtx];
if (!wantCopy)
@@ -700,7 +696,6 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
{
@@ -710,7 +705,7 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
NSString *oldMessageURL;
MAPIStoreMapping *mapping;
SOGoUser *ownerUser;
struct Binary_r *targetChangeKey, *targetPredecessorChangeList;
struct Binary_r *targetChangeKey;
//TALLOC_CTX *memCtx;
//memCtx = talloc_zero (NULL, TALLOC_CTX);
@@ -731,21 +726,14 @@ Class NSExceptionK, MAPIStoreFAIMessageK, MAPIStoreMessageTableK, MAPIStoreFAIMe
if (oldMessageURL)
{
[oldMessageURLs addObject: oldMessageURL];
if (targetChangeKeys && targetPredecessorChangeList)
{
targetChangeKey = targetChangeKeys[count];
targetPredecessorChangeList = targetPredecessorChangeLists[count];
}
if (targetChangeKeys)
targetChangeKey = targetChangeKeys[count];
else
{
targetChangeKey = NULL;
targetPredecessorChangeList = NULL;
}
targetChangeKey = NULL;
rc = [self _moveCopyMessageWithMID: srcMids[count]
fromFolder: sourceFolder
withMID: targetMids[count]
andChangeKey: targetChangeKey
andPredecessorChangeList: targetPredecessorChangeList
wantCopy: wantCopy
inMemCtx: memCtx];
}
+1 -2
View File
@@ -42,8 +42,7 @@
/* synchronisation */
- (BOOL) synchroniseCache;
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
withChangeKey: (NSData *) oldChangeKey
andPredecessorChangeList: (NSData *) pcl;
withChangeKey: (NSData *) newChangeKey;
- (NSNumber *) lastModifiedFromMessageChangeNumber: (NSString *) changeNumber;
- (NSString *) changeNumberForMessageWithKey: (NSString *) messageKey;
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey;
+18 -85
View File
@@ -75,7 +75,6 @@ static Class NSNumberK;
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
inContainer: dbFolder]);
[versionsMessage setObjectType: MAPIInternalCacheObject];
[versionsMessage reloadIfNeeded];
}
- (void) dealloc
@@ -262,6 +261,7 @@ static Class NSNumberK;
*/
- (void) _setChangeKey: (NSData *) changeKey
forMessageEntry: (NSMutableDictionary *) messageEntry
inChangeListOnly: (BOOL) inChangeListOnly
{
struct XID *xid;
NSString *guid;
@@ -270,15 +270,19 @@ static Class NSNumberK;
NSMutableDictionary *changeList;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
guid = [NSString stringWithGUID: &xid->GUID];
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
talloc_free (xid);
/* 1. set change key association */
changeKeyDict = [NSDictionary dictionaryWithObjectsAndKeys: guid, @"GUID",
globCnt, @"LocalId",
nil];
[messageEntry setObject: changeKeyDict forKey: @"ChangeKey"];
if (!inChangeListOnly)
{
/* 1. set change key association */
changeKeyDict = [NSDictionary dictionaryWithObjectsAndKeys:
guid, @"GUID",
globCnt, @"LocalId",
nil];
[messageEntry setObject: changeKeyDict forKey: @"ChangeKey"];
}
/* 2. append/update predecessor change list */
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
@@ -292,77 +296,6 @@ static Class NSNumberK;
[changeList setObject: globCnt forKey: guid];
}
- (void) _updatePredecessorChangeList: (NSData *) predecessorChangeList
forMessageEntry: (NSMutableDictionary *) messageEntry
withOldChangeKey: (NSData *) oldChangeKey
{
NSData *globCnt, *oldGlobCnt;
NSDictionary *changeKeyDict;
NSString *guid;
NSMutableDictionary *changeList;
struct SizedXid *sizedXIDList;
struct XID xid, *givenChangeKey;
TALLOC_CTX *localMemCtx;
uint32_t i, length;
localMemCtx = talloc_new (NULL);
if (!localMemCtx)
{
[self errorWithFormat: @"No more memory"];
return;
}
if (predecessorChangeList)
{
sizedXIDList = [predecessorChangeList asSizedXidArrayInMemCtx: localMemCtx with: &length];
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
if (!changeList)
{
changeList = [NSMutableDictionary new];
[messageEntry setObject: changeList
forKey: @"PredecessorChangeList"];
[changeList release];
}
if (sizedXIDList) {
for (i = 0; i < length; i++)
{
xid = sizedXIDList[i].XID;
guid = [NSString stringWithGUID: &xid.NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid.LocalId.data length: xid.LocalId.length];
oldGlobCnt = [changeList objectForKey: guid];
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
[changeList setObject: globCnt forKey: guid];
}
}
}
if (oldChangeKey)
{
givenChangeKey = [oldChangeKey asXIDInMemCtx: localMemCtx];
if (givenChangeKey) {
guid = [NSString stringWithGUID: &givenChangeKey->NameSpaceGuid];
globCnt = [NSData dataWithBytes: givenChangeKey->LocalId.data length: givenChangeKey->LocalId.length];
changeKeyDict = [messageEntry objectForKey: @"ChangeKey"];
if (!changeKeyDict ||
([guid isEqualToString: [changeKeyDict objectForKey: @"GUID"]]
&& ([globCnt compare: [changeKeyDict objectForKey: @"LocalId"]] == NSOrderedDescending)))
{
/* The given change key is greater than current one stored in
metadata or it does not exist */
[messageEntry setObject: [NSDictionary dictionaryWithObjectsAndKeys: guid, @"GUID",
globCnt, @"LocalId",
nil]
forKey: @"ChangeKey"];
}
}
}
talloc_free (localMemCtx);
}
- (EOQualifier *) componentQualifier
{
if (!componentQualifier)
@@ -532,7 +465,8 @@ static Class NSNumberK;
// A GLOBCNT structure is a 6-byte global namespace counter,
// we strip the first 2 bytes. The first two bytes is the ReplicaId
changeKey = [self getReplicaKeyFromGlobCnt: newChangeNum >> 16];
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
[self _setChangeKey: changeKey forMessageEntry: messageEntry
inChangeListOnly: NO];
}
now = [NSCalendarDate date];
@@ -549,13 +483,12 @@ static Class NSNumberK;
}
- (void) updateVersionsForMessageWithKey: (NSString *) messageKey
withChangeKey: (NSData *) oldChangeKey
andPredecessorChangeList: (NSData *) pcl
withChangeKey: (NSData *) newChangeKey
{
NSMutableDictionary *messages, *messageEntry;
[self synchroniseCache];
if (oldChangeKey || pcl)
if (newChangeKey)
{
messages = [[versionsMessage properties] objectForKey: @"Messages"];
messageEntry = [messages objectForKey: messageKey];
@@ -563,8 +496,8 @@ static Class NSNumberK;
[NSException raise: @"MAPIStoreIOException"
format: @"no version record found for message '%@'",
messageKey];
[self _updatePredecessorChangeList: pcl forMessageEntry: messageEntry
withOldChangeKey: oldChangeKey];
[self _setChangeKey: newChangeKey forMessageEntry: messageEntry
inChangeListOnly: YES];
[versionsMessage save];
}
}
+3 -6
View File
@@ -209,16 +209,13 @@
- (void) updateVersions
{
/* Update ChangeKey and PredecessorChangeList on message's save */
NSData *newChangeKey, *predecessorChangeList;
NSData *newChangeKey;
newChangeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
predecessorChangeList = [properties objectForKey: MAPIPropertyKey (PR_PREDECESSOR_CHANGE_LIST)];
[(MAPIStoreGCSFolder *) container
updateVersionsForMessageWithKey: [self nameInContainer]
withChangeKey: newChangeKey
andPredecessorChangeList: predecessorChangeList];
updateVersionsForMessageWithKey: [self nameInContainer]
withChangeKey: newChangeKey];
}
@end
+15 -4
View File
@@ -30,7 +30,6 @@
#import <SOGo/NSArray+Utilities.h>
#import <Mailer/SOGoMailBodyPart.h>
#import <Mailer/SOGoMailObject.h>
#import <Mailer/NSDictionary+Mail.h>
#import "MAPIStoreTypes.h"
#import "MAPIStoreMailMessage.h"
@@ -109,7 +108,7 @@
static char recordBytes[] = {0xd9, 0xd8, 0x11, 0xa3, 0xe2, 0x90, 0x18, 0x41,
0x9e, 0x04, 0x58, 0x46, 0x9d, 0x6d, 0x1b,
0x68};
*data = [[NSData dataWithBytes: recordBytes length: 16]
asBinaryInMemCtx: memCtx];
@@ -118,7 +117,19 @@
- (NSString *) _fileName
{
return [bodyInfo filename];
NSString *fileName;
NSDictionary *parameters;
fileName = [[bodyInfo objectForKey: @"parameterList"]
objectForKey: @"name"];
if (!fileName)
{
parameters = [[bodyInfo objectForKey: @"disposition"]
objectForKey: @"parameterList"];
fileName = [parameters objectForKey: @"filename"];
}
return fileName;
}
- (int) getPidTagAttachLongFilename: (void **) data
@@ -167,7 +178,7 @@
- (int) getPidTagAttachContentId: (void **) data
inMemCtx: (TALLOC_CTX *) memCtx
{
{
*data = [[bodyInfo objectForKey: @"bodyId"]
asUnicodeInMemCtx: memCtx];
-2
View File
@@ -52,8 +52,6 @@
- (NSString *) changeNumberForMessageUID: (NSString *) messageUid;
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey;
- (BOOL) updatePredecessorChangeListWith: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey;
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey;
- (NSData *) predecessorChangeListForMessageWithKey: (NSString *) messageKey;
+10 -98
View File
@@ -68,8 +68,6 @@
static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
#include <gen_ndr/exchange.h>
#undef DEBUG
#include <util/attr.h>
#include <libmapi/libmapi.h>
@@ -111,7 +109,6 @@ static Class SOGoMailFolderK, MAPIStoreMailFolderK, MAPIStoreOutboxFolderK;
[SOGoMAPIDBMessage objectWithName: @"versions.plist"
inContainer: dbFolder]);
[versionsMessage setObjectType: MAPIInternalCacheObject];
[versionsMessage reloadIfNeeded];
}
- (BOOL) ensureFolderExists
@@ -519,44 +516,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return [modseq1 compare: modseq2];
}
- (void) _updatePredecessorChangeListWith: (NSData *) predecessorChangeList
forMessageEntry: (NSMutableDictionary *) messageEntry
{
NSData *globCnt, *oldGlobCnt;
NSMutableDictionary *changeList;
NSString *guid;
struct SizedXid *sizedXIDList;
struct XID xid;
uint32_t i, length;
sizedXIDList = [predecessorChangeList asSizedXidArrayInMemCtx: NULL with: &length];
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
if (!changeList)
{
changeList = [NSMutableDictionary new];
[messageEntry setObject: changeList
forKey: @"PredecessorChangeList"];
[changeList release];
}
if (sizedXIDList) {
for (i = 0; i < length; i++)
{
xid = sizedXIDList[i].XID;
guid = [NSString stringWithGUID: &xid.NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid.LocalId.data length: xid.LocalId.length];
oldGlobCnt = [changeList objectForKey: guid];
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
[changeList setObject: globCnt forKey: guid];
}
talloc_free (sizedXIDList);
}
[versionsMessage save];
}
- (void) _setChangeKey: (NSData *) changeKey
forMessageEntry: (NSMutableDictionary *) messageEntry
{
@@ -567,8 +526,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
NSMutableDictionary *changeList;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
guid = [NSString stringWithGUID: &xid->GUID];
globCnt = [NSData dataWithBytes: xid->Data length: xid->Size];
talloc_free (xid);
/* 1. set change key association */
@@ -965,7 +924,8 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
return changeNumber;
}
- (NSMutableDictionary *) _messageEntryFromMessageKey: (NSString *) messageKey
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
NSMutableDictionary *messages, *messageEntry;
NSString *messageUid;
@@ -976,7 +936,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
messageEntry = [messages objectForKey: messageUid];
if (!messageEntry)
{
[self warnWithFormat: @"attempting to synchronise to get the message entry for "
[self warnWithFormat: @"attempting to synchronise to set the change key for "
@"this message %@", messageKey];
synced = [self synchroniseCacheForUID: messageUid];
if (synced)
@@ -987,57 +947,11 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
abort ();
}
}
return messageEntry;
}
- (void) setChangeKey: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
[self _setChangeKey: changeKey
forMessageEntry: [self _messageEntryFromMessageKey: messageKey]];
[self _setChangeKey: changeKey forMessageEntry: messageEntry];
[versionsMessage save];
}
- (BOOL) updatePredecessorChangeListWith: (NSData *) changeKey
forMessageWithKey: (NSString *) messageKey
{
/* Update predecessor change list property given the change key. It
returns if the change key has been added to the list or not */
BOOL added = NO;
NSData *globCnt, *oldGlobCnt;
NSDictionary *messageEntry;
NSMutableDictionary *changeList;
NSString *guid;
struct XID *xid;
xid = [changeKey asXIDInMemCtx: NULL];
guid = [NSString stringWithGUID: &xid->NameSpaceGuid];
globCnt = [NSData dataWithBytes: xid->LocalId.data length: xid->LocalId.length];
talloc_free (xid);
messageEntry = [self _messageEntryFromMessageKey: messageKey];
if (messageEntry)
{
changeList = [messageEntry objectForKey: @"PredecessorChangeList"];
if (changeList)
{
oldGlobCnt = [changeList objectForKey: guid];
if (!oldGlobCnt || ([globCnt compare: oldGlobCnt] == NSOrderedDescending))
{
[changeList setObject: globCnt forKey: guid];
[versionsMessage save];
added = YES;
}
}
else
[self errorWithFormat: @"Missing predecessor change list to update"];
}
return added;
}
- (NSData *) changeKeyForMessageWithKey: (NSString *) messageKey
{
NSDictionary *messages, *changeKeyDict;
@@ -1303,7 +1217,6 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
fromFolder: (MAPIStoreFolder *) sourceFolder
withMIDs: (uint64_t *) targetMids
andChangeKeys: (struct Binary_r **) targetChangeKeys
andPredecessorChangeLists: (struct Binary_r **) targetPredecessorChangeLists
wantCopy: (uint8_t) wantCopy
inMemCtx: (TALLOC_CTX *) memCtx
@@ -1318,13 +1231,12 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
NSDictionary *result;
NSUInteger count;
NSArray *a;
NSData *changeList;
NSData *changeKey;
if (![sourceFolder isKindOfClass: [MAPIStoreMailFolder class]])
return [super moveCopyMessagesWithMIDs: srcMids andCount: midCount
fromFolder: sourceFolder withMIDs: targetMids
andChangeKeys: targetChangeKeys
andPredecessorChangeLists: targetPredecessorChangeLists
wantCopy: wantCopy
inMemCtx: memCtx];
@@ -1413,11 +1325,11 @@ _parseCOPYUID (NSString *line, NSArray **destUIDsP)
[self synchroniseCache];
for (count = 0; count < midCount; count++)
{
changeList = [NSData dataWithBinary: targetPredecessorChangeLists[count]];
changeKey = [NSData dataWithBinary: targetChangeKeys[count]];
messageKey = [NSString stringWithFormat: @"%@.eml",
[destUIDs objectAtIndex: count]];
[self _updatePredecessorChangeListWith: changeList
forMessageEntry: [self _messageEntryFromMessageKey: messageKey]];
[self setChangeKey: changeKey
forMessageWithKey: messageKey];
}
}
+26 -38
View File
@@ -40,7 +40,6 @@
#import <Mailer/NSData+Mail.h>
#import <Mailer/SOGoMailBodyPart.h>
#import <Mailer/SOGoMailObject.h>
#import <Mailer/NSDictionary+Mail.h>
#import "Codepages.h"
#import "NSData+MAPIStore.h"
@@ -197,7 +196,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
count1 = [keys indexOfObject: data1];
data2 = [entry2 objectForKey: @"mimeType"];
count2 = [keys indexOfObject: data2];
if (count1 == count2)
{
data1 = [entry1 objectForKey: @"key"];
@@ -530,7 +529,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
else
stringValue = @"";
*data = [stringValue asUnicodeInMemCtx: memCtx];
return MAPISTORE_SUCCESS;
}
@@ -625,7 +624,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
NSDictionary *coreInfos;
NSArray *flags;
unsigned int v = 0;
coreInfos = [sogoObject fetchCoreInfos];
flags = [coreInfos objectForKey: @"flags"];
@@ -637,7 +636,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if ([[self attachmentKeys]
count] > 0)
v |= MSGFLAG_HASATTACH;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -657,7 +656,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
v = 2;
else
v = 0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -669,15 +668,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
NSDictionary *coreInfos;
NSArray *flags;
unsigned int v;
coreInfos = [sogoObject fetchCoreInfos];
flags = [coreInfos objectForKey: @"flags"];
if ([flags containsObject: @"flagged"])
v = 6;
else
v = 0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -756,7 +755,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if ([ngAddress isKindOfClass: [NGMailAddress class]])
{
cn = [ngAddress displayName];
// If we don't have a displayName, we use the email address instead. This
// avoid bug #2119 - where Outlook won't display anything in the "From" field,
// nor in the recipient field if we reply to the email.
@@ -810,7 +809,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
entryId = MAPIStoreExternalEntryId (cn, email);
*data = [entryId asBinaryInMemCtx: memCtx];
rc = MAPISTORE_SUCCESS;
}
else
@@ -967,15 +966,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
{
uint32_t v;
NSString *s;
s = [[sogoObject mailHeaders] objectForKey: @"x-priority"];
v = 0x1;
if ([s hasPrefix: @"1"]) v = 0x2;
else if ([s hasPrefix: @"2"]) v = 0x2;
else if ([s hasPrefix: @"4"]) v = 0x0;
else if ([s hasPrefix: @"5"]) v = 0x0;
*data = MAPILongValue (memCtx, v);
return MAPISTORE_SUCCESS;
@@ -1164,14 +1163,14 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
if (!headerSetup)
[self _fetchHeaderData];
if ([headerMimeType isEqualToString: @"text/plain"])
format = EDITOR_FORMAT_PLAINTEXT;
else if ([headerMimeType isEqualToString: @"text/html"])
format = EDITOR_FORMAT_HTML;
else
format = 0; /* EDITOR_FORMAT_DONTKNOW */
*data = MAPILongValue (memCtx, format);
return MAPISTORE_SUCCESS;
@@ -1518,7 +1517,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
p = 0;
recipient->data = talloc_array (msgData, void *, msgData->columns->cValues);
memset (recipient->data, 0, msgData->columns->cValues * sizeof (void *));
// PR_OBJECT_TYPE = MAPI_MAILUSER (see MAPI_OBJTYPE)
recipient->data[p] = MAPILongValue (msgData, MAPI_MAILUSER);
p++;
@@ -1526,7 +1525,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
// PR_DISPLAY_TYPE = DT_MAILUSER (see MS-NSPI)
recipient->data[p] = MAPILongValue (msgData, 0);
p++;
// PR_7BIT_DISPLAY_NAME_UNICODE
recipient->data[p] = [cn asUnicodeInMemCtx: msgData];
p++;
@@ -1534,7 +1533,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
// PR_SMTP_ADDRESS_UNICODE
recipient->data[p] = [email asUnicodeInMemCtx: msgData];
p++;
// PR_SEND_INTERNET_ENCODING = 0x00060000 (plain text, see OXCMAIL)
recipient->data[p] = MAPILongValue (msgData, 0x00060000);
p++;
@@ -1567,9 +1566,12 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
withPrefix: (NSString *) keyPrefix
{
NSArray *parts;
NSDictionary *parameters;
NSUInteger count, max;
if ([[bodyInfo filename] length] > 0)
parameters = [[bodyInfo objectForKey: @"disposition"]
objectForKey: @"parameterList"];
if ([[parameters objectForKey: @"filename"] length] > 0)
{
if ([keyPrefix length] == 0)
keyPrefix = @"0";
@@ -1643,29 +1645,15 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
return attachment;
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
- (int) setReadFlag: (uint8_t) flag
{
BOOL modified = NO;
BOOL alreadyRead = NO;
NSString *imapFlag = @"\\Seen";
alreadyRead = [[[sogoObject fetchCoreInfos] objectForKey: @"flags"]
containsObject: @"seen"];
/* TODO: notifications should probably be emitted from here */
if (flag & CLEAR_READ_FLAG)
{
[sogoObject removeFlags: imapFlag];
modified = alreadyRead;
}
[sogoObject removeFlags: imapFlag];
else
{
[sogoObject addFlags: imapFlag];
modified = !alreadyRead;
}
if (modified)
[(MAPIStoreMailFolder *)[self container] synchroniseCache];
[sogoObject addFlags: imapFlag];
return MAPISTORE_SUCCESS;
}
@@ -1706,7 +1694,7 @@ _compareBodyKeysByPriority (id entry1, id entry2, void *data)
return nil;
}
- (void) save: (TALLOC_CTX *) memCtx
- (void) save: (TALLOC_CTX *) memCtx
{
NSNumber *value;
+7 -22
View File
@@ -161,30 +161,15 @@ static Class MAPIStoreMailMessageK, NSDataK, NSStringK;
//[self logWithFormat: @"change number from oxcfxics: %.16lx", [value unsignedLongLongValue]];
//[self logWithFormat: @" modseq: %.16lx", [modseq unsignedLongLongValue]];
if (modseq)
{
if (res->relop == RELOP_GT)
modseq = [NSNumber numberWithUnsignedLongLong:
[modseq unsignedLongLongValue] + 1];
}
modseq = [NSNumber numberWithUnsignedLongLong:
[modseq unsignedLongLongValue] + 1];
else
modseq = [NSNumber numberWithUnsignedLongLong: 0];
if (res->relop == RELOP_GT || res->relop == RELOP_GE)
{
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"MODSEQ"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: modseq];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
}
else
{
/* Ignore other operations as IMAP only support MODSEQ >= X */
[self warnWithFormat: @"Ignoring %@ as only supported operators are > and >=",
[self operatorFromRestrictionOperator: res->relop]];
rc = MAPIRestrictionStateAlwaysTrue;
}
*qualifier = [[EOKeyValueQualifier alloc] initWithKey: @"MODSEQ"
operatorSelector: EOQualifierOperatorGreaterThanOrEqualTo
value: modseq];
[*qualifier autorelease];
rc = MAPIRestrictionStateNeedsEval;
}
break;
+11 -25
View File
@@ -34,7 +34,6 @@
#import <NGExtensions/NGBase64Coding.h>
#import <NGExtensions/NGHashMap.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSObject+Values.h>
#import <NGExtensions/NSString+Encoding.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeMultipartBody.h>
@@ -286,7 +285,7 @@ static NSString *recTypes[] = { @"orig", @"to", @"cc", @"bcc" };
version = [properties objectForKey: @"version"];
return (version
? [version unsignedLongLongValue]
? exchange_globcnt ([version unsignedLongLongValue])
: ULLONG_MAX);
}
@@ -1111,8 +1110,7 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
- (void) save: (TALLOC_CTX *) memCtx
{
BOOL updatedMetadata;
NSString *folderName, *flag, *newIdString, *messageKey, *changeNumber;
NSString *folderName, *flag, *newIdString, *messageKey;
NSData *changeKey, *messageData;
NGImap4Connection *connection;
NGImap4Client *client;
@@ -1148,33 +1146,21 @@ MakeMessageBody (NSDictionary *mailProperties, NSDictionary *attachmentParts, NS
[sogoObject setNameInContainer: messageKey];
[mapping registerURL: [self url] withID: mid];
/* synchronise the cache and update the predecessor change list
with the change key provided by the client. Before doing
this, lets issue a unselect/select combo because of timing
issues with Dovecot in obtaining the latest modseq.
Sometimes, Dovecot doesn't return the newly appended UID if
we do a "UID SORT (DATE) UTF-8 (MODSEQ XYZ) (NOT DELETED)"
command (where XYZ is the HIGHESTMODSEQ+1) immediately after
IMAP APPEND */
/* synchronise the cache and update the change key with the one provided
by the client. Before doing this, lets issue a unselect/select combo
because of timing issues with Dovecot in obtaining the latest modseq.
Sometimes, Dovecot doesn't return the newly appended UID if we do
a "UID SORT (DATE) UTF-8 (MODSEQ XYZ) (NOT DELETED)" command (where
XYZ is the HIGHESTMODSEQ+1) immediately after IMAP APPEND */
[client unselect];
[client select: folderName];
[(MAPIStoreMailFolder *) container synchroniseCache];
changeKey = [properties objectForKey: MAPIPropertyKey (PR_CHANGE_KEY)];
if (changeKey)
{
updatedMetadata = [(MAPIStoreMailFolder *) container updatePredecessorChangeListWith: changeKey
forMessageWithKey: messageKey];
if (!updatedMetadata)
[self warnWithFormat: @"Predecessor change list not updated with client data"];
}
/* Update version property (PR_CHANGE_KEY indeed) as it is
requested once it is saved */
changeNumber = [(MAPIStoreMailFolder *) container changeNumberForMessageUID: newIdString];
if (changeNumber)
[properties setObject: [NSNumber numberWithUnsignedLongLong: [changeNumber unsignedLongLongValue] >> 16]
forKey: @"version"];
[(MAPIStoreMailFolder *) container
setChangeKey: changeKey
forMessageWithKey: messageKey];
}
}
+1 -1
View File
@@ -63,7 +63,7 @@
withAID: (uint32_t) aid;
- (int) getAttachmentTable: (MAPIStoreAttachmentTable **) tablePtr
andRowCount: (uint32_t *) countPtr;
- (enum mapistore_error) setReadFlag: (uint8_t) flag;
- (int) setReadFlag: (uint8_t) flag;
- (enum mapistore_error) saveMessage: (TALLOC_CTX *) memCtx;
- (NSArray *) activeContainerMessageTables;
+2 -3
View File
@@ -549,12 +549,11 @@ rtf2html (NSData *compressedRTF)
}
[self save: memCtx];
/* We make sure that any change-related properties are removed from the
/* We make sure that any change-related properties are removes from the
properties dictionary, to make sure that related methods will be
invoked the next time they are requested. */
[properties removeObjectForKey: MAPIPropertyKey (PidTagChangeKey)];
[properties removeObjectForKey: MAPIPropertyKey (PidTagChangeNumber)];
[properties removeObjectForKey: MAPIPropertyKey (PidTagPredecessorChangeList)];
if ([container isKindOfClass: MAPIStoreFolderK])
{
@@ -919,7 +918,7 @@ rtf2html (NSData *compressedRTF)
return [self getNo: data inMemCtx: memCtx];;
}
- (enum mapistore_error) setReadFlag: (uint8_t) flag
- (int) setReadFlag: (uint8_t) flag
{
// [self subclassResponsibility: _cmd];
+1 -3
View File
@@ -674,7 +674,6 @@ sogo_folder_move_copy_messages(void *folder_object,
uint32_t mid_count,
uint64_t *src_mids, uint64_t *t_mids,
struct Binary_r **target_change_keys,
struct Binary_r **target_predecessor_change_lists,
uint8_t want_copy)
{
MAPIStoreFolder *sourceFolder, *targetFolder;
@@ -699,7 +698,6 @@ sogo_folder_move_copy_messages(void *folder_object,
fromFolder: sourceFolder
withMIDs: t_mids
andChangeKeys: target_change_keys
andPredecessorChangeLists: target_predecessor_change_lists
wantCopy: want_copy
inMemCtx: mem_ctx];
TRYCATCH_END(pool)
@@ -1120,7 +1118,7 @@ sogo_message_set_read_flag (void *message_object, uint8_t flag)
struct MAPIStoreTallocWrapper *wrapper;
NSAutoreleasePool *pool;
MAPIStoreMessage *message;
enum mapistore_error rc;
int rc;
if (message_object)
{
+1 -1
View File
@@ -531,7 +531,7 @@
}
[vToDo setTimeStampAsDate: now];
[sogoObject saveCalendar: vCalendar];
[sogoObject saveComponent: vCalendar];
[self updateVersions];
}
-2
View File
@@ -41,8 +41,6 @@
+ (id) dataWithXID: (const struct XID *) xid;
- (struct XID *) asXIDInMemCtx: (void *) memCtx;
- (struct SizedXid *) asSizedXidArrayInMemCtx: (void *) memCtx
with: (uint32_t *) length;
+ (id) dataWithChangeKeyGUID: (NSString *) guidString
andCnt: (NSData *) globCnt;
+6 -40
View File
@@ -22,7 +22,6 @@
#import <NGExtensions/NSObject+Logs.h>
#import "MAPIStoreTypes.h"
#import "NSObject+MAPIStore.h"
#import "NSString+MAPIStore.h"
@@ -30,7 +29,6 @@
#undef DEBUG
#include <stdbool.h>
#include <libmapi/libmapi.h>
#include <talloc.h>
#include <util/time.h>
#include <gen_ndr/exchange.h>
@@ -138,11 +136,11 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
NSMutableData *xidData;
struct FlatUID_r flatUID;
_fillFlatUIDWithGUID (&flatUID, &xid->NameSpaceGuid);
_fillFlatUIDWithGUID (&flatUID, &xid->GUID);
xidData = [NSMutableData dataWithCapacity: 16 + xid->LocalId.length];
xidData = [NSMutableData dataWithCapacity: 16 + xid->Size];
[xidData appendBytes: flatUID.ab length: 16];
[xidData appendBytes: xid->LocalId.data length: xid->LocalId.length];
[xidData appendBytes: xid->Data length: xid->Size];
return xidData;
}
@@ -158,12 +156,12 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
{
xid = talloc_zero (memCtx, struct XID);
[self _extractGUID: &xid->NameSpaceGuid];
[self _extractGUID: &xid->GUID];
xid->LocalId.length = max - 16;
xid->Size = max - 16;
bytes = (uint8_t *) [self bytes];
xid->LocalId.data = talloc_memdup (xid, (bytes+16), xid->LocalId.length);
xid->Data = talloc_memdup (xid, (bytes+16), xid->Size);
}
else
{
@@ -174,38 +172,6 @@ static void _fillFlatUIDWithGUID (struct FlatUID_r *flatUID, const struct GUID *
return xid;
}
- (struct SizedXid *) asSizedXidArrayInMemCtx: (void *) memCtx
with: (uint32_t *) length
{
struct Binary_r bin;
struct SizedXid *sizedXIDArray;
bin.cb = [self length];
bin.lpb = (uint8_t *)[self bytes];
sizedXIDArray = get_SizedXidArray(memCtx, &bin, length);
if (!sizedXIDArray)
{
NSLog (@"Impossible to parse SizedXID array");
return NULL;
}
return sizedXIDArray;
}
- (NSComparisonResult) compare: (NSData *) otherGlobCnt
{
uint64_t globCnt = 0, oGlobCnt = 0;
if ([self length] > 0)
globCnt = *(uint64_t *) [self bytes];
if ([otherGlobCnt length] > 0)
oGlobCnt = *(uint64_t *) [otherGlobCnt bytes];
return MAPICNCompare (globCnt, oGlobCnt, NULL);
}
+ (id) dataWithChangeKeyGUID: (NSString *) guidString
andCnt: (NSData *) globCnt;
{
+4 -36
View File
@@ -161,11 +161,8 @@
minute: [tzStart minuteOfHour] second: 0
timeZone: [NSTimeZone timeZoneWithName: @"GMT"]];
tmpDate = [tmpDate addYear: 0
month: ((pos > 0) ? 0 : 1)
day: 0
hour: 0
minute: 0
tmpDate = [tmpDate addYear: 0 month: ((pos > 0) ? 0 : 1)
day: 0 hour: 0 minute: 0
second: 0];
/* If the day of the time change is "-XSU", we need to determine whether the
@@ -200,41 +197,12 @@
END:STANDARD
END:VTIMEZONE
The time changes occur on a Sunday, but in March, the 1st is a Sunday itself and in November
The time changes occure on a Sunday, but in March, the 1st is a Sunday itself and in November
the 1st is also a Sunday. If we don't decrement "pos" by one, tmpDate (which is set to March or November 1st
because of "day: 1" will have 14 more days added for March and 7 more days added for November - which will
effectively shift the time change by a whole week.
In Europe/Berlin, we have a different use-case for November. In 2015, November 1st is a Sunday.
The time change in November must occur on October 25th but since tmpDate will be November 1st,
so a Sunday, dateDayOfWeek will be 0 and dayOfWeek will also be 0 we would decrement tmpDate by 14 days,
which is incorrect because it would shift the timezone change one week earlier. We take care about this
one with check if pos is greater or equal than 0 and if so, we don't decrement it.
BEGIN:VCALENDAR
PRODID:-//Inverse inc.//NONSGML Olson 2014g//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Europe/Berlin
X-LIC-LOCATION:Europe/Berlin
BEGIN:DAYLIGHT
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:19700329T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
TZNAME:CET
DTSTART:19701025T030000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
*/
if (dayOfWeek == dateDayOfWeek && pos >= 0)
if (dayOfWeek == dateDayOfWeek)
pos--;
offset = (dayOfWeek - dateDayOfWeek) + (pos * 7);
@@ -2988,10 +2988,7 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir
theUser = [SOGoUser userWithLogin: theUID];
aParent = [theUser calendarsFolderInContext: context];
if ([aParent isKindOfClass: [NSException class]])
return nil;
aFolders = [aParent subFolders];
e = [aFolders objectEnumerator];
while ((aFolder = [e nextObject]))
@@ -1493,9 +1493,8 @@ inRecurrenceExceptionsForEvent: (iCalEvent *) theEvent
// the database. We do this ONLY when using SOGo from the
// Web interface or over ActiveSync.
// Over DAV, it'll be handled directly in PUTAction:
if (![context request] ||
[[context request] handledByDefaultHandler] ||
[[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"])
if (![context request] || [[context request] handledByDefaultHandler]
|| [[[context request] requestHandlerKey] isEqualToString: @"Microsoft-Server-ActiveSync"])
{
// If an alarm was specified, let's use it. This would happen if an attendee accepts/declines/etc. an
// event invitation and also sets an alarm along the way. This would happen ONLY from the web interface.
@@ -84,30 +84,17 @@
*/
- (NSDictionary *) attributesInContext: (WOContext *) context
{
NSArray *allEvents, *rules;
NSArray *rules;
NSCalendarDate *untilDate;
NSMutableDictionary *data, *repeat;
NSString *frequency;
NSTimeZone *timeZone;
SOGoUserDefaults *ud;
iCalEvent *masterComponent;
iCalRecurrenceRule *rule;
data = [NSMutableDictionary dictionaryWithDictionary: [super attributesInContext: context]];
if ([self recurrenceId])
{
// If the component is an occurrence of a recurrent component,
// consider the recurrence rules of the master component.
allEvents = [[self parent] events];
masterComponent = [allEvents objectAtIndex: 0];
rules = [masterComponent recurrenceRules];
}
else
{
rules = [self recurrenceRules];
}
rules = [self recurrenceRules];
if ([rules count] > 0)
{
rule = [rules objectAtIndex: 0];
+1 -5
View File
@@ -1,6 +1,6 @@
/* NGVCard+SOGo.h - this file is part of SOGo
*
* Copyright (C) 2009-2015 Inverse inc.
* Copyright (C) 2009-2014 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -31,10 +31,6 @@
- (CardElement *) elementWithTag: (NSString *) elementTag
ofType: (NSString *) type;
- (void) addElementWithTag: (NSString *) elementTag
ofType: (NSString *) type
withValue: (id) value;
- (void) updateFromLDIFRecord: (NSDictionary *) ldifRecord;
- (NSMutableDictionary *) asLDIFRecord;
+2 -2
View File
@@ -188,8 +188,8 @@ convention:
}
- (void) addElementWithTag: (NSString *) elementTag
ofType: (NSString *) type
withValue: (id) value
ofType: (NSString *) type
withValue: (id) value
{
NSArray *allValues;
NSEnumerator *list;
+3 -6
View File
@@ -1,5 +1,6 @@
/*
Copyright (C) 2006-2015 Inverse inc.
Copyright (C) 2006-2014 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo.
@@ -31,13 +32,9 @@
@class NSString;
@interface SOGoContactGCSFolder : SOGoGCSFolder <SOGoContactFolder>
{
NSString *baseCardDAVURL, *basePublicCardDAVURL;
}
- (void) fixupContactRecord: (NSMutableDictionary *) contactRecord;
- (NSDictionary *) lookupContactWithName: (NSString *) aName;
- (NSString *) cardDavURL;
- (NSString *) publicCardDavURL;
@end
+2 -63
View File
@@ -1,5 +1,6 @@
/*
Copyright (C) 2006-2013-2015 Inverse inc.
Copyright (C) 2006-2013 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of SOGo.
@@ -66,24 +67,6 @@ static NSArray *folderListingFields = nil;
@"c_component", nil];
}
- (id) init
{
if ((self = [super init]))
{
baseCardDAVURL = nil;
basePublicCardDAVURL = nil;
}
return self;
}
- (void) dealloc
{
[baseCardDAVURL release];
[basePublicCardDAVURL release];
[super dealloc];
}
- (Class) objectClassForContent: (NSString *) content
{
CardGroup *cardEntry;
@@ -453,48 +436,4 @@ static NSArray *folderListingFields = nil;
return [NSString stringWithFormat: @"%@:%@", nsRep, nodeName];
}
- (NSString *) _baseCardDAVURL
{
NSString *davURL;
if (!baseCardDAVURL)
{
davURL = [[self realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCardDAVURL = davURL;
[baseCardDAVURL retain];
}
return baseCardDAVURL;
}
- (NSString *) cardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCardDAVURL]];
}
- (NSString *) _basePublicCardDAVURL
{
NSString *davURL;
if (!basePublicCardDAVURL)
{
davURL = [[self publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCardDAVURL = davURL;
[basePublicCardDAVURL retain];
}
return basePublicCardDAVURL;
}
- (NSString *) publicCardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCardDAVURL]];
}
@end /* SOGoContactGCSFolder */
+4 -4
View File
@@ -97,10 +97,10 @@
- (NSString *) davCollectionTag;
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress;
- (NSArray *) syncTokenFieldsWithProperties: (NSArray *) theProperties
matchingSyncToken: (NSString *) theSyncToken
fromDate: (NSCalendarDate *) theStartDate;
/* flags */
- (NSException *) addFlagsToAllMessages: (id) _f;
+1 -5
View File
@@ -2119,7 +2119,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
- (NSArray *) syncTokenFieldsWithProperties: (NSArray *) theProperties
matchingSyncToken: (NSString *) theSyncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress
{
EOQualifier *searchQualifier;
NSMutableArray *allTokens;
@@ -2196,9 +2195,6 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
for (i = 0; i < [fetchResults count]; i++)
{
if ([[[fetchResults objectAtIndex: i] objectForKey: @"flags"] containsObject: @"deleted"] && initialLoadInProgress)
continue;
d = [NSDictionary dictionaryWithObject: ([[[fetchResults objectAtIndex: i] objectForKey: @"flags"] containsObject: @"deleted"]) ? [NSNull null] : [[fetchResults objectAtIndex: i] objectForKey: @"modseq"]
forKey: [[[fetchResults objectAtIndex: i] objectForKey: @"uid"] stringValue]];
[allTokens addObject: d];
@@ -2209,7 +2205,7 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
if (highestmodseq == 0)
highestmodseq = 1;
if (highestmodseq > 0 && !initialLoadInProgress)
if (highestmodseq > 0)
{
id uid;
+3 -1
View File
@@ -1,6 +1,8 @@
/* NSArray+Utilities.h - this file is part of SOGo
*
* Copyright (C) 2006-2015 Inverse inc.
* Copyright (C) 2006 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+3 -1
View File
@@ -1,6 +1,8 @@
/* NSArray+Utilities.m - this file is part of SOGo
*
* Copyright (C) 2006-2015 Inverse inc.
* Copyright (C) 2006-2011 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+3 -1
View File
@@ -1,6 +1,8 @@
/* NSObject+Utilities.h - this file is part of SOGo
*
* Copyright (C) 2007-2015 Inverse inc.
* Copyright (C) 2007 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+3 -1
View File
@@ -1,6 +1,8 @@
/* NSObject+Utilities.m - this file is part of SOGo
*
* Copyright (C) 2007-2015 Inverse inc.
* Copyright (C) 2007 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+2 -8
View File
@@ -373,7 +373,6 @@ static int cssEscapingCount;
- (NSString *) asCSSIdentifier
{
NSCharacterSet *numericSet;
NSMutableString *cssIdentifier;
unichar currentChar;
int count, max, idx;
@@ -382,12 +381,10 @@ static int cssEscapingCount;
[self _setupCSSEscaping];
cssIdentifier = [NSMutableString string];
numericSet = [NSCharacterSet decimalDigitCharacterSet];
max = [self length];
if (max > 0)
{
if ([numericSet characterIsMember: [self characterAtIndex: 0]])
if (isdigit([self characterAtIndex: 0]))
// A CSS identifier can't start with a digit; we add an underscore
[cssIdentifier appendString: @"_"];
for (count = 0; count < max; count++)
@@ -418,7 +415,6 @@ static int cssEscapingCount;
- (NSString *) fromCSSIdentifier
{
NSCharacterSet *numericSet;
NSMutableString *newString;
NSString *currentString;
int count, length, max, idx;
@@ -427,14 +423,12 @@ static int cssEscapingCount;
if (!cssEscapingStrings)
[self _setupCSSEscaping];
numericSet = [NSCharacterSet decimalDigitCharacterSet];
newString = [NSMutableString string];
max = [self length];
count = 0;
if (max > 0
&& [self characterAtIndex: 0] == '_'
&& [numericSet characterIsMember: [self characterAtIndex: 1]])
&& isdigit([self characterAtIndex: 1]))
{
/* If the identifier starts with an underscore followed by a digit,
we remove the underscore */
+2 -2
View File
@@ -128,8 +128,8 @@
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress;
fromDate: (NSCalendarDate *) theStartDate;
/* multiget helper */
- (WOResponse *) performMultigetInContext: (WOContext *) queryContext
+8 -15
View File
@@ -1151,7 +1151,6 @@ static NSArray *childRecordFields = nil;
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) properties
matchingSyncToken: (NSString *) syncToken
fromDate: (NSCalendarDate *) theStartDate
initialLoad: (BOOL) initialLoadInProgress
{
/* TODO:
- validation:
@@ -1201,17 +1200,13 @@ static NSArray *childRecordFields = nil;
mRecords = [NSMutableArray arrayWithArray: [self _fetchFields: fields
withQualifier: qualifier
ignoreDeleted: YES]];
if (!initialLoadInProgress)
{
qualifier = [EOQualifier qualifierWithQualifierFormat:
@"c_lastmodified > %d and c_deleted == 1",
syncTokenInt];
fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_lastmodified", @"c_deleted", nil];
[mRecords addObjectsFromArray: [self _fetchFields: fields
withQualifier: qualifier
ignoreDeleted: NO]];
}
qualifier = [EOQualifier qualifierWithQualifierFormat:
@"c_lastmodified > %d and c_deleted == 1",
syncTokenInt];
fields = [NSMutableArray arrayWithObjects: @"c_name", @"c_deleted", nil];
[mRecords addObjectsFromArray: [self _fetchFields: fields
withQualifier: qualifier
ignoreDeleted: NO]];
records = mRecords;
}
else
@@ -1504,9 +1499,7 @@ static NSArray *childRecordFields = nil;
properties = [self parseDAVRequestedProperties: propElement];
records = [self syncTokenFieldsWithProperties: properties
matchingSyncToken: syncToken
fromDate: nil
initialLoad: NO];
fromDate: nil];
[self _appendComponentProperties: [properties allKeys]
fromRecords: records
matchingSyncToken: [syncToken intValue]
+1 -4
View File
@@ -778,10 +778,7 @@ static Class NSNullK;
NSRange r;
r = [theUID rangeOfString: [NSString stringWithFormat: @"@%@", theDomain]];
// We check if the range is ok here since we could be using DomainLessLogin
if (r.location != NSNotFound)
theUID = [theUID substringToIndex: r.location];
theUID = [theUID substringToIndex: r.location];
}
sogoSources = [[self authenticationSourceIDsInDomain: theDomain] objectEnumerator];
+1 -5
View File
@@ -70,11 +70,7 @@ class Carddav:
if not self.cards:
url = "/SOGo/so/%s/Contacts/personal/view" % (self.login)
content = self._get(url)
#print "\nCONTENT:", content
if 'cards' in content:
self.cards = content['cards']
else:
self.cards = []
self.cards = content['cards']
return self.cards
def get_cards(self, pattern):
+4 -4
View File
@@ -89,9 +89,9 @@ class preferences:
get = HTTPPreferencesGET(url)
get.cookie = self.cookie
self.client.execute(get)
if DEBUG: print "DEBUG (url):", url
if DEBUG: print "DEBUG (status):", get.response["status"]
if DEBUG: print "DEBUG (body):", get.response['body']
if DEBUG: print "LUC (url):", url
if DEBUG: print "LUC (status):", get.response["status"]
if DEBUG: print "LUC (body):", get.response['body']
content = simplejson.loads(get.response['body'])
result = None
try:
@@ -117,7 +117,7 @@ class preferences:
# Get the right sub-dict and change the key/value
subdict = self.find_key(self.preferences, preference)
if not subdict:
raise AttributeError("ERROR(nosubdict): looking for %s in: %s" %(preference, str(self.preferences)))
print "LUC(nosubdict):", preference, self.preferences
subdict[preference] = value
def set(self, preference, value=None):
-24
View File
@@ -85,13 +85,6 @@ class JsonDavPhoneTests(unittest.TestCase):
self.allphones = list(self.newphone)
self.allphones.extend(self.newphones_difftype)
self.allphones.extend(self.newphones_sametype)
#- In case there are no cards for this user
try:
self._get_card()
except IndexError:
path = 'Contacts/personal'
(card, path, gid) = self._create_new_card(path)
self._save_card(card)
def tearDown(self):
self._connect_as_user()
@@ -109,23 +102,6 @@ class JsonDavPhoneTests(unittest.TestCase):
def _connect_as_user(self, newuser=username, newpassword=password):
self.dv = carddav.Carddav(newuser, newpassword)
def _create_new_card(self, path):
gid = self.dv.newguid(path)
card = {'c_categories': None,
'c_cn': 'John Doe',
'c_component': 'vcard',
'c_givenname': 'John Doe',
'c_mail': 'johndoe@nothere.com',
'c_name': gid,
'c_o': '',
'c_screenname': '',
'c_sn': '',
'c_telephonenumber': '123.456.7890',
'emails': [{'type': 'pref', 'value': 'johndoe@nothere.com'}],
'phones': [{'type': 'home', 'value': '111.222.3333'}],
'id': gid}
return (card, path, gid)
def _get_card(self, name="John Doe"):
tmp_card = self.dv.get_cards(name)[0]
self.card = self.dv.get_card(tmp_card['c_name'])
@@ -28,14 +28,12 @@ class preventInvitationsTest(unittest.TestCase):
self.caldav = CalDAVSchedulingTest(self)
cal = self.prefs.get("Calendar")
if "PreventInvitationsWhitelist" not in cal:
#print "LUC (cal):", cal
cal["PreventInvitationsWhitelist"] = None
self.prefs.set("PreventInvitationsWhitelist", None)
if "PreventInvitations" not in cal:
cal["PreventInvitations"] = 0
self.prefs.set("PreventInvitations", 0)
def tearDown(self):
#self.prefs.set("autoReplyText", "")
self.prefs.set("autoReplyText", "")
self.prefs.set('PreventInvitations', 0)
self.prefs.set("PreventInvitationsWhitelist", None)
#- Manual Cleanup, not called because classs is not derived from unittest
+6 -4
View File
@@ -21,8 +21,8 @@ class UIPostsTests(unittest.TestCase):
self.cookie = sogoLogin.getAuthCookie(hostname, port, username, password)
def _urlPostData(self, client, url, data, exp_status=200):
post = webdavlib.HTTPPOST(url, simplejson.dumps(data))
post.content_type = "application/json"
post = webdavlib.HTTPPOST(url, data)
post.content_type = "application/x-www-form-urlencoded"
post.cookie = self.cookie
client.execute(post)
@@ -43,14 +43,16 @@ class UIPostsTests(unittest.TestCase):
""" Add Web Calendar """
ret=True
data = {"url":"%s" % webCalendarURL}
data = "url=%s" % webCalendarURL
calendarBaseURL="/SOGo/so/%s/Calendar" % username
addWebCalendarURL = "%s/addWebCalendar" % calendarBaseURL
response = self._urlPostData(self.client, addWebCalendarURL, data)
respJSON = simplejson.loads(response['body'])
calID = respJSON['id']
folderID = respJSON['folderID']
#sogo1:Calendar/C07-5006F300-1-370E2480
(_, calID) = folderID.split('/', 1)
self.assertNotEqual(calID, None)
# reload the cal
-2
View File
@@ -22,8 +22,6 @@ $(TEST_TOOL)_OBJC_FILES += \
TestSBJsonParser.m \
\
TestNGMimeAddressHeaderFieldGenerator.m \
TestNGMimeMessageGenerator.m \
\
TestNSData+Crypto.m \
TestNSString+Crypto.m \
TestNSString+URLEscaping.m \
-4
View File
@@ -25,8 +25,6 @@
#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSData.h>
#import <Foundation/NSValue.h>
@class NSArray;
@@ -54,8 +52,6 @@
- (BOOL) run;
- (NSString*) stringFromDiffBetween: (NSString*) str1
and: (NSString*) str2;
@end
#define test(c) { \
-75
View File
@@ -185,79 +185,4 @@ static NSString *SOGoTestAssertException = @"SOGoTestAssertException";
return YES;
}
/* Helper function for diffForString:andString */
NSString *_stringForCharacterAtIndex(NSUInteger index, NSString *str, NSUInteger length)
{
NSString *chrStr;
unichar chr;
if (index < length)
{
chr = [str characterAtIndex: index];
if (isprint(chr))
{
chrStr = [NSString stringWithFormat: @"%c", chr];
}
else
{
if (chr == 10)
chrStr = @"[NL]";
else if (chr == 0)
chrStr = @"[\0]";
else
chrStr = [NSString stringWithFormat: @"[NP: %u]", chr];
}
}
else
{
chrStr = @"[none]";
}
return chrStr;
}
/*
Returns a string with a very verbose diff of the two strings.
In case the strings are equal it returns an empty string.
Example output for the strings 'flower' and 'flotera':
<begin of example>
0 |f|
1 |l|
2 |o|
3 |w|t|<--
4 |e|
5 |r|
6 |[none]|a|<--
<end of example>
*/
- (NSString*) stringFromDiffBetween: (NSString*) str1
and: (NSString*) str2
{
BOOL differencesFound = NO;
NSString *finalSTR = @"";
NSUInteger i, length1, length2;
NSString *sc1, *sc2;
length1 = [str1 length];
length2 = [str2 length];
for (i = 0; i < length1 || i < length2; i++)
{
sc1 = _stringForCharacterAtIndex(i, str1, length1);
sc2 = _stringForCharacterAtIndex(i, str2, length2);
if ([sc1 isEqualToString: sc2])
finalSTR = [finalSTR stringByAppendingFormat: @"%u |%@|\n", i, sc1];
else
{
finalSTR = [finalSTR stringByAppendingFormat: @"%u |%@|%@|<--\n", i, sc1, sc2];
differencesFound = YES;
}
}
if (!differencesFound)
return @"";
return finalSTR;
}
@end
-134
View File
@@ -1,134 +0,0 @@
/* TestNGMimeMessageGenerator.m - this file is part of SOGo
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#import "SOGoTest.h"
#import <NGMail/NGMimeMessageGenerator.h>
@interface TestNGMimeMessageGenerator : SOGoTest
@end
@implementation TestNGMimeMessageGenerator
- (void) test_generateDataForHeaderField_value
{
NGMimeMessageGenerator *generator;
NSArray *cases = [NSArray arrayWithObjects:
[NSArray arrayWithObjects: @"Message-ID", @"<CADCKkzo+9X1SniJFY3yc7YGafNrmAts419RmcqNkMzd-PBqNbA@mail.gmail.com>", @"<CADCKkzo+9X1SniJFY3yc7YGafNrmAts419RmcqNkMzd-PBqNbA@mail.gmail.com>", nil],
[NSArray arrayWithObjects: @"Content-Type",
@"text/plain; charset=utf-8; format=flowed",
@"text/plain; charset=utf-8; format=flowed",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderOneHebrewOneLatin",
@"עs",
@"=?utf-8?q?=D7=A2s?=",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderOneLatineOneHebrew",
@"sע",
@"=?utf-8?q?s=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderOneCharacterHebrew",
@"ע",
@"=?utf-8?q?=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderOneCharacterRussian",
@"Б",
@"=?utf-8?q?=D0=91?=",
nil],
[NSArray arrayWithObjects: @"X-FullHeaderParameter",
@"parameter=ע",
@"parameter==?utf-8?q?=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-MixedHeaderParameters",
@"plain; parameter=ע; parameter-plain; parameter2=ea",
@"plain;\n parameter==?utf-8?q?=D7=A2?=;\n parameter-plain; parameter2=ea",
nil],
[NSArray arrayWithObjects: @"X-MixedHeaderAndNoParameter",
@"plain; parameter=ע; parameter-plain; ע",
@"plain;\n parameter==?utf-8?q?=D7=A2?=; parameter-plain;\n =?utf-8?q?=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-MixedHeaderAndTwoParameter",
@"plain; parameter=ע; parameter-plain; z=ע",
@"plain;\n parameter==?utf-8?q?=D7=A2?=; parameter-plain;\n z==?utf-8?q?=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"X-MixedHeaderExtrablanks",
@"plain; parameter=ע; parameter 2spaces; parameter2=ea",
@"plain;\n \\ parameter==?utf-8?q?=D7=A2?=;\n parameter 2spaces; parameter2=ea",
nil],
[NSArray arrayWithObjects: @"X-Encoded-Unbalanced-Paramter-Quote",
@"text/plain; name=\"ע",
@"text/plain;\n name==?utf-8?q?=22=D7=A2?=",
nil],
[NSArray arrayWithObjects: @"content-type",
@"text/plain; name=\"АБВГДЕЁЖЗИЙ, КЛМНОПРСТУФ y ЦЧШЩЪЫЬЭЮЯ.txt\"",
@"text/plain;\n name=\"=?utf-8?q?=D0=90=D0=91=D0=92=D0=93=D0=94=D0=95=D0=81=D0=96=D0=97=D0=98=D0=99=2C_=D0=9A=D0=9B=D0=9C=D0=9D=D0=9E=D0=9F=D0=A0=D0=A1=D0=A2=D0=A3=D0=A4_y_=D0=A6=D0=A7=D0=A8=D0=A9=D0=AA=D0=AB=D0=AC=D0=AD=D0=AE=D0=AF=2Etxt?=\"",
nil],
[NSArray arrayWithObjects: @"content-disposition",
@"attachment; filename=\"АБВГДЕЁЖЗИЙ, КЛМНОПРСТУФ y ЦЧШЩЪЫЬЭЮЯ.txt\"",
@"attachment;\n filename=\"=?utf-8?q?=D0=90=D0=91=D0=92=D0=93=D0=94=D0=95=D0=81=D0=96=D0=97=D0=98=D0=99=2C_=D0=9A=D0=9B=D0=9C=D0=9D=D0=9E=D0=9F=D0=A0=D0=A1=D0=A2=D0=A3=D0=A4_y_=D0=A6=D0=A7=D0=A8=D0=A9=D0=AA=D0=AB=D0=AC=D0=AD=D0=AE=D0=AF=2Etxt?=\"",
nil],
[NSArray arrayWithObjects: @"content-length", @"2912", @"2912", nil],
[NSArray arrayWithObjects: @"content-transfer-encoding", @"quoted-printable", @"quoted-printable", nil],
nil
];
NSEnumerator *enumerator;
NSArray *testCase;
[NGMimeMessageGenerator initialize];
generator = [[NGMimeMessageGenerator alloc] init];
[generator autorelease];
enumerator = [cases objectEnumerator];
while ((testCase = [enumerator nextObject]) != nil)
{
NSData *result;
NSMutableData *resultWithNulByte;
NSString *header = [testCase objectAtIndex: 0];
NSData *headerData = [testCase objectAtIndex: 1];
NSString *expected = [testCase objectAtIndex: 2];
result = [generator generateDataForHeaderField: header
value: headerData];
if (result == nil)
result = [@"[nil]" dataUsingEncoding: NSUTF8StringEncoding];
resultWithNulByte = [result mutableCopy];
[resultWithNulByte appendBytes: "\0" length: 1];
NSString *resultString = [NSString stringWithCString:[resultWithNulByte bytes]];
BOOL testResult = [resultString isEqualToString: expected];
NSString *diff = [self stringFromDiffBetween: [NSString stringWithString: resultString]
and: [NSString stringWithString: expected]];
NSString *testErrorMsg = [NSString
stringWithFormat: @">> For %@ header received:\n%@[END]\n>> instead of:\n%@[END]\n>> for:\n%@\n>> diff:\n%@\n>> lengthReceived: %u lengthExpected: %u",
header,
resultString,
expected,
headerData,
diff,
[resultString length],
[expected length]
];
testWithMessage(testResult, testErrorMsg);
}
}
@end
+1 -1
View File
@@ -35,7 +35,7 @@
{
const char *inStrings[] = { "éléphant", "2š", "sogo+test", NULL };
const char **inString;
NSString *outStrings[] = { @"%C3%A9l%C3%A9phant", @"2%C5%A1", @"sogo%2Btest" };
NSString *outStrings[] = { @"%C3%A9l%C3%A9phant", @"2%C5%A1", @"sogo+test" };
NSString **outString;
NSString *result, *error;
+1 -9
View File
@@ -116,19 +116,13 @@ static NSURL *tableURL = nil;
- (void) _removeUserFolders: (NSString *) userId
{
NSArray *folderPaths;
NSString *path;
int count, max;
folderPaths = [self _userFolderPaths: userId];
max = [folderPaths count];
if (max > 0)
for (count = 0; count < max; count++)
{
path = [folderPaths objectAtIndex: count];
[fm deleteFolderAtPath: path];
if (verbose)
NSLog (@"Deleting %@", path);
}
[fm deleteFolderAtPath: [folderPaths objectAtIndex: count]];
else
NSLog (@"No folder returned for user '%@'", userId);
}
@@ -150,8 +144,6 @@ static NSURL *tableURL = nil;
userId];
if ([fc evaluateExpressionX: sql])
NSLog (@"Unable to delete the preference record for '%@'", userId);
else if (verbose)
NSLog (@"Removed preference record for '%@'", userId);
[cm releaseChannel: fc];
}
}
-5
View File
@@ -46,11 +46,6 @@
[super dealloc];
}
- (NSString *) modulePath
{
return @"Administration";
}
- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request
inContext: (WOContext*) context
{
+8 -10
View File
@@ -408,17 +408,16 @@
- (id <WOActionResults>) copyAction
{
WORequest *request;
id <WOActionResults> response;
NSString *destinationFolderId;
NSArray *contactsId;
NSDictionary *data;
NSException *ex;
data = [[[context request] contentAsString] objectFromJSONString];
contactsId = [data objectForKey: @"uids"];
destinationFolderId = [data objectForKey: @"folder"];
request = [context request];
if (destinationFolderId && [contactsId count])
if ((destinationFolderId = [request formValueForKey: @"folder"]) &&
(contactsId = [request formValuesForKey: @"uid"]))
ex = [self _moveContacts: contactsId
toFolder: destinationFolderId
andKeepCopy: YES];
@@ -437,17 +436,16 @@
- (id <WOActionResults>) moveAction
{
WORequest *request;
id <WOActionResults> response;
NSString *destinationFolderId;
NSDictionary *data;
NSArray *contactsId;
NSException *ex;
data = [[[context request] contentAsString] objectFromJSONString];
contactsId = [data objectForKey: @"uids"];
destinationFolderId = [data objectForKey: @"folder"];
request = [context request];
if (destinationFolderId && [contactsId count])
if ((destinationFolderId = [request formValueForKey: @"folder"])
&& (contactsId = [request formValuesForKey: @"uid"]))
ex = [self _moveContacts: contactsId
toFolder: destinationFolderId
andKeepCopy: NO];
+42 -7
View File
@@ -249,7 +249,14 @@
- (NSString *) commonLocalizableStrings
{
return [NSString stringWithFormat: @"var clabels = %@;", [self _stringsForFramework: nil]];
NSString *rc;
if (isPopup)
rc = @"";
else
rc = [NSString stringWithFormat: @"var clabels = %@;", [self _stringsForFramework: nil]];
return rc;
}
- (NSString *) productLocalizableStrings
@@ -433,6 +440,40 @@
return ([[self productCSSURL] length] > 0);
}
- (BOOL) _moduleIs: (NSString *) moduleName
{
NSString *frameworkName;
frameworkName = [[context page] frameworkName];
return [frameworkName isEqualToString: moduleName];
}
- (BOOL) isCalendar
{
return [self _moduleIs: @"SchedulerUI"];
}
- (BOOL) isContacts
{
return [self _moduleIs: @"ContactsUI"];
}
- (BOOL) isMail
{
return [self _moduleIs: @"MailerUI"];
}
- (BOOL) isPreferences
{
return [self _moduleIs: @"PreferencesUI"];
}
- (BOOL) isAdministration
{
return [self _moduleIs: @"AdministrationUI"];
}
- (void) setToolbar: (NSString *) newToolbar
{
ASSIGN (toolbar, newToolbar);
@@ -468,12 +509,6 @@
return [[context activeUser] cn];
}
- (NSString *) userEmail
{
return [[context activeUser] systemEmail];
}
- (BOOL) canLogoff
{
BOOL canLogoff;
@@ -43,24 +43,6 @@
"Copy To" = "Copy To";
"Add to:" = "Add to:";
/* Subheader of empty addressbook */
"No contact" = "No contact";
/* Number of contacts in addressbook; string is prefixed by number */
"contacts" = "contacts";
/* No contact matching search criteria */
"No matching contact" = "No matching contact";
/* Number of contacts matching search criteria; string is prefixed by number */
"matching contacts" = "matching contacts";
/* Number of selected contacts in list */
"selected" = "selected";
/* Empty right pane */
"No contact selected" = "No contact selected";
/* Tooltips */
"Create a new address book card" = "Create a new address book card";
+6 -11
View File
@@ -367,8 +367,6 @@ static Class SOGoContactGCSEntryK = Nil;
day = [date dayOfMonth];
[card setBday: [NSString stringWithFormat: @"%.4d%.2d%.2d", year, month, day]];
}
else
[card setBday: nil];
if ([[attributes objectForKey: @"addresses"] isKindOfClass: [NSArray class]])
{
@@ -432,9 +430,8 @@ static Class SOGoContactGCSEntryK = Nil;
{
if ([attrs isKindOfClass: [NSDictionary class]])
{
[card addElementWithTag: @"tel"
ofType: [attrs objectForKey: @"type"]
withValue: [attrs objectForKey: @"value"]];
element = [card elementWithTag: @"tel" ofType: [attrs objectForKey: @"type"]];
[element setSingleValue: [attrs objectForKey: @"value"] forKey: @""];
}
}
}
@@ -451,9 +448,8 @@ static Class SOGoContactGCSEntryK = Nil;
{
if ([o isKindOfClass: [NSDictionary class]])
{
[card addElementWithTag: @"email"
ofType: [o objectForKey: @"type"]
withValue: [o objectForKey: @"value"]];
element = [card elementWithTag: @"email" ofType: [o objectForKey: @"type"]];
[element setSingleValue: [o objectForKey: @"value"] forKey: @""];
}
}
}
@@ -470,9 +466,8 @@ static Class SOGoContactGCSEntryK = Nil;
{
if ([attrs isKindOfClass: [NSDictionary class]])
{
[card addElementWithTag: @"url"
ofType: [attrs objectForKey: @"type"]
withValue: [attrs objectForKey: @"value"]];
element = [card elementWithTag: @"url" ofType: [attrs objectForKey: @"type"]];
[element setSingleValue: [attrs objectForKey: @"value"] forKey: @""];
}
}
}
+13 -7
View File
@@ -112,11 +112,15 @@
request = [context request];
rc = [NSMutableDictionary dictionary];
data = [[[[[request httpRequest] body] parts] lastObject] body];
fileContent = [[NSString alloc] initWithData: (NSData *) data
encoding: NSUTF8StringEncoding];
[fileContent autorelease];
data = [request formValueForKey: @"contactsFile"];
if ([data respondsToSelector: @selector(isEqualToString:)])
fileContent = (NSString *) data;
else
{
fileContent = [[NSString alloc] initWithData: (NSData *) data
encoding: NSUTF8StringEncoding];
[fileContent autorelease];
}
if (fileContent && [fileContent length])
{
@@ -128,10 +132,12 @@
imported = 0;
}
[rc setObject: [NSNumber numberWithInt: imported] forKey: @"imported"];
[rc setObject: [NSNumber numberWithInt: imported]
forKey: @"imported"];
response = [self responseWithStatus: 200];
[response setHeader: @"text/html" forKey: @"content-type"];
[response setHeader: @"text/html"
forKey: @"content-type"];
[(WOResponse*)response appendContentString: [rc jsonRepresentation]];
return response;
@@ -20,6 +20,14 @@
#import <SOGoUI/UIxComponent.h>
@class NSString;
@class SOGoContactGCSFolder;
@interface UIxContactFolderLinksTemplate : UIxComponent
{
SOGoContactGCSFolder *addressBook;
NSString *baseCardDAVURL, *basePublicCardDAVURL;
}
@end
@@ -24,6 +24,69 @@
@implementation UIxContactFolderLinksTemplate
- (id) init
{
if ((self = [super init]))
{
addressBook = [self clientObject];
baseCardDAVURL = nil;
basePublicCardDAVURL = nil;
}
return self;
}
- (void) dealloc
{
[baseCardDAVURL release];
[basePublicCardDAVURL release];
[super dealloc];
}
- (NSString *) _baseCardDAVURL
{
NSString *davURL;
if (!baseCardDAVURL)
{
davURL = [[addressBook realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCardDAVURL = davURL;
[baseCardDAVURL retain];
}
return baseCardDAVURL;
}
- (NSString *) cardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCardDAVURL]];
}
- (NSString *) _basePublicCardDAVURL
{
NSString *davURL;
if (!basePublicCardDAVURL)
{
davURL = [[addressBook publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCardDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCardDAVURL = davURL;
[basePublicCardDAVURL retain];
}
return basePublicCardDAVURL;
}
- (NSString *) publicCardDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCardDAVURL]];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
+11 -48
View File
@@ -71,11 +71,6 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
return self;
}
- (NSString *) modulePath
{
return @"Contacts";
}
- (void) _setupContext
{
SOGoUser *activeUser;
@@ -280,70 +275,32 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
return [[self queryParameterForKey: @"popup"] boolValue];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
return [[SOGoSystemDefaults sharedSystemDefaults] enablePublicAccess];
}
- (NSString *) contactFolders
{
SOGoContactFolders *folderContainer;
NSMutableDictionary *urls, *acls;
NSArray *folders;
NSMutableArray *foldersAttrs;
NSString *userLogin, *owner;
NSArray *folders, *allACLs;
NSDictionary *folderAttrs;
id currentFolder;
BOOL objectCreator, objectEditor, objectEraser;
int max, i;
userLogin = [[context activeUser] login];
folderContainer = [self clientObject];
folders = [folderContainer subFolders];
// return [folderContainer subFolders];
folders = [folderContainer subFolders];
max = [folders count];
foldersAttrs = [NSMutableArray arrayWithCapacity: max];
urls = nil;
for (i = 0; i < max; i++)
{
currentFolder = [folders objectAtIndex: i];
owner = [currentFolder ownerInContext: context];
// We extract URLs for this address book
if ([currentFolder respondsToSelector: @selector(cardDavURL)])
{
urls = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[currentFolder cardDavURL], @"cardDavURL",
nil];
if ([self isPublicAccessEnabled])
{
[urls setObject: [currentFolder publicCardDavURL] forKey: @"publicCardDavURL"];
}
}
// We extract ACLs for this address book
allACLs = ([owner isEqualToString: userLogin] ? nil : [currentFolder aclsForUser: userLogin]);
objectCreator = ([owner isEqualToString: userLogin] || [allACLs containsObject: SOGoRole_ObjectCreator]);
objectEditor = ([owner isEqualToString: userLogin] || [allACLs containsObject: SOGoRole_ObjectEditor]);
objectEraser = ([owner isEqualToString: userLogin] || [allACLs containsObject: SOGoRole_ObjectEraser]);
acls = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool: objectCreator], @"objectCreator",
[NSNumber numberWithBool: objectEditor], @"objectEditor",
[NSNumber numberWithBool: objectEraser], @"objectEraser", nil];
// NOTE: keep urls as the last key/value here, to avoid chopping the dictionary
// if it is not a GCS folder
folderAttrs = [NSDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithFormat: @"%@", [currentFolder nameInContainer]], @"id",
[currentFolder displayName], @"name",
owner, @"owner",
[currentFolder ownerInContext: context], @"owner",
[NSNumber numberWithBool: [currentFolder isKindOfClass: SOGoGCSFolderK]], @"isEditable",
[NSNumber numberWithBool: [currentFolder isKindOfClass: SOGoContactSourceFolderK]
&& ![currentFolder isPersonalSource]], @"isRemote",
acls, @"acls",
urls, @"urls",
nil];
[foldersAttrs addObject: folderAttrs];
}
@@ -462,6 +419,12 @@ Class SOGoContactSourceFolderK, SOGoGCSFolderK;
return [super defaultAction];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
return [[SOGoSystemDefaults sharedSystemDefaults] enablePublicAccess];
}
@end
@interface UIxContactViewTemplate : UIxComponent
+4 -3
View File
@@ -838,10 +838,11 @@
o = [card birthday];
if (o)
{
[data setObject: [NSNumber numberWithInt: [o timeIntervalSince1970]]
forKey: @"birthday"];
NSNumber *time = [NSNumber numberWithInt: [o timeIntervalSince1970]];
[data setObject: time forKey: @"birthday"];
}
// o = [card source];
// if (o) [data setObject: o forKey: @"source"];
o = [card tz];
if (o) [data setObject: o forKey: @"tz"];
+1 -31
View File
@@ -1,5 +1,5 @@
/*
Copyright (C) 2006-2015 Inverse inc.
Copyright (C) 2006-2014 Inverse inc.
This file is part of SOGo.
@@ -39,9 +39,6 @@
#import <Contacts/SOGoContactFolder.h>
#import <Contacts/SOGoContactFolders.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserSettings.h>
#import <NGCards/NGVCard.h>
#import <NGCards/NGVList.h>
#import <SoObjects/Contacts/SOGoContactGCSEntry.h>
@@ -88,31 +85,6 @@
return s;
}
- (void) saveSortValue
{
NSMutableDictionary *contactSettings;
NSString *ascending, *sort;
SOGoUserSettings *us;
sort = [[context request] formValueForKey: @"sort"];
ascending = [[context request] formValueForKey: @"asc"];
if ([sort length])
{
us = [[context activeUser] userSettings];
contactSettings = [us objectForKey: @"Contact"];
// Must create if it doesn't exist
if (!contactSettings)
{
contactSettings = [NSMutableDictionary dictionary];
[us setObject: contactSettings forKey: @"Contact"];
}
[contactSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", [ascending intValue]], nil]
forKey: @"SortingState"];
[us synchronize];
}
}
- (NSArray *) contactInfos
{
id <SOGoContactFolder> folder;
@@ -125,8 +97,6 @@
WORequest *rq;
unsigned int i;
[self saveSortValue];
if (!contactInfos)
{
folder = [self clientObject];
+3 -1
View File
@@ -1,6 +1,8 @@
/* UIxMailPartTextViewer.h - this file is part of $PROJECT_NAME_HERE$
*
* Copyright (C) 2006-2015 Inverse inc.
* Copyright (C) 2006 Inverse inc.
*
* Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
+20 -19
View File
@@ -1,22 +1,23 @@
/* UIxMailPartTextViewer.m - this file is part of $PROJECT_NAME_HERE$
*
* Copyright (C) 2006-2015 Inverse inc.
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
OGo is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public
License along with OGo; see the file COPYING. If not, write to the
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
/*
UIxMailPartTextViewer
@@ -65,9 +65,6 @@
"Account: " = "Account: ";
"Shared Account: " = "Shared Account: ";
/* Empty right pane */
"No message selected" = "No message selected";
/* acls */
"Access rights to" = "Access rights to";
"For user" = "For user";
@@ -231,9 +228,6 @@
"Delete Message" = "Delete Message";
"Delete Selected Messages" = "Delete Selected Messages";
/* Number of selected messages in list */
"selected" = "selected";
"This Folder" = "This Folder";
/* Label popup menu */
+8 -15
View File
@@ -92,7 +92,7 @@
}
else
{
errorFormat = [self labelForKey: @"The folder with name \"%@\" could not be created." inContext: context];
errorFormat = [self labelForKey: @"The folder with name \"%@\" could not be created."];
jsonResponse = [NSDictionary dictionaryWithObject: [NSString stringWithFormat: errorFormat, folderName]
forKey: @"error"];
response = [self responseWithStatus: 500
@@ -101,7 +101,7 @@
}
else
{
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing 'name' parameter." inContext: context]
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing 'name' parameter."]
forKey: @"error"];
response = [self responseWithStatus: 500
andJSONRepresentation: jsonResponse];
@@ -149,7 +149,7 @@
if (!newFolderName || [newFolderName length] == 0)
{
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing name parameter" inContext: context]
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Missing name parameter"]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [message jsonRepresentation]];
@@ -160,7 +160,7 @@
error = [co renameTo: newFolderName];
if (error)
{
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to rename folder." inContext: context]
message = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to rename folder."]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [message jsonRepresentation]];
@@ -251,7 +251,7 @@
error = [connection moveMailboxAtURL: srcURL toURL: destURL];
if (error)
{
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder." inContext: context]
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder."]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [jsonResponse jsonRepresentation]];
@@ -283,7 +283,7 @@
}
else
{
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder." inContext: context]
jsonResponse = [NSDictionary dictionaryWithObject: [self labelForKey: @"Unable to move folder."]
forKey: @"error"];
response = [self responseWithStatus: 500
andString: [jsonResponse jsonRepresentation]];
@@ -374,7 +374,7 @@
{
uids = [value componentsSeparatedByString: @","];
response = [co archiveUIDs: uids
inArchiveNamed: [self labelForKey: @"Saved Messages.zip" inContext: context]
inArchiveNamed: [self labelForKey: @"Saved Messages.zip"]
inContext: context];
if (!response)
response = [self responseWith204];
@@ -739,7 +739,6 @@
NSDictionary *content, *result;
BOOL addOrRemove;
NGImap4Client *client;
id flag;
int i;
@@ -752,13 +751,7 @@
// We unescape our flags
for (i = [flags count]-1; i >= 0; i--)
{
flag = [flags objectAtIndex: i];
if ([flag isKindOfClass: [NSString class]])
[flags replaceObjectAtIndex: i withObject: [flag fromCSSIdentifier]];
else
[flags removeObjectAtIndex: i];
}
[flags replaceObjectAtIndex: i withObject: [[flags objectAtIndex: i] fromCSSIdentifier]];
co = [self clientObject];
client = [[co imap4Connection] client];
+13 -18
View File
@@ -55,7 +55,6 @@
#import <Mailer/SOGoSentFolder.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSDictionary+Utilities.h>
#import <SOGo/NSObject+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoDateFormatter.h>
#import <SOGo/SOGoUser.h>
@@ -132,7 +131,7 @@
else if ([now dayOfCommonEra] - [messageDate dayOfCommonEra] == 1)
{
// Yesterday
return [self labelForKey: @"Yesterday" inContext: context];
return [self labelForKey: @"Yesterday"];
}
else if ([now dayOfCommonEra] - [messageDate dayOfCommonEra] < 7)
{
@@ -191,10 +190,10 @@
s = [[s substringFromIndex: r.location+1]
stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([s hasPrefix: @"1"]) result = [self labelForKey: @"highest" inContext: context];
else if ([s hasPrefix: @"2"]) result = [self labelForKey: @"high" inContext: context];
else if ([s hasPrefix: @"4"]) result = [self labelForKey: @"low" inContext: context];
else if ([s hasPrefix: @"5"]) result = [self labelForKey: @"lowest" inContext: context];
if ([s hasPrefix: @"1"]) result = [self labelForKey: @"highest"];
else if ([s hasPrefix: @"2"]) result = [self labelForKey: @"high"];
else if ([s hasPrefix: @"4"]) result = [self labelForKey: @"low"];
else if ([s hasPrefix: @"5"]) result = [self labelForKey: @"lowest"];
}
}
}
@@ -243,7 +242,7 @@
{
NSString *s;
s = [self labelForKey:@"View Mail Folder" inContext: context];
s = [self labelForKey:@"View Mail Folder"];
s = [s stringByAppendingString:@": "];
s = [s stringByAppendingString:[self objectTitle]];
return s;
@@ -623,12 +622,11 @@
- (NSDictionary *) getUIDsInFolder: (SOGoMailFolder *) folder
withHeaders: (BOOL) includeHeaders
{
NSArray *uids, *threadedUids, *headers;
NSMutableDictionary *data;
NSArray *uids, *threadedUids, *headers;
NSRange r;
SOGoMailAccount *account;
id quota;
NSRange r;
int count;
data = [NSMutableDictionary dictionary];
@@ -646,14 +644,10 @@
if (includeHeaders)
{
// Also retrieve the first headers, up to 'headersPrefetchMaxSize'
NSArray *a;
a = [uids flattenedArray];
count = [a count];
if (count > headersPrefetchMaxSize)
count = headersPrefetchMaxSize;
count = [[uids flattenedArray] count];
if (count > headersPrefetchMaxSize) count = headersPrefetchMaxSize;
r = NSMakeRange(0, count);
headers = [self getHeadersForUIDs: [a subarrayWithRange: r]
headers = [self getHeadersForUIDs: [[uids flattenedArray] subarrayWithRange: r]
inFolder: folder];
[data setObject: headers forKey: @"headers"];
@@ -750,6 +744,7 @@
noHeaders = [[[requestContent objectForKey: @"sortingAttributes"] objectForKey:@"noHeaders"] boolValue];
data = [self getUIDsInFolder: folder
withHeaders: !noHeaders];
[response appendContentString: [data jsonRepresentation]];
return response;
@@ -883,7 +878,7 @@
data = [NSDictionary dictionaryWithObjectsAndKeys:
@"No UID specified", @"error", nil];
return [self responseWithStatus: 404 /* Not Found */
andString: [data jsonRepresentation]];
andString: [data jsonRepresentation]];
}
uids = [data objectForKey: @"uids"];
-17
View File
@@ -87,11 +87,6 @@
[super dealloc];
}
- (NSString *) modulePath
{
return @"Mail";
}
- (void) _setupContext
{
SOGoUser *activeUser;
@@ -123,18 +118,6 @@
return [accounts jsonRepresentation];
}
- (WOResponse *) mailAccountsAction
{
WOResponse *response;
NSString *s;
s = [self mailAccounts];
response = [self responseWithStatus: 200
andString: s];
return response;
}
- (NSString *) userNames
{
NSArray *accounts, *userNames;
-9
View File
@@ -342,19 +342,10 @@
actionClass = "UIxMailMainFrame";
actionName = "unseenCount";
};
mailAccounts = {
protectedBy = "View";
actionClass = "UIxMailMainFrame";
actionName = "mailAccounts";
};
// viewerTemplate = {
// protectedBy = "View";
// pageName = "UIxMailViewTemplate";
// };
UIxMailPopupView = {
protectedBy = "View";
pageName = "UIxMailPopupView";
};
UIxMailEditor = {
protectedBy = "View";
pageName = "UIxMailEditor";
@@ -9,7 +9,6 @@
"Connect" = "Connect";
"Authentication Failed" = "Authentication Failed";
"Wrong username or password." = "Wrong username or password.";
"cookiesNotEnabled" = "You cannot login because your browser's cookies are disabled. Please enable cookies in your browser's settings and try again.";
+6 -7
View File
@@ -183,6 +183,7 @@
SOGoWebAuthenticator *auth;
SOGoAppointmentFolders *calendars;
SOGoUserDefaults *ud;
SOGoSystemDefaults *sd;
SOGoUser *loggedInUser;
NSDictionary *params;
NSString *username, *password, *language, *domain, *remoteHost;
@@ -225,15 +226,13 @@
response = [self responseWithStatus: 200
andJSONRepresentation: json];
// We get the proper username for cookie creation. If we are using a multidomain
// environment with SOGoEnableDomainBasedUID, we could have to append the domain
// to the username. Also when SOGoEnableDomainBasedUID is enabled, we could be in
// the DomainLessLogin situation, so we would NOT add the domain. -getUIDForEmail
// has all the logic for this, so lets use it.
if ([domain isNotNull])
{
username = [[SOGoUserManager sharedUserManager] getUIDForEmail: username];
sd = [SOGoSystemDefaults sharedSystemDefaults];
if ([sd enableDomainBasedUID] &&
[username rangeOfString: @"@"].location == NSNotFound)
username = [NSString stringWithFormat: @"%@@%@", username, domain];
}
authCookie = [auth cookieWithUsername: username
@@ -1,7 +1,6 @@
/* toolbar */
"Save and Close" = "Save and Close";
"Close" = "Close";
"Preferences saved" = "Preferences saved";
/* tabs */
"General" = "General";
+20 -35
View File
@@ -61,8 +61,7 @@ static SoProduct *preferencesProduct = nil;
SOGoUserDefaults *defaults;
NSMutableArray *accounts;
NSArray *categoryLabels;
NSDictionary *locale;
if (!preferencesProduct)
{
preferencesProduct = [[SoProduct alloc] initWithBundle:
@@ -79,7 +78,7 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoTimeZone"])
[[defaults source] setObject: [defaults timeZoneName] forKey: @"SOGoTimeZone"];
if (![[defaults source] objectForKey: @"SOGoShortDateFormat"])
[[defaults source] setObject: [defaults shortDateFormat] forKey: @"SOGoShortDateFormat"];
@@ -91,7 +90,7 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoLoginModule"])
[[defaults source] setObject: [defaults loginModule] forKey: @"SOGoLoginModule"];
if (![[defaults source] objectForKey: @"SOGoRefreshViewCheck"])
[[defaults source] setObject: [defaults refreshViewCheck] forKey: @"SOGoRefreshViewCheck"];
@@ -100,13 +99,13 @@ static SoProduct *preferencesProduct = nil;
//
if (![[defaults source] objectForKey: @"SOGoFirstDayOfWeek"])
[[defaults source] setObject: [NSNumber numberWithInt: [defaults firstDayOfWeek]] forKey: @"SOGoFirstDayOfWeek"];
if (![[defaults source] objectForKey: @"SOGoDayStartTime"])
[[defaults source] setObject: [NSString stringWithFormat: @"%02d:00", [defaults dayStartHour]] forKey: @"SOGoDayStartTime"];
if (![[defaults source] objectForKey: @"SOGoDayEndTime"])
[[defaults source] setObject: [NSString stringWithFormat: @"%02d:00", [defaults dayEndHour]] forKey: @"SOGoDayEndTime"];
if (![[defaults source] objectForKey: @"SOGoFirstWeekOfYear"])
[[defaults source] setObject: [defaults firstWeekOfYear] forKey: @"SOGoFirstWeekOfYear"];
@@ -115,20 +114,20 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoCalendarEventsDefaultClassification"])
[[defaults source] setObject: [defaults calendarEventsDefaultClassification] forKey: @"SOGoCalendarEventsDefaultClassification"];
if (![[defaults source] objectForKey: @"SOGoCalendarTasksDefaultClassification"])
[[defaults source] setObject: [defaults calendarTasksDefaultClassification] forKey: @"SOGoCalendarTasksDefaultClassification"];
if (![[defaults source] objectForKey: @"SOGoCalendarDefaultReminder"])
[[defaults source] setObject: [defaults calendarDefaultReminder] forKey: @"SOGoCalendarDefaultReminder"];
// Populate default calendar categories, based on the user's preferred language
if (![defaults calendarCategories])
{
categoryLabels = [[[self labelForKey: @"calendar_category_labels" withResourceManager: [preferencesProduct resourceManager]]
componentsSeparatedByString: @","]
sortedArrayUsingSelector: @selector (localizedCaseInsensitiveCompare:)];
[defaults setCalendarCategories: categoryLabels];
}
if (![defaults calendarCategoriesColors])
@@ -144,7 +143,7 @@ static SoProduct *preferencesProduct = nil;
[defaults setCalendarCategoriesColors: colors];
}
//
// Default Contacts preferences
//
@@ -155,10 +154,10 @@ static SoProduct *preferencesProduct = nil;
categoryLabels = [[[self labelForKey: @"contacts_category_labels" withResourceManager: [preferencesProduct resourceManager]]
componentsSeparatedByString: @","]
sortedArrayUsingSelector: @selector (localizedCaseInsensitiveCompare:)];
if (!categoryLabels)
categoryLabels = [NSArray array];
[defaults setContactsCategories: categoryLabels];
}
@@ -173,27 +172,27 @@ static SoProduct *preferencesProduct = nil;
if (![[defaults source] objectForKey: @"SOGoMailReplyPlacement"])
[[defaults source] setObject: [defaults mailReplyPlacement] forKey: @"SOGoMailReplyPlacement"];
if (![[defaults source] objectForKey: @"SOGoMailSignaturePlacement"])
[[defaults source] setObject: [defaults mailSignaturePlacement] forKey: @"SOGoMailSignaturePlacement"];
if (![[defaults source] objectForKey: @"SOGoMailComposeMessageType"])
[[defaults source] setObject: [defaults mailComposeMessageType] forKey: @"SOGoMailComposeMessageType"];
if (![[defaults source] objectForKey: @"SOGoMailDisplayRemoteInlineImages"])
[[defaults source] setObject: [defaults mailDisplayRemoteInlineImages] forKey: @"SOGoMailDisplayRemoteInlineImages"];
// Populate default mail lablels, based on the user's preferred language
if (![[defaults source] objectForKey: @"SOGoMailLabelsColors"])
{
SOGoMailLabel *label;
NSArray *labels;
id v;
int i;
v = [defaults mailLabelsColors];
labels = [SOGoMailLabel labelsFromDefaults: v component: self];
v = [NSMutableDictionary dictionary];
for (i = 0; i < [labels count]; i++)
@@ -202,7 +201,7 @@ static SoProduct *preferencesProduct = nil;
[v setObject: [NSArray arrayWithObjects: [label label], [label color], nil]
forKey: [label name]];
}
[defaults setMailLabelsColors: v];
}
@@ -215,10 +214,6 @@ static SoProduct *preferencesProduct = nil;
//
values = [[[[defaults source] values] mutableCopy] autorelease];
// Add locale code (used by CK Editor)
locale = [[preferencesProduct resourceManager] localeForLanguageNamed: [defaults language]];
[values setObject: [locale objectForKey: @"NSLocaleCode"] forKey: @"LocaleCode"];
accounts = [NSMutableArray arrayWithArray: [values objectForKey: @"AuxiliaryMailAccounts"]];
account = [[[context activeUser] mailAccounts] objectAtIndex: 0];
if (![account objectForKey: @"receipts"])
@@ -255,16 +250,6 @@ static SoProduct *preferencesProduct = nil;
forKey: @"PreventInvitationsWhitelist"];
}
// Initialize some default values
if (![settings objectForKey: @"Calendar"])
[settings setObject: [NSMutableDictionary dictionary] forKey: @"Calendar"];
if (![settings objectForKey: @"Contact"])
[settings setObject: [NSMutableDictionary dictionary] forKey: @"Contact"];
if (![settings objectForKey: @"Mail"])
[settings setObject: [NSMutableDictionary dictionary] forKey: @"Mail"];
return [self _makeResponse: [[settings source] values]];
}
+1 -10
View File
@@ -169,11 +169,6 @@ static NSArray *reminderValues = nil;
[super dealloc];
}
- (NSString *) modulePath
{
return @"Preferences";
}
// - (void) setHasChanged: (BOOL) newHasChanged
// {
// hasChanged = newHasChanged;
@@ -1069,7 +1064,7 @@ static NSArray *reminderValues = nil;
if (!capabilities)
{
if ([self isSieveScriptsEnabled] && [self _sieveClient])
if ([self _sieveClient])
capabilities = [[self _sieveClient] capabilities];
else
capabilities = [NSArray array];
@@ -2091,10 +2086,6 @@ static NSArray *reminderValues = nil;
// We convert our object into a mutable one
v = [[v mutableCopy] autorelease];
if ([[v objectForKey: @"SOGoLoginModule"] isEqualToString: @"Last"])
[v setObject: [NSNumber numberWithBool: YES] forKey: @"SOGoRememberLastModule"];
else
[v setObject: [NSNumber numberWithBool: NO] forKey: @"SOGoRememberLastModule"];
// We remove short/long date formats if they are default ones
if ([[v objectForKey: @"SOGoShortDateFormat"] isEqualToString: @"default"])
+30 -9
View File
@@ -360,19 +360,40 @@ static SoProduct *commonProduct = nil;
- (NSString *) modulePath
{
if ([[self parent] respondsToSelector: @selector(modulePath)])
SOGoObject *currentClient, *parent;
BOOL found;
NSString *hostLessURL;
Class objectClass, userFolderClass;
// , groupFolderClass
currentClient = [self clientObject];
if (currentClient
&& [currentClient isKindOfClass: [SOGoObject class]])
{
NSString *baseURL;
// groupFolderClass = [SOGoCustomGroupFolder class];
userFolderClass = [SOGoUserFolder class];
baseURL = [[self clientObject] baseURLInContext: context];
if ([baseURL hasSuffix: [NSString stringWithFormat: @"%@/", [[self parent] modulePath]]])
return baseURL;
return [NSString stringWithFormat: @"%@%@", baseURL, [[self parent] modulePath]];
objectClass = [currentClient class];
// found = (objectClass == groupFolderClass || objectClass == userFolderClass);
found = (objectClass == userFolderClass);
while (!found && currentClient)
{
parent = [currentClient container];
objectClass = [parent class];
if (// objectClass == groupFolderClass
// ||
objectClass == userFolderClass)
found = YES;
else
currentClient = parent;
}
}
else
currentClient = [WOApplication application];
hostLessURL = [[currentClient baseURLInContext: context] hostlessURL];
return @"SOGo";
return [hostLessURL substringToIndex: [hostLessURL length] - 1];
}
- (NSString *) ownPath
+1 -1
View File
@@ -39,7 +39,7 @@
NSArray *weekDays;
NSString *currentView, *timeFormat, *currentTableHour;
NSCalendarDate *startDate, *currentTableDay;
NSMutableArray *daysToDisplay, *calendarsToDisplay, *currentCalendar, *hoursToDisplay;
NSMutableArray *daysToDisplay, *calendarsToDisplay, *currentCalendar, *hoursToDisplay;
unsigned int numberOfDays;
}
-35
View File
@@ -37,9 +37,6 @@
#import <SOGo/SOGoUserDefaults.h>
#import <SOGo/WOResourceManager+SOGo.h>
#import <SoObjects/Appointments/SOGoAppointmentFolder.h>
#import <SoObjects/Appointments/SOGoAppointmentFolders.h>
#import "UIxCalDayTable.h"
@class SOGoAppointment;
@@ -295,28 +292,6 @@
return [weekDays objectAtIndex: [currentTableDay dayOfWeek]];
}
- (NSString *) labelForMonth
{
NSCalendarDate *nextDay;
BOOL isLastOrFirstDay;
isLastOrFirstDay = NO;
if ([currentTableDay dayOfMonth] == 1)
{
isLastOrFirstDay = YES;
}
else if ([currentTableDay dayOfMonth] > 27)
{
nextDay = [currentTableDay dateByAddingYears: 0
months: 0
days: 1];
if ([nextDay dayOfMonth] == 1)
isLastOrFirstDay = YES;
}
return isLastOrFirstDay? [currentTableDay descriptionWithCalendarFormat: @"%b" locale: locale] : nil;
}
- (NSString *) labelForDate
{
return [dateFormatter shortFormattedDate: currentTableDay];
@@ -435,11 +410,6 @@
return daysView;
}
- (NSString *) daysViewHeaderClasses
{
return [NSString stringWithFormat: @"%@ daysHeader", [self daysViewClasses]];
}
- (NSString *) dayClasses
{
NSMutableString *classes;
@@ -498,9 +468,4 @@
return NO;
}
- (BOOL) isWeekView
{
return [currentView isEqualToString:@"weekview"];
}
@end
+20 -49
View File
@@ -94,7 +94,7 @@ static NSArray *tasksFields = nil;
@"c_classification", @"c_category",
@"c_partmails", @"c_partstates", @"c_owner",
@"c_iscycle", @"c_nextalarm",
@"c_recurrence_id", @"isException", @"viewable", @"editable",
@"c_recurrence_id", @"isException", @"editable",
@"erasable", @"ownerIsOrganizer", @"c_description", nil];
[eventsFields retain];
}
@@ -104,7 +104,7 @@ static NSArray *tasksFields = nil;
@"calendarName",
@"c_status", @"c_title", @"c_enddate",
@"c_classification", @"c_location", @"c_category",
@"viewable", @"editable", @"erasable",
@"editable", @"erasable",
@"c_priority", @"c_owner",
@"c_iscycle", @"c_nextalarm",
@"c_recurrence_id", @"isException", @"c_description", nil];
@@ -258,7 +258,7 @@ static NSArray *tasksFields = nil;
labelKey = [NSString stringWithFormat: @"%@_class%@",
type, [component objectForKey: @"c_classification"]];
[component setObject: [self labelForKey: labelKey inContext: context]
[component setObject: [self labelForKey: labelKey]
forKey: @"c_title"];
}
@@ -441,35 +441,21 @@ static NSArray *tasksFields = nil;
}
else
{
currentInfos = [[currentFolder fetchCoreInfosFrom: startDate
id foo;
foo = [currentFolder fetchCoreInfosFrom: startDate
to: endDate
title: value
component: component]
objectEnumerator];
component: component];
currentInfos = [foo objectEnumerator];
}
owner = [currentFolder ownerInContext: context];
ownerUser = [SOGoUser userWithLogin: owner];
isErasable = ([owner isEqualToString: userLogin] || [[currentFolder aclsForUser: userLogin] containsObject: SOGoRole_ObjectEraser]);
while ((newInfo = [currentInfos nextObject]))
{
if ([fields containsObject: @"viewable"])
{
if ([owner isEqualToString: userLogin])
[newInfo setObject: [NSNumber numberWithInt: 1] forKey: @"viewable"];
else
{
role = [currentFolder roleForComponentsWithAccessClass: [[newInfo objectForKey: @"c_classification"] intValue]
forUser : userLogin];
if ([role isEqualToString: @"ComponentViewer"])
[newInfo setObject: [NSNumber numberWithInt: 1] forKey: @"viewable"];
else
[newInfo setObject: [NSNumber numberWithInt: 0] forKey: @"viewable"];
}
}
if ([fields containsObject: @"editable"])
{
if (folderIsRemote)
@@ -562,19 +548,19 @@ static NSArray *tasksFields = nil;
{
// Same day
if (forAllDay)
return [self labelForKey: @"Today" inContext: context];
return [self labelForKey: @"Today"];
else
return [dateFormatter formattedTime: date];
}
else if ([now dayOfCommonEra] - [date dayOfCommonEra] == 1)
{
// Yesterday
return [self labelForKey: @"Yesterday" inContext: context];
return [self labelForKey: @"Yesterday"];
}
else if ([date dayOfCommonEra] - [now dayOfCommonEra] == 1)
{
// Tomorrow
return [self labelForKey: @"Tomorrow" inContext: context];
return [self labelForKey: @"Tomorrow"];
}
else if (abs(delta = [date dayOfCommonEra] - [now dayOfCommonEra]) < 7)
{
@@ -587,7 +573,7 @@ static NSArray *tasksFields = nil;
return dayOfWeek;
else
// With the past 7 days
return [NSString stringWithFormat: [self labelForKey: @"last %@" inContext: context], dayOfWeek];
return [NSString stringWithFormat: [self labelForKey: @"last %@"], dayOfWeek];
}
else
{
@@ -666,11 +652,10 @@ static NSArray *tasksFields = nil;
return [self _responseWithData: data];
}
- (void) saveFilterValue: (NSString *) submodule
- (void) saveFilterValue
{
NSString *filter;
SOGoUserSettings *us;
NSMutableDictionary *calendarSettings;
filter = [[context request] formValueForKey: @"filterpopup"];
if ([filter length]
@@ -678,20 +663,12 @@ static NSArray *tasksFields = nil;
&& ![filter isEqualToString: @"view_future"])
{
us = [[context activeUser] userSettings];
calendarSettings = [us objectForKey: @"Calendar"];
// Must create if it doesn't exist
if (!calendarSettings)
{
calendarSettings = [NSMutableDictionary dictionary];
[us setObject: calendarSettings forKey: @"Calendar"];
}
//[us setObject: filter forKey: submodule];
[calendarSettings setObject: filter forKey: submodule];
[us setObject: filter forKey: @"CalendarDefaultFilter"];
[us synchronize];
}
}
- (void) saveSortValue: (NSString *) submodule
- (void) saveSortValue
{
NSString *sort, *ascending;
SOGoUserSettings *us;
@@ -709,8 +686,7 @@ static NSArray *tasksFields = nil;
calendarSettings = [NSMutableDictionary dictionary];
[us setObject: calendarSettings forKey: @"Calendar"];
}
[calendarSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", [ascending intValue]], nil]
forKey: submodule];
[calendarSettings setObject: [NSArray arrayWithObjects: [sort lowercaseString], [NSString stringWithFormat: @"%d", (ascending?1:0)], nil] forKey: @"EventsSortingState"];
[us synchronize];
}
}
@@ -746,11 +722,10 @@ static NSArray *tasksFields = nil;
* @apiSuccess (Success 200) {String[]} events.c_partmails Participants email addresses
* @apiSuccess (Success 200) {String[]} events.c_partstates Participants states
* @apiSuccess (Success 200) {String} events.c_owner Event's owner
* @apiSuccess (Success 200) {Number} events.c_iscycle 1 if the event is cyclic/recurring
* @apiSuccess (Success 200) {Number} events.c_iscycle 1 if the event is cyclic
* @apiSuccess (Success 200) {Number} events.c_nextalarm Epoch time of next alarm
* @apiSuccess (Success 200) {String} [events.c_recurrence_id] Recurrence ID if event is cyclic
* @apiSuccess (Success 200) {Number} events.isException 1 if recurrence is an exception
* @apiSuccess (Success 200) {Number} events.viewable 1 if active user can view the event
* @apiSuccess (Success 200) {Number} events.editable 1 if active user can edit the event
* @apiSuccess (Success 200) {Number} events.erasable 1 if active user can erase the event
* @apiSuccess (Success 200) {Number} events.ownerIsOrganizer 1 if owner is also the organizer
@@ -768,8 +743,8 @@ static NSArray *tasksFields = nil;
NSString *sort, *ascending;
[self _setupContext];
[self saveFilterValue: @"EventsFilterState"];
[self saveSortValue: @"EventsSortingState"];
[self saveFilterValue];
[self saveSortValue];
newEvents = [NSMutableArray array];
events = [[self _fetchFields: eventsFields
@@ -1269,7 +1244,6 @@ _computeBlocksPosition (NSArray *blocks)
* @apiSuccess (Success 200) {Number} events.c_nextalarm Epoch time of next alarm
* @apiSuccess (Success 200) {String} [events.c_recurrence_id] Recurrence ID if event is cyclic
* @apiSuccess (Success 200) {Number} events.isException 1 if recurrence is an exception
* @apiSuccess (Success 200) {Number} events.viewable 1 if active user can view the event
* @apiSuccess (Success 200) {Number} events.editable 1 if active user can edit the event
* @apiSuccess (Success 200) {Number} events.erasable 1 if active user can erase the event
* @apiSuccess (Success 200) {Number} events.ownerIsOrganizer 1 if owner is also the organizer
@@ -1434,7 +1408,6 @@ _computeBlocksPosition (NSArray *blocks)
* @apiSuccess (Success 200) {Number} tasks.c_classification 0: Public, 1: Private, 2: Confidential
* @apiSuccess (Success 200) {String} tasks.c_location Location
* @apiSuccess (Success 200) {String} tasks.c_category Category
* @apiSuccess (Success 200) {Number} tasks.viewable 1 if task is viewable by the active user
* @apiSuccess (Success 200) {Number} tasks.editable 1 if task is editable by the active user
* @apiSuccess (Success 200) {Number} tasks.erasable 1 if task is erasable by the active user
* @apiSuccess (Success 200) {String} tasks.c_priority Priority (0-9)
@@ -1463,8 +1436,6 @@ _computeBlocksPosition (NSArray *blocks)
filteredTasks = [NSMutableArray array];
[self _setupContext];
[self saveFilterValue: @"TasksFilterState"];
[self saveSortValue: @"TasksSortingState"];
startSecs = (unsigned int) [startDate timeIntervalSince1970];
endsSecs = (unsigned int) [endDate timeIntervalSince1970];
+1 -9
View File
@@ -33,7 +33,6 @@
#import <NGCards/iCalPerson.h>
#import <SOGo/NSArray+Utilities.h>
#import <SOGo/NSString+Utilities.h>
#import <SOGo/SOGoPermissions.h>
#import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h>
@@ -46,11 +45,6 @@
@implementation UIxCalMainView
- (NSString *) modulePath
{
return @"Calendar";
}
- (void) checkDefaultModulePreference
{
SOGoUserDefaults *ud;
@@ -267,14 +261,12 @@
- (WOResponse *) saveSelectedListAction
{
WORequest *request;
NSDictionary *params;
NSString *selectedList;
[self _setupContext];
request = [context request];
params = [[request contentAsString] objectFromJSONString];
selectedList = [params objectForKey: @"list"];
selectedList = [request formValueForKey: @"list"];
[moduleSettings setObject: selectedList
forKey: @"SelectedList"];
[us synchronize];
@@ -20,6 +20,14 @@
#import <SOGoUI/UIxComponent.h>
@class NSString;
@class SOGoAppointmentFolder;
@interface UIxCalendarFolderLinksTemplate : UIxComponent
{
SOGoAppointmentFolder *calendar;
NSString *baseCalDAVURL, *basePublicCalDAVURL;
}
@end
@@ -28,6 +28,89 @@
@implementation UIxCalendarFolderLinksTemplate
- (id) init
{
if ((self = [super init]))
{
calendar = [self clientObject];
baseCalDAVURL = nil;
basePublicCalDAVURL = nil;
}
return self;
}
- (void) dealloc
{
[baseCalDAVURL release];
[basePublicCalDAVURL release];
[super dealloc];
}
- (NSString *) _baseCalDAVURL
{
NSString *davURL;
if (!baseCalDAVURL)
{
davURL = [[calendar realDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
baseCalDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
baseCalDAVURL = davURL;
[baseCalDAVURL retain];
}
return baseCalDAVURL;
}
- (NSString *) calDavURL
{
return [NSString stringWithFormat: @"%@/", [self _baseCalDAVURL]];
}
- (NSString *) webDavICSURL
{
return [calendar webDavICSURL];
}
- (NSString *) webDavXMLURL
{
return [calendar webDavXMLURL];
}
- (NSString *) _basePublicCalDAVURL
{
NSString *davURL;
if (!basePublicCalDAVURL)
{
davURL = [[calendar publicDavURL] absoluteString];
if ([davURL hasSuffix: @"/"])
basePublicCalDAVURL = [davURL substringToIndex: [davURL length] - 1];
else
basePublicCalDAVURL = davURL;
[basePublicCalDAVURL retain];
}
return basePublicCalDAVURL;
}
- (NSString *) publicCalDavURL
{
return [NSString stringWithFormat: @"%@/", [self _basePublicCalDAVURL]];
}
- (NSString *) publicWebDavICSURL
{
return [calendar publicWebDavICSURL];
}
- (NSString *) publicWebDavXMLURL
{
return [calendar publicWebDavXMLURL];
}
- (BOOL) isPublicAccessEnabled
{
// NOTE: This method is the same found in Common/UIxAclEditor.m
@@ -35,4 +118,14 @@
enablePublicAccess];
}
- (BOOL) isWebCalendar
{
return ([calendar isKindOfClass: [SOGoWebAppointmentFolder class]]);
}
- (NSString *) webCalendarURL
{
return [calendar folderPropertyValueInCategory: @"WebCalendars"];
}
@end
+1 -1
View File
@@ -842,7 +842,7 @@ static NSArray *reminderValues = nil;
clientObject = [self clientObject];
ownerUser = [SOGoUser userWithLogin: [clientObject ownerInContext: context]];
if ([[clientObject container] isKindOfClass: [SOGoWebAppointmentFolder class]])
if ([clientObject isKindOfClass: [SOGoWebAppointmentFolder class]])
rc = componentReadableOnly;
else
{
+1 -1
View File
@@ -193,7 +193,7 @@
actionName = "saveUserRights";
};
links = {
protectedBy = "Access Contents Information";
protectedBy = "View";
pageName = "UIxCalendarFolderLinksTemplate";
};
};
@@ -7,51 +7,34 @@
xmlns:label="OGo:label"
className="UIxPageFrame"
title="title"
const:jsFiles="Common.js, Administration.js, Administration.services.js, Contacts.services.js">
const:toolbar="UIxAdministration.toolbar"
const:jsFiles="dtree.js">
<main class="view layout-fill" ui-view="administration" layout="row" ng-controller="navController">
<!-- administration -->
</main>
<script type="text/ng-template" id="administration.html">
<md-sidenav id="left-sidenav" class="md-sidenav-left md-whiteframe-z1" md-component-id="left" md-is-locked-open="$mdMedia('gt-md')" layout="column">
<var:component className="UIxSidenavToolbarTemplate" />
<md-content md-scroll-y="md-scroll-y" class="md-flex md-hue-2">
<md-list>
<md-list-item ng-click="app.go('rights')"
ui-sref="administration.rights"
ui-sref-active="md-default-theme md-background md-bg md-hue-1">
<md-icon>security</md-icon>
<p class="sg-item-name"><var:string label:value="Rights"/></p>
</md-list-item>
</md-list>
</md-content>
</md-sidenav>
<section layout="column" class="layout-fill">
<!-- TOP RIGHT TOOLBAR -->
<md-toolbar layout="row" layout-align="space-between start" class="md-tall">
<div class="md-toolbar-tools md-toolbar-tools-top" layout="row" layout-align="space-between start">
<var:component className="UIxTopnavToolbarTemplate" />
</div>
</md-toolbar>
<md-content>
<div ui-view="module"><!-- view --></div>
</md-content>
</section>
</script>
<!--
RIGHTS ADMINISTRATION PANEL
-->
<script type="text/ng-template" id="rights.html">
<div id="helpDialog" style="display: none" class="dialog left">
<div>
<label>TODO!</label>
<h3><!-- module title --></h3>
<div><!-- module description --></div>
<a href="#" class="button" id="helpDialogClose"><span><var:string label:value="Close"/></span></a>
</div>
</script>
</div>
<div id="leftPanel">
<div class="titlediv"><var:string label:value="Modules" /></div>
<div id="administrationModules">
<ul>
<li id="ACLs" class="_selected"><var:string label:value="ACLs"/></li>
</ul>
</div>
</div>
<div class="dragHandle" id="verticalDragHandle"><!-- space --></div>
<div id="rightPanel">
<span><h1 id="moduleTitle"><var:string label:value="ACLs_title"/></h1>
<div id="moduleDescription" style="display: none"><var:string label:value="ACLs_description" const:escapeHTML="NO"/></div></span>
<var:component className="UIxAdministrationFilterPanel" qualifier="qualifier" />
<div id="administrationContent"><!-- space --></div>
</div>
<var:string value="errorAlertJavaScript" const:escapeHTML="NO" />
</var:component>
@@ -5,29 +5,8 @@
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label">
<div layout="column" class="layout-fill">
<md-toolbar>
<div class="md-toolbar-tools">
<md-button class="sg-icon-button"
label:aria-label="Cancel"
ng-click="editor.cancel()">
<md-icon>close</md-icon>
</md-button>
<div class="md-flex"><!-- spacer --></div>
<md-button class="sg-icon-button"
label:aria-label="Reset"
ng-click="editor.reset()">
<md-icon>undo</md-icon>
</md-button>
<md-button class="sg-icon-button"
label:aria-label="Save"
ng-click="editor.save(editor.card.$isCard()?cardForm:listForm)">
<md-icon>save</md-icon>
</md-button>
</div>
</md-toolbar>
<md-content class="md-flex md-static">
<div layout="column" class="viewer md-padding">
<md-content md-scroll-y="true" class="md-padding md-whiteframe-z1 bg-sogoPaper-50">
<div class="editor md-padding">
<hgroup class="header">
<h1 class="sg-md-display-2--light" ng-bind-html="editor.card.$fullname()"><!-- fullname --></h1>
<h6 class="sg-md-display-2-subheader">{{editor.card.$description()}}</h6>
@@ -42,7 +21,7 @@
</label>
<input type="text" ng-maxlength="30" ng-model="editor.card.c_cn"/>
</md-input-container>
<!-- todo: look for better reset/normalization. Semanticaly this should be a fieldset, but content doesn't flex properly du to browser styles -->
<!-- todo: look for better reset/normalization. Semanticaly this should be a fieldset, but content doesn't flex properly du to browser styles-->
<div id="contact-identification" class="sg-fieldset" layout="row" layout-sm="column">
<md-input-container flex="40">
<label>
@@ -122,11 +101,9 @@
<div class="section">
<div class="attr" ng-repeat="email in editor.card.emails">
<div class="md-layout-margin" layout="row" layout-align="space-between end">
<md-input-container flex="20">
<md-select ng-model="email.type" label:placeholder="Type">
<md-option ng-repeat="type in ::editor.allEmailTypes" ng-value="type">{{ type }}</md-option>
</md-select>
</md-input-container>
<md-select ng-model="email.type" label:placeholder="Type">
<md-option ng-repeat="type in ::editor.allEmailTypes" ng-value="type">{{ type }}</md-option>
</md-select>
<md-input-container>
<label>
<var:string label:value="Email Address"/>
@@ -138,6 +115,7 @@
<md-icon>remove_circle</md-icon>
</md-button>
</div>
</div>
<div class="md-layout-margin" layout="row" layout-align="start center">
<md-button class="sg-icon-button" type="button" ng-click="editor.addEmail()">
@@ -150,38 +128,20 @@
</div>
<!-- birthday -->
<div class="section">
<div layout="row" layout-align="space-between end" ng-show="editor.card.birthday">
<div class="pseudo-input-container">
<label class="pseudo-input-label">
<var:string label:value="Birthday"/>
</label>
<md-datepicker class="pseudo-input-field"
ng-model="editor.card.birthday"><!-- birthday --></md-datepicker>
</div>
<md-button class="sg-icon-button" type="button" ng-click="editor.card.$delete('birthday', -1)">
<md-icon>remove_circle</md-icon>
</md-button>
</div>
<div class="md-layout-margin" layout="row" layout-align="start center" ng-hide="editor.card.birthday">
<md-button class="sg-icon-button" type="button" ng-click="editor.addBirthday()">
<md-icon>add_circle</md-icon>
</md-button>
<label class="button-label">
<var:string label:value="Add Birthday"/>
</label>
</div>
</div>
<md-input-container>
<label>
<var:string label:value="Birthday"/>
</label>
<input type="date" ng-model="editor.card.birthday"/>
</md-input-container>
<!-- phones -->
<div class="section">
<div class="attr" ng-repeat="phone in editor.card.phones">
<div class="md-layout-margin" layout="row" layout-align="space-between end">
<md-input-container flex="20">
<md-select ng-model="phone.type" label:placeholder="Type">
<md-option ng-repeat="type in ::editor.allTelTypes" ng-value="type">{{ type }}</md-option>
</md-select>
</md-input-container>
<md-select ng-model="phone.type" label:placeholder="Type">
<md-option ng-repeat="type in ::editor.allTelTypes" ng-value="type">{{ type }}</md-option>
</md-select>
<md-input-container>
<label>
<var:string label:value="Phone Number"/>
@@ -209,11 +169,9 @@
<div class="section">
<div class="attr" ng-repeat="url in editor.card.urls">
<div class="md-layout-margin" layout="row" layout-align="space-between end">
<md-input-container flex="20">
<md-select ng-model="url.type" label:placeholder="Type">
<md-option ng-repeat="type in ::editor.allUrlTypes" ng-value="type">{{ type }}</md-option>
</md-select>
</md-input-container>
<md-select ng-model="url.type" label:placeholder="Type">
<md-option ng-repeat="type in ::editor.allUrlTypes" ng-value="type">{{ type }}</md-option>
</md-select>
<md-input-container>
<label>
<var:string label:value="URL"/>
@@ -239,11 +197,9 @@
<div class="section">
<div class="attr" ng-repeat="address in editor.card.addresses">
<div layout="row" layout-align="center end">
<md-input-container flex="20">
<md-select ng-model="address.type" label:placeholder="Type">
<md-option ng-repeat="type in ::editor.allAddressTypes" ng-value="type">{{ type }}</md-option>
</md-select>
</md-input-container>
<md-select ng-model="address.type" label:placeholder="Type">
<md-option ng-repeat="type in ::editor.allAddressTypes" ng-value="type">{{ type }}</md-option>
</md-select>
<md-input-container>
<label>
<var:string label:value="street"/>
@@ -319,10 +275,22 @@
</label>
<textarea ng-model="editor.card.note"><!-- note --></textarea>
</md-input-container>
<div class="fieldset md-layout-margin" layout="row" layout-align="end center">
<md-button type="button" ng-click="editor.cancel()">
<var:string label:value="Cancel"/>
</md-button>
<md-button type="button" ng-click="editor.reset()">
<var:string label:value="Reset"/>
</md-button>
<md-button class="md-primary md-hue-3" type="submit">
<var:string label:value="Save"/>
</md-button>
</div>
</form>
<!-- list editor -->
<form name="listForm" ng-show="editor.card.$isList()" ng-submit="editor.save(listForm)">
<form name="listForm" ng-if="editor.card.$isList()" ng-submit="editor.save(listForm)">
<md-input-container>
<label>
<var:string label:value="Display"/>
@@ -359,8 +327,22 @@
filter-selected="false"
label:placeholder="Add Member"><!-- members --></md-contact-chips>
</div>
<div class="fieldset md-layout-margin" layout="row" layout-align="end center">
<md-button type="button" ng-click="editor.cancel()">
<var:string label:value="Cancel"/>
</md-button>
<md-button type="button" ng-click="editor.reset()">
<var:string label:value="Reset"/>
</md-button>
<var:if condition="canCreateOrModify">
<md-button class="md-primary md-hue-3" type="submit">
<var:string label:value="Save"/>
</md-button>
</var:if>
</div>
</form>
</div>
</md-content>
</div>
</container>
@@ -6,15 +6,14 @@
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label"
>
<md-dialog flex="60" flex-sm="100">
<md-toolbar>
<md-icon class="material-icons sg-icon-toolbar-bg">link</md-icon>
<md-dialog flex="40">
<md-toolbar class="sg-padded">
<div class="md-toolbar-tools">
<div class="pseudo-input-container md-flex">
<label class="pseudo-input-label"><var:string label:value="Links to this Address Book"/></label>
<div class="pseudo-input-field sg-md-title">{{links.addressbook.name}}</div>
<div class="pseudo-input-field sg-md-title"><var:string value="addressBook.displayName"/></div>
</div>
<md-button class="md-icon-button" ng-click="links.close()">
<md-button class="md-icon-button" ng-click="close()">
<md-icon aria-label="Close dialog">close</md-icon>
</md-button>
</div>
@@ -25,7 +24,7 @@
<div class="sg-md-title"><var:string label:value="Authenticated User Access"/></div>
<md-input-container>
<label><var:string label:value="CardDAV URL"/></label>
<input type="text" ng-model="links.addressbook.urls.cardDavURL" ng-readonly="true"/>
<input var:value="cardDavURL" ng-readonly="true"/>
</md-input-container>
</div>
<var:if condition="isPublicAccessEnabled">
@@ -33,7 +32,7 @@
<div class="sg-md-title"><var:string label:value="Public Access"/></div>
<md-input-container>
<label><var:string label:value="CardDAV URL"/></label>
<input type="text" ng-model="links.addressbook.urls.publicCardDavURL" ng-readonly="true"/>
<input var:value="publicCardDavURL" ng-readonly="true"/>
</md-input-container>
</div>
</var:if>
+189 -163
View File
@@ -9,26 +9,104 @@
className="UIxPageFrame"
title="title"
const:userDefaultsKeys="SOGoContactsCategories"
const:jsFiles="vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, Common.js, Preferences.services.js, Mailer.services.js, Contacts.js, Contacts.services.js, vendor/angular-file-upload.min.js">
const:jsFiles="Common.js, Preferences.services.js, Contacts.js, Contacts.services.js">
<script type="text/javascript">
var contactFolders = <var:string value="contactFolders" const:escapeHTML="NO" />;
</script>
<main class="view layout-fill" ui-view="addressbooks" layout="row" ng-controller="navController"><!-- addressbooks list --></main>
<!--
<div id="uploadDialog" style="display: none" class="dialog left">
<div>
<h3><var:string label:value="Import Cards"/></h3>
<p class="prompt"><var:string label:value="Select a vCard or LDIF file."/></p>
<form const:href="" name="uploadForm" id="uploadForm" method="post"
enctype="multipart/form-data"
onsubmit="return AIM.submit(this, {'onStart' : validateUploadForm,
'onComplete' : uploadCompleted})">
<p class="prompt"><input type="file" name="contactsFile" id="contactsFile" class="textField" required="required" />
<a href="#" class="button actionButton" id="uploadSubmit"
onclick="if($('uploadForm').onsubmit()) $('uploadForm').submit()">
<span><var:string label:value="Upload" /></span></a>
<a href="#" class="button" const:id="uploadCancel">
<span><var:string label:value="Cancel" /></span></a></p>
</form>
<hr/>
</div>
</div>
<div id="uploadResults" dropdown-content="dropdown-content" class="f-dropdown">
<div>
<h3><var:string label:value="Import Cards"/></h3>
<p id="uploadResultsContent">!- empty -</p>
<p><a href="#" class="button" const:id="uploadOK">
<span><var:string label:value="Done" /></span></a></p>
<hr/>
</div>
</div>
<ul class="f-dropdown" dropdown-content="dropdown-content" id="contactMenu">
<li><var:string label:value="Properties" /></li>
<li><var:string label:value="Categories" /></li>
<li>!- separator -</li>
<li><var:string label:value="Write" /></li>
<li><var:string label:value="Instant Message" /></li>
<li>!- separator -</li>
<li><var:string label:value="Delete" /></li>
<li>!- separator -</li>
<li><var:string label:value="Move To" /></li>
<li><var:string label:value="Copy To" /></li>
<li><var:string label:value="Export" /></li>
<li><var:string label:value="View Raw Source" /></li>
</ul>
-->
<!--
MAIN CONTENT ROW
Content of the application view injected injected in the element bellow
MUST be the first html element after body
SHOULD be a main tag (with role="main")
Templates script tag wrappers goes next
-->
<main class="view md-layout-fill" ui-view="addressbooks" layout="row" ng-controller="navController"><!-- addressbooks list --></main>
<!-- TEMPLATE SCRIPT WRAPPER -->
<script type="text/ng-template" id="UIxContactFoldersView">
<!-- dropdown menu for addressbook options button -->
<div id="addressbookProperties" class="f-dropdown icons-dropdown" ng-hide="true">
<ul class="button-group">
<li label:tooltip="Links to this Address Book"
tooltip-popup-delay="500">
<span class="button"
sg-dropdown-content-toggle="#addressbookLinks">
<i class="icon-hyperlink"><!-- links --></i>
</span>
</li>
<li label:tooltip="Rename"
tooltip-popup-delay="500">
<span class="button" ng-click="edit()">
<i class="icon-pencil"><!-- rename --></i>
</span>
</li>
<li label:tooltip="Import Cards"
tooltip-popup-delay="500">
<span class="button" ng-click="importCards()">
<i class="icon-file"><!-- import --></i>
</span>
</li>
</ul>
</div>
<!-- Sidenav -->
<md-sidenav id="left-sidenav" class="md-sidenav-left md-whiteframe-z1" md-component-id="left" md-is-locked-open="isGtMedium" layout="column">
<var:component className="UIxSidenavToolbarTemplate" />
<!-- <div style="position: relative; display: flex;"> -->
<md-content md-scroll-y="md-scroll-y" class="md-flex md-hue-2">
<md-content md-scroll-y="md-scroll-y">
<!-- User's addressbooks -->
<section>
<md-subheader class="sg-md-subheader md-hue-2">
<md-subheader class="sg-md-subheader">
<div layout="row" layout-align="space-between center">
<span><var:string label:value="Address Books"/></span>
<span><var:string label:value="AddressBooks"/></span>
<md-button class="sg-icon-button"
label:aria-label="New Addressbook..."
ng-click="app.newAddressbook()">
@@ -41,13 +119,13 @@
ng-click="app.select(folder)"
ng-dblclick="app.edit(folder)"
ui-sref="app.addressbook({addressbookId: folder.id})"
ui-sref-active="md-default-theme md-background md-bg md-hue-1">
ui-sref-active="sg-active">
<md-icon>contacts</md-icon>
<p class="sg-item-name"
ng-show="app.editMode!=folder.id">{{folder.name}}</p>
<md-input-container class="md-flex md-tile-content"
ng-show="app.editMode == folder.id">
<input class="sg-item-name" type="text"
<input class="folder-name" type="text"
label:aria-label="Name of the Address Book"
ng-model="folder.name"
ng-cloak="ng-cloak"
@@ -63,21 +141,10 @@
md-menu-origin="md-menu-origin">more_vert</md-icon>
<md-menu-content width="2">
<md-menu-item>
<md-button type="button" ng-click="app.edit(folder)">
<var:string label:value="Rename"/>
</md-button>
</md-menu-item>
<md-menu-item ng-show="folder.id != 'personal'">
<md-button type="button" ng-click="app.confirmDelete()">
<var:string label:value="Delete"/>
</md-button>
</md-menu-item>
<md-menu-divider><!-- divider --></md-menu-divider>
<md-menu-item>
<md-button type="button" ng-click="app.importCards($event, folder)">
<var:string label:value="Import"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button type="button" ng-click="app.exportCards()">
<var:string label:value="Export"/>
@@ -101,7 +168,7 @@
</section>
<!-- Subscriptions -->
<section>
<md-subheader class="sg-md-subheader md-hue-2">
<md-subheader class="sg-md-subheader">
<div layout="row" layout-align="space-between center">
<span><var:string label:value="Subscriptions"/></span>
<md-button class="sg-icon-button"
@@ -117,13 +184,13 @@
ng-click="app.select(folder)"
ng-dblclick="app.edit(folder)"
ui-sref="app.addressbook({addressbookId: folder.id})"
ui-sref-active="md-default-theme md-background md-bg md-hue-1">
ui-sref-active="sg-active">
<md-icon>contacts</md-icon>
<p class="sg-item-name"
ng-show="editMode!=folder.id">{{folder.name}}</p>
<md-input-container class="md-flex md-tile-content"
ng-show="app.editMode==folder.id">
<input class="sg-item-name" type="text"
<input class="folder-name" type="text"
label:aria-label="Name of the Address Book"
ng-model="folder.name"
ng-cloak="ng-cloak"
@@ -138,11 +205,6 @@
ng-click="$mdOpenMenu()"
md-menu-origin="md-menu-origin">more_vert</md-icon>
<md-menu-content width="2">
<md-menu-item>
<md-button type="button" ng-click="app.edit(folder)">
<var:string label:value="Rename"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button type="button" ng-click="app.confirmDelete(folder)">
<var:string label:value="Delete"/>
@@ -155,14 +217,14 @@
</section>
<!-- Remote/domain addressbooks -->
<section>
<md-subheader class="sg-md-subheader md-hue-2">
<md-subheader class="sg-md-subheader md-padding">
<var:string label:value="Global Addressbooks"/>
</md-subheader>
<md-list>
<md-list-item ng-repeat="folder in app.service.$remotes track by folder.id"
ng-click="app.select(folder)"
ui-sref="app.addressbook({addressbookId: folder.id})"
ui-sref-active="md-default-theme md-background md-bg md-hue-1">
ui-sref-active="sg-active">
<md-icon>public</md-icon>
<p class="sg-item-name">{{folder.name}}</p>
</md-list-item>
@@ -172,27 +234,26 @@
<!-- </div> -->
</md-sidenav>
<section layout="column" class="sg-app-content layout-fill" ui-view="addressbook"><!-- contacts list --></section>
<section layout="column" class="sg-app-content md-layout-fill" ui-view="addressbook"><!-- contacts list --></section>
</script>
<script type="text/ng-template" id="addressbook">
<md-toolbar layout="row" layout-align="space-between center" class="toolbar-main">
<var:component className="UIxTopnavToolbarTemplate" />
</md-toolbar>
<md-toolbar layout="column" layout-align="space-between start" class="md-tall toolbar-main">
<div class="md-toolbar-tools md-toolbar-tools-top sg-padded" layout="row" layout-align="space-between start">
<var:component className="UIxTopnavToolbarTemplate" />
</div>
<div class="md-toolbar-tools" layout="row" layout-align="space-between center"
ng-show="addressbook.selectedFolder.$selectedCount() == 0">
<div layout="row" class="md-flex">
<div class="view-list" layout="column">
<!-- single-selection toolbar -->
<md-toolbar ng-show="addressbook.selectedFolder.$selectedCount() == 0">
<!-- sort mode (default) -->
<div class="md-toolbar-tools" ng-hide="addressbook.mode.search">
<div class="view-list" layout="row" layout-align="space-between center"
ng-hide="addressbook.mode.search">
<div class="sg-toolbar-group">
<md-button class="sg-icon-button" label:aria-label="Search"
ng-click="addressbook.mode.search = true">
<md-icon>search</md-icon>
</md-button>
<div class="sg-folder-name">{{addressbook.selectedFolder.name}}</div>
</div>
<div class="sg-toolbar-group-last">
<md-menu>
<md-button class="sg-icon-button" label:aria-label="Sort"
ng-click="$mdOpenMenu()">
@@ -234,10 +295,9 @@
</md-menu-item>
<md-menu-divider> <!-- divider --></md-menu-divider>
<md-menu-item >
<md-button ng-href="#">
<md-button ng-click="addressbook.sort()">
<md-checkbox
ng-change="addressbook.sort(addressbook.selectedFolder.constructor.$query.sort)"
ng-model="addressbook.selectedFolder.constructor.$query.asc"
ng-model="addressbook.selectedFolder.$query.asc"
ng-true-value="0"
ng-false-value="1"><var:string label:value="Descending Order"/></md-checkbox>
</md-button>
@@ -248,120 +308,100 @@
<md-icon>refresh</md-icon>
</md-button>
</div>
<!-- search mode -->
<div class="md-toolbar-tools sg-toolbar-secondary"
ng-show="addressbook.mode.search"
sg-search="addressbook.selectedFolder.$filter(searchText, { search: searchField })">
<md-button class="sg-icon-button"
sg-search-cancel="addressbook.cancelSearch()"
label:aria-label="Back">
<md-icon>arrow_back</md-icon>
</md-button>
<md-input-container>
<input name="folderSearch" type="search" ng-minlength="3"/>
</md-input-container>
<md-input-container flex="25">
<md-select>
<md-option value="name_or_address" selected="selected"><var:string label:value="Name or Email"/></md-option>
<md-option value="category"><var:string label:value="Category"/></md-option>
<md-option value="organization"><var:string label:value="Organization"/></md-option>
</md-select>
</md-input-container>
</div>
</md-toolbar>
</div>
<!-- search mode -->
<div class="view-list sg-padded--right sg-toolbar-search" layout="row" layout-align="space-between center"
ng-show="addressbook.mode.search"
sg-search="addressbook.selectedFolder.$filter(searchText, { search: searchField })">
<md-button class="sg-icon-button"
sg-search-cancel="addressbook.cancelSearch()"
label:aria-label="Back">
<md-icon>arrow_back</md-icon>
</md-button>
<md-input-container>
<input name="folderSearch" type="search" />
</md-input-container>
<md-input-container>
<md-select>
<md-option value="name_or_address" selected="selected"><var:string label:value="Name or Email"/></md-option>
<md-option value="category"><var:string label:value="Category"/></md-option>
<md-option value="organization"><var:string label:value="Organization"/></md-option>
</md-select>
</md-input-container>
</div>
</div>
<!-- multiple selection mode -->
<md-toolbar class="md-hue-1 sg-toolbar-secondary"
ng-show="addressbook.selectedFolder.$selectedCount() > 0">
<div class="md-toolbar-tools">
<md-button class="sg-icon-button" ng-click="addressbook.unselectCards()">
<md-icon>arrow_back</md-icon>
</md-button>
<label>{{addressbook.selectedFolder.$selectedCount()}} <var:string label:value="selected"/></label>
<div class="md-flex"><!-- spacer --></div>
<md-button class="sg-icon-button" ng-click="addressbook.selectAll()">
<md-tooltip md-direction="left"><var:string label:value="Select All"/></md-tooltip>
<md-icon>select_all</md-icon>
</md-button>
<md-button class="sg-icon-button"
ng-show="addressbook.selectedFolder.acls.objectEraser"
ng-click="addressbook.confirmDeleteSelectedCards()">
<md-icon>delete</md-icon>
</md-button>
<md-menu>
<md-button class="sg-icon-button" label:aria-label="Copy contacts" ng-click="$mdOpenMenu()">
<md-tooltip md-direction="left"><var:string label:value="Copy To"/></md-tooltip>
<md-icon>content_copy</md-icon>
<div layout="row" layout-align="start center" ng-show="addressbook.selectedFolder.$selectedCount() > 0">
<md-button class="sg-icon-button" ng-click="addressbook.unselectCards()">
<md-icon>arrow_back</md-icon>
</md-button>
<label>{{addressbook.selectedFolder.$selectedCount()}} selected</label>
<md-button class="sg-icon-button" ng-click="addressbook.selectAll()">
<md-tooltip md-direction="left"><var:string label:value="Select All"/></md-tooltip>
<md-icon>select_all</md-icon>
</md-button>
<md-button class="sg-icon-button" ng-click="addressbook.confirmDeleteSelectedCards()">
<md-icon>delete</md-icon>
</md-button>
<md-menu>
<md-button class="sg-icon-button" label:aria-label="More messages options" ng-click="$mdOpenMenu()">
<md-icon>more_vert</md-icon>
</md-button>
<md-menu-content width="2">
<md-menu-item>
<md-button>
<var:string label:value="Write"/>
</md-button>
<md-menu-content width="4">
<md-menu-item>
<md-button class="md-primary" ng-disabled="true"><var:string label:value="Address Books"/></md-button>
</md-menu-item>
<md-menu-divider><!-- divider --></md-menu-divider>
<md-menu-item ng-repeat="folder in app.service.$addressbooks track by folder.id"
ng-hide="addressbook.selectedFolder.id == folder.id">
<md-button ng-click="addressbook.copySelectedCards(folder.id)">
<span ng-class="'sg-child-level-' + folder.level">{{folder.name}}</span>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
<md-menu>
<md-button class="sg-icon-button" label:aria-label="More messages options" ng-click="$mdOpenMenu()">
<md-icon>more_vert</md-icon>
</md-menu-item>
<md-menu-item>
<md-button ng-click="addressbook.saveSelectedCards()">
<var:string label:value="Export"/>
</md-button>
<md-menu-content width="2">
<md-menu-item>
<md-button ng-click="addressbook.newMessageWithSelectedCards($event)">
<var:string label:value="Write"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="addressbook.saveSelectedCards()">
<var:string label:value="Export"/>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
</div>
</md-toolbar>
</md-menu-item>
</md-menu-content>
</md-menu>
</div>
</md-toolbar>
<div layout="row" class="md-flex" layout-align="space-between">
<div class="view-list" layout="column">
<md-content id="contactsList" layout="column" class="md-flex">
<md-subheader ng-show="addressbook.service.$query.value">
<span ng-switch="addressbook.selectedFolder.cards.length">
<span ng-switch-when="0"><var:string label:value="No matching contact"/></span>
<span ng-switch-default="true">{{addressbook.selectedFolder.cards.length}} <var:string label:value="matching contacts"/></span>
</span>
</md-subheader>
<md-subheader ng-hide="addressbook.service.$query.value"
ng-switch="addressbook.selectedFolder.isRemote">
<span ng-switch-when="1">
<var:string label:value="Start a search to browse this address book"/>
</span>
<span ng-switch-default="true">
<span ng-switch="addressbook.selectedFolder.cards.length">
<span ng-switch-when="0"><var:string label:value="No contact"/></span>
<span ng-switch-default="true">{{addressbook.selectedFolder.cards.length}} <var:string label:value="contacts"/></span>
</span>
</span>
</md-subheader>
<header class="sg-md-subheader sg-md-subheader--fixed">
<h2 class="md-default-theme sg-md-subhead-solo fg-sogoBlue-700">
<span ng-show="addressbook.selectedFolder.cards.length > 0">{{addressbook.selectedFolder.cards.length}} <var:string label:value="contacts"/></span>
<span ng-show="addressbook.selectedFolder.cards.length == 0"><var:string label:value="No contact"/></span>
</h2>
</header>
<!-- Search field & special results
<input type="text" placeholder="Search" ng-model="search.filter" ng-keyup="doSearch($event)" />
<div ng-switch="search.status">
<div ng-switch-when="min-char" class="alert-bg">
<i class="icon-warning"></i><var:string label:value="Please enter at least three characters"/>
</div>
<div ng-switch-when="no-result" class="alert-bg">
<i class="icon-ion-search"></i><var:string label:value="No matching card"/>
</div>
<div ng-switch-when="remote-addressbook" ng-show="addressbook.cards.length == 0" class="alert-bg">
<i class="icon-ion-search"></i><var:string label:value="Initiate a search"/>
</div>
</div>
-->
<md-virtual-repeat-container class="md-flex">
<md-list class="sg-section-list">
<div md-virtual-repeat="currentCard in addressbook.selectedFolder.cards" md-item-size="72">
<md-list>
<div md-virtual-repeat="currentCard in addressbook.selectedFolder.cards">
<md-list-item
ng-class="{'sg-active': currentCard.id == addressbook.selectedFolder.selectedCard}"
ng-click="addressbook.selectCard(currentCard)"
ui-sref="app.addressbook.card.view({addressbookId: addressbook.selectedFolder.id, cardId: currentCard.id})"
ui-sref-active="md-default-theme md-background md-bg md-hue-1">
ui-sref="app.addressbook.card.view({addressbookId: addressbook.selectedFolder.id, cardId: currentCard.id})">
<!-- ui-sref-active="sg-active" -->
<div class="sg-selected-avatar" ng-show="currentCard.selected"
ng-click="addressbook.toggleCardSelection($event, currentCard)">
ng-click="currentCard.selected = !currentCard.selected">
<!-- selected avatar -->
</div>
<sg-avatar-image class="md-tile-left"
ng-show="addressbook.notSelectedComponent(currentCard, 'vcard')"
ng-click="addressbook.toggleCardSelection($event, currentCard)"
sg-email="currentCard.$preferredEmail(addressbook.selectedFolder.constructor.$query)"
size="40">
ng-click="currentCard.selected = !currentCard.selected"
sg-email="currentCard.$preferredEmail(addressbook.selectedFolder.$query)"
size="48">
<!-- contact avatar -->
</sg-avatar-image>
<div class="sg-list-avatar"
@@ -371,37 +411,23 @@
</div>
<div class="sg-tile-content">
<div class="sg-md-subhead-multi" ng-bind-html="currentCard.$fullname()"><!-- cn --></div>
<div class="sg-md-body-multi">{{currentCard.$preferredEmail(addressbook.selectedFolder.constructor.$query)}}</div>
<div class="sg-md-body-multi">{{currentCard.$preferredEmail(addressbook.selectedFolder.$query)}}</div>
</div>
</md-list-item>
</div>
</md-list>
</md-virtual-repeat-container>
<div class="sg-progress-circular-floating"
ng-show="addressbook.selectedFolder.$isLoading">
<md-progress-circular class="md-accent"
md-mode="indeterminate"
md-diameter="32"><!-- progress --></md-progress-circular>
</div>
<md-button class="md-fab md-fab-bottom-right md-accent"
label:aria-label="New Contact"
ng-show="addressbook.selectedFolder.acls.objectCreator"
ng-click="addressbook.newComponent($event)">
<md-icon>add</md-icon>
</md-button>
</md-content>
</div>
<div id="detailView" class="view-detail" layout="column" layout-align="start center"
ng-class="{ 'sg-close': !addressbook.selectedFolder.selectedCard }"
ui-view="card">
<md-toolbar class="hide-sm"><!-- empty toolbar --></md-toolbar>
<md-content class="hide-sm md-flex layout-fill md-hue-1" layout="column">
<md-input-container layout-align="center center">
<label class="sg-md-title"><var:string label:value="No contact selected"/></label>
</md-input-container>
</md-content>
</div>
<div id="detailView" class="view-detail ng-cloak" layout="column" ui-view="card"
ng-show="addressbook.selectedFolder.selectedCard"><!-- card view --></div>
</div>
</script>
+136 -146
View File
@@ -3,177 +3,167 @@
xmlns="http://www.w3.org/1999/xhtml"
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label">
xmlns:label="OGo:label"
>
<md-card class="viewer md-flex">
<md-card-content>
<header class="msg-header">
<div ng-show="editor.card.tag == 'vcard'">
<sg-avatar-image class="md-tile-left"
sg-email="editor.card.$preferredEmail()"
sg-src="editor.card.photoURL"
size="48"><!-- avatar --></sg-avatar-image>
</div>
<div class="sg-list-avatar" ng-show="editor.card.tag == 'vlist'">
<!--list avatar-->
</div>
<div class="msg-header-content">
<h1 class="sg-md-display-2--light" ng-bind-html="editor.card.$fullname()"><!-- fullname --></h1>
<h6 class="sg-md-display-2-subheader">{{editor.card.$description()}}</h6>
<md-chips ng-model="editor.card.categories"
class="sg-readonly" readonly="true">
<md-chip-template>
{{$chip.value}}
</md-chip-template>
</md-chips>
</div>
<div class="sg-icon-bar--vertical">
<md-button class="sg-icon-button show-sm"
label:aria-label="Close"
ng-click="toggleDetailView()">
<md-icon>close</md-icon>
</md-button>
<md-button class="sg-icon-button"
label:aria-label="Edit"
ng-show="editor.currentFolder.isEditable"
ui-sref="app.addressbook.card.editor({addressbookId: editor.currentFolder.id, cardId: editor.card.id})">
<md-tooltip md-direction="left"><var:string label:value="Edit"/></md-tooltip>
<md-icon>mode_edit</md-icon>
</md-button>
<md-button class="sg-icon-button"
label:aria-label="Delete"
ng-click="editor.confirmDelete(editor.card)">
<md-tooltip md-direction="left"><var:string label:value="Delete"/></md-tooltip>
<md-icon>delete</md-icon>
</md-button>
<div layout="column" class="layout-fill">
<md-toolbar>
<div class="md-toolbar-tools">
<md-button class="sg-icon-button hide show-sm"
label:aria-label="Close"
ng-click="editor.close()">
<md-icon>close</md-icon>
</md-button>
<div class="md-flex"><!-- spacer --></div>
<md-button class="sg-icon-button"
label:aria-label="Edit"
ng-show="editor.currentFolder.acls.objectEditor"
ui-sref="app.addressbook.card.editor({addressbookId: editor.currentFolder.id, cardId: editor.card.id})">
<md-tooltip md-direction="left"><var:string label:value="Edit"/></md-tooltip>
<md-icon>mode_edit</md-icon>
</md-button>
<md-button class="sg-icon-button"
label:aria-label="Delete"
ng-show="editor.currentFolder.acls.objectEraser"
ng-click="editor.confirmDelete(editor.card)">
<md-tooltip md-direction="left"><var:string label:value="Delete"/></md-tooltip>
<md-icon>delete</md-icon>
</md-button>
<md-menu>
<md-button label:aria-label="More contact options" class="sg-icon-button" ng-click="$mdOpenMenu($event)">
<md-icon>more_vert</md-icon>
</md-button>
<md-menu-content>
<md-menu-item>
<md-button label:aria-label="Export"
ng-click="editor.exportCard()">
<var:string label:value="Export"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button label:aria-label="View Raw Source"
<md-button aria-label="View Raw Source"
ng-click="editor.viewRawSource($event)">
<var:string label:value="View Raw Source"/>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
</div>
</md-toolbar>
<md-content class="md-flex md-static">
<div layout="column" class="viewer md-padding">
<header class="msg-header">
<div ng-show="editor.card.c_component == 'vcard'">
<sg-avatar-image class="md-tile-left"
sg-email="editor.card.$preferredEmail()"
sg-src="editor.card.photoURL"
size="40"><!-- avatar --></sg-avatar-image>
</div>
<div class="sg-list-avatar" ng-show="editor.card.c_component == 'vlist'">
<!--list avatar-->
</div>
<div class="msg-header-content">
<h1 class="sg-md-display-2--light" ng-bind-html="editor.card.$fullname()"><!-- fullname --></h1>
<h6 class="sg-md-display-2-subheader">{{editor.card.$description()}}</h6>
<md-chips ng-model="editor.card.categories"
class="sg-readonly" readonly="true">
<md-chip-template>
{{$chip.value}}
</md-chip-template>
</md-chips>
</div>
</header>
<section class="msg-body">
<div class="pseudo-input-container" ng-show="editor.card.$birthday().length">
<div class="key">
<label class="pseudo-input-label">
<var:string label:value="Birthday"/>
</label>
</div>
<div class="pseudo-input-field">
<span>{{editor.card.$birthday()}}</span>
</div>
</div>
</div>
</header>
<section class="msg-body">
<!-- list members -->
<div class="section" ng-show="editor.card.refs.length > 0">
<div class="pseudo-input-container" ng-show="editor.card.birthday">
<div class="key">
<label class="pseudo-input-label">
<var:string label:value="Members"/>
<var:string label:value="Birthday"/>
</label>
<md-list>
<md-list-item class="md-3-line" ng-repeat="ref in editor.card.refs track by ref.reference">
<sg-avatar-image class="md-tile-left"
sg-email="ref.$preferredEmail()"
size="48">
<!-- contact avatar -->
</sg-avatar-image>
<div class="md-list-item-text">
<h3>
<a ui-sref="app.addressbook.card.view({addressbookId: editor.currentFolder.id, cardId: ref.reference})">
{{ ref.$fullname() }}
</a>
</h3>
<h4 ng-show="ref.email">
<a ui-sref="mailto:{{ref.email}}" ng-class="ng-scope">
{{ ref.email }}
</a>
</h4>
</div>
</md-list-item>
</md-list>
</div>
<div class="section" ng-show="editor.card.emails.length > 0">
<div class="pseudo-input-container" ng-repeat="email in editor.card.emails">
<div class="key">
<label class="pseudo-input-label"><var:entity const:name="nbsp"/>{{email.type}}</label>
</div>
<div class="pseudo-input-field">
<a href="#" ng-bind="email.value"
ng-click="addressbook.newMessageWithRecipient($event, email.value, editor.card.$fullname())"><!-- recipient --></a>
</div>
</div>
<div class="pseudo-input-field">
<span>{{editor.card.$birthday() | date}}</span>
</div>
</div>
<div class="section" ng-show="editor.card.phones.length > 0">
<div class="pseudo-input-container" ng-repeat="phone in editor.card.phones">
<div class="key">
<label class="pseudo-input-label">{{phone.type}}</label>
<!-- list members -->
<div class="section" ng-show="editor.card.refs.length > 0">
<label class="pseudo-input-label">
<var:string label:value="Members"/>
</label>
<md-list>
<md-list-item class="md-3-line" ng-repeat="ref in editor.card.refs track by ref.reference">
<sg-avatar-image class="md-tile-left"
sg-email="ref.$preferredEmail()"
size="48">
<!-- contact avatar -->
</sg-avatar-image>
<div class="md-list-item-text">
<h3>
<a ui-sref="app.addressbook.card.view({addressbookId: editor.currentFolder.id, cardId: ref.reference})">
{{ ref.$fullname() }}
</a>
</h3>
<h4 ng-show="ref.email">
<a ui-sref="mailto:{{ref.email}}" ng-class="ng-scope">
{{ ref.email }}
</a>
</h4>
</div>
<div class="pseudo-input-field">
<a href="tel:{{phone.value}}">{{phone.value}}</a>
</div>
</div>
</div>
<div class="section" ng-show="editor.card.urls">
<div class="pseudo-input-container" ng-repeat="url in editor.card.urls">
<div class="key">
<label class="pseudo-input-label"><var:entity const:name="nbsp"/>{{url.type}}
</label>
</div>
<div class="pseudo-input-field">
<a href="#" ng-href="{{url.value}}">{{url.value}}</a>
</div>
</div>
</div>
<div class="section" ng-show="editor.card.addresses">
<div class="pseudo-input-container" ng-repeat="address in editor.card.addresses">
<div class="key">
<label class="pseudo-input-label">{{address.type}}</label>
</div>
<div class="pseudo-input-field">
<div sg-address="address"><!-- address --></div>
</div>
</div>
</div>
<div class="pseudo-input-container" ng-show="editor.card.note">
</md-list-item>
</md-list>
</div>
<div class="section" ng-show="editor.card.emails.length > 0">
<div class="pseudo-input-container" ng-repeat="email in editor.card.emails">
<div class="key">
<label class="pseudo-input-label">
<var:string label:value="Note"/>
<label class="pseudo-input-label"><var:entity const:name="nbsp"/>{{email.type}}</label>
</div>
<div class="pseudo-input-field">
<a href="mailto:{{email.value}}">{{email.value}}</a>
</div>
</div>
</div>
<div class="section" ng-show="editor.card.phones.length > 0">
<div class="pseudo-input-container" ng-repeat="phone in editor.card.phones">
<div class="key">
<label class="pseudo-input-label">{{phone.type}}</label>
</div>
<div class="pseudo-input-field">
<a href="tel:{{phone.value}}">{{phone.value}}</a>
</div>
</div>
</div>
<div class="section" ng-show="editor.card.urls">
<div class="pseudo-input-container" ng-repeat="url in editor.card.urls">
<div class="key">
<label class="pseudo-input-label"><var:entity const:name="nbsp"/>{{url.type}}
</label>
</div>
<div class="pseudo-input-field">
<div ng-bind-html="editor.card.note"><!-- note --></div>
<a href="#" ng-href="{{url.value}}">{{url.value}}</a>
</div>
</div>
</div>
<div class="section" ng-show="editor.card.addresses">
<div class="pseudo-input-container" ng-repeat="address in editor.card.addresses">
<div class="key">
<label class="pseudo-input-label">{{address.type}}</label>
</div>
<div class="pseudo-input-field">
<div sg-address="address"><!-- address --></div>
</div>
</div>
</section>
</div>
</md-content>
</div>
</div>
<div class="pseudo-input-container" ng-show="editor.card.note">
<div class="key">
<label class="pseudo-input-label">
<var:string label:value="Note"/>
</label>
</div>
<div class="pseudo-input-field">
<div ng-bind-html="editor.card.note"><!-- note --></div>
</div>
</div>
</section>
</md-card-content>
</md-card>
</container>
@@ -7,15 +7,8 @@
xmlns:label="OGo:label"
xmlns:uix="OGo:uix">
<md-dialog flex="50" flex-sm="100">
<md-toolbar>
<div class="md-toolbar-tools">
<div class="sg-md-title md-flex"><var:string label:value="Subscribe to a shared folder"/></div>
<md-button class="md-icon-button" ng-click="subscribe.close()">
<md-icon label:aria-label="Close">close</md-icon>
</md-button>
</div>
</md-toolbar>
<md-dialog-content>
<h2 class="md-title"><var:string label:value="Subscribe to a shared folder"/></h2>
<md-input-container>
<label><md-icon>search</md-icon><var:string label:value="Search User"/></label>
<input type="input"
@@ -40,7 +33,8 @@
<div class="sg-md-subhead-multi">{{user.cn}} {{user.userClass}}</div>
<div class="sg-md-body-multi">{{user.c_email}}</div>
</div>
<md-icon ng-class="{'md-rotate-180': user.uid == subscribe.selectedUser.uid}">expand_more</md-icon>
<md-icon ng-class="{ 'icon-expand-more': user.uid != subscribe.selectedUser.uid,
'icon-expand-less': user.uid == subscribe.selectedUser.uid }"><!--icon--></md-icon>
</div>
</md-button>
<md-card-content ng-show="user == subscribe.selectedUser">
@@ -6,7 +6,7 @@
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
xmlns:uix="OGo:uix">
<div layout="column">
<div class="addressbookUserRights" layout="column">
<md-checkbox name="canViewObjects"
ng-model="acl.selectedUser.rights.canViewObjects"
@@ -7,7 +7,6 @@
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label">
<var:string value="cssContent" const:escapeHTML="NO" />
<div class="SOGoHTMLMail-CSS-Delimiter mailer_htmlcontent">
<var:string value="flatContentAsString" const:escapeHTML="NO" />
</div>
<div class="SOGoHTMLMail-CSS-Delimiter mailer_htmlcontent"
><var:string value="flatContentAsString" const:escapeHTML="NO" /></div>
</container>
@@ -250,16 +250,14 @@
<label class="pseudo-input-label"><var:string label:value="Attendees"/></label>
<!-- md-contact-chips don't support "readonly", so we build them using md-chips
in readonly mode and a template similar to the one of md-contact-chips -->
<md-chips class="md-contact-chips sg-readonly"
ng-model="::part.participants"
<md-chips class="md-contact-chips"
ng-model="part.participants"
readonly="true">
<md-chip-template>
<div class="md-contact-avatar">
<sg-avatar-image sg-email="$chip.email" size="32"><!-- avatar --></sg-avatar-image>
</div>
<div class="md-contact-avatar"><img src="#" ng-src="{{$chip.image}}" alt="{{$chip.name}}"/></div>
<div class="md-contact-name">{{$chip.name}}</div>
<md-icon ng-class="'icon-' + $chip.status"><!-- partstat --></md-icon>
</md-chip-template>
</md-chip-template>
</md-chips>
</div>
</md-list>
@@ -11,14 +11,13 @@
var:title="filenameForDisplay"
class="md-card-image"></img>
<md-card-content>
<p class="md-caption">
<p>
<var:string value="filenameForDisplay" /><br/>
<var:string value="bodyInfo.size" formatter="sizeFormatter"/>
</p>
</md-card-content>
<div class="md-actions" layout="row" layout-align="end center">
<md-button class="sg-icon-button" var:href="pathForDownload">
<md-tooltip md-direction="left"><var:string label:value="Save Attachment"/></md-tooltip>
<md-icon>save</md-icon>
</md-button>
</div>
@@ -4,16 +4,24 @@
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:const="http://www.skyrix.com/od/constant"
xmlns:label="OGo:label">
<md-card>
<md-card-content>
<md-whiteframe class="md-whiteframe-z1" layout="row" layout-align="space-between center">
<div layout="row">
<div>
<img var:src="mimeImageURL"/>
<p class="md-caption">
<var:string value="filenameForDisplay"/><br/>
</div>
<div layout="column" layout-align="start start" layout-padding="layout-padding">
<div>
<var:string value="filenameForDisplay"/>
</div>
<div class="md-caption">
<var:string value="bodyInfo.size" formatter="sizeFormatter"/>
</p>
</md-card-content>
<div class="md-actions" layout="row" layout-align="end center">
<md-button class="sg-icon-button" var:href="pathToAttachment" target="_blank">
</div>
</div>
</div>
<div layout="row" layout-align="end end">
<md-button class="sg-icon-button" var:href="pathToAttachment">
<md-tooltip md-direction="left"><var:string label:value="View Attachment"/></md-tooltip>
<md-icon>open_in_browser</md-icon>
</md-button>
@@ -42,5 +50,5 @@
PartPath: <var:string value="partPath" /><br />
-->
</md-card>
</md-whiteframe>
</div>
+161 -226
View File
@@ -6,245 +6,180 @@
xmlns:label="OGo:label">
<md-dialog flex="80" flex-sm="100">
<md-toolbar>
<md-toolbar class="md-padding">
<!-- TOOLBAR TO SHOW CC/BCC/ATTACHMENT FIELDS -->
<div class="md-toolbar-tools">
<md-icon class="material-icons sg-icon-toolbar-bg">edit</md-icon>
<!-- from -->
<sg-avatar-image hide-sm="hide-sm"
sg-email="editor.message.editable.from"
size="32"><!-- avatar --></sg-avatar-image>
<md-input-container flex="flex">
<label><var:string label:value="From"/></label>
<md-select name="from"
ng-model="editor.message.editable.from">
<md-option ng-value="identity" ng-repeat="identity in editor.identities">{{identity}}</md-option>
</md-select>
</md-input-container>
<div flex-gt-sm="flex-gt-sm"><!-- spacer --></div>
<md-button class="sg-icon-button" ng-click="editor.send()">
<md-icon>send</md-icon>
</md-button>
<md-button class="sg-icon-button" ng-click="editor.message.$save()">
<md-icon>save</md-icon>
</md-button>
<md-button class="sg-icon-button" ng-click="editor.cancel()">
<md-icon>close</md-icon>
<md-button ng-show="editor.hideCc" ng-click="editor.hideCc = false">Cc</md-button>
<md-button ng-show="editor.hideBcc" ng-click="editor.hideBcc = false">Bcc</md-button>
<md-button>
<label for="file-input">
Add files
</label>
</md-button>
<input id="file-input" type="file"
nv-file-select="nv-file-select"
multiple="multiple"
uploader="editor.uploader"
ng-show="false"/>
<md-menu>
<md-button label:aria-label="More mail options" class="iconBtton" ng-click="$mdOpenMenu($event)">
<md-icon>more_vert</md-icon>
</md-button>
<md-menu-content width="4">
<md-menu-item>
<md-button>
<md-checkbox ng-model="editor.message.editable.receipt">
<var:string label:value="Receipt"/>
</md-checkbox>
</md-button>
</md-menu-item>
<md-menu-item>
<div layout="row" layout-align="center center">
<span>
<var:string label:value="Priority"/>
</span>
<md-slider flex="flex"
md-discrete="md-discrete"
ng-model="editor.message.editable.priority"
step="1"
min="1"
max="5"
label:aria-label="Priority">
</md-slider>
</div>
</md-menu-item>
</md-menu-content>
</md-menu>
</div>
</md-toolbar>
<md-dialog-content>
<header>
<div class="msg-header-content">
<form>
<header>
<!-- to -->
<div class="pseudo-input-container">
<label class="pseudo-input-label">
<var:string label:value="To"/>
</label>
<md-chips ng-model="editor.message.editable.to"
md-on-append="editor.addRecipient($chip)">
<md-autocomplete
class="sg-chips-autocomplete"
md-autofocus="true"
md-search-text="editor.autocomplete.to.searchText"
md-selected-item="editor.autocomplete.to.selected"
md-items="user in editor.contactFilter(editor.autocomplete.to.searchText)"
md-min-length="3"
md-delay="300"
md-no-cache="true"
label:placeholder="Add a recipient">
<md-item-template>
<span class="md-contact-suggestion">
<span class="md-contact-name"
md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user.$$fullname}}</span>
<span class="md-contact-email"
md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user.$$email}}</span>
</span>
</md-item-template>
</md-autocomplete>
<md-chip-template>{{$chip}}</md-chip-template>
</md-chips>
<!-- FROM / TO / CC/ BCC / SUBJECT / ATTACHMENTS -->
<div class="msg-header-content">
<md-input-container>
<label> <var:string label:value="From"/></label>
<md-select name="from"
ng-model="editor.message.editable.from">
<md-option ng-value="identity" ng-repeat="identity in editor.identities">{{identity}}</md-option>
</md-select>
</md-input-container>
<div class="pseudo-input-container">
<label class="pseudo-input-label">
<var:string label:value="To"/>
</label>
<md-chips ng-model="editor.message.editable.to">
<md-autocomplete
md-selected-item="editor.autocomplete.to.selected"
md-search-text="editor.autocomplete.to.searchText"
md-items="user in editor.contactFilter(editor.autocomplete.to.searchText)"
label:placeholder="Add a recipient">
<span md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user}}</span>
</md-autocomplete>
<md-chip-template>{{$chip}}</md-chip-template>
</md-chips>
</div>
<div class="pseudo-input-container" ng-hide="editor.hideCc">
<label class="pseudo-input-label">
<var:string label:value="Cc"/>
</label>
<md-chips ng-model="editor.message.editable.cc">
<md-autocomplete
md-selected-item="editor.autocomplete.cc.selected"
md-search-text="editor.autocomplete.cc.searchText"
md-items="user in editor.contactFilter(editor.autocomplete.cc.searchText)"
label:placeholder="Add a recipient">
<span md-highlight-text="editor.autocomplete.cc.searchText"
md-highlight-flags="^i">{{user}}</span>
</md-autocomplete>
<md-chip-template>{{$chip}}</md-chip-template>
</md-chips>
</div>
<div class="pseudo-input-container" ng-hide="editor.hideBcc">
<label class="pseudo-input-label">
<var:string label:value="Bcc"/>
</label>
<md-chips ng-model="editor.message.editable.bcc">
<md-autocomplete
md-selected-item="editor.autocomplete.bcc.selected"
md-search-text="editor.autocomplete.bcc.searchText"
md-items="user in editor.contactFilter(editor.autocomplete.bcc.searchText)"
label:placeholder="Add a recipient">
<span md-highlight-text="editor.autocomplete.bcc.searchText"
md-highlight-flags="^i">{{user}}</span>
</md-autocomplete>
<md-chip-template>{{$chip}}</md-chip-template>
</md-chips>
</div>
<md-input-container>
<label>
<var:string label:value="Subject"/>
</label>
<input type="text" name="subject" ng-model="editor.message.editable.subject"/>
</md-input-container>
<md-list>
<!-- FILES ALREADY UPLOADED, FOR EXAMPLE WHEN WE FORWARD A
MAIL WITH ATTACHMENTS -->
<md-list-item ng-repeat="item in editor.message.editable.attachmentAttrs">
<div layout="row" layout-align="space-between center">
<span ng-bind="item.filename"><!-- filename --></span>
<md-button class="sg-icon-button"
ng-click="editor.message.$deleteAttachment(item.filename);">
<md-icon>remove_circle_outline</md-icon>
</md-button>
</div>
</md-list-item>
<!-- FILE BEING ATTACHED TO A MAIL -->
<md-list-item ng-repeat="item in editor.uploader.queue">
<div layout="row" layout-align="space-between center">
<span ng-bind="item.file.name"><!-- filename --></span>
<md-progress-linear md-mode="determinate"
value="{{item.progress}}"
ng-show="item.isUploading" ><!-- progress--></md-progress-linear>
<md-button class="sg-icon-button"
ng-click="editor.uploader.cancelItem(item)"
ng-show="item.isUploading">
<md-icon>cancel</md-icon>
</md-button>
<md-button class="sg-icon-button"
ng-click="editor.message.$deleteAttachment(item.file.name); item.remove();"
ng-show="!item.isUploading">
<md-icon>remove_circle_outline</md-icon>
</md-button>
</div>
</md-list-item>
</md-list>
</div>
<!-- cc -->
<div class="pseudo-input-container" ng-hide="editor.hideCc">
<label class="pseudo-input-label">
<var:string label:value="Cc"/>
</label>
<md-chips ng-model="editor.message.editable.cc"
md-on-append="editor.addRecipient($chip)">
<md-autocomplete
class="sg-chips-autocomplete"
md-search-text="editor.autocomplete.cc.searchText"
md-selected-item="editor.autocomplete.cc.selected"
md-items="user in editor.contactFilter(editor.autocomplete.cc.searchText)"
md-min-length="3"
md-delay="300"
md-no-cache="true"
label:placeholder="Add a recipient">
<md-item-template>
<span class="md-contact-suggestion">
<span class="md-contact-name"
md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user.$$fullname}}</span>
<span class="md-contact-email"
md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user.$$email}}</span>
</span>
</md-item-template>
</md-autocomplete>
<md-chip-template>{{$chip}}</md-chip-template>
</md-chips>
</div>
</header>
<!-- bcc -->
<div class="pseudo-input-container" ng-hide="editor.hideBcc">
<label class="pseudo-input-label">
<var:string label:value="Bcc"/>
</label>
<md-chips ng-model="editor.message.editable.bcc"
md-on-append="editor.addRecipient($chip)">
<md-autocomplete
class="sg-chips-autocomplete"
md-selected-item="editor.autocomplete.bcc.selected"
md-search-text="editor.autocomplete.bcc.searchText"
md-items="user in editor.contactFilter(editor.autocomplete.bcc.searchText)"
md-min-length="3"
md-delay="300"
md-no-cache="true"
label:placeholder="Add a recipient">
<md-item-template>
<span class="md-contact-suggestion">
<span class="md-contact-name"
md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user.$$fullname}}</span>
<span class="md-contact-email"
md-highlight-text="editor.autocomplete.to.searchText"
md-highlight-flags="^i">{{user.$$email}}</span>
</span>
</md-item-template>
</md-autocomplete>
<md-chip-template>{{$chip}}</md-chip-template>
</md-chips>
</div>
<!-- MESSAGE CONTENT -->
<textarea name="content" var:class="editorClass"
ng-model="editor.message.editable.text"/>
<!-- subject -->
<md-input-container>
<label>
<var:string label:value="Subject"/>
</label>
<input type="text" name="subject" ng-model="editor.message.editable.subject"/>
</md-input-container>
<!-- TOOLBAR BUTTONS -->
<div class="buttonsToolbar">
<div layout="row" layout-align="end center">
<md-button ng-click="editor.cancel()"><var:string label:value="Cancel"/></md-button>
<md-button ng-click="editor.message.$save()"><var:string label:value="Save"/></md-button>
<md-button class="fg-sogoBlue-700 md-primary md-hue-3"
ng-click="editor.send()"><var:string label:value="Send"/></md-button>
</div>
</div>
<!-- toolbar to show cc/bcc/attachment fields -->
<div layout="column" layout-align="end end">
<md-button class="sg-icon-button " ng-show="editor.hideCc" ng-click="editor.hideCc = false">Cc</md-button>
<md-button class="sg-icon-button " ng-show="editor.hideBcc" ng-click="editor.hideBcc = false">Bcc</md-button>
<md-menu>
<md-button label:aria-label="More mail options" class="sg-icon-button" ng-click="$mdOpenMenu($event)">
<md-icon>more_vert</md-icon>
</md-button>
<md-menu-content width="4">
<md-menu-item>
<md-button ng-href="#">
<md-checkbox ng-model="editor.message.editable.receipt">
<var:string label:value="Return Receipt"/>
</md-checkbox>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button class="md-primary" ng-disabled="true"><var:string label:value="Priority"/></md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="editor.message.editable.priority = 1">
<md-icon ng-class="{ 'icon-check': editor.message.editable.priority == 1}"><!-- highest --></md-icon>
<var:string label:value="highest"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="editor.message.editable.priority = 2">
<md-icon ng-class="{ 'icon-check': editor.message.editable.priority == 2}"><!-- high --></md-icon>
<var:string label:value="high"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="editor.message.editable.priority = 3">
<md-icon ng-class="{ 'icon-check':
!editor.message.editable.priority
|| editor.message.editable.priority == 3}"><!-- normal --></md-icon>
<var:string label:value="normal"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="editor.message.editable.priority = 4">
<md-icon ng-class="{ 'icon-check': editor.message.editable.priority == 4}"><!-- low --></md-icon>
<var:string label:value="low"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="editor.message.editable.priority = 5">
<md-icon ng-class="{ 'icon-check': editor.message.editable.priority == 5}"><!-- lowest --></md-icon>
<var:string label:value="lowest"/>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
</div>
</header>
<!-- MESSAGE CONTENT -->
<textarea name="content" var:class="editorClass"
ck-locale="editor.localeCode"
ng-model="editor.message.editable.text"/>
</form>
</md-dialog-content>
<!-- TOOLBAR BUTTONS -->
<div class="md-actions">
<div layout="row" layout-align="space-between center" layout-fill="layout-fill">
<div>
<!-- md-icon does not play well with Firefox - makes the
file input never appear. This should eventually be
fixed in Angular Material -->
<label class="md-button" for="file-input">
<span><var:string label:value="Attach"/></span>
</label>
<input id="file-input" type="file"
nv-file-select="nv-file-select"
multiple="multiple"
uploader="editor.uploader"
ng-show="false"/>
</div>
<div>
<!-- FILES ALREADY UPLOADED, FOR EXAMPLE WHEN WE FORWARD A
MAIL WITH ATTACHMENTS -->
<md-chips ng-model="editor.message.editable.attachmentAttrs"
class="sg-readonly" readonly="true">
<md-chip-template>
<span>{{$chip.filename}}</span>
<md-icon ng-click="editor.message.$deleteAttachment($chip.filename);">close</md-icon>
</md-chip-template>
</md-chips>
<!-- FILE BEING ATTACHED TO A MAIL -->
<md-chips ng-model="editor.uploader.queue"
class="sg-readonly" readonly="true">
<md-chip-template>
<span>{{$chip.file.name}}</span>
<md-icon ng-show="!$chip.isUploading"
ng-click="editor.message.$deleteAttachment($chip.file.name);
$chip.remove();">close</md-icon>
<md-icon ng-show="$chip.isUploading"
ng-click="editor.uploader.cancelItem($chip)"
ng-style="{ 'color': '#F00' }">cancel</md-icon>
<span ng-show="$chip.isUploading">{{$chip.progress}} %</span>
</md-chip-template>
</md-chips>
</div>
</div>
</div>
</md-dialog>
</container>
+39 -249
View File
@@ -4,250 +4,48 @@
xmlns:var="http://www.skyrix.com/od/binding"
xmlns:label="OGo:label">
<div class="view-list" layout="column">
<!-- in virtual mailbox mode -->
<md-toolbar ng-hide="!app.showingAdvancedSearch || mailbox.selectedFolder.$selectedCount() > 0">
<div class="md-toolbar-tools">
<md-input-container>
<label><var:string label:value="Search messages in"/></label>
<md-select ng-model="app.search.mailbox">
<md-option ng-value="">
<span>{{app.accounts[0].name}}</span>
</md-option>
<md-option ng-repeat="folder in
app.accounts[0].$flattenMailboxes()
track by folder.path"
ng-value="folder.path">
<span ng-class="'sg-child-level-' + folder.level">{{folder.name}}</span>
</md-option>
</md-select>
</md-input-container>
<md-menu>
<md-button class="sg-icon-button" label:aria-label="More search options" ng-click="$mdOpenMenu()">
<md-icon>settings</md-icon>
</md-button>
<md-menu-content>
<md-menu-item>
<md-button ng-href="#">
<md-checkbox ng-model="app.search.subfolders"
ng-true-value="1"
ng-false-value="0">
<var:string label:value="Search subfolders"/>
</md-checkbox>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="app.search.match='AND'">
<md-icon ng-class="{ 'icon-check': app.search.match == 'AND'}">
<!-- all --></md-icon><var:string label:value="Match all of the following"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="app.search.match='OR'">
<md-icon ng-class="{ 'icon-check': app.search.match == 'OR'}">
<!-- any --></md-icon><var:string label:value="Match any of the following"/>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
<md-button class="sg-icon-button"
ng-click="app.toggleAdvancedSearch()"
ng-disabled="app.search.params.length == 0">
<md-icon ng-class="{'icon-stop': app.service.selectedFolder.$isLoading,
'icon-search': !app.service.selectedFolder.$isLoading}"><!-- icon --></md-icon>
</md-button>
</div>
</md-toolbar>
<!-- single-selection toolbars -->
<md-toolbar ng-hide="app.showingAdvancedSearch || mailbox.selectedFolder.$selectedCount() > 0">
<!-- sort mode (default) -->
<div class="md-toolbar-tools" ng-hide="mailbox.mode.search">
<md-button class="sg-icon-button" label:aria-label="Search"
ng-click="mailbox.mode.search = true">
<md-icon>search</md-icon>
</md-button>
<div class="sg-folder-name">{{mailbox.selectedFolder.name}}</div>
<md-menu>
<md-button class="sg-icon-button" label:aria-label="Sort"
ng-click="$mdOpenMenu()">
<md-icon>sort</md-icon>
</md-button>
<md-menu-content>
<md-menu-item>
<md-button ng-click="mailbox.sort('subject')">
<md-icon ng-class="{ 'icon-check': mailbox.sortedBy('subject') }">
<!-- subject --></md-icon> <var:string label:value="Subject"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="mailbox.sort('from')">
<md-icon ng-class="{ 'icon-check': mailbox.sortedBy('from') }">
<!-- from --></md-icon> <var:string label:value="From"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="mailbox.sort('date')">
<md-icon ng-class="{ 'icon-check': mailbox.sortedBy('date') }">
<!-- date --></md-icon> <var:string label:value="Date"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="mailbox.sort('size')">
<md-icon ng-class="{ 'icon-check': mailbox.sortedBy('size') }">
<!-- size --></md-icon> <var:string label:value="Size"/>
</md-button>
</md-menu-item>
<md-menu-divider> <!-- divider --></md-menu-divider>
<md-menu-item>
<md-button ng-click="mailbox.selectedFolder.$filter()">
<md-checkbox
ng-model="mailbox.service.$query.asc"
ng-true-value="0"
ng-false-value="1"><var:string label:value="Descending Order"/></md-checkbox>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
<md-button class="sg-icon-button" aria-label="Refresh" ng-click="mailbox.selectedFolder.$filter()" >
<md-icon>refresh</md-icon>
</md-button>
</div>
<!-- search mode -->
<div class="md-toolbar-tools sg-toolbar-secondary"
ng-show="mailbox.mode.search"
sg-search="mailbox.selectedFolder.$filter({ sort: 'date', asc: false }, [{ searchBy: searchField, searchInput: searchText }])">
<md-button class="sg-icon-button"
sg-search-cancel="mailbox.cancelSearch()"
label:aria-label="Back">
<md-icon>arrow_back</md-icon>
</md-button>
<md-input-container>
<input name="folderSearch" type="search" ng-minlength="3"/>
</md-input-container>
<md-input-container flex="25">
<md-select>
<md-option value="subject" selected="selected"><var:string label:value="Subject"/></md-option>
<md-option value="from"><var:string label:value="Sender"/></md-option>
<md-option value="subject_or_from"><var:string label:value="Subject or Sender"/></md-option>
<md-option value="to_or_cc"><var:string label:value="To or Cc"/></md-option>
<md-option value="body"><var:string label:value="Entire Message"/></md-option>
</md-select>
</md-input-container>
</div>
</md-toolbar>
<!-- multiple-selection mode -->
<md-toolbar class="md-hue-1 sg-toolbar-secondary" ng-show="mailbox.selectedFolder.$selectedCount() > 0">
<div class="md-toolbar-tools">
<md-button class="sg-icon-button" ng-click="mailbox.unselectMessages()">
<md-icon>arrow_back</md-icon>
</md-button>
<label>{{mailbox.service.selectedFolder.$selectedCount()}} <var:string label:value="selected"/></label>
<div class="md-flex"><!-- spacer --></div>
<md-button class="sg-icon-button" ng-click="mailbox.selectAll()">
<md-tooltip md-direction="bottom"><var:string label:value="Select All"/></md-tooltip>
<md-icon>select_all</md-icon>
</md-button>
<md-button class="sg-icon-button" ng-click="mailbox.confirmDeleteSelectedMessages()">
<md-icon>delete</md-icon>
</md-button>
<md-menu>
<md-button class="sg-icon-button" label:aria-label="Copy messages" ng-click="$mdOpenMenu()">
<md-tooltip md-direction="bottom"><var:string label:value="Copy To"/></md-tooltip>
<md-icon>content_copy</md-icon>
</md-button>
<md-menu-content width="4">
<div ng-repeat="account in mailbox.accounts track by account.id">
<md-menu-item>
<md-button class="md-primary" ng-disabled="true">{{account.name}}</md-button>
</md-menu-item>
<md-menu-divider><!-- divider --></md-menu-divider>
<md-menu-item ng-repeat="folder in
account.$flattenMailboxes()
track by folder.path"
ng-hide="mailbox.id == folder.id">
<md-button ng-click="mailbox.copySelectedMessages(folder.id)">
<span ng-class="'sg-child-level-' + folder.level">{{folder.name}}</span>
</md-button>
</md-menu-item>
</div>
</md-menu-content>
</md-menu>
<md-menu>
<md-button class="sg-icon-button" label:aria-label="More messages options" ng-click="$mdOpenMenu()">
<md-icon>more_vert</md-icon>
</md-button>
<md-menu-content width="2">
<md-menu-item>
<md-button ng-click="mailbox.markSelectedMessagesAsFlagged()">
<var:string label:value="Flag"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="mailbox.markSelectedMessagesAsUnread()">
<var:string label:value="Mark as Unread"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="mailbox.saveSelectedMessages()">
<var:string label:value="Save As..."/>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
</div>
</md-toolbar>
<md-content id="messagesList" layout="column" class="md-flex">
<md-subheader>
<span ng-switch="mailbox.service.selectedFolder.getLength()">
<span ng-switch-when="0"><var:string label:value="No message"/></span>
<span ng-switch-default="true">{{mailbox.service.selectedFolder.getLength()}} <var:string label:value="messages"/></span>
</span>
</md-subheader>
<md-progress-linear ng-show="mailbox.selectedFolder.$isLoading" md-mode="indeterminate"><!-- progress --></md-progress-linear>
<header class="sg-md-subheader sg-md-subheader--fixed">
<h2 class="md-default-theme sg-md-subhead-solo fg-sogoBlue-700">
<span ng-show="mailbox.selectedFolder.$messages.length > 0">{{mailbox.selectedFolder.$messages.length}} <var:string label:value="messages"/></span>
<span ng-show="mailbox.selectedFolder.$messages.length == 0"><var:string label:value="No message"/></span>
</h2>
</header>
<md-virtual-repeat-container class="md-flex">
<md-list class="sg-section-list">
<md-list-item
md-virtual-repeat="currentMessage in mailbox.service.selectedFolder"
md-on-demand="md-on-demand"
ng-class="{'sg-active': currentMessage.uid == mailbox.service.selectedFolder.selectedMessage, unread: !currentMessage.isread}"
ng-click="mailbox.selectMessage(currentMessage)"
ui-sref="mail.account.mailbox.message({accountId: mailbox.account.id, mailboxId: (mailbox.selectedFolder.path | encodeUri), messageId: currentMessage.uid})"
ui-sref-active="md-default-theme md-background md-bg md-hue-1">
<div class="sg-selected-avatar"
ng-show="currentMessage.selected"
ng-click="mailbox.toggleMessageSelection($event, currentMessage)"><!-- selected avatar --></div>
<sg-avatar-image class="md-tile-left"
ng-show="!currentMessage.selected"
ng-click="mailbox.toggleMessageSelection($event, currentMessage)"
sg-email="::currentMessage.from[0].email"
size="40"><!-- avatar --></sg-avatar-image>
<div class="sg-tile-content">
<span class="msg-date"
ng-bind-html="currentMessage.relativedate"><!-- date --></span>
<div class="sg-md-subhead-multi">
<span class="sg-label-outline"
ng-show="mailbox.service.$virtualMode">{{currentMessage.$mailbox.name}}</span>
{{currentMessage.$shortAddress('from')}}
<md-list>
<div md-virtual-repeat="currentMessage in mailbox.selectedFolder.$messages">
<md-list-item
ng-class="{'sg-active': currentMessage.uid == mailbox.selectedFolder.selectedMessage, unread: !currentMessage.isread}"
ng-click="mailbox.selectMessage(currentMessage)"
ui-sref="mail.account.mailbox.message({accountId: mailbox.account.id, mailboxId: (mailbox.selectedFolder.path | encodeUri), messageId: currentMessage.uid})">
<!-- ui-sref-active="sg-active"> -->
<div class="sg-selected-avatar"
ng-show="currentMessage.selected"
ng-click="currentMessage.selected = !currentMessage.selected"><!-- selected avatar --></div>
<sg-avatar-image class="md-tile-left"
ng-show="!currentMessage.selected"
ng-click="currentMessage.selected = !currentMessage.selected"
sg-email="currentMessage['from'][0].email"
size="48"><!-- avatar --></sg-avatar-image>
<div class="sg-tile-content">
<span class="msg-date"
ng-bind-html="currentMessage.relativedate"><!-- date --></span>
<div class="sg-md-subhead-multi">{{currentMessage.$shortAddress('from')}}</div>
<div class="sg-md-body-multi">{{currentMessage.subject}}</div>
<i class="icon-ion-refresh"
ng-hide="mailbox.selectedFolder.$loadMessage(currentMessage.uid)"><!-- loading --></i>
</div>
<div class="sg-md-body-multi">{{currentMessage.subject}}</div>
</div>
<div class="sg-tile-icons">
<md-icon ng-show="currentMessage.priority == 'highest' || currentMessage.priority == 'high'">warning</md-icon>
<md-icon ng-show="currentMessage.isflagged">star</md-icon>
<md-icon ng-show="currentMessage.isanswered">reply</md-icon>
<md-icon ng-show="currentMessage.isforwarded">forward</md-icon>
<md-icon ng-show="currentMessage.hasattachment">attach_file</md-icon>
</div>
</md-list-item>
<div class="sg-tile-icons">
<md-icon ng-show="currentMessage.isflagged">star</md-icon>
<md-icon ng-show="currentMessage.isanswered">reply</md-icon>
<md-icon ng-show="currentMessage.isforwarded">forward</md-icon>
<md-icon ng-show="currentMessage.hasattachment">attach_file</md-icon>
</div>
</md-list-item>
</div>
</md-list>
</md-virtual-repeat-container>
<div class="sg-progress-circular-floating"
ng-show="mailbox.service.selectedFolder.$isLoading">
<md-progress-circular class="md-accent"
md-mode="indeterminate"
md-diameter="32"><!-- progress --></md-progress-circular>
</div>
<md-button class="md-fab md-fab-bottom-right md-accent"
label:aria-label="Write a new message"
ng-click="mailbox.newMessage($event)">
@@ -256,15 +54,7 @@
</md-content>
</div>
<div id="detailView" class="view-detail" layout="column" layout-align="start center"
ng-class="{ 'sg-close': !mailbox.service.selectedFolder.selectedMessage }"
ui-view="message">
<md-toolbar class="hide-sm"><!-- empty toolbar --></md-toolbar>
<md-content class="hide-sm md-flex layout-fill md-hue-1" layout="column">
<md-input-container layout-align="center center">
<label class="sg-md-title"><var:string label:value="No message selected"/></label>
</md-input-container>
</md-content>
</div>
<div id="detailView" class="view-detail ng-cloak" layout="column" ui-view="message"
ng-show="mailbox.selectedFolder.selectedMessage"><!-- message view --></div>
</container>
Loaded 100 of 280 files, more files were not shown because too many files have changed in this diff. Show more